diff --git a/test/style/glyph_store.cpp b/test/style/glyph_store.cpp new file mode 100644 index 000000000..fb4355b98 --- /dev/null +++ b/test/style/glyph_store.cpp @@ -0,0 +1,225 @@ +#include "../fixtures/fixture_log_observer.hpp" +#include "../fixtures/mock_file_source.hpp" +#include "../fixtures/util.hpp" + +#include +#include +#include +#include + +using namespace mbgl; + +using GlyphStoreTestCallback = std::function; + +struct GlyphStoreParams { + const std::string url; + const std::string stack; + const std::set ranges; +}; + +class GlyphStoreThread : public GlyphStore::Observer { +public: + GlyphStoreThread(FileSource* fileSource, GlyphStoreTestCallback callback) : callback_(callback) { + util::ThreadContext::setFileSource(fileSource); + } + + void loadGlyphStore(const GlyphStoreParams& params) { + glyphStore_.reset(new GlyphStore()); + + glyphStore_->setObserver(this); + glyphStore_->setURL(params.url); + + ASSERT_FALSE(glyphStore_->hasGlyphRanges(params.stack, params.ranges)); + } + + void unloadGlyphStore() { + glyphStore_->setObserver(nullptr); + glyphStore_.reset(); + } + + void onGlyphRangeLoaded() override { + callback_(glyphStore_.get(), nullptr); + } + + void onGlyphRangeLoadingFailed(std::exception_ptr error) override { + callback_(glyphStore_.get(), error); + } + +private: + std::unique_ptr glyphStore_; + GlyphStoreTestCallback callback_; +}; + +class GlyphStoreTest : public testing::Test { +protected: + void runTest(const GlyphStoreParams& params, FileSource* fileSource, GlyphStoreTestCallback callback) { + util::RunLoop loop(uv_default_loop()); + + async_ = std::make_unique(loop.get(), [&]{ loop.stop(); }); + async_->unref(); + + const util::ThreadContext context = {"Map", util::ThreadType::Map, util::ThreadPriority::Regular}; + + util::Thread tester(context, fileSource, callback); + tester.invoke(&GlyphStoreThread::loadGlyphStore, params); + + uv_run(loop.get(), UV_RUN_DEFAULT); + + tester.invoke(&GlyphStoreThread::unloadGlyphStore); + } + + void stopTest() { + testDone = true; + async_->send(); + } + + bool isDone() const { + return testDone; + } + +private: + bool testDone = false; + + std::unique_ptr async_; +}; + +TEST_F(GlyphStoreTest, LoadingSuccess) { + GlyphStoreParams params = { + "test/fixtures/resources/glyphs.pbf", + "Test Stack", + {{0, 255}, {256, 511}} + }; + + auto callback = [this, ¶ms](GlyphStore* store, std::exception_ptr error) { + ASSERT_TRUE(util::ThreadContext::currentlyOn(util::ThreadType::Map)); + + // We need to check if the test is over because checking + // if the GlyphStore has glyphs below will cause more requests + // to happen and we don't want this endless loop. + if (isDone()) { + return; + } + + ASSERT_EQ(error, nullptr); + + if (!store->hasGlyphRanges(params.stack, params.ranges)) { + return; + } + + ASSERT_FALSE(store->hasGlyphRanges("Foobar", params.ranges)); + ASSERT_FALSE(store->hasGlyphRanges("Foobar", {{512, 767}})); + ASSERT_FALSE(store->hasGlyphRanges("Test Stack", {{512, 767}})); + + auto fontStack = store->getFontStack(params.stack); + ASSERT_FALSE(fontStack->getMetrics().empty()); + ASSERT_FALSE(fontStack->getSDFs().empty()); + + stopTest(); + }; + + MockFileSource fileSource(MockFileSource::Success, ""); + runTest(params, &fileSource, callback); +} + +TEST_F(GlyphStoreTest, LoadingFail) { + GlyphStoreParams params = { + "test/fixtures/resources/glyphs.pbf", + "Test Stack", + {{0, 255}, {256, 511}} + }; + + auto callback = [this, ¶ms](GlyphStore* store, std::exception_ptr error) { + ASSERT_TRUE(util::ThreadContext::currentlyOn(util::ThreadType::Map)); + + if (isDone()) { + return; + } + + ASSERT_TRUE(error != nullptr); + + auto fontStack = store->getFontStack(params.stack); + ASSERT_EQ(*fontStack, nullptr); + + for (const auto& range : params.ranges) { + ASSERT_FALSE(store->hasGlyphRanges(params.stack, {range})); + } + + ASSERT_FALSE(store->hasGlyphRanges(params.stack, params.ranges)); + ASSERT_FALSE(store->hasGlyphRanges("Foobar", params.ranges)); + ASSERT_FALSE(store->hasGlyphRanges("Foobar", {{512, 767}})); + + stopTest(); + }; + + MockFileSource fileSource(MockFileSource::RequestFail, "glyphs.pbf"); + runTest(params, &fileSource, callback); +} + +TEST_F(GlyphStoreTest, LoadingCorrupted) { + GlyphStoreParams params = { + "test/fixtures/resources/glyphs.pbf", + "Test Stack", + {{0, 255}, {256, 511}} + }; + + auto callback = [this, ¶ms](GlyphStore* store, std::exception_ptr error) { + ASSERT_TRUE(util::ThreadContext::currentlyOn(util::ThreadType::Map)); + + if (isDone()) { + return; + } + + ASSERT_TRUE(error != nullptr); + + for (const auto& range : params.ranges) { + ASSERT_FALSE(store->hasGlyphRanges(params.stack, {range})); + } + + ASSERT_FALSE(store->hasGlyphRanges(params.stack, params.ranges)); + ASSERT_FALSE(store->hasGlyphRanges("Foobar", params.ranges)); + ASSERT_FALSE(store->hasGlyphRanges("Foobar", {{512, 767}})); + + stopTest(); + }; + + MockFileSource fileSource(MockFileSource::RequestWithCorruptedData, "glyphs.pbf"); + runTest(params, &fileSource, callback); +} + +TEST_F(GlyphStoreTest, LoadingCancel) { + GlyphStoreParams params = { + "test/fixtures/resources/glyphs.pbf", + "Test Stack", + {{0, 255}, {256, 511}} + }; + + auto callback = [this](GlyphStore*, std::exception_ptr) { + FAIL() << "Should never be called"; + }; + + MockFileSource fileSource(MockFileSource::SuccessWithDelay, "glyphs.pbf"); + fileSource.setOnRequestDelayedCallback([this]{ + stopTest(); + }); + runTest(params, &fileSource, callback); +} + +TEST_F(GlyphStoreTest, InvalidURL) { + GlyphStoreParams params = { + "foo bar", + "Test Stack", + {{0, 255}, {256, 511}} + }; + + auto callback = [this, ¶ms](GlyphStore* store, std::exception_ptr error) { + ASSERT_TRUE(error != nullptr); + + auto fontStack = store->getFontStack(params.stack); + ASSERT_EQ(*fontStack, nullptr); + + stopTest(); + }; + + MockFileSource fileSource(MockFileSource::Success, ""); + runTest(params, &fileSource, callback); +} diff --git a/test/test.gypi b/test/test.gypi index 7d97290c8..142445d98 100644 --- a/test/test.gypi +++ b/test/test.gypi @@ -88,6 +88,7 @@ 'storage/http_other_loop.cpp', 'storage/http_reading.cpp', + 'style/glyph_store.cpp', 'style/pending_resources.cpp', 'style/resource_loading.cpp', 'style/sprite.cpp',