diff options
Diffstat (limited to 'test')
78 files changed, 1775 insertions, 1411 deletions
diff --git a/test/.clang-tidy b/test/.clang-tidy new file mode 100644 index 0000000000..492d4affbd --- /dev/null +++ b/test/.clang-tidy @@ -0,0 +1,2 @@ +Checks: 'modernize-*,-modernize-use-equals-delete,-modernize-use-equals-default,misc-static-assert,llvm-namespace-comment,-clang-analyzer-security.insecureAPI.rand,-clang-analyzer-core.uninitialized.UndefReturn,-clang-analyzer-core.StackAddressEscape,-clang-analyzer-core.CallAndMessage,-clang-diagnostic-unused-command-line-argument,-clang-analyzer-core.uninitialized.*,-clang-analyzer-core.NullDereference,-clang-analyzer-core.NonNullParamChecker' +HeaderFilterRegex: '\/mbgl\/' diff --git a/test/actor/actor.test.cpp b/test/actor/actor.test.cpp index 03f41a6e64..3d97469628 100644 --- a/test/actor/actor.test.cpp +++ b/test/actor/actor.test.cpp @@ -6,6 +6,7 @@ #include <chrono> #include <functional> #include <future> +#include <memory> using namespace mbgl; using namespace std::chrono_literals; @@ -26,7 +27,7 @@ TEST(Actor, Construction) { EXPECT_TRUE(constructed); } -TEST(Actor, DestructionClosesMailbox) { +TEST(Actor, DestructionBlocksOnReceive) { // Destruction blocks until the actor is not receiving. struct Test { @@ -67,6 +68,149 @@ TEST(Actor, DestructionClosesMailbox) { exitingPromise.set_value(); } +TEST(Actor, DestructionBlocksOnSend) { + // Destruction blocks until the actor is not being sent a message. + + struct TestScheduler : public Scheduler { + std::promise<void> promise; + std::future<void> future; + std::atomic<bool> waited; + + TestScheduler(std::promise<void> promise_, std::future<void> future_) + : promise(std::move(promise_)), + future(std::move(future_)), + waited(false) { + } + + ~TestScheduler() { + EXPECT_TRUE(waited.load()); + } + + void schedule(std::weak_ptr<Mailbox>) final { + promise.set_value(); + future.wait(); + std::this_thread::sleep_for(1ms); + waited = true; + } + }; + + struct Test { + Test(ActorRef<Test>) {} + void message() {} + }; + + std::promise<void> enteredPromise; + std::future<void> enteredFuture = enteredPromise.get_future(); + + std::promise<void> exitingPromise; + std::future<void> exitingFuture = exitingPromise.get_future(); + + auto scheduler = std::make_unique<TestScheduler>(std::move(enteredPromise), std::move(exitingFuture)); + auto actor = std::make_unique<Actor<Test>>(*scheduler); + + std::thread thread { + [] (ActorRef<Test> ref) { + ref.invoke(&Test::message); + }, + actor->self() + }; + + enteredFuture.wait(); + exitingPromise.set_value(); + + actor.reset(); + scheduler.reset(); + + thread.join(); +} + +TEST(Actor, DestructionAllowedInReceiveOnSameThread) { + // Destruction doesn't block if occurring on the same + // thread as receive(). This prevents deadlocks and + // allows for self-closing actors + + struct Test { + + Test(ActorRef<Test>){}; + + void callMeBack(std::function<void ()> callback) { + callback(); + } + }; + + ThreadPool pool { 1 }; + + std::promise<void> callbackFiredPromise; + + auto test = std::make_unique<Actor<Test>>(pool); + + // Callback (triggered while mutex is locked in Mailbox::receive()) + test->invoke(&Test::callMeBack, [&]() { + // Destroy the Actor/Mailbox in the same thread + test.reset(); + callbackFiredPromise.set_value(); + }); + + auto status = callbackFiredPromise.get_future().wait_for(std::chrono::seconds(1)); + ASSERT_EQ(std::future_status::ready, status); +} + +TEST(Actor, SelfDestructionDoesntCrashWaitingReceivingThreads) { + // Ensures destruction doesn't cause waiting threads to + // crash when a actor closes it's own mailbox from a + // callback + + struct Test { + + Test(ActorRef<Test>){}; + + void callMeBack(std::function<void ()> callback) { + callback(); + } + }; + + + ThreadPool pool { 2 }; + + std::promise<void> actorClosedPromise; + + auto closingActor = std::make_unique<Actor<Test>>(pool); + auto waitingActor = std::make_unique<Actor<Test>>(pool); + + std::atomic<bool> waitingMessageProcessed {false}; + + // Callback (triggered while mutex is locked in Mailbox::receive()) + closingActor->invoke(&Test::callMeBack, [&]() { + + // Queue up another message from another thread + std::promise<void> messageQueuedPromise; + waitingActor->invoke(&Test::callMeBack, [&]() { + // This will be waiting on the mutex in + // Mailbox::receive(), holding a lock + // on the weak_ptr so the mailbox is not + // destroyed + closingActor->invoke(&Test::callMeBack, [&]() { + waitingMessageProcessed.store(true); + }); + messageQueuedPromise.set_value(); + }); + + // Wait for the message to be queued + ASSERT_EQ( + messageQueuedPromise.get_future().wait_for(std::chrono::seconds(1)), + std::future_status::ready + ); + + // Destroy the Actor/Mailbox in the same thread + closingActor.reset(); + actorClosedPromise.set_value(); + }); + + auto status = actorClosedPromise.get_future().wait_for(std::chrono::seconds(1)); + ASSERT_EQ(std::future_status::ready, status); + ASSERT_FALSE(waitingMessageProcessed.load()); +} + TEST(Actor, OrderedMailbox) { // Messages are processed in order. diff --git a/test/api/annotations.test.cpp b/test/api/annotations.test.cpp index 97ccaae684..7594d5ed73 100644 --- a/test/api/annotations.test.cpp +++ b/test/api/annotations.test.cpp @@ -3,6 +3,7 @@ #include <mbgl/util/default_thread_pool.hpp> #include <mbgl/annotation/annotation.hpp> +#include <mbgl/style/style.hpp> #include <mbgl/style/image.hpp> #include <mbgl/map/map.hpp> #include <mbgl/map/backend_scope.hpp> @@ -16,9 +17,12 @@ using namespace mbgl; namespace { +PremultipliedImage namedImage(const std::string& name) { + return decodeImage(util::read_file("test/fixtures/sprites/" + name + ".png")); +} + std::unique_ptr<style::Image> namedMarker(const std::string& name) { - PremultipliedImage image = decodeImage(util::read_file("test/fixtures/sprites/" + name)); - return std::make_unique<style::Image>(std::move(image), 1.0); + return std::make_unique<style::Image>(name, namedImage(name), 1.0); } class AnnotationTest { @@ -42,8 +46,8 @@ public: TEST(Annotations, SymbolAnnotation) { AnnotationTest test; - test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); - test.map.addAnnotationImage("default_marker", namedMarker("default_marker.png")); + test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.addAnnotationImage(namedMarker("default_marker")); test.map.addAnnotation(SymbolAnnotation { Point<double>(0, 0), "default_marker" }); test.checkRendering("point_annotation"); @@ -64,7 +68,7 @@ TEST(Annotations, LineAnnotation) { annotation.color = Color::red(); annotation.width = { 5 }; - test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); test.map.addAnnotation(annotation); test.checkRendering("line_annotation"); @@ -79,7 +83,7 @@ TEST(Annotations, FillAnnotation) { FillAnnotation annotation { polygon }; annotation.color = Color::red(); - test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); test.map.addAnnotation(annotation); test.checkRendering("fill_annotation"); @@ -92,7 +96,7 @@ TEST(Annotations, AntimeridianAnnotationSmall) { double antimeridian = 180; test.map.setLatLngZoom(mbgl::LatLng(0, antimeridian), 0); - test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); LineString<double> line = {{ { antimeridian, 20 }, { antimeridian, -20 } }}; LineAnnotation lineAnnotation { line }; @@ -113,7 +117,7 @@ TEST(Annotations, AntimeridianAnnotationLarge) { double antimeridian = 180; test.map.setLatLngZoom(mbgl::LatLng(0, antimeridian), 0); - test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); LineString<double> line = {{ { antimeridian, 20 }, { antimeridian, -20 } }}; LineAnnotation lineAnnotation { line }; @@ -138,27 +142,17 @@ TEST(Annotations, OverlappingFillAnnotation) { FillAnnotation overlaidAnnotation { polygon }; overlaidAnnotation.color = Color::red(); - test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); test.map.addAnnotation(underlaidAnnotation); test.map.addAnnotation(overlaidAnnotation); test.checkRendering("overlapping_fill_annotation"); } -TEST(Annotations, StyleSourcedShapeAnnotation) { - AnnotationTest test; - - Polygon<double> polygon = { {{ { 0, 0 }, { 0, 45 }, { 45, 45 }, { 45, 0 } }} }; - - test.map.setStyleJSON(util::read_file("test/fixtures/api/annotation.json")); - test.map.addAnnotation(StyleSourcedAnnotation { polygon, "annotation" }); - test.checkRendering("style_sourced_shape_annotation"); -} - TEST(Annotations, AddMultiple) { AnnotationTest test; - test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); - test.map.addAnnotationImage("default_marker", namedMarker("default_marker.png")); + test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.addAnnotationImage(namedMarker("default_marker")); test.map.addAnnotation(SymbolAnnotation { Point<double> { -10, 0 }, "default_marker" }); test::render(test.map, test.view); @@ -170,7 +164,7 @@ TEST(Annotations, AddMultiple) { TEST(Annotations, NonImmediateAdd) { AnnotationTest test; - test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); test::render(test.map, test.view); Polygon<double> polygon = { {{ { 0, 0 }, { 0, 45 }, { 45, 45 }, { 45, 0 } }} }; @@ -184,9 +178,9 @@ TEST(Annotations, NonImmediateAdd) { TEST(Annotations, UpdateSymbolAnnotationGeometry) { AnnotationTest test; - test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); - test.map.addAnnotationImage("default_marker", namedMarker("default_marker.png")); - test.map.addAnnotationImage("flipped_marker", namedMarker("flipped_marker.png")); + test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.addAnnotationImage(namedMarker("default_marker")); + test.map.addAnnotationImage(namedMarker("flipped_marker")); AnnotationID point = test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" }); test::render(test.map, test.view); @@ -198,9 +192,9 @@ TEST(Annotations, UpdateSymbolAnnotationGeometry) { TEST(Annotations, UpdateSymbolAnnotationIcon) { AnnotationTest test; - test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); - test.map.addAnnotationImage("default_marker", namedMarker("default_marker.png")); - test.map.addAnnotationImage("flipped_marker", namedMarker("flipped_marker.png")); + test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.addAnnotationImage(namedMarker("default_marker")); + test.map.addAnnotationImage(namedMarker("flipped_marker")); AnnotationID point = test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" }); test::render(test.map, test.view); @@ -216,7 +210,7 @@ TEST(Annotations, UpdateLineAnnotationGeometry) { annotation.color = Color::red(); annotation.width = { 5 }; - test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); AnnotationID line = test.map.addAnnotation(annotation); test::render(test.map, test.view); @@ -233,7 +227,7 @@ TEST(Annotations, UpdateLineAnnotationStyle) { annotation.color = Color::red(); annotation.width = { 5 }; - test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); AnnotationID line = test.map.addAnnotation(annotation); test::render(test.map, test.view); @@ -250,7 +244,7 @@ TEST(Annotations, UpdateFillAnnotationGeometry) { FillAnnotation annotation { Polygon<double> { {{ { 0, 0 }, { 0, 45 }, { 45, 45 }, { 45, 0 } }} } }; annotation.color = Color::red(); - test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); AnnotationID fill = test.map.addAnnotation(annotation); test::render(test.map, test.view); @@ -267,7 +261,7 @@ TEST(Annotations, UpdateFillAnnotationStyle) { FillAnnotation annotation { polygon }; annotation.color = Color::red(); - test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); AnnotationID fill = test.map.addAnnotation(annotation); test::render(test.map, test.view); @@ -280,8 +274,8 @@ TEST(Annotations, UpdateFillAnnotationStyle) { TEST(Annotations, RemovePoint) { AnnotationTest test; - test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); - test.map.addAnnotationImage("default_marker", namedMarker("default_marker.png")); + test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.addAnnotationImage(namedMarker("default_marker")); AnnotationID point = test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" }); test::render(test.map, test.view); @@ -298,7 +292,7 @@ TEST(Annotations, RemoveShape) { annotation.color = Color::red(); annotation.width = { 5 }; - test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); AnnotationID shape = test.map.addAnnotation(annotation); test::render(test.map, test.view); @@ -311,7 +305,7 @@ TEST(Annotations, ImmediateRemoveShape) { AnnotationTest test; test.map.removeAnnotation(test.map.addAnnotation(LineAnnotation { LineString<double>() })); - test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); test::render(test.map, test.view); } @@ -319,21 +313,34 @@ TEST(Annotations, ImmediateRemoveShape) { TEST(Annotations, SwitchStyle) { AnnotationTest test; - test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); - test.map.addAnnotationImage("default_marker", namedMarker("default_marker.png")); + test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.addAnnotationImage(namedMarker("default_marker")); test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" }); test::render(test.map, test.view); - test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); test.checkRendering("switch_style"); } +TEST(Annotations, ReaddImage) { + AnnotationTest test; + + test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.addAnnotationImage(namedMarker("default_marker")); + test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" }); + + test::render(test.map, test.view); + + test.map.addAnnotationImage(std::make_unique<style::Image>("default_marker", namedImage("flipped_marker"), 1.0)); + test.checkRendering("readd_image"); +} + TEST(Annotations, QueryRenderedFeatures) { AnnotationTest test; - test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); - test.map.addAnnotationImage("default_marker", namedMarker("default_marker.png")); + test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.addAnnotationImage(namedMarker("default_marker")); test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" }); test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 50 }, "default_marker" }); @@ -356,8 +363,8 @@ TEST(Annotations, QueryFractionalZoomLevels) { auto viewSize = test.view.getSize(); auto box = ScreenBox { {}, { double(viewSize.width), double(viewSize.height) } }; - test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); - test.map.addAnnotationImage("default_marker", namedMarker("default_marker.png")); + test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.addAnnotationImage(namedMarker("default_marker")); std::vector<mbgl::AnnotationID> ids; for (int longitude = 0; longitude < 10; ++longitude) { @@ -388,8 +395,8 @@ TEST(Annotations, VisibleFeatures) { auto viewSize = test.view.getSize(); auto box = ScreenBox { {}, { double(viewSize.width), double(viewSize.height) } }; - test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); - test.map.addAnnotationImage("default_marker", namedMarker("default_marker.png")); + test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.addAnnotationImage(namedMarker("default_marker")); test.map.setLatLngZoom({ 5, 5 }, 3); std::vector<mbgl::AnnotationID> ids; @@ -419,6 +426,12 @@ TEST(Annotations, VisibleFeatures) { EXPECT_EQ(features.size(), ids.size()); } +TEST(Annotations, TopOffsetPixels) { + AnnotationTest test; + + test.map.addAnnotationImage(namedMarker("default_marker")); + EXPECT_EQ(test.map.getTopOffsetPixelsForAnnotationImage("default_marker"), -28); +} TEST(Annotations, DebugEmpty) { // This test should render nothing, not even the tile borders. Tile borders are only rendered @@ -426,7 +439,7 @@ TEST(Annotations, DebugEmpty) { // should not render them. AnnotationTest test; - test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); test.map.setDebug(MapDebugOptions::TileBorders); test.map.setZoom(1); @@ -439,10 +452,10 @@ TEST(Annotations, DebugSparse) { // tiles because they're all empty. AnnotationTest test; - test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); test.map.setDebug(MapDebugOptions::TileBorders); test.map.setZoom(1); - test.map.addAnnotationImage("default_marker", namedMarker("default_marker.png")); + test.map.addAnnotationImage(namedMarker("default_marker")); test.map.addAnnotation(SymbolAnnotation { Point<double>(10, 10), "default_marker" }); test.checkRendering("debug_sparse"); diff --git a/test/api/api_misuse.test.cpp b/test/api/api_misuse.test.cpp index af703fddfb..54cde8d9b5 100644 --- a/test/api/api_misuse.test.cpp +++ b/test/api/api_misuse.test.cpp @@ -44,31 +44,3 @@ TEST(API, RenderWithoutCallback) { EXPECT_EQ(log->count(logMessage), 1u); } - -TEST(API, RenderWithoutStyle) { - util::RunLoop loop; - - HeadlessBackend backend { test::sharedDisplay() }; - BackendScope scope { backend }; - OffscreenView view { backend.getContext(), { 128, 512 } }; - StubFileSource fileSource; - ThreadPool threadPool(4); - - Map map(backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still); - - std::exception_ptr error; - map.renderStill(view, [&](std::exception_ptr error_) { - error = error_; - loop.stop(); - }); - - loop.run(); - - try { - std::rethrow_exception(error); - } catch (const util::MisuseException& ex) { - EXPECT_EQ(std::string(ex.what()), "Map doesn't have a style"); - } catch (const std::exception&) { - EXPECT_TRUE(false) << "Unhandled exception."; - } -} diff --git a/test/api/custom_layer.test.cpp b/test/api/custom_layer.test.cpp index 5a30220cd7..2b1138a1d3 100644 --- a/test/api/custom_layer.test.cpp +++ b/test/api/custom_layer.test.cpp @@ -7,6 +7,7 @@ #include <mbgl/gl/offscreen_view.hpp> #include <mbgl/util/default_thread_pool.hpp> #include <mbgl/storage/default_file_source.hpp> +#include <mbgl/style/style.hpp> #include <mbgl/style/layers/custom_layer.hpp> #include <mbgl/style/layers/fill_layer.hpp> #include <mbgl/util/io.hpp> @@ -93,9 +94,9 @@ TEST(CustomLayer, Basic) { ThreadPool threadPool(4); Map map(backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still); - map.setStyleJSON(util::read_file("test/fixtures/api/water.json")); + map.getStyle().loadJSON(util::read_file("test/fixtures/api/water.json")); map.setLatLngZoom({ 37.8, -122.5 }, 10); - map.addLayer(std::make_unique<CustomLayer>( + map.getStyle().addLayer(std::make_unique<CustomLayer>( "custom", [] (void* context) { reinterpret_cast<TestLayer*>(context)->initialize(); @@ -110,7 +111,7 @@ TEST(CustomLayer, Basic) { auto layer = std::make_unique<FillLayer>("landcover", "mapbox"); layer->setSourceLayer("landcover"); layer->setFillColor(Color{ 1.0, 1.0, 0.0, 1.0 }); - map.addLayer(std::move(layer)); + map.getStyle().addLayer(std::move(layer)); test::checkImage("test/fixtures/custom_layer/basic", test::render(map, view), 0.0006, 0.1); } diff --git a/test/api/query.test.cpp b/test/api/query.test.cpp index c509753d2d..0b6d75ec4f 100644 --- a/test/api/query.test.cpp +++ b/test/api/query.test.cpp @@ -8,6 +8,7 @@ #include <mbgl/util/image.hpp> #include <mbgl/util/io.hpp> #include <mbgl/util/run_loop.hpp> +#include <mbgl/style/style.hpp> #include <mbgl/style/image.hpp> #include <mbgl/style/source.hpp> @@ -19,8 +20,8 @@ namespace { class QueryTest { public: QueryTest() { - map.setStyleJSON(util::read_file("test/fixtures/api/query_style.json")); - map.addImage("test-icon", std::make_unique<style::Image>( + map.getStyle().loadJSON(util::read_file("test/fixtures/api/query_style.json")); + map.getStyle().addImage(std::make_unique<style::Image>("test-icon", decodeImage(util::read_file("test/fixtures/sprites/default_marker.png")), 1.0)); test::render(map, view); diff --git a/test/api/render_missing.test.cpp b/test/api/render_missing.test.cpp index 6e99501708..2e0c4401f4 100644 --- a/test/api/render_missing.test.cpp +++ b/test/api/render_missing.test.cpp @@ -10,6 +10,7 @@ #include <mbgl/util/image.hpp> #include <mbgl/util/io.hpp> #include <mbgl/util/run_loop.hpp> +#include <mbgl/style/style.hpp> #include <future> @@ -40,7 +41,7 @@ TEST(API, TEST_REQUIRES_SERVER(RenderMissingTile)) { // This host does not respond (== connection error). // Are you seeing this test fail? Make sure you don't have a server running on port 3001! - map.setStyleJSON(style); + map.getStyle().loadJSON(style); map.renderStill(view, [&](std::exception_ptr err) { ASSERT_TRUE(err.operator bool()); try { diff --git a/test/api/repeated_render.test.cpp b/test/api/repeated_render.test.cpp index 0b9726d3fa..dd9085efd7 100644 --- a/test/api/repeated_render.test.cpp +++ b/test/api/repeated_render.test.cpp @@ -10,6 +10,9 @@ #include <mbgl/util/image.hpp> #include <mbgl/util/io.hpp> #include <mbgl/util/run_loop.hpp> +#include <mbgl/style/style.hpp> +#include <mbgl/style/layers/line_layer.hpp> +#include <mbgl/style/sources/geojson_source.hpp> #include <future> @@ -24,7 +27,7 @@ TEST(API, RepeatedRender) { HeadlessBackend backend { test::sharedDisplay() }; BackendScope scope { backend }; - OffscreenView view { backend.getContext(), { 256, 512 } }; + OffscreenView view { backend.getContext(), { 512, 512 } }; DefaultFileSource fileSource(":memory:", "test/fixtures/api/assets"); ThreadPool threadPool(4); @@ -33,7 +36,7 @@ TEST(API, RepeatedRender) { Map map(backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still); { - map.setStyleJSON(style); + map.getStyle().loadJSON(style); PremultipliedImage result; map.renderStill(view, [&](std::exception_ptr) { result = view.readStillImage(); @@ -43,15 +46,13 @@ TEST(API, RepeatedRender) { loop.runOnce(); } - ASSERT_EQ(256u, result.size.width); + ASSERT_EQ(512u, result.size.width); ASSERT_EQ(512u, result.size.height); -#if !TEST_READ_ONLY - util::write_file("test/fixtures/api/1.png", encodePNG(result)); -#endif + test::checkImage("test/fixtures/api/repeated_render", result, 0.0003, 0.1); } { - map.setStyleJSON(style); + map.getStyle().loadJSON(style); PremultipliedImage result; map.renderStill(view, [&](std::exception_ptr) { result = view.readStillImage(); @@ -61,11 +62,69 @@ TEST(API, RepeatedRender) { loop.runOnce(); } - ASSERT_EQ(256u, result.size.width); + ASSERT_EQ(512u, result.size.width); ASSERT_EQ(512u, result.size.height); -#if !TEST_READ_ONLY - util::write_file("test/fixtures/api/2.png", encodePNG(result)); -#endif + test::checkImage("test/fixtures/api/repeated_render", result, 0.0003, 0.1); + } + + auto observer = Log::removeObserver(); + auto flo = dynamic_cast<FixtureLogObserver*>(observer.get()); + auto unchecked = flo->unchecked(); + EXPECT_TRUE(unchecked.empty()) << unchecked; +} + +TEST(API, ZoomHistory) { + util::RunLoop loop; + + const auto style = util::read_file("test/fixtures/api/empty.json"); + + HeadlessBackend backend { test::sharedDisplay() }; + BackendScope scope { backend }; + OffscreenView view { backend.getContext(), { 512, 512 } }; + DefaultFileSource fileSource(":memory:", "."); + ThreadPool threadPool(4); + + Log::setObserver(std::make_unique<FixtureLogObserver>()); + + Map map(backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still); + map.getStyle().loadJSON(style); + + auto geojson = mapbox::geojson::parse(R"t({ "type": "FeatureCollection", "features": [{ "type": "Feature", "properties": {}, "geometry": { "type": "LineString", "coordinates": [ [ -150, -75 ], [ 150, 75 ] ] } } ] })t"); + auto source = std::make_unique<mbgl::style::GeoJSONSource>("testSource"); + source->setGeoJSON(std::move(geojson)); + map.getStyle().addSource(std::move(source)); + + auto layer = std::make_unique<mbgl::style::LineLayer>("testLayer", "testSource"); + layer->setLineDasharray({ { 1.0f, 2.0f } }); + layer->setLineWidth({ 16.0f }); + map.getStyle().addLayer(std::move(layer)); + + { + PremultipliedImage result; + map.renderStill(view, [&](std::exception_ptr) { + result = view.readStillImage(); + }); + + while (!result.valid()) { + loop.runOnce(); + } + + test::checkImage("test/fixtures/api/z0", result, 0.0002, 0.1); + } + + { + map.setZoom(1.0); + + PremultipliedImage result; + map.renderStill(view, [&](std::exception_ptr) { + result = view.readStillImage(); + }); + + while (!result.valid()) { + loop.runOnce(); + } + + test::checkImage("test/fixtures/api/z1", result, 0.0002, 0.1); } auto observer = Log::removeObserver(); diff --git a/test/fixtures/annotations/readd_image/expected.png b/test/fixtures/annotations/readd_image/expected.png Binary files differnew file mode 100644 index 0000000000..3c4847f3a7 --- /dev/null +++ b/test/fixtures/annotations/readd_image/expected.png diff --git a/test/fixtures/api/repeated_render/expected.png b/test/fixtures/api/repeated_render/expected.png Binary files differnew file mode 100644 index 0000000000..927f6d4c82 --- /dev/null +++ b/test/fixtures/api/repeated_render/expected.png diff --git a/test/fixtures/api/z0/expected.png b/test/fixtures/api/z0/expected.png Binary files differnew file mode 100644 index 0000000000..0867a8cbf6 --- /dev/null +++ b/test/fixtures/api/z0/expected.png diff --git a/test/fixtures/api/z1/expected.png b/test/fixtures/api/z1/expected.png Binary files differnew file mode 100644 index 0000000000..897dc196cc --- /dev/null +++ b/test/fixtures/api/z1/expected.png diff --git a/test/fixtures/image_manager/basic/expected.png b/test/fixtures/image_manager/basic/expected.png Binary files differnew file mode 100644 index 0000000000..8c615234dc --- /dev/null +++ b/test/fixtures/image_manager/basic/expected.png diff --git a/test/fixtures/image_manager/updates_after/expected.png b/test/fixtures/image_manager/updates_after/expected.png Binary files differnew file mode 100644 index 0000000000..db588c739b --- /dev/null +++ b/test/fixtures/image_manager/updates_after/expected.png diff --git a/test/fixtures/image_manager/updates_before/expected.png b/test/fixtures/image_manager/updates_before/expected.png Binary files differnew file mode 100644 index 0000000000..1466a92fe7 --- /dev/null +++ b/test/fixtures/image_manager/updates_before/expected.png diff --git a/test/fixtures/offline_download/radar.gif b/test/fixtures/offline_download/radar.gif Binary files differnew file mode 100644 index 0000000000..7398a060c0 --- /dev/null +++ b/test/fixtures/offline_download/radar.gif diff --git a/test/fixtures/offline_download/style.json b/test/fixtures/offline_download/style.json index 978df3aae3..618afd45f0 100644 --- a/test/fixtures/offline_download/style.json +++ b/test/fixtures/offline_download/style.json @@ -5,6 +5,16 @@ "mapbox": { "type": "vector", "url": "http://127.0.0.1:3000/streets.json" + }, + "radar": { + "type": "image", + "url":"http://127.0.0.1:3000/radar.gif", + "coordinates": [ + [-180, -85.0511], + [180, -85.0511], + [180, 85.0511], + [-180, 85.0511] + ] } }, "glyphs": "http://127.0.0.1:3000/{fontstack}/{range}.pbf", diff --git a/test/fixtures/sprite_atlas/basic/expected.png b/test/fixtures/sprite_atlas/basic/expected.png Binary files differdeleted file mode 100644 index cd13d16df6..0000000000 --- a/test/fixtures/sprite_atlas/basic/expected.png +++ /dev/null diff --git a/test/fixtures/sprite_atlas/size/expected.png b/test/fixtures/sprite_atlas/size/expected.png Binary files differdeleted file mode 100644 index d9ae7dab47..0000000000 --- a/test/fixtures/sprite_atlas/size/expected.png +++ /dev/null diff --git a/test/fixtures/sprite_atlas/updates_after/expected.png b/test/fixtures/sprite_atlas/updates_after/expected.png Binary files differdeleted file mode 100644 index 3c850c0a25..0000000000 --- a/test/fixtures/sprite_atlas/updates_after/expected.png +++ /dev/null diff --git a/test/fixtures/sprite_atlas/updates_before/expected.png b/test/fixtures/sprite_atlas/updates_before/expected.png Binary files differdeleted file mode 100644 index effcd38f1e..0000000000 --- a/test/fixtures/sprite_atlas/updates_before/expected.png +++ /dev/null diff --git a/test/fixtures/style_parser/center-not-latlong.info.json b/test/fixtures/style_parser/center-not-latlong.info.json new file mode 100644 index 0000000000..c79de0a525 --- /dev/null +++ b/test/fixtures/style_parser/center-not-latlong.info.json @@ -0,0 +1,7 @@ +{ + "default": { + "log": [ + [1, "WARNING", "ParseStyle", "center coordinate must be a longitude, latitude pair"] + ] + } +}
\ No newline at end of file diff --git a/test/fixtures/style_parser/center-not-latlong.style.json b/test/fixtures/style_parser/center-not-latlong.style.json new file mode 100644 index 0000000000..eb847868f5 --- /dev/null +++ b/test/fixtures/style_parser/center-not-latlong.style.json @@ -0,0 +1,4 @@ +{ + "version": 8, + "center" : [ 75.123, 181.123] +}
\ No newline at end of file diff --git a/test/fixtures/style_parser/image-coordinates.info.json b/test/fixtures/style_parser/image-coordinates.info.json new file mode 100644 index 0000000000..5ef44badba --- /dev/null +++ b/test/fixtures/style_parser/image-coordinates.info.json @@ -0,0 +1,7 @@ +{ + "default": { + "log": [ + [1, "WARNING", "ParseStyle", "Image coordinates must be an array of four longitude latitude pairs"] + ] + } +}
\ No newline at end of file diff --git a/test/fixtures/style_parser/image-coordinates.style.json b/test/fixtures/style_parser/image-coordinates.style.json new file mode 100644 index 0000000000..51b0e93ee7 --- /dev/null +++ b/test/fixtures/style_parser/image-coordinates.style.json @@ -0,0 +1,14 @@ +{ + "version": 8, + "center" : [ 75.123, 81.123], + "sources": { + "image": { + "type": "image", + "url": "local://0.png", + "coordinates": [ + [ 12.2344, 87.23434], + [-12.234, 12.41242] + ] + } + } +}
\ No newline at end of file diff --git a/test/fixtures/style_parser/image-url.info.json b/test/fixtures/style_parser/image-url.info.json new file mode 100644 index 0000000000..988e89c011 --- /dev/null +++ b/test/fixtures/style_parser/image-url.info.json @@ -0,0 +1,7 @@ +{ + "default": { + "log": [ + [1, "WARNING", "ParseStyle", "Image source must have a url value"] + ] + } +}
\ No newline at end of file diff --git a/test/fixtures/style_parser/image-url.style.json b/test/fixtures/style_parser/image-url.style.json new file mode 100644 index 0000000000..94614cb7ab --- /dev/null +++ b/test/fixtures/style_parser/image-url.style.json @@ -0,0 +1,9 @@ +{ + "version": 8, + "center" : [ 75.123, 81.123], + "sources": { + "image": { + "type": "image" + } + } +}
\ No newline at end of file diff --git a/test/geometry/binpack.test.cpp b/test/geometry/binpack.test.cpp deleted file mode 100644 index 0b74df7fa9..0000000000 --- a/test/geometry/binpack.test.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include <mbgl/test/util.hpp> - -#include <mbgl/geometry/binpack.hpp> - -#include <iosfwd> -#include <array> - -namespace mbgl { -template <typename T> ::std::ostream& operator<<(::std::ostream& os, const Rect<T>& t) { - return os << "Rect { " << t.x << ", " << t.y << ", " << t.w << ", " << t.h << " }"; -} -} // namespace mbgl - -TEST(BinPack, Allocating) { - mbgl::BinPack<uint16_t> bin(128, 128); - std::array<mbgl::Rect<uint16_t>, 4> rects; - - rects[0] = bin.allocate(32, 48); - ASSERT_EQ(mbgl::Rect<uint16_t>(0, 0, 32, 48), rects[0]); - rects[1] = bin.allocate(8, 17); - ASSERT_EQ(mbgl::Rect<uint16_t>(32, 0, 8, 17), rects[1]); - rects[2] = bin.allocate(8, 17); - ASSERT_EQ(mbgl::Rect<uint16_t>(0, 48, 8, 17), rects[2]); - - bin.release(rects[0]); - rects[0] = bin.allocate(32, 24); - ASSERT_EQ(mbgl::Rect<uint16_t>(0, 0, 32, 24), rects[0]); - rects[3] = bin.allocate(32, 24); - ASSERT_EQ(mbgl::Rect<uint16_t>(32, 17, 32, 24), rects[3]); -} - - -TEST(BinPack, Full) { - mbgl::BinPack<uint16_t> bin(128, 128); - std::vector<mbgl::Rect<uint16_t>> rects; - - for (int j = 0; j < 3; j++) { - for (int i = 0; i < 256; i++) { - auto rect = bin.allocate(8, 8); - ASSERT_TRUE(rect.hasArea()); - rects.push_back(rect); - } - - ASSERT_FALSE(bin.allocate(8, 8).hasArea()); - - for (auto& rect: rects) { - bin.release(rect); - } - rects.clear(); - } -} diff --git a/test/gl/bucket.test.cpp b/test/gl/bucket.test.cpp index e21d82321d..ee9ea54414 100644 --- a/test/gl/bucket.test.cpp +++ b/test/gl/bucket.test.cpp @@ -1,28 +1,30 @@ #include <mbgl/test/util.hpp> -#include <mbgl/renderer/circle_bucket.hpp> -#include <mbgl/renderer/fill_bucket.hpp> -#include <mbgl/renderer/line_bucket.hpp> -#include <mbgl/renderer/symbol_bucket.hpp> +#include <mbgl/renderer/buckets/circle_bucket.hpp> +#include <mbgl/renderer/buckets/fill_bucket.hpp> +#include <mbgl/renderer/buckets/line_bucket.hpp> +#include <mbgl/renderer/buckets/raster_bucket.hpp> +#include <mbgl/renderer/buckets/symbol_bucket.hpp> #include <mbgl/renderer/bucket_parameters.hpp> #include <mbgl/style/layers/symbol_layer_properties.hpp> +#include <mbgl/gl/context.hpp> #include <mbgl/map/mode.hpp> using namespace mbgl; TEST(Buckets, CircleBucket) { - CircleBucket bucket { { {0, 0, 0}, MapMode::Still }, {} }; + CircleBucket bucket { { {0, 0, 0}, MapMode::Still, 1.0 }, {} }; ASSERT_FALSE(bucket.hasData()); } TEST(Buckets, FillBucket) { - FillBucket bucket { { {0, 0, 0}, MapMode::Still }, {} }; + FillBucket bucket { { {0, 0, 0}, MapMode::Still, 1.0 }, {} }; ASSERT_FALSE(bucket.hasData()); } TEST(Buckets, LineBucket) { - LineBucket bucket { { {0, 0, 0}, MapMode::Still }, {}, {} }; + LineBucket bucket { { {0, 0, 0}, MapMode::Still, 1.0 }, {}, {} }; ASSERT_FALSE(bucket.hasData()); } @@ -36,3 +38,17 @@ TEST(Buckets, SymbolBucket) { ASSERT_FALSE(bucket.hasTextData()); ASSERT_FALSE(bucket.hasCollisionBoxData()); } + +TEST(Buckets, RasterBucket) { + gl::Context context; + UnassociatedImage rgba({ 1, 1 }); + + RasterBucket bucket = { std::move(rgba) }; + ASSERT_TRUE(bucket.needsUpload()); + + bucket.upload(context); + ASSERT_FALSE(bucket.needsUpload()); + + bucket.clear(); + ASSERT_TRUE(bucket.needsUpload()); +} diff --git a/test/map/map.test.cpp b/test/map/map.test.cpp index c24f736fcd..a820590fb4 100644 --- a/test/map/map.test.cpp +++ b/test/map/map.test.cpp @@ -16,6 +16,7 @@ #include <mbgl/util/io.hpp> #include <mbgl/util/run_loop.hpp> #include <mbgl/util/async_task.hpp> +#include <mbgl/style/style.hpp> #include <mbgl/style/image.hpp> #include <mbgl/style/layers/background_layer.hpp> #include <mbgl/util/color.hpp> @@ -28,6 +29,26 @@ class BackendTest : public HeadlessBackend { public: BackendTest() : HeadlessBackend(test::sharedDisplay()) {} + void invalidate() { + if (invalidateCallback) { + invalidateCallback(); + } + } + + std::function<void()> invalidateCallback; + + void onWillStartLoadingMap() final { + if (onWillStartLoadingMapCallback) { + onWillStartLoadingMapCallback(); + } + } + + void onDidFinishLoadingMap() final { + if (onDidFinishLoadingMapCallback) { + onDidFinishLoadingMapCallback(); + } + } + void onDidFailLoadingMap(std::exception_ptr) final { if (didFailLoadingMapCallback) { didFailLoadingMapCallback(); @@ -40,6 +61,8 @@ public: } } + std::function<void()> onWillStartLoadingMapCallback; + std::function<void()> onDidFinishLoadingMapCallback; std::function<void()> didFailLoadingMapCallback; std::function<void()> didFinishLoadingStyleCallback; }; @@ -57,8 +80,6 @@ TEST(Map, LatLngBehavior) { MapTest test; Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still); - map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); - map.setLatLngZoom({ 1, 1 }, 0); auto latLng1 = map.getLatLng(); @@ -123,7 +144,7 @@ TEST(Map, Offline) { NetworkStatus::Set(NetworkStatus::Status::Offline); Map map(test.backend, test.view.getSize(), 1, fileSource, test.threadPool, MapMode::Still); - map.setStyleURL(prefix + "style.json"); + map.getStyle().loadURL(prefix + "style.json"); test::checkImage("test/fixtures/map/offline", test::render(map, test.view), @@ -146,7 +167,7 @@ TEST(Map, SetStyleInvalidJSON) { { Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still); - map.setStyleJSON("invalid"); + map.getStyle().loadJSON("invalid"); } EXPECT_TRUE(fail); @@ -175,7 +196,7 @@ TEST(Map, SetStyleInvalidURL) { }; Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still); - map.setStyleURL("mapbox://bar"); + map.getStyle().loadURL("mapbox://bar"); test.runLoop.run(); } @@ -184,8 +205,8 @@ TEST(Map, DoubleStyleLoad) { MapTest test; Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still); - map.setStyleJSON(""); - map.setStyleJSON(""); + map.getStyle().loadJSON(""); + map.getStyle().loadJSON(""); } TEST(Map, StyleFresh) { @@ -195,7 +216,7 @@ TEST(Map, StyleFresh) { FakeFileSource fileSource; Map map(test.backend, test.view.getSize(), 1, fileSource, test.threadPool, MapMode::Still); - map.setStyleURL("mapbox://styles/test"); + map.getStyle().loadURL("mapbox://styles/test"); EXPECT_EQ(1u, fileSource.requests.size()); Response response; @@ -215,7 +236,7 @@ TEST(Map, StyleExpired) { FakeFileSource fileSource; Map map(test.backend, test.view.getSize(), 1, fileSource, test.threadPool, MapMode::Still); - map.setStyleURL("mapbox://styles/test"); + map.getStyle().loadURL("mapbox://styles/test"); EXPECT_EQ(1u, fileSource.requests.size()); Response response; @@ -225,12 +246,12 @@ TEST(Map, StyleExpired) { fileSource.respond(Resource::Style, response); EXPECT_EQ(1u, fileSource.requests.size()); - map.addLayer(std::make_unique<style::BackgroundLayer>("bg")); + map.getStyle().addLayer(std::make_unique<style::BackgroundLayer>("bg")); EXPECT_EQ(1u, fileSource.requests.size()); fileSource.respond(Resource::Style, response); EXPECT_EQ(0u, fileSource.requests.size()); - EXPECT_NE(nullptr, map.getLayer("bg")); + EXPECT_NE(nullptr, map.getStyle().getLayer("bg")); } TEST(Map, StyleExpiredWithAnnotations) { @@ -242,7 +263,7 @@ TEST(Map, StyleExpiredWithAnnotations) { FakeFileSource fileSource; Map map(test.backend, test.view.getSize(), 1, fileSource, test.threadPool, MapMode::Still); - map.setStyleURL("mapbox://styles/test"); + map.getStyle().loadURL("mapbox://styles/test"); EXPECT_EQ(1u, fileSource.requests.size()); Response response; @@ -259,6 +280,32 @@ TEST(Map, StyleExpiredWithAnnotations) { EXPECT_EQ(1u, fileSource.requests.size()); } +TEST(Map, StyleExpiredWithRender) { + // Rendering should not prevent revalidation of an expired style. + + using namespace std::chrono_literals; + + MapTest test; + FakeFileSource fileSource; + + Map map(test.backend, test.view.getSize(), 1, fileSource, test.threadPool, MapMode::Still); + map.getStyle().loadURL("mapbox://styles/test"); + EXPECT_EQ(1u, fileSource.requests.size()); + + Response response; + response.data = std::make_shared<std::string>(util::read_file("test/fixtures/api/empty.json")); + response.expires = util::now() - 1h; + + fileSource.respond(Resource::Style, response); + EXPECT_EQ(1u, fileSource.requests.size()); + + map.render(test.view); + EXPECT_EQ(1u, fileSource.requests.size()); + + fileSource.respond(Resource::Style, response); + EXPECT_EQ(1u, fileSource.requests.size()); +} + TEST(Map, StyleEarlyMutation) { // An early mutation should not prevent the initial style load. @@ -266,15 +313,43 @@ TEST(Map, StyleEarlyMutation) { FakeFileSource fileSource; Map map(test.backend, test.view.getSize(), 1, fileSource, test.threadPool, MapMode::Still); - map.setStyleURL("mapbox://styles/test"); - map.addLayer(std::make_unique<style::BackgroundLayer>("bg")); + map.getStyle().loadURL("mapbox://styles/test"); + map.getStyle().addLayer(std::make_unique<style::BackgroundLayer>("bg")); Response response; response.data = std::make_shared<std::string>(util::read_file("test/fixtures/api/water.json")); fileSource.respond(Resource::Style, response); EXPECT_EQ(0u, fileSource.requests.size()); - EXPECT_NE(nullptr, map.getLayer("water")); + EXPECT_NE(nullptr, map.getStyle().getLayer("water")); +} + +TEST(Map, MapLoadingSignal) { + MapTest test; + Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still); + + bool emitted = false; + test.backend.onWillStartLoadingMapCallback = [&]() { + emitted = true; + }; + map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + EXPECT_TRUE(emitted); +} + +TEST(Map, MapLoadedSignal) { + MapTest test; + Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Continuous); + + test.backend.onDidFinishLoadingMapCallback = [&]() { + test.runLoop.stop(); + }; + + test.backend.invalidateCallback = [&]() { + map.render(test.view); + }; + + map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.runLoop.run(); } TEST(Map, StyleLoadedSignal) { @@ -286,12 +361,12 @@ TEST(Map, StyleLoadedSignal) { test.backend.didFinishLoadingStyleCallback = [&]() { emitted = true; }; - map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); + map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); EXPECT_TRUE(emitted); // But not when the style couldn't be parsed emitted = false; - map.setStyleJSON("invalid"); + map.getStyle().loadJSON("invalid"); EXPECT_FALSE(emitted); } @@ -301,7 +376,7 @@ TEST(Map, TEST_REQUIRES_SERVER(StyleNetworkErrorRetry)) { OnlineFileSource fileSource; Map map(test.backend, test.view.getSize(), 1, fileSource, test.threadPool, MapMode::Still); - map.setStyleURL("http://127.0.0.1:3000/style-fail-once-500"); + map.getStyle().loadURL("http://127.0.0.1:3000/style-fail-once-500"); test.backend.didFinishLoadingStyleCallback = [&]() { test.runLoop.stop(); @@ -315,7 +390,7 @@ TEST(Map, TEST_REQUIRES_SERVER(StyleNotFound)) { OnlineFileSource fileSource; Map map(test.backend, test.view.getSize(), 1, fileSource, test.threadPool, MapMode::Still); - map.setStyleURL("http://127.0.0.1:3000/style-fail-once-404"); + map.getStyle().loadURL("http://127.0.0.1:3000/style-fail-once-404"); using namespace std::chrono_literals; util::Timer timer; @@ -334,7 +409,7 @@ TEST(Map, TEST_REQUIRES_SERVER(StyleNotFound)) { test.runLoop.run(); // Should also not retry if the response has cache headers. - map.setStyleURL("http://127.0.0.1:3000/style-fail-once-404-cache"); + map.getStyle().loadURL("http://127.0.0.1:3000/style-fail-once-404-cache"); test.runLoop.run(); } @@ -342,11 +417,11 @@ TEST(Map, AddLayer) { MapTest test; Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still); - map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); + map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); auto layer = std::make_unique<BackgroundLayer>("background"); layer->setBackgroundColor({ { 1, 0, 0, 1 } }); - map.addLayer(std::move(layer)); + map.getStyle().addLayer(std::move(layer)); test::checkImage("test/fixtures/map/add_layer", test::render(map, test.view)); } @@ -359,7 +434,7 @@ TEST(Map, WithoutVAOExtension) { DefaultFileSource fileSource(":memory:", "test/fixtures/api/assets"); Map map(test.backend, test.view.getSize(), 1, fileSource, test.threadPool, MapMode::Still); - map.setStyleJSON(util::read_file("test/fixtures/api/water.json")); + map.getStyle().loadJSON(util::read_file("test/fixtures/api/water.json")); test::checkImage("test/fixtures/map/no_vao", test::render(map, test.view), 0.002); } @@ -368,12 +443,12 @@ TEST(Map, RemoveLayer) { MapTest test; Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still); - map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); + map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); auto layer = std::make_unique<BackgroundLayer>("background"); layer->setBackgroundColor({{ 1, 0, 0, 1 }}); - map.addLayer(std::move(layer)); - map.removeLayer("background"); + map.getStyle().addLayer(std::move(layer)); + map.getStyle().removeLayer("background"); test::checkImage("test/fixtures/map/remove_layer", test::render(map, test.view)); } @@ -400,7 +475,7 @@ TEST(Map, DisabledSources) { // to an opacity of 0.5). Then, we are zooming back out to a zoom level of 0.5 and rerender. // The "raster1" layer should not be visible anymore since it has minzoom 1, while "raster2" // should still be there. Both layers have a distinct color through "raster-hue-rotate". - map.setStyleJSON(R"STYLE( + map.getStyle().loadJSON(R"STYLE( { "version": 8, "name": "Test", @@ -439,87 +514,11 @@ TEST(Map, DisabledSources) { test::checkImage("test/fixtures/map/disabled_layers/second", test::render(map, test.view)); } -TEST(Map, Classes) { - MapTest test; - - Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still); - map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); - - EXPECT_FALSE(map.getTransitionOptions().duration); - - auto duration = mbgl::Duration(mbgl::Milliseconds(300)); - map.setTransitionOptions({ duration }); - EXPECT_EQ(map.getTransitionOptions().duration, duration); - - map.addClass("test"); - EXPECT_TRUE(map.hasClass("test")); - - map.removeClass("test"); - EXPECT_TRUE(map.getClasses().empty()); - - std::vector<std::string> classes = { "foo", "bar" }; - map.setClasses(classes); - EXPECT_FALSE(map.hasClass("test")); - EXPECT_TRUE(map.hasClass("foo")); - EXPECT_TRUE(map.hasClass("bar")); - - // Does nothing - same style JSON. - map.setStyleJSON(util::read_file("test/fixtures/api/empty.json")); - EXPECT_TRUE(map.hasClass("foo")); - EXPECT_EQ(map.getTransitionOptions().duration, duration); - - map.setStyleJSON(util::read_file("test/fixtures/api/water.json")); - EXPECT_TRUE(map.getClasses().empty()); - EXPECT_FALSE(map.getTransitionOptions().duration); -} - -TEST(Map, AddImage) { - MapTest test; - - Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still); - auto decoded1 = decodeImage(util::read_file("test/fixtures/sprites/default_marker.png")); - auto decoded2 = decodeImage(util::read_file("test/fixtures/sprites/default_marker.png")); - auto image1 = std::make_unique<style::Image>(std::move(decoded1), 1.0); - auto image2 = std::make_unique<style::Image>(std::move(decoded2), 1.0); - - // No-op. - map.addImage("test-icon", std::move(image1)); - - map.setStyleJSON(util::read_file("test/fixtures/api/icon_style.json")); - map.addImage("test-icon", std::move(image2)); - test::checkImage("test/fixtures/map/add_icon", test::render(map, test.view)); -} - -TEST(Map, RemoveImage) { - MapTest test; - - Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still); - auto decoded = decodeImage(util::read_file("test/fixtures/sprites/default_marker.png")); - auto image = std::make_unique<style::Image>(std::move(decoded), 1.0); - - map.setStyleJSON(util::read_file("test/fixtures/api/icon_style.json")); - map.addImage("test-icon", std::move(image)); - map.removeImage("test-icon"); - test::checkImage("test/fixtures/map/remove_icon", test::render(map, test.view)); -} - -TEST(Map, GetImage) { - MapTest test; - - Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still); - auto decoded = decodeImage(util::read_file("test/fixtures/sprites/default_marker.png")); - auto image = std::make_unique<style::Image>(std::move(decoded), 1.0); - - map.setStyleJSON(util::read_file("test/fixtures/api/icon_style.json")); - map.addImage("test-icon", std::move(image)); - test::checkImage("test/fixtures/map/get_icon", map.getImage("test-icon")->image); -} - TEST(Map, DontLoadUnneededTiles) { MapTest test; Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still); - map.setStyleJSON(R"STYLE({ + map.getStyle().loadJSON(R"STYLE({ "sources": { "a": { "type": "vector", "tiles": [ "a/{z}/{x}/{y}" ] } }, @@ -616,6 +615,6 @@ TEST(Map, TEST_DISABLED_ON_CI(ContinuousRendering)) { render.send(); }; - map.setStyleJSON(util::read_file("test/fixtures/api/water.json")); + map.getStyle().loadJSON(util::read_file("test/fixtures/api/water.json")); util::RunLoop::Get()->run(); } diff --git a/test/map/transform.test.cpp b/test/map/transform.test.cpp index aa49d250b6..11c2c1cc6b 100644 --- a/test/map/transform.test.cpp +++ b/test/map/transform.test.cpp @@ -28,6 +28,27 @@ TEST(Transform, InvalidZoom) { ASSERT_DOUBLE_EQ(0, transform.getLatLng().latitude()); ASSERT_DOUBLE_EQ(0, transform.getLatLng().longitude()); ASSERT_DOUBLE_EQ(1, transform.getZoom()); + + transform.setZoom(transform.getState().getMaxZoom() + 0.1); + ASSERT_DOUBLE_EQ(transform.getZoom(), transform.getState().getMaxZoom()); + + CameraOptions cameraOptions; + cameraOptions.center = LatLng { util::LATITUDE_MAX, util::LONGITUDE_MAX }; + cameraOptions.zoom = transform.getState().getMaxZoom(); + + // Executing flyTo with an empty size causes frameZoom to be NaN. + transform.flyTo(cameraOptions); + transform.updateTransitions(transform.getTransitionStart() + transform.getTransitionDuration()); + ASSERT_DOUBLE_EQ(transform.getZoom(), transform.getState().getMaxZoom()); + + // Executing flyTo with maximum zoom level to the same zoom level causes + // frameZoom to be bigger than maximum zoom. + transform.resize(Size { 100, 100 }); + transform.flyTo(cameraOptions); + transform.updateTransitions(transform.getTransitionStart() + transform.getTransitionDuration()); + + ASSERT_TRUE(transform.getState().valid()); + ASSERT_DOUBLE_EQ(transform.getState().getMaxZoom(), transform.getZoom()); } diff --git a/test/programs/symbol_program.test.cpp b/test/programs/symbol_program.test.cpp new file mode 100644 index 0000000000..ef1e71c269 --- /dev/null +++ b/test/programs/symbol_program.test.cpp @@ -0,0 +1,61 @@ +#include <mbgl/test/util.hpp> + +#include <mbgl/programs/symbol_program.hpp> + +using namespace mbgl; + +TEST(SymbolProgram, SymbolSizeBinder) { + auto binder = SymbolSizeBinder::create(5.0f, 12.0f, 0.0f); + auto uniformValues = binder->uniformValues(5.5f); + EXPECT_EQ(uniformValues.get<uniforms::u_is_size_zoom_constant>().t, true); + EXPECT_EQ(uniformValues.get<uniforms::u_is_size_feature_constant>().t, true); + EXPECT_EQ(uniformValues.get<uniforms::u_size>().t, 12.0f); + EXPECT_EQ(uniformValues.get<uniforms::u_layout_size>().t, 12.0f); + + binder = SymbolSizeBinder::create(1.0f, style::CameraFunction<float>(style::ExponentialStops<float>({ + {0.0f, 8.0f}, + {10.0f, 18.0f} + }, 1.0f)), 0.0f); + uniformValues = binder->uniformValues(1.5f); + EXPECT_EQ(uniformValues.get<uniforms::u_is_size_zoom_constant>().t, false); + EXPECT_EQ(uniformValues.get<uniforms::u_is_size_feature_constant>().t, true); + EXPECT_EQ(uniformValues.get<uniforms::u_size>().t, 9.5f); + EXPECT_EQ(uniformValues.get<uniforms::u_layout_size>().t, 10.0f); + + binder = SymbolSizeBinder::create(0.0f, style::CameraFunction<float>(style::ExponentialStops<float>({ + {1.0f, 8.0f}, + {11.0f, 18.0f} + }, 1.0f)), 0.0f); + uniformValues = binder->uniformValues(0.5f); + EXPECT_EQ(uniformValues.get<uniforms::u_is_size_zoom_constant>().t, false); + EXPECT_EQ(uniformValues.get<uniforms::u_is_size_feature_constant>().t, true); + EXPECT_EQ(uniformValues.get<uniforms::u_size>().t, 8.0f); + EXPECT_EQ(uniformValues.get<uniforms::u_layout_size>().t, 8.0f); + + binder = SymbolSizeBinder::create(12.0f, style::CameraFunction<float>(style::ExponentialStops<float>({ + {1.0f, 8.0f}, + {11.0f, 18.0f} + }, 1.0f)), 0.0f); + uniformValues = binder->uniformValues(12.5f); + EXPECT_EQ(uniformValues.get<uniforms::u_is_size_zoom_constant>().t, false); + EXPECT_EQ(uniformValues.get<uniforms::u_is_size_feature_constant>().t, true); + EXPECT_EQ(uniformValues.get<uniforms::u_size>().t, 18.0f); + EXPECT_EQ(uniformValues.get<uniforms::u_layout_size>().t, 18.0f); + + binder = SymbolSizeBinder::create(0.0f, style::SourceFunction<float>("x", style::ExponentialStops<float>({ + {1.0f, 8.0f}, + {11.0f, 18.0f} + }, 1.0f)), 0.0f); + uniformValues = binder->uniformValues(12.5f); + EXPECT_EQ(uniformValues.get<uniforms::u_is_size_zoom_constant>().t, true); + EXPECT_EQ(uniformValues.get<uniforms::u_is_size_feature_constant>().t, false); + + binder = SymbolSizeBinder::create(5.0f, style::CompositeFunction<float>("x", style::CompositeExponentialStops<float>({ + {1.0f, {{0.0f, 8.0f}, {100.0f, 18.0f}}}, + {11.0f, {{0.0f, 12.0f}, {100.0f, 24.9f}}} + }, 1.0f)), 0.0f); + uniformValues = binder->uniformValues(5.5f); + EXPECT_EQ(uniformValues.get<uniforms::u_is_size_zoom_constant>().t, false); + EXPECT_EQ(uniformValues.get<uniforms::u_is_size_feature_constant>().t, false); + EXPECT_EQ(uniformValues.get<uniforms::u_size_t>().t, 0.45f); +} diff --git a/test/renderer/group_by_layout.test.cpp b/test/renderer/group_by_layout.test.cpp index 9c8e09e222..958f1bdf24 100644 --- a/test/renderer/group_by_layout.test.cpp +++ b/test/renderer/group_by_layout.test.cpp @@ -13,7 +13,7 @@ static std::vector<std::unique_ptr<RenderLayer>> toRenderLayers(const std::vecto std::vector<std::unique_ptr<RenderLayer>> result; result.reserve(layers.size()); for (auto& layer : layers) { - result.push_back(layer->baseImpl->createRenderLayer()); + result.push_back(RenderLayer::create(layer->baseImpl)); } return result; } diff --git a/test/renderer/image_manager.test.cpp b/test/renderer/image_manager.test.cpp new file mode 100644 index 0000000000..203e05d492 --- /dev/null +++ b/test/renderer/image_manager.test.cpp @@ -0,0 +1,147 @@ +#include <mbgl/test/util.hpp> +#include <mbgl/test/fixture_log_observer.hpp> +#include <mbgl/test/stub_file_source.hpp> +#include <mbgl/test/stub_style_observer.hpp> + +#include <mbgl/renderer/image_manager.hpp> +#include <mbgl/sprite/sprite_parser.hpp> +#include <mbgl/style/image_impl.hpp> +#include <mbgl/util/io.hpp> +#include <mbgl/util/image.hpp> +#include <mbgl/util/run_loop.hpp> +#include <mbgl/util/default_thread_pool.hpp> +#include <mbgl/util/string.hpp> + +#include <utility> + +using namespace mbgl; + +TEST(ImageManager, Missing) { + ImageManager imageManager; + EXPECT_FALSE(imageManager.getImage("doesnotexist")); +} + +TEST(ImageManager, Basic) { + FixtureLog log; + ImageManager imageManager; + + auto images = parseSprite(util::read_file("test/fixtures/annotations/emerald.png"), + util::read_file("test/fixtures/annotations/emerald.json")); + for (auto& image : images) { + imageManager.addImage(image->baseImpl); + } + + auto metro = *imageManager.getPattern("metro"); + EXPECT_EQ(1, metro.tl()[0]); + EXPECT_EQ(1, metro.tl()[1]); + EXPECT_EQ(19, metro.br()[0]); + EXPECT_EQ(19, metro.br()[1]); + EXPECT_EQ(18, metro.displaySize()[0]); + EXPECT_EQ(18, metro.displaySize()[1]); + EXPECT_EQ(1.0f, metro.pixelRatio); + EXPECT_EQ(imageManager.getPixelSize(), imageManager.getAtlasImage().size); + + test::checkImage("test/fixtures/image_manager/basic", imageManager.getAtlasImage()); +} + +TEST(ImageManager, Updates) { + ImageManager imageManager; + + PremultipliedImage imageA({ 16, 12 }); + imageA.fill(255); + imageManager.addImage(makeMutable<style::Image::Impl>("one", std::move(imageA), 1)); + + auto a = *imageManager.getPattern("one"); + EXPECT_EQ(1, a.tl()[0]); + EXPECT_EQ(1, a.tl()[1]); + EXPECT_EQ(17, a.br()[0]); + EXPECT_EQ(13, a.br()[1]); + EXPECT_EQ(16, a.displaySize()[0]); + EXPECT_EQ(12, a.displaySize()[1]); + EXPECT_EQ(1.0f, a.pixelRatio); + test::checkImage("test/fixtures/image_manager/updates_before", imageManager.getAtlasImage()); + + PremultipliedImage imageB({ 5, 5 }); + imageA.fill(200); + imageManager.updateImage(makeMutable<style::Image::Impl>("one", std::move(imageB), 1)); + + auto b = *imageManager.getPattern("one"); + EXPECT_EQ(1, b.tl()[0]); + EXPECT_EQ(1, b.tl()[1]); + EXPECT_EQ(6, b.br()[0]); + EXPECT_EQ(6, b.br()[1]); + EXPECT_EQ(5, b.displaySize()[0]); + EXPECT_EQ(5, b.displaySize()[1]); + EXPECT_EQ(1.0f, b.pixelRatio); + test::checkImage("test/fixtures/image_manager/updates_after", imageManager.getAtlasImage()); +} + +TEST(ImageManager, AddRemove) { + FixtureLog log; + ImageManager imageManager; + + imageManager.addImage(makeMutable<style::Image::Impl>("one", PremultipliedImage({ 16, 16 }), 2)); + imageManager.addImage(makeMutable<style::Image::Impl>("two", PremultipliedImage({ 16, 16 }), 2)); + imageManager.addImage(makeMutable<style::Image::Impl>("three", PremultipliedImage({ 16, 16 }), 2)); + + imageManager.removeImage("one"); + imageManager.removeImage("two"); + + EXPECT_NE(nullptr, imageManager.getImage("three")); + EXPECT_EQ(nullptr, imageManager.getImage("two")); + EXPECT_EQ(nullptr, imageManager.getImage("four")); +} + +TEST(ImageManager, RemoveReleasesBinPackRect) { + FixtureLog log; + ImageManager imageManager; + + imageManager.addImage(makeMutable<style::Image::Impl>("big", PremultipliedImage({ 32, 32 }), 1)); + EXPECT_TRUE(imageManager.getImage("big")); + + imageManager.removeImage("big"); + + imageManager.addImage(makeMutable<style::Image::Impl>("big", PremultipliedImage({ 32, 32 }), 1)); + EXPECT_TRUE(imageManager.getImage("big")); + EXPECT_TRUE(log.empty()); +} + +class StubImageRequestor : public ImageRequestor { +public: + void onImagesAvailable(ImageMap images) final { + if (imagesAvailable) imagesAvailable(images); + } + + std::function<void (ImageMap)> imagesAvailable; +}; + +TEST(ImageManager, NotifiesRequestorWhenSpriteIsLoaded) { + ImageManager imageManager; + StubImageRequestor requestor; + bool notified = false; + + requestor.imagesAvailable = [&] (ImageMap) { + notified = true; + }; + + imageManager.getImages(requestor, {"one"}); + ASSERT_FALSE(notified); + + imageManager.onSpriteLoaded(); + ASSERT_TRUE(notified); +} + +TEST(ImageManager, NotifiesRequestorImmediatelyIfDependenciesAreSatisfied) { + ImageManager imageManager; + StubImageRequestor requestor; + bool notified = false; + + requestor.imagesAvailable = [&] (ImageMap) { + notified = true; + }; + + imageManager.addImage(makeMutable<style::Image::Impl>("one", PremultipliedImage({ 16, 16 }), 2)); + imageManager.getImages(requestor, {"one"}); + + ASSERT_TRUE(notified); +} diff --git a/test/sprite/sprite_atlas.test.cpp b/test/sprite/sprite_atlas.test.cpp deleted file mode 100644 index 08388f0a93..0000000000 --- a/test/sprite/sprite_atlas.test.cpp +++ /dev/null @@ -1,373 +0,0 @@ -#include <mbgl/test/util.hpp> -#include <mbgl/test/fixture_log_observer.hpp> -#include <mbgl/test/stub_file_source.hpp> -#include <mbgl/test/stub_style_observer.hpp> - -#include <mbgl/sprite/sprite_atlas.hpp> -#include <mbgl/sprite/sprite_atlas.hpp> -#include <mbgl/sprite/sprite_parser.hpp> -#include <mbgl/util/io.hpp> -#include <mbgl/util/image.hpp> -#include <mbgl/util/run_loop.hpp> -#include <mbgl/util/default_thread_pool.hpp> -#include <mbgl/util/string.hpp> - -#include <utility> - -using namespace mbgl; - -TEST(SpriteAtlas, Basic) { - FixtureLog log; - SpriteAtlas atlas({ 63, 112 }, 1); - - auto images = parseSprite(util::read_file("test/fixtures/annotations/emerald.png"), - util::read_file("test/fixtures/annotations/emerald.json")); - for (auto& pair : images) { - atlas.addImage(pair.first, std::move(pair.second)); - } - - EXPECT_EQ(1.0f, atlas.getPixelRatio()); - EXPECT_EQ(63u, atlas.getSize().width); - EXPECT_EQ(112u, atlas.getSize().height); - - auto metro = *atlas.getIcon("metro"); - float imagePixelRatio = metro.relativePixelRatio * atlas.getPixelRatio(); - EXPECT_EQ(0, metro.pos.x); - EXPECT_EQ(0, metro.pos.y); - EXPECT_EQ(20, metro.pos.w); - EXPECT_EQ(20, metro.pos.h); - EXPECT_EQ(18, metro.width); - EXPECT_EQ(18, metro.height); - EXPECT_EQ(18u, metro.width * imagePixelRatio); - EXPECT_EQ(18u, metro.height * imagePixelRatio); - EXPECT_EQ(1.0f, imagePixelRatio); - - - EXPECT_EQ(63u, atlas.getAtlasImage().size.width); - EXPECT_EQ(112u, atlas.getAtlasImage().size.height); - - auto pos = *atlas.getIcon("metro"); - EXPECT_DOUBLE_EQ(18, pos.size[0]); - EXPECT_DOUBLE_EQ(18, pos.size[1]); - EXPECT_DOUBLE_EQ(1.0f / 63, pos.tl[0]); - EXPECT_DOUBLE_EQ(1.0f / 112, pos.tl[1]); - EXPECT_DOUBLE_EQ(19.0f / 63, pos.br[0]); - EXPECT_DOUBLE_EQ(19.0f / 112, pos.br[1]); - - auto missing = atlas.getIcon("doesnotexist"); - EXPECT_FALSE(missing); - - EXPECT_EQ(1u, log.count({ - EventSeverity::Info, - Event::Sprite, - int64_t(-1), - "Can't find sprite named 'doesnotexist'", - })); - - // Different wrapping mode produces different image. - auto metro2 = *atlas.getPattern("metro"); - EXPECT_EQ(20, metro2.pos.x); - EXPECT_EQ(0, metro2.pos.y); - EXPECT_EQ(20, metro2.pos.w); - EXPECT_EQ(20, metro2.pos.h); - - test::checkImage("test/fixtures/sprite_atlas/basic", atlas.getAtlasImage()); -} - -TEST(SpriteAtlas, Size) { - SpriteAtlas atlas({ 63, 112 }, 1.4); - - auto images = parseSprite(util::read_file("test/fixtures/annotations/emerald.png"), - util::read_file("test/fixtures/annotations/emerald.json")); - for (auto& pair : images) { - atlas.addImage(pair.first, std::move(pair.second)); - } - - EXPECT_DOUBLE_EQ(1.4f, atlas.getPixelRatio()); - EXPECT_EQ(63u, atlas.getSize().width); - EXPECT_EQ(112u, atlas.getSize().height); - - auto metro = *atlas.getIcon("metro"); - float imagePixelRatio = metro.relativePixelRatio * atlas.getPixelRatio(); - EXPECT_EQ(0, metro.pos.x); - EXPECT_EQ(0, metro.pos.y); - EXPECT_EQ(16, metro.pos.w); - EXPECT_EQ(16, metro.pos.h); - EXPECT_EQ(18, metro.width); - EXPECT_EQ(18, metro.height); - EXPECT_EQ(18u, metro.width * imagePixelRatio); - EXPECT_EQ(18u, metro.height * imagePixelRatio); - EXPECT_EQ(1.0f, imagePixelRatio); - - // Now the image was created lazily. - EXPECT_EQ(89u, atlas.getAtlasImage().size.width); - EXPECT_EQ(157u, atlas.getAtlasImage().size.height); - - test::checkImage("test/fixtures/sprite_atlas/size", atlas.getAtlasImage()); -} - -TEST(SpriteAtlas, Updates) { - SpriteAtlas atlas({ 32, 32 }, 1); - - EXPECT_EQ(1.0f, atlas.getPixelRatio()); - EXPECT_EQ(32u, atlas.getSize().width); - EXPECT_EQ(32u, atlas.getSize().height); - - atlas.addImage("one", std::make_unique<style::Image>(PremultipliedImage({ 16, 12 }), 1)); - auto one = *atlas.getIcon("one"); - float imagePixelRatio = one.relativePixelRatio * atlas.getPixelRatio(); - EXPECT_EQ(0, one.pos.x); - EXPECT_EQ(0, one.pos.y); - EXPECT_EQ(20, one.pos.w); - EXPECT_EQ(16, one.pos.h); - EXPECT_EQ(16, one.width); - EXPECT_EQ(12, one.height); - EXPECT_EQ(16u, one.width * imagePixelRatio); - EXPECT_EQ(12u, one.height * imagePixelRatio); - EXPECT_EQ(1.0f, imagePixelRatio); - - // Now the image was created lazily. - EXPECT_EQ(32u, atlas.getAtlasImage().size.width); - EXPECT_EQ(32u, atlas.getAtlasImage().size.height); - - test::checkImage("test/fixtures/sprite_atlas/updates_before", atlas.getAtlasImage()); - - // Update image - PremultipliedImage image2({ 16, 12 }); - for (size_t i = 0; i < image2.bytes(); i++) { - image2.data.get()[i] = 255; - } - atlas.addImage("one", std::make_unique<style::Image>(std::move(image2), 1)); - - test::checkImage("test/fixtures/sprite_atlas/updates_after", atlas.getAtlasImage()); -} - -TEST(SpriteAtlas, AddRemove) { - FixtureLog log; - SpriteAtlas atlas({ 32, 32 }, 1); - - atlas.addImage("one", std::make_unique<style::Image>(PremultipliedImage({ 16, 16 }), 2)); - atlas.addImage("two", std::make_unique<style::Image>(PremultipliedImage({ 16, 16 }), 2)); - atlas.addImage("three", std::make_unique<style::Image>(PremultipliedImage({ 16, 16 }), 2)); - - atlas.removeImage("one"); - atlas.removeImage("two"); - - EXPECT_NE(nullptr, atlas.getImage("three")); - EXPECT_EQ(nullptr, atlas.getImage("two")); - EXPECT_EQ(nullptr, atlas.getImage("four")); - - EXPECT_EQ(1u, log.count({ - EventSeverity::Info, - Event::Sprite, - int64_t(-1), - "Can't find sprite named 'two'", - })); - EXPECT_EQ(1u, log.count({ - EventSeverity::Info, - Event::Sprite, - int64_t(-1), - "Can't find sprite named 'four'", - })); -} - -TEST(SpriteAtlas, RemoveReleasesBinPackRect) { - FixtureLog log; - - SpriteAtlas atlas({ 36, 36 }, 1); - - atlas.addImage("big", std::make_unique<style::Image>(PremultipliedImage({ 32, 32 }), 1)); - EXPECT_TRUE(atlas.getIcon("big")); - - atlas.removeImage("big"); - - atlas.addImage("big", std::make_unique<style::Image>(PremultipliedImage({ 32, 32 }), 1)); - EXPECT_TRUE(atlas.getIcon("big")); - EXPECT_TRUE(log.empty()); -} - -TEST(SpriteAtlas, OtherPixelRatio) { - FixtureLog log; - SpriteAtlas atlas({ 32, 32 }, 1); - - // Adding mismatched sprite image - atlas.addImage("one", std::make_unique<style::Image>(PremultipliedImage({ 8, 8 }), 2)); -} - -TEST(SpriteAtlas, Replace) { - FixtureLog log; - SpriteAtlas atlas({ 32, 32 }, 1); - - atlas.addImage("sprite", std::make_unique<style::Image>(PremultipliedImage({ 16, 16 }), 2)); - auto image = atlas.getImage("sprite"); - atlas.addImage("sprite", std::make_unique<style::Image>(PremultipliedImage({ 16, 16 }), 2)); - EXPECT_NE(image, atlas.getImage("sprite")); -} - -TEST(SpriteAtlas, ReplaceWithDifferentDimensions) { - FixtureLog log; - SpriteAtlas atlas({ 32, 32 }, 1); - - atlas.addImage("sprite", std::make_unique<style::Image>(PremultipliedImage({ 16, 16 }), 2)); - atlas.addImage("sprite", std::make_unique<style::Image>(PremultipliedImage({ 18, 18 }), 2)); - - EXPECT_EQ(1u, log.count({ - EventSeverity::Warning, - Event::Sprite, - int64_t(-1), - "Can't change sprite dimensions for 'sprite'", - })); -} - -class SpriteAtlasTest { -public: - SpriteAtlasTest() = default; - - util::RunLoop loop; - StubFileSource fileSource; - StubStyleObserver observer; - ThreadPool threadPool { 1 }; - SpriteAtlas spriteAtlas{ { 32, 32 }, 1 }; - - void run() { - // Squelch logging. - Log::setObserver(std::make_unique<Log::NullObserver>()); - - spriteAtlas.setObserver(&observer); - spriteAtlas.load("test/fixtures/resources/sprite", threadPool, fileSource); - - loop.run(); - } - - void end() { - loop.stop(); - } -}; - -Response successfulSpriteImageResponse(const Resource& resource) { - EXPECT_EQ("test/fixtures/resources/sprite.png", resource.url); - Response response; - response.data = std::make_unique<std::string>(util::read_file(resource.url)); - return response; -} - -Response successfulSpriteJSONResponse(const Resource& resource) { - EXPECT_EQ("test/fixtures/resources/sprite.json", resource.url); - Response response; - response.data = std::make_unique<std::string>(util::read_file(resource.url)); - return response; -} - -Response failedSpriteResponse(const Resource&) { - Response response; - response.error = std::make_unique<Response::Error>( - Response::Error::Reason::Other, - "Failed by the test case"); - return response; -} - -Response corruptSpriteResponse(const Resource&) { - Response response; - response.data = std::make_unique<std::string>("CORRUPT"); - return response; -} - -TEST(SpriteAtlas, LoadingSuccess) { - SpriteAtlasTest test; - - test.fileSource.spriteImageResponse = successfulSpriteImageResponse; - test.fileSource.spriteJSONResponse = successfulSpriteJSONResponse; - - test.observer.spriteError = [&] (std::exception_ptr error) { - FAIL() << util::toString(error); - test.end(); - }; - - test.observer.spriteLoaded = [&] () { - EXPECT_EQ(1.0, test.spriteAtlas.getPixelRatio()); - EXPECT_TRUE(test.spriteAtlas.isLoaded()); - test.end(); - }; - - test.run(); -} - -TEST(SpriteAtlas, JSONLoadingFail) { - SpriteAtlasTest test; - - test.fileSource.spriteImageResponse = successfulSpriteImageResponse; - test.fileSource.spriteJSONResponse = failedSpriteResponse; - - test.observer.spriteError = [&] (std::exception_ptr error) { - EXPECT_TRUE(error != nullptr); - EXPECT_EQ("Failed by the test case", util::toString(error)); - EXPECT_FALSE(test.spriteAtlas.isLoaded()); - test.end(); - }; - - test.run(); -} - -TEST(SpriteAtlas, ImageLoadingFail) { - SpriteAtlasTest test; - - test.fileSource.spriteImageResponse = failedSpriteResponse; - test.fileSource.spriteJSONResponse = successfulSpriteJSONResponse; - - test.observer.spriteError = [&] (std::exception_ptr error) { - EXPECT_TRUE(error != nullptr); - EXPECT_EQ("Failed by the test case", util::toString(error)); - EXPECT_FALSE(test.spriteAtlas.isLoaded()); - test.end(); - }; - - test.run(); -} - -TEST(SpriteAtlas, JSONLoadingCorrupted) { - SpriteAtlasTest test; - - test.fileSource.spriteImageResponse = successfulSpriteImageResponse; - test.fileSource.spriteJSONResponse = corruptSpriteResponse; - - test.observer.spriteError = [&] (std::exception_ptr error) { - EXPECT_TRUE(error != nullptr); - EXPECT_EQ("Failed to parse JSON: Invalid value. at offset 0", util::toString(error)); - EXPECT_FALSE(test.spriteAtlas.isLoaded()); - test.end(); - }; - - test.run(); -} - -TEST(SpriteAtlas, ImageLoadingCorrupted) { - SpriteAtlasTest test; - - test.fileSource.spriteImageResponse = corruptSpriteResponse; - test.fileSource.spriteJSONResponse = successfulSpriteJSONResponse; - - test.observer.spriteError = [&] (std::exception_ptr error) { - EXPECT_TRUE(error != nullptr); - // Not asserting on platform-specific error text. - EXPECT_FALSE(test.spriteAtlas.isLoaded()); - test.end(); - }; - - test.run(); -} - -TEST(SpriteAtlas, LoadingCancel) { - SpriteAtlasTest test; - - test.fileSource.spriteImageResponse = - test.fileSource.spriteJSONResponse = [&] (const Resource&) { - test.end(); - return optional<Response>(); - }; - - test.observer.spriteLoaded = [&] () { - FAIL() << "Should never be called"; - }; - - test.run(); -} diff --git a/test/sprite/sprite_loader.test.cpp b/test/sprite/sprite_loader.test.cpp new file mode 100644 index 0000000000..3691572265 --- /dev/null +++ b/test/sprite/sprite_loader.test.cpp @@ -0,0 +1,179 @@ +#include <mbgl/test/util.hpp> +#include <mbgl/test/fixture_log_observer.hpp> +#include <mbgl/test/stub_file_source.hpp> + +#include <mbgl/sprite/sprite_loader.hpp> +#include <mbgl/sprite/sprite_loader_observer.hpp> +#include <mbgl/sprite/sprite_parser.hpp> +#include <mbgl/util/io.hpp> +#include <mbgl/util/image.hpp> +#include <mbgl/util/run_loop.hpp> +#include <mbgl/util/default_thread_pool.hpp> +#include <mbgl/util/string.hpp> + +#include <utility> + +using namespace mbgl; +using namespace mbgl::style; + +class StubSpriteLoaderObserver : public SpriteLoaderObserver { +public: + void onSpriteLoaded(std::vector<std::unique_ptr<style::Image>>&& images) override { + if (spriteLoaded) spriteLoaded(std::move(images)); + } + + void onSpriteError(std::exception_ptr error) override { + if (spriteError) spriteError(error); + } + + std::function<void (std::vector<std::unique_ptr<style::Image>>&&)> spriteLoaded; + std::function<void (std::exception_ptr)> spriteError; +}; + +class SpriteLoaderTest { +public: + SpriteLoaderTest() = default; + + util::RunLoop loop; + StubFileSource fileSource; + StubSpriteLoaderObserver observer; + ThreadPool threadPool { 1 }; + SpriteLoader spriteLoader{ 1 }; + + void run() { + // Squelch logging. + Log::setObserver(std::make_unique<Log::NullObserver>()); + + spriteLoader.setObserver(&observer); + spriteLoader.load("test/fixtures/resources/sprite", threadPool, fileSource); + + loop.run(); + } + + void end() { + loop.stop(); + } +}; + +Response successfulSpriteImageResponse(const Resource& resource) { + EXPECT_EQ("test/fixtures/resources/sprite.png", resource.url); + Response response; + response.data = std::make_unique<std::string>(util::read_file(resource.url)); + return response; +} + +Response successfulSpriteJSONResponse(const Resource& resource) { + EXPECT_EQ("test/fixtures/resources/sprite.json", resource.url); + Response response; + response.data = std::make_unique<std::string>(util::read_file(resource.url)); + return response; +} + +Response failedSpriteResponse(const Resource&) { + Response response; + response.error = std::make_unique<Response::Error>( + Response::Error::Reason::Other, + "Failed by the test case"); + return response; +} + +Response corruptSpriteResponse(const Resource&) { + Response response; + response.data = std::make_unique<std::string>("CORRUPT"); + return response; +} + +TEST(SpriteLoader, LoadingSuccess) { + SpriteLoaderTest test; + + test.fileSource.spriteImageResponse = successfulSpriteImageResponse; + test.fileSource.spriteJSONResponse = successfulSpriteJSONResponse; + + test.observer.spriteError = [&] (std::exception_ptr error) { + FAIL() << util::toString(error); + test.end(); + }; + + test.observer.spriteLoaded = [&] (std::vector<std::unique_ptr<style::Image>>&& images) { + EXPECT_EQ(images.size(), 367u); + test.end(); + }; + + test.run(); +} + +TEST(SpriteLoader, JSONLoadingFail) { + SpriteLoaderTest test; + + test.fileSource.spriteImageResponse = successfulSpriteImageResponse; + test.fileSource.spriteJSONResponse = failedSpriteResponse; + + test.observer.spriteError = [&] (std::exception_ptr error) { + EXPECT_TRUE(error != nullptr); + EXPECT_EQ("Failed by the test case", util::toString(error)); + test.end(); + }; + + test.run(); +} + +TEST(SpriteLoader, ImageLoadingFail) { + SpriteLoaderTest test; + + test.fileSource.spriteImageResponse = failedSpriteResponse; + test.fileSource.spriteJSONResponse = successfulSpriteJSONResponse; + + test.observer.spriteError = [&] (std::exception_ptr error) { + EXPECT_TRUE(error != nullptr); + EXPECT_EQ("Failed by the test case", util::toString(error)); + test.end(); + }; + + test.run(); +} + +TEST(SpriteLoader, JSONLoadingCorrupted) { + SpriteLoaderTest test; + + test.fileSource.spriteImageResponse = successfulSpriteImageResponse; + test.fileSource.spriteJSONResponse = corruptSpriteResponse; + + test.observer.spriteError = [&] (std::exception_ptr error) { + EXPECT_TRUE(error != nullptr); + EXPECT_EQ("Failed to parse JSON: Invalid value. at offset 0", util::toString(error)); + test.end(); + }; + + test.run(); +} + +TEST(SpriteLoader, ImageLoadingCorrupted) { + SpriteLoaderTest test; + + test.fileSource.spriteImageResponse = corruptSpriteResponse; + test.fileSource.spriteJSONResponse = successfulSpriteJSONResponse; + + test.observer.spriteError = [&] (std::exception_ptr error) { + EXPECT_TRUE(error != nullptr); + // Not asserting on platform-specific error text. + test.end(); + }; + + test.run(); +} + +TEST(SpriteLoader, LoadingCancel) { + SpriteLoaderTest test; + + test.fileSource.spriteImageResponse = + test.fileSource.spriteJSONResponse = [&] (const Resource&) { + test.end(); + return optional<Response>(); + }; + + test.observer.spriteLoaded = [&] (const std::vector<std::unique_ptr<style::Image>>&) { + FAIL() << "Should never be called"; + }; + + test.run(); +} diff --git a/test/sprite/sprite_parser.test.cpp b/test/sprite/sprite_parser.test.cpp index bb8e71db95..529e4c75e8 100644 --- a/test/sprite/sprite_parser.test.cpp +++ b/test/sprite/sprite_parser.test.cpp @@ -27,19 +27,19 @@ TEST(Sprite, SpriteImageCreationInvalid) { ASSERT_EQ(200u, image_1x.size.width); ASSERT_EQ(299u, image_1x.size.height); - ASSERT_EQ(nullptr, createStyleImage(image_1x, 0, 0, 0, 16, 1, false)); // width == 0 - ASSERT_EQ(nullptr, createStyleImage(image_1x, 0, 0, 16, 0, 1, false)); // height == 0 - ASSERT_EQ(nullptr, createStyleImage(image_1x, 0, 0, -1, 16, 1, false)); // width < 0 - ASSERT_EQ(nullptr, createStyleImage(image_1x, 0, 0, 16, -1, 1, false)); // height < 0 - ASSERT_EQ(nullptr, createStyleImage(image_1x, 0, 0, 1, 1, 0, false)); // ratio == 0 - ASSERT_EQ(nullptr, createStyleImage(image_1x, 0, 0, 1, 1, -1, false)); // ratio < 0 - ASSERT_EQ(nullptr, createStyleImage(image_1x, 0, 0, 1, 1, 23, false)); // ratio too large - ASSERT_EQ(nullptr, createStyleImage(image_1x, 0, 0, 2048, 16, 1, false)); // too wide - ASSERT_EQ(nullptr, createStyleImage(image_1x, 0, 0, 16, 1025, 1, false)); // too tall - ASSERT_EQ(nullptr, createStyleImage(image_1x, -1, 0, 16, 16, 1, false)); // srcX < 0 - ASSERT_EQ(nullptr, createStyleImage(image_1x, 0, -1, 16, 16, 1, false)); // srcY < 0 - ASSERT_EQ(nullptr, createStyleImage(image_1x, 0, 0, image_1x.size.width + 1, 16, 1, false)); // right edge out of bounds - ASSERT_EQ(nullptr, createStyleImage(image_1x, 0, 0, 16, image_1x.size.height + 1, 1, false)); // bottom edge out of bounds + ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, 0, 0, 16, 1, false)); // width == 0 + ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, 0, 16, 0, 1, false)); // height == 0 + ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, 0, -1, 16, 1, false)); // width < 0 + ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, 0, 16, -1, 1, false)); // height < 0 + ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, 0, 1, 1, 0, false)); // ratio == 0 + ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, 0, 1, 1, -1, false)); // ratio < 0 + ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, 0, 1, 1, 23, false)); // ratio too large + ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, 0, 2048, 16, 1, false)); // too wide + ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, 0, 16, 1025, 1, false)); // too tall + ASSERT_EQ(nullptr, createStyleImage("test", image_1x, -1, 0, 16, 16, 1, false)); // srcX < 0 + ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, -1, 16, 16, 1, false)); // srcY < 0 + ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, 0, image_1x.size.width + 1, 16, 1, false)); // right edge out of bounds + ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, 0, 16, image_1x.size.height + 1, 1, false)); // bottom edge out of bounds EXPECT_EQ(1u, log.count({ EventSeverity::Error, @@ -141,15 +141,13 @@ TEST(Sprite, SpriteImageCreation1x) { ASSERT_EQ(299u, image_1x.size.height); { // "museum_icon":{"x":177,"y":187,"width":18,"height":18,"pixelRatio":1,"sdf":false} - const auto sprite = createStyleImage(image_1x, 177, 187, 18, 18, 1, false); + const auto sprite = createStyleImage("test", image_1x, 177, 187, 18, 18, 1, false); ASSERT_TRUE(sprite.get()); - EXPECT_EQ(18, sprite->getWidth()); - EXPECT_EQ(18, sprite->getHeight()); - EXPECT_EQ(18u, sprite->image.size.width); - EXPECT_EQ(18u, sprite->image.size.height); - EXPECT_EQ(1, sprite->pixelRatio); + EXPECT_EQ(18u, sprite->getImage().size.width); + EXPECT_EQ(18u, sprite->getImage().size.height); + EXPECT_EQ(1, sprite->getPixelRatio()); EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteimagecreation1x-museum.png"), - sprite->image); + sprite->getImage()); } } @@ -157,41 +155,35 @@ TEST(Sprite, SpriteImageCreation2x) { const PremultipliedImage image_2x = decodeImage(util::read_file("test/fixtures/annotations/emerald@2x.png")); // "museum_icon":{"x":354,"y":374,"width":36,"height":36,"pixelRatio":2,"sdf":false} - const auto sprite = createStyleImage(image_2x, 354, 374, 36, 36, 2, false); + const auto sprite = createStyleImage("test", image_2x, 354, 374, 36, 36, 2, false); ASSERT_TRUE(sprite.get()); - EXPECT_EQ(18, sprite->getWidth()); - EXPECT_EQ(18, sprite->getHeight()); - EXPECT_EQ(36u, sprite->image.size.width); - EXPECT_EQ(36u, sprite->image.size.height); - EXPECT_EQ(2, sprite->pixelRatio); + EXPECT_EQ(36u, sprite->getImage().size.width); + EXPECT_EQ(36u, sprite->getImage().size.height); + EXPECT_EQ(2, sprite->getPixelRatio()); EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteimagecreation2x.png"), - sprite->image); + sprite->getImage()); } TEST(Sprite, SpriteImageCreation1_5x) { const PremultipliedImage image_2x = decodeImage(util::read_file("test/fixtures/annotations/emerald@2x.png")); // "museum_icon":{"x":354,"y":374,"width":36,"height":36,"pixelRatio":2,"sdf":false} - const auto sprite = createStyleImage(image_2x, 354, 374, 36, 36, 1.5, false); + const auto sprite = createStyleImage("test", image_2x, 354, 374, 36, 36, 1.5, false); ASSERT_TRUE(sprite.get()); - EXPECT_EQ(24, sprite->getWidth()); - EXPECT_EQ(24, sprite->getHeight()); - EXPECT_EQ(36u, sprite->image.size.width); - EXPECT_EQ(36u, sprite->image.size.height); - EXPECT_EQ(1.5, sprite->pixelRatio); + EXPECT_EQ(36u, sprite->getImage().size.width); + EXPECT_EQ(36u, sprite->getImage().size.height); + EXPECT_EQ(1.5, sprite->getPixelRatio()); EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteimagecreation1_5x-museum.png"), - sprite->image); + sprite->getImage()); // "hospital_icon":{"x":314,"y":518,"width":36,"height":36,"pixelRatio":2,"sdf":false} - const auto sprite2 = createStyleImage(image_2x, 314, 518, 35, 35, 1.5, false); + const auto sprite2 = createStyleImage("test", image_2x, 314, 518, 35, 35, 1.5, false); ASSERT_TRUE(sprite2.get()); - EXPECT_EQ(float(35 / 1.5), sprite2->getWidth()); - EXPECT_EQ(float(35 / 1.5), sprite2->getHeight()); - EXPECT_EQ(35u, sprite2->image.size.width); - EXPECT_EQ(35u, sprite2->image.size.height); - EXPECT_EQ(1.5, sprite2->pixelRatio); + EXPECT_EQ(35u, sprite2->getImage().size.width); + EXPECT_EQ(35u, sprite2->getImage().size.height); + EXPECT_EQ(1.5, sprite2->getPixelRatio()); EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteimagecreation1_5x-hospital.png"), - sprite2->image); + sprite2->getImage()); } TEST(Sprite, SpriteParsing) { @@ -202,7 +194,7 @@ TEST(Sprite, SpriteParsing) { std::set<std::string> names; std::transform(images.begin(), images.end(), std::inserter(names, names.begin()), - [](const auto& pair) { return pair.first; }); + [](const auto& image) { return image->getID(); }); EXPECT_EQ(std::set<std::string>({ "airfield_icon", "airport_icon", @@ -280,13 +272,11 @@ TEST(Sprite, SpriteParsing) { names); { - auto& sprite = images.find("generic-metro")->second; - EXPECT_EQ(18, sprite->getWidth()); - EXPECT_EQ(18, sprite->getHeight()); - EXPECT_EQ(18u, sprite->image.size.width); - EXPECT_EQ(18u, sprite->image.size.height); - EXPECT_EQ(1, sprite->pixelRatio); - EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteparsing.png"), sprite->image); + auto& sprite = *std::find_if(images.begin(), images.end(), [] (const auto& image) { return image->getID() == "generic-metro"; }); + EXPECT_EQ(18u, sprite->getImage().size.width); + EXPECT_EQ(18u, sprite->getImage().size.height); + EXPECT_EQ(1, sprite->getPixelRatio()); + EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteparsing.png"), sprite->getImage()); } } diff --git a/test/src/mbgl/test/conversion_stubs.hpp b/test/src/mbgl/test/conversion_stubs.hpp index e6581c5e53..30395ddb97 100644 --- a/test/src/mbgl/test/conversion_stubs.hpp +++ b/test/src/mbgl/test/conversion_stubs.hpp @@ -17,6 +17,7 @@ using ValueMap = std::unordered_map<std::string, Value>; using ValueVector = std::vector<Value>; class Value : public mbgl::variant<std::string, float, + double, bool, mapbox::util::recursive_wrapper<ValueMap>, mapbox::util::recursive_wrapper<ValueVector>> { @@ -90,6 +91,14 @@ inline optional<float> toNumber(const Value& value) { return {}; } + +inline optional<double> toDouble(const Value& value) { + if (value.is<double>()) { + return value.get<double>(); + } + return {}; +} + inline optional<std::string> toString(const Value& value) { if (value.is<std::string>()) { return value.get<std::string>(); diff --git a/test/src/mbgl/test/fixture_log_observer.cpp b/test/src/mbgl/test/fixture_log_observer.cpp index fc0239bb1c..717d2da753 100644 --- a/test/src/mbgl/test/fixture_log_observer.cpp +++ b/test/src/mbgl/test/fixture_log_observer.cpp @@ -15,9 +15,6 @@ bool FixtureLog::Message::operator==(const Message& rhs) const { return severity == rhs.severity && event == rhs.event && code == rhs.code && msg == rhs.msg; } -FixtureLog::Message::Message() : severity(), event(), code(), msg() { -} - FixtureLog::Observer::Observer(FixtureLog* log_) : log(log_) { } @@ -97,10 +94,10 @@ std::vector<FixtureLog::Message> FixtureLogObserver::unchecked() const { } ::std::ostream& operator<<(::std::ostream& os, const FixtureLog::Message& message) { - os << "[\"" << Enum<EventSeverity>::toString(message.severity) << "\", \""; - os << Enum<Event>::toString(message.event) << "\""; + os << R"([")" << Enum<EventSeverity>::toString(message.severity) << R"(", ")"; + os << Enum<Event>::toString(message.event) << R"(")"; os << ", " << message.code; - os << ", \"" << message.msg << "\""; + os << R"(, ")" << message.msg << R"(")"; return os << "]" << std::endl; } diff --git a/test/src/mbgl/test/fixture_log_observer.hpp b/test/src/mbgl/test/fixture_log_observer.hpp index 96ddc2c54f..328d4753a8 100644 --- a/test/src/mbgl/test/fixture_log_observer.hpp +++ b/test/src/mbgl/test/fixture_log_observer.hpp @@ -12,15 +12,15 @@ namespace mbgl { class FixtureLog { public: struct Message { + Message() = default; Message(EventSeverity severity_, Event event_, int64_t code_, std::string msg_); - Message(); bool operator==(const Message& rhs) const; - const EventSeverity severity; - const Event event; - const int64_t code; - const std::string msg; + const EventSeverity severity {}; + const Event event {}; + const int64_t code {}; + const std::string msg {}; mutable bool checked = false; }; diff --git a/test/src/mbgl/test/getrss.cpp b/test/src/mbgl/test/getrss.cpp index 9f57ad8e7b..c21b653eaa 100644 --- a/test/src/mbgl/test/getrss.cpp +++ b/test/src/mbgl/test/getrss.cpp @@ -80,8 +80,8 @@ size_t getCurrentRSS( ) #elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__) /* Linux ---------------------------------------------------- */ long rss = 0L; - FILE* fp = NULL; - if ( (fp = fopen( "/proc/self/statm", "r" )) == NULL ) + FILE* fp = nullptr; + if ( (fp = fopen( "/proc/self/statm", "r" )) == nullptr ) return (size_t)0L; /* Can't open? */ if ( fscanf( fp, "%*s%ld", &rss ) != 1 ) { diff --git a/test/src/mbgl/test/getrss.hpp b/test/src/mbgl/test/getrss.hpp index a4420c4b5f..be45ae889a 100644 --- a/test/src/mbgl/test/getrss.hpp +++ b/test/src/mbgl/test/getrss.hpp @@ -41,5 +41,5 @@ size_t getPeakRSS(); */ size_t getCurrentRSS(); -} -} +} // namespace test +} // namespace mbgl diff --git a/test/src/mbgl/test/stub_file_source.cpp b/test/src/mbgl/test/stub_file_source.cpp index ec0545e88c..7891d5d907 100644 --- a/test/src/mbgl/test/stub_file_source.cpp +++ b/test/src/mbgl/test/stub_file_source.cpp @@ -77,6 +77,9 @@ optional<Response> StubFileSource::defaultResponse(const Resource& resource) { case Resource::Kind::SpriteImage: if (!spriteImageResponse) throw std::runtime_error("unexpected sprite image request"); return spriteImageResponse(resource); + case Resource::Kind::Image: + if (!imageResponse) throw std::runtime_error("unexpected image request"); + return imageResponse(resource); case Resource::Kind::Unknown: throw std::runtime_error("unknown resource type"); } diff --git a/test/src/mbgl/test/stub_file_source.hpp b/test/src/mbgl/test/stub_file_source.hpp index ee4175cc3f..85118e1a77 100644 --- a/test/src/mbgl/test/stub_file_source.hpp +++ b/test/src/mbgl/test/stub_file_source.hpp @@ -29,6 +29,7 @@ public: ResponseFunction glyphsResponse; ResponseFunction spriteJSONResponse; ResponseFunction spriteImageResponse; + ResponseFunction imageResponse; private: // The default behavior is to throw if no per-kind callback has been set. diff --git a/test/src/mbgl/test/stub_layer_observer.hpp b/test/src/mbgl/test/stub_layer_observer.hpp index 9acd4b077a..0fa413aefe 100644 --- a/test/src/mbgl/test/stub_layer_observer.hpp +++ b/test/src/mbgl/test/stub_layer_observer.hpp @@ -10,29 +10,9 @@ using namespace mbgl::style; */ class StubLayerObserver : public style::LayerObserver { public: - void onLayerFilterChanged(Layer& layer) override { - if (layerFilterChanged) layerFilterChanged(layer); + void onLayerChanged(Layer& layer) override { + if (layerChanged) layerChanged(layer); } - void onLayerVisibilityChanged(Layer& layer) override { - if (layerVisibilityChanged) layerVisibilityChanged(layer); - } - - void onLayerPaintPropertyChanged(Layer& layer) override { - if (layerPaintPropertyChanged) layerPaintPropertyChanged(layer); - } - - void onLayerDataDrivenPaintPropertyChanged(Layer& layer) override { - if (layerDataDrivenPaintPropertyChanged) layerDataDrivenPaintPropertyChanged(layer); - } - - void onLayerLayoutPropertyChanged(Layer& layer, const char * property) override { - if (layerLayoutPropertyChanged) layerLayoutPropertyChanged(layer, property); - } - - std::function<void (Layer&)> layerFilterChanged; - std::function<void (Layer&)> layerVisibilityChanged; - std::function<void (Layer&)> layerPaintPropertyChanged; - std::function<void (Layer&)> layerDataDrivenPaintPropertyChanged; - std::function<void (Layer&, const char *)> layerLayoutPropertyChanged; + std::function<void (Layer&)> layerChanged; }; diff --git a/test/src/mbgl/test/stub_style_observer.hpp b/test/src/mbgl/test/stub_style_observer.hpp index 7e22c68823..b97911cdb0 100644 --- a/test/src/mbgl/test/stub_style_observer.hpp +++ b/test/src/mbgl/test/stub_style_observer.hpp @@ -10,22 +10,6 @@ using namespace mbgl::style; */ class StubStyleObserver : public style::Observer { public: - void onGlyphsLoaded(const FontStack& fontStack, const GlyphRange& glyphRange) override { - if (glyphsLoaded) glyphsLoaded(fontStack, glyphRange); - } - - void onGlyphsError(const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) override { - if (glyphsError) glyphsError(fontStack, glyphRange, error); - } - - void onSpriteLoaded() override { - if (spriteLoaded) spriteLoaded(); - } - - void onSpriteError(std::exception_ptr error) override { - if (spriteError) spriteError(error); - } - void onSourceLoaded(Source& source) override { if (sourceLoaded) sourceLoaded(source); } @@ -46,10 +30,6 @@ public: if (resourceError) resourceError(error); }; - std::function<void (const FontStack&, const GlyphRange&)> glyphsLoaded; - std::function<void (const FontStack&, const GlyphRange&, std::exception_ptr)> glyphsError; - std::function<void ()> spriteLoaded; - std::function<void (std::exception_ptr)> spriteError; std::function<void (Source&)> sourceLoaded; std::function<void (Source&)> sourceChanged; std::function<void (Source&, std::exception_ptr)> sourceError; diff --git a/test/storage/asset_file_source.test.cpp b/test/storage/asset_file_source.test.cpp index 010a2c9dc7..a39d2963d2 100644 --- a/test/storage/asset_file_source.test.cpp +++ b/test/storage/asset_file_source.test.cpp @@ -3,8 +3,10 @@ #include <mbgl/util/chrono.hpp> #include <mbgl/util/run_loop.hpp> #include <mbgl/util/thread.hpp> +#include <mbgl/actor/actor_ref.hpp> #include <gtest/gtest.h> +#include <atomic> using namespace mbgl; @@ -20,16 +22,11 @@ TEST(AssetFileSource, Load) { #else unsigned numThreads = 50; #endif - - auto callback = [&] { - if (!--numThreads) { - loop.stop(); - } - }; + std::atomic_uint completed(numThreads); class TestWorker { public: - TestWorker(mbgl::AssetFileSource* fs_) : fs(fs_) {} + TestWorker(ActorRef<TestWorker>, mbgl::AssetFileSource* fs_) : fs(fs_) {} void run(std::function<void()> endCallback) { const std::string asset("asset://nonempty"); @@ -60,16 +57,12 @@ TEST(AssetFileSource, Load) { }; std::vector<std::unique_ptr<util::Thread<TestWorker>>> threads; - std::vector<std::unique_ptr<mbgl::AsyncRequest>> requests; - util::ThreadContext context = { "Test" }; for (unsigned i = 0; i < numThreads; ++i) { std::unique_ptr<util::Thread<TestWorker>> thread = - std::make_unique<util::Thread<TestWorker>>(context, &fs); - - requests.push_back( - thread->invokeWithCallback(&TestWorker::run, callback)); + std::make_unique<util::Thread<TestWorker>>("Test", &fs); + thread->actor().invoke(&TestWorker::run, [&] { if (!--completed) loop.stop(); }); threads.push_back(std::move(thread)); } diff --git a/test/storage/default_file_source.test.cpp b/test/storage/default_file_source.test.cpp index 03f1076559..41e305a692 100644 --- a/test/storage/default_file_source.test.cpp +++ b/test/storage/default_file_source.test.cpp @@ -1,5 +1,7 @@ +#include <mbgl/actor/actor.hpp> #include <mbgl/test/util.hpp> #include <mbgl/storage/default_file_source.hpp> +#include <mbgl/storage/resource_transform.hpp> #include <mbgl/util/run_loop.hpp> using namespace mbgl; @@ -482,7 +484,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(SetResourceTransform)) { DefaultFileSource fs(":memory:", "."); // Translates the URL "localhost://test to http://127.0.0.1:3000/test - fs.setResourceTransform([](Resource::Kind, std::string&& url) -> std::string { + Actor<ResourceTransform> transform(loop, [](Resource::Kind, const std::string&& url) -> std::string { if (url == "localhost://test") { return "http://127.0.0.1:3000/test"; } else { @@ -490,10 +492,27 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(SetResourceTransform)) { } }); - const Resource resource { Resource::Unknown, "localhost://test" }; + fs.setResourceTransform(transform.self()); + const Resource resource1 { Resource::Unknown, "localhost://test" }; std::unique_ptr<AsyncRequest> req; - req = fs.request(resource, [&](Response res) { + req = fs.request(resource1, [&](Response res) { + req.reset(); + EXPECT_EQ(nullptr, res.error); + ASSERT_TRUE(res.data.get()); + EXPECT_EQ("Hello World!", *res.data); + EXPECT_FALSE(bool(res.expires)); + EXPECT_FALSE(bool(res.modified)); + EXPECT_FALSE(bool(res.etag)); + loop.stop(); + }); + + loop.run(); + + fs.setResourceTransform({}); + const Resource resource2 { Resource::Unknown, "http://127.0.0.1:3000/test" }; + + req = fs.request(resource2, [&](Response res) { req.reset(); EXPECT_EQ(nullptr, res.error); ASSERT_TRUE(res.data.get()); diff --git a/test/storage/local_file_source.test.cpp b/test/storage/local_file_source.test.cpp index 1b90e5bb1e..4d509e6c7d 100644 --- a/test/storage/local_file_source.test.cpp +++ b/test/storage/local_file_source.test.cpp @@ -3,7 +3,7 @@ #include <mbgl/util/run_loop.hpp> #include <unistd.h> -#include <limits.h> +#include <climits> #include <gtest/gtest.h> namespace { diff --git a/test/storage/offline_download.test.cpp b/test/storage/offline_download.test.cpp index 27e57771c8..57780eba40 100644 --- a/test/storage/offline_download.test.cpp +++ b/test/storage/offline_download.test.cpp @@ -191,6 +191,11 @@ TEST(OfflineDownload, Activate) { return test.response("sprite.png"); }; + test.fileSource.imageResponse = [&] (const Resource& resource) { + EXPECT_EQ("http://127.0.0.1:3000/radar.gif", resource.url); + return test.response("radar.gif"); + }; + test.fileSource.spriteJSONResponse = [&] (const Resource& resource) { EXPECT_EQ("http://127.0.0.1:3000/sprite.json", resource.url); return test.response("sprite.json"); @@ -219,7 +224,7 @@ TEST(OfflineDownload, Activate) { observer->statusChangedFn = [&] (OfflineRegionStatus status) { if (status.complete()) { - EXPECT_EQ(261u, status.completedResourceCount); // 256 glyphs, 1 tile, 1 style, source, sprite image, and sprite json + EXPECT_EQ(262u, status.completedResourceCount); // 256 glyphs, 1 tile, 1 style, source, image, sprite image, and sprite json EXPECT_EQ(test.size, status.completedResourceSize); download.setState(OfflineRegionDownloadState::Inactive); @@ -299,7 +304,7 @@ TEST(OfflineDownload, GetStatusStyleComplete) { EXPECT_EQ(OfflineRegionDownloadState::Inactive, status.downloadState); EXPECT_EQ(1u, status.completedResourceCount); EXPECT_EQ(test.size, status.completedResourceSize); - EXPECT_EQ(260u, status.requiredResourceCount); + EXPECT_EQ(261u, status.requiredResourceCount); EXPECT_FALSE(status.requiredResourceCountIsPrecise); EXPECT_FALSE(status.complete()); } @@ -325,7 +330,7 @@ TEST(OfflineDownload, GetStatusStyleAndSourceComplete) { EXPECT_EQ(OfflineRegionDownloadState::Inactive, status.downloadState); EXPECT_EQ(2u, status.completedResourceCount); EXPECT_EQ(test.size, status.completedResourceSize); - EXPECT_EQ(261u, status.requiredResourceCount); + EXPECT_EQ(262u, status.requiredResourceCount); EXPECT_TRUE(status.requiredResourceCountIsPrecise); EXPECT_FALSE(status.complete()); } diff --git a/test/storage/resource.test.cpp b/test/storage/resource.test.cpp index 1c15fe6503..5a27aa98a5 100644 --- a/test/storage/resource.test.cpp +++ b/test/storage/resource.test.cpp @@ -117,6 +117,13 @@ TEST(Resource, SpriteImage) { EXPECT_EQ("http://example.com/sprite@2x.png", resource.url); } +TEST(Resource, Image) { + using namespace mbgl; + Resource resource = Resource::image("http://example.com/sprite.jpg"); + EXPECT_EQ(Resource::Kind::Image, resource.kind); + EXPECT_EQ("http://example.com/sprite.jpg", resource.url); +} + TEST(Resource, SpriteJSON) { using namespace mbgl; Resource resource = Resource::spriteJSON("http://example.com/sprite", 2.0); diff --git a/test/style/conversion/function.test.cpp b/test/style/conversion/function.test.cpp index 08637d40cb..1eff94d939 100644 --- a/test/style/conversion/function.test.cpp +++ b/test/style/conversion/function.test.cpp @@ -19,26 +19,26 @@ TEST(StyleConversion, Function) { return convert<CameraFunction<float>, JSValue>(doc, error); }; - auto fn1 = parseFunction("{\"stops\":[]}"); + auto fn1 = parseFunction(R"({"stops":[]})"); ASSERT_FALSE(fn1); ASSERT_EQ("function must have at least one stop", error.message); - auto fn2 = parseFunction("{\"stops\":[1]}"); + auto fn2 = parseFunction(R"({"stops":[1]})"); ASSERT_FALSE(fn2); ASSERT_EQ("function stop must be an array", error.message); - auto fn3 = parseFunction("{\"stops\":[[]]}"); + auto fn3 = parseFunction(R"({"stops":[[]]})"); ASSERT_FALSE(fn3); ASSERT_EQ("function stop must have two elements", error.message); - auto fn4 = parseFunction("{\"stops\":[[-1,-1]]}"); + auto fn4 = parseFunction(R"({"stops":[[-1,-1]]})"); ASSERT_TRUE(bool(fn4)); - auto fn5 = parseFunction("{\"stops\":[[0,1,2]]}"); + auto fn5 = parseFunction(R"({"stops":[[0,1,2]]})"); ASSERT_FALSE(fn5); ASSERT_EQ("function stop must have two elements", error.message); - auto fn6 = parseFunction("{\"stops\":[[0,\"x\"]]}"); + auto fn6 = parseFunction(R"({"stops":[[0,"x"]]})"); ASSERT_FALSE(fn6); ASSERT_EQ("value must be a number", error.message); @@ -50,7 +50,7 @@ TEST(StyleConversion, Function) { ASSERT_FALSE(fn8); ASSERT_EQ("function must be an object", error.message); - auto fn9 = parseFunction("{\"stops\":[[0,0]],\"base\":false}"); + auto fn9 = parseFunction(R"({"stops":[[0,0]],"base":false})"); ASSERT_FALSE(fn9); ASSERT_EQ("function base must be a number", error.message); } diff --git a/test/style/conversion/layer.test.cpp b/test/style/conversion/layer.test.cpp index ae8d4058ab..d51d7d33e2 100644 --- a/test/style/conversion/layer.test.cpp +++ b/test/style/conversion/layer.test.cpp @@ -27,21 +27,11 @@ TEST(StyleConversion, LayerTransition) { "duration": 400, "delay": 500 } - }, - "paint.class": { - "background-color-transition": { - "duration": 100 - } } })JSON"); - ASSERT_EQ(400ms, *layer->as<BackgroundLayer>()->impl->cascading - .get<BackgroundColor>().getTransition({}).duration); - ASSERT_EQ(500ms, *layer->as<BackgroundLayer>()->impl->cascading - .get<BackgroundColor>().getTransition({}).delay); - - ASSERT_EQ(100ms, *layer->as<BackgroundLayer>()->impl->cascading - .get<BackgroundColor>().getTransition({"class"}).duration); - ASSERT_FALSE(bool(layer->as<BackgroundLayer>()->impl->cascading - .get<BackgroundColor>().getTransition({"class"}).delay)); + ASSERT_EQ(400ms, *layer->as<BackgroundLayer>()->impl().paint + .get<BackgroundColor>().options.duration); + ASSERT_EQ(500ms, *layer->as<BackgroundLayer>()->impl().paint + .get<BackgroundColor>().options.delay); } diff --git a/test/style/conversion/light.test.cpp b/test/style/conversion/light.test.cpp index a2185906d6..28e22b3550 100644 --- a/test/style/conversion/light.test.cpp +++ b/test/style/conversion/light.test.cpp @@ -30,7 +30,7 @@ TEST(StyleConversion, Light) { } { - auto light = parseLight("{\"color\":{\"stops\":[[14,\"blue\"],[16,\"red\"]]},\"intensity\":0.3,\"position\":[3,90,90]}"); + auto light = parseLight(R"({"color":{"stops":[[14,"blue"],[16,"red"]]},"intensity":0.3,"position":[3,90,90]})"); ASSERT_TRUE((bool) light); ASSERT_TRUE(light->getAnchor().isUndefined()); @@ -54,7 +54,7 @@ TEST(StyleConversion, Light) { } { - auto light = parseLight("{\"color\":\"blue\",\"intensity\":0.3,\"color-transition\":{\"duration\":1000}}"); + auto light = parseLight(R"({"color":"blue","intensity":0.3,"color-transition":{"duration":1000}})"); ASSERT_TRUE((bool) light); ASSERT_FALSE(light->getColor().isUndefined()); @@ -65,35 +65,35 @@ TEST(StyleConversion, Light) { } { - auto light = parseLight("{\"intensity\":false}"); + auto light = parseLight(R"({"intensity":false})"); ASSERT_FALSE((bool) light); ASSERT_EQ("value must be a number", error.message); } { - auto light = parseLight("{\"intensity\":{\"stops\":[[15,\"red\"],[17,\"blue\"]]}}"); + auto light = parseLight(R"({"intensity":{"stops":[[15,"red"],[17,"blue"]]}})"); ASSERT_FALSE((bool) light); ASSERT_EQ("value must be a number", error.message); } { - auto light = parseLight("{\"color\":5}"); + auto light = parseLight(R"({"color":5})"); ASSERT_FALSE((bool) light); ASSERT_EQ("value must be a string", error.message); } { - auto light = parseLight("{\"position\":[0,5]}"); + auto light = parseLight(R"({"position":[0,5]})"); ASSERT_FALSE((bool) light); ASSERT_EQ("value must be an array of 3 numbers", error.message); } { - auto light = parseLight("{\"anchor\":\"something\"}"); + auto light = parseLight(R"({"anchor":"something"})"); ASSERT_FALSE((bool) light); ASSERT_EQ("value must be a valid enumeration value", error.message); diff --git a/test/style/conversion/stringify.test.cpp b/test/style/conversion/stringify.test.cpp index 1dae20b26b..0b2940a0e0 100644 --- a/test/style/conversion/stringify.test.cpp +++ b/test/style/conversion/stringify.test.cpp @@ -121,10 +121,17 @@ TEST(Stringify, PropertyValue) { } TEST(Stringify, Layout) { - ASSERT_EQ(stringify(SymbolLayoutProperties()), "{}"); - - SymbolLayoutProperties layout; - layout.unevaluated.get<SymbolAvoidEdges>() = true; - layout.unevaluated.get<IconPadding>() = 2.0; + auto stringify = [] (const SymbolLayoutProperties::Unevaluated& layout) { + rapidjson::StringBuffer s; + rapidjson::Writer<rapidjson::StringBuffer> writer(s); + layout.stringify(writer); + return std::string(s.GetString()); + }; + + ASSERT_EQ(stringify(SymbolLayoutProperties::Unevaluated()), "{}"); + + SymbolLayoutProperties::Unevaluated layout; + layout.get<SymbolAvoidEdges>() = true; + layout.get<IconPadding>() = 2.0; ASSERT_EQ(stringify(layout), "{\"symbol-avoid-edges\":true,\"icon-padding\":2.0}"); } diff --git a/test/style/filter.test.cpp b/test/style/filter.test.cpp index c70792d8ef..96de125945 100644 --- a/test/style/filter.test.cpp +++ b/test/style/filter.test.cpp @@ -29,13 +29,13 @@ Feature feature(const PropertyMap& properties, const Geometry<double>& geometry } TEST(Filter, EqualsString) { - Filter f = parse("[\"==\", \"foo\", \"bar\"]"); + Filter f = parse(R"(["==", "foo", "bar"])"); ASSERT_TRUE(f(feature({{ "foo", std::string("bar") }}))); ASSERT_FALSE(f(feature({{ "foo", std::string("baz") }}))); } TEST(Filter, EqualsNumber) { - Filter f = parse("[\"==\", \"foo\", 0]"); + Filter f = parse(R"(["==", "foo", 0])"); ASSERT_TRUE(f(feature({{ "foo", int64_t(0) }}))); ASSERT_TRUE(f(feature({{ "foo", uint64_t(0) }}))); ASSERT_TRUE(f(feature({{ "foo", double(0) }}))); @@ -50,13 +50,13 @@ TEST(Filter, EqualsNumber) { } TEST(Filter, EqualsType) { - Filter f = parse("[\"==\", \"$type\", \"LineString\"]"); + Filter f = parse(R"(["==", "$type", "LineString"])"); ASSERT_FALSE(f(feature({{}}, Point<double>()))); ASSERT_TRUE(f(feature({{}}, LineString<double>()))); } TEST(Filter, InType) { - Filter f = parse("[\"in\", \"$type\", \"LineString\", \"Polygon\"]"); + Filter f = parse(R"(["in", "$type", "LineString", "Polygon"])"); ASSERT_FALSE(f(feature({{}}, Point<double>()))); ASSERT_TRUE(f(feature({{}}, LineString<double>()))); ASSERT_TRUE(f(feature({{}}, Polygon<double>()))); diff --git a/test/style/function/exponential_stops.test.cpp b/test/style/function/exponential_stops.test.cpp new file mode 100644 index 0000000000..81438ec952 --- /dev/null +++ b/test/style/function/exponential_stops.test.cpp @@ -0,0 +1,20 @@ +#include <mbgl/test/util.hpp> + +#include <mbgl/style/function/exponential_stops.hpp> + +using namespace mbgl; +using namespace mbgl::style; + +TEST(ExponentialStops, Empty) { + ExponentialStops<float> stops; + EXPECT_FALSE(bool(stops.evaluate(0))); +} + +TEST(ExponentialStops, NonNumericInput) { + ExponentialStops<float> stops(std::map<float, float> {{0.0f, 0.0f}}); + EXPECT_FALSE(bool(stops.evaluate(Value(NullValue())))); + EXPECT_FALSE(bool(stops.evaluate(Value(false)))); + EXPECT_FALSE(bool(stops.evaluate(Value(std::string())))); + EXPECT_FALSE(bool(stops.evaluate(Value(std::vector<Value>())))); + EXPECT_FALSE(bool(stops.evaluate(Value(std::unordered_map<std::string, Value>())))); +} diff --git a/test/style/function/interval_stops.test.cpp b/test/style/function/interval_stops.test.cpp new file mode 100644 index 0000000000..8a5e74b8b6 --- /dev/null +++ b/test/style/function/interval_stops.test.cpp @@ -0,0 +1,20 @@ +#include <mbgl/test/util.hpp> + +#include <mbgl/style/function/interval_stops.hpp> + +using namespace mbgl; +using namespace mbgl::style; + +TEST(IntervalStops, Empty) { + IntervalStops<float> stops; + EXPECT_FALSE(bool(stops.evaluate(0))); +} + +TEST(IntervalStops, NonNumericInput) { + IntervalStops<float> stops(std::map<float, float> {{0.0f, 0.0f}}); + EXPECT_FALSE(bool(stops.evaluate(Value(NullValue())))); + EXPECT_FALSE(bool(stops.evaluate(Value(false)))); + EXPECT_FALSE(bool(stops.evaluate(Value(std::string())))); + EXPECT_FALSE(bool(stops.evaluate(Value(std::vector<Value>())))); + EXPECT_FALSE(bool(stops.evaluate(Value(std::unordered_map<std::string, Value>())))); +} diff --git a/test/style/paint_property.test.cpp b/test/style/properties.test.cpp index fcca05f3bd..279fadb8c2 100644 --- a/test/style/paint_property.test.cpp +++ b/test/style/properties.test.cpp @@ -1,13 +1,14 @@ #include <mbgl/test/util.hpp> -#include <mbgl/style/paint_property.hpp> -#include <mbgl/renderer/transitioning_property.hpp> +#include <mbgl/style/properties.hpp> +#include <mbgl/renderer/property_evaluator.hpp> +#include <mbgl/renderer/data_driven_property_evaluator.hpp> using namespace mbgl; using namespace mbgl::style; using namespace std::literals::chrono_literals; -float evaluate(TransitioningProperty<PropertyValue<float>>& property, Duration delta = Duration::zero()) { +float evaluate(Transitioning<PropertyValue<float>>& property, Duration delta = Duration::zero()) { ZoomHistory zoomHistory; zoomHistory.update(0, TimePoint::min() + delta); @@ -25,7 +26,7 @@ float evaluate(TransitioningProperty<PropertyValue<float>>& property, Duration d return property.evaluate(evaluator, parameters.now); } -PossiblyEvaluatedPropertyValue<float> evaluate(TransitioningProperty<DataDrivenPropertyValue<float>>& property, Duration delta = Duration::zero()) { +PossiblyEvaluatedPropertyValue<float> evaluate(Transitioning<DataDrivenPropertyValue<float>>& property, Duration delta = Duration::zero()) { ZoomHistory zoomHistory; zoomHistory.update(0, TimePoint::min() + delta); @@ -43,15 +44,15 @@ PossiblyEvaluatedPropertyValue<float> evaluate(TransitioningProperty<DataDrivenP return property.evaluate(evaluator, parameters.now); } -TEST(TransitioningProperty, EvaluateDefaultValue) { - TransitioningProperty<PropertyValue<float>> property; +TEST(TransitioningPropertyValue, EvaluateDefaultValue) { + Transitioning<PropertyValue<float>> property; ASSERT_EQ(0.0f, evaluate(property)); } -TEST(TransitioningProperty, EvaluateUntransitionedConstant) { - TransitioningProperty<PropertyValue<float>> property { +TEST(TransitioningPropertyValue, EvaluateUntransitionedConstant) { + Transitioning<PropertyValue<float>> property { PropertyValue<float>(1.0f), - TransitioningProperty<PropertyValue<float>>(), + Transitioning<PropertyValue<float>>(), TransitionOptions(), TimePoint::min() }; @@ -59,18 +60,18 @@ TEST(TransitioningProperty, EvaluateUntransitionedConstant) { ASSERT_EQ(1.0f, evaluate(property)); } -TEST(TransitioningProperty, EvaluateTransitionedConstantWithoutDelay) { +TEST(TransitioningPropertyValue, EvaluateTransitionedConstantWithoutDelay) { TransitionOptions transition; transition.duration = { 1000ms }; - TransitioningProperty<PropertyValue<float>> t0 { + Transitioning<PropertyValue<float>> t0 { PropertyValue<float>(0.0f), - TransitioningProperty<PropertyValue<float>>(), + Transitioning<PropertyValue<float>>(), TransitionOptions(), TimePoint::min() }; - TransitioningProperty<PropertyValue<float>> t1 { + Transitioning<PropertyValue<float>> t1 { PropertyValue<float>(1.0f), t0, transition, @@ -82,19 +83,19 @@ TEST(TransitioningProperty, EvaluateTransitionedConstantWithoutDelay) { ASSERT_FLOAT_EQ(1.0f, evaluate(t1, 1500ms)); } -TEST(TransitioningProperty, EvaluateTransitionedConstantWithDelay) { +TEST(TransitioningPropertyValue, EvaluateTransitionedConstantWithDelay) { TransitionOptions transition; transition.delay = { 1000ms }; transition.duration = { 1000ms }; - TransitioningProperty<PropertyValue<float>> t0 { + Transitioning<PropertyValue<float>> t0 { PropertyValue<float>(0.0f), - TransitioningProperty<PropertyValue<float>>(), + Transitioning<PropertyValue<float>>(), TransitionOptions(), TimePoint::min() }; - TransitioningProperty<PropertyValue<float>> t1 { + Transitioning<PropertyValue<float>> t1 { PropertyValue<float>(1.0f), t0, transition, @@ -108,14 +109,14 @@ TEST(TransitioningProperty, EvaluateTransitionedConstantWithDelay) { ASSERT_FLOAT_EQ(1.0f, evaluate(t1, 2500ms)); } -TEST(TransitioningProperty, EvaluateDataDrivenValue) { +TEST(TransitioningDataDrivenPropertyValue, Evaluate) { TransitionOptions transition; transition.delay = { 1000ms }; transition.duration = { 1000ms }; - TransitioningProperty<DataDrivenPropertyValue<float>> t0 { + Transitioning<DataDrivenPropertyValue<float>> t0 { DataDrivenPropertyValue<float>(0.0f), - TransitioningProperty<DataDrivenPropertyValue<float>>(), + Transitioning<DataDrivenPropertyValue<float>>(), TransitionOptions(), TimePoint::min() }; @@ -125,7 +126,7 @@ TEST(TransitioningProperty, EvaluateDataDrivenValue) { IdentityStops<float>() }; - TransitioningProperty<DataDrivenPropertyValue<float>> t1 { + Transitioning<DataDrivenPropertyValue<float>> t1 { DataDrivenPropertyValue<float>(sourceFunction), t0, transition, diff --git a/test/style/source.test.cpp b/test/style/source.test.cpp index c60a473589..eaa3c72877 100644 --- a/test/style/source.test.cpp +++ b/test/style/source.test.cpp @@ -7,6 +7,9 @@ #include <mbgl/style/sources/raster_source.hpp> #include <mbgl/style/sources/vector_source.hpp> #include <mbgl/style/sources/geojson_source.hpp> +#include <mbgl/style/sources/image_source.hpp> +#include <mbgl/style/layers/raster_layer.cpp> +#include <mbgl/style/layers/line_layer.hpp> #include <mbgl/renderer/sources/render_raster_source.hpp> #include <mbgl/renderer/sources/render_vector_source.hpp> @@ -16,6 +19,9 @@ #include <mbgl/util/run_loop.hpp> #include <mbgl/util/string.hpp> #include <mbgl/util/io.hpp> +#include <mbgl/util/premultiply.hpp> +#include <mbgl/util/image.hpp> + #include <mbgl/util/tileset.hpp> #include <mbgl/util/default_thread_pool.hpp> #include <mbgl/util/logging.hpp> @@ -23,12 +29,10 @@ #include <mbgl/util/range.hpp> #include <mbgl/map/transform.hpp> -#include <mbgl/style/style.hpp> -#include <mbgl/style/layers/line_layer.hpp> #include <mbgl/annotation/annotation_manager.hpp> #include <mbgl/annotation/annotation_source.hpp> - -#include <mapbox/geojsonvt.hpp> +#include <mbgl/renderer/image_manager.hpp> +#include <mbgl/text/glyph_manager.hpp> #include <cstdint> @@ -43,8 +47,9 @@ public: Transform transform; TransformState transformState; ThreadPool threadPool { 1 }; - AnnotationManager annotationManager { 1.0 }; - style::Style style { threadPool, fileSource, 1.0 }; + AnnotationManager annotationManager; + ImageManager imageManager; + GlyphManager glyphManager { fileSource }; TileParameters tileParameters { 1.0, @@ -54,7 +59,8 @@ public: fileSource, MapMode::Continuous, annotationManager, - style + imageManager, + glyphManager }; SourceTest() { @@ -95,8 +101,8 @@ TEST(Source, LoadingFail) { }; VectorSource source("source", "url"); - source.baseImpl->setObserver(&test.styleObserver); - source.baseImpl->loadDescription(test.fileSource); + source.setObserver(&test.styleObserver); + source.loadDescription(test.fileSource); test.run(); } @@ -118,8 +124,8 @@ TEST(Source, LoadingCorrupt) { }; VectorSource source("source", "url"); - source.baseImpl->setObserver(&test.styleObserver); - source.baseImpl->loadDescription(test.fileSource); + source.setObserver(&test.styleObserver); + source.loadDescription(test.fileSource); test.run(); } @@ -133,14 +139,17 @@ TEST(Source, RasterTileEmpty) { return response; }; + RasterLayer layer("id", "source"); + std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }}; + Tileset tileset; tileset.tiles = { "tiles" }; RasterSource source("source", tileset, 512); - source.baseImpl->loadDescription(test.fileSource); + source.loadDescription(test.fileSource); test.renderSourceObserver.tileChanged = [&] (RenderSource& source_, const OverscaledTileID&) { - EXPECT_EQ("source", source_.baseImpl.id); + EXPECT_EQ("source", source_.baseImpl->id); test.end(); }; @@ -148,9 +157,13 @@ TEST(Source, RasterTileEmpty) { FAIL() << "Should never be called"; }; - RenderRasterSource renderSource(*source.impl); - renderSource.setObserver(&test.renderSourceObserver); - renderSource.updateTiles(test.tileParameters); + auto renderSource = RenderSource::create(source.baseImpl); + renderSource->setObserver(&test.renderSourceObserver); + renderSource->update(source.baseImpl, + layers, + true, + true, + test.tileParameters); test.run(); } @@ -164,14 +177,19 @@ TEST(Source, VectorTileEmpty) { return response; }; + LineLayer layer("id", "source"); + layer.setSourceLayer("water"); + + std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }}; + Tileset tileset; tileset.tiles = { "tiles" }; VectorSource source("source", tileset); - source.baseImpl->loadDescription(test.fileSource); + source.loadDescription(test.fileSource); test.renderSourceObserver.tileChanged = [&] (RenderSource& source_, const OverscaledTileID&) { - EXPECT_EQ("source", source_.baseImpl.id); + EXPECT_EQ("source", source_.baseImpl->id); test.end(); }; @@ -179,9 +197,13 @@ TEST(Source, VectorTileEmpty) { FAIL() << "Should never be called"; }; - RenderVectorSource renderSource(*source.impl); - renderSource.setObserver(&test.renderSourceObserver); - renderSource.updateTiles(test.tileParameters); + auto renderSource = RenderSource::create(source.baseImpl); + renderSource->setObserver(&test.renderSourceObserver); + renderSource->update(source.baseImpl, + layers, + true, + true, + test.tileParameters); test.run(); } @@ -197,22 +219,29 @@ TEST(Source, RasterTileFail) { return response; }; + RasterLayer layer("id", "source"); + std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }}; + Tileset tileset; tileset.tiles = { "tiles" }; RasterSource source("source", tileset, 512); - source.baseImpl->loadDescription(test.fileSource); + source.loadDescription(test.fileSource); test.renderSourceObserver.tileError = [&] (RenderSource& source_, const OverscaledTileID& tileID, std::exception_ptr error) { - EXPECT_EQ(SourceType::Raster, source_.baseImpl.type); + EXPECT_EQ(SourceType::Raster, source_.baseImpl->type); EXPECT_EQ(OverscaledTileID(0, 0, 0), tileID); EXPECT_EQ("Failed by the test case", util::toString(error)); test.end(); }; - RenderRasterSource renderSource(*source.impl); - renderSource.setObserver(&test.renderSourceObserver); - renderSource.updateTiles(test.tileParameters); + auto renderSource = RenderSource::create(source.baseImpl); + renderSource->setObserver(&test.renderSourceObserver); + renderSource->update(source.baseImpl, + layers, + true, + true, + test.tileParameters); test.run(); } @@ -228,22 +257,31 @@ TEST(Source, VectorTileFail) { return response; }; + LineLayer layer("id", "source"); + layer.setSourceLayer("water"); + + std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }}; + Tileset tileset; tileset.tiles = { "tiles" }; VectorSource source("source", tileset); - source.baseImpl->loadDescription(test.fileSource); + source.loadDescription(test.fileSource); test.renderSourceObserver.tileError = [&] (RenderSource& source_, const OverscaledTileID& tileID, std::exception_ptr error) { - EXPECT_EQ(SourceType::Vector, source_.baseImpl.type); + EXPECT_EQ(SourceType::Vector, source_.baseImpl->type); EXPECT_EQ(OverscaledTileID(0, 0, 0), tileID); EXPECT_EQ("Failed by the test case", util::toString(error)); test.end(); }; - RenderVectorSource renderSource(*source.impl); - renderSource.setObserver(&test.renderSourceObserver); - renderSource.updateTiles(test.tileParameters); + auto renderSource = RenderSource::create(source.baseImpl); + renderSource->setObserver(&test.renderSourceObserver); + renderSource->update(source.baseImpl, + layers, + true, + true, + test.tileParameters); test.run(); } @@ -257,23 +295,30 @@ TEST(Source, RasterTileCorrupt) { return response; }; + RasterLayer layer("id", "source"); + std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }}; + Tileset tileset; tileset.tiles = { "tiles" }; RasterSource source("source", tileset, 512); - source.baseImpl->loadDescription(test.fileSource); + source.loadDescription(test.fileSource); test.renderSourceObserver.tileError = [&] (RenderSource& source_, const OverscaledTileID& tileID, std::exception_ptr error) { - EXPECT_EQ(source_.baseImpl.type, SourceType::Raster); + EXPECT_EQ(source_.baseImpl->type, SourceType::Raster); EXPECT_EQ(OverscaledTileID(0, 0, 0), tileID); EXPECT_TRUE(bool(error)); // Not asserting on platform-specific error text. test.end(); }; - RenderRasterSource renderSource(*source.impl); - renderSource.setObserver(&test.renderSourceObserver); - renderSource.updateTiles(test.tileParameters); + auto renderSource = RenderSource::create(source.baseImpl); + renderSource->setObserver(&test.renderSourceObserver); + renderSource->update(source.baseImpl, + layers, + true, + true, + test.tileParameters); test.run(); } @@ -287,27 +332,31 @@ TEST(Source, VectorTileCorrupt) { return response; }; - // Need to have at least one layer that uses the source. - auto layer = std::make_unique<LineLayer>("id", "source"); - layer->setSourceLayer("water"); - test.style.addLayer(std::move(layer)); + LineLayer layer("id", "source"); + layer.setSourceLayer("water"); + + std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }}; Tileset tileset; tileset.tiles = { "tiles" }; VectorSource source("source", tileset); - source.baseImpl->loadDescription(test.fileSource); + source.loadDescription(test.fileSource); test.renderSourceObserver.tileError = [&] (RenderSource& source_, const OverscaledTileID& tileID, std::exception_ptr error) { - EXPECT_EQ(source_.baseImpl.type, SourceType::Vector); + EXPECT_EQ(source_.baseImpl->type, SourceType::Vector); EXPECT_EQ(OverscaledTileID(0, 0, 0), tileID); EXPECT_EQ(util::toString(error), "unknown pbf field type exception"); test.end(); }; - RenderVectorSource renderSource(*source.impl); - renderSource.setObserver(&test.renderSourceObserver); - renderSource.updateTiles(test.tileParameters); + auto renderSource = RenderSource::create(source.baseImpl); + renderSource->setObserver(&test.renderSourceObserver); + renderSource->update(source.baseImpl, + layers, + true, + true, + test.tileParameters); test.run(); } @@ -320,11 +369,14 @@ TEST(Source, RasterTileCancel) { return optional<Response>(); }; + RasterLayer layer("id", "source"); + std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }}; + Tileset tileset; tileset.tiles = { "tiles" }; RasterSource source("source", tileset, 512); - source.baseImpl->loadDescription(test.fileSource); + source.loadDescription(test.fileSource); test.renderSourceObserver.tileChanged = [&] (RenderSource&, const OverscaledTileID&) { FAIL() << "Should never be called"; @@ -334,9 +386,13 @@ TEST(Source, RasterTileCancel) { FAIL() << "Should never be called"; }; - RenderRasterSource renderSource(*source.impl); - renderSource.setObserver(&test.renderSourceObserver); - renderSource.updateTiles(test.tileParameters); + auto renderSource = RenderSource::create(source.baseImpl); + renderSource->setObserver(&test.renderSourceObserver); + renderSource->update(source.baseImpl, + layers, + true, + true, + test.tileParameters); test.run(); } @@ -349,11 +405,16 @@ TEST(Source, VectorTileCancel) { return optional<Response>(); }; + LineLayer layer("id", "source"); + layer.setSourceLayer("water"); + + std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }}; + Tileset tileset; tileset.tiles = { "tiles" }; VectorSource source("source", tileset); - source.baseImpl->loadDescription(test.fileSource); + source.loadDescription(test.fileSource); test.renderSourceObserver.tileChanged = [&] (RenderSource&, const OverscaledTileID&) { FAIL() << "Should never be called"; @@ -363,9 +424,13 @@ TEST(Source, VectorTileCancel) { FAIL() << "Should never be called"; }; - RenderVectorSource renderSource(*source.impl); - renderSource.setObserver(&test.renderSourceObserver); - renderSource.updateTiles(test.tileParameters); + auto renderSource = RenderSource::create(source.baseImpl); + renderSource->setObserver(&test.renderSourceObserver); + renderSource->update(source.baseImpl, + layers, + true, + true, + test.tileParameters); test.run(); } @@ -373,6 +438,9 @@ TEST(Source, VectorTileCancel) { TEST(Source, RasterTileAttribution) { SourceTest test; + RasterLayer layer("id", "source"); + std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }}; + std::string mapboxOSM = ("<a href='https://www.mapbox.com/about/maps/' target='_blank'>© Mapbox</a> " "<a href='http://www.openstreetmap.org/about/' target='_blank'>©️ OpenStreetMap</a>"); @@ -398,11 +466,15 @@ TEST(Source, RasterTileAttribution) { }; RasterSource source("source", "url", 512); - source.baseImpl->setObserver(&test.styleObserver); - source.baseImpl->loadDescription(test.fileSource); + source.setObserver(&test.styleObserver); + source.loadDescription(test.fileSource); - RenderRasterSource renderSource(*source.impl); - renderSource.updateTiles(test.tileParameters); + auto renderSource = RenderSource::create(source.baseImpl); + renderSource->update(source.baseImpl, + layers, + true, + true, + test.tileParameters); test.run(); } @@ -413,7 +485,7 @@ TEST(Source, GeoJSonSourceUrlUpdate) { test.fileSource.sourceResponse = [&] (const Resource& resource) { EXPECT_EQ("url", resource.url); Response response; - response.data = std::make_unique<std::string>("{\"geometry\": {\"type\": \"Point\", \"coordinates\": [1.1, 1.1]}, \"type\": \"Feature\", \"properties\": {}}"); + response.data = std::make_unique<std::string>(R"({"geometry": {"type": "Point", "coordinates": [1.1, 1.1]}, "type": "Feature", "properties": {}})"); return response; }; @@ -423,10 +495,10 @@ TEST(Source, GeoJSonSourceUrlUpdate) { }; GeoJSONSource source("source"); - source.baseImpl->setObserver(&test.styleObserver); + source.setObserver(&test.styleObserver); // Load initial, so the source state will be loaded=true - source.baseImpl->loadDescription(test.fileSource); + source.loadDescription(test.fileSource); // Schedule an update test.loop.invoke([&] () { @@ -436,3 +508,39 @@ TEST(Source, GeoJSonSourceUrlUpdate) { test.run(); } + +TEST(Source, ImageSourceImageUpdate) { + SourceTest test; + + test.fileSource.response = [&] (const Resource& resource) { + EXPECT_EQ("http://url", resource.url); + Response response; + response.data = std::make_unique<std::string>(util::read_file("test/fixtures/image/no_profile.png")); + return response; + }; + test.styleObserver.sourceChanged = [&] (Source&) { + // Should be called (test will hang if it doesn't) + test.end(); + }; + std::array<LatLng, 4> coords; + + ImageSource source("source", coords); + source.setURL("http://url"); + source.setObserver(&test.styleObserver); + + // Load initial, so the source state will be loaded=true + source.loadDescription(test.fileSource); + UnassociatedImage rgba({ 1, 1 }); + rgba.data[0] = 255; + rgba.data[1] = 254; + rgba.data[2] = 253; + rgba.data[3] = 128; + + // Schedule an update + test.loop.invoke([&] () { + // Update the url + source.setImage(std::move(rgba)); + }); + + test.run(); +} diff --git a/test/style/style.test.cpp b/test/style/style.test.cpp index 841c7b291b..ab58eb1024 100644 --- a/test/style/style.test.cpp +++ b/test/style/style.test.cpp @@ -2,7 +2,7 @@ #include <mbgl/test/stub_file_source.hpp> #include <mbgl/test/fixture_log_observer.hpp> -#include <mbgl/style/style.hpp> +#include <mbgl/style/style_impl.hpp> #include <mbgl/style/source_impl.hpp> #include <mbgl/style/sources/vector_source.hpp> #include <mbgl/style/layer.hpp> @@ -21,29 +21,29 @@ TEST(Style, Properties) { ThreadPool threadPool{ 1 }; StubFileSource fileSource; - Style style { threadPool, fileSource, 1.0 }; + Style::Impl style { threadPool, fileSource, 1.0 }; - style.setJSON(R"STYLE({"name": "Test"})STYLE"); + style.loadJSON(R"STYLE({"name": "Test"})STYLE"); ASSERT_EQ("Test", style.getName()); - style.setJSON(R"STYLE({"center": [10, 20]})STYLE"); + style.loadJSON(R"STYLE({"center": [10, 20]})STYLE"); ASSERT_EQ("", style.getName()); ASSERT_EQ((LatLng{20, 10}), style.getDefaultLatLng()); - style.setJSON(R"STYLE({"bearing": 24})STYLE"); + style.loadJSON(R"STYLE({"bearing": 24})STYLE"); ASSERT_EQ("", style.getName()); ASSERT_EQ((LatLng{0, 0}), style.getDefaultLatLng()); ASSERT_EQ(24, style.getDefaultBearing()); - style.setJSON(R"STYLE({"zoom": 13.3})STYLE"); + style.loadJSON(R"STYLE({"zoom": 13.3})STYLE"); ASSERT_EQ("", style.getName()); ASSERT_EQ(13.3, style.getDefaultZoom()); - style.setJSON(R"STYLE({"pitch": 60})STYLE"); + style.loadJSON(R"STYLE({"pitch": 60})STYLE"); ASSERT_EQ("", style.getName()); ASSERT_EQ(60, style.getDefaultPitch()); - style.setJSON(R"STYLE({"name": 23, "center": {}, "bearing": "north", "zoom": null, "pitch": "wide"})STYLE"); + style.loadJSON(R"STYLE({"name": 23, "center": {}, "bearing": "north", "zoom": null, "pitch": "wide"})STYLE"); ASSERT_EQ("", style.getName()); ASSERT_EQ((LatLng{0, 0}), style.getDefaultLatLng()); ASSERT_EQ(0, style.getDefaultBearing()); @@ -56,9 +56,9 @@ TEST(Style, DuplicateSource) { ThreadPool threadPool{ 1 }; StubFileSource fileSource; - Style style { threadPool, fileSource, 1.0 }; + Style::Impl style { threadPool, fileSource, 1.0 }; - style.setJSON(util::read_file("test/fixtures/resources/style-unused-sources.json")); + style.loadJSON(util::read_file("test/fixtures/resources/style-unused-sources.json")); style.addSource(std::make_unique<VectorSource>("sourceId", "mapbox://mapbox.mapbox-terrain-v2")); @@ -78,9 +78,9 @@ TEST(Style, RemoveSourceInUse) { ThreadPool threadPool{ 1 }; StubFileSource fileSource; - Style style { threadPool, fileSource, 1.0 }; + Style::Impl style { threadPool, fileSource, 1.0 }; - style.setJSON(util::read_file("test/fixtures/resources/style-unused-sources.json")); + style.loadJSON(util::read_file("test/fixtures/resources/style-unused-sources.json")); style.addSource(std::make_unique<VectorSource>("sourceId", "mapbox://mapbox.mapbox-terrain-v2")); style.addLayer(std::make_unique<LineLayer>("layerId", "sourceId")); diff --git a/test/style/style_image.test.cpp b/test/style/style_image.test.cpp index 319120df83..e49bf37582 100644 --- a/test/style/style_image.test.cpp +++ b/test/style/style_image.test.cpp @@ -8,7 +8,7 @@ using namespace mbgl; TEST(StyleImage, ZeroWidth) { try { - style::Image(PremultipliedImage({ 0, 16 }), 2.0); + style::Image("test", PremultipliedImage({ 0, 16 }), 2.0); FAIL() << "Expected exception"; } catch (util::SpriteImageException& ex) { EXPECT_STREQ("Sprite image dimensions may not be zero", ex.what()); @@ -17,7 +17,7 @@ TEST(StyleImage, ZeroWidth) { TEST(StyleImage, ZeroHeight) { try { - style::Image(PremultipliedImage({ 16, 0 }), 2.0); + style::Image("test", PremultipliedImage({ 16, 0 }), 2.0); FAIL() << "Expected exception"; } catch (util::SpriteImageException& ex) { EXPECT_STREQ("Sprite image dimensions may not be zero", ex.what()); @@ -26,7 +26,7 @@ TEST(StyleImage, ZeroHeight) { TEST(StyleImage, ZeroRatio) { try { - style::Image(PremultipliedImage({ 16, 16 }), 0.0); + style::Image("test", PremultipliedImage({ 16, 16 }), 0.0); FAIL() << "Expected exception"; } catch (util::SpriteImageException& ex) { EXPECT_STREQ("Sprite pixelRatio may not be <= 0", ex.what()); @@ -34,19 +34,15 @@ TEST(StyleImage, ZeroRatio) { } TEST(StyleImage, Retina) { - style::Image image(PremultipliedImage({ 32, 24 }), 2.0); - EXPECT_EQ(16, image.getWidth()); - EXPECT_EQ(32u, image.image.size.width); - EXPECT_EQ(12, image.getHeight()); - EXPECT_EQ(24u, image.image.size.height); - EXPECT_EQ(2, image.pixelRatio); + style::Image image("test", PremultipliedImage({ 32, 24 }), 2.0); + EXPECT_EQ(32u, image.getImage().size.width); + EXPECT_EQ(24u, image.getImage().size.height); + EXPECT_EQ(2, image.getPixelRatio()); } TEST(StyleImage, FractionalRatio) { - style::Image image(PremultipliedImage({ 20, 12 }), 1.5); - EXPECT_EQ(float(20.0 / 1.5), image.getWidth()); - EXPECT_EQ(20u, image.image.size.width); - EXPECT_EQ(float(12.0 / 1.5), image.getHeight()); - EXPECT_EQ(12u, image.image.size.height); - EXPECT_EQ(1.5, image.pixelRatio); + style::Image image("test", PremultipliedImage({ 20, 12 }), 1.5); + EXPECT_EQ(20u, image.getImage().size.width); + EXPECT_EQ(12u, image.getImage().size.height); + EXPECT_EQ(1.5, image.getPixelRatio()); } diff --git a/test/style/style_layer.test.cpp b/test/style/style_layer.test.cpp index 657dc24a70..77acca2868 100644 --- a/test/style/style_layer.test.cpp +++ b/test/style/style_layer.test.cpp @@ -1,7 +1,7 @@ #include <mbgl/test/util.hpp> #include <mbgl/test/stub_layer_observer.hpp> #include <mbgl/test/stub_file_source.hpp> -#include <mbgl/style/style.hpp> +#include <mbgl/style/style_impl.hpp> #include <mbgl/style/layers/background_layer.hpp> #include <mbgl/style/layers/background_layer_impl.hpp> #include <mbgl/style/layers/circle_layer.hpp> @@ -28,15 +28,6 @@ using namespace mbgl::style; namespace { -template <class T, class... Params> void testClone(Params... params) { - auto layer = std::make_unique<T>(std::forward<Params>(params)...); - auto clone = layer->baseImpl->clone(); - EXPECT_NE(layer.get(), clone.get()); - EXPECT_TRUE(reinterpret_cast<typename T::Impl*>(clone->baseImpl.get())); - layer->impl->id = "test"; - EXPECT_EQ("test", layer->baseImpl->clone()->getID()); -} - const auto color = Color { 1, 0, 0, 1 }; const auto opacity = 1.0f; const auto radius = 1.0f; @@ -61,16 +52,6 @@ const auto duration = 1.0f; } // namespace -TEST(Layer, Clone) { - testClone<BackgroundLayer>("background"); - testClone<CircleLayer>("circle", "source"); - testClone<CustomLayer>("custom", [](void*){}, [](void*, const CustomLayerRenderParameters&){}, [](void*){}, nullptr), - testClone<FillLayer>("fill", "source"); - testClone<LineLayer>("line", "source"); - testClone<RasterLayer>("raster", "source"); - testClone<SymbolLayer>("symbol", "source"); -} - TEST(Layer, BackgroundProperties) { auto layer = std::make_unique<BackgroundLayer>("background"); EXPECT_TRUE(layer->is<BackgroundLayer>()); @@ -222,11 +203,11 @@ TEST(Layer, RasterProperties) { TEST(Layer, Observer) { auto layer = std::make_unique<LineLayer>("line", "source"); StubLayerObserver observer; - layer->baseImpl->setObserver(&observer); + layer->setObserver(&observer); // Notifies observer on filter change. bool filterChanged = false; - observer.layerFilterChanged = [&] (Layer& layer_) { + observer.layerChanged = [&] (Layer& layer_) { EXPECT_EQ(layer.get(), &layer_); filterChanged = true; }; @@ -235,7 +216,7 @@ TEST(Layer, Observer) { // Notifies observer on visibility change. bool visibilityChanged = false; - observer.layerVisibilityChanged = [&] (Layer& layer_) { + observer.layerChanged = [&] (Layer& layer_) { EXPECT_EQ(layer.get(), &layer_); visibilityChanged = true; }; @@ -244,7 +225,7 @@ TEST(Layer, Observer) { // Notifies observer on paint property change. bool paintPropertyChanged = false; - observer.layerPaintPropertyChanged = [&] (Layer& layer_) { + observer.layerChanged = [&] (Layer& layer_) { EXPECT_EQ(layer.get(), &layer_); paintPropertyChanged = true; }; @@ -253,7 +234,7 @@ TEST(Layer, Observer) { // Notifies observer on layout property change. bool layoutPropertyChanged = false; - observer.layerLayoutPropertyChanged = [&] (Layer& layer_, const char *) { + observer.layerChanged = [&] (Layer& layer_) { EXPECT_EQ(layer.get(), &layer_); layoutPropertyChanged = true; }; @@ -262,16 +243,28 @@ TEST(Layer, Observer) { // Does not notify observer on no-op visibility change. visibilityChanged = false; + observer.layerChanged = [&] (Layer& layer_) { + EXPECT_EQ(layer.get(), &layer_); + visibilityChanged = true; + }; layer->setVisibility(VisibilityType::None); EXPECT_FALSE(visibilityChanged); // Does not notify observer on no-op paint property change. paintPropertyChanged = false; + observer.layerChanged = [&] (Layer& layer_) { + EXPECT_EQ(layer.get(), &layer_); + paintPropertyChanged = true; + }; layer->setLineColor(color); EXPECT_FALSE(paintPropertyChanged); // Does not notify observer on no-op layout property change. layoutPropertyChanged = false; + observer.layerChanged = [&] (Layer& layer_) { + EXPECT_EQ(layer.get(), &layer_); + layoutPropertyChanged = true; + }; layer->setLineCap(lineCap); EXPECT_FALSE(layoutPropertyChanged); } @@ -282,8 +275,8 @@ TEST(Layer, DuplicateLayer) { // Setup style ThreadPool threadPool{ 1 }; StubFileSource fileSource; - Style style { threadPool, fileSource, 1.0 }; - style.setJSON(util::read_file("test/fixtures/resources/style-unused-sources.json")); + Style::Impl style { threadPool, fileSource, 1.0 }; + style.loadJSON(util::read_file("test/fixtures/resources/style-unused-sources.json")); // Add initial layer style.addLayer(std::make_unique<LineLayer>("line", "unusedsource")); diff --git a/test/style/style_parser.test.cpp b/test/style/style_parser.test.cpp index e3c1da582f..5fa81b47e9 100644 --- a/test/style/style_parser.test.cpp +++ b/test/style/style_parser.test.cpp @@ -16,8 +16,8 @@ using namespace mbgl; -typedef std::pair<uint32_t, std::string> Message; -typedef std::vector<Message> Messages; +using Message = std::pair<uint32_t, std::string>; +using Messages = std::vector<Message>; class StyleParserTest : public ::testing::TestWithParam<std::string> {}; diff --git a/test/text/glyph_atlas.test.cpp b/test/text/glyph_loader.test.cpp index 5aff1ee441..be197ebb46 100644 --- a/test/text/glyph_atlas.test.cpp +++ b/test/text/glyph_loader.test.cpp @@ -1,8 +1,7 @@ #include <mbgl/test/util.hpp> #include <mbgl/test/stub_file_source.hpp> -#include <mbgl/test/stub_style_observer.hpp> -#include <mbgl/text/glyph_atlas.hpp> +#include <mbgl/text/glyph_manager.hpp> #include <mbgl/util/run_loop.hpp> #include <mbgl/util/string.hpp> #include <mbgl/util/io.hpp> @@ -10,30 +9,44 @@ using namespace mbgl; +class StubGlyphManagerObserver : public GlyphManagerObserver { +public: + void onGlyphsLoaded(const FontStack& fontStack, const GlyphRange& glyphRange) override { + if (glyphsLoaded) glyphsLoaded(fontStack, glyphRange); + } + + void onGlyphsError(const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) override { + if (glyphsError) glyphsError(fontStack, glyphRange, error); + } + + std::function<void (const FontStack&, const GlyphRange&)> glyphsLoaded; + std::function<void (const FontStack&, const GlyphRange&, std::exception_ptr)> glyphsError; +}; + class StubGlyphRequestor : public GlyphRequestor { public: - void onGlyphsAvailable(GlyphPositionMap positions) override { - if (glyphsAvailable) glyphsAvailable(std::move(positions)); + void onGlyphsAvailable(GlyphMap glyphs) override { + if (glyphsAvailable) glyphsAvailable(std::move(glyphs)); } - std::function<void (GlyphPositionMap)> glyphsAvailable; + std::function<void (GlyphMap)> glyphsAvailable; }; -class GlyphAtlasTest { +class GlyphManagerTest { public: util::RunLoop loop; StubFileSource fileSource; - StubStyleObserver observer; + StubGlyphManagerObserver observer; StubGlyphRequestor requestor; - GlyphAtlas glyphAtlas{ { 32, 32 }, fileSource }; + GlyphManager glyphManager { fileSource }; void run(const std::string& url, GlyphDependencies dependencies) { // Squelch logging. Log::setObserver(std::make_unique<Log::NullObserver>()); - glyphAtlas.setURL(url); - glyphAtlas.setObserver(&observer); - glyphAtlas.getGlyphs(requestor, std::move(dependencies)); + glyphManager.setURL(url); + glyphManager.setObserver(&observer); + glyphManager.getGlyphs(requestor, std::move(dependencies)); loop.run(); } @@ -43,8 +56,8 @@ public: } }; -TEST(GlyphAtlas, LoadingSuccess) { - GlyphAtlasTest test; +TEST(GlyphManager, LoadingSuccess) { + GlyphManagerTest test; test.fileSource.glyphsResponse = [&] (const Resource& resource) { EXPECT_EQ(Resource::Kind::Glyphs, resource.kind); @@ -63,8 +76,8 @@ TEST(GlyphAtlas, LoadingSuccess) { ASSERT_EQ(range, GlyphRange(0, 255)); }; - test.requestor.glyphsAvailable = [&] (GlyphPositionMap positions) { - const auto& testPositions = positions.at({{"Test Stack"}}); + test.requestor.glyphsAvailable = [&] (GlyphMap glyphs) { + const auto& testPositions = glyphs.at({{"Test Stack"}}); ASSERT_EQ(testPositions.size(), 3u); ASSERT_EQ(testPositions.count(u'a'), 1u); @@ -82,8 +95,8 @@ TEST(GlyphAtlas, LoadingSuccess) { }); } -TEST(GlyphAtlas, LoadingFail) { - GlyphAtlasTest test; +TEST(GlyphManager, LoadingFail) { + GlyphManagerTest test; test.fileSource.glyphsResponse = [&] (const Resource&) { Response response; @@ -103,7 +116,7 @@ TEST(GlyphAtlas, LoadingFail) { test.end(); }; - test.requestor.glyphsAvailable = [&] (GlyphPositionMap) { + test.requestor.glyphsAvailable = [&] (GlyphMap) { FAIL(); test.end(); }; @@ -115,8 +128,8 @@ TEST(GlyphAtlas, LoadingFail) { }); } -TEST(GlyphAtlas, LoadingCorrupted) { - GlyphAtlasTest test; +TEST(GlyphManager, LoadingCorrupted) { + GlyphManagerTest test; test.fileSource.glyphsResponse = [&] (const Resource&) { Response response; @@ -134,7 +147,7 @@ TEST(GlyphAtlas, LoadingCorrupted) { test.end(); }; - test.requestor.glyphsAvailable = [&] (GlyphPositionMap) { + test.requestor.glyphsAvailable = [&] (GlyphMap) { FAIL(); test.end(); }; @@ -146,8 +159,8 @@ TEST(GlyphAtlas, LoadingCorrupted) { }); } -TEST(GlyphAtlas, LoadingCancel) { - GlyphAtlasTest test; +TEST(GlyphManager, LoadingCancel) { + GlyphManagerTest test; test.fileSource.glyphsResponse = [&] (const Resource&) { test.end(); @@ -165,8 +178,8 @@ TEST(GlyphAtlas, LoadingCancel) { }); } -TEST(GlyphAtlas, LoadingInvalid) { - GlyphAtlasTest test; +TEST(GlyphManager, LoadingInvalid) { + GlyphManagerTest test; test.fileSource.glyphsResponse = [&] (const Resource& resource) { EXPECT_EQ(Resource::Kind::Glyphs, resource.kind); @@ -185,8 +198,8 @@ TEST(GlyphAtlas, LoadingInvalid) { ASSERT_EQ(range, GlyphRange(0, 255)); }; - test.requestor.glyphsAvailable = [&] (GlyphPositionMap positions) { - const auto& testPositions = positions.at({{"Test Stack"}}); + test.requestor.glyphsAvailable = [&] (GlyphMap glyphs) { + const auto& testPositions = glyphs.at({{"Test Stack"}}); ASSERT_EQ(testPositions.size(), 2u); ASSERT_FALSE(bool(testPositions.at(u'A'))); diff --git a/test/text/quads.test.cpp b/test/text/quads.test.cpp index 83fd249535..efc3912aaa 100644 --- a/test/text/quads.test.cpp +++ b/test/text/quads.test.cpp @@ -1,5 +1,5 @@ #include <mbgl/geometry/anchor.hpp> -#include <mbgl/sprite/sprite_atlas.hpp> +#include <mbgl/style/image_impl.hpp> #include <mbgl/test/util.hpp> #include <mbgl/text/quads.hpp> #include <mbgl/text/shaping.hpp> @@ -12,48 +12,42 @@ using namespace mbgl::style; TEST(getIconQuads, normal) { SymbolLayoutProperties::Evaluated layout; Anchor anchor(2.0, 3.0, 0.0, 0.5f, 0); - SpriteAtlasElement image = { - Rect<uint16_t>( 0, 0, 15, 11 ), - style::Image(PremultipliedImage({1,1}), 1.0), - { 0, 0 }, - 1.0f + ImagePosition image = { + mapbox::Bin(-1, 15, 11, 0, 0), + style::Image::Impl("test", PremultipliedImage({1,1}), 1.0) }; auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -6.5f, -4.5f }}, 0); - ASSERT_TRUE(shapedIcon); GeometryCoordinates line; Shaping shapedText; SymbolQuad quad = - getIconQuad(anchor, *shapedIcon, line, layout, 16.0f, SymbolPlacementType::Point, shapedText); - - ASSERT_EQ(quad.anchorPoint.x, 2); - ASSERT_EQ(quad.anchorPoint.y, 3); - ASSERT_EQ(quad.tl.x, -8); - ASSERT_EQ(quad.tl.y, -6); - ASSERT_EQ(quad.tr.x, 7); - ASSERT_EQ(quad.tr.y, -6); - ASSERT_EQ(quad.bl.x, -8); - ASSERT_EQ(quad.bl.y, 5); - ASSERT_EQ(quad.br.x, 7); - ASSERT_EQ(quad.br.y, 5); - ASSERT_EQ(quad.anchorAngle, 0.0f); - ASSERT_EQ(quad.glyphAngle, 0.0f); - ASSERT_EQ(quad.minScale, 0.5f); + getIconQuad(anchor, shapedIcon, line, layout, 16.0f, SymbolPlacementType::Point, shapedText); + + EXPECT_EQ(quad.anchorPoint.x, 2); + EXPECT_EQ(quad.anchorPoint.y, 3); + EXPECT_EQ(quad.tl.x, -14); + EXPECT_EQ(quad.tl.y, -10); + EXPECT_EQ(quad.tr.x, 1); + EXPECT_EQ(quad.tr.y, -10); + EXPECT_EQ(quad.bl.x, -14); + EXPECT_EQ(quad.bl.y, 1); + EXPECT_EQ(quad.br.x, 1); + EXPECT_EQ(quad.br.y, 1); + EXPECT_EQ(quad.anchorAngle, 0.0f); + EXPECT_EQ(quad.glyphAngle, 0.0f); + EXPECT_EQ(quad.minScale, 0.5f); } TEST(getIconQuads, style) { Anchor anchor(0.0, 0.0, 0.0, 0.5f, 0); - SpriteAtlasElement image = { - Rect<uint16_t>( 0, 0, 20, 20 ), - style::Image(PremultipliedImage({1,1}), 1.0), - { 0, 0 }, - 1.0f + ImagePosition image = { + mapbox::Bin(-1, 20, 20, 0, 0), + style::Image::Impl("test", PremultipliedImage({1,1}), 1.0) }; auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, 0); - ASSERT_TRUE(shapedIcon); GeometryCoordinates line; Shaping shapedText; @@ -67,21 +61,21 @@ TEST(getIconQuads, style) { { SymbolLayoutProperties::Evaluated layout; SymbolQuad quad = - getIconQuad(anchor, *shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText); - - ASSERT_EQ(quad.anchorPoint.x, 0); - ASSERT_EQ(quad.anchorPoint.y, 0); - ASSERT_EQ(quad.tl.x, -11); - ASSERT_EQ(quad.tl.y, -11); - ASSERT_EQ(quad.tr.x, 9); - ASSERT_EQ(quad.tr.y, -11); - ASSERT_EQ(quad.bl.x, -11); - ASSERT_EQ(quad.bl.y, 9); - ASSERT_EQ(quad.br.x, 9); - ASSERT_EQ(quad.br.y, 9); - ASSERT_EQ(quad.anchorAngle, 0.0f); - ASSERT_EQ(quad.glyphAngle, 0.0f); - ASSERT_EQ(quad.minScale, 0.5f); + getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText); + + EXPECT_EQ(quad.anchorPoint.x, 0); + EXPECT_EQ(quad.anchorPoint.y, 0); + EXPECT_EQ(quad.tl.x, -19.5); + EXPECT_EQ(quad.tl.y, -19.5); + EXPECT_EQ(quad.tr.x, 0.5); + EXPECT_EQ(quad.tr.y, -19.5); + EXPECT_EQ(quad.bl.x, -19.5); + EXPECT_EQ(quad.bl.y, 0.5); + EXPECT_EQ(quad.br.x, 0.5); + EXPECT_EQ(quad.br.y, 0.5); + EXPECT_EQ(quad.anchorAngle, 0.0f); + EXPECT_EQ(quad.glyphAngle, 0.0f); + EXPECT_EQ(quad.minScale, 0.5f); } // width @@ -90,16 +84,16 @@ TEST(getIconQuads, style) { layout.get<TextSize>() = 24.0f; layout.get<IconTextFit>() = IconTextFitType::Width; SymbolQuad quad = - getIconQuad(anchor, *shapedIcon, line, layout, 24.0f, SymbolPlacementType::Point, shapedText); - - ASSERT_EQ(quad.tl.x, -60); - ASSERT_EQ(quad.tl.y, 0); - ASSERT_EQ(quad.tr.x, 20); - ASSERT_EQ(quad.tr.y, 0); - ASSERT_EQ(quad.bl.x, -60); - ASSERT_EQ(quad.bl.y, 20); - ASSERT_EQ(quad.br.x, 20); - ASSERT_EQ(quad.br.y, 20); + getIconQuad(anchor, shapedIcon, line, layout, 24.0f, SymbolPlacementType::Point, shapedText); + + EXPECT_EQ(quad.tl.x, -60); + EXPECT_EQ(quad.tl.y, 0); + EXPECT_EQ(quad.tr.x, 20); + EXPECT_EQ(quad.tr.y, 0); + EXPECT_EQ(quad.bl.x, -60); + EXPECT_EQ(quad.bl.y, 20); + EXPECT_EQ(quad.br.x, 20); + EXPECT_EQ(quad.br.y, 20); } // width x textSize @@ -108,16 +102,16 @@ TEST(getIconQuads, style) { layout.get<TextSize>() = 12.0f; layout.get<IconTextFit>() = IconTextFitType::Width; SymbolQuad quad = - getIconQuad(anchor, *shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText); - - ASSERT_EQ(quad.tl.x, -30); - ASSERT_EQ(quad.tl.y, -5); - ASSERT_EQ(quad.tr.x, 10); - ASSERT_EQ(quad.tr.y, -5); - ASSERT_EQ(quad.bl.x, -30); - ASSERT_EQ(quad.bl.y, 15); - ASSERT_EQ(quad.br.x, 10); - ASSERT_EQ(quad.br.y, 15); + getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText); + + EXPECT_EQ(quad.tl.x, -30); + EXPECT_EQ(quad.tl.y, -5); + EXPECT_EQ(quad.tr.x, 10); + EXPECT_EQ(quad.tr.y, -5); + EXPECT_EQ(quad.bl.x, -30); + EXPECT_EQ(quad.bl.y, 15); + EXPECT_EQ(quad.br.x, 10); + EXPECT_EQ(quad.br.y, 15); } // width x textSize + padding @@ -130,16 +124,16 @@ TEST(getIconQuads, style) { layout.get<IconTextFitPadding>()[2] = 5.0f; layout.get<IconTextFitPadding>()[3] = 10.0f; SymbolQuad quad = - getIconQuad(anchor, *shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText); - - ASSERT_EQ(quad.tl.x, -40); - ASSERT_EQ(quad.tl.y, -10); - ASSERT_EQ(quad.tr.x, 20); - ASSERT_EQ(quad.tr.y, -10); - ASSERT_EQ(quad.bl.x, -40); - ASSERT_EQ(quad.bl.y, 20); - ASSERT_EQ(quad.br.x, 20); - ASSERT_EQ(quad.br.y, 20); + getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText); + + EXPECT_EQ(quad.tl.x, -40); + EXPECT_EQ(quad.tl.y, -10); + EXPECT_EQ(quad.tr.x, 20); + EXPECT_EQ(quad.tr.y, -10); + EXPECT_EQ(quad.bl.x, -40); + EXPECT_EQ(quad.bl.y, 20); + EXPECT_EQ(quad.br.x, 20); + EXPECT_EQ(quad.br.y, 20); } // height @@ -148,16 +142,16 @@ TEST(getIconQuads, style) { layout.get<TextSize>() = 24.0f; layout.get<IconTextFit>() = IconTextFitType::Height; SymbolQuad quad = - getIconQuad(anchor, *shapedIcon, line, layout, 24.0f, SymbolPlacementType::Point, shapedText); - - ASSERT_EQ(quad.tl.x, -30); - ASSERT_EQ(quad.tl.y, -10); - ASSERT_EQ(quad.tr.x, -10); - ASSERT_EQ(quad.tr.y, -10); - ASSERT_EQ(quad.bl.x, -30); - ASSERT_EQ(quad.bl.y, 30); - ASSERT_EQ(quad.br.x, -10); - ASSERT_EQ(quad.br.y, 30); + getIconQuad(anchor, shapedIcon, line, layout, 24.0f, SymbolPlacementType::Point, shapedText); + + EXPECT_EQ(quad.tl.x, -30); + EXPECT_EQ(quad.tl.y, -10); + EXPECT_EQ(quad.tr.x, -10); + EXPECT_EQ(quad.tr.y, -10); + EXPECT_EQ(quad.bl.x, -30); + EXPECT_EQ(quad.bl.y, 30); + EXPECT_EQ(quad.br.x, -10); + EXPECT_EQ(quad.br.y, 30); } // height x textSize @@ -166,16 +160,16 @@ TEST(getIconQuads, style) { layout.get<TextSize>() = 12.0f; layout.get<IconTextFit>() = IconTextFitType::Height; SymbolQuad quad = - getIconQuad(anchor, *shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText); - - ASSERT_EQ(quad.tl.x, -20); - ASSERT_EQ(quad.tl.y, -5); - ASSERT_EQ(quad.tr.x, 0); - ASSERT_EQ(quad.tr.y, -5); - ASSERT_EQ(quad.bl.x, -20); - ASSERT_EQ(quad.bl.y, 15); - ASSERT_EQ(quad.br.x, 0); - ASSERT_EQ(quad.br.y, 15); + getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText); + + EXPECT_EQ(quad.tl.x, -20); + EXPECT_EQ(quad.tl.y, -5); + EXPECT_EQ(quad.tr.x, 0); + EXPECT_EQ(quad.tr.y, -5); + EXPECT_EQ(quad.bl.x, -20); + EXPECT_EQ(quad.bl.y, 15); + EXPECT_EQ(quad.br.x, 0); + EXPECT_EQ(quad.br.y, 15); } // height x textSize + padding @@ -188,16 +182,16 @@ TEST(getIconQuads, style) { layout.get<IconTextFitPadding>()[2] = 5.0f; layout.get<IconTextFitPadding>()[3] = 10.0f; SymbolQuad quad = - getIconQuad(anchor, *shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText); - - ASSERT_EQ(quad.tl.x, -30); - ASSERT_EQ(quad.tl.y, -10); - ASSERT_EQ(quad.tr.x, 10); - ASSERT_EQ(quad.tr.y, -10); - ASSERT_EQ(quad.bl.x, -30); - ASSERT_EQ(quad.bl.y, 20); - ASSERT_EQ(quad.br.x, 10); - ASSERT_EQ(quad.br.y, 20); + getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText); + + EXPECT_EQ(quad.tl.x, -30); + EXPECT_EQ(quad.tl.y, -10); + EXPECT_EQ(quad.tr.x, 10); + EXPECT_EQ(quad.tr.y, -10); + EXPECT_EQ(quad.bl.x, -30); + EXPECT_EQ(quad.bl.y, 20); + EXPECT_EQ(quad.br.x, 10); + EXPECT_EQ(quad.br.y, 20); } // both @@ -206,16 +200,16 @@ TEST(getIconQuads, style) { layout.get<TextSize>() = 24.0f; layout.get<IconTextFit>() = IconTextFitType::Both; SymbolQuad quad = - getIconQuad(anchor, *shapedIcon, line, layout, 24.0f, SymbolPlacementType::Point, shapedText); - - ASSERT_EQ(quad.tl.x, -60); - ASSERT_EQ(quad.tl.y, -10); - ASSERT_EQ(quad.tr.x, 20); - ASSERT_EQ(quad.tr.y, -10); - ASSERT_EQ(quad.bl.x, -60); - ASSERT_EQ(quad.bl.y, 30); - ASSERT_EQ(quad.br.x, 20); - ASSERT_EQ(quad.br.y, 30); + getIconQuad(anchor, shapedIcon, line, layout, 24.0f, SymbolPlacementType::Point, shapedText); + + EXPECT_EQ(quad.tl.x, -60); + EXPECT_EQ(quad.tl.y, -10); + EXPECT_EQ(quad.tr.x, 20); + EXPECT_EQ(quad.tr.y, -10); + EXPECT_EQ(quad.bl.x, -60); + EXPECT_EQ(quad.bl.y, 30); + EXPECT_EQ(quad.br.x, 20); + EXPECT_EQ(quad.br.y, 30); } // both x textSize @@ -224,16 +218,16 @@ TEST(getIconQuads, style) { layout.get<TextSize>() = 12.0f; layout.get<IconTextFit>() = IconTextFitType::Both; SymbolQuad quad = - getIconQuad(anchor, *shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText); - - ASSERT_EQ(quad.tl.x, -30); - ASSERT_EQ(quad.tl.y, -5); - ASSERT_EQ(quad.tr.x, 10); - ASSERT_EQ(quad.tr.y, -5); - ASSERT_EQ(quad.bl.x, -30); - ASSERT_EQ(quad.bl.y, 15); - ASSERT_EQ(quad.br.x, 10); - ASSERT_EQ(quad.br.y, 15); + getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText); + + EXPECT_EQ(quad.tl.x, -30); + EXPECT_EQ(quad.tl.y, -5); + EXPECT_EQ(quad.tr.x, 10); + EXPECT_EQ(quad.tr.y, -5); + EXPECT_EQ(quad.bl.x, -30); + EXPECT_EQ(quad.bl.y, 15); + EXPECT_EQ(quad.br.x, 10); + EXPECT_EQ(quad.br.y, 15); } // both x textSize + padding @@ -246,16 +240,16 @@ TEST(getIconQuads, style) { layout.get<IconTextFitPadding>()[2] = 5.0f; layout.get<IconTextFitPadding>()[3] = 10.0f; SymbolQuad quad = - getIconQuad(anchor, *shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText); - - ASSERT_EQ(quad.tl.x, -40); - ASSERT_EQ(quad.tl.y, -10); - ASSERT_EQ(quad.tr.x, 20); - ASSERT_EQ(quad.tr.y, -10); - ASSERT_EQ(quad.bl.x, -40); - ASSERT_EQ(quad.bl.y, 20); - ASSERT_EQ(quad.br.x, 20); - ASSERT_EQ(quad.br.y, 20); + getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText); + + EXPECT_EQ(quad.tl.x, -40); + EXPECT_EQ(quad.tl.y, -10); + EXPECT_EQ(quad.tr.x, 20); + EXPECT_EQ(quad.tr.y, -10); + EXPECT_EQ(quad.bl.x, -40); + EXPECT_EQ(quad.bl.y, 20); + EXPECT_EQ(quad.br.x, 20); + EXPECT_EQ(quad.br.y, 20); } // both x textSize + padding t/r/b/l @@ -268,16 +262,16 @@ TEST(getIconQuads, style) { layout.get<IconTextFitPadding>()[2] = 10.0f; layout.get<IconTextFitPadding>()[3] = 15.0f; SymbolQuad quad = - getIconQuad(anchor, *shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText); - - ASSERT_EQ(quad.tl.x, -45); - ASSERT_EQ(quad.tl.y, -5); - ASSERT_EQ(quad.tr.x, 15); - ASSERT_EQ(quad.tr.y, -5); - ASSERT_EQ(quad.bl.x, -45); - ASSERT_EQ(quad.bl.y, 25); - ASSERT_EQ(quad.br.x, 15); - ASSERT_EQ(quad.br.y, 25); + getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText); + + EXPECT_EQ(quad.tl.x, -45); + EXPECT_EQ(quad.tl.y, -5); + EXPECT_EQ(quad.tr.x, 15); + EXPECT_EQ(quad.tr.y, -5); + EXPECT_EQ(quad.bl.x, -45); + EXPECT_EQ(quad.bl.y, 25); + EXPECT_EQ(quad.br.x, 15); + EXPECT_EQ(quad.br.y, 25); } } diff --git a/test/tile/annotation_tile.test.cpp b/test/tile/annotation_tile.test.cpp index 4d71c5b0b4..6d00a3236a 100644 --- a/test/tile/annotation_tile.test.cpp +++ b/test/tile/annotation_tile.test.cpp @@ -5,13 +5,17 @@ #include <mbgl/util/run_loop.hpp> #include <mbgl/map/transform.hpp> #include <mbgl/map/query.hpp> -#include <mbgl/style/style.hpp> +#include <mbgl/renderer/render_style.hpp> #include <mbgl/renderer/tile_parameters.hpp> #include <mbgl/map/query.hpp> #include <mbgl/text/collision_tile.hpp> #include <mbgl/geometry/feature_index.hpp> #include <mbgl/annotation/annotation_manager.hpp> #include <mbgl/annotation/annotation_tile.hpp> +#include <mbgl/renderer/image_manager.hpp> +#include <mbgl/text/glyph_manager.hpp> +#include <mbgl/map/backend_scope.hpp> +#include <mbgl/gl/headless_backend.hpp> #include <memory> @@ -23,8 +27,12 @@ public: TransformState transformState; util::RunLoop loop; ThreadPool threadPool { 1 }; - AnnotationManager annotationManager { 1.0 }; - style::Style style { threadPool, fileSource, 1.0 }; + AnnotationManager annotationManager; + HeadlessBackend backend { test::sharedDisplay() }; + BackendScope scope { backend }; + RenderStyle style { threadPool, fileSource }; + ImageManager imageManager; + GlyphManager glyphManager { fileSource }; TileParameters tileParameters { 1.0, @@ -34,7 +42,8 @@ public: fileSource, MapMode::Continuous, annotationManager, - style + imageManager, + glyphManager }; }; @@ -44,15 +53,14 @@ TEST(AnnotationTile, Issue8289) { AnnotationTile tile(OverscaledTileID(0, 0, 0), test.tileParameters); auto data = std::make_unique<AnnotationTileData>(); - data->layers.emplace("test", AnnotationTileLayer("test")); - data->layers.at("test").features.push_back(AnnotationTileFeature(0, FeatureType::Point, GeometryCollection())); + data->addLayer("test")->addFeature(0, FeatureType::Point, GeometryCollection()); // Simulate layout and placement of a symbol layer. tile.onLayout(GeometryTile::LayoutResult { {}, - std::make_unique<FeatureIndex>(), - std::move(data), - 0 + std::make_unique<FeatureIndex>(), + std::move(data), + 0 }); auto collisionTile = std::make_unique<CollisionTile>(PlacementConfig()); @@ -64,16 +72,18 @@ TEST(AnnotationTile, Issue8289) { tile.onPlacement(GeometryTile::PlacementResult { {}, - std::move(collisionTile), - 0 + std::move(collisionTile), + {}, + {}, + 0 }); // Simulate a second layout with empty data. tile.onLayout(GeometryTile::LayoutResult { {}, - std::make_unique<FeatureIndex>(), - std::make_unique<AnnotationTileData>(), - 0 + std::make_unique<FeatureIndex>(), + std::make_unique<AnnotationTileData>(), + 0 }); std::unordered_map<std::string, std::vector<Feature>> result; @@ -81,7 +91,7 @@ TEST(AnnotationTile, Issue8289) { TransformState transformState; RenderedQueryOptions options; - tile.queryRenderedFeatures(result, queryGeometry, transformState, options); + tile.queryRenderedFeatures(result, queryGeometry, transformState, test.style, options); EXPECT_TRUE(result.empty()); } diff --git a/test/tile/geojson_tile.test.cpp b/test/tile/geojson_tile.test.cpp index a0383f06c9..2aa85c3860 100644 --- a/test/tile/geojson_tile.test.cpp +++ b/test/tile/geojson_tile.test.cpp @@ -7,10 +7,11 @@ #include <mbgl/util/default_thread_pool.hpp> #include <mbgl/util/run_loop.hpp> #include <mbgl/map/transform.hpp> -#include <mbgl/style/style.hpp> #include <mbgl/renderer/tile_parameters.hpp> #include <mbgl/style/layers/circle_layer.hpp> #include <mbgl/annotation/annotation_manager.hpp> +#include <mbgl/renderer/image_manager.hpp> +#include <mbgl/text/glyph_manager.hpp> #include <memory> @@ -23,8 +24,9 @@ public: TransformState transformState; util::RunLoop loop; ThreadPool threadPool { 1 }; - AnnotationManager annotationManager { 1.0 }; - style::Style style { threadPool, fileSource, 1.0 }; + AnnotationManager annotationManager; + ImageManager imageManager; + GlyphManager glyphManager { fileSource }; Tileset tileset { { "https://example.com" }, { 0, 22 }, "none" }; TileParameters tileParameters { @@ -35,14 +37,15 @@ public: fileSource, MapMode::Continuous, annotationManager, - style + imageManager, + glyphManager }; }; TEST(GeoJSONTile, Issue7648) { GeoJSONTileTest test; - test.style.addLayer(std::make_unique<CircleLayer>("circle", "source")); + CircleLayer layer("circle", "source"); mapbox::geometry::feature_collection<int16_t> features; features.push_back(mapbox::geometry::feature<int16_t> { @@ -55,9 +58,10 @@ TEST(GeoJSONTile, Issue7648) { observer.tileChanged = [&] (const Tile&) { // Once present, the bucket should never "disappear", which would cause // flickering. - ASSERT_NE(nullptr, tile.getBucket(*test.style.getRenderLayer("circle"))); + ASSERT_NE(nullptr, tile.getBucket(*layer.baseImpl)); }; + tile.setLayers({{ layer.baseImpl }}); tile.setObserver(&observer); tile.setPlacementConfig({}); diff --git a/test/tile/raster_tile.test.cpp b/test/tile/raster_tile.test.cpp index f841a82e68..a0666c2146 100644 --- a/test/tile/raster_tile.test.cpp +++ b/test/tile/raster_tile.test.cpp @@ -6,10 +6,11 @@ #include <mbgl/util/default_thread_pool.hpp> #include <mbgl/util/run_loop.hpp> #include <mbgl/map/transform.hpp> -#include <mbgl/style/style.hpp> #include <mbgl/annotation/annotation_manager.hpp> #include <mbgl/renderer/tile_parameters.hpp> -#include <mbgl/renderer/raster_bucket.hpp> +#include <mbgl/renderer/buckets/raster_bucket.hpp> +#include <mbgl/renderer/image_manager.hpp> +#include <mbgl/text/glyph_manager.hpp> using namespace mbgl; @@ -19,8 +20,9 @@ public: TransformState transformState; util::RunLoop loop; ThreadPool threadPool { 1 }; - AnnotationManager annotationManager { 1.0 }; - style::Style style { threadPool, fileSource, 1.0 }; + AnnotationManager annotationManager; + ImageManager imageManager; + GlyphManager glyphManager { fileSource }; Tileset tileset { { "https://example.com" }, { 0, 22 }, "none" }; TileParameters tileParameters { @@ -31,7 +33,8 @@ public: fileSource, MapMode::Continuous, annotationManager, - style + imageManager, + glyphManager }; }; diff --git a/test/tile/vector_tile.test.cpp b/test/tile/vector_tile.test.cpp index 37bfe8512d..f24733dc9b 100644 --- a/test/tile/vector_tile.test.cpp +++ b/test/tile/vector_tile.test.cpp @@ -7,13 +7,14 @@ #include <mbgl/util/run_loop.hpp> #include <mbgl/map/transform.hpp> #include <mbgl/map/query.hpp> -#include <mbgl/style/style.hpp> #include <mbgl/style/layers/symbol_layer.hpp> #include <mbgl/renderer/tile_parameters.hpp> -#include <mbgl/renderer/symbol_bucket.hpp> +#include <mbgl/renderer/buckets/symbol_bucket.hpp> #include <mbgl/text/collision_tile.hpp> #include <mbgl/geometry/feature_index.hpp> #include <mbgl/annotation/annotation_manager.hpp> +#include <mbgl/renderer/image_manager.hpp> +#include <mbgl/text/glyph_manager.hpp> #include <memory> @@ -25,8 +26,9 @@ public: TransformState transformState; util::RunLoop loop; ThreadPool threadPool { 1 }; - AnnotationManager annotationManager { 1.0 }; - style::Style style { threadPool, fileSource, 1.0 }; + AnnotationManager annotationManager; + ImageManager imageManager; + GlyphManager glyphManager { fileSource }; Tileset tileset { { "https://example.com" }, { 0, 22 }, "none" }; TileParameters tileParameters { @@ -37,7 +39,8 @@ public: fileSource, MapMode::Continuous, annotationManager, - style + imageManager, + glyphManager }; }; @@ -68,7 +71,7 @@ TEST(VectorTile, Issue7615) { style::SymbolLayoutProperties::PossiblyEvaluated(), std::map< std::string, - std::pair<style::IconPaintProperties::Evaluated, style::TextPaintProperties::Evaluated>>(), + std::pair<style::IconPaintProperties::PossiblyEvaluated, style::TextPaintProperties::PossiblyEvaluated>>(), 16.0f, 1.0f, 0.0f, false, false); // Simulate placement of a symbol layer. @@ -78,6 +81,8 @@ TEST(VectorTile, Issue7615) { symbolBucket }}, nullptr, + {}, + {}, 0 }); @@ -89,7 +94,7 @@ TEST(VectorTile, Issue7615) { 0 }); - EXPECT_EQ(symbolBucket.get(), tile.getBucket(*symbolLayer.baseImpl->createRenderLayer())); + EXPECT_EQ(symbolBucket.get(), tile.getBucket(*symbolLayer.baseImpl)); } TEST(VectorTile, Issue8542) { diff --git a/test/util/async_task.test.cpp b/test/util/async_task.test.cpp index 78dc79dd19..f3025e8952 100644 --- a/test/util/async_task.test.cpp +++ b/test/util/async_task.test.cpp @@ -1,9 +1,12 @@ #include <mbgl/util/async_task.hpp> #include <mbgl/util/run_loop.hpp> -#include <mbgl/util/thread.hpp> +#include <mbgl/util/default_thread_pool.hpp> +#include <mbgl/actor/actor_ref.hpp> #include <mbgl/test/util.hpp> +#include <atomic> +#include <future> #include <vector> using namespace mbgl::util; @@ -29,6 +32,10 @@ public: cb(); } + void sync(std::promise<void> barrier) { + barrier.set_value(); + } + private: AsyncTask *async; }; @@ -94,23 +101,24 @@ TEST(AsyncTask, DestroyAfterSignaling) { TEST(AsyncTask, RequestCoalescingMultithreaded) { RunLoop loop; - unsigned count = 0; + unsigned count = 0, numThreads = 25; AsyncTask async([&count] { ++count; }); - std::vector<std::unique_ptr<Thread<TestWorker>>> threads; - ThreadContext context = {"Test"}; + mbgl::ThreadPool threads(numThreads); + auto mailbox = std::make_shared<mbgl::Mailbox>(threads); - unsigned numThreads = 25; - for (unsigned i = 0; i < numThreads; ++i) { - std::unique_ptr<Thread<TestWorker>> thread = - std::make_unique<Thread<TestWorker>>(context, &async); + TestWorker worker(&async); + mbgl::ActorRef<TestWorker> workerRef(worker, mailbox); - thread->invoke(&TestWorker::run); - threads.push_back(std::move(thread)); + for (unsigned i = 0; i < numThreads; ++i) { + workerRef.invoke(&TestWorker::run); } - // Join all the threads - threads.clear(); + std::promise<void> barrier; + std::future<void> barrierFuture = barrier.get_future(); + + workerRef.invoke(&TestWorker::sync, std::move(barrier)); + barrierFuture.wait(); loop.runOnce(); @@ -120,29 +128,20 @@ TEST(AsyncTask, RequestCoalescingMultithreaded) { TEST(AsyncTask, ThreadSafety) { RunLoop loop; - unsigned count = 0; - AsyncTask async([&count] { ++count; }); + unsigned count = 0, numThreads = 25; + std::atomic_uint completed(numThreads); - unsigned numThreads = 25; + AsyncTask async([&count] { ++count; }); - auto callback = [&] { - if (!--numThreads) { - loop.stop(); - } - }; + mbgl::ThreadPool threads(numThreads); + auto mailbox = std::make_shared<mbgl::Mailbox>(threads); - std::vector<std::unique_ptr<Thread<TestWorker>>> threads; - std::vector<std::unique_ptr<mbgl::AsyncRequest>> requests; - ThreadContext context = {"Test"}; + TestWorker worker(&async); + mbgl::ActorRef<TestWorker> workerRef(worker, mailbox); for (unsigned i = 0; i < numThreads; ++i) { - std::unique_ptr<Thread<TestWorker>> thread = - std::make_unique<Thread<TestWorker>>(context, &async); - - requests.push_back( - thread->invokeWithCallback(&TestWorker::runWithCallback, callback)); - - threads.push_back(std::move(thread)); + // The callback runs on the worker, thus the atomic type. + workerRef.invoke(&TestWorker::runWithCallback, [&] { if (!--completed) loop.stop(); }); } loop.run(); diff --git a/test/util/image.test.cpp b/test/util/image.test.cpp index 4cacf89253..f4a6473040 100644 --- a/test/util/image.test.cpp +++ b/test/util/image.test.cpp @@ -86,33 +86,49 @@ TEST(Image, WebPTile) { } #endif // !defined(__ANDROID__) && !defined(__APPLE__) && !defined(QT_IMAGE_DECODERS) +TEST(Image, Resize) { + AlphaImage image({0, 0}); + + image.resize({1, 1}); + EXPECT_EQ(image.size, Size({1, 1})); + + image.fill(100); + image.resize({2, 1}); + EXPECT_EQ(image.size, Size({2, 1})); + EXPECT_EQ(image.data[0], 100); + EXPECT_EQ(image.data[1], 0); + + image.resize({0, 0}); + EXPECT_EQ(image.size, Size({0, 0})); +} + TEST(Image, Copy) { PremultipliedImage src5({5, 5}); PremultipliedImage dst5({5, 5}); PremultipliedImage src10({10, 10}); PremultipliedImage dst10({10, 10}); - EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {0, 0}, {0, 0}, {6, 0}), std::out_of_range); - EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {0, 0}, {0, 0}, {0, 6}), std::out_of_range); - EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {1, 1}, {0, 0}, {5, 0}), std::out_of_range); - EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {1, 1}, {0, 0}, {0, 5}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {0, 0}, {0, 0}, {6, 1}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {0, 0}, {0, 0}, {1, 6}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {1, 1}, {0, 0}, {5, 1}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {1, 1}, {0, 0}, {1, 5}), std::out_of_range); - EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {0, 0}, {6, 0}), std::out_of_range); - EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {0, 0}, {0, 6}), std::out_of_range); - EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {1, 1}, {5, 0}), std::out_of_range); - EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {1, 1}, {0, 5}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {0, 0}, {6, 1}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {0, 0}, {1, 6}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {1, 1}, {5, 1}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {1, 1}, {1, 5}), std::out_of_range); const uint32_t max = std::numeric_limits<uint32_t>::max(); - EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {max, 0}, {0, 0}, {1, 0}), std::out_of_range); - EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, max}, {0, 0}, {0, 1}), std::out_of_range); - EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {max, 0}, {1, 0}), std::out_of_range); - EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {0, max}, {0, 1}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {max, 0}, {0, 0}, {1, 1}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, max}, {0, 0}, {1, 1}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {max, 0}, {1, 1}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {0, max}, {1, 1}), std::out_of_range); - EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {1, 0}, {0, 0}, {max, 0}), std::out_of_range); - EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 1}, {0, 0}, {0, max}), std::out_of_range); - EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {1, 0}, {max, 0}), std::out_of_range); - EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {0, 1}, {0, max}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {1, 0}, {0, 0}, {max, 1}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 1}, {0, 0}, {1, max}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {1, 0}, {max, 1}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {0, 1}, {1, max}), std::out_of_range); } TEST(Image, Move) { @@ -142,4 +158,8 @@ TEST(Image, Premultiply) { EXPECT_EQ(127, image.data[1]); EXPECT_EQ(127, image.data[2]); EXPECT_EQ(128, image.data[3]); + EXPECT_EQ(1u, image.size.width); + EXPECT_EQ(1u, image.size.height); + EXPECT_EQ(0u, rgba.size.width); + EXPECT_EQ(0u, rgba.size.height); } diff --git a/test/util/memory.test.cpp b/test/util/memory.test.cpp index 065d024bef..bca538c6c6 100644 --- a/test/util/memory.test.cpp +++ b/test/util/memory.test.cpp @@ -9,6 +9,7 @@ #include <mbgl/util/default_thread_pool.hpp> #include <mbgl/util/io.hpp> #include <mbgl/util/run_loop.hpp> +#include <mbgl/style/style.hpp> #include <algorithm> #include <iostream> @@ -17,7 +18,7 @@ #include <unordered_map> #include <utility> -#include <stdlib.h> +#include <cstdlib> #include <unistd.h> using namespace mbgl; @@ -75,7 +76,7 @@ TEST(Memory, Vector) { Map map(test.backend, { 256, 256 }, 2, test.fileSource, test.threadPool, MapMode::Still); map.setZoom(16); // more map features - map.setStyleURL("mapbox://streets"); + map.getStyle().loadURL("mapbox://streets"); test::render(map, test.view); } @@ -84,7 +85,7 @@ TEST(Memory, Raster) { MemoryTest test; Map map(test.backend, { 256, 256 }, 2, test.fileSource, test.threadPool, MapMode::Still); - map.setStyleURL("mapbox://satellite"); + map.getStyle().loadURL("mapbox://satellite"); test::render(map, test.view); } @@ -118,7 +119,7 @@ TEST(Memory, Footprint) { auto renderMap = [&](Map& map, const char* style){ map.setZoom(16); - map.setStyleURL(style); + map.getStyle().loadURL(style); test::render(map, test.view); }; @@ -159,7 +160,7 @@ TEST(Memory, Footprint) { RecordProperty("vectorFootprint", vectorFootprint); RecordProperty("rasterFootprint", rasterFootprint); - ASSERT_LT(vectorFootprint, 65.2 * 1024 * 1024) << "\ + ASSERT_LT(vectorFootprint, 40 * 1024 * 1024) << "\ mbgl::Map footprint over 65.2MB for vector styles."; ASSERT_LT(rasterFootprint, 25 * 1024 * 1024) << "\ diff --git a/test/util/merge_lines.test.cpp b/test/util/merge_lines.test.cpp index 8a3a400887..6c8387c451 100644 --- a/test/util/merge_lines.test.cpp +++ b/test/util/merge_lines.test.cpp @@ -2,6 +2,7 @@ #include <mbgl/layout/merge_lines.hpp> #include <mbgl/layout/symbol_feature.hpp> +#include <utility> const std::u16string aaa = u"a"; const std::u16string bbb = u"b"; @@ -12,10 +13,10 @@ class GeometryTileFeatureStub : public GeometryTileFeature { public: GeometryTileFeatureStub(optional<FeatureIdentifier> id_, FeatureType type_, GeometryCollection geometry_, std::unordered_map<std::string, Value> properties_) : - id(id_), + id(std::move(id_)), type(type_), - geometry(geometry_), - properties(properties_) + geometry(std::move(geometry_)), + properties(std::move(properties_)) {} FeatureType getType() const override { return type; } diff --git a/test/util/offscreen_texture.test.cpp b/test/util/offscreen_texture.test.cpp index feaabf2630..8c0d4f7011 100644 --- a/test/util/offscreen_texture.test.cpp +++ b/test/util/offscreen_texture.test.cpp @@ -14,6 +14,11 @@ TEST(OffscreenTexture, EmptyRed) { HeadlessBackend backend { test::sharedDisplay() }; BackendScope scope { backend }; OffscreenView view(backend.getContext(), { 512, 256 }); + + // Scissor test shouldn't leak after OffscreenView::bind(). + MBGL_CHECK_ERROR(glScissor(64, 64, 128, 128)); + backend.getContext().scissorTest.setCurrentValue(true); + view.bind(); MBGL_CHECK_ERROR(glClearColor(1.0f, 0.0f, 0.0f, 1.0f)); @@ -35,7 +40,7 @@ struct Shader { MBGL_CHECK_ERROR(glCompileShader(fragmentShader)); MBGL_CHECK_ERROR(glAttachShader(program, fragmentShader)); MBGL_CHECK_ERROR(glLinkProgram(program)); - a_pos = glGetAttribLocation(program, "a_pos"); + a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos")); } ~Shader() { @@ -114,7 +119,7 @@ void main() { } )MBGL_SHADER"); - GLuint u_texture = glGetUniformLocation(compositeShader.program, "u_texture"); + GLuint u_texture = MBGL_CHECK_ERROR(glGetUniformLocation(compositeShader.program, "u_texture")); Buffer triangleBuffer({ 0, 0.5, 0.5, -0.5, -0.5, -0.5 }); Buffer viewportBuffer({ -1, -1, 1, -1, -1, 1, 1, 1 }); @@ -128,6 +133,11 @@ void main() { // Then, create a texture, bind it, and render yellow to that texture. This should not // affect the originally bound FBO. OffscreenTexture texture(context, { 128, 128 }); + + // Scissor test shouldn't leak after OffscreenTexture::bind(). + MBGL_CHECK_ERROR(glScissor(32, 32, 64, 64)); + context.scissorTest.setCurrentValue(true); + texture.bind(); context.clear(Color(), {}, {}); diff --git a/test/util/thread.test.cpp b/test/util/thread.test.cpp index 972bddf383..76fb5ce3f0 100644 --- a/test/util/thread.test.cpp +++ b/test/util/thread.test.cpp @@ -1,63 +1,58 @@ -#include <mbgl/util/thread.hpp> -#include <mbgl/util/run_loop.hpp> - +#include <mbgl/actor/actor_ref.hpp> #include <mbgl/test/util.hpp> +#include <mbgl/util/default_thread_pool.hpp> +#include <mbgl/util/run_loop.hpp> +#include <mbgl/util/thread.hpp> +#include <mbgl/util/timer.hpp> #include <atomic> +#include <memory> +using namespace mbgl; using namespace mbgl::util; class TestObject { public: - TestObject(std::thread::id otherTid) + TestObject(ActorRef<TestObject>, std::thread::id otherTid) : tid(std::this_thread::get_id()) { EXPECT_NE(tid, otherTid); } - void fn1(int val) { + ~TestObject() { EXPECT_EQ(tid, std::this_thread::get_id()); - EXPECT_EQ(val, 1); } - void fn2(std::function<void (int)> cb) { + void fn1(int val) const { EXPECT_EQ(tid, std::this_thread::get_id()); - cb(1); - } - - void transferIn(std::unique_ptr<int> val) { - EXPECT_EQ(tid, std::this_thread::get_id()); - EXPECT_EQ(*val, 1); + EXPECT_EQ(val, 1); } - void transferOut(std::function<void (std::unique_ptr<int>)> cb) { + void fn2(std::function<void (int)> cb) const { EXPECT_EQ(tid, std::this_thread::get_id()); - cb(std::make_unique<int>(1)); + cb(1); } - void transferInOut(std::unique_ptr<int> val, std::function<void (std::unique_ptr<int>)> cb) { + void transferIn(std::unique_ptr<int> val) const { EXPECT_EQ(tid, std::this_thread::get_id()); EXPECT_EQ(*val, 1); - cb(std::move(val)); } - void transferInShared(std::shared_ptr<int> val) { + void transferInShared(std::shared_ptr<int> val) const { EXPECT_EQ(tid, std::this_thread::get_id()); EXPECT_EQ(*val, 1); } - void transferOutShared(std::function<void (std::shared_ptr<int>)> cb) { + void transferString(const std::string& string) const { EXPECT_EQ(tid, std::this_thread::get_id()); - cb(std::make_shared<int>(1)); + EXPECT_EQ(string, "test"); } - void transferString(const std::string& string, std::function<void (std::string)> cb) { - EXPECT_EQ(tid, std::this_thread::get_id()); - EXPECT_EQ(string, "test"); - cb(string); + void checkContext(std::promise<bool> result) const { + result.set_value(tid == std::this_thread::get_id()); } - void checkContext(std::function<void (bool)> cb) const { - cb(tid == std::this_thread::get_id()); + void sync(std::promise<void> result) const { + result.set_value(); } const std::thread::id tid; @@ -65,95 +60,61 @@ public: TEST(Thread, invoke) { const std::thread::id tid = std::this_thread::get_id(); + Thread<TestObject> thread("Test", tid); - RunLoop loop; - std::vector<std::unique_ptr<mbgl::AsyncRequest>> requests; + thread.actor().invoke(&TestObject::fn1, 1); + thread.actor().invoke(&TestObject::fn2, [] (int result) { EXPECT_EQ(result, 1); } ); + thread.actor().invoke(&TestObject::transferIn, std::make_unique<int>(1)); + thread.actor().invoke(&TestObject::transferInShared, std::make_shared<int>(1)); - loop.invoke([&] { - EXPECT_EQ(tid, std::this_thread::get_id()); - Thread<TestObject> thread({"Test"}, 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<int>(1)); - requests.push_back(thread.invokeWithCallback(&TestObject::transferOut, [&] (std::unique_ptr<int> result) { - EXPECT_EQ(tid, std::this_thread::get_id()); - EXPECT_EQ(*result, 1); - })); - - requests.push_back(thread.invokeWithCallback(&TestObject::transferInOut, std::make_unique<int>(1), [&] (std::unique_ptr<int> result) { - EXPECT_EQ(tid, std::this_thread::get_id()); - EXPECT_EQ(*result, 1); - })); - - thread.invoke(&TestObject::transferInShared, std::make_shared<int>(1)); - requests.push_back(thread.invokeWithCallback(&TestObject::transferOutShared, [&] (std::shared_ptr<int> 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"); + thread.actor().invoke(&TestObject::transferString, test); - std::string test("test"); - requests.push_back(thread.invokeWithCallback(&TestObject::transferString, test, [&] (std::string result){ - EXPECT_EQ(tid, std::this_thread::get_id()); - EXPECT_EQ(result, "test"); - loop.stop(); - })); - test.clear(); - }); - - loop.run(); + // Make sure the message queue was consumed before ending the test. + std::promise<void> result; + auto resultFuture = result.get_future(); + thread.actor().invoke(&TestObject::sync, std::move(result)); + resultFuture.get(); } -TEST(Thread, context) { +TEST(Thread, Context) { const std::thread::id tid = std::this_thread::get_id(); + Thread<TestObject> thread("Test", tid); - RunLoop loop; - std::vector<std::unique_ptr<mbgl::AsyncRequest>> requests; - - loop.invoke([&] { - Thread<TestObject> thread({"Test"}, tid); - - requests.push_back(thread.invokeWithCallback(&TestObject::checkContext, [&] (bool inTestThreadContext) { - EXPECT_EQ(inTestThreadContext, true); - loop.stop(); - })); - }); + std::promise<bool> result; + auto resultFuture = result.get_future(); - loop.run(); + thread.actor().invoke(&TestObject::checkContext, std::move(result)); + EXPECT_EQ(resultFuture.get(), true); } class TestWorker { public: - TestWorker() = default; + TestWorker(ActorRef<TestWorker>) {} - void send(std::function<void ()> fn, std::function<void ()> cb) { - fn(); + void send(std::function<void ()> cb) { cb(); } + + void sendDelayed(std::function<void ()> cb) { + timer.start(Milliseconds(300), mbgl::Duration::zero(), [cb] { + cb(); + }); + } + +private: + Timer timer; }; TEST(Thread, ExecutesAfter) { RunLoop loop; - Thread<TestWorker> thread({"Test"}); + Thread<TestWorker> thread("Test"); bool didWork = false; bool didAfter = false; - auto request = thread.invokeWithCallback(&TestWorker::send, [&] { - didWork = true; - }, [&] { - didAfter = true; - loop.stop(); - }); + thread.actor().invoke(&TestWorker::send, [&] { didWork = true; }); + thread.actor().invoke(&TestWorker::send, [&] { didAfter = true; loop.stop(); }); loop.run(); @@ -161,72 +122,92 @@ TEST(Thread, ExecutesAfter) { EXPECT_TRUE(didAfter); } -TEST(Thread, WorkRequestDeletionWaitsForWorkToComplete) { +TEST(Thread, CanSelfWakeUp) { RunLoop loop; + Thread<TestWorker> thread("Test"); - Thread<TestWorker> thread({"Test"}); + thread.actor().invoke(&TestWorker::sendDelayed, [&] { + loop.stop(); + }); - std::promise<void> started; - bool didWork = false; + loop.run(); +} - auto request = thread.invokeWithCallback(&TestWorker::send, [&] { - started.set_value(); - usleep(10000); - didWork = true; - }, [&] {}); +TEST(Thread, Concurrency) { + auto loop = std::make_shared<RunLoop>(); + + unsigned numMessages = 100000; + std::atomic_uint completed(numMessages); + + ThreadPool threadPool(10); + Actor<TestWorker> poolWorker(threadPool); + auto poolWorkerRef = poolWorker.self(); + + Thread<TestWorker> threadedObject("Test"); + auto threadedObjectRef = threadedObject.actor(); + + // 10 threads sending 100k messages to the Thread. The + // idea here is to test if the scheduler is handling concurrency + // correctly, otherwise this test should crash. + for (unsigned i = 0; i < numMessages; ++i) { + poolWorkerRef.invoke(&TestWorker::send, [threadedObjectRef, loop, &completed] () mutable { + threadedObjectRef.invoke(&TestWorker::send, [loop, &completed] () { + if (!--completed) { + loop->stop(); + } + }); + }); + }; - started.get_future().get(); - request.reset(); - EXPECT_TRUE(didWork); + loop->run(); } -TEST(Thread, WorkRequestDeletionCancelsAfter) { - RunLoop loop; - Thread<TestWorker> thread({"Test"}); +TEST(Thread, ThreadPoolMessaging) { + auto loop = std::make_shared<RunLoop>(); - std::promise<void> started; - bool didAfter = false; + ThreadPool threadPool(1); + Actor<TestWorker> poolWorker(threadPool); + auto poolWorkerRef = poolWorker.self(); + + Thread<TestWorker> threadedObject("Test"); + auto threadedObjectRef = threadedObject.actor(); - auto request = thread.invokeWithCallback(&TestWorker::send, [&] { - started.set_value(); - }, [&] { - didAfter = true; + // This is sending a message to the Thread from the main + // thread. Then the Thread will send another message to + // a worker on the ThreadPool. + threadedObjectRef.invoke(&TestWorker::send, [poolWorkerRef, loop] () mutable { + poolWorkerRef.invoke(&TestWorker::send, [loop] () { loop->stop(); }); }); - started.get_future().get(); - request.reset(); - loop.runOnce(); - EXPECT_FALSE(didAfter); -} + loop->run(); -TEST(Thread, WorkRequestDeletionCancelsImmediately) { - RunLoop loop; - Thread<TestWorker> thread({"Test"}); + // Same as before, but in the opposite direction. + poolWorkerRef.invoke(&TestWorker::send, [threadedObjectRef, loop] () mutable { + threadedObjectRef.invoke(&TestWorker::send, [loop] () { loop->stop(); }); + }); - std::promise<void> started; + loop->run(); +} + +TEST(Thread, ReferenceCanOutliveThread) { + auto thread = std::make_unique<Thread<TestWorker>>("Test"); + auto worker = thread->actor(); - auto request1 = thread.invokeWithCallback(&TestWorker::send, [&] { - usleep(10000); - started.set_value(); - }, [&] {}); + thread.reset(); - auto request2 = thread.invokeWithCallback(&TestWorker::send, [&] { - ADD_FAILURE() << "Second work item should not be invoked"; - }, [&] {}); - request2.reset(); + for (unsigned i = 0; i < 1000; ++i) { + worker.invoke(&TestWorker::send, [&] { ADD_FAILURE() << "Should never happen"; }); + } - started.get_future().get(); - request1.reset(); + usleep(10000); } TEST(Thread, DeletePausedThread) { - RunLoop loop; - std::atomic_bool flag(false); - auto thread = std::make_unique<Thread<TestWorker>>(ThreadContext{"Test"}); + auto thread = std::make_unique<Thread<TestWorker>>("Test"); thread->pause(); - thread->invoke(&TestWorker::send, [&] { flag = true; }, [] {}); + thread->actor().invoke(&TestWorker::send, [&] { flag = true; }); // Should not hang. thread.reset(); @@ -240,18 +221,18 @@ TEST(Thread, Pause) { std::atomic_bool flag(false); - Thread<TestWorker> thread1({"Test1"}); + Thread<TestWorker> thread1("Test1"); thread1.pause(); - Thread<TestWorker> thread2({"Test2"}); + Thread<TestWorker> thread2("Test2"); for (unsigned i = 0; i < 100; ++i) { - thread1.invoke(&TestWorker::send, [&] { flag = true; }, [] {}); - thread2.invoke(&TestWorker::send, [&] { ASSERT_FALSE(flag); }, [] {}); + thread1.actor().invoke(&TestWorker::send, [&] { flag = true; }); + thread2.actor().invoke(&TestWorker::send, [&] { ASSERT_FALSE(flag); }); } // Queue a message at the end of thread2 queue. - thread2.invoke(&TestWorker::send, [&] { loop.stop(); }, [] {}); + thread2.actor().invoke(&TestWorker::send, [&] { loop.stop(); }); loop.run(); } @@ -260,16 +241,16 @@ TEST(Thread, Resume) { std::atomic_bool flag(false); - Thread<TestWorker> thread({"Test"}); + Thread<TestWorker> thread("Test"); thread.pause(); for (unsigned i = 0; i < 100; ++i) { - thread.invoke(&TestWorker::send, [&] { flag = true; }, [] {}); + thread.actor().invoke(&TestWorker::send, [&] { flag = true; }); } // Thread messages are ondered, when we resume, this is going // to me the last thing to run on the message queue. - thread.invoke(&TestWorker::send, [&] { loop.stop(); }, [] {}); + thread.actor().invoke(&TestWorker::send, [&] { loop.stop(); }); // This test will be flaky if the thread doesn't get paused. ASSERT_FALSE(flag); @@ -283,7 +264,7 @@ TEST(Thread, Resume) { TEST(Thread, PauseResume) { RunLoop loop; - Thread<TestWorker> thread({"Test"}); + Thread<TestWorker> thread("Test"); // Test if multiple pause/resume work. for (unsigned i = 0; i < 100; ++i) { @@ -291,6 +272,6 @@ TEST(Thread, PauseResume) { thread.resume(); } - thread.invoke(&TestWorker::send, [&] { loop.stop(); }, [] {}); + thread.actor().invoke(&TestWorker::send, [&] { loop.stop(); }); loop.run(); } diff --git a/test/util/thread_local.test.cpp b/test/util/thread_local.test.cpp index 4ee7042580..0590e8b4dc 100644 --- a/test/util/thread_local.test.cpp +++ b/test/util/thread_local.test.cpp @@ -4,13 +4,15 @@ #include <mbgl/test/util.hpp> +#include <future> + using namespace mbgl::util; namespace { class TestThread { public: - TestThread(int *number_) { + TestThread(mbgl::ActorRef<TestThread>, int *number_) { number.set(number_); } @@ -18,8 +20,8 @@ public: number.set(nullptr); } - int getNumber() { - return *number.get(); + void getNumber(std::promise<int> result){ + result.set_value(*number.get()); } private: @@ -37,15 +39,28 @@ TEST(ThreadLocalStorage, Basic) { int number2 = 2; int number3 = 3; - ThreadContext context = {"Test"}; + Thread<TestThread> thread1("Test", &number1); + Thread<TestThread> thread2("Test", &number2); + Thread<TestThread> thread3("Test", &number3); + + auto thread1Ref = thread1.actor(); + auto thread2Ref = thread2.actor(); + auto thread3Ref = thread3.actor(); - Thread<TestThread> thread1(context, &number1); - Thread<TestThread> thread2(context, &number2); - Thread<TestThread> thread3(context, &number3); + std::promise<int> result1; + auto result1Future = result1.get_future(); + thread1Ref.invoke(&TestThread::getNumber, std::move(result1)); + EXPECT_EQ(number1, result1Future.get()); - EXPECT_EQ(number1, thread1.invokeSync(&TestThread::getNumber)); - EXPECT_EQ(number2, thread2.invokeSync(&TestThread::getNumber)); - EXPECT_EQ(number3, thread3.invokeSync(&TestThread::getNumber)); + std::promise<int> result2; + auto result2Future = result2.get_future(); + thread2Ref.invoke(&TestThread::getNumber, std::move(result2)); + EXPECT_EQ(number2, result2Future.get()); + + std::promise<int> result3; + auto result3Future = result3.get_future(); + thread3Ref.invoke(&TestThread::getNumber, std::move(result3)); + EXPECT_EQ(number3, result3Future.get()); } TEST(ThreadLocalStorage, NotSetReturnsNull) { @@ -63,7 +78,7 @@ struct DtorCounter { class TestThreadReclaim { public: - TestThreadReclaim(DtorCounter* counter_) { + TestThreadReclaim(mbgl::ActorRef<TestThreadReclaim>, DtorCounter* counter_) { counter.set(counter_); } @@ -83,10 +98,8 @@ TEST(ThreadLocalStorage, AutoReclaim) { auto dtorCounter1 = new DtorCounter{ &counter }; auto dtorCounter2 = new DtorCounter{ &counter }; - ThreadContext context = {"Test"}; - - auto thread1 = std::make_unique<Thread<TestThreadReclaim>>(context, dtorCounter1); - auto thread2 = std::make_unique<Thread<TestThreadReclaim>>(context, dtorCounter2); + auto thread1 = std::make_unique<Thread<TestThreadReclaim>>("Test", dtorCounter1); + auto thread2 = std::make_unique<Thread<TestThreadReclaim>>("Test", dtorCounter2); thread1.reset(); thread2.reset(); diff --git a/test/util/work_queue.test.cpp b/test/util/work_queue.test.cpp deleted file mode 100644 index 60c72f7358..0000000000 --- a/test/util/work_queue.test.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include <mbgl/test/util.hpp> - -#include <mbgl/util/run_loop.hpp> -#include <mbgl/util/thread.hpp> -#include <mbgl/util/work_queue.hpp> - -#include <thread> - -using namespace mbgl::util; - -class TestThread { -public: - TestThread(WorkQueue* queue_) : queue(queue_) {} - - void send(std::function<void()>&& fn) { - queue->push(std::move(fn)); - } - -private: - WorkQueue* queue; -}; - -TEST(WorkQueue, push) { - RunLoop loop; - - WorkQueue queue; - Thread<TestThread> thread({"Test"}, &queue); - - uint8_t count = 0; - - auto endTest = [&]() { - 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); -} |