diff options
-rw-r--r-- | test/fixtures/mock_file_source.cpp | 69 | ||||
-rw-r--r-- | test/fixtures/mock_file_source.hpp | 55 | ||||
-rw-r--r-- | test/fixtures/resources/style-unused-sources.json | 6 | ||||
-rw-r--r-- | test/fixtures/stub_file_source.cpp | 66 | ||||
-rw-r--r-- | test/fixtures/stub_file_source.hpp | 43 | ||||
-rw-r--r-- | test/sprite/sprite_store.cpp | 134 | ||||
-rw-r--r-- | test/style/glyph_store.cpp | 81 | ||||
-rw-r--r-- | test/style/pending_resources.cpp | 62 | ||||
-rw-r--r-- | test/style/resource_loading.cpp | 283 | ||||
-rw-r--r-- | test/style/source.cpp | 290 | ||||
-rw-r--r-- | test/style/style.cpp | 45 | ||||
-rw-r--r-- | test/test.gypi | 8 |
12 files changed, 592 insertions, 550 deletions
diff --git a/test/fixtures/mock_file_source.cpp b/test/fixtures/mock_file_source.cpp deleted file mode 100644 index 078271a422..0000000000 --- a/test/fixtures/mock_file_source.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include "mock_file_source.hpp" -#include <mbgl/util/io.hpp> -#include <mbgl/util/chrono.hpp> - -namespace mbgl { - -class MockFileRequest : public FileRequest { -public: - MockFileRequest(MockFileSource& fileSource_) - : fileSource(fileSource_) { - } - - ~MockFileRequest() { - fileSource.pending.erase(this); - } - - MockFileSource& fileSource; -}; - -MockFileSource::MockFileSource(Type type_, const std::string& match_) - : type(type_), match(match_) { - timer.start(Milliseconds(10), Milliseconds(10), [this] { - // Explicit move to avoid iterator invalidation if ~MockFileRequest gets called within the loop. - auto pending_ = std::move(pending); - for (auto& pair : pending_) { - respond(pair.second.first, pair.second.second); - } - }); -} - -MockFileSource::~MockFileSource() { - timer.stop(); -} - -void MockFileSource::respond(Resource resource, Callback callback) const { - if (type == Type::Success || resource.url.find(match) == std::string::npos) { - Response res; - try { - res.data = std::make_shared<const std::string>(util::read_file(resource.url)); - } catch (const std::exception& err) { - res.error = std::make_unique<Response::Error>(Response::Error::Reason::Other, err.what()); - } - callback(res); - } else if (type == Type::RequestFail) { - Response res; - res.error = std::make_unique<Response::Error>(Response::Error::Reason::Other, "Failed by the test case"); - callback(res); - } else if (type == Type::RequestWithCorruptedData) { - Response res; - auto data = std::make_shared<std::string>(util::read_file(resource.url)); - data->insert(0, "CORRUPTED"); - res.data = std::move(data); - callback(res); - } -} - -std::unique_ptr<FileRequest> MockFileSource::request(const Resource& resource, Callback callback) { - auto req = std::make_unique<MockFileRequest>(*this); - - pending.emplace(req.get(), std::make_pair(resource, callback)); - - if (requestEnqueuedCallback && resource.url.find(match) != std::string::npos) { - requestEnqueuedCallback(); - } - - return std::move(req); -} - -} // namespace mbgl diff --git a/test/fixtures/mock_file_source.hpp b/test/fixtures/mock_file_source.hpp deleted file mode 100644 index 245e0da0eb..0000000000 --- a/test/fixtures/mock_file_source.hpp +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef TEST_RESOURCES_MOCK_FILE_SOURCE -#define TEST_RESOURCES_MOCK_FILE_SOURCE - -#include <mbgl/storage/file_source.hpp> -#include <mbgl/util/timer.hpp> - -#include <string> -#include <unordered_map> - -namespace mbgl { - -// The MockFileSource is a FileSource that can simulate different -// types of failures and it will work completely offline. -class MockFileSource : public FileSource { -public: - // Success: - // Will reply to every request correctly with valid data. - // - // RequestFail: - // Will reply with an error to requests that contains - // the "match" string on the URL. - // - // RequestWithCorruptedData: - // Will answer every request successfully but will return - // corrupt data on the requests that contains the "match" - // string on the URL. - enum Type { - Success, - RequestFail, - RequestWithCorruptedData - }; - - MockFileSource(Type, const std::string& match); - ~MockFileSource() override; - - // Function that gets called when a matching resource is enqueued. - std::function<void (void)> requestEnqueuedCallback; - - // FileSource implementation. - std::unique_ptr<FileRequest> request(const Resource&, Callback) override; - -private: - void respond(Resource, Callback) const; - - friend class MockFileRequest; - - Type type; - std::string match; - std::unordered_map<FileRequest*, std::pair<Resource, Callback>> pending; - util::Timer timer; -}; - -} - -#endif diff --git a/test/fixtures/resources/style-unused-sources.json b/test/fixtures/resources/style-unused-sources.json index 3878f85dd8..ede1c9adfd 100644 --- a/test/fixtures/resources/style-unused-sources.json +++ b/test/fixtures/resources/style-unused-sources.json @@ -3,16 +3,14 @@ "name": "Test", "sources": { "usedsource": { - "url": "test/fixtures/resources/source_vector.json", + "tiles": [], "type": "vector" }, "unusedsource": { - "url": "test/fixtures/resources/source_vector.json", + "tiles": [], "type": "vector" } }, - "sprite": "test/fixtures/resources/sprite", - "glyphs": "test/fixtures/resources/glyphs.pbf", "layers": [{ "id": "usedlayer", "type": "symbol", diff --git a/test/fixtures/stub_file_source.cpp b/test/fixtures/stub_file_source.cpp new file mode 100644 index 0000000000..57bc178ac1 --- /dev/null +++ b/test/fixtures/stub_file_source.cpp @@ -0,0 +1,66 @@ +#include "stub_file_source.hpp" + +namespace mbgl { + +using namespace std::chrono_literals; + +class StubFileRequest : public FileRequest { +public: + StubFileRequest(StubFileSource& fileSource_) + : fileSource(fileSource_) { + } + + ~StubFileRequest() { + fileSource.pending.erase(this); + } + + StubFileSource& fileSource; +}; + +StubFileSource::StubFileSource() { + timer.start(10ms, 10ms, [this] { + // Explicit move to avoid iterator invalidation if ~StubFileRequest gets called within the loop. + auto pending_ = std::move(pending); + for (auto& pair : pending_) { + pair.second.second(pair.second.first); + } + }); +} + +StubFileSource::~StubFileSource() = default; + +std::unique_ptr<FileRequest> StubFileSource::request(const Resource& resource, Callback callback) { + auto req = std::make_unique<StubFileRequest>(*this); + pending.emplace(req.get(), std::make_pair(response(resource), callback)); + return std::move(req); +} + +Response StubFileSource::defaultResponse(const Resource& resource) { + switch (resource.kind) { + case Resource::Kind::Style: + if (!styleResponse) throw std::runtime_error("unexpected style request"); + return styleResponse(resource); + case Resource::Kind::Source: + if (!sourceResponse) throw std::runtime_error("unexpected source request"); + return sourceResponse(resource); + case Resource::Kind::Tile: + if (!tileResponse) throw std::runtime_error("unexpected tile request"); + return tileResponse(resource); + case Resource::Kind::Glyphs: + if (!glyphsResponse) throw std::runtime_error("unexpected glyphs request"); + return glyphsResponse(resource); + case Resource::Kind::SpriteJSON: + if (!spriteJSONResponse) throw std::runtime_error("unexpected sprite JSON request"); + return spriteJSONResponse(resource); + case Resource::Kind::SpriteImage: + if (!spriteImageResponse) throw std::runtime_error("unexpected sprite image request"); + return spriteImageResponse(resource); + case Resource::Kind::Unknown: + throw std::runtime_error("unknown resource type"); + } + + // The above switch is exhaustive, but placate GCC nonetheless: + return Response(); +} + +} // namespace mbgl diff --git a/test/fixtures/stub_file_source.hpp b/test/fixtures/stub_file_source.hpp new file mode 100644 index 0000000000..7cb9c89320 --- /dev/null +++ b/test/fixtures/stub_file_source.hpp @@ -0,0 +1,43 @@ +#ifndef TEST_RESOURCES_STUB_FILE_SOURCE +#define TEST_RESOURCES_STUB_FILE_SOURCE + +#include <mbgl/storage/file_source.hpp> +#include <mbgl/util/timer.hpp> + +#include <unordered_map> + +namespace mbgl { + +class StubFileSource : public FileSource { +public: + StubFileSource(); + ~StubFileSource() override; + + std::unique_ptr<FileRequest> request(const Resource&, Callback) override; + + // You can set the response callback on a global level by assigning this callback: + std::function<Response (const Resource&)> response = [this] (const Resource& resource) { + return defaultResponse(resource); + }; + + // Or set per-kind responses by setting these callbacks: + std::function<Response (const Resource&)> styleResponse; + std::function<Response (const Resource&)> sourceResponse; + std::function<Response (const Resource&)> tileResponse; + std::function<Response (const Resource&)> glyphsResponse; + std::function<Response (const Resource&)> spriteJSONResponse; + std::function<Response (const Resource&)> spriteImageResponse; + +private: + friend class StubFileRequest; + + // The default behavior is to throw if no per-kind callback has been set. + Response defaultResponse(const Resource&); + + std::unordered_map<FileRequest*, std::pair<Response, Callback>> pending; + util::Timer timer; +}; + +} + +#endif diff --git a/test/sprite/sprite_store.cpp b/test/sprite/sprite_store.cpp index ca5f0ed9e4..08977eac4f 100644 --- a/test/sprite/sprite_store.cpp +++ b/test/sprite/sprite_store.cpp @@ -1,10 +1,12 @@ #include "../fixtures/util.hpp" #include "../fixtures/fixture_log_observer.hpp" -#include "../fixtures/mock_file_source.hpp" +#include "../fixtures/stub_file_source.hpp" #include "../fixtures/stub_style_observer.hpp" #include <mbgl/sprite/sprite_store.hpp> #include <mbgl/util/run_loop.hpp> +#include <mbgl/util/string.hpp> +#include <mbgl/util/io.hpp> #include <utility> @@ -151,17 +153,16 @@ TEST(SpriteStore, ReplaceWithDifferentDimensions) { class SpriteStoreTest { public: - SpriteStoreTest(MockFileSource::Type type, const std::string& resource) - : fileSource(type, resource), - spriteStore(1.0) {} + SpriteStoreTest() + : spriteStore(1.0) {} util::ThreadContext context { "Map", util::ThreadType::Map, util::ThreadPriority::Regular }; util::RunLoop loop; - MockFileSource fileSource; + StubFileSource fileSource; StubStyleObserver observer; SpriteStore spriteStore; - void run(const std::string& url) { + void run() { // Squelch logging. Log::setObserver(std::make_unique<Log::NullObserver>()); @@ -169,7 +170,7 @@ public: util::ThreadContext::setFileSource(&fileSource); spriteStore.setObserver(&observer); - spriteStore.setURL(url); + spriteStore.setURL("test/fixtures/resources/sprite"); loop.run(); } @@ -179,70 +180,131 @@ public: } }; +Response successfulSpriteImageResponse(const Resource& resource) { + EXPECT_EQ("test/fixtures/resources/sprite.png", resource.url); + Response response; + response.data = std::make_shared<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_shared<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_shared<std::string>("CORRUPT"); + return response; +}; + TEST(SpriteStore, LoadingSuccess) { - SpriteStoreTest test(MockFileSource::Success, ""); + SpriteStoreTest test; - test.observer.spriteError = [&] (std::exception_ptr) { - FAIL(); + test.fileSource.spriteImageResponse = successfulSpriteImageResponse; + test.fileSource.spriteJSONResponse = successfulSpriteJSONResponse; + + test.observer.spriteError = [&] (std::exception_ptr error) { + FAIL() << util::toString(error); test.end(); }; test.observer.spriteLoaded = [&] () { - ASSERT_TRUE(!test.spriteStore.getDirty().empty()); - ASSERT_EQ(test.spriteStore.pixelRatio, 1.0); - ASSERT_TRUE(test.spriteStore.isLoaded()); + EXPECT_TRUE(!test.spriteStore.getDirty().empty()); + EXPECT_EQ(1.0, test.spriteStore.pixelRatio); + EXPECT_TRUE(test.spriteStore.isLoaded()); test.end(); }; - test.run("test/fixtures/resources/sprite"); + test.run(); } -TEST(SpriteStore, LoadingFail) { - SpriteStoreTest test(MockFileSource::RequestFail, "sprite.json"); +TEST(SpriteStore, JSONLoadingFail) { + SpriteStoreTest test; + + test.fileSource.spriteImageResponse = successfulSpriteImageResponse; + test.fileSource.spriteJSONResponse = failedSpriteResponse; test.observer.spriteError = [&] (std::exception_ptr error) { - ASSERT_TRUE(error != nullptr); - ASSERT_FALSE(test.spriteStore.isLoaded()); + EXPECT_TRUE(error != nullptr); + EXPECT_EQ("Failed by the test case", util::toString(error)); + EXPECT_FALSE(test.spriteStore.isLoaded()); test.end(); }; - test.run("test/fixtures/resources/sprite"); + test.run(); } -TEST(SpriteStore, LoadingCorrupted) { - SpriteStoreTest test(MockFileSource::RequestWithCorruptedData, "sprite.json"); +TEST(SpriteStore, ImageLoadingFail) { + SpriteStoreTest test; + + test.fileSource.spriteImageResponse = failedSpriteResponse; + test.fileSource.spriteJSONResponse = successfulSpriteJSONResponse; test.observer.spriteError = [&] (std::exception_ptr error) { - ASSERT_TRUE(error != nullptr); - ASSERT_FALSE(test.spriteStore.isLoaded()); + EXPECT_TRUE(error != nullptr); + EXPECT_EQ("Failed by the test case", util::toString(error)); + EXPECT_FALSE(test.spriteStore.isLoaded()); test.end(); }; - test.run("test/fixtures/resources/sprite"); + test.run(); } -TEST(SpriteStore, LoadingCancel) { - SpriteStoreTest test(MockFileSource::Success, "sprite.json"); +TEST(SpriteStore, JSONLoadingCorrupted) { + SpriteStoreTest test; - test.observer.spriteLoaded = [&] () { - FAIL() << "Should never be called"; - }; + test.fileSource.spriteImageResponse = successfulSpriteImageResponse; + test.fileSource.spriteJSONResponse = corruptSpriteResponse; - test.fileSource.requestEnqueuedCallback = [&]{ + 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.spriteStore.isLoaded()); test.end(); }; - test.run("test/fixtures/resources/sprite"); + test.run(); } -TEST(SpriteStore, InvalidURL) { - SpriteStoreTest test(MockFileSource::Success, ""); +TEST(SpriteStore, ImageLoadingCorrupted) { + SpriteStoreTest test; + + test.fileSource.spriteImageResponse = corruptSpriteResponse; + test.fileSource.spriteJSONResponse = successfulSpriteJSONResponse; test.observer.spriteError = [&] (std::exception_ptr error) { - ASSERT_TRUE(error != nullptr); - ASSERT_EQ(test.spriteStore.isLoaded(), false); + EXPECT_TRUE(error != nullptr); + // Not asserting on platform-specific error text. + EXPECT_FALSE(test.spriteStore.isLoaded()); + test.end(); + }; + + test.run(); +} + +TEST(SpriteStore, LoadingCancel) { + SpriteStoreTest test; + + test.fileSource.spriteImageResponse = + test.fileSource.spriteJSONResponse = [&] (const Resource&) { test.end(); + return Response(); + }; + + test.observer.spriteLoaded = [&] () { + FAIL() << "Should never be called"; }; - test.run("foo bar"); + test.run(); } diff --git a/test/style/glyph_store.cpp b/test/style/glyph_store.cpp index 0215dff3ff..a4bc0e3135 100644 --- a/test/style/glyph_store.cpp +++ b/test/style/glyph_store.cpp @@ -1,22 +1,21 @@ #include "../fixtures/util.hpp" -#include "../fixtures/mock_file_source.hpp" +#include "../fixtures/stub_file_source.hpp" #include "../fixtures/stub_style_observer.hpp" #include <mbgl/text/font_stack.hpp> #include <mbgl/text/glyph_store.hpp> #include <mbgl/util/run_loop.hpp> +#include <mbgl/util/string.hpp> +#include <mbgl/util/io.hpp> #include <mbgl/platform/log.hpp> using namespace mbgl; class GlyphStoreTest { public: - GlyphStoreTest(MockFileSource::Type type, const std::string& resource) - : fileSource(type, resource) {} - util::ThreadContext context { "Map", util::ThreadType::Map, util::ThreadPriority::Regular }; util::RunLoop loop; - MockFileSource fileSource; + StubFileSource fileSource; StubStyleObserver observer; GlyphStore glyphStore; @@ -40,7 +39,14 @@ public: }; TEST(GlyphStore, LoadingSuccess) { - GlyphStoreTest test(MockFileSource::Success, ""); + GlyphStoreTest test; + + test.fileSource.glyphsResponse = [&] (const Resource& resource) { + EXPECT_EQ(Resource::Kind::Glyphs, resource.kind); + Response response; + response.data = std::make_shared<std::string>(util::read_file("test/fixtures/resources/glyphs.pbf")); + return response; + }; test.observer.glyphsError = [&] (const std::string&, const GlyphRange&, std::exception_ptr) { FAIL(); @@ -64,12 +70,22 @@ TEST(GlyphStore, LoadingSuccess) { } TEST(GlyphStore, LoadingFail) { - GlyphStoreTest test(MockFileSource::RequestFail, "glyphs.pbf"); + GlyphStoreTest test; + + test.fileSource.glyphsResponse = [&] (const Resource&) { + Response response; + response.error = std::make_unique<Response::Error>( + Response::Error::Reason::Other, + "Failed by the test case"); + return response; + }; test.observer.glyphsError = [&] (const std::string& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) { - ASSERT_TRUE(error != nullptr); - ASSERT_EQ(fontStack, "Test Stack"); - ASSERT_EQ(glyphRange, GlyphRange(0, 255)); + EXPECT_EQ(fontStack, "Test Stack"); + EXPECT_EQ(glyphRange, GlyphRange(0, 255)); + + EXPECT_TRUE(error != nullptr); + EXPECT_EQ(util::toString(error), "Failed by the test case"); auto stack = test.glyphStore.getFontStack("Test Stack"); ASSERT_TRUE(stack->getSDFs().empty()); @@ -85,12 +101,20 @@ TEST(GlyphStore, LoadingFail) { } TEST(GlyphStore, LoadingCorrupted) { - GlyphStoreTest test(MockFileSource::RequestWithCorruptedData, "glyphs.pbf"); + GlyphStoreTest test; + + test.fileSource.glyphsResponse = [&] (const Resource&) { + Response response; + response.data = std::make_unique<std::string>("CORRUPTED"); + return response; + }; test.observer.glyphsError = [&] (const std::string& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) { - ASSERT_TRUE(error != nullptr); - ASSERT_EQ(fontStack, "Test Stack"); - ASSERT_EQ(glyphRange, GlyphRange(0, 255)); + EXPECT_EQ(fontStack, "Test Stack"); + EXPECT_EQ(glyphRange, GlyphRange(0, 255)); + + EXPECT_TRUE(error != nullptr); + EXPECT_EQ(util::toString(error), "pbf unknown field type exception"); auto stack = test.glyphStore.getFontStack("Test Stack"); ASSERT_TRUE(stack->getSDFs().empty()); @@ -106,36 +130,19 @@ TEST(GlyphStore, LoadingCorrupted) { } TEST(GlyphStore, LoadingCancel) { - GlyphStoreTest test(MockFileSource::Success, "glyphs.pbf"); + GlyphStoreTest test; - test.observer.glyphsLoaded = [&] (const std::string&, const GlyphRange&) { - FAIL() << "Should never be called"; - }; - - test.fileSource.requestEnqueuedCallback = [&]{ + test.fileSource.glyphsResponse = [&] (const Resource&) { test.end(); + return Response(); }; - test.run( - "test/fixtures/resources/glyphs.pbf", - "Test Stack", - {{0, 255}}); -} - -TEST(GlyphStore, InvalidURL) { - GlyphStoreTest test(MockFileSource::Success, ""); - - test.observer.glyphsError = [&] (const std::string&, const GlyphRange&, std::exception_ptr error) { - ASSERT_TRUE(error != nullptr); - - auto stack = test.glyphStore.getFontStack("Test Stack"); - ASSERT_TRUE(stack->getSDFs().empty()); - - test.end(); + test.observer.glyphsLoaded = [&] (const std::string&, const GlyphRange&) { + FAIL() << "Should never be called"; }; test.run( - "foo bar", + "test/fixtures/resources/glyphs.pbf", "Test Stack", {{0, 255}}); } diff --git a/test/style/pending_resources.cpp b/test/style/pending_resources.cpp deleted file mode 100644 index 1115274227..0000000000 --- a/test/style/pending_resources.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include "../fixtures/fixture_log_observer.hpp" -#include "../fixtures/mock_file_source.hpp" -#include "../fixtures/util.hpp" - -#include <mbgl/map/map.hpp> -#include <mbgl/platform/default/headless_display.hpp> -#include <mbgl/platform/default/headless_view.hpp> -#include <mbgl/util/async_task.hpp> -#include <mbgl/util/io.hpp> -#include <mbgl/util/run_loop.hpp> - -using namespace mbgl; - -class PendingResources : public ::testing::TestWithParam<std::string> { -}; - -// This test will load a Style but one of the resources requested will not be -// replied immediately like the others. We get an notification by the -// MockFileSource when some resource is artificially delayed and we destroy -// the Map object after that. The idea here is to test if these pending requests -// are getting canceled correctly if on shutdown. -TEST_P(PendingResources, DeleteMapObjectWithPendingRequest) { - util::RunLoop loop; - - auto display = std::make_shared<mbgl::HeadlessDisplay>(); - HeadlessView view(display, 1, 1000, 1000); - MockFileSource fileSource(MockFileSource::Success, GetParam()); - - std::unique_ptr<Map> map = std::make_unique<Map>(view, fileSource, MapMode::Still); - - util::AsyncTask endTest([&map, &loop] { - map.reset(); - loop.stop(); - }); - - fileSource.requestEnqueuedCallback = [&endTest] { endTest.send(); }; - - const std::string style = util::read_file("test/fixtures/resources/style.json"); - map->setStyleJSON(style, "."); - - map->renderStill([](std::exception_ptr, PremultipliedImage&&) { - EXPECT_TRUE(false) << "Should never happen."; - }); - - loop.run(); -} - -// In the test data below, "sprite" will match both "sprite.json" and "sprite.png" and cause two -// requests to be canceled. "resources" will match everything but in practice will only test the -// cancellation of the sprites and "source_*.json" because we only load the rest after "source_*.json" -// gets parsed. -INSTANTIATE_TEST_CASE_P(Style, PendingResources, - ::testing::Values( - "source_raster.json", - "source_vector.json", - "sprite.json", - "sprite.png", - "sprite", - "raster.png", - "vector.pbf", - "glyphs.pbf", - "resources")); diff --git a/test/style/resource_loading.cpp b/test/style/resource_loading.cpp deleted file mode 100644 index b4a539643a..0000000000 --- a/test/style/resource_loading.cpp +++ /dev/null @@ -1,283 +0,0 @@ -#include "../fixtures/fixture_log_observer.hpp" -#include "../fixtures/util.hpp" -#include "../fixtures/mock_file_source.hpp" -#include "../fixtures/mock_view.hpp" -#include "../fixtures/stub_style_observer.hpp" - -#include <mbgl/map/map_data.hpp> -#include <mbgl/map/transform.hpp> -#include <mbgl/style/style.hpp> -#include <mbgl/util/exception.hpp> -#include <mbgl/util/io.hpp> -#include <mbgl/util/run_loop.hpp> -#include <mbgl/util/texture_pool.hpp> -#include <mbgl/util/thread.hpp> -#include <mbgl/util/string.hpp> - -using namespace mbgl; - -class ResourceLoadingTest { -public: - ResourceLoadingTest(MockFileSource::Type type, const std::string& resource) - : fileSource(type, resource) {} - - util::ThreadContext context { "Map", util::ThreadType::Map, util::ThreadPriority::Regular }; - util::RunLoop loop; - MockFileSource fileSource; - StubStyleObserver observer; - std::function<void ()> onFullyLoaded; - - MapData data { MapMode::Still, GLContextMode::Unique, 1.0 }; - MockView view; - Transform transform { view, ConstrainMode::HeightOnly }; - TexturePool texturePool; - Style style { data }; - - void run(const std::string& stylePath) { - // Squelch logging. - Log::setObserver(std::make_unique<Log::NullObserver>()); - - util::ThreadContext::Set(&context); - util::ThreadContext::setFileSource(&fileSource); - - observer.resourceLoaded = [&] () { - style.update(transform.getState(), texturePool); - if (style.isLoaded() && onFullyLoaded) { - onFullyLoaded(); - } - }; - - transform.resize({{ 512, 512 }}); - transform.setLatLngZoom({0, 0}, 0); - - style.setObserver(&observer); - style.setJSON(util::read_file(stylePath), ""); - style.cascade(); - style.recalculate(0); - - loop.run(); - } - - void end() { - loop.stop(); - } -}; - -TEST(ResourceLoading, Success) { - ResourceLoadingTest test(MockFileSource::Success, ""); - - test.observer.resourceError = [&] (std::exception_ptr error) { - FAIL() << util::toString(error); - }; - - test.onFullyLoaded = [&] () { - SUCCEED(); - test.end(); - }; - - test.run("test/fixtures/resources/style.json"); -} - -TEST(ResourceLoading, RasterSourceFail) { - ResourceLoadingTest test(MockFileSource::RequestFail, "source_raster.json"); - - test.observer.sourceError = [&] (Source& source, std::exception_ptr error) { - EXPECT_EQ(source.id, "rastersource"); - EXPECT_EQ(util::toString(error), "Failed by the test case"); - test.end(); - }; - - test.run("test/fixtures/resources/style.json"); -} - -TEST(ResourceLoading, VectorSourceFail) { - ResourceLoadingTest test(MockFileSource::RequestFail, "source_vector.json"); - - test.observer.sourceError = [&] (Source& source, std::exception_ptr error) { - EXPECT_EQ(source.id, "vectorsource"); - EXPECT_EQ(util::toString(error), "Failed by the test case"); - test.end(); - }; - - test.run("test/fixtures/resources/style.json"); -} - -TEST(ResourceLoading, SpriteJSONFail) { - ResourceLoadingTest test(MockFileSource::RequestFail, "sprite.json"); - - test.observer.spriteError = [&] (std::exception_ptr error) { - EXPECT_EQ(util::toString(error), "Failed by the test case"); - test.end(); - }; - - test.run("test/fixtures/resources/style.json"); -} - -TEST(ResourceLoading, SpriteImageFail) { - ResourceLoadingTest test(MockFileSource::RequestFail, "sprite.png"); - - test.observer.spriteError = [&] (std::exception_ptr error) { - EXPECT_EQ(util::toString(error), "Failed by the test case"); - test.end(); - }; - - test.run("test/fixtures/resources/style.json"); -} - -TEST(ResourceLoading, RasterTileFail) { - ResourceLoadingTest test(MockFileSource::RequestFail, "raster.png"); - - test.observer.tileError = [&] (Source& source, const TileID& tileID, std::exception_ptr error) { - EXPECT_EQ(source.id, "rastersource"); - EXPECT_EQ(std::string(tileID), "0/0/0"); - EXPECT_EQ(util::toString(error), "Failed by the test case"); - test.end(); - }; - - test.run("test/fixtures/resources/style.json"); -} - -TEST(ResourceLoading, VectorTileFail) { - ResourceLoadingTest test(MockFileSource::RequestFail, "vector.pbf"); - - test.observer.tileError = [&] (Source& source, const TileID& tileID, std::exception_ptr error) { - EXPECT_EQ(source.id, "vectorsource"); - EXPECT_EQ(std::string(tileID), "0/0/0"); - EXPECT_EQ(util::toString(error), "Failed by the test case"); - test.end(); - }; - - test.run("test/fixtures/resources/style.json"); -} - -TEST(ResourceLoading, GlyphsFail) { - ResourceLoadingTest test(MockFileSource::RequestFail, "glyphs.pbf"); - - test.observer.glyphsError = [&] (const std::string& fontStack, const GlyphRange&, std::exception_ptr error) { - EXPECT_EQ(fontStack, "Open Sans Regular,Arial Unicode MS Regular"); - EXPECT_EQ(util::toString(error), "Failed by the test case"); - test.end(); - }; - - test.run("test/fixtures/resources/style.json"); -} - -TEST(ResourceLoading, RasterSourceCorrupt) { - ResourceLoadingTest test(MockFileSource::RequestWithCorruptedData, "source_raster.json"); - - test.observer.sourceError = [&] (Source& source, std::exception_ptr error) { - EXPECT_EQ(source.id, "rastersource"); - EXPECT_EQ(util::toString(error), "0 - Invalid value."); - test.end(); - }; - - test.run("test/fixtures/resources/style.json"); -} - -TEST(ResourceLoading, VectorSourceCorrupt) { - ResourceLoadingTest test(MockFileSource::RequestWithCorruptedData, "source_vector.json"); - - test.observer.sourceError = [&] (Source& source, std::exception_ptr error) { - EXPECT_EQ(source.id, "vectorsource"); - EXPECT_EQ(util::toString(error), "0 - Invalid value."); - test.end(); - }; - - test.run("test/fixtures/resources/style.json"); -} - -TEST(ResourceLoading, SpriteJSONCorrupt) { - ResourceLoadingTest test(MockFileSource::RequestWithCorruptedData, "sprite.json"); - - test.observer.spriteError = [&] (std::exception_ptr error) { - EXPECT_EQ(util::toString(error), "Failed to parse JSON: Invalid value. at offset 0"); - test.end(); - }; - - test.run("test/fixtures/resources/style.json"); -} - -TEST(ResourceLoading, SpriteImageCorrupt) { - ResourceLoadingTest test(MockFileSource::RequestWithCorruptedData, "sprite.png"); - - test.observer.spriteError = [&] (std::exception_ptr error) { - EXPECT_TRUE(bool(error)); - // Not asserting on platform-specific error text. - test.end(); - }; - - test.run("test/fixtures/resources/style.json"); -} - -TEST(ResourceLoading, RasterTileCorrupt) { - ResourceLoadingTest test(MockFileSource::RequestWithCorruptedData, "raster.png"); - - test.observer.tileError = [&] (Source& source, const TileID& tileID, std::exception_ptr error) { - EXPECT_EQ(source.id, "rastersource"); - EXPECT_EQ(std::string(tileID), "0/0/0"); - EXPECT_TRUE(bool(error)); - // Not asserting on platform-specific error text. - test.end(); - }; - - test.run("test/fixtures/resources/style.json"); -} - -TEST(ResourceLoading, VectorTileCorrupt) { - ResourceLoadingTest test(MockFileSource::RequestWithCorruptedData, "vector.pbf"); - - test.observer.tileError = [&] (Source& source, const TileID& tileID, std::exception_ptr error) { - EXPECT_EQ(source.id, "vectorsource"); - EXPECT_EQ(std::string(tileID), "0/0/0"); - EXPECT_EQ(util::toString(error), "pbf unknown field type exception"); - test.end(); - }; - - test.run("test/fixtures/resources/style.json"); -} - -TEST(ResourceLoading, GlyphsCorrupt) { - ResourceLoadingTest test(MockFileSource::RequestWithCorruptedData, "glyphs.pbf"); - - test.observer.glyphsError = [&] (const std::string& fontStack, const GlyphRange&, std::exception_ptr error) { - EXPECT_EQ(fontStack, "Open Sans Regular,Arial Unicode MS Regular"); - EXPECT_EQ(util::toString(error), "pbf unknown field type exception"); - test.end(); - }; - - test.run("test/fixtures/resources/style.json"); -} - -TEST(ResourceLoading, UnusedSource) { - ResourceLoadingTest test(MockFileSource::Success, ""); - - test.onFullyLoaded = [&] () { - Source *usedSource = test.style.getSource("usedsource"); - EXPECT_TRUE(usedSource); - EXPECT_TRUE(usedSource->isLoaded()); - - Source *unusedSource = test.style.getSource("unusedsource"); - EXPECT_TRUE(unusedSource); - EXPECT_FALSE(unusedSource->isLoaded()); - - test.end(); - }; - - test.run("test/fixtures/resources/style-unused-sources.json"); -} - -TEST(ResourceLoading, UnusedSourceActiveViaClassUpdate) { - ResourceLoadingTest test(MockFileSource::Success, ""); - - test.data.addClass("visible"); - - test.onFullyLoaded = [&] () { - Source *unusedSource = test.style.getSource("unusedsource"); - EXPECT_TRUE(unusedSource); - EXPECT_TRUE(unusedSource->isLoaded()); - - test.end(); - }; - - test.run("test/fixtures/resources/style-unused-sources.json"); -} diff --git a/test/style/source.cpp b/test/style/source.cpp new file mode 100644 index 0000000000..d1944f3842 --- /dev/null +++ b/test/style/source.cpp @@ -0,0 +1,290 @@ +#include "../fixtures/util.hpp" +#include "../fixtures/stub_file_source.hpp" +#include "../fixtures/mock_view.hpp" +#include "../fixtures/stub_style_observer.hpp" + +#include <mbgl/map/source.hpp> +#include <mbgl/util/run_loop.hpp> +#include <mbgl/util/string.hpp> +#include <mbgl/util/io.hpp> +#include <mbgl/platform/log.hpp> + +#include <mbgl/map/transform.hpp> +#include <mbgl/map/map_data.hpp> +#include <mbgl/util/worker.hpp> +#include <mbgl/util/texture_pool.hpp> +#include <mbgl/style/style.hpp> +#include <mbgl/style/style_update_parameters.hpp> +#include <mbgl/layer/line_layer.hpp> + +using namespace mbgl; + +class SourceTest { +public: + util::ThreadContext context { "Map", util::ThreadType::Map, util::ThreadPriority::Regular }; + util::RunLoop loop; + StubFileSource fileSource; + StubStyleObserver observer; + MockView view; + Transform transform { view, ConstrainMode::HeightOnly }; + TransformState transformState; + Worker worker { 1 }; + TexturePool texturePool; + MapData mapData { MapMode::Still, GLContextMode::Unique, 1.0 }; + Style style { mapData }; + + StyleUpdateParameters updateParameters { + 1.0, + MapDebugOptions(), + TimePoint(), + transformState, + worker, + texturePool, + true, + MapMode::Continuous, + mapData, + style + }; + + SourceTest() { + // Squelch logging. + Log::setObserver(std::make_unique<Log::NullObserver>()); + + util::ThreadContext::Set(&context); + util::ThreadContext::setFileSource(&fileSource); + + transform.resize({{ 512, 512 }}); + transform.setLatLngZoom({0, 0}, 0); + + transformState = transform.getState(); + } + + void run() { + loop.run(); + } + + void end() { + loop.stop(); + } +}; + +TEST(Source, LoadingFail) { + SourceTest test; + + test.fileSource.sourceResponse = [&] (const Resource& resource) { + EXPECT_EQ("url", resource.url); + Response response; + response.error = std::make_unique<Response::Error>( + Response::Error::Reason::Other, + "Failed by the test case"); + return response; + }; + + test.observer.sourceError = [&] (Source& source, std::exception_ptr error) { + EXPECT_EQ("url", source.url); + EXPECT_EQ("Failed by the test case", util::toString(error)); + test.end(); + }; + + Source source(SourceType::Vector, "source", "url", 512, nullptr, nullptr); + source.setObserver(&test.observer); + source.load(); + + test.run(); +} + +TEST(Source, LoadingCorrupt) { + SourceTest test; + + test.fileSource.sourceResponse = [&] (const Resource& resource) { + EXPECT_EQ("url", resource.url); + Response response; + response.data = std::make_unique<std::string>("CORRUPTED"); + return response; + }; + + test.observer.sourceError = [&] (Source& source, std::exception_ptr error) { + EXPECT_EQ("url", source.url); + EXPECT_EQ("0 - Invalid value.", util::toString(error)); + test.end(); + }; + + Source source(SourceType::Vector, "source", "url", 512, nullptr, nullptr); + source.setObserver(&test.observer); + source.load(); + + test.run(); +} + +TEST(Source, RasterTileFail) { + SourceTest test; + + test.fileSource.tileResponse = [&] (const Resource&) { + Response response; + response.error = std::make_unique<Response::Error>( + Response::Error::Reason::Other, + "Failed by the test case"); + return response; + }; + + test.observer.tileError = [&] (Source& source, const TileID& tileID, std::exception_ptr error) { + EXPECT_EQ(SourceType::Raster, source.type); + EXPECT_EQ("0/0/0", std::string(tileID)); + EXPECT_EQ("Failed by the test case", util::toString(error)); + test.end(); + }; + + auto info = std::make_unique<SourceInfo>(); + info->tiles = { "tiles" }; + + Source source(SourceType::Raster, "source", "", 512, std::move(info), nullptr); + source.setObserver(&test.observer); + source.load(); + source.update(test.updateParameters); + + test.run(); +} + +TEST(Source, VectorTileFail) { + SourceTest test; + + test.fileSource.tileResponse = [&] (const Resource&) { + Response response; + response.error = std::make_unique<Response::Error>( + Response::Error::Reason::Other, + "Failed by the test case"); + return response; + }; + + test.observer.tileError = [&] (Source& source, const TileID& tileID, std::exception_ptr error) { + EXPECT_EQ(SourceType::Vector, source.type); + EXPECT_EQ("0/0/0", std::string(tileID)); + EXPECT_EQ("Failed by the test case", util::toString(error)); + test.end(); + }; + + auto info = std::make_unique<SourceInfo>(); + info->tiles = { "tiles" }; + + Source source(SourceType::Vector, "source", "", 512, std::move(info), nullptr); + source.setObserver(&test.observer); + source.load(); + source.update(test.updateParameters); + + test.run(); +} + +TEST(Source, RasterTileCorrupt) { + SourceTest test; + + test.fileSource.tileResponse = [&] (const Resource&) { + Response response; + response.data = std::make_unique<std::string>("CORRUPTED"); + return response; + }; + + test.observer.tileError = [&] (Source& source, const TileID& tileID, std::exception_ptr error) { + EXPECT_EQ(source.type, SourceType::Raster); + EXPECT_EQ(std::string(tileID), "0/0/0"); + EXPECT_TRUE(bool(error)); + // Not asserting on platform-specific error text. + test.end(); + }; + + auto info = std::make_unique<SourceInfo>(); + info->tiles = { "tiles" }; + + Source source(SourceType::Raster, "source", "", 512, std::move(info), nullptr); + source.setObserver(&test.observer); + source.load(); + source.update(test.updateParameters); + + test.run(); +} + +TEST(Source, VectorTileCorrupt) { + SourceTest test; + + test.fileSource.tileResponse = [&] (const Resource&) { + Response response; + response.data = std::make_unique<std::string>("CORRUPTED"); + return response; + }; + + test.observer.tileError = [&] (Source& source, const TileID& tileID, std::exception_ptr error) { + EXPECT_EQ(source.type, SourceType::Vector); + EXPECT_EQ(std::string(tileID), "0/0/0"); + EXPECT_EQ(util::toString(error), "pbf unknown field type exception"); + test.end(); + }; + + // Need to have at least one layer that uses the source. + auto layer = std::make_unique<LineLayer>(); + layer->source = "source"; + layer->sourceLayer = "water"; + test.style.addLayer(std::move(layer)); + + auto info = std::make_unique<SourceInfo>(); + info->tiles = { "tiles" }; + + Source source(SourceType::Vector, "source", "", 512, std::move(info), nullptr); + source.setObserver(&test.observer); + source.load(); + source.update(test.updateParameters); + + test.run(); +} + +TEST(Source, RasterTileCancel) { + SourceTest test; + + test.fileSource.tileResponse = [&] (const Resource&) { + test.end(); + return Response(); + }; + + test.observer.tileLoaded = [&] (Source&, const TileID&, bool) { + FAIL() << "Should never be called"; + }; + + test.observer.tileError = [&] (Source&, const TileID&, std::exception_ptr) { + FAIL() << "Should never be called"; + }; + + auto info = std::make_unique<SourceInfo>(); + info->tiles = { "tiles" }; + + Source source(SourceType::Raster, "source", "", 512, std::move(info), nullptr); + source.setObserver(&test.observer); + source.load(); + source.update(test.updateParameters); + + test.run(); +} + +TEST(Source, VectorTileCancel) { + SourceTest test; + + test.fileSource.tileResponse = [&] (const Resource&) { + test.end(); + return Response(); + }; + + test.observer.tileLoaded = [&] (Source&, const TileID&, bool) { + FAIL() << "Should never be called"; + }; + + test.observer.tileError = [&] (Source&, const TileID&, std::exception_ptr) { + FAIL() << "Should never be called"; + }; + + auto info = std::make_unique<SourceInfo>(); + info->tiles = { "tiles" }; + + Source source(SourceType::Vector, "source", "", 512, std::move(info), nullptr); + source.setObserver(&test.observer); + source.load(); + source.update(test.updateParameters); + + test.run(); +} diff --git a/test/style/style.cpp b/test/style/style.cpp new file mode 100644 index 0000000000..59366ea7dd --- /dev/null +++ b/test/style/style.cpp @@ -0,0 +1,45 @@ +#include "../fixtures/util.hpp" + +#include <mbgl/map/map_data.hpp> +#include <mbgl/style/style.hpp> +#include <mbgl/util/io.hpp> + +using namespace mbgl; + +TEST(Style, UnusedSource) { + util::ThreadContext context { "Map", util::ThreadType::Map, util::ThreadPriority::Regular }; + util::ThreadContext::Set(&context); + + MapData data { MapMode::Still, GLContextMode::Unique, 1.0 }; + Style style { data }; + + style.setJSON(util::read_file("test/fixtures/resources/style-unused-sources.json"), ""); + style.cascade(); + style.recalculate(0); + + Source *usedSource = style.getSource("usedsource"); + EXPECT_TRUE(usedSource); + EXPECT_TRUE(usedSource->isLoaded()); + + Source *unusedSource = style.getSource("unusedsource"); + EXPECT_TRUE(unusedSource); + EXPECT_FALSE(unusedSource->isLoaded()); +} + +TEST(Style, UnusedSourceActiveViaClassUpdate) { + util::ThreadContext context { "Map", util::ThreadType::Map, util::ThreadPriority::Regular }; + util::ThreadContext::Set(&context); + + MapData data { MapMode::Still, GLContextMode::Unique, 1.0 }; + Style style { data }; + + data.addClass("visible"); + + style.setJSON(util::read_file("test/fixtures/resources/style-unused-sources.json"), ""); + style.cascade(); + style.recalculate(0); + + Source *unusedSource = style.getSource("unusedsource"); + EXPECT_TRUE(unusedSource); + EXPECT_TRUE(unusedSource->isLoaded()); +} diff --git a/test/test.gypi b/test/test.gypi index 628ae3eb3e..93613cebf9 100644 --- a/test/test.gypi +++ b/test/test.gypi @@ -28,8 +28,8 @@ ], 'sources': [ 'fixtures/main.cpp', - 'fixtures/mock_file_source.cpp', - 'fixtures/mock_file_source.hpp', + 'fixtures/stub_file_source.cpp', + 'fixtures/stub_file_source.hpp', 'fixtures/mock_view.hpp', 'fixtures/util.hpp', 'fixtures/util.cpp', @@ -90,8 +90,8 @@ 'storage/resource.cpp', 'style/glyph_store.cpp', - 'style/pending_resources.cpp', - 'style/resource_loading.cpp', + 'style/source.cpp', + 'style/style.cpp', 'style/style_layer.cpp', 'sprite/sprite_atlas.cpp', |