diff --git a/test/fixtures/mock_file_source.cpp b/test/fixtures/mock_file_source.cpp deleted file mode 100644 index 078271a42..000000000 --- a/test/fixtures/mock_file_source.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include "mock_file_source.hpp" -#include -#include - -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(util::read_file(resource.url)); - } catch (const std::exception& err) { - res.error = std::make_unique(Response::Error::Reason::Other, err.what()); - } - callback(res); - } else if (type == Type::RequestFail) { - Response res; - res.error = std::make_unique(Response::Error::Reason::Other, "Failed by the test case"); - callback(res); - } else if (type == Type::RequestWithCorruptedData) { - Response res; - auto data = std::make_shared(util::read_file(resource.url)); - data->insert(0, "CORRUPTED"); - res.data = std::move(data); - callback(res); - } -} - -std::unique_ptr MockFileSource::request(const Resource& resource, Callback callback) { - auto req = std::make_unique(*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 245e0da0e..000000000 --- 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 -#include - -#include -#include - -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 requestEnqueuedCallback; - - // FileSource implementation. - std::unique_ptr request(const Resource&, Callback) override; - -private: - void respond(Resource, Callback) const; - - friend class MockFileRequest; - - Type type; - std::string match; - std::unordered_map> pending; - util::Timer timer; -}; - -} - -#endif diff --git a/test/fixtures/resources/style-unused-sources.json b/test/fixtures/resources/style-unused-sources.json index 3878f85dd..ede1c9adf 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 000000000..57bc178ac --- /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 StubFileSource::request(const Resource& resource, Callback callback) { + auto req = std::make_unique(*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 000000000..7cb9c8932 --- /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 +#include + +#include + +namespace mbgl { + +class StubFileSource : public FileSource { +public: + StubFileSource(); + ~StubFileSource() override; + + std::unique_ptr request(const Resource&, Callback) override; + + // You can set the response callback on a global level by assigning this callback: + std::function response = [this] (const Resource& resource) { + return defaultResponse(resource); + }; + + // Or set per-kind responses by setting these callbacks: + std::function styleResponse; + std::function sourceResponse; + std::function tileResponse; + std::function glyphsResponse; + std::function spriteJSONResponse; + std::function 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> pending; + util::Timer timer; +}; + +} + +#endif diff --git a/test/sprite/sprite_store.cpp b/test/sprite/sprite_store.cpp index ca5f0ed9e..08977eac4 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 #include +#include +#include #include @@ -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()); @@ -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: } }; -TEST(SpriteStore, LoadingSuccess) { - SpriteStoreTest test(MockFileSource::Success, ""); +Response successfulSpriteImageResponse(const Resource& resource) { + EXPECT_EQ("test/fixtures/resources/sprite.png", resource.url); + Response response; + response.data = std::make_shared(util::read_file(resource.url)); + return response; +}; - test.observer.spriteError = [&] (std::exception_ptr) { - FAIL(); +Response successfulSpriteJSONResponse(const Resource& resource) { + EXPECT_EQ("test/fixtures/resources/sprite.json", resource.url); + Response response; + response.data = std::make_shared(util::read_file(resource.url)); + return response; +}; + +Response failedSpriteResponse(const Resource&) { + Response response; + response.error = std::make_unique( + Response::Error::Reason::Other, + "Failed by the test case"); + return response; +}; + +Response corruptSpriteResponse(const Resource&) { + Response response; + response.data = std::make_shared("CORRUPT"); + return response; +}; + +TEST(SpriteStore, LoadingSuccess) { + SpriteStoreTest 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 = [&] () { - 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, JSONLoadingCorrupted) { + SpriteStoreTest 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.spriteStore.isLoaded()); + test.end(); + }; + + test.run(); +} + +TEST(SpriteStore, ImageLoadingCorrupted) { + SpriteStoreTest 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.spriteStore.isLoaded()); + test.end(); + }; + + test.run(); } TEST(SpriteStore, LoadingCancel) { - SpriteStoreTest test(MockFileSource::Success, "sprite.json"); + SpriteStoreTest test; + + test.fileSource.spriteImageResponse = + test.fileSource.spriteJSONResponse = [&] (const Resource&) { + test.end(); + return Response(); + }; test.observer.spriteLoaded = [&] () { FAIL() << "Should never be called"; }; - test.fileSource.requestEnqueuedCallback = [&]{ - test.end(); - }; - - test.run("test/fixtures/resources/sprite"); -} - -TEST(SpriteStore, InvalidURL) { - SpriteStoreTest test(MockFileSource::Success, ""); - - test.observer.spriteError = [&] (std::exception_ptr error) { - ASSERT_TRUE(error != nullptr); - ASSERT_EQ(test.spriteStore.isLoaded(), false); - test.end(); - }; - - test.run("foo bar"); + test.run(); } diff --git a/test/style/glyph_store.cpp b/test/style/glyph_store.cpp index 0215dff3f..a4bc0e313 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 #include #include +#include +#include #include 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(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::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("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.fileSource.glyphsResponse = [&] (const Resource&) { + test.end(); + return Response(); + }; test.observer.glyphsLoaded = [&] (const std::string&, const GlyphRange&) { FAIL() << "Should never be called"; }; - test.fileSource.requestEnqueuedCallback = [&]{ - test.end(); - }; - 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.run( - "foo bar", - "Test Stack", - {{0, 255}}); -} diff --git a/test/style/pending_resources.cpp b/test/style/pending_resources.cpp deleted file mode 100644 index 111527422..000000000 --- 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 -#include -#include -#include -#include -#include - -using namespace mbgl; - -class PendingResources : public ::testing::TestWithParam { -}; - -// 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(); - HeadlessView view(display, 1, 1000, 1000); - MockFileSource fileSource(MockFileSource::Success, GetParam()); - - std::unique_ptr map = std::make_unique(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 b4a539643..000000000 --- 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 -#include -#include -#include -#include -#include -#include -#include -#include - -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 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()); - - 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 000000000..d1944f384 --- /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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +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()); + + 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::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("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::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(); + 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::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(); + 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("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(); + 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("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(); + layer->source = "source"; + layer->sourceLayer = "water"; + test.style.addLayer(std::move(layer)); + + auto info = std::make_unique(); + 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(); + 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(); + 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 000000000..59366ea7d --- /dev/null +++ b/test/style/style.cpp @@ -0,0 +1,45 @@ +#include "../fixtures/util.hpp" + +#include +#include +#include + +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 628ae3eb3..93613cebf 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',