From b97dcbc12592fc93d2c8137f3d56a523e994a136 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Tue, 2 Feb 2016 11:09:16 -0800 Subject: [tests] Reorganize tests to match src structure --- test/util/assert.cpp | 9 ++ test/util/async_task.cpp | 126 +++++++++++++++++++++ test/util/clip_ids.cpp | 251 +++++++++++++++++++++++++++++++++++++++++ test/util/geo.cpp | 61 ++++++++++ test/util/image.cpp | 101 +++++++++++++++++ test/util/mapbox.cpp | 145 ++++++++++++++++++++++++ test/util/merge_lines.cpp | 75 ++++++++++++ test/util/run_loop.cpp | 75 ++++++++++++ test/util/text_conversions.cpp | 35 ++++++ test/util/thread.cpp | 226 +++++++++++++++++++++++++++++++++++++ test/util/thread_local.cpp | 95 ++++++++++++++++ test/util/timer.cpp | 173 ++++++++++++++++++++++++++++ test/util/token.cpp | 50 ++++++++ test/util/work_queue.cpp | 63 +++++++++++ 14 files changed, 1485 insertions(+) create mode 100644 test/util/assert.cpp create mode 100644 test/util/async_task.cpp create mode 100644 test/util/clip_ids.cpp create mode 100644 test/util/geo.cpp create mode 100644 test/util/image.cpp create mode 100644 test/util/mapbox.cpp create mode 100644 test/util/merge_lines.cpp create mode 100644 test/util/run_loop.cpp create mode 100644 test/util/text_conversions.cpp create mode 100644 test/util/thread.cpp create mode 100644 test/util/thread_local.cpp create mode 100644 test/util/timer.cpp create mode 100644 test/util/token.cpp create mode 100644 test/util/work_queue.cpp (limited to 'test/util') diff --git a/test/util/assert.cpp b/test/util/assert.cpp new file mode 100644 index 0000000000..f3eba009f0 --- /dev/null +++ b/test/util/assert.cpp @@ -0,0 +1,9 @@ +#include "../fixtures/util.hpp" + +#include + +using namespace mbgl; + +TEST(Assert, Always) { + EXPECT_DEATH(assert_always(true == false), "failed assertion `true == false'"); +} diff --git a/test/util/async_task.cpp b/test/util/async_task.cpp new file mode 100644 index 0000000000..8073670fe1 --- /dev/null +++ b/test/util/async_task.cpp @@ -0,0 +1,126 @@ +#include +#include +#include + +#include "../fixtures/util.hpp" + +#include + +using namespace mbgl::util; + +namespace { + +class TestWorker { +public: + TestWorker(AsyncTask *async_) + : async(async_) {} + + void run() { + for (unsigned i = 0; i < 100000; ++i) { + async->send(); + } + } + + void runWithCallback(std::function cb) { + for (unsigned i = 0; i < 100000; ++i) { + async->send(); + } + + cb(); + } + +private: + AsyncTask *async; +}; + +} // namespace + +TEST(AsyncTask, RequestCoalescing) { + RunLoop loop; + + unsigned count = 0; + AsyncTask async([&count] { ++count; }); + + async.send(); + async.send(); + async.send(); + async.send(); + async.send(); + + loop.runOnce(); + + EXPECT_EQ(count, 1); +} + +TEST(AsyncTask, DestroyShouldNotRunQueue) { + RunLoop loop; + + unsigned count = 0; + auto async = std::make_unique([&count] { ++count; }); + + async->send(); + async.reset(); + + EXPECT_EQ(count, 0); +} + +TEST(AsyncTask, RequestCoalescingMultithreaded) { + RunLoop loop; + + unsigned count = 0; + AsyncTask async([&count] { ++count; }); + + std::vector>> threads; + ThreadContext context = {"Test", ThreadType::Map, ThreadPriority::Regular}; + + unsigned numThreads = 25; + for (unsigned i = 0; i < numThreads; ++i) { + std::unique_ptr> thread = + std::make_unique>(context, &async); + + thread->invoke(&TestWorker::run); + threads.push_back(std::move(thread)); + } + + // Join all the threads + threads.clear(); + + loop.runOnce(); + + EXPECT_EQ(count, 1); +} + +TEST(AsyncTask, ThreadSafety) { + RunLoop loop; + + unsigned count = 0; + AsyncTask async([&count] { ++count; }); + + unsigned numThreads = 25; + + auto callback = [&] { + if (!--numThreads) { + loop.stop(); + } + }; + + std::vector>> threads; + std::vector> requests; + ThreadContext context = {"Test", ThreadType::Map, ThreadPriority::Regular}; + + for (unsigned i = 0; i < numThreads; ++i) { + std::unique_ptr> thread = + std::make_unique>(context, &async); + + requests.push_back( + thread->invokeWithCallback(&TestWorker::runWithCallback, callback)); + + threads.push_back(std::move(thread)); + } + + loop.run(); + + // We expect here more than 1 but 1 would also be + // a valid result, although very unlikely (I hope). + EXPECT_GT(count, 1); +} diff --git a/test/util/clip_ids.cpp b/test/util/clip_ids.cpp new file mode 100644 index 0000000000..4ecd1797e6 --- /dev/null +++ b/test/util/clip_ids.cpp @@ -0,0 +1,251 @@ +#include +#include "../fixtures/util.hpp" + +#include + +#include +#include + +using namespace mbgl; + +template void generate(const T &sources) { + ClipIDGenerator generator; + + for (size_t j = 0; j < sources.size(); j++) { + std::forward_list tile_ptrs; + std::transform(sources[j].begin(), sources[j].end(), std::front_inserter(tile_ptrs), [](const std::shared_ptr &tile) { return tile.get(); }); + generator.update(tile_ptrs); + } +} + +template void print(const T &sources) { + for (size_t j = 0; j < sources.size(); j++) { + for (size_t i = 0; i < sources[j].size(); i++) { + std::cout << " ASSERT_EQ(ClipID(\"" << sources[j][i]->clip.mask << "\", \"" << sources[j][i]->clip.reference << "\"), sources[" << j << "][" << i << "]->clip);\n"; + } + } +} + +TEST(ClipIDs, ParentAndFourChildren) { + const std::vector>> sources = { + { + std::make_shared(TileID { 1, 0, 0, 1 }), + std::make_shared(TileID { 1, 0, 1, 1 }), + std::make_shared(TileID { 1, 1, 0, 1 }), + std::make_shared(TileID { 1, 1, 1, 1 }), + std::make_shared(TileID { 0, 0, 0, 0 }), + }, + }; + + generate(sources); + // print(sources); + + ASSERT_EQ(ClipID("00000111", "00000010"), sources[0][0]->clip); + ASSERT_EQ(ClipID("00000111", "00000011"), sources[0][1]->clip); + ASSERT_EQ(ClipID("00000111", "00000100"), sources[0][2]->clip); + ASSERT_EQ(ClipID("00000111", "00000101"), sources[0][3]->clip); + ASSERT_EQ(ClipID("00000111", "00000001"), sources[0][4]->clip); +} + +TEST(ClipIDs, ParentAndFourChildrenNegative) { + const std::vector>> sources = { + { + std::make_shared(TileID { 1, -2, 0, 1 }), + std::make_shared(TileID { 1, -2, 1, 1 }), + std::make_shared(TileID { 1, -1, 0, 1 }), + std::make_shared(TileID { 1, -1, 1, 1 }), + std::make_shared(TileID { 0, -1, 0, 0 }), + }, + }; + + generate(sources); + // print(sources); + + ASSERT_EQ(ClipID("00000111", "00000010"), sources[0][0]->clip); + ASSERT_EQ(ClipID("00000111", "00000011"), sources[0][1]->clip); + ASSERT_EQ(ClipID("00000111", "00000100"), sources[0][2]->clip); + ASSERT_EQ(ClipID("00000111", "00000101"), sources[0][3]->clip); + ASSERT_EQ(ClipID("00000111", "00000001"), sources[0][4]->clip); +} + +TEST(ClipIDs, NegativeParentAndMissingLevel) { + const std::vector>> sources = { + { + std::make_shared(TileID { 1, -1, 0, 1 }), + std::make_shared(TileID { 2, -1, 0, 2 }), + std::make_shared(TileID { 2, -2, 1, 2 }), + std::make_shared(TileID { 2, -1, 1, 2 }), + std::make_shared(TileID { 2, -2, 0, 2 }), + }, + }; + + generate(sources); + // print(sources); + + ASSERT_EQ(ClipID("00000111", "00000001"), sources[0][0]->clip); + ASSERT_EQ(ClipID("00000111", "00000100"), sources[0][1]->clip); + ASSERT_EQ(ClipID("00000111", "00000011"), sources[0][2]->clip); + ASSERT_EQ(ClipID("00000111", "00000101"), sources[0][3]->clip); + ASSERT_EQ(ClipID("00000111", "00000010"), sources[0][4]->clip); +} + + +TEST(ClipIDs, SevenOnSameLevel) { + const std::vector>> sources = { + { + std::make_shared(TileID { 2, 0, 0, 2 }), + std::make_shared(TileID { 2, 0, 1, 2 }), + std::make_shared(TileID { 2, 0, 2, 2 }), + std::make_shared(TileID { 2, 1, 0, 2 }), + std::make_shared(TileID { 2, 1, 1, 2 }), + std::make_shared(TileID { 2, 1, 2, 2 }), + std::make_shared(TileID { 2, 2, 0, 2 }), + }, + }; + + generate(sources); + // print(sources); + + ASSERT_EQ(ClipID("00000111", "00000001"), sources[0][0]->clip); + ASSERT_EQ(ClipID("00000111", "00000010"), sources[0][1]->clip); + ASSERT_EQ(ClipID("00000111", "00000011"), sources[0][2]->clip); + ASSERT_EQ(ClipID("00000111", "00000100"), sources[0][3]->clip); + ASSERT_EQ(ClipID("00000111", "00000101"), sources[0][4]->clip); + ASSERT_EQ(ClipID("00000111", "00000110"), sources[0][5]->clip); + ASSERT_EQ(ClipID("00000111", "00000111"), sources[0][6]->clip); +} + +TEST(ClipIDs, MultipleLevels) { + const std::vector>> sources = { + { + std::make_shared(TileID { 2, 0, 0, 2 }), + std::make_shared(TileID { 3, 0, 0, 3 }), + std::make_shared(TileID { 3, 0, 1, 3 }), + std::make_shared(TileID { 4, 0, 2, 4 }), + std::make_shared(TileID { 4, 1, 2, 4 }), + std::make_shared(TileID { 4, 0, 3, 4 }), + std::make_shared(TileID { 4, 1, 3, 4 }), + std::make_shared(TileID { 3, 1, 0, 3 }), + std::make_shared(TileID { 3, 1, 1, 3 }), + std::make_shared(TileID { 2, 1, 0, 2 }), + std::make_shared(TileID { 3, 2, 0, 3 }), + std::make_shared(TileID { 3, 2, 1, 3 }), + }, + }; + + generate(sources); + // print(sources); + + ASSERT_EQ(ClipID("00001111", "00000001"), sources[0][0]->clip); + ASSERT_EQ(ClipID("00001111", "00000011"), sources[0][1]->clip); + ASSERT_EQ(ClipID("00001111", "00000100"), sources[0][2]->clip); + ASSERT_EQ(ClipID("00001111", "00001001"), sources[0][3]->clip); + ASSERT_EQ(ClipID("00001111", "00001011"), sources[0][4]->clip); + ASSERT_EQ(ClipID("00001111", "00001010"), sources[0][5]->clip); + ASSERT_EQ(ClipID("00001111", "00001100"), sources[0][6]->clip); + ASSERT_EQ(ClipID("00001111", "00000101"), sources[0][7]->clip); + ASSERT_EQ(ClipID("00001111", "00000110"), sources[0][8]->clip); + ASSERT_EQ(ClipID("00001111", "00000010"), sources[0][9]->clip); + ASSERT_EQ(ClipID("00001111", "00000111"), sources[0][10]->clip); + ASSERT_EQ(ClipID("00001111", "00001000"), sources[0][11]->clip); +} + + +TEST(ClipIDs, Bug206) { + const std::vector>> sources = { + { + std::make_shared(TileID { 10, 162, 395, 10 }), + std::make_shared(TileID { 10, 162, 396, 10 }), + std::make_shared(TileID { 10, 163, 395, 10 }), + std::make_shared(TileID { 11, 326, 791, 10 }), + std::make_shared(TileID { 12, 654, 1582, 10 }), + std::make_shared(TileID { 12, 654, 1583, 10 }), + std::make_shared(TileID { 12, 655, 1582, 10 }), + std::make_shared(TileID { 12, 655, 1583, 10 }), + std::make_shared(TileID { 10, 163, 396, 10 }), + std::make_shared(TileID { 10, 164, 395, 10 }), + std::make_shared(TileID { 10, 164, 396, 10 }), + }, + }; + + generate(sources); + // print(sources); + + ASSERT_EQ(ClipID("00001111", "00000001"), sources[0][0]->clip); + ASSERT_EQ(ClipID("00001111", "00000010"), sources[0][1]->clip); + ASSERT_EQ(ClipID("00001111", "00000011"), sources[0][2]->clip); + ASSERT_EQ(ClipID("00001111", "00000111"), sources[0][3]->clip); + ASSERT_EQ(ClipID("00001111", "00001000"), sources[0][4]->clip); + ASSERT_EQ(ClipID("00001111", "00001001"), sources[0][5]->clip); + ASSERT_EQ(ClipID("00001111", "00001010"), sources[0][6]->clip); + ASSERT_EQ(ClipID("00001111", "00001011"), sources[0][7]->clip); + ASSERT_EQ(ClipID("00001111", "00000100"), sources[0][8]->clip); + ASSERT_EQ(ClipID("00001111", "00000101"), sources[0][9]->clip); + ASSERT_EQ(ClipID("00001111", "00000110"), sources[0][10]->clip); +} + + +TEST(ClipIDs, MultipleSources) { + const std::vector>> sources = { + { + std::make_shared(TileID { 0, 0, 0, 0 }), + std::make_shared(TileID { 1, 1, 1, 1 }), + std::make_shared(TileID { 2, 2, 1, 2 }), + std::make_shared(TileID { 2, 2, 2, 2 }), + }, + { + std::make_shared(TileID { 0, 0, 0, 0 }), + std::make_shared(TileID { 1, 1, 1, 1 }), + std::make_shared(TileID { 2, 1, 1, 2 }), + std::make_shared(TileID { 2, 2, 2, 2 }), + }, + { + std::make_shared(TileID { 1, 0, 0, 1 }), + std::make_shared(TileID { 1, 0, 1, 1 }), + std::make_shared(TileID { 1, 1, 0, 1 }), + std::make_shared(TileID { 1, 1, 1, 1 }), + std::make_shared(TileID { 2, 1, 1, 2 }), + }, + }; + + generate(sources); + // print(sources); + + ASSERT_EQ(ClipID("00000111", "00000001"), sources[0][0]->clip); + ASSERT_EQ(ClipID("00000111", "00000010"), sources[0][1]->clip); + ASSERT_EQ(ClipID("00000111", "00000011"), sources[0][2]->clip); + ASSERT_EQ(ClipID("00000111", "00000100"), sources[0][3]->clip); + ASSERT_EQ(ClipID("00011000", "00001000"), sources[1][0]->clip); + ASSERT_EQ(ClipID("00000111", "00000010"), sources[1][1]->clip); + ASSERT_EQ(ClipID("00011000", "00010000"), sources[1][2]->clip); + ASSERT_EQ(ClipID("00000111", "00000100"), sources[1][3]->clip); + ASSERT_EQ(ClipID("11100000", "00100000"), sources[2][0]->clip); + ASSERT_EQ(ClipID("11100000", "01000000"), sources[2][1]->clip); + ASSERT_EQ(ClipID("11100000", "01100000"), sources[2][2]->clip); + ASSERT_EQ(ClipID("11100000", "10000000"), sources[2][3]->clip); + ASSERT_EQ(ClipID("00011000", "00010000"), sources[2][4]->clip); +} + + +TEST(ClipIDs, DuplicateIDs) { + const std::vector>> sources = { + { + std::make_shared(TileID { 2, 0, 0, 2 }), + std::make_shared(TileID { 2, 0, 1, 2 }), + }, + { + std::make_shared(TileID { 2, 0, 0, 2 }), + std::make_shared(TileID { 2, 0, 1, 2 }), + std::make_shared(TileID { 2, 0, 1, 2 }), + } + }; + + generate(sources); + // print(sources); + + ASSERT_EQ(ClipID("00000011", "00000001"), sources[0][0]->clip); + ASSERT_EQ(ClipID("00000011", "00000010"), sources[0][1]->clip); + ASSERT_EQ(ClipID("00000011", "00000001"), sources[1][0]->clip); + ASSERT_EQ(ClipID("00000011", "00000010"), sources[1][1]->clip); + ASSERT_EQ(ClipID("00000011", "00000010"), sources[1][2]->clip); +} diff --git a/test/util/geo.cpp b/test/util/geo.cpp new file mode 100644 index 0000000000..88f8bc496b --- /dev/null +++ b/test/util/geo.cpp @@ -0,0 +1,61 @@ +#include "../fixtures/util.hpp" + +#include +#include + +using namespace mbgl; + +TEST(Geo, LatLngFromTileID) { + for (int i = 0; i < 20; i++) { + const LatLng ll{ TileID(i, 0, 0, 0) }; + ASSERT_DOUBLE_EQ(-180, ll.longitude); + ASSERT_DOUBLE_EQ(85.051128779806604, ll.latitude); + } + + { + const LatLng ll{ TileID(0, 1, 0, 0) }; + ASSERT_DOUBLE_EQ(180, ll.longitude); + ASSERT_DOUBLE_EQ(85.051128779806604, ll.latitude); + } + + { + const LatLng ll{ TileID(0, -1, 0, 0) }; + ASSERT_DOUBLE_EQ(-540, ll.longitude); + ASSERT_DOUBLE_EQ(85.051128779806604, ll.latitude); + } +} + + +TEST(Geo, LatLngBoundsFromTileID) { + { + const LatLngBounds bounds{ TileID(0, 0, 0, 0) }; + ASSERT_DOUBLE_EQ(-180, bounds.sw.longitude); + ASSERT_DOUBLE_EQ(-85.051128779806604, bounds.sw.latitude); + ASSERT_DOUBLE_EQ(180, bounds.ne.longitude); + ASSERT_DOUBLE_EQ(85.051128779806604, bounds.ne.latitude); + } + + { + const LatLngBounds bounds{ TileID(1, 0, 1, 0) }; + ASSERT_DOUBLE_EQ(-180, bounds.sw.longitude); + ASSERT_DOUBLE_EQ(-85.051128779806604, bounds.sw.latitude); + ASSERT_DOUBLE_EQ(0, bounds.ne.longitude); + ASSERT_DOUBLE_EQ(0, bounds.ne.latitude); + } + + { + const LatLngBounds bounds{ TileID(1, 1, 1, 0) }; + ASSERT_DOUBLE_EQ(0, bounds.sw.longitude); + ASSERT_DOUBLE_EQ(-85.051128779806604, bounds.sw.latitude); + ASSERT_DOUBLE_EQ(180, bounds.ne.longitude); + ASSERT_DOUBLE_EQ(0, bounds.ne.latitude); + } + + { + const LatLngBounds bounds{ TileID(1, 0, 0, 0) }; + ASSERT_DOUBLE_EQ(-180, bounds.sw.longitude); + ASSERT_DOUBLE_EQ(0, bounds.sw.latitude); + ASSERT_DOUBLE_EQ(0, bounds.ne.longitude); + ASSERT_DOUBLE_EQ(85.051128779806604, bounds.ne.latitude); + } +} diff --git a/test/util/image.cpp b/test/util/image.cpp new file mode 100644 index 0000000000..9886eede45 --- /dev/null +++ b/test/util/image.cpp @@ -0,0 +1,101 @@ +#include "../fixtures/util.hpp" + +#include +#include +#include + +using namespace mbgl; + +TEST(Image, PNGRoundTrip) { + PremultipliedImage rgba { 1, 1 }; + rgba.data[0] = 128; + rgba.data[1] = 0; + rgba.data[2] = 0; + rgba.data[3] = 255; + + PremultipliedImage image = decodeImage(encodePNG(rgba)); + EXPECT_EQ(128, image.data[0]); + EXPECT_EQ(0, image.data[1]); + EXPECT_EQ(0, image.data[2]); + EXPECT_EQ(255, image.data[3]); +} + +TEST(Image, PNGRoundTripAlpha) { + PremultipliedImage rgba { 1, 1 }; + rgba.data[0] = 128; + rgba.data[1] = 0; + rgba.data[2] = 0; + rgba.data[3] = 128; + + PremultipliedImage image = decodeImage(encodePNG(rgba)); + EXPECT_EQ(128, image.data[0]); + EXPECT_EQ(0, image.data[1]); + EXPECT_EQ(0, image.data[2]); + EXPECT_EQ(128, image.data[3]); +} + +TEST(Image, PNGReadNoProfile) { + PremultipliedImage image = decodeImage(util::read_file("test/fixtures/image/no_profile.png")); + EXPECT_EQ(128, image.data[0]); + EXPECT_EQ(0, image.data[1]); + EXPECT_EQ(0, image.data[2]); + EXPECT_EQ(255, image.data[3]); +} + +TEST(Image, PNGReadNoProfileAlpha) { + PremultipliedImage image = decodeImage(util::read_file("test/fixtures/image/no_profile_alpha.png")); + EXPECT_EQ(64, image.data[0]); + EXPECT_EQ(0, image.data[1]); + EXPECT_EQ(0, image.data[2]); + EXPECT_EQ(128, image.data[3]); +} + +TEST(Image, PNGReadProfile) { + PremultipliedImage image = decodeImage(util::read_file("test/fixtures/image/profile.png")); + EXPECT_EQ(128, image.data[0]); + EXPECT_EQ(0, image.data[1]); + EXPECT_EQ(0, image.data[2]); + EXPECT_EQ(255, image.data[3]); +} + +TEST(Image, PNGReadProfileAlpha) { + PremultipliedImage image = decodeImage(util::read_file("test/fixtures/image/profile_alpha.png")); + EXPECT_EQ(64, image.data[0]); + EXPECT_EQ(0, image.data[1]); + EXPECT_EQ(0, image.data[2]); + EXPECT_EQ(128, image.data[3]); +} + +TEST(Image, PNGTile) { + PremultipliedImage image = decodeImage(util::read_file("test/fixtures/image/tile.png")); + EXPECT_EQ(256, image.width); + EXPECT_EQ(256, image.height); +} + +TEST(Image, JPEGTile) { + PremultipliedImage image = decodeImage(util::read_file("test/fixtures/image/tile.jpeg")); + EXPECT_EQ(256, image.width); + EXPECT_EQ(256, image.height); +} + +#if !defined(__ANDROID__) && !defined(__APPLE__) +TEST(Image, WebPTile) { + PremultipliedImage image = decodeImage(util::read_file("test/fixtures/image/tile.webp")); + EXPECT_EQ(256, image.width); + EXPECT_EQ(256, image.height); +} +#endif // !defined(__ANDROID__) && !defined(__APPLE__) + +TEST(Image, Premultiply) { + UnassociatedImage rgba { 1, 1 }; + rgba.data[0] = 255; + rgba.data[1] = 254; + rgba.data[2] = 253; + rgba.data[3] = 128; + + PremultipliedImage image = util::premultiply(std::move(rgba)); + EXPECT_EQ(128, image.data[0]); + EXPECT_EQ(127, image.data[1]); + EXPECT_EQ(127, image.data[2]); + EXPECT_EQ(128, image.data[3]); +} diff --git a/test/util/mapbox.cpp b/test/util/mapbox.cpp new file mode 100644 index 0000000000..d6f9948e66 --- /dev/null +++ b/test/util/mapbox.cpp @@ -0,0 +1,145 @@ +#include "../fixtures/util.hpp" + +#include +#include +#include +#include + +using namespace mbgl; + +TEST(Mapbox, SourceURL) { + EXPECT_EQ(mbgl::util::mapbox::normalizeSourceURL("mapbox://user.map", "key"), "https://api.mapbox.com/v4/user.map.json?access_token=key&secure"); + EXPECT_EQ(mbgl::util::mapbox::normalizeSourceURL("mapbox://user.map", "token"), "https://api.mapbox.com/v4/user.map.json?access_token=token&secure"); + EXPECT_THROW(mbgl::util::mapbox::normalizeSourceURL("mapbox://user.map", ""), std::runtime_error); +} + +TEST(Mapbox, GlyphsURL) { + EXPECT_EQ(mbgl::util::mapbox::normalizeGlyphsURL("mapbox://fonts/boxmap/Comic%20Sans/0-255.pbf", "key"), "https://api.mapbox.com/fonts/v1/boxmap/Comic%20Sans/0-255.pbf?access_token=key"); + EXPECT_EQ(mbgl::util::mapbox::normalizeGlyphsURL("mapbox://fonts/boxmap/{fontstack}/{range}.pbf", "key"), "https://api.mapbox.com/fonts/v1/boxmap/{fontstack}/{range}.pbf?access_token=key"); + EXPECT_EQ(mbgl::util::mapbox::normalizeGlyphsURL("http://path", "key"), "http://path"); + EXPECT_EQ(mbgl::util::mapbox::normalizeGlyphsURL("mapbox://path", "key"), "mapbox://path"); +} + +TEST(Mapbox, StyleURL) { + EXPECT_EQ(mbgl::util::mapbox::normalizeStyleURL("mapbox://foo", "key"), "mapbox://foo"); + EXPECT_EQ(mbgl::util::mapbox::normalizeStyleURL("mapbox://styles/user/style", "key"), "https://api.mapbox.com/styles/v1/user/style?access_token=key"); + EXPECT_EQ(mbgl::util::mapbox::normalizeStyleURL("mapbox://styles/user/style/draft", "key"), "https://api.mapbox.com/styles/v1/user/style/draft?access_token=key"); + EXPECT_EQ(mbgl::util::mapbox::normalizeStyleURL("http://path", "key"), "http://path"); +} + +TEST(Mapbox, SpriteURL) { + EXPECT_EQ(mbgl::util::mapbox::normalizeSpriteURL("map/box/sprites@2x.json", "key"), "map/box/sprites@2x.json"); + EXPECT_EQ(mbgl::util::mapbox::normalizeSpriteURL("mapbox://foo", "key"), "mapbox://foo"); + EXPECT_EQ(mbgl::util::mapbox::normalizeSpriteURL("mapbox://sprites/mapbox/streets-v8.json", "key"), "https://api.mapbox.com/styles/v1/mapbox/streets-v8/sprite.json?access_token=key"); + EXPECT_EQ(mbgl::util::mapbox::normalizeSpriteURL("mapbox://sprites/mapbox/streets-v8@2x.png", "key"), "https://api.mapbox.com/styles/v1/mapbox/streets-v8/sprite@2x.png?access_token=key"); + EXPECT_EQ(mbgl::util::mapbox::normalizeSpriteURL("mapbox://sprites/mapbox/streets-v8/draft@2x.png", "key"), "https://api.mapbox.com/styles/v1/mapbox/streets-v8/draft/sprite@2x.png?access_token=key"); +} + +TEST(Mapbox, TileURL) { + try { +#if defined(__ANDROID__) || defined(__APPLE__) + EXPECT_EQ("http://path.png/tile{ratio}.png", mbgl::util::mapbox::normalizeRasterTileURL("http://path.png/tile.png")); + EXPECT_EQ("http://path.png/tile{ratio}.png32", mbgl::util::mapbox::normalizeRasterTileURL("http://path.png/tile.png32")); + EXPECT_EQ("http://path.png/tile{ratio}.png70", mbgl::util::mapbox::normalizeRasterTileURL("http://path.png/tile.png70")); + EXPECT_EQ("http://path.png/tile{ratio}.png?access_token=foo", mbgl::util::mapbox::normalizeRasterTileURL("http://path.png/tile.png?access_token=foo")); +#else + EXPECT_EQ("http://path.png/tile{ratio}.webp", mbgl::util::mapbox::normalizeRasterTileURL("http://path.png/tile.png")); + EXPECT_EQ("http://path.png/tile{ratio}.webp32", mbgl::util::mapbox::normalizeRasterTileURL("http://path.png/tile.png32")); + EXPECT_EQ("http://path.png/tile{ratio}.webp70", mbgl::util::mapbox::normalizeRasterTileURL("http://path.png/tile.png70")); + EXPECT_EQ("http://path.png/tile{ratio}.webp?access_token=foo", mbgl::util::mapbox::normalizeRasterTileURL("http://path.png/tile.png?access_token=foo")); +#endif // defined(__ANDROID__) || defined(__APPLE__) + EXPECT_EQ("http://path.png/tile{ratio}.pbf", mbgl::util::mapbox::normalizeRasterTileURL("http://path.png/tile.pbf")); + EXPECT_EQ("http://path.png/tile{ratio}.pbf?access_token=foo", mbgl::util::mapbox::normalizeRasterTileURL("http://path.png/tile.pbf?access_token=foo")); + EXPECT_EQ("http://path.png/tile{ratio}.pbf?access_token=foo.png", mbgl::util::mapbox::normalizeRasterTileURL("http://path.png/tile.pbf?access_token=foo.png")); + EXPECT_EQ("http://path.png/tile{ratio}.pbf?access_token=foo.png/bar", mbgl::util::mapbox::normalizeRasterTileURL("http://path.png/tile.pbf?access_token=foo.png/bar")); + EXPECT_EQ("http://path.png/tile{ratio}.pbf?access_token=foo.png/bar.png", mbgl::util::mapbox::normalizeRasterTileURL("http://path.png/tile.pbf?access_token=foo.png/bar.png")); + } catch (const std::regex_error& e) { + const char *error = "unknown"; + switch (e.code()) { + case std::regex_constants::error_collate: + error = "error_collate"; break; + case std::regex_constants::error_ctype: + error = "error_ctype"; break; + case std::regex_constants::error_escape: + error = "error_escape"; break; + case std::regex_constants::error_backref: + error = "error_backref"; break; + case std::regex_constants::error_paren: + error = "error_paren"; break; + case std::regex_constants::error_brace: + error = "error_brace"; break; + case std::regex_constants::error_badbrace: + error = "error_badbrace"; break; + case std::regex_constants::error_range: + error = "error_range"; break; + case std::regex_constants::error_space: + error = "error_space"; break; + case std::regex_constants::error_badrepeat: + error = "error_badrepeat"; break; + case std::regex_constants::error_complexity: + error = "error_complexity"; break; + case std::regex_constants::error_stack: + error = "error_stack"; break; + default: + break; + } + mbgl::Log::Error(mbgl::Event::General, "regex_error caught: %s - %s (%d)", e.what(), error, e.code()); + throw e; + } +} + +TEST(Mapbox, CanonicalURL) { + using mbgl::util::mapbox::canonicalURL; + EXPECT_EQ( + canonicalURL("https://a.tiles.mapbox.com/v4/" + "mapbox.mapbox-terrain-v2,mapbox.mapbox-streets-v6/15/17599/" + "10744.vector.pbf?access_token=pk.kAeslEm93Sjf3mXk." + "vbiF02XnvkPkzlFhGSn2iIm6De3Cxsk5tmips2tvkG8sF"), + "mapbox://v4/mapbox.mapbox-terrain-v2,mapbox.mapbox-streets-v6/15/17599/10744.vector.pbf"); + + EXPECT_EQ( + canonicalURL("http://a.tiles.mapbox.com/v4/" + "mapbox.mapbox-terrain-v2,mapbox.mapbox-streets-v6/15/17599/" + "10744.vector.pbf?access_token=pk.kAeslEm93Sjf3mXk." + "vbiF02XnvkPkzlFhGSn2iIm6De3Cxsk5tmips2tvkG8sF"), + "mapbox://v4/mapbox.mapbox-terrain-v2,mapbox.mapbox-streets-v6/15/17599/10744.vector.pbf"); + + EXPECT_EQ( + canonicalURL("https://b.tiles.mapbox.com/v4/" + "mapbox.mapbox-terrain-v2,mapbox.mapbox-streets-v6/15/17599/" + "10744.vector.pbf?access_token=pk.kAeslEm93Sjf3mXk." + "vbiF02XnvkPkzlFhGSn2iIm6De3Cxsk5tmips2tvkG8sF"), + "mapbox://v4/mapbox.mapbox-terrain-v2,mapbox.mapbox-streets-v6/15/17599/10744.vector.pbf"); + + EXPECT_EQ( + canonicalURL("http://c.tiles.mapbox.com/v4/" + "mapbox.mapbox-terrain-v2,mapbox.mapbox-streets-v6/15/17599/" + "10744.vector.pbf?access_token=pk.kAeslEm93Sjf3mXk." + "vbiF02XnvkPkzlFhGSn2iIm6De3Cxsk5tmips2tvkG8sF"), + "mapbox://v4/mapbox.mapbox-terrain-v2,mapbox.mapbox-streets-v6/15/17599/10744.vector.pbf"); + + EXPECT_EQ( + canonicalURL("https://api.mapbox.com/v4/" + "mapbox.mapbox-terrain-v2,mapbox.mapbox-streets-v6/15/17599/" + "10744.vector.pbf?access_token=pk.kAeslEm93Sjf3mXk." + "vbiF02XnvkPkzlFhGSn2iIm6De3Cxsk5tmips2tvkG8sF"), + "mapbox://v4/mapbox.mapbox-terrain-v2,mapbox.mapbox-streets-v6/15/17599/10744.vector.pbf"); + + EXPECT_EQ( + canonicalURL("http://api.mapbox.com/v4/" + "mapbox.mapbox-terrain-v2,mapbox.mapbox-streets-v6/15/17599/" + "10744.vector.pbf"), + "mapbox://v4/mapbox.mapbox-terrain-v2,mapbox.mapbox-streets-v6/15/17599/10744.vector.pbf"); + + EXPECT_EQ(canonicalURL("https://api.mapbox.com/fonts/v1/mapbox/" + "DIN%20Offc%20Pro%20Italic%2cArial%20Unicode%20MS%20Regular/" + "0-255.pbf?access_token=pk.kAeslEm93Sjf3mXk." + "vbiF02XnvkPkzlFhGSn2iIm6De3Cxsk5tmips2tvkG8sF"), + "mapbox://fonts/v1/mapbox/DIN%20Offc%20Pro%20Italic%2cArial%20Unicode%20MS%20Regular/" + "0-255.pbf"); + + EXPECT_EQ(canonicalURL("https://api.mapbox.com/styles/v1/mapbox/streets-v8/" + "sprite.json?access_token=pk.kAeslEm93Sjf3mXk." + "vbiF02XnvkPkzlFhGSn2iIm6De3Cxsk5tmips2tvkG8sF"), + "mapbox://styles/v1/mapbox/streets-v8/sprite.json"); +} diff --git a/test/util/merge_lines.cpp b/test/util/merge_lines.cpp new file mode 100644 index 0000000000..90625e9e0a --- /dev/null +++ b/test/util/merge_lines.cpp @@ -0,0 +1,75 @@ +#include "../fixtures/util.hpp" + +#include + +const std::u32string aaa = U"a"; +const std::u32string bbb = U"b"; + +TEST(MergeLines, SameText) { + // merges lines with the same text + std::vector input1 = { + { {{{0, 0}, {1, 0}, {2, 0}}}, aaa, "" }, + { {{{4, 0}, {5, 0}, {6, 0}}}, bbb, "" }, + { {{{8, 0}, {9, 0}}}, aaa, "" }, + { {{{2, 0}, {3, 0}, {4, 0}}}, aaa, "" }, + { {{{6, 0}, {7, 0}, {8, 0}}}, aaa, "" }, + { {{{5, 0}, {6, 0}}}, aaa, "" } + }; + + const std::vector expected1 = { + { {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}}}, aaa, "" }, + { {{{4, 0}, {5, 0}, {6, 0}}}, bbb, "" }, + { {{{5, 0}, {6, 0}, {7, 0}, {8, 0}, {9, 0}}}, aaa, "" }, + { {{}}, aaa, "" }, + { {{}}, aaa, "" }, + { {{}}, aaa, "" } + }; + + mbgl::util::mergeLines(input1); + + for (int i = 0; i < 6; i++) { + EXPECT_EQ(input1[i].geometry, expected1[i].geometry); + } +} + +TEST(MergeLines, BothEnds) { + // mergeLines handles merge from both ends + std::vector input2 = { + { {{{0, 0}, {1, 0}, {2, 0}}}, aaa, "" }, + { {{{4, 0}, {5, 0}, {6, 0}}}, aaa, "" }, + { {{{2, 0}, {3, 0}, {4, 0}}}, aaa, "" } + }; + + const std::vector expected2 = { + { {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}}}, aaa, "" }, + { {{}}, aaa, "" }, + { {{}}, aaa, "" } + }; + + mbgl::util::mergeLines(input2); + + for (int i = 0; i < 3; i++) { + EXPECT_EQ(input2[i].geometry, expected2[i].geometry); + } +} + +TEST(MergeLines, CircularLines) { + // mergeLines handles circular lines + std::vector input3 = { + { {{{0, 0}, {1, 0}, {2, 0}}}, aaa, "" }, + { {{{2, 0}, {3, 0}, {4, 0}}}, aaa, "" }, + { {{{4, 0}, {0, 0}}}, aaa, "" } + }; + + const std::vector expected3 = { + { {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {0, 0}}}, aaa, "" }, + { {{}}, aaa, "" }, + { {{}}, aaa, "" } + }; + + mbgl::util::mergeLines(input3); + + for (int i = 0; i < 3; i++) { + EXPECT_EQ(input3[i].geometry, expected3[i].geometry); + } +} diff --git a/test/util/run_loop.cpp b/test/util/run_loop.cpp new file mode 100644 index 0000000000..f00f7248b5 --- /dev/null +++ b/test/util/run_loop.cpp @@ -0,0 +1,75 @@ +#include +#include + +#include "../fixtures/util.hpp" + +using namespace mbgl::util; + +TEST(RunLoop, Stop) { + RunLoop loop(RunLoop::Type::New); + + Timer timer; + timer.start(mbgl::Duration::zero(), mbgl::Duration::zero(), [&] { + loop.stop(); + }); + + loop.run(); +} + +TEST(RunLoop, MultipleStop) { + RunLoop loop(RunLoop::Type::New); + + Timer timer; + timer.start(mbgl::Duration::zero(), mbgl::Duration::zero(), [&] { + loop.stop(); + loop.stop(); + loop.stop(); + loop.stop(); + }); + + loop.run(); +} + +TEST(RunLoop, UnrefShouldStop) { + RunLoop loop(RunLoop::Type::New); + + Timer timer; + timer.start(mbgl::Duration::zero(), mbgl::Duration::zero(), [&] { + loop.unref(); + }); + + loop.run(); +} + +TEST(RunLoop, RefUnref) { + RunLoop loop(RunLoop::Type::New); + + Timer timer; + auto zero = mbgl::Duration::zero(); + + auto cb3 = [&] { + loop.stop(); + }; + + auto cb2 = [&] { + loop.unref(); + loop.unref(); + + loop.ref(); + + timer.start(zero, zero, cb3); + }; + + auto cb1 = [&] { + loop.ref(); + loop.ref(); + + loop.unref(); + + timer.start(zero, zero, cb2); + }; + + timer.start(zero, zero, cb1); + + loop.run(); +} diff --git a/test/util/text_conversions.cpp b/test/util/text_conversions.cpp new file mode 100644 index 0000000000..78d88ed12e --- /dev/null +++ b/test/util/text_conversions.cpp @@ -0,0 +1,35 @@ +#include +#include "../fixtures/util.hpp" + +#include +#include + +using namespace mbgl; + +TEST(TextConversions, to_upper) { + EXPECT_EQ(std::string("STREET"), platform::uppercase("strEEt")); // EN + EXPECT_EQ(std::string("ROAD"), platform::uppercase("rOAd")); // EN + + EXPECT_EQ(std::string("STRASSE"), platform::uppercase("straße")); // DE + EXPECT_EQ(std::string("MASSE"), platform::uppercase("maße")); // DE + EXPECT_EQ(std::string("WEISSKOPFSEEADLER"), platform::uppercase("weißkopfseeadler")); // DE + + EXPECT_EQ(std::string("AZƏRBAYCAN"), platform::uppercase("Azərbaycan")); // AZ + + EXPECT_EQ(std::string("ὈΔΥΣΣΕΎΣ"), platform::uppercase("Ὀδυσσεύς")); // GR +} + + +TEST(TextConversions, to_lower) { + EXPECT_EQ(std::string("street"), platform::lowercase("strEEt")); // EN + EXPECT_EQ(std::string("road"), platform::lowercase("rOAd")); // EN + + EXPECT_EQ(std::string("straße"), platform::lowercase("Straße")); // DE + EXPECT_EQ(std::string("strasse"), platform::lowercase("STRASSE")); // DE + EXPECT_EQ(std::string("masse"), platform::lowercase("MASSE")); // DE + EXPECT_EQ(std::string("weisskopfseeadler"), platform::lowercase("weiSSkopfseeadler")); // DE + + EXPECT_EQ(std::string("azərbaycan"), platform::lowercase("AZƏRBAYCAN")); // AZ + EXPECT_EQ(std::string("ὀδυσσεύς"), platform::lowercase("ὈΔΥΣΣΕΎΣ")); // GR + +} diff --git a/test/util/thread.cpp b/test/util/thread.cpp new file mode 100644 index 0000000000..f60d7d43dd --- /dev/null +++ b/test/util/thread.cpp @@ -0,0 +1,226 @@ +#include +#include + +#include "../fixtures/util.hpp" + +using namespace mbgl::util; + +class TestObject { +public: + TestObject(std::thread::id otherTid) + : tid(std::this_thread::get_id()) { + EXPECT_NE(tid, otherTid); + } + + void fn1(int val) { + EXPECT_EQ(tid, std::this_thread::get_id()); + EXPECT_EQ(val, 1); + } + + void fn2(std::function cb) { + EXPECT_EQ(tid, std::this_thread::get_id()); + cb(1); + } + + void transferIn(std::unique_ptr val) { + EXPECT_EQ(tid, std::this_thread::get_id()); + EXPECT_EQ(*val, 1); + } + + void transferOut(std::function)> cb) { + EXPECT_EQ(tid, std::this_thread::get_id()); + cb(std::make_unique(1)); + } + + void transferInOut(std::unique_ptr val, std::function)> cb) { + EXPECT_EQ(tid, std::this_thread::get_id()); + EXPECT_EQ(*val, 1); + cb(std::move(val)); + } + + void transferInShared(std::shared_ptr val) { + EXPECT_EQ(tid, std::this_thread::get_id()); + EXPECT_EQ(*val, 1); + } + + void transferOutShared(std::function)> cb) { + EXPECT_EQ(tid, std::this_thread::get_id()); + cb(std::make_shared(1)); + } + + void transferString(const std::string& string, std::function cb) { + EXPECT_EQ(tid, std::this_thread::get_id()); + EXPECT_EQ(string, "test"); + cb(string); + } + + void checkContext(std::function cb) const { + cb(ThreadContext::currentlyOn(ThreadType::Worker) + && ThreadContext::getName() == "Test" + && ThreadContext::getPriority() == ThreadPriority::Low); + } + + const std::thread::id tid; +}; + +TEST(Thread, invoke) { + const std::thread::id tid = std::this_thread::get_id(); + + RunLoop loop; + std::vector> requests; + + loop.invoke([&] { + EXPECT_EQ(tid, std::this_thread::get_id()); + Thread thread({"Test", ThreadType::Map, ThreadPriority::Regular}, tid); + + thread.invoke(&TestObject::fn1, 1); + requests.push_back(thread.invokeWithCallback(&TestObject::fn2, [&] (int result) { + EXPECT_EQ(tid, std::this_thread::get_id()); + EXPECT_EQ(result, 1); + })); + + thread.invoke(&TestObject::transferIn, std::make_unique(1)); + requests.push_back(thread.invokeWithCallback(&TestObject::transferOut, [&] (std::unique_ptr result) { + EXPECT_EQ(tid, std::this_thread::get_id()); + EXPECT_EQ(*result, 1); + })); + + requests.push_back(thread.invokeWithCallback(&TestObject::transferInOut, [&] (std::unique_ptr result) { + EXPECT_EQ(tid, std::this_thread::get_id()); + EXPECT_EQ(*result, 1); + }, std::make_unique(1))); + + thread.invoke(&TestObject::transferInShared, std::make_shared(1)); + requests.push_back(thread.invokeWithCallback(&TestObject::transferOutShared, [&] (std::shared_ptr result) { + EXPECT_EQ(tid, std::this_thread::get_id()); + EXPECT_EQ(*result, 1); + })); + + // Cancelled request + thread.invokeWithCallback(&TestObject::fn2, [&] (int) { + ADD_FAILURE(); + }); + + std::string test("test"); + requests.push_back(thread.invokeWithCallback(&TestObject::transferString, [&] (std::string result){ + EXPECT_EQ(tid, std::this_thread::get_id()); + EXPECT_EQ(result, "test"); + loop.stop(); + }, test)); + test.clear(); + }); + + loop.run(); +} + +TEST(Thread, context) { + bool isMainThreadContext = ThreadContext::currentlyOn(ThreadType::Main) + && ThreadContext::getName() == "Main" + && ThreadContext::getPriority() == ThreadPriority::Regular; + + EXPECT_EQ(isMainThreadContext, true); + + const std::thread::id tid = std::this_thread::get_id(); + + RunLoop loop; + std::vector> requests; + + loop.invoke([&] { + Thread thread({"Test", ThreadType::Worker, ThreadPriority::Low}, tid); + + requests.push_back(thread.invokeWithCallback(&TestObject::checkContext, [&] (bool inTestThreadContext) { + EXPECT_EQ(inTestThreadContext, true); + loop.stop(); + })); + }); + + loop.run(); +} + +class TestWorker { +public: + TestWorker() = default; + + void send(std::function fn, std::function cb) { + fn(); + cb(); + } +}; + +TEST(Thread, ExecutesAfter) { + RunLoop loop; + Thread thread({"Test", ThreadType::Map, ThreadPriority::Regular}); + + bool didWork = false; + bool didAfter = false; + + auto request = thread.invokeWithCallback(&TestWorker::send, [&] { + didAfter = true; + loop.stop(); + }, [&] { + didWork = true; + }); + + loop.run(); + + EXPECT_TRUE(didWork); + EXPECT_TRUE(didAfter); +} + +TEST(Thread, WorkRequestDeletionWaitsForWorkToComplete) { + RunLoop loop; + + Thread thread({"Test", ThreadType::Map, ThreadPriority::Regular}); + + std::promise started; + bool didWork = false; + + auto request = thread.invokeWithCallback(&TestWorker::send, [&] {}, [&] { + started.set_value(); + usleep(10000); + didWork = true; + }); + + started.get_future().get(); + request.reset(); + EXPECT_TRUE(didWork); +} + +TEST(Thread, WorkRequestDeletionCancelsAfter) { + RunLoop loop; + Thread thread({"Test", ThreadType::Map, ThreadPriority::Regular}); + + std::promise started; + bool didAfter = false; + + auto request = thread.invokeWithCallback(&TestWorker::send, [&] { + didAfter = true; + }, [&] { + started.set_value(); + }); + + started.get_future().get(); + request.reset(); + loop.runOnce(); + EXPECT_FALSE(didAfter); +} + +TEST(Thread, WorkRequestDeletionCancelsImmediately) { + RunLoop loop; + Thread thread({"Test", ThreadType::Map, ThreadPriority::Regular}); + + std::promise started; + + auto request1 = thread.invokeWithCallback(&TestWorker::send, [&] {}, [&] { + usleep(10000); + started.set_value(); + }); + + auto request2 = thread.invokeWithCallback(&TestWorker::send, [&] {}, [&] { + ADD_FAILURE() << "Second work item should not be invoked"; + }); + request2.reset(); + + started.get_future().get(); + request1.reset(); +} diff --git a/test/util/thread_local.cpp b/test/util/thread_local.cpp new file mode 100644 index 0000000000..aeaf187540 --- /dev/null +++ b/test/util/thread_local.cpp @@ -0,0 +1,95 @@ +#include +#include +#include + +#include "../fixtures/util.hpp" + +using namespace mbgl::util; + +namespace { + +class TestThread { +public: + TestThread(int *number_) { + number.set(number_); + } + + ~TestThread() { + number.set(nullptr); + } + + int getNumber() { + return *number.get(); + } + +private: + static ThreadLocal number; +}; + +ThreadLocal TestThread::number; + +} // namespace + +TEST(ThreadLocalStorage, Basic) { + RunLoop loop; + + int number1 = 1; + int number2 = 2; + int number3 = 3; + + ThreadContext context = {"Test", ThreadType::Map, ThreadPriority::Regular}; + + Thread thread1(context, &number1); + Thread thread2(context, &number2); + Thread thread3(context, &number3); + + EXPECT_EQ(number1, thread1.invokeSync(&TestThread::getNumber)); + EXPECT_EQ(number2, thread2.invokeSync(&TestThread::getNumber)); + EXPECT_EQ(number3, thread3.invokeSync(&TestThread::getNumber)); +} + +TEST(ThreadLocalStorage, NotSetReturnsNull) { + static ThreadLocal number; + + EXPECT_EQ(nullptr, number.get()); +} + +namespace { + +struct DtorCounter { + ~DtorCounter() { ++(*value); } + unsigned *value; +}; + +class TestThreadReclaim { +public: + TestThreadReclaim(DtorCounter* counter_) { + counter.set(counter_); + } + +private: + static ThreadLocal counter; +}; + +ThreadLocal TestThreadReclaim::counter; + +} // namespace + +TEST(ThreadLocalStorage, AutoReclaim) { + RunLoop loop; + + unsigned counter = 0; + + DtorCounter* dtorCounter1 = new DtorCounter{ &counter }; + DtorCounter* dtorCounter2 = new DtorCounter{ &counter }; + + ThreadContext context = {"Test", ThreadType::Map, ThreadPriority::Regular}; + + auto thread1 = std::make_unique>(context, dtorCounter1); + auto thread2 = std::make_unique>(context, dtorCounter2); + + thread1.reset(); + thread2.reset(); + + EXPECT_EQ(counter, 2); +} diff --git a/test/util/timer.cpp b/test/util/timer.cpp new file mode 100644 index 0000000000..1ac72d8068 --- /dev/null +++ b/test/util/timer.cpp @@ -0,0 +1,173 @@ +#include +#include +#include +#include + +#include + +#include "../fixtures/util.hpp" + +using namespace mbgl::util; + +TEST(Timer, Basic) { + RunLoop loop; + + Timer timer; + + auto callback = [&loop] { loop.stop(); }; + + auto interval = mbgl::Milliseconds(300); + auto expectedTotalTime = interval; + + auto first = mbgl::Clock::now(); + timer.start(interval, mbgl::Duration::zero(), callback); + + loop.run(); + + auto totalTime = std::chrono::duration_cast(mbgl::Clock::now() - first); + + // These are not high precision timers. Especially libuv uses + // cached time from the beginning of of the main loop iteration + // and it is very prone to fire earlier, which is, odd. + EXPECT_GE(totalTime, expectedTotalTime * 0.8); + EXPECT_LE(totalTime, expectedTotalTime * 1.2); +} + +TEST(Timer, Repeat) { + RunLoop loop; + + Timer timer; + + unsigned count = 10; + auto callback = [&] { + if (!--count) { + loop.stop(); + } + }; + + auto interval = mbgl::Milliseconds(50); + auto expectedTotalTime = interval * count; + + auto first = mbgl::Clock::now(); + timer.start(interval, interval, callback); + + loop.run(); + + auto totalTime = std::chrono::duration_cast(mbgl::Clock::now() - first); + + EXPECT_GE(totalTime, expectedTotalTime * 0.8); + EXPECT_LE(totalTime, expectedTotalTime * 1.2); +} + +TEST(Timer, Stop) { + RunLoop loop; + + Timer timer1; + Timer timer2; + + auto interval1 = mbgl::Milliseconds(50); + auto interval2 = mbgl::Milliseconds(250); + auto expectedTotalTime = interval2; + + int count = 0; + + auto callback1 = [&] { + ++count; + timer1.stop(); + }; + + auto callback2 = [&] { + ++count; + loop.stop(); + }; + + auto first = mbgl::Clock::now(); + timer1.start(interval1, interval1, callback1); + timer2.start(interval2, mbgl::Duration::zero(), callback2); + + loop.run(); + + auto totalTime = std::chrono::duration_cast(mbgl::Clock::now() - first); + + EXPECT_EQ(count, 2); + + EXPECT_GE(totalTime, expectedTotalTime * 0.8); + EXPECT_LE(totalTime, expectedTotalTime * 1.2); +} + +TEST(Timer, DestroyShouldStop) { + RunLoop loop; + + auto timer1 = std::make_unique(); + Timer timer2; + + auto interval1 = mbgl::Milliseconds(50); + auto interval2 = mbgl::Milliseconds(250); + auto expectedTotalTime = interval2; + + int count = 0; + + auto callback1 = [&] { + ++count; + timer1.reset(); + }; + + auto callback2 = [&] { + ++count; + loop.stop(); + }; + + auto first = mbgl::Clock::now(); + timer1->start(interval1, interval1, callback1); + timer2.start(interval2, mbgl::Duration::zero(), callback2); + + loop.run(); + + auto totalTime = std::chrono::duration_cast(mbgl::Clock::now() - first); + + EXPECT_EQ(count, 2); + + EXPECT_GE(totalTime, expectedTotalTime * 0.8); + EXPECT_LE(totalTime, expectedTotalTime * 1.2); +} + +TEST(Timer, StartOverrides) { + RunLoop loop; + + Timer timer; + + auto interval1 = mbgl::Milliseconds(50); + auto interval2 = mbgl::Milliseconds(250); + auto expectedTotalTime = interval1 + interval2; + + int count = 0; + + auto callback2 = [&] { + ++count; + loop.stop(); + }; + + auto callback1 = [&] { + ++count; + timer.start(interval2, mbgl::Duration::zero(), callback2); + }; + + auto first = mbgl::Clock::now(); + timer.start(interval1, mbgl::Duration::zero(), callback1); + + loop.run(); + + auto totalTime = std::chrono::duration_cast(mbgl::Clock::now() - first); + + EXPECT_EQ(count, 2); + + EXPECT_GE(totalTime, expectedTotalTime * 0.8); + EXPECT_LE(totalTime, expectedTotalTime * 1.2); +} + +TEST(Timer, CanStopNonStartedTimer) { + RunLoop loop; + + Timer timer; + timer.stop(); +} diff --git a/test/util/token.cpp b/test/util/token.cpp new file mode 100644 index 0000000000..add31afbad --- /dev/null +++ b/test/util/token.cpp @@ -0,0 +1,50 @@ +#include +#include "../fixtures/util.hpp" + +#include + +using namespace mbgl; + +TEST(Token, replaceTokens) { + EXPECT_EQ("literal", mbgl::util::replaceTokens("literal", [](const std::string& token) -> std::string { + if (token == "name") return "14th St NW"; + return ""; + })); + EXPECT_EQ("14th St NW", mbgl::util::replaceTokens("{name}", [](const std::string& token) -> std::string { + if (token == "name") return "14th St NW"; + return ""; + })); + EXPECT_EQ("", mbgl::util::replaceTokens("{name}", [](const std::string& token) -> std::string { + if (token == "text") return "14th St NW"; + return ""; + })); + EXPECT_EQ("1400", mbgl::util::replaceTokens("{num}", [](const std::string& token) -> std::string { + if (token == "num") return "1400"; + return ""; + })); + EXPECT_EQ("500 m", mbgl::util::replaceTokens("{num} m", [](const std::string& token) -> std::string { + if (token == "num") return "500"; + return ""; + })); + EXPECT_EQ("3 Fine Fields", mbgl::util::replaceTokens("{a} {b} {c}", [](const std::string& token) -> std::string { + if (token == "a") return "3"; + if (token == "b") return "Fine"; + if (token == "c") return "Fields"; + return ""; + })); + EXPECT_EQ(" but still", mbgl::util::replaceTokens("{notset} but still", [](const std::string&) -> std::string { + return ""; + })); + EXPECT_EQ("dashed", mbgl::util::replaceTokens("{dashed-property}", [](const std::string& token) -> std::string { + if (token == "dashed-property") return "dashed"; + return ""; + })); + EXPECT_EQ("150 m", mbgl::util::replaceTokens("{HØYDE} m", [](const std::string& token) -> std::string { + if (token == "HØYDE") return "150"; + return ""; + })); + EXPECT_EQ("reserved {for:future} use", mbgl::util::replaceTokens("reserved {for:future} use", [](const std::string& token) -> std::string { + if (token == "for:future") return "unknown"; + return ""; + })); +} diff --git a/test/util/work_queue.cpp b/test/util/work_queue.cpp new file mode 100644 index 0000000000..a6cd6c3f88 --- /dev/null +++ b/test/util/work_queue.cpp @@ -0,0 +1,63 @@ +#include "../fixtures/util.hpp" + +#include +#include +#include + +#include + +using namespace mbgl::util; + +class TestThread { +public: + TestThread(WorkQueue* queue_) : queue(queue_) {} + + void send(std::function&& fn) { + EXPECT_TRUE(ThreadContext::currentlyOn(ThreadType::Map)); + + queue->push(std::move(fn)); + } + +private: + WorkQueue* queue; +}; + +TEST(WorkQueue, push) { + RunLoop loop; + + WorkQueue queue; + Thread thread({"Test", ThreadType::Map, ThreadPriority::Regular}, &queue); + + uint8_t count = 0; + + auto endTest = [&]() { + EXPECT_TRUE(ThreadContext::currentlyOn(ThreadType::Main)); + + if (++count == 4) { + loop.stop(); + } + }; + + thread.invoke(&TestThread::send, endTest); + thread.invoke(&TestThread::send, endTest); + thread.invoke(&TestThread::send, endTest); + thread.invoke(&TestThread::send, endTest); + + loop.run(); +} + +TEST(WorkQueue, cancel) { + RunLoop loop; + + WorkQueue queue; + + auto work = [&]() { + FAIL() << "Should never be called"; + }; + + queue.push(work); + queue.push(work); + queue.push(work); + queue.push(work); + queue.push(work); +} -- cgit v1.2.1