diff options
author | Antonio Zugaldia <antonio@silicalabs.com> | 2015-09-14 17:24:59 -0400 |
---|---|---|
committer | Antonio Zugaldia <antonio@silicalabs.com> | 2015-09-14 17:24:59 -0400 |
commit | 7756f7727c0418a27ca987559a007bbc9a93bf51 (patch) | |
tree | 362dd1786a22d4ddf199a7aada081d37e4d7ab82 /test | |
parent | 5c74d202d3e0b603ce842c9e60eecc617a9cdbdd (diff) | |
parent | 5ef7011089e761dda0a26782c04dad9f61e65d34 (diff) | |
download | qtlocation-mapboxgl-7756f7727c0418a27ca987559a007bbc9a93bf51.tar.gz |
Merge remote-tracking branch 'mapbox/master' into 2317-espresso
Conflicts:
android/java/MapboxGLAndroidSDKTestApp/build.gradle
Diffstat (limited to 'test')
69 files changed, 1877 insertions, 536 deletions
diff --git a/test/annotations/sprite_atlas.cpp b/test/annotations/sprite_atlas.cpp new file mode 100644 index 0000000000..0eece6c47c --- /dev/null +++ b/test/annotations/sprite_atlas.cpp @@ -0,0 +1,173 @@ +#include "../fixtures/util.hpp" +#include "../fixtures/fixture_log_observer.hpp" + +#include <mbgl/geometry/sprite_atlas.hpp> +#include <mbgl/annotation/sprite_store.hpp> +#include <mbgl/annotation/sprite_parser.hpp> +#include <mbgl/util/io.hpp> +#include <mbgl/util/image.hpp> + +using namespace mbgl; + +TEST(Annotations, SpriteAtlas) { + FixtureLog log; + + auto spriteParseResult = parseSprite(util::read_file("test/fixtures/annotations/emerald.png"), + util::read_file("test/fixtures/annotations/emerald.json")); + + SpriteStore store; + store.setSprites(spriteParseResult.get<Sprites>()); + + SpriteAtlas atlas(63, 112, 1, store); + + EXPECT_EQ(1.0f, atlas.getPixelRatio()); + EXPECT_EQ(63, atlas.getWidth()); + EXPECT_EQ(112, atlas.getHeight()); + EXPECT_EQ(63, atlas.getTextureWidth()); + EXPECT_EQ(112, atlas.getTextureHeight()); + + // Image hasn't been created yet. + EXPECT_TRUE(atlas.getData()); + + auto metro = atlas.getImage("metro", false); + 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.pos.originalW); + EXPECT_EQ(18, metro.pos.originalH); + EXPECT_EQ(18, metro.texture->width); + EXPECT_EQ(18, metro.texture->height); + EXPECT_EQ(18, metro.texture->pixelWidth); + EXPECT_EQ(18, metro.texture->pixelHeight); + EXPECT_EQ(1.0f, metro.texture->pixelRatio); + + auto pos = atlas.getPosition("metro", false); + EXPECT_DOUBLE_EQ(20, pos.size[0]); + EXPECT_DOUBLE_EQ(20, pos.size[1]); + EXPECT_DOUBLE_EQ(1.0f / 63, pos.tl[0]); + EXPECT_DOUBLE_EQ(1.0f / 112, pos.tl[1]); + EXPECT_DOUBLE_EQ(21.0f / 63, pos.br[0]); + EXPECT_DOUBLE_EQ(21.0f / 112, pos.br[1]); + + + auto missing = atlas.getImage("doesnotexist", false); + EXPECT_FALSE(missing.pos.hasArea()); + EXPECT_EQ(0, missing.pos.x); + EXPECT_EQ(0, missing.pos.y); + EXPECT_EQ(0, missing.pos.w); + EXPECT_EQ(0, missing.pos.h); + EXPECT_EQ(0, missing.pos.originalW); + EXPECT_EQ(0, missing.pos.originalH); + EXPECT_FALSE(missing.texture); + + 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.getImage("metro", true); + EXPECT_EQ(20, metro2.pos.x); + EXPECT_EQ(0, metro2.pos.y); + EXPECT_EQ(20, metro2.pos.w); + EXPECT_EQ(20, metro2.pos.h); + EXPECT_EQ(18, metro2.pos.originalW); + EXPECT_EQ(18, metro2.pos.originalH); + + const size_t bytes = atlas.getTextureWidth() * atlas.getTextureHeight() * 4; + const auto hash = test::crc64(reinterpret_cast<const char*>(atlas.getData()), bytes); + EXPECT_EQ(0x9875FC0595489A9Fu, hash) << std::hex << hash; + + // util::write_file( + // "test/fixtures/annotations/atlas1.png", + // util::compress_png(atlas.getTextureWidth(), atlas.getTextureHeight(), atlas.getData())); +} + +TEST(Annotations, SpriteAtlasSize) { + auto spriteParseResult = parseSprite(util::read_file("test/fixtures/annotations/emerald.png"), + util::read_file("test/fixtures/annotations/emerald.json")); + + SpriteStore store; + store.setSprites(spriteParseResult.get<Sprites>()); + + SpriteAtlas atlas(63, 112, 1.4, store); + + EXPECT_DOUBLE_EQ(1.4f, atlas.getPixelRatio()); + EXPECT_EQ(63, atlas.getWidth()); + EXPECT_EQ(112, atlas.getHeight()); + EXPECT_EQ(89, atlas.getTextureWidth()); + EXPECT_EQ(157, atlas.getTextureHeight()); + + auto metro = atlas.getImage("metro", false); + 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.pos.originalW); + EXPECT_EQ(18, metro.pos.originalH); + EXPECT_EQ(18, metro.texture->width); + EXPECT_EQ(18, metro.texture->height); + EXPECT_EQ(18, metro.texture->pixelWidth); + EXPECT_EQ(18, metro.texture->pixelHeight); + EXPECT_EQ(1.0f, metro.texture->pixelRatio); + + const size_t bytes = atlas.getTextureWidth() * atlas.getTextureHeight() * 4; + const auto hash = test::crc64(reinterpret_cast<const char*>(atlas.getData()), bytes); + EXPECT_EQ(0x2CDDA7DBB04D116Du, hash) << std::hex << hash; + + // util::write_file( + // "test/fixtures/annotations/atlas2.png", + // util::compress_png(atlas.getTextureWidth(), atlas.getTextureHeight(), atlas.getData())); +} + +TEST(Annotations, SpriteAtlasUpdates) { + SpriteStore store; + + SpriteAtlas atlas(32, 32, 1, store); + + EXPECT_EQ(1.0f, atlas.getPixelRatio()); + EXPECT_EQ(32, atlas.getWidth()); + EXPECT_EQ(32, atlas.getHeight()); + EXPECT_EQ(32, atlas.getTextureWidth()); + EXPECT_EQ(32, atlas.getTextureHeight()); + + store.setSprite("one", std::make_shared<SpriteImage>(16, 12, 1, std::string(16 * 12 * 4, '\x00'))); + auto one = atlas.getImage("one", false); + 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.pos.originalW); + EXPECT_EQ(12, one.pos.originalH); + EXPECT_EQ(16, one.texture->width); + EXPECT_EQ(12, one.texture->height); + EXPECT_EQ(16, one.texture->pixelWidth); + EXPECT_EQ(12, one.texture->pixelHeight); + EXPECT_EQ(1.0f, one.texture->pixelRatio); + + const size_t bytes = atlas.getTextureWidth() * atlas.getTextureHeight() * 4; + const auto hash = test::crc64(reinterpret_cast<const char*>(atlas.getData()), bytes); + EXPECT_EQ(0x0000000000000000u, hash) << std::hex << hash; + + // Update sprite + auto newSprite = std::make_shared<SpriteImage>(16, 12, 1, std::string(16 * 12 * 4, '\xFF')); + store.setSprite("one", newSprite); + ASSERT_EQ(newSprite, store.getSprite("one")); + + // Atlas texture hasn't changed yet. + const auto hash2 = test::crc64(reinterpret_cast<const char*>(atlas.getData()), bytes); + EXPECT_EQ(0x0000000000000000u, hash2) << std::hex << hash2; + + atlas.updateDirty(); + + // Now the atlas texture has changed. + const auto hash3 = test::crc64(reinterpret_cast<const char*>(atlas.getData()), bytes); + EXPECT_EQ(0x4E6D4900CD2D9149u, hash3) << std::hex << hash3; + + // util::write_file( + // "test/fixtures/annotations/atlas3.png", + // util::compress_png(atlas.getTextureWidth(), atlas.getTextureHeight(), atlas.getData())); +} diff --git a/test/annotations/sprite_image.cpp b/test/annotations/sprite_image.cpp new file mode 100644 index 0000000000..9c5ca79ac9 --- /dev/null +++ b/test/annotations/sprite_image.cpp @@ -0,0 +1,64 @@ +#include "../fixtures/util.hpp" + +#include <mbgl/annotation/sprite_image.hpp> +#include <mbgl/util/exception.hpp> + +using namespace mbgl; + +TEST(Annotations, SpriteImageZeroWidth) { + try { + SpriteImage(0, 16, 2, ""); + FAIL() << "Expected exception"; + } catch (util::SpriteImageException& ex) { + EXPECT_STREQ("Sprite image dimensions may not be zero", ex.what()); + } +} + +TEST(Annotations, SpriteImageZeroHeight) { + try { + SpriteImage(16, 0, 2, ""); + FAIL() << "Expected exception"; + } catch (util::SpriteImageException& ex) { + EXPECT_STREQ("Sprite image dimensions may not be zero", ex.what()); + } +} + +TEST(Annotations, SpriteImageZeroRatio) { + try { + SpriteImage(16, 16, 0, ""); + FAIL() << "Expected exception"; + } catch (util::SpriteImageException& ex) { + EXPECT_STREQ("Sprite image dimensions may not be zero", ex.what()); + } +} + +TEST(Annotations, SpriteImageMismatchedData) { + try { + SpriteImage(16, 16, 2, ""); + FAIL() << "Expected exception"; + } catch (util::SpriteImageException& ex) { + EXPECT_STREQ("Sprite image pixel count mismatch", ex.what()); + } +} + +TEST(Annotations, SpriteImage) { + std::string pixels(32 * 24 * 4, '\0'); + SpriteImage sprite(16, 12, 2, std::move(pixels)); + EXPECT_EQ(16, sprite.width); + EXPECT_EQ(32, sprite.pixelWidth); + EXPECT_EQ(12, sprite.height); + EXPECT_EQ(24, sprite.pixelHeight); + EXPECT_EQ(2, sprite.pixelRatio); + EXPECT_EQ(32u * 24 * 4, sprite.data.size()); +} + +TEST(Annotations, SpriteImageFractionalRatio) { + std::string pixels(20 * 12 * 4, '\0'); + SpriteImage sprite(13, 8, 1.5, std::move(pixels)); + EXPECT_EQ(13, sprite.width); + EXPECT_EQ(20, sprite.pixelWidth); + EXPECT_EQ(8, sprite.height); + EXPECT_EQ(12, sprite.pixelHeight); + EXPECT_EQ(1.5, sprite.pixelRatio); + EXPECT_EQ(20u * 12 * 4, sprite.data.size()); +} diff --git a/test/annotations/sprite_parser.cpp b/test/annotations/sprite_parser.cpp new file mode 100644 index 0000000000..7033152581 --- /dev/null +++ b/test/annotations/sprite_parser.cpp @@ -0,0 +1,310 @@ +#include "../fixtures/util.hpp" +#include "../fixtures/fixture_log_observer.hpp" + +#include <mbgl/annotation/sprite_parser.hpp> +#include <mbgl/annotation/sprite_image.hpp> +#include <mbgl/util/image.hpp> +#include <mbgl/util/io.hpp> + +#include <algorithm> + +using namespace mbgl; + +TEST(Annotations, SpriteImageCreationInvalid) { + FixtureLog log; + + const util::Image image_1x(util::read_file("test/fixtures/annotations/emerald.png")); + ASSERT_TRUE(image_1x); + ASSERT_EQ(200u, image_1x.getWidth()); + ASSERT_EQ(299u, image_1x.getHeight()); + + ASSERT_EQ(nullptr, createSpriteImage(image_1x, 0, 0, 0, 16, 1, false)); // width == 0 + ASSERT_EQ(nullptr, createSpriteImage(image_1x, 0, 0, 16, 0, 1, false)); // height == 0 + ASSERT_EQ(nullptr, createSpriteImage(image_1x, 0, 0, 1, 1, 0, false)); // ratio == 0 + ASSERT_EQ(nullptr, createSpriteImage(image_1x, 0, 0, 1, 1, 23, false)); // ratio too large + ASSERT_EQ(nullptr, createSpriteImage(image_1x, 0, 0, 2048, 16, 1, false)); // too wide + ASSERT_EQ(nullptr, createSpriteImage(image_1x, 0, 0, 16, 1025, 1, false)); // too tall + + EXPECT_EQ(6u, log.count({ + EventSeverity::Warning, + Event::Sprite, + int64_t(-1), + "Can't create sprite with invalid metrics", + })); +} + +TEST(Annotations, SpriteImageCreation1x) { + const util::Image image_1x(util::read_file("test/fixtures/annotations/emerald.png")); + ASSERT_TRUE(image_1x); + ASSERT_EQ(200u, image_1x.getWidth()); + ASSERT_EQ(299u, image_1x.getHeight()); + + { // "museum_icon":{"x":177,"y":187,"width":18,"height":18,"pixelRatio":1,"sdf":false} + const auto sprite = createSpriteImage(image_1x, 177, 187, 18, 18, 1, false); + ASSERT_TRUE(sprite.get()); + EXPECT_EQ(18, sprite->width); + EXPECT_EQ(18, sprite->height); + EXPECT_EQ(18, sprite->pixelWidth); + EXPECT_EQ(18, sprite->pixelHeight); + EXPECT_EQ(1, sprite->pixelRatio); + EXPECT_EQ(0x7FCC5F263D1FFE16u, test::crc64(sprite->data)); + } + + { // outside image == blank + const auto sprite = createSpriteImage(image_1x, 200, 0, 16, 16, 1, false); + ASSERT_TRUE(sprite.get()); + EXPECT_EQ(16, sprite->width); + EXPECT_EQ(16, sprite->height); + EXPECT_EQ(16, sprite->pixelWidth); + EXPECT_EQ(16, sprite->pixelHeight); + EXPECT_EQ(1, sprite->pixelRatio); + EXPECT_EQ(0x0000000000000000u, test::crc64(sprite->data)) << std::hex << test::crc64(sprite->data); + } + + { // outside image == blank + const auto sprite = createSpriteImage(image_1x, 0, 300, 16, 16, 1, false); + ASSERT_TRUE(sprite.get()); + EXPECT_EQ(16, sprite->width); + EXPECT_EQ(16, sprite->height); + EXPECT_EQ(16, sprite->pixelWidth); + EXPECT_EQ(16, sprite->pixelHeight); + EXPECT_EQ(1, sprite->pixelRatio); + EXPECT_EQ(0x0000000000000000u, test::crc64(sprite->data)) << std::hex << test::crc64(sprite->data); + } +} + +TEST(Annotations, SpriteImageCreation2x) { + const util::Image image_2x(util::read_file("test/fixtures/annotations/emerald@2x.png")); + ASSERT_TRUE(image_2x); + + // "museum_icon":{"x":354,"y":374,"width":36,"height":36,"pixelRatio":2,"sdf":false} + const auto sprite = createSpriteImage(image_2x, 354, 374, 36, 36, 2, false); + ASSERT_TRUE(sprite.get()); + EXPECT_EQ(18, sprite->width); + EXPECT_EQ(18, sprite->height); + EXPECT_EQ(36, sprite->pixelWidth); + EXPECT_EQ(36, sprite->pixelHeight); + EXPECT_EQ(2, sprite->pixelRatio); + EXPECT_EQ(0x85F345098DD4F9E3u, test::crc64(sprite->data)); +} + +TEST(Annotations, SpriteImageCreation1_5x) { + const util::Image image_2x(util::read_file("test/fixtures/annotations/emerald@2x.png")); + ASSERT_TRUE(image_2x); + + // "museum_icon":{"x":354,"y":374,"width":36,"height":36,"pixelRatio":2,"sdf":false} + const auto sprite = createSpriteImage(image_2x, 354, 374, 36, 36, 1.5, false); + ASSERT_TRUE(sprite.get()); + EXPECT_EQ(24, sprite->width); + EXPECT_EQ(24, sprite->height); + EXPECT_EQ(36, sprite->pixelWidth); + EXPECT_EQ(36, sprite->pixelHeight); + EXPECT_EQ(1.5, sprite->pixelRatio); + EXPECT_EQ(0x85F345098DD4F9E3u, test::crc64(sprite->data)); + + // "hospital_icon":{"x":314,"y":518,"width":36,"height":36,"pixelRatio":2,"sdf":false} + const auto sprite2 = createSpriteImage(image_2x, 314, 518, 35, 35, 1.5, false); + ASSERT_TRUE(sprite2.get()); + EXPECT_EQ(24, sprite2->width); + EXPECT_EQ(24, sprite2->height); + EXPECT_EQ(36, sprite2->pixelWidth); + EXPECT_EQ(36, sprite2->pixelHeight); + EXPECT_EQ(1.5, sprite2->pixelRatio); + EXPECT_EQ(0x134A530C742DD141u, test::crc64(sprite2->data)); +} + +TEST(Annotations, SpriteParsing) { + const auto image_1x = util::read_file("test/fixtures/annotations/emerald.png"); + const auto json_1x = util::read_file("test/fixtures/annotations/emerald.json"); + + const auto images = parseSprite(image_1x, json_1x).get<Sprites>(); + + std::set<std::string> names; + std::transform(images.begin(), images.end(), std::inserter(names, names.begin()), + [](const auto& pair) { return pair.first; }); + + EXPECT_EQ(std::set<std::string>({ "airfield_icon", + "airport_icon", + "background", + "cemetery_icon", + "college_icon", + "default_1", + "default_2", + "default_3", + "default_4", + "default_5", + "default_6", + "default_marker", + "dlr", + "dlr.london-overground.london-underground.national-rail", + "dlr.london-underground", + "dlr.london-underground.national-rail", + "dlr.national-rail", + "dot", + "embassy_icon", + "fire-station_icon", + "generic-metro", + "generic-rail", + "generic_icon", + "golf_icon", + "government_icon", + "grass_pattern", + "harbor_icon", + "hospital_icon", + "hospital_striped", + "interstate_1", + "interstate_2", + "interstate_3", + "library_icon", + "london-overground", + "london-overground.london-underground", + "london-overground.london-underground.national-rail", + "london-overground.national-rail", + "london-underground", + "london-underground.national-rail", + "marker_icon", + "metro", + "metro.rer", + "monument_icon", + "moscow-metro", + "museum_icon", + "national-rail", + "oneway_motorway", + "oneway_road", + "park_icon", + "police_icon", + "post_icon", + "prison_icon", + "religious-christian_icon", + "religious-jewish_icon", + "religious-muslim_icon", + "rer", + "rer.transilien", + "s-bahn", + "s-bahn.u-bahn", + "sand_noise", + "school_icon", + "school_striped", + "secondary_marker", + "u-bahn", + "us_highway_1", + "us_highway_2", + "us_highway_3", + "us_state_1", + "us_state_2", + "us_state_3", + "washington-metro", + "wiener-linien", + "zoo_icon" }), + names); + + { + auto sprite = images.find("generic-metro")->second; + EXPECT_EQ(18, sprite->width); + EXPECT_EQ(18, sprite->height); + EXPECT_EQ(18, sprite->pixelWidth); + EXPECT_EQ(18, sprite->pixelHeight); + EXPECT_EQ(1, sprite->pixelRatio); + EXPECT_EQ(0xFF56F5F48F707147u, test::crc64(sprite->data)); + } +} + +TEST(Annotations, SpriteParsingInvalidJSON) { + const auto image_1x = util::read_file("test/fixtures/annotations/emerald.png"); + const auto json_1x = R"JSON({ "image": " })JSON"; + + const auto error = parseSprite(image_1x, json_1x).get<std::string>(); + + EXPECT_EQ(error, + std::string("Failed to parse JSON: lacks ending quotation before the end of string at offset 10")); +} + +TEST(Annotations, SpriteParsingEmptyImage) { + FixtureLog log; + + const auto image_1x = util::read_file("test/fixtures/annotations/emerald.png"); + const auto json_1x = R"JSON({ "image": {} })JSON"; + + const auto images = parseSprite(image_1x, json_1x).get<Sprites>(); + EXPECT_EQ(0u, images.size()); + + EXPECT_EQ(1u, log.count({ + EventSeverity::Warning, + Event::Sprite, + int64_t(-1), + "Can't create sprite with invalid metrics", + })); +} + +TEST(Annotations, SpriteParsingSimpleWidthHeight) { + FixtureLog log; + + const auto image_1x = util::read_file("test/fixtures/annotations/emerald.png"); + const auto json_1x = R"JSON({ "image": { "width": 32, "height": 32 } })JSON"; + + const auto images = parseSprite(image_1x, json_1x).get<Sprites>(); + EXPECT_EQ(1u, images.size()); +} + +TEST(Annotations, SpriteParsingWidthTooBig) { + FixtureLog log; + + const auto image_1x = util::read_file("test/fixtures/annotations/emerald.png"); + const auto json_1x = R"JSON({ "image": { "width": 65536, "height": 32 } })JSON"; + + const auto images = parseSprite(image_1x, json_1x).get<Sprites>(); + EXPECT_EQ(0u, images.size()); + + EXPECT_EQ(1u, log.count({ + EventSeverity::Warning, + Event::Sprite, + int64_t(-1), + "Value of 'width' must be an integer between 0 and 65535", + })); + EXPECT_EQ(1u, log.count({ + EventSeverity::Warning, + Event::Sprite, + int64_t(-1), + "Can't create sprite with invalid metrics", + })); +} + +TEST(Annotations, SpriteParsingNegativeWidth) { + FixtureLog log; + + const auto image_1x = util::read_file("test/fixtures/annotations/emerald.png"); + const auto json_1x = R"JSON({ "image": { "width": -1, "height": 32 } })JSON"; + + const auto images = parseSprite(image_1x, json_1x).get<Sprites>(); + EXPECT_EQ(0u, images.size()); + + EXPECT_EQ(1u, log.count({ + EventSeverity::Warning, + Event::Sprite, + int64_t(-1), + "Value of 'width' must be an integer between 0 and 65535", + })); + EXPECT_EQ(1u, log.count({ + EventSeverity::Warning, + Event::Sprite, + int64_t(-1), + "Can't create sprite with invalid metrics", + })); +} + +TEST(Annotations, SpriteParsingNullRatio) { + FixtureLog log; + + const auto image_1x = util::read_file("test/fixtures/annotations/emerald.png"); + const auto json_1x = R"JSON({ "image": { "width": 32, "height": 32, "pixelRatio": 0 } })JSON"; + + const auto images = parseSprite(image_1x, json_1x).get<Sprites>(); + EXPECT_EQ(0u, images.size()); + + EXPECT_EQ(1u, log.count({ + EventSeverity::Warning, + Event::Sprite, + int64_t(-1), + "Can't create sprite with invalid metrics", + })); +} diff --git a/test/annotations/sprite_store.cpp b/test/annotations/sprite_store.cpp new file mode 100644 index 0000000000..16eaad2499 --- /dev/null +++ b/test/annotations/sprite_store.cpp @@ -0,0 +1,143 @@ +#include "../fixtures/util.hpp" +#include "../fixtures/fixture_log_observer.hpp" + +#include <mbgl/annotation/sprite_store.hpp> + +using namespace mbgl; + +TEST(Annotations, SpriteStore) { + FixtureLog log; + + const auto sprite1 = std::make_shared<SpriteImage>(8, 8, 2, std::string(16 * 16 * 4, '\0')); + const auto sprite2 = std::make_shared<SpriteImage>(8, 8, 2, std::string(16 * 16 * 4, '\0')); + const auto sprite3 = std::make_shared<SpriteImage>(8, 8, 2, std::string(16 * 16 * 4, '\0')); + + using Sprites = std::map<std::string, std::shared_ptr<const SpriteImage>>; + SpriteStore store; + + // Adding single + store.setSprite("one", sprite1); + EXPECT_EQ(Sprites({ + { "one", sprite1 }, + }), + store.getDirty()); + EXPECT_EQ(Sprites(), store.getDirty()); + + // Adding multiple + store.setSprite("two", sprite2); + store.setSprite("three", sprite3); + EXPECT_EQ(Sprites({ + { "two", sprite2 }, { "three", sprite3 }, + }), + store.getDirty()); + EXPECT_EQ(Sprites(), store.getDirty()); + + // Removing + store.removeSprite("one"); + store.removeSprite("two"); + EXPECT_EQ(Sprites({ + { "one", nullptr }, { "two", nullptr }, + }), + store.getDirty()); + EXPECT_EQ(Sprites(), store.getDirty()); + + // Accessing + EXPECT_EQ(sprite3, store.getSprite("three")); + + EXPECT_TRUE(log.empty()); + + EXPECT_EQ(nullptr, store.getSprite("two")); + EXPECT_EQ(nullptr, store.getSprite("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'", + })); + + // Overwriting + store.setSprite("three", sprite1); + EXPECT_EQ(Sprites({ + { "three", sprite1 }, + }), + store.getDirty()); + EXPECT_EQ(Sprites(), store.getDirty()); +} + +TEST(Annotations, SpriteStoreOtherPixelRatio) { + FixtureLog log; + + const auto sprite1 = std::make_shared<SpriteImage>(8, 8, 1, std::string(8 * 8 * 4, '\0')); + + using Sprites = std::map<std::string, std::shared_ptr<const SpriteImage>>; + SpriteStore store; + + // Adding mismatched sprite image + store.setSprite("one", sprite1); + EXPECT_EQ(Sprites({ { "one", sprite1 } }), store.getDirty()); +} + +TEST(Annotations, SpriteStoreMultiple) { + const auto sprite1 = std::make_shared<SpriteImage>(8, 8, 2, std::string(16 * 16 * 4, '\0')); + const auto sprite2 = std::make_shared<SpriteImage>(8, 8, 2, std::string(16 * 16 * 4, '\0')); + + using Sprites = std::map<std::string, std::shared_ptr<const SpriteImage>>; + SpriteStore store; + + store.setSprites({ + { "one", sprite1 }, { "two", sprite2 }, + }); + EXPECT_EQ(Sprites({ + { "one", sprite1 }, { "two", sprite2 }, + }), + store.getDirty()); + EXPECT_EQ(Sprites(), store.getDirty()); +} + +TEST(Annotations, SpriteStoreReplace) { + FixtureLog log; + + const auto sprite1 = std::make_shared<SpriteImage>(8, 8, 2, std::string(16 * 16 * 4, '\0')); + const auto sprite2 = std::make_shared<SpriteImage>(8, 8, 2, std::string(16 * 16 * 4, '\0')); + + using Sprites = std::map<std::string, std::shared_ptr<const SpriteImage>>; + SpriteStore store; + + store.setSprite("sprite", sprite1); + EXPECT_EQ(sprite1, store.getSprite("sprite")); + store.setSprite("sprite", sprite2); + EXPECT_EQ(sprite2, store.getSprite("sprite")); + + EXPECT_EQ(Sprites({ { "sprite", sprite2 } }), store.getDirty()); +} + +TEST(Annotations, SpriteStoreReplaceWithDifferentDimensions) { + FixtureLog log; + + const auto sprite1 = std::make_shared<SpriteImage>(8, 8, 2, std::string(16 * 16 * 4, '\0')); + const auto sprite2 = std::make_shared<SpriteImage>(9, 9, 2, std::string(18 * 18 * 4, '\0')); + + using Sprites = std::map<std::string, std::shared_ptr<const SpriteImage>>; + SpriteStore store; + + store.setSprite("sprite", sprite1); + store.setSprite("sprite", sprite2); + + EXPECT_EQ(1u, log.count({ + EventSeverity::Warning, + Event::Sprite, + int64_t(-1), + "Can't change sprite dimensions for 'sprite'", + })); + + EXPECT_EQ(sprite1, store.getSprite("sprite")); + + EXPECT_EQ(Sprites({ { "sprite", sprite1 } }), store.getDirty()); +} diff --git a/test/api/annotations.cpp b/test/api/annotations.cpp new file mode 100644 index 0000000000..33374d4386 --- /dev/null +++ b/test/api/annotations.cpp @@ -0,0 +1,36 @@ +#include "../fixtures/fixture_log_observer.hpp" +#include "../fixtures/mock_file_source.hpp" +#include "../fixtures/util.hpp" + +#include <mbgl/annotation/point_annotation.hpp> +#include <mbgl/map/map.hpp> +#include <mbgl/map/still_image.hpp> +#include <mbgl/platform/default/headless_display.hpp> +#include <mbgl/platform/default/headless_view.hpp> + +#include <future> +#include <vector> + +TEST(API, PointAnnotation) { + using namespace mbgl; + + auto display = std::make_shared<mbgl::HeadlessDisplay>(); + HeadlessView view(display, 1); + + MockFileSource fileSource(MockFileSource::Success, ""); + + Map map(view, fileSource, MapMode::Still); + map.setStyleURL("test/fixtures/resources/style.json"); + + std::vector<PointAnnotation> points; + points.emplace_back(PointAnnotation({ 50.0, 50.0 }, "default_marker")); + + map.addPointAnnotations(points); + + std::promise<bool> promise; + map.renderStill([&promise](std::exception_ptr, std::unique_ptr<const StillImage>) { + promise.set_value(true); + }); + + promise.get_future().get(); +} diff --git a/test/api/api_misuse.cpp b/test/api/api_misuse.cpp index a98cf04101..1f54855323 100644 --- a/test/api/api_misuse.cpp +++ b/test/api/api_misuse.cpp @@ -17,11 +17,11 @@ TEST(API, RenderWithoutCallback) { Log::setObserver(std::unique_ptr<Log::Observer>(log)); auto display = std::make_shared<mbgl::HeadlessDisplay>(); - HeadlessView view(display); + HeadlessView view(display, 1); + view.resize(128, 512); DefaultFileSource fileSource(nullptr); std::unique_ptr<Map> map = std::make_unique<Map>(view, fileSource, MapMode::Still); - map->resize(128, 512, 1); map->renderStill(nullptr); // Force Map thread to join. @@ -39,11 +39,11 @@ TEST(API, RenderWithoutCallback) { TEST(API, RenderWithoutStyle) { auto display = std::make_shared<mbgl::HeadlessDisplay>(); - HeadlessView view(display); + HeadlessView view(display, 1); + view.resize(128, 512); DefaultFileSource fileSource(nullptr); Map map(view, fileSource, MapMode::Still); - map.resize(128, 512, 1); std::promise<std::exception_ptr> promise; map.renderStill([&promise](std::exception_ptr error, std::unique_ptr<const StillImage>) { diff --git a/test/api/repeated_render.cpp b/test/api/repeated_render.cpp index 7f55237744..3317b4e3a4 100644 --- a/test/api/repeated_render.cpp +++ b/test/api/repeated_render.cpp @@ -17,7 +17,7 @@ TEST(API, RepeatedRender) { const auto style = util::read_file("test/fixtures/api/water.json"); auto display = std::make_shared<mbgl::HeadlessDisplay>(); - HeadlessView view(display); + HeadlessView view(display, 1, 256, 512); DefaultFileSource fileSource(nullptr); Log::setObserver(std::make_unique<FixtureLogObserver>()); @@ -25,29 +25,27 @@ TEST(API, RepeatedRender) { Map map(view, fileSource, MapMode::Still); { - map.resize(128, 512, 1); - map.setStyleJSON(style, "test/suite"); + map.setStyleJSON(style, ""); std::promise<std::unique_ptr<const StillImage>> promise; map.renderStill([&promise](std::exception_ptr, std::unique_ptr<const StillImage> image) { promise.set_value(std::move(image)); }); auto result = promise.get_future().get(); - ASSERT_EQ(128, result->width); + ASSERT_EQ(256, result->width); ASSERT_EQ(512, result->height); const std::string png = util::compress_png(result->width, result->height, result->pixels.get()); util::write_file("test/fixtures/api/1.png", png); } { - map.resize(512, 512, 2); map.setStyleJSON(style, "TEST_DATA/suite"); std::promise<std::unique_ptr<const StillImage>> promise; map.renderStill([&promise](std::exception_ptr, std::unique_ptr<const StillImage> image) { promise.set_value(std::move(image)); }); auto result = promise.get_future().get(); - ASSERT_EQ(1024, result->width); - ASSERT_EQ(1024, result->height); + ASSERT_EQ(256, result->width); + ASSERT_EQ(512, result->height); const std::string png = util::compress_png(result->width, result->height, result->pixels.get()); util::write_file("test/fixtures/api/2.png", png); } diff --git a/test/api/set_style.cpp b/test/api/set_style.cpp index 72260e6343..c3e62def20 100644 --- a/test/api/set_style.cpp +++ b/test/api/set_style.cpp @@ -11,14 +11,14 @@ TEST(API, SetStyle) { using namespace mbgl; auto display = std::make_shared<mbgl::HeadlessDisplay>(); - HeadlessView view(display); + HeadlessView view(display, 1); DefaultFileSource fileSource(nullptr); Log::setObserver(std::make_unique<FixtureLogObserver>()); { Map map(view, fileSource, MapMode::Still); - map.setStyleJSON("invalid", "test/suite"); + map.setStyleJSON("invalid", ""); } auto observer = Log::removeObserver(); diff --git a/test/fixtures/annotations/emerald.json b/test/fixtures/annotations/emerald.json new file mode 100644 index 0000000000..dcc2b4808c --- /dev/null +++ b/test/fixtures/annotations/emerald.json @@ -0,0 +1 @@ +{"background":{"x":0,"y":20,"width":50,"height":50,"pixelRatio":1,"sdf":false},"grass_pattern":{"x":100,"y":80,"width":50,"height":50,"pixelRatio":1,"sdf":false},"interstate_1":{"x":0,"y":100,"width":41,"height":40,"pixelRatio":1,"sdf":false},"interstate_2":{"x":0,"y":100,"width":41,"height":40,"pixelRatio":1,"sdf":false},"interstate_3":{"x":41,"y":100,"width":48,"height":39,"pixelRatio":1,"sdf":false},"us_state_1":{"x":0,"y":73,"width":29,"height":24,"pixelRatio":1,"sdf":false},"us_state_2":{"x":0,"y":73,"width":29,"height":24,"pixelRatio":1,"sdf":false},"us_state_3":{"x":30,"y":73,"width":32,"height":24,"pixelRatio":1,"sdf":false},"us_highway_1":{"x":0,"y":142,"width":29,"height":29,"pixelRatio":1,"sdf":false},"us_highway_2":{"x":30,"y":142,"width":33,"height":29,"pixelRatio":1,"sdf":false},"us_highway_3":{"x":64,"y":142,"width":36,"height":29,"pixelRatio":1,"sdf":false},"default_1":{"x":0,"y":0,"width":17,"height":16,"pixelRatio":1,"sdf":false},"default_2":{"x":17,"y":0,"width":22,"height":16,"pixelRatio":1,"sdf":false},"default_3":{"x":39,"y":0,"width":27,"height":16,"pixelRatio":1,"sdf":false},"default_4":{"x":66,"y":0,"width":32,"height":16,"pixelRatio":1,"sdf":false},"default_5":{"x":98,"y":0,"width":37,"height":16,"pixelRatio":1,"sdf":false},"default_6":{"x":135,"y":0,"width":42,"height":16,"pixelRatio":1,"sdf":false},"london-overground":{"x":70,"y":25,"width":18,"height":18,"pixelRatio":1,"sdf":false},"london-underground":{"x":88,"y":25,"width":18,"height":18,"pixelRatio":1,"sdf":false},"national-rail":{"x":106,"y":25,"width":18,"height":18,"pixelRatio":1,"sdf":false},"dlr":{"x":106,"y":25,"width":18,"height":18,"pixelRatio":1,"sdf":false},"dlr.london-overground.london-underground.national-rail":{"x":70,"y":25,"width":72,"height":18,"pixelRatio":1,"sdf":false},"dlr.london-underground":{"x":88,"y":25,"width":36,"height":18,"pixelRatio":1,"sdf":false},"dlr.london-underground.national-rail":{"x":88,"y":25,"width":54,"height":18,"pixelRatio":1,"sdf":false},"dlr.national-rail":{"x":106,"y":25,"width":36,"height":18,"pixelRatio":1,"sdf":false},"london-overground.london-underground":{"x":70,"y":25,"width":36,"height":18,"pixelRatio":1,"sdf":false},"london-overground.london-underground.national-rail":{"x":124,"y":25,"width":54,"height":18,"pixelRatio":1,"sdf":false},"london-overground.national-rail":{"x":124,"y":25,"width":36,"height":18,"pixelRatio":1,"sdf":false},"london-underground.national-rail":{"x":124,"y":43,"width":36,"height":18,"pixelRatio":1,"sdf":false},"metro":{"x":71,"y":43,"width":18,"height":18,"pixelRatio":1,"sdf":false},"rer":{"x":87,"y":43,"width":18,"height":18,"pixelRatio":1,"sdf":false},"metro.rer":{"x":71,"y":43,"width":34,"height":18,"pixelRatio":1,"sdf":false},"rer.transilien":{"x":87,"y":43,"width":36,"height":18,"pixelRatio":1,"sdf":false},"u-bahn":{"x":70,"y":62,"width":18,"height":18,"pixelRatio":1,"sdf":false},"s-bahn":{"x":88,"y":62,"width":18,"height":18,"pixelRatio":1,"sdf":false},"s-bahn.u-bahn":{"x":70,"y":62,"width":36,"height":18,"pixelRatio":1,"sdf":false},"washington-metro":{"x":106,"y":62,"width":18,"height":18,"pixelRatio":1,"sdf":false},"wiener-linien":{"x":124,"y":62,"width":18,"height":18,"pixelRatio":1,"sdf":false},"moscow-metro":{"x":142,"y":61,"width":21,"height":18,"pixelRatio":1,"sdf":false},"generic-metro":{"x":160,"y":43,"width":18,"height":18,"pixelRatio":1,"sdf":false},"generic-rail":{"x":178,"y":43,"width":18,"height":18,"pixelRatio":1,"sdf":false},"dot":{"x":166,"y":63,"width":13,"height":13,"pixelRatio":1,"sdf":false},"default_marker":{"x":0,"y":175,"width":33,"height":86,"pixelRatio":1,"sdf":false},"secondary_marker":{"x":33,"y":175,"width":33,"height":86,"pixelRatio":1,"sdf":false},"oneway_motorway":{"x":178,"y":24,"width":21,"height":19,"pixelRatio":1,"sdf":false},"oneway_road":{"x":178,"y":62,"width":21,"height":19,"pixelRatio":1,"sdf":false},"hospital_icon":{"x":157,"y":259,"width":18,"height":18,"pixelRatio":1,"sdf":false},"fire-station_icon":{"x":157,"y":241,"width":18,"height":18,"pixelRatio":1,"sdf":false},"cemetery_icon":{"x":157,"y":79,"width":18,"height":18,"pixelRatio":1,"sdf":false},"zoo_icon":{"x":177,"y":79,"width":18,"height":18,"pixelRatio":1,"sdf":false},"park_icon":{"x":177,"y":97,"width":18,"height":18,"pixelRatio":1,"sdf":false},"golf_icon":{"x":177,"y":115,"width":18,"height":18,"pixelRatio":1,"sdf":false},"school_icon":{"x":177,"y":133,"width":18,"height":18,"pixelRatio":1,"sdf":false},"monument_icon":{"x":177,"y":151,"width":18,"height":18,"pixelRatio":1,"sdf":false},"library_icon":{"x":177,"y":169,"width":18,"height":18,"pixelRatio":1,"sdf":false},"museum_icon":{"x":177,"y":187,"width":18,"height":18,"pixelRatio":1,"sdf":false},"college_icon":{"x":177,"y":205,"width":18,"height":18,"pixelRatio":1,"sdf":false},"religious-christian_icon":{"x":157,"y":115,"width":18,"height":18,"pixelRatio":1,"sdf":false},"religious-jewish_icon":{"x":157,"y":133,"width":18,"height":18,"pixelRatio":1,"sdf":false},"religious-muslim_icon":{"x":157,"y":151,"width":18,"height":18,"pixelRatio":1,"sdf":false},"government_icon":{"x":157,"y":169,"width":18,"height":18,"pixelRatio":1,"sdf":false},"post_icon":{"x":157,"y":205,"width":18,"height":18,"pixelRatio":1,"sdf":false},"embassy_icon":{"x":157,"y":223,"width":18,"height":18,"pixelRatio":1,"sdf":false},"police_icon":{"x":157,"y":169,"width":18,"height":18,"pixelRatio":1,"sdf":false},"marker_icon":{"x":157,"y":169,"width":18,"height":18,"pixelRatio":1,"sdf":false},"prison_icon":{"x":157,"y":169,"width":18,"height":18,"pixelRatio":1,"sdf":false},"airfield_icon":{"x":157,"y":187,"width":18,"height":18,"pixelRatio":1,"sdf":false},"airport_icon":{"x":157,"y":187,"width":18,"height":18,"pixelRatio":1,"sdf":false},"harbor_icon":{"x":139,"y":169,"width":18,"height":18,"pixelRatio":1,"sdf":false},"generic_icon":{"x":139,"y":187,"width":18,"height":18,"pixelRatio":1,"sdf":false},"hospital_striped":{"x":117,"y":135,"width":3,"height":3,"pixelRatio":1,"sdf":false},"school_striped":{"x":114,"y":135,"width":3,"height":3,"pixelRatio":1,"sdf":false},"sand_noise":{"x":75,"y":174,"width":50,"height":50,"pixelRatio":1,"sdf":false}}
\ No newline at end of file diff --git a/test/fixtures/annotations/emerald.png b/test/fixtures/annotations/emerald.png Binary files differnew file mode 100644 index 0000000000..967f2e76a6 --- /dev/null +++ b/test/fixtures/annotations/emerald.png diff --git a/test/fixtures/annotations/emerald@2x.json b/test/fixtures/annotations/emerald@2x.json new file mode 100644 index 0000000000..250aa36194 --- /dev/null +++ b/test/fixtures/annotations/emerald@2x.json @@ -0,0 +1 @@ +{"background":{"x":0,"y":40,"width":100,"height":100,"pixelRatio":2,"sdf":false},"grass_pattern":{"x":200,"y":160,"width":100,"height":100,"pixelRatio":2,"sdf":false},"interstate_1":{"x":0,"y":200,"width":82,"height":80,"pixelRatio":2,"sdf":false},"interstate_2":{"x":0,"y":200,"width":82,"height":80,"pixelRatio":2,"sdf":false},"interstate_3":{"x":82,"y":200,"width":96,"height":78,"pixelRatio":2,"sdf":false},"us_state_1":{"x":0,"y":146,"width":58,"height":48,"pixelRatio":2,"sdf":false},"us_state_2":{"x":0,"y":146,"width":58,"height":48,"pixelRatio":2,"sdf":false},"us_state_3":{"x":60,"y":146,"width":64,"height":48,"pixelRatio":2,"sdf":false},"us_highway_1":{"x":0,"y":284,"width":58,"height":58,"pixelRatio":2,"sdf":false},"us_highway_2":{"x":60,"y":284,"width":66,"height":58,"pixelRatio":2,"sdf":false},"us_highway_3":{"x":128,"y":284,"width":72,"height":58,"pixelRatio":2,"sdf":false},"default_1":{"x":0,"y":0,"width":34,"height":32,"pixelRatio":2,"sdf":false},"default_2":{"x":34,"y":0,"width":44,"height":32,"pixelRatio":2,"sdf":false},"default_3":{"x":78,"y":0,"width":54,"height":32,"pixelRatio":2,"sdf":false},"default_4":{"x":132,"y":0,"width":64,"height":32,"pixelRatio":2,"sdf":false},"default_5":{"x":196,"y":0,"width":74,"height":32,"pixelRatio":2,"sdf":false},"default_6":{"x":270,"y":0,"width":84,"height":32,"pixelRatio":2,"sdf":false},"london-overground":{"x":140,"y":50,"width":36,"height":36,"pixelRatio":2,"sdf":false},"london-underground":{"x":176,"y":50,"width":36,"height":36,"pixelRatio":2,"sdf":false},"national-rail":{"x":212,"y":50,"width":36,"height":36,"pixelRatio":2,"sdf":false},"dlr":{"x":212,"y":50,"width":36,"height":36,"pixelRatio":2,"sdf":false},"dlr.london-overground.london-underground.national-rail":{"x":140,"y":50,"width":144,"height":36,"pixelRatio":2,"sdf":false},"dlr.london-underground":{"x":176,"y":50,"width":72,"height":36,"pixelRatio":2,"sdf":false},"dlr.london-underground.national-rail":{"x":176,"y":50,"width":108,"height":36,"pixelRatio":2,"sdf":false},"dlr.national-rail":{"x":212,"y":50,"width":72,"height":36,"pixelRatio":2,"sdf":false},"london-overground.london-underground":{"x":140,"y":50,"width":72,"height":36,"pixelRatio":2,"sdf":false},"london-overground.london-underground.national-rail":{"x":248,"y":50,"width":108,"height":36,"pixelRatio":2,"sdf":false},"london-overground.national-rail":{"x":248,"y":50,"width":72,"height":36,"pixelRatio":2,"sdf":false},"london-underground.national-rail":{"x":248,"y":86,"width":72,"height":36,"pixelRatio":2,"sdf":false},"metro":{"x":142,"y":86,"width":36,"height":36,"pixelRatio":2,"sdf":false},"rer":{"x":174,"y":86,"width":36,"height":36,"pixelRatio":2,"sdf":false},"metro.rer":{"x":142,"y":86,"width":68,"height":36,"pixelRatio":2,"sdf":false},"rer.transilien":{"x":174,"y":86,"width":72,"height":36,"pixelRatio":2,"sdf":false},"u-bahn":{"x":140,"y":124,"width":36,"height":36,"pixelRatio":2,"sdf":false},"s-bahn":{"x":176,"y":124,"width":36,"height":36,"pixelRatio":2,"sdf":false},"s-bahn.u-bahn":{"x":140,"y":124,"width":72,"height":36,"pixelRatio":2,"sdf":false},"washington-metro":{"x":212,"y":124,"width":36,"height":36,"pixelRatio":2,"sdf":false},"wiener-linien":{"x":248,"y":124,"width":36,"height":36,"pixelRatio":2,"sdf":false},"moscow-metro":{"x":284,"y":122,"width":42,"height":36,"pixelRatio":2,"sdf":false},"generic-metro":{"x":320,"y":86,"width":36,"height":36,"pixelRatio":2,"sdf":false},"generic-rail":{"x":356,"y":86,"width":36,"height":36,"pixelRatio":2,"sdf":false},"dot":{"x":332,"y":126,"width":26,"height":26,"pixelRatio":2,"sdf":false},"default_marker":{"x":0,"y":350,"width":66,"height":172,"pixelRatio":2,"sdf":false},"secondary_marker":{"x":66,"y":350,"width":66,"height":172,"pixelRatio":2,"sdf":false},"oneway_motorway":{"x":356,"y":48,"width":42,"height":38,"pixelRatio":2,"sdf":false},"oneway_road":{"x":356,"y":124,"width":42,"height":38,"pixelRatio":2,"sdf":false},"hospital_icon":{"x":314,"y":518,"width":36,"height":36,"pixelRatio":2,"sdf":false},"fire-station_icon":{"x":314,"y":482,"width":36,"height":36,"pixelRatio":2,"sdf":false},"cemetery_icon":{"x":314,"y":158,"width":36,"height":36,"pixelRatio":2,"sdf":false},"zoo_icon":{"x":354,"y":158,"width":36,"height":36,"pixelRatio":2,"sdf":false},"park_icon":{"x":354,"y":194,"width":36,"height":36,"pixelRatio":2,"sdf":false},"golf_icon":{"x":354,"y":230,"width":36,"height":36,"pixelRatio":2,"sdf":false},"school_icon":{"x":354,"y":266,"width":36,"height":36,"pixelRatio":2,"sdf":false},"monument_icon":{"x":354,"y":302,"width":36,"height":36,"pixelRatio":2,"sdf":false},"library_icon":{"x":354,"y":338,"width":36,"height":36,"pixelRatio":2,"sdf":false},"museum_icon":{"x":354,"y":374,"width":36,"height":36,"pixelRatio":2,"sdf":false},"college_icon":{"x":354,"y":410,"width":36,"height":36,"pixelRatio":2,"sdf":false},"religious-christian_icon":{"x":314,"y":230,"width":36,"height":36,"pixelRatio":2,"sdf":false},"religious-jewish_icon":{"x":314,"y":266,"width":36,"height":36,"pixelRatio":2,"sdf":false},"religious-muslim_icon":{"x":314,"y":302,"width":36,"height":36,"pixelRatio":2,"sdf":false},"government_icon":{"x":314,"y":338,"width":36,"height":36,"pixelRatio":2,"sdf":false},"post_icon":{"x":314,"y":410,"width":36,"height":36,"pixelRatio":2,"sdf":false},"embassy_icon":{"x":314,"y":446,"width":36,"height":36,"pixelRatio":2,"sdf":false},"police_icon":{"x":314,"y":338,"width":36,"height":36,"pixelRatio":2,"sdf":false},"marker_icon":{"x":314,"y":338,"width":36,"height":36,"pixelRatio":2,"sdf":false},"prison_icon":{"x":314,"y":338,"width":36,"height":36,"pixelRatio":2,"sdf":false},"airfield_icon":{"x":314,"y":374,"width":36,"height":36,"pixelRatio":2,"sdf":false},"airport_icon":{"x":314,"y":374,"width":36,"height":36,"pixelRatio":2,"sdf":false},"harbor_icon":{"x":278,"y":338,"width":36,"height":36,"pixelRatio":2,"sdf":false},"generic_icon":{"x":278,"y":374,"width":36,"height":36,"pixelRatio":2,"sdf":false},"hospital_striped":{"x":234,"y":270,"width":6,"height":6,"pixelRatio":2,"sdf":false},"school_striped":{"x":228,"y":270,"width":6,"height":6,"pixelRatio":2,"sdf":false},"sand_noise":{"x":150,"y":348,"width":100,"height":100,"pixelRatio":2,"sdf":false}}
\ No newline at end of file diff --git a/test/fixtures/annotations/emerald@2x.png b/test/fixtures/annotations/emerald@2x.png Binary files differnew file mode 100644 index 0000000000..a1ffbd95ea --- /dev/null +++ b/test/fixtures/annotations/emerald@2x.png diff --git a/test/fixtures/api/water.json b/test/fixtures/api/water.json index 2bcce11992..c969b345ae 100644 --- a/test/fixtures/api/water.json +++ b/test/fixtures/api/water.json @@ -1,5 +1,5 @@ { - "version": 7, + "version": 8, "name": "Water", "sources": { "mapbox": { diff --git a/test/fixtures/fixture_log_observer.cpp b/test/fixtures/fixture_log_observer.cpp index b3b8e56fdb..a6e89e2e79 100644 --- a/test/fixtures/fixture_log_observer.cpp +++ b/test/fixtures/fixture_log_observer.cpp @@ -3,33 +3,50 @@ namespace mbgl { -FixtureLogObserver::LogMessage::LogMessage(EventSeverity severity_, Event event_, int64_t code_, - const std::string &msg_) +FixtureLog::Message::Message(EventSeverity severity_, + Event event_, + int64_t code_, + const std::string& msg_) : severity(severity_), event(event_), code(code_), msg(msg_) { } -FixtureLogObserver::LogMessage::LogMessage() : severity(), event(), code(), msg() { +bool FixtureLog::Message::operator==(const Message& rhs) const { + return severity == rhs.severity && event == rhs.event && code == rhs.code && msg == rhs.msg; } -bool FixtureLogObserver::LogMessage::operator==(const LogMessage &rhs) const { - return (!severity || !rhs.severity || severity.get() == rhs.severity.get()) && - (!event || !rhs.event || event.get() == rhs.event.get()) && - (!code || !rhs.code || code.get() == rhs.code.get()) && - (!msg || !rhs.msg || msg.get() == rhs.msg.get()); +FixtureLog::Message::Message() : severity(), event(), code(), msg() { } -FixtureLogObserver::~FixtureLogObserver() { +FixtureLog::Observer::Observer(FixtureLog* log_) : log(log_) { +} + +FixtureLog::Observer::~Observer() { + if (log) { + log->observer = nullptr; + } std::cerr << unchecked(); } -bool FixtureLogObserver::onRecord(EventSeverity severity, Event event, int64_t code, - const std::string &msg) { +bool FixtureLog::Observer::onRecord(EventSeverity severity, + Event event, + int64_t code, + const std::string& msg) { + std::lock_guard<std::mutex> lock(messagesMutex); + messages.emplace_back(severity, event, code, msg); return true; } -size_t FixtureLogObserver::count(const LogMessage &message) const { +bool FixtureLog::Observer::empty() const { + std::lock_guard<std::mutex> lock(messagesMutex); + + return messages.empty(); +} + +size_t FixtureLog::Observer::count(const Message& message) const { + std::lock_guard<std::mutex> lock(messagesMutex); + size_t message_count = 0; for (const auto& msg : messages) { if (msg == message) { @@ -40,8 +57,28 @@ size_t FixtureLogObserver::count(const LogMessage &message) const { return message_count; } -std::vector<FixtureLogObserver::LogMessage> FixtureLogObserver::unchecked() const { - std::vector<LogMessage> unchecked_messages; +FixtureLog::FixtureLog() : observer(new FixtureLogObserver(this)) { + Log::setObserver(std::unique_ptr<Log::Observer>(observer)); +} + +bool FixtureLog::empty() const { + return observer ? observer->empty() : true; +} + +size_t FixtureLog::count(const FixtureLog::Message& message) const { + return observer ? observer->count(message) : 0; +} + +FixtureLog::~FixtureLog() { + if (observer) { + Log::removeObserver(); + } +} + +std::vector<FixtureLog::Message> FixtureLogObserver::unchecked() const { + std::lock_guard<std::mutex> lock(messagesMutex); + + std::vector<Message> unchecked_messages; for (const auto& msg : messages) { if (!msg.checked) { unchecked_messages.push_back(msg); @@ -51,21 +88,18 @@ std::vector<FixtureLogObserver::LogMessage> FixtureLogObserver::unchecked() cons return unchecked_messages; } -::std::ostream &operator<<(::std::ostream &os, - const std::vector<FixtureLogObserver::LogMessage> &messages) { +::std::ostream& operator<<(::std::ostream& os, const std::vector<FixtureLog::Message>& messages) { for (const auto& message : messages) { os << "- " << message; } return os; } -::std::ostream &operator<<(::std::ostream &os, const FixtureLogObserver::LogMessage &message) { - os << "[\"" << message.severity.get() << "\", \"" << message.event.get() << "\""; - if (message.code) - os << ", " << message.code.get(); - if (message.msg) - os << ", \"" << message.msg.get() << "\""; +::std::ostream& operator<<(::std::ostream& os, const FixtureLog::Message& message) { + os << "[\"" << message.severity << "\", \"" << message.event << "\""; + os << ", " << message.code; + os << ", \"" << message.msg << "\""; return os << "]" << std::endl; } -} +} // namespace mbgl diff --git a/test/fixtures/fixture_log_observer.hpp b/test/fixtures/fixture_log_observer.hpp index 7e419a617f..f2ccb5cb58 100644 --- a/test/fixtures/fixture_log_observer.hpp +++ b/test/fixtures/fixture_log_observer.hpp @@ -2,46 +2,70 @@ #define MBGL_TEST_FIXTURE_LOG_OBSERVER #include <mbgl/platform/log.hpp> -#include <mbgl/util/optional.hpp> #include <vector> #include <cstdarg> +#include <mutex> #include <iostream> namespace mbgl { -class FixtureLogObserver : public Log::Observer { +class FixtureLog { public: - struct LogMessage { - LogMessage(EventSeverity severity_, Event event_, int64_t code_, const std::string &msg_); - LogMessage(); + struct Message { + Message(EventSeverity severity_, Event event_, int64_t code_, const std::string &msg_); + Message(); - bool operator==(const LogMessage &rhs) const; + bool operator==(const Message& rhs) const; - const mapbox::util::optional<EventSeverity> severity; - const mapbox::util::optional<Event> event; - const mapbox::util::optional<int64_t> code; - const mapbox::util::optional<std::string> msg; + const EventSeverity severity; + const Event event; + const int64_t code; + const std::string msg; mutable bool checked = false; }; - ~FixtureLogObserver(); + class Observer : public Log::Observer { + public: + using LogMessage = Message; - // Log::Observer implementation - virtual bool onRecord(EventSeverity severity, Event event, int64_t code, const std::string &msg) override; + Observer(FixtureLog* log = nullptr); + ~Observer(); - size_t count(const LogMessage &message) const; - std::vector<LogMessage> unchecked() const; + // Log::Observer implementation + virtual bool onRecord(EventSeverity severity, + Event event, + int64_t code, + const std::string& msg) override; -public: - std::vector<LogMessage> messages; + bool empty() const; + size_t count(const Message& message) const; + std::vector<Message> unchecked() const; + + private: + FixtureLog* log; + std::vector<Message> messages; + mutable std::mutex messagesMutex; + }; + + FixtureLog(); + + bool empty() const; + size_t count(const Message& message) const; + + ~FixtureLog(); + +private: + Observer* observer; }; ::std::ostream &operator<<(::std::ostream &os, - const std::vector<FixtureLogObserver::LogMessage> &messages); -::std::ostream &operator<<(::std::ostream &os, const FixtureLogObserver::LogMessage &message); + const std::vector<FixtureLog::Observer::LogMessage> &messages); +::std::ostream &operator<<(::std::ostream &os, const FixtureLog::Observer::LogMessage &message); + +using FixtureLogObserver = FixtureLog::Observer; -} +} // namespace mbgl #endif diff --git a/test/fixtures/headless/pois.json b/test/fixtures/headless/pois.json new file mode 100644 index 0000000000..a7fcfdb9dc --- /dev/null +++ b/test/fixtures/headless/pois.json @@ -0,0 +1,26 @@ +{ + "version": 8, + "name": "POIs", + "sources": { + "mapbox": { + "type": "vector", + "url": "asset://TEST_DATA/fixtures/tiles/streets.json" + } + }, + "layers": [{ + "id": "background", + "type": "background", + "paint": { + "background-color": "#CCCCCC" + } + }, { + "id": "pois", + "type": "symbol", + "source": "mapbox", + "source-layer": "poi_label", + "filter": ["in", "maki", "cafe", "bakery"], + "layout": { + "icon-image": "{maki}" + } + }] +} diff --git a/test/style/mock_file_source.cpp b/test/fixtures/mock_file_source.cpp index 6aa735d0ea..b420ba7298 100644 --- a/test/style/mock_file_source.cpp +++ b/test/fixtures/mock_file_source.cpp @@ -17,8 +17,8 @@ namespace mbgl { class MockFileSource::Impl { public: - Impl(uv_loop_t* loop, Type type, const std::string& match) - : type_(type), match_(match), timer_(loop) { + Impl(Type type, const std::string& match) + : type_(type), match_(match), timer_(util::RunLoop::getLoop()) { timer_.start(timeout, timeout, [this] { dispatchPendingRequests(); }); timer_.unref(); } @@ -53,7 +53,13 @@ private: void MockFileSource::Impl::replyWithSuccess(Request* req) const { std::shared_ptr<Response> res = std::make_shared<Response>(); res->status = Response::Status::Successful; - res->data = util::read_file(req->resource.url); + + try { + res->data = util::read_file(req->resource.url); + } catch (const std::exception& err) { + res->status = Response::Status::Error; + res->message = err.what(); + } req->notify(res); } @@ -117,11 +123,13 @@ void MockFileSource::Impl::handleRequest(Request* req) { void MockFileSource::Impl::cancelRequest(Request* req) { auto it = std::find(pendingRequests_.begin(), pendingRequests_.end(), req); if (it != pendingRequests_.end()) { - (*it)->destruct(); pendingRequests_.erase(it); } else { - EXPECT_TRUE(false) << "Should never be reached."; + // There is no request for this URL anymore. Likely, the request already completed + // before we got around to process the cancelation request. } + + req->destruct(); } void MockFileSource::Impl::dispatchPendingRequests() { @@ -142,7 +150,7 @@ void MockFileSource::setOnRequestDelayedCallback(std::function<void(void)> callb Request* MockFileSource::request(const Resource& resource, uv_loop_t* loop, Callback callback) { Request* req = new Request(resource, loop, std::move(callback)); - thread_->invokeSync(&Impl::handleRequest, req); + thread_->invoke(&Impl::handleRequest, req); return req; } diff --git a/test/style/mock_file_source.hpp b/test/fixtures/mock_file_source.hpp index 6bee95dcf8..6bee95dcf8 100644 --- a/test/style/mock_file_source.hpp +++ b/test/fixtures/mock_file_source.hpp diff --git a/test/fixtures/mock_view.hpp b/test/fixtures/mock_view.hpp index dcbe3fef84..e608545da5 100644 --- a/test/fixtures/mock_view.hpp +++ b/test/fixtures/mock_view.hpp @@ -3,6 +3,8 @@ #include <mbgl/map/view.hpp> +#include <array> + namespace mbgl { class MockView : public View { @@ -10,11 +12,16 @@ public: MockView() = default; // View implementation. + float getPixelRatio() const override { return 1; } + std::array<uint16_t, 2> getSize() const override { return {{ 0, 0 }}; } + std::array<uint16_t, 2> getFramebufferSize() const override { return {{ 0, 0 }}; } + void activate() override {}; void deactivate() override {}; void notify() override {}; void invalidate() override {} - void swap() override {} + void beforeRender() override {} + void afterRender() override {} }; } diff --git a/test/fixtures/resources/raster.png b/test/fixtures/resources/raster.png Binary files differnew file mode 100644 index 0000000000..78ad885baf --- /dev/null +++ b/test/fixtures/resources/raster.png diff --git a/test/fixtures/resources/source_raster.json b/test/fixtures/resources/source_raster.json new file mode 100644 index 0000000000..b114988edb --- /dev/null +++ b/test/fixtures/resources/source_raster.json @@ -0,0 +1 @@ +{"attribution":"<a href=\"https://www.mapbox.com/about/maps/\" target=\"_blank\">© Mapbox</a> <a href=\"http://www.openstreetmap.org/about/\" target=\"_blank\">© OpenStreetMap</a> <a class=\"mapbox-improve-map\" href=\"https://www.mapbox.com/map-feedback/\" target=\"_blank\">Improve this map</a> <a href=\"https://www.digitalglobe.com/\" target=\"_blank\">© DigitalGlobe</a>","autoscale":true,"bounds":[-180,-85,180,85],"center":[0,0,3],"description":"","id":"mapbox.satellite","maxzoom":19,"minzoom":0,"name":"Mapbox Satellite","private":true,"scheme":"xyz","tilejson":"2.0.0","tiles":["test/fixtures/resources/raster.png"]} diff --git a/test/fixtures/resources/source.json b/test/fixtures/resources/source_vector.json index db516f9f95..db516f9f95 100644 --- a/test/fixtures/resources/source.json +++ b/test/fixtures/resources/source_vector.json diff --git a/test/fixtures/resources/style.json b/test/fixtures/resources/style.json index 698f3fb1e8..fc5f9c973b 100644 --- a/test/fixtures/resources/style.json +++ b/test/fixtures/resources/style.json @@ -1,10 +1,15 @@ { - "version": 1, + "version": 8, "name": "Test", "sources": { - "compositedsource": { - "url": "test/fixtures/resources/source.json", + "vectorsource": { + "url": "test/fixtures/resources/source_vector.json", "type": "vector" + }, + "rastersource": { + "url": "test/fixtures/resources/source_raster.json", + "type": "raster", + "tileSize": 256 } }, "sprite": "test/fixtures/resources/sprite", @@ -12,19 +17,19 @@ "layers": [{ "id": "road", "type": "symbol", - "source": "compositedsource", + "source": "vectorsource", "source-layer": "road_label", "layout": { - "text-font": "Open Sans Regular, Arial Unicode MS Regular", + "text-font": ["Open Sans Regular", "Arial Unicode MS Regular"], "text-field": "{name_en}" } }, { "id": "poi", "type": "symbol", - "source": "compositedsource", + "source": "vectorsource", "source-layer": "poi_label", "layout": { - "text-font": "Open Sans Regular, Arial Unicode MS Regular", + "text-font": ["Open Sans Regular", "Arial Unicode MS Regular"], "icon-image": "{maki}_icon" } }] diff --git a/test/fixtures/style_parser/circle-blur.info.json b/test/fixtures/style_parser/circle-blur.info.json new file mode 100644 index 0000000000..397e4cd4d1 --- /dev/null +++ b/test/fixtures/style_parser/circle-blur.info.json @@ -0,0 +1,7 @@ +{ + "default": { + "log": [ + [1, "WARNING", "ParseStyle", "value of 'circle-blur' must be a number, or a number function"] + ] + } +} diff --git a/test/fixtures/style_parser/circle-blur.style.json b/test/fixtures/style_parser/circle-blur.style.json new file mode 100644 index 0000000000..8140ad5e47 --- /dev/null +++ b/test/fixtures/style_parser/circle-blur.style.json @@ -0,0 +1,18 @@ +{ + "version": 8, + "sources": { + "mapbox": { + "type": "vector", + "url": "mapbox://mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5", + "maxzoom": 14 + } + }, + "layers": [{ + "id": "circle_blur_example", + "type": "circle", + "source": "mapbox", + "paint": { + "circle-blur": null + } + }] +} diff --git a/test/fixtures/style_parser/circle-color.info.json b/test/fixtures/style_parser/circle-color.info.json new file mode 100644 index 0000000000..b1708c2f05 --- /dev/null +++ b/test/fixtures/style_parser/circle-color.info.json @@ -0,0 +1,7 @@ +{ + "default": { + "log": [ + [1, "WARNING", "ParseStyle", "color value must be a string"] + ] + } +} diff --git a/test/fixtures/style_parser/circle-color.style.json b/test/fixtures/style_parser/circle-color.style.json new file mode 100644 index 0000000000..44c32f99ce --- /dev/null +++ b/test/fixtures/style_parser/circle-color.style.json @@ -0,0 +1,18 @@ +{ + "version": 8, + "sources": { + "mapbox": { + "type": "vector", + "url": "mapbox://mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5", + "maxzoom": 14 + } + }, + "layers": [{ + "id": "circle_color_example", + "type": "circle", + "source": "mapbox", + "paint": { + "circle-color": null + } + }] +} diff --git a/test/fixtures/style_parser/circle-opacity.info.json b/test/fixtures/style_parser/circle-opacity.info.json new file mode 100644 index 0000000000..3e8662bdbe --- /dev/null +++ b/test/fixtures/style_parser/circle-opacity.info.json @@ -0,0 +1,7 @@ +{ + "default": { + "log": [ + [1, "WARNING", "ParseStyle", "value of 'circle-opacity' must be a number, or a number function"] + ] + } +} diff --git a/test/fixtures/style_parser/circle-opacity.style.json b/test/fixtures/style_parser/circle-opacity.style.json new file mode 100644 index 0000000000..601e81a51b --- /dev/null +++ b/test/fixtures/style_parser/circle-opacity.style.json @@ -0,0 +1,18 @@ +{ + "version": 8, + "sources": { + "mapbox": { + "type": "vector", + "url": "mapbox://mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5", + "maxzoom": 14 + } + }, + "layers": [{ + "id": "circle_opacity_example", + "type": "circle", + "source": "mapbox", + "paint": { + "circle-opacity": null + } + }] +} diff --git a/test/fixtures/style_parser/circle-radius.info.json b/test/fixtures/style_parser/circle-radius.info.json new file mode 100644 index 0000000000..7e87aa4fdb --- /dev/null +++ b/test/fixtures/style_parser/circle-radius.info.json @@ -0,0 +1,7 @@ +{ + "default": { + "log": [ + [1, "WARNING", "ParseStyle", "value of 'circle-radius' must be a number, or a number function"] + ] + } +} diff --git a/test/fixtures/style_parser/circle-radius.style.json b/test/fixtures/style_parser/circle-radius.style.json new file mode 100644 index 0000000000..a7fb28b2d3 --- /dev/null +++ b/test/fixtures/style_parser/circle-radius.style.json @@ -0,0 +1,18 @@ +{ + "version": 8, + "sources": { + "mapbox": { + "type": "vector", + "url": "mapbox://mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5", + "maxzoom": 14 + } + }, + "layers": [{ + "id": "circle_radius_example", + "type": "circle", + "source": "mapbox", + "paint": { + "circle-radius": null + } + }] +} diff --git a/test/fixtures/style_parser/colors.style.json b/test/fixtures/style_parser/colors.style.json index 24aa2e5d1c..8996548885 100644 --- a/test/fixtures/style_parser/colors.style.json +++ b/test/fixtures/style_parser/colors.style.json @@ -1,5 +1,5 @@ { - "version": 6, + "version": 8, "constants": { "@land": "r44,239,225)", "@snow": "f4f8foNGbjf#", diff --git a/test/fixtures/style_parser/function-numeric.style.json b/test/fixtures/style_parser/function-numeric.style.json index 2db5624ac9..f3fca7e1d0 100644 --- a/test/fixtures/style_parser/function-numeric.style.json +++ b/test/fixtures/style_parser/function-numeric.style.json @@ -1,5 +1,5 @@ { - "version": 6, + "version": 8, "sources": { "mapbox": { "type": "vector", diff --git a/test/fixtures/style_parser/function-string-bool-enum.style.json b/test/fixtures/style_parser/function-string-bool-enum.style.json index c38f195259..0447990911 100644 --- a/test/fixtures/style_parser/function-string-bool-enum.style.json +++ b/test/fixtures/style_parser/function-string-bool-enum.style.json @@ -1,5 +1,5 @@ { - "version": 6, + "version": 8, "sources": { "mapbox": { "type": "vector", @@ -31,7 +31,7 @@ "source-layer": "waterway", "layout": { "text-font": { - "stops": [[0, "Open Sans Regular"]] + "stops": [[0, ["Open Sans Regular"]]] }, "text-keep-upright": { "stops": [[0, false], [10, true]] diff --git a/test/fixtures/style_parser/function-type.style.json b/test/fixtures/style_parser/function-type.style.json index c992e71f04..cde8dfc76b 100644 --- a/test/fixtures/style_parser/function-type.style.json +++ b/test/fixtures/style_parser/function-type.style.json @@ -1,5 +1,5 @@ { - "version": 6, + "version": 8, "sources": { "mapbox": { "type": "vector", diff --git a/test/fixtures/style_parser/line-opacity.style.json b/test/fixtures/style_parser/line-opacity.style.json index 8f5221b8ad..712b2b6f50 100644 --- a/test/fixtures/style_parser/line-opacity.style.json +++ b/test/fixtures/style_parser/line-opacity.style.json @@ -1,5 +1,5 @@ { - "version": 6, + "version": 8, "sources": { "mapbox": { "type": "vector", diff --git a/test/fixtures/style_parser/line-translate.style.json b/test/fixtures/style_parser/line-translate.style.json index 3c079f3520..a32b2d8ee4 100644 --- a/test/fixtures/style_parser/line-translate.style.json +++ b/test/fixtures/style_parser/line-translate.style.json @@ -1,5 +1,5 @@ { - "version": 6, + "version": 8, "sources": { "mapbox": { "type": "vector", diff --git a/test/fixtures/style_parser/line-width.style.json b/test/fixtures/style_parser/line-width.style.json index 18653074db..e5fe6fa8e1 100644 --- a/test/fixtures/style_parser/line-width.style.json +++ b/test/fixtures/style_parser/line-width.style.json @@ -1,5 +1,5 @@ { - "version": 6, + "version": 8, "sources": { "mapbox": { "type": "vector", diff --git a/test/fixtures/style_parser/stop-zoom-value.style.json b/test/fixtures/style_parser/stop-zoom-value.style.json index 5f907cd8e9..520db9a904 100644 --- a/test/fixtures/style_parser/stop-zoom-value.style.json +++ b/test/fixtures/style_parser/stop-zoom-value.style.json @@ -1,5 +1,5 @@ { - "version": 6, + "version": 8, "sources": { "mapbox": { "type": "vector", diff --git a/test/fixtures/style_parser/stops-array.style.json b/test/fixtures/style_parser/stops-array.style.json index 107c1c064c..daa48214d9 100644 --- a/test/fixtures/style_parser/stops-array.style.json +++ b/test/fixtures/style_parser/stops-array.style.json @@ -1,5 +1,5 @@ { - "version": 6, + "version": 8, "sources": { "mapbox": { "type": "vector", diff --git a/test/fixtures/style_parser/text-size.style.json b/test/fixtures/style_parser/text-size.style.json index 175ce456ad..46dd33e3c4 100644 --- a/test/fixtures/style_parser/text-size.style.json +++ b/test/fixtures/style_parser/text-size.style.json @@ -1,5 +1,5 @@ { - "version": 6, + "version": 8, "sources": { "mapbox": { "type": "vector", diff --git a/test/fixtures/tiles/streets/15-17605-10749.vector.pbf b/test/fixtures/tiles/streets/15-17605-10749.vector.pbf Binary files differnew file mode 100644 index 0000000000..1f43a6c487 --- /dev/null +++ b/test/fixtures/tiles/streets/15-17605-10749.vector.pbf diff --git a/test/fixtures/tiles/streets/15-17605-10750.vector.pbf b/test/fixtures/tiles/streets/15-17605-10750.vector.pbf Binary files differnew file mode 100644 index 0000000000..1ad616a56c --- /dev/null +++ b/test/fixtures/tiles/streets/15-17605-10750.vector.pbf diff --git a/test/fixtures/util.cpp b/test/fixtures/util.cpp index 068a3d849f..7527f64f91 100644 --- a/test/fixtures/util.cpp +++ b/test/fixtures/util.cpp @@ -2,6 +2,11 @@ #include <mbgl/platform/log.hpp> +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshadow" +#include <boost/crc.hpp> +#pragma GCC diagnostic pop + #include <csignal> #include <unistd.h> @@ -57,5 +62,16 @@ void stopServer(pid_t pid) { kill(pid, SIGTERM); } +// from https://gist.github.com/ArtemGr/997887 +uint64_t crc64(const char* data, size_t size) { + boost::crc_optimal<64, 0x04C11DB7, 0, 0, false, false> crc; + crc.process_bytes(data, size); + return crc.checksum(); +} + +uint64_t crc64(const std::string& str) { + return crc64(str.data(), str.size()); +} + } } diff --git a/test/fixtures/util.hpp b/test/fixtures/util.hpp index dd8539c846..fbee03c2d1 100644 --- a/test/fixtures/util.hpp +++ b/test/fixtures/util.hpp @@ -17,6 +17,9 @@ namespace test { pid_t startServer(const char *executable); void stopServer(pid_t pid); +uint64_t crc64(const char*, size_t); +uint64_t crc64(const std::string&); + } } diff --git a/test/headless/headless.cpp b/test/headless/headless.cpp deleted file mode 100644 index c65e9855e7..0000000000 --- a/test/headless/headless.cpp +++ /dev/null @@ -1,181 +0,0 @@ -#include "../fixtures/util.hpp" -#include "../fixtures/fixture_log_observer.hpp" - -#include <mbgl/map/map.hpp> -#include <mbgl/map/still_image.hpp> -#include <mbgl/util/image.hpp> - -#include <mbgl/util/io.hpp> -#include <rapidjson/document.h> -#include <rapidjson/writer.h> -#include <rapidjson/stringbuffer.h> - -#include <mbgl/platform/platform.hpp> -#include <mbgl/platform/default/headless_view.hpp> -#include <mbgl/platform/default/headless_display.hpp> -#include <mbgl/storage/default_file_source.hpp> - -#include <dirent.h> - -#include <future> - -void rewriteLocalScheme(rapidjson::Value &value, rapidjson::Document::AllocatorType &allocator) { - ASSERT_TRUE(value.IsString()); - auto string = std::string { value.GetString(),value.GetStringLength() }; - if (string.compare(0, 8, "local://") == 0) { - string.replace(0, 8, "http://127.0.0.1:2900/"); - value.SetString(string.data(), string.size(), allocator); - } -} - - -class HeadlessTest : public ::testing::TestWithParam<std::string> { -public: - static void SetUpTestCase() { - const auto server = mbgl::platform::applicationRoot() + "/TEST_DATA/headless/server.js"; - pid = mbgl::test::startServer(server.c_str()); - display = std::make_shared<mbgl::HeadlessDisplay>(); - } - - static void TearDownTestCase() { - display.reset(); - mbgl::test::stopServer(pid); - } - -protected: - static pid_t pid; - static std::shared_ptr<mbgl::HeadlessDisplay> display; -}; - -pid_t HeadlessTest::pid = 0; -std::shared_ptr<mbgl::HeadlessDisplay> HeadlessTest::display; - -TEST_P(HeadlessTest, render) { - using namespace mbgl; - - const std::string& base = GetParam(); - - std::string style = util::read_file("test/suite/tests/" + base + "/style.json"); - std::string info = util::read_file("test/suite/tests/" + base + "/info.json"); - - // Parse style. - rapidjson::Document styleDoc; - styleDoc.Parse<0>((const char *const)style.c_str()); - ASSERT_FALSE(styleDoc.HasParseError()); - ASSERT_TRUE(styleDoc.IsObject()); - - // Rewrite "local://" to "http://127.0.0.1:2900/". - if (styleDoc.HasMember("sprite")) { - rewriteLocalScheme(styleDoc["sprite"], styleDoc.GetAllocator()); - } - - if (styleDoc.HasMember("glyphs")) { - rewriteLocalScheme(styleDoc["glyphs"], styleDoc.GetAllocator()); - } - - if (styleDoc.HasMember("sources")) { - auto &sources = styleDoc["sources"]; - ASSERT_TRUE(sources.IsObject()); - for (auto source = sources.MemberBegin(), end = sources.MemberEnd(); source != end; source++) { - if (source->value.HasMember("tiles")) { - auto &tiles = source->value["tiles"]; - ASSERT_TRUE(tiles.IsArray()); - for (rapidjson::SizeType i = 0; i < tiles.Size(); i++) { - rewriteLocalScheme(tiles[i], styleDoc.GetAllocator()); - } - } - - if (source->value.HasMember("url") && source->value["url"].IsString()) { - rewriteLocalScheme(source->value["url"], styleDoc.GetAllocator()); - } - } - } - - // Convert the JSON object back into a stringified version. - rapidjson::StringBuffer buffer; - rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); - styleDoc.Accept(writer); - style = buffer.GetString(); - - // Parse settings. - rapidjson::Document infoDoc; - infoDoc.Parse<0>((const char *const)info.c_str()); - ASSERT_FALSE(infoDoc.HasParseError()); - ASSERT_TRUE(infoDoc.IsObject()); - - for (auto it = infoDoc.MemberBegin(), end = infoDoc.MemberEnd(); it != end; it++) { - const std::string name { - it->name.GetString(), it->name.GetStringLength() - }; - - Log::Info(Event::General, "%s %s", base.c_str(), name.c_str()); - - const rapidjson::Value& value = it->value; - ASSERT_TRUE(value.IsObject()); - - if (value.HasMember("native") && !value["native"].GetBool()) - continue; - - if (value.HasMember("center")) ASSERT_TRUE(value["center"].IsArray()); - - const std::string actual_image = "test/suite/tests/" + base + "/" + name + "/actual.png"; - - const double zoom = value.HasMember("zoom") ? value["zoom"].GetDouble() : 0; - const double bearing = value.HasMember("bearing") ? value["bearing"].GetDouble() : 0; - const double latitude = value.HasMember("center") ? value["center"][rapidjson::SizeType(0)].GetDouble() : 0; - const double longitude = value.HasMember("center") ? value["center"][rapidjson::SizeType(1)].GetDouble() : 0; - const unsigned int width = value.HasMember("width") ? value["width"].GetUint() : 512; - const unsigned int height = value.HasMember("height") ? value["height"].GetUint() : 512; - const unsigned int pixelRatio = value.HasMember("pixelRatio") ? value["pixelRatio"].GetUint() : 1; - - std::vector<std::string> classes; - if (value.HasMember("classes")) { - const rapidjson::Value& js_classes = value["classes"]; - ASSERT_TRUE(js_classes.IsArray()); - for (rapidjson::SizeType i = 0; i < js_classes.Size(); i++) { - const rapidjson::Value& js_class = js_classes[i]; - ASSERT_TRUE(js_class.IsString()); - classes.push_back({ js_class.GetString(), js_class.GetStringLength() }); - } - } - - std::promise<void> promise; - - HeadlessView view(display, width, height, pixelRatio); - DefaultFileSource fileSource(nullptr); - Map map(view, fileSource, MapMode::Still); - - map.resize(width, height, pixelRatio); - map.setClasses(classes); - map.setStyleJSON(style, "test/suite"); - map.setLatLngZoom(mbgl::LatLng(latitude, longitude), zoom); - map.setBearing(bearing); - - map.renderStill([&](std::exception_ptr, std::unique_ptr<const StillImage> image) { - const std::string png = util::compress_png(image->width, image->height, image->pixels.get()); - util::write_file("test/suite/tests/" + base + "/" + name + "/actual.png", png); - promise.set_value(); - }); - - promise.get_future().get(); - } -} - -INSTANTIATE_TEST_CASE_P(Headless, HeadlessTest, ::testing::ValuesIn([] { - std::vector<std::string> names; - - const auto tests = mbgl::platform::applicationRoot() + "/TEST_DATA/suite/tests"; - DIR *dir = opendir(tests.c_str()); - if (dir != nullptr) { - for (dirent *dp = nullptr; (dp = readdir(dir)) != nullptr;) { - const std::string name = dp->d_name; - if (name != "index.html" && !(name.size() >= 1 && name[0] == '.')) { - names.push_back(name); - } - } - closedir(dir); - } - - EXPECT_GT(names.size(), 0ul); - return names; -}())); diff --git a/test/headless/server.js b/test/headless/server.js deleted file mode 100755 index ff349b050e..0000000000 --- a/test/headless/server.js +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env node -/* jshint node: true */ -'use strict'; - -var express = require('express'); -var app = express(); - -app.use(express.static('test/suite')); - -var server = app.listen(2900, function () { - var host = server.address().address; - var port = server.address().port; - console.log('Test server listening at http://%s:%s', host, port); - - if (process.argv[2]) { - // Allow the test to continue running. - process.stdin.write("Go!\n"); - } -}); diff --git a/test/ios/KIFTestActor+MapboxGL.m b/test/ios/KIFTestActor+MapboxGL.m index 330adfdc28..6ebd248448 100644 --- a/test/ios/KIFTestActor+MapboxGL.m +++ b/test/ios/KIFTestActor+MapboxGL.m @@ -1,6 +1,6 @@ #import "KIFTestActor+MapboxGL.h" -#import "MapboxGL.h" +#import "Mapbox.h" #import <KIF/UIApplication-KIFAdditions.h> diff --git a/test/ios/MGLTAppDelegate.m b/test/ios/MGLTAppDelegate.m index 3fa68b6da1..ba49f96d22 100644 --- a/test/ios/MGLTAppDelegate.m +++ b/test/ios/MGLTAppDelegate.m @@ -1,6 +1,6 @@ #import "MGLTAppDelegate.h" #import "MGLTViewController.h" -#import "MapboxGL.h" +#import "Mapbox.h" @implementation MGLTAppDelegate diff --git a/test/ios/MGLTViewController.m b/test/ios/MGLTViewController.m index e3357616f8..01a053f50d 100644 --- a/test/ios/MGLTViewController.m +++ b/test/ios/MGLTViewController.m @@ -1,5 +1,5 @@ #import "MGLTViewController.h" -#import "MapboxGL.h" +#import "Mapbox.h" @implementation MGLTViewController { diff --git a/test/ios/MapViewTests.m b/test/ios/MapViewTests.m index 82360db231..f4ae501d7e 100644 --- a/test/ios/MapViewTests.m +++ b/test/ios/MapViewTests.m @@ -3,7 +3,7 @@ #import "KIFTestActor+MapboxGL.h" -#import "MapboxGL.h" +#import "Mapbox.h" #import "MGLTViewController.h" #import "LocationMocker/LocationMocker.h" @@ -544,7 +544,7 @@ - (void)testUserTrackingModeFollow { tester.mapView.userTrackingMode = MGLUserTrackingModeFollow; - [tester waitForTimeInterval:1]; + [tester acknowledgeSystemAlert]; XCTAssertEqual(tester.mapView.userLocationVisible, YES, @@ -570,7 +570,7 @@ - (void)testUserTrackingModeFollowWithHeading { tester.mapView.userTrackingMode = MGLUserTrackingModeFollowWithHeading; - [tester waitForTimeInterval:1]; + [tester acknowledgeSystemAlert]; XCTAssertEqual(tester.mapView.userLocationVisible, YES, diff --git a/test/ios/MetricsTests.m b/test/ios/MetricsTests.m index 7c71ee6739..d3b99dc63b 100644 --- a/test/ios/MetricsTests.m +++ b/test/ios/MetricsTests.m @@ -5,7 +5,7 @@ #import <KIF/KIF.h> #import <OCMock/OCMock.h> -#import "MapboxGL.h" +#import "Mapbox.h" #import "OHHTTPStubs.h" const NSUInteger MGLMaximumEventsPerFlush = 20; @@ -102,7 +102,7 @@ const NSTimeInterval MGLFlushInterval = 60; [self pushFakeEvent]; - [self waitForExpectationsWithTimeout:1.0 handler:nil]; + [self waitForExpectationsWithTimeout:2.0 handler:nil]; } - (void)testTimerDestroyedAfterFlush { @@ -119,7 +119,7 @@ const NSTimeInterval MGLFlushInterval = 60; [[MGLMapboxEvents sharedManager] flush]; - [self waitForExpectationsWithTimeout:1.0 handler:nil]; + [self waitForExpectationsWithTimeout:2.0 handler:nil]; XCTAssertNil([[MGLMapboxEvents sharedManager] timer]); } @@ -138,12 +138,12 @@ const NSTimeInterval MGLFlushInterval = 60; [[MGLMapboxEvents sharedManager] flush]; - [self waitForExpectationsWithTimeout:1.0 handler:nil]; + [self waitForExpectationsWithTimeout:2.0 handler:nil]; id eventsMock = [OCMockObject partialMockForObject:[MGLMapboxEvents sharedManager]]; [[[eventsMock expect] andForwardToRealObject] startTimer]; [self pushFakeEvent]; - [eventsMock verifyWithDelay:0.1]; + [eventsMock verifyWithDelay:2.0]; XCTAssertEqual([[[MGLMapboxEvents sharedManager] eventQueue] count], 1); XCTAssertNotNil([[MGLMapboxEvents sharedManager] timer]); @@ -164,7 +164,7 @@ const NSTimeInterval MGLFlushInterval = 60; [[MGLMapboxEvents sharedManager] flush]; - [self waitForExpectationsWithTimeout:1.0 handler:nil]; + [self waitForExpectationsWithTimeout:2.0 handler:nil]; [self pushFakeEvent]; id eventsMock = [OCMockObject partialMockForObject:[MGLMapboxEvents sharedManager]]; @@ -178,7 +178,7 @@ const NSTimeInterval MGLFlushInterval = 60; id eventsMock = [OCMockObject partialMockForObject:[MGLMapboxEvents sharedManager]]; [[eventsMock expect] postEvents:events]; [[MGLMapboxEvents sharedManager] flush]; - [eventsMock verifyWithDelay:1.0]; + [eventsMock verifyWithDelay:2.0]; XCTAssertTrue([[[MGLMapboxEvents sharedManager] eventQueue] count] == 0); } @@ -209,7 +209,7 @@ const NSTimeInterval MGLFlushInterval = 60; [self pushFakeEvent]; [MGLMapboxEvents flush]; - [self waitForExpectationsWithTimeout:1.0 handler:nil]; + [self waitForExpectationsWithTimeout:2.0 handler:nil]; } - (void)testPushEventAddsToEventQueue { @@ -228,7 +228,7 @@ const NSTimeInterval MGLFlushInterval = 60; [self pushFakeEvent]; - [self waitForExpectationsWithTimeout:1.0 handler:nil]; + [self waitForExpectationsWithTimeout:2.0 handler:nil]; } - (void)testOptOutUpfrontDisablesMetrics { @@ -266,7 +266,7 @@ const NSTimeInterval MGLFlushInterval = 60; [[MGLMapboxEvents sharedManager] flush]; - [tester waitForTimeInterval:1.0]; + [tester waitForTimeInterval:2.0]; } @end diff --git a/test/ios/ios-tests.xcodeproj/project.pbxproj b/test/ios/ios-tests.xcodeproj/project.pbxproj index 30494412e7..6408372430 100644 --- a/test/ios/ios-tests.xcodeproj/project.pbxproj +++ b/test/ios/ios-tests.xcodeproj/project.pbxproj @@ -17,8 +17,6 @@ DD043366196DBBE000E6F39D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DD043365196DBBE000E6F39D /* main.m */; }; DD0580E81ACB628200B112C9 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD0580E71ACB628200B112C9 /* IOKit.framework */; }; DD0581041ACB661200B112C9 /* Headers in Resources */ = {isa = PBXBuildFile; fileRef = DD0581031ACB661200B112C9 /* Headers */; }; - DD0581061ACB661C00B112C9 /* MapboxGL.bundle in Resources */ = {isa = PBXBuildFile; fileRef = DD0581051ACB661C00B112C9 /* MapboxGL.bundle */; }; - DD0581081ACB663200B112C9 /* libMapboxGL.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DD0581071ACB663200B112C9 /* libMapboxGL.a */; }; DD0E6F671B01806600DC035A /* MetricsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DD0E6F661B01806600DC035A /* MetricsTests.m */; }; DD0E6F841B0190E200DC035A /* libOCMock.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DD0E6F701B0190E200DC035A /* libOCMock.a */; }; DD0E6F981B01B68E00DC035A /* OHHTTPStubs.m in Sources */ = {isa = PBXBuildFile; fileRef = DD0E6F8E1B01B68E00DC035A /* OHHTTPStubs.m */; }; @@ -38,6 +36,8 @@ DDBD016C196DC4740033959E /* MapViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DDBD0168196DC4740033959E /* MapViewTests.m */; }; DDBD016D196DC4740033959E /* KIFTestActor+MapboxGL.m in Sources */ = {isa = PBXBuildFile; fileRef = DDBD016A196DC4740033959E /* KIFTestActor+MapboxGL.m */; }; DDBD016E196DC4A10033959E /* libKIF.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DDBD0144196DC3AE0033959E /* libKIF.a */; }; + DDC5C7BC1B84D62B00E1EA6B /* libMapbox.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DDC5C7BA1B84D62B00E1EA6B /* libMapbox.a */; }; + DDC5C7BD1B84D62B00E1EA6B /* Mapbox.bundle in Resources */ = {isa = PBXBuildFile; fileRef = DDC5C7BB1B84D62B00E1EA6B /* Mapbox.bundle */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -105,8 +105,6 @@ DD0580E71ACB628200B112C9 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/IOKit.framework; sourceTree = DEVELOPER_DIR; }; DD0580EF1ACB62BE00B112C9 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; DD0581031ACB661200B112C9 /* Headers */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Headers; path = ../../build/ios/pkg/static/Headers; sourceTree = SOURCE_ROOT; }; - DD0581051ACB661C00B112C9 /* MapboxGL.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = MapboxGL.bundle; path = ../../build/ios/pkg/static/MapboxGL.bundle; sourceTree = SOURCE_ROOT; }; - DD0581071ACB663200B112C9 /* libMapboxGL.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libMapboxGL.a; path = ../../build/ios/pkg/static/libMapboxGL.a; sourceTree = SOURCE_ROOT; }; DD0E6F661B01806600DC035A /* MetricsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MetricsTests.m; sourceTree = SOURCE_ROOT; }; DD0E6F701B0190E200DC035A /* libOCMock.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libOCMock.a; path = OCMock/libOCMock.a; sourceTree = SOURCE_ROOT; }; DD0E6F721B0190E200DC035A /* NSNotificationCenter+OCMAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNotificationCenter+OCMAdditions.h"; sourceTree = "<group>"; }; @@ -144,6 +142,8 @@ DDBD0168196DC4740033959E /* MapViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MapViewTests.m; sourceTree = SOURCE_ROOT; }; DDBD016A196DC4740033959E /* KIFTestActor+MapboxGL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "KIFTestActor+MapboxGL.m"; sourceTree = SOURCE_ROOT; }; DDBD016B196DC4740033959E /* KIFTestActor+MapboxGL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "KIFTestActor+MapboxGL.h"; sourceTree = SOURCE_ROOT; }; + DDC5C7BA1B84D62B00E1EA6B /* libMapbox.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libMapbox.a; path = ../../../build/ios/pkg/static/libMapbox.a; sourceTree = "<group>"; }; + DDC5C7BB1B84D62B00E1EA6B /* Mapbox.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = Mapbox.bundle; path = ../../../build/ios/pkg/static/Mapbox.bundle; sourceTree = "<group>"; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -155,8 +155,8 @@ DD41CE171ACB5DE700FA7979 /* GLKit.framework in Frameworks */, DD41CE151ACB5DE000FA7979 /* ImageIO.framework in Frameworks */, DD41CE131ACB5DDA00FA7979 /* MobileCoreServices.framework in Frameworks */, + DDC5C7BC1B84D62B00E1EA6B /* libMapbox.a in Frameworks */, DD41CE0B1ACB5DC400FA7979 /* SystemConfiguration.framework in Frameworks */, - DD0581081ACB663200B112C9 /* libMapboxGL.a in Frameworks */, DD41CE0D1ACB5DCB00FA7979 /* libc++.dylib in Frameworks */, DD41CE0F1ACB5DD000FA7979 /* libsqlite3.dylib in Frameworks */, DD41CE111ACB5DD500FA7979 /* libz.dylib in Frameworks */, @@ -300,8 +300,8 @@ children = ( DD0581031ACB661200B112C9 /* Headers */, DACAD7111B08719F009119DC /* MGLMapboxEvents.h */, - DD0581071ACB663200B112C9 /* libMapboxGL.a */, - DD0581051ACB661C00B112C9 /* MapboxGL.bundle */, + DDC5C7BA1B84D62B00E1EA6B /* libMapbox.a */, + DDC5C7BB1B84D62B00E1EA6B /* Mapbox.bundle */, ); name = "GL Library"; sourceTree = "<group>"; @@ -474,7 +474,7 @@ files = ( 96567A311B0E8BB900D78776 /* Images.xcassets in Resources */, DD0581041ACB661200B112C9 /* Headers in Resources */, - DD0581061ACB661C00B112C9 /* MapboxGL.bundle in Resources */, + DDC5C7BD1B84D62B00E1EA6B /* Mapbox.bundle in Resources */, 96567A231B0E84CD00D78776 /* LaunchScreen.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/test/ios/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme b/test/ios/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme index f7173ee644..2737f5185f 100644 --- a/test/ios/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme +++ b/test/ios/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme @@ -37,10 +37,10 @@ </BuildActionEntries> </BuildAction> <TestAction + buildConfiguration = "Release" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES" - buildConfiguration = "Release"> + shouldUseLaunchSchemeArgsEnv = "YES"> <Testables> <TestableReference skipped = "NO"> @@ -59,6 +59,9 @@ Identifier = "MapViewTests/testUserTrackingModeFollowWithHeading"> </Test> <Test + Identifier = "MetricsTests"> + </Test> + <Test Identifier = "MetricsTests/testFlushPostsEvents"> </Test> <Test @@ -79,15 +82,18 @@ ReferencedContainer = "container:ios-tests.xcodeproj"> </BuildableReference> </MacroExpansion> + <AdditionalOptions> + </AdditionalOptions> </TestAction> <LaunchAction + buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" launchStyle = "0" useCustomWorkingDirectory = "NO" - buildConfiguration = "Debug" ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" + debugServiceExtension = "internal" allowLocationSimulation = "YES"> <BuildableProductRunnable runnableDebuggingMode = "0"> @@ -103,10 +109,10 @@ </AdditionalOptions> </LaunchAction> <ProfileAction + buildConfiguration = "Release" shouldUseLaunchSchemeArgsEnv = "YES" savedToolIdentifier = "" useCustomWorkingDirectory = "NO" - buildConfiguration = "Release" debugDocumentVersioning = "YES"> <BuildableProductRunnable runnableDebuggingMode = "0"> diff --git a/test/miscellaneous/custom_sprites.cpp b/test/miscellaneous/custom_sprites.cpp new file mode 100644 index 0000000000..a72119ea61 --- /dev/null +++ b/test/miscellaneous/custom_sprites.cpp @@ -0,0 +1,61 @@ +#include "../fixtures/util.hpp" +#include "../fixtures/fixture_log_observer.hpp" + +#include <mbgl/map/map.hpp> +#include <mbgl/map/still_image.hpp> +#include <mbgl/util/image.hpp> +#include <mbgl/annotation/sprite_image.hpp> + +#include <mbgl/util/io.hpp> + +#include <mbgl/platform/default/headless_view.hpp> +#include <mbgl/platform/default/headless_display.hpp> +#include <mbgl/storage/default_file_source.hpp> + +#include <future> + +using namespace mbgl; + +TEST(Headless, CustomSpriteImages) { + FixtureLog log; + + auto display = std::make_shared<mbgl::HeadlessDisplay>(); + HeadlessView view(display, 1); + view.resize(256, 256); + DefaultFileSource fileSource(nullptr); + + const auto style = util::read_file("test/fixtures/headless/pois.json"); + + Map map(view, fileSource, MapMode::Still); + + map.setLatLngZoom(LatLng{ 52.499167, 13.418056 }, 15); + + map.setStyleJSON(style, ""); + map.setSprite("cafe", + std::make_shared<SpriteImage>(12, 12, 1, std::string(12 * 12 * 4, '\xFF'))); + std::promise<std::unique_ptr<const StillImage>> promise; + map.renderStill([&promise](std::exception_ptr error, std::unique_ptr<const StillImage> image) { + if (error) { + promise.set_exception(error); + } else { + promise.set_value(std::move(image)); + } + }); + auto result = promise.get_future().get(); + ASSERT_EQ(256, result->width); + ASSERT_EQ(256, result->height); + + EXPECT_EQ( + 21u, + log.count({ + EventSeverity::Info, Event::Sprite, int64_t(-1), "Can't find sprite named 'bakery'", + })); + + // const size_t bytes = result->width * result->height * 4; + // const auto hash = test::crc64(reinterpret_cast<const char*>(result->pixels.get()), bytes); + // EXPECT_EQ(0xC40A4BCD76AEC564u, hash) << std::hex << hash; + + // const std::string png = util::compress_png(result->width, result->height, + // result->pixels.get()); + // util::write_file("test/fixtures/headless/1.actual.png", png); +} diff --git a/test/miscellaneous/map.cpp b/test/miscellaneous/map.cpp index b8707f3902..fdd33c1466 100644 --- a/test/miscellaneous/map.cpp +++ b/test/miscellaneous/map.cpp @@ -11,7 +11,7 @@ TEST(Map, PauseResume) { using namespace mbgl; auto display = std::make_shared<mbgl::HeadlessDisplay>(); - HeadlessView view(display); + HeadlessView view(display, 1); DefaultFileSource fileSource(nullptr); Map map(view, fileSource, MapMode::Continuous); @@ -24,7 +24,7 @@ TEST(Map, DoublePause) { using namespace mbgl; auto display = std::make_shared<mbgl::HeadlessDisplay>(); - HeadlessView view(display); + HeadlessView view(display, 1); DefaultFileSource fileSource(nullptr); Map map(view, fileSource, MapMode::Continuous); @@ -38,7 +38,7 @@ TEST(Map, ResumeWithoutPause) { using namespace mbgl; auto display = std::make_shared<mbgl::HeadlessDisplay>(); - HeadlessView view(display); + HeadlessView view(display, 1); DefaultFileSource fileSource(nullptr); Map map(view, fileSource, MapMode::Continuous); @@ -50,7 +50,7 @@ TEST(Map, DestroyPaused) { using namespace mbgl; auto display = std::make_shared<mbgl::HeadlessDisplay>(); - HeadlessView view(display); + HeadlessView view(display, 1); DefaultFileSource fileSource(nullptr); Map map(view, fileSource, MapMode::Continuous); diff --git a/test/miscellaneous/map_context.cpp b/test/miscellaneous/map_context.cpp index f1f26a9c47..ec88862f05 100644 --- a/test/miscellaneous/map_context.cpp +++ b/test/miscellaneous/map_context.cpp @@ -11,9 +11,9 @@ using namespace mbgl; TEST(MapContext, DoubleStyleLoad) { std::shared_ptr<HeadlessDisplay> display = std::make_shared<HeadlessDisplay>(); - HeadlessView view(display, 512, 512, 1); + HeadlessView view(display, 1, 512, 512); DefaultFileSource fileSource(nullptr); - MapData data(MapMode::Continuous); + MapData data(MapMode::Continuous, view.getPixelRatio()); util::Thread<MapContext> context({"Map", util::ThreadType::Map, util::ThreadPriority::Regular}, view, fileSource, data); diff --git a/test/miscellaneous/mapbox.cpp b/test/miscellaneous/mapbox.cpp index 1aff2494a4..a0f9208298 100644 --- a/test/miscellaneous/mapbox.cpp +++ b/test/miscellaneous/mapbox.cpp @@ -8,21 +8,30 @@ using namespace mbgl; TEST(Mapbox, SourceURL) { - EXPECT_EQ(mbgl::util::mapbox::normalizeSourceURL("mapbox://user.map", "key"), "https://api.tiles.mapbox.com/v4/user.map.json?access_token=key&secure"); - EXPECT_EQ(mbgl::util::mapbox::normalizeSourceURL("mapbox://user.map", "token"), "https://api.tiles.mapbox.com/v4/user.map.json?access_token=token&secure"); + EXPECT_EQ(mbgl::util::mapbox::normalizeSourceURL("mapbox://user.map", "key"), "https://api.mapbox.com/v4/user.map.json?access_token=key&secure"); + EXPECT_EQ(mbgl::util::mapbox::normalizeSourceURL("mapbox://user.map", "token"), "https://api.mapbox.com/v4/user.map.json?access_token=token&secure"); EXPECT_THROW(mbgl::util::mapbox::normalizeSourceURL("mapbox://user.map", ""), std::runtime_error); } TEST(Mapbox, GlyphsURL) { - EXPECT_EQ(mbgl::util::mapbox::normalizeGlyphsURL("mapbox://fontstack/{fontstack}/{range}.pbf", "key"), "https://api.tiles.mapbox.com/v4/fontstack/{fontstack}/{range}.pbf?access_token=key"); + EXPECT_EQ(mbgl::util::mapbox::normalizeGlyphsURL("mapbox://fonts/boxmap/Comic%20Sans/0-255.pbf", "key"), "https://api.mapbox.com/fonts/v1/boxmap/Comic%20Sans/0-255.pbf?access_token=key"); + EXPECT_EQ(mbgl::util::mapbox::normalizeGlyphsURL("mapbox://fonts/boxmap/{fontstack}/{range}.pbf", "key"), "https://api.mapbox.com/fonts/v1/boxmap/{fontstack}/{range}.pbf?access_token=key"); EXPECT_EQ(mbgl::util::mapbox::normalizeGlyphsURL("http://path", "key"), "http://path"); } TEST(Mapbox, StyleURL) { - EXPECT_EQ(mbgl::util::mapbox::normalizeStyleURL("mapbox://user.style", "key"), "https://api.tiles.mapbox.com/styles/v1/user/user.style?access_token=key"); + EXPECT_EQ(mbgl::util::mapbox::normalizeStyleURL("mapbox://styles/user/style", "key"), "https://api.mapbox.com/styles/v1/user/style?access_token=key"); + EXPECT_EQ(mbgl::util::mapbox::normalizeStyleURL("mapbox://styles/user/style/draft", "key"), "https://api.mapbox.com/styles/v1/user/style/draft?access_token=key"); EXPECT_EQ(mbgl::util::mapbox::normalizeStyleURL("http://path", "key"), "http://path"); } +TEST(Mapbox, SpriteURL) { + EXPECT_EQ(mbgl::util::mapbox::normalizeSpriteURL("map/box/sprites@2x.json", "key"), "map/box/sprites@2x.json"); + EXPECT_EQ(mbgl::util::mapbox::normalizeSpriteURL("mapbox://sprites/mapbox/streets-v8.json", "key"), "https://api.mapbox.com/styles/v1/mapbox/streets-v8/sprite.json?access_token=key"); + EXPECT_EQ(mbgl::util::mapbox::normalizeSpriteURL("mapbox://sprites/mapbox/streets-v8@2x.png", "key"), "https://api.mapbox.com/styles/v1/mapbox/streets-v8/sprite@2x.png?access_token=key"); + EXPECT_EQ(mbgl::util::mapbox::normalizeSpriteURL("mapbox://sprites/mapbox/streets-v8/draft@2x.png", "key"), "https://api.mapbox.com/styles/v1/mapbox/streets-v8/draft/sprite@2x.png?access_token=key"); +} + TEST(Mapbox, TileURL) { try { EXPECT_EQ(mbgl::util::mapbox::normalizeTileURL("http://path.png/tile.png", "mapbox://user.map", SourceType::Raster), "http://path.png/tile{ratio}.png"); diff --git a/test/miscellaneous/style_parser.cpp b/test/miscellaneous/style_parser.cpp index 7a38ba054d..e88d798411 100644 --- a/test/miscellaneous/style_parser.cpp +++ b/test/miscellaneous/style_parser.cpp @@ -3,6 +3,9 @@ #include <mbgl/style/style_parser.hpp> #include <mbgl/util/io.hpp> +#include <mbgl/map/mode.hpp> +#include <mbgl/map/map_data.hpp> + #include <rapidjson/document.h> #include "../fixtures/fixture_log_observer.hpp" @@ -35,7 +38,11 @@ TEST_P(StyleParserTest, ParseStyle) { FixtureLogObserver* observer = new FixtureLogObserver(); Log::setObserver(std::unique_ptr<Log::Observer>(observer)); - StyleParser parser; + MapMode mapMode = MapMode::Continuous; + const float pixelRatio = 1.0f; + + MapData data(mapMode, pixelRatio); + StyleParser parser(data); parser.parse(styleDoc); for (auto it = infoDoc.MemberBegin(), end = infoDoc.MemberEnd(); it != end; it++) { diff --git a/test/miscellaneous/thread.cpp b/test/miscellaneous/thread.cpp index f8d91bf97f..9d8f6b6327 100644 --- a/test/miscellaneous/thread.cpp +++ b/test/miscellaneous/thread.cpp @@ -7,7 +7,7 @@ using namespace mbgl::util; class TestObject { public: - TestObject(uv_loop_t*, std::thread::id otherTid) + TestObject(std::thread::id otherTid) : tid(std::this_thread::get_id()) { EXPECT_NE(tid, otherTid); } @@ -17,9 +17,9 @@ public: EXPECT_EQ(val, 1); } - int fn2() { + void fn2(std::function<void (int)> cb) { EXPECT_EQ(tid, std::this_thread::get_id()); - return 1; + cb(1); } void transferIn(std::unique_ptr<int> val) { @@ -27,15 +27,15 @@ public: EXPECT_EQ(*val, 1); } - std::unique_ptr<int> transferOut() { + void transferOut(std::function<void (std::unique_ptr<int>)> cb) { EXPECT_EQ(tid, std::this_thread::get_id()); - return std::make_unique<int>(1); + cb(std::make_unique<int>(1)); } - std::unique_ptr<int> transferInOut(std::unique_ptr<int> val) { + void transferInOut(std::unique_ptr<int> val, std::function<void (std::unique_ptr<int>)> cb) { EXPECT_EQ(tid, std::this_thread::get_id()); EXPECT_EQ(*val, 1); - return std::move(val); + cb(std::move(val)); } void transferInShared(std::shared_ptr<int> val) { @@ -43,21 +43,21 @@ public: EXPECT_EQ(*val, 1); } - std::shared_ptr<int> transferOutShared() { + void transferOutShared(std::function<void (std::shared_ptr<int>)> cb) { EXPECT_EQ(tid, std::this_thread::get_id()); - return std::make_shared<int>(1); + cb(std::make_shared<int>(1)); } - std::string transferString(const std::string& string) { + void transferString(const std::string& string, std::function<void (std::string)> cb) { EXPECT_EQ(tid, std::this_thread::get_id()); EXPECT_EQ(string, "test"); - return string; + cb(string); } - bool checkContext() const { - return ThreadContext::currentlyOn(ThreadType::Worker) + void checkContext(std::function<void (bool)> cb) const { + cb(ThreadContext::currentlyOn(ThreadType::Worker) && ThreadContext::getName() == "Test" - && ThreadContext::getPriority() == ThreadPriority::Low; + && ThreadContext::getPriority() == ThreadPriority::Low); } const std::thread::id tid; @@ -67,40 +67,46 @@ TEST(Thread, invoke) { const std::thread::id tid = std::this_thread::get_id(); RunLoop loop(uv_default_loop()); + std::vector<std::unique_ptr<mbgl::WorkRequest>> requests; loop.invoke([&] { EXPECT_EQ(tid, std::this_thread::get_id()); Thread<TestObject> thread({"Test", ThreadType::Map, ThreadPriority::Regular}, tid); thread.invoke(&TestObject::fn1, 1); - thread.invokeWithResult<int>(&TestObject::fn2, [&] (int result) { + 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)); - thread.invokeWithResult<std::unique_ptr<int>>(&TestObject::transferOut, [&] (std::unique_ptr<int> result) { + requests.push_back(thread.invokeWithCallback(&TestObject::transferOut, [&] (std::unique_ptr<int> result) { EXPECT_EQ(tid, std::this_thread::get_id()); EXPECT_EQ(*result, 1); - }); + })); - thread.invokeWithResult<std::unique_ptr<int>>(&TestObject::transferInOut, [&] (std::unique_ptr<int> result) { + requests.push_back(thread.invokeWithCallback(&TestObject::transferInOut, [&] (std::unique_ptr<int> result) { EXPECT_EQ(tid, std::this_thread::get_id()); EXPECT_EQ(*result, 1); - }, std::make_unique<int>(1)); + }, std::make_unique<int>(1))); thread.invoke(&TestObject::transferInShared, std::make_shared<int>(1)); - thread.invokeWithResult<std::shared_ptr<int>>(&TestObject::transferOutShared, [&] (std::shared_ptr<int> result) { + 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.invokeWithResult<std::string>(&TestObject::transferString, [&] (std::string result){ + requests.push_back(thread.invokeWithCallback(&TestObject::transferString, [&] (std::string result){ EXPECT_EQ(tid, std::this_thread::get_id()); EXPECT_EQ(result, "test"); loop.stop(); - }, test); + }, test)); test.clear(); }); @@ -117,15 +123,103 @@ TEST(Thread, context) { const std::thread::id tid = std::this_thread::get_id(); RunLoop loop(uv_default_loop()); + std::vector<std::unique_ptr<mbgl::WorkRequest>> requests; loop.invoke([&] { Thread<TestObject> thread({"Test", ThreadType::Worker, ThreadPriority::Low}, tid); - thread.invokeWithResult<bool>(&TestObject::checkContext, [&] (bool inTestThreadContext) { + requests.push_back(thread.invokeWithCallback(&TestObject::checkContext, [&] (bool inTestThreadContext) { EXPECT_EQ(inTestThreadContext, true); loop.stop(); - }); + })); }); uv_run(uv_default_loop(), UV_RUN_DEFAULT); } + +class TestWorker { +public: + TestWorker() = default; + + void send(std::function<void ()> fn, std::function<void ()> cb) { + fn(); + cb(); + } +}; + +TEST(Thread, ExecutesAfter) { + RunLoop loop(uv_default_loop()); + Thread<TestWorker> thread({"Test", ThreadType::Map, ThreadPriority::Regular}); + + bool didWork = false; + bool didAfter = false; + + auto request = thread.invokeWithCallback(&TestWorker::send, [&] { + didAfter = true; + loop.stop(); + }, [&] { + didWork = true; + }); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + EXPECT_TRUE(didWork); + EXPECT_TRUE(didAfter); +} + +TEST(Thread, WorkRequestDeletionWaitsForWorkToComplete) { + RunLoop loop(uv_default_loop()); + Thread<TestWorker> thread({"Test", ThreadType::Map, ThreadPriority::Regular}); + + std::promise<void> started; + bool didWork = false; + + auto request = thread.invokeWithCallback(&TestWorker::send, [&] {}, [&] { + started.set_value(); + usleep(10000); + didWork = true; + }); + + started.get_future().get(); + request.reset(); + EXPECT_TRUE(didWork); +} + +TEST(Thread, WorkRequestDeletionCancelsAfter) { + RunLoop loop(uv_default_loop()); + Thread<TestWorker> thread({"Test", ThreadType::Map, ThreadPriority::Regular}); + + std::promise<void> started; + bool didAfter = false; + + auto request = thread.invokeWithCallback(&TestWorker::send, [&] { + didAfter = true; + }, [&] { + started.set_value(); + }); + + started.get_future().get(); + request.reset(); + uv_run(uv_default_loop(), UV_RUN_ONCE); + EXPECT_FALSE(didAfter); +} + +TEST(Thread, WorkRequestDeletionCancelsImmediately) { + RunLoop loop(uv_default_loop()); + Thread<TestWorker> thread({"Test", ThreadType::Map, ThreadPriority::Regular}); + + std::promise<void> started; + + auto request1 = thread.invokeWithCallback(&TestWorker::send, [&] {}, [&] { + usleep(10000); + started.set_value(); + }); + + auto request2 = thread.invokeWithCallback(&TestWorker::send, [&] {}, [&] { + ADD_FAILURE() << "Second work item should not be invoked"; + }); + request2.reset(); + + started.get_future().get(); + request1.reset(); +} diff --git a/test/miscellaneous/transform.cpp b/test/miscellaneous/transform.cpp index 5d00bfd20b..426e9e534d 100644 --- a/test/miscellaneous/transform.cpp +++ b/test/miscellaneous/transform.cpp @@ -105,3 +105,35 @@ TEST(Transform, InvalidBearing) { ASSERT_DOUBLE_EQ(2, transform.getScale()); ASSERT_DOUBLE_EQ(2, transform.getAngle()); } + +TEST(Transform, PerspectiveProjection) { + MockView view; + Transform transform(view); + + transform.resize({{ 1000, 1000 }}); + transform.setScale(1024); + transform.setPitch(0.9); + transform.setLatLng(LatLng(38, -77)); + + // expected values are from mapbox-gl-js + + LatLng loc = transform.getState().pointToLatLng({ 500, 500 }); + ASSERT_NEAR(-77, loc.longitude, 0.0001); + ASSERT_NEAR(38, loc.latitude, 0.0001); + + loc = transform.getState().pointToLatLng({ 0, 1000 }); + ASSERT_NEAR(-77.59198961199148, loc.longitude, 0.0002); + ASSERT_NEAR(38.74661326302018, loc.latitude, 0.0001); + + loc = transform.getState().pointToLatLng({ 1000, 0 }); + ASSERT_NEAR(-76.75823239205641, loc.longitude, 0.0001); + ASSERT_NEAR(37.692872969426375, loc.latitude, 0.0001); + + vec2<double> point = transform.getState().latLngToPoint({38.74661326302018, -77.59198961199148}); + ASSERT_NEAR(point.x, 0, 0.01); + ASSERT_NEAR(point.y, 1000, 0.01); + + point = transform.getState().latLngToPoint({37.692872969426375, -76.75823239205641}); + ASSERT_NEAR(point.x, 1000, 0.02); + ASSERT_NEAR(point.y, 0, 0.02); +} diff --git a/test/miscellaneous/work_queue.cpp b/test/miscellaneous/work_queue.cpp new file mode 100644 index 0000000000..a5f616fe5b --- /dev/null +++ b/test/miscellaneous/work_queue.cpp @@ -0,0 +1,63 @@ +#include "../fixtures/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) { + EXPECT_TRUE(ThreadContext::currentlyOn(ThreadType::Map)); + + queue->push(std::move(fn)); + } + +private: + WorkQueue* queue; +}; + +TEST(WorkQueue, push) { + RunLoop loop(uv_default_loop()); + + WorkQueue queue; + Thread<TestThread> thread({"Test", ThreadType::Map, ThreadPriority::Regular}, &queue); + + uint8_t count = 0; + + auto endTest = [&]() { + EXPECT_TRUE(ThreadContext::currentlyOn(ThreadType::Main)); + + if (++count == 4) { + loop.stop(); + } + }; + + thread.invoke(&TestThread::send, endTest); + thread.invoke(&TestThread::send, endTest); + thread.invoke(&TestThread::send, endTest); + thread.invoke(&TestThread::send, endTest); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); +} + +TEST(WorkQueue, cancel) { + RunLoop loop(uv_default_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); +} diff --git a/test/miscellaneous/worker.cpp b/test/miscellaneous/worker.cpp deleted file mode 100644 index 3d3c781b8c..0000000000 --- a/test/miscellaneous/worker.cpp +++ /dev/null @@ -1,104 +0,0 @@ -#include "../fixtures/util.hpp" - -#include <mbgl/util/worker.hpp> -#include <mbgl/util/work_request.hpp> -#include <mbgl/util/run_loop.hpp> - -using namespace mbgl; -using namespace mbgl::util; - -TEST(Worker, ExecutesWorkAndAfter) { - RunLoop loop(uv_default_loop()); - - Worker worker(1); - std::unique_ptr<WorkRequest> request; - - bool didWork = false; - bool didAfter = false; - - loop.invoke([&] { - request = worker.send([&] { - didWork = true; - }, [&] { - didAfter = true; - loop.stop(); - }); - }); - - uv_run(uv_default_loop(), UV_RUN_DEFAULT); - EXPECT_TRUE(didWork); - EXPECT_TRUE(didAfter); -} - -TEST(Worker, WorkRequestDeletionWaitsForWorkToComplete) { - RunLoop loop(uv_default_loop()); - - Worker worker(1); - std::promise<void> started; - bool didWork = false; - - loop.invoke([&] { - auto request = worker.send([&] { - started.set_value(); - usleep(10000); - didWork = true; - }, [&] {}); - started.get_future().get(); - - request.reset(); - EXPECT_TRUE(didWork); - loop.stop(); - }); - - uv_run(uv_default_loop(), UV_RUN_DEFAULT); -} - -TEST(Worker, WorkRequestJoinCancelsAfter) { - RunLoop loop(uv_default_loop()); - - Worker worker(1); - std::promise<void> started; - bool didAfter = false; - - loop.invoke([&] { - auto request = worker.send([&] { - started.set_value(); - }, [&] { - didAfter = true; - }); - started.get_future().get(); - - request.reset(); - loop.stop(); - }); - - uv_run(uv_default_loop(), UV_RUN_DEFAULT); - EXPECT_FALSE(didAfter); -} - -TEST(Worker, WorkRequestCancelsImmediately) { - RunLoop loop(uv_default_loop()); - - Worker worker(1); - - loop.invoke([&] { - std::promise<void> started; - // First worker item. - auto request1 = worker.send([&] { - usleep(10000); - started.set_value(); - }, [&] {}); - - auto request2 = worker.send([&] { - ADD_FAILURE() << "Second work item should not be invoked"; - }, [&] {}); - request2.reset(); - - started.get_future().get(); - request1.reset(); - - loop.stop(); - }); - - uv_run(uv_default_loop(), UV_RUN_DEFAULT); -} diff --git a/test/storage/database.cpp b/test/storage/database.cpp index b54b69a133..b40474c004 100644 --- a/test/storage/database.cpp +++ b/test/storage/database.cpp @@ -13,10 +13,11 @@ TEST_F(Storage, DatabaseDoesNotExist) { Log::setObserver(std::make_unique<FixtureLogObserver>()); - SQLiteCache::Impl cache(nullptr, "test/fixtures/404/cache.db"); + SQLiteCache::Impl cache("test/fixtures/404/cache.db"); - std::unique_ptr<Response> res = cache.get({ Resource::Unknown, "mapbox://test" }); - EXPECT_EQ(nullptr, res.get()); + cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr<Response> res) { + EXPECT_EQ(nullptr, res.get()); + }); auto observer = Log::removeObserver(); EXPECT_EQ(1ul, dynamic_cast<FixtureLogObserver*>(observer.get())->count({ EventSeverity::Error, Event::Database, 14, "unable to open database file" })); @@ -54,10 +55,11 @@ TEST_F(Storage, DatabaseCreate) { Log::setObserver(std::make_unique<FixtureLogObserver>()); - SQLiteCache::Impl cache(nullptr, "test/fixtures/database/cache.db"); + SQLiteCache::Impl cache("test/fixtures/database/cache.db"); - std::unique_ptr<Response> res = cache.get({ Resource::Unknown, "mapbox://test" }); - EXPECT_EQ(nullptr, res.get()); + cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr<Response> res) { + EXPECT_EQ(nullptr, res.get()); + }); Log::removeObserver(); } @@ -109,14 +111,15 @@ TEST_F(Storage, DatabaseLockedRead) { deleteFile("test/fixtures/database/locked.db"); FileLock guard("test/fixtures/database/locked.db"); - SQLiteCache::Impl cache(nullptr, "test/fixtures/database/locked.db"); + SQLiteCache::Impl cache("test/fixtures/database/locked.db"); { // First request should fail. Log::setObserver(std::make_unique<FixtureLogObserver>()); - std::unique_ptr<Response> res = cache.get({ Resource::Unknown, "mapbox://test" }); - EXPECT_EQ(nullptr, res.get()); + cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr<Response> res) { + EXPECT_EQ(nullptr, res.get()); + }); // Make sure that we got a few "database locked" errors auto observer = Log::removeObserver(); @@ -131,8 +134,9 @@ TEST_F(Storage, DatabaseLockedRead) { // First, try getting a file (the cache value should not exist). Log::setObserver(std::make_unique<FixtureLogObserver>()); - std::unique_ptr<Response> res = cache.get({ Resource::Unknown, "mapbox://test" }); - EXPECT_EQ(nullptr, res.get()); + cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr<Response> res) { + EXPECT_EQ(nullptr, res.get()); + }); // Make sure that we got a no errors Log::removeObserver(); @@ -149,7 +153,7 @@ TEST_F(Storage, DatabaseLockedWrite) { deleteFile("test/fixtures/database/locked.db"); FileLock guard("test/fixtures/database/locked.db"); - SQLiteCache::Impl cache(nullptr, "test/fixtures/database/locked.db"); + SQLiteCache::Impl cache("test/fixtures/database/locked.db"); { // Adds a file (which should fail). @@ -157,8 +161,9 @@ TEST_F(Storage, DatabaseLockedWrite) { auto response = std::make_shared<Response>(); cache.put({ Resource::Unknown, "mapbox://test" }, response); - std::unique_ptr<Response> res = cache.get({ Resource::Unknown, "mapbox://test" }); - EXPECT_EQ(nullptr, res.get()); + cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr<Response> res) { + EXPECT_EQ(nullptr, res.get()); + }); auto observer = Log::removeObserver(); auto flo = dynamic_cast<FixtureLogObserver*>(observer.get()); @@ -175,9 +180,10 @@ TEST_F(Storage, DatabaseLockedWrite) { auto response = std::make_shared<Response>(); response->data = "Demo"; cache.put({ Resource::Unknown, "mapbox://test" }, response); - std::unique_ptr<Response> res = cache.get({ Resource::Unknown, "mapbox://test" }); - EXPECT_NE(nullptr, res.get()); - EXPECT_EQ("Demo", res->data); + cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr<Response> res) { + EXPECT_NE(nullptr, res.get()); + EXPECT_EQ("Demo", res->data); + }); // Make sure that we got a no errors Log::removeObserver(); @@ -194,7 +200,7 @@ TEST_F(Storage, DatabaseLockedRefresh) { createDir("test/fixtures/database"); deleteFile("test/fixtures/database/locked.db"); - SQLiteCache::Impl cache(nullptr, "test/fixtures/database/locked.db"); + SQLiteCache::Impl cache("test/fixtures/database/locked.db"); // Then, lock the file and try again. FileLock guard("test/fixtures/database/locked.db"); @@ -206,8 +212,9 @@ TEST_F(Storage, DatabaseLockedRefresh) { auto response = std::make_shared<Response>(); response->data = "Demo"; cache.put({ Resource::Unknown, "mapbox://test" }, response); - std::unique_ptr<Response> res = cache.get({ Resource::Unknown, "mapbox://test" }); - EXPECT_EQ(nullptr, res.get()); + cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr<Response> res) { + EXPECT_EQ(nullptr, res.get()); + }); auto observer = Log::removeObserver(); auto flo = dynamic_cast<FixtureLogObserver*>(observer.get()); @@ -221,8 +228,9 @@ TEST_F(Storage, DatabaseLockedRefresh) { auto response = std::make_shared<Response>(); response->data = "Demo"; cache.refresh({ Resource::Unknown, "mapbox://test" }, response->expires); - std::unique_ptr<Response> res = cache.get({ Resource::Unknown, "mapbox://test" }); - EXPECT_EQ(nullptr, res.get()); + cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr<Response> res) { + EXPECT_EQ(nullptr, res.get()); + }); // Make sure that we got the right errors. auto observer = Log::removeObserver(); @@ -240,7 +248,7 @@ TEST_F(Storage, DatabaseDeleted) { createDir("test/fixtures/database"); deleteFile("test/fixtures/database/locked.db"); - SQLiteCache::Impl cache(nullptr, "test/fixtures/database/locked.db"); + SQLiteCache::Impl cache("test/fixtures/database/locked.db"); { // Adds a file. @@ -249,9 +257,10 @@ TEST_F(Storage, DatabaseDeleted) { auto response = std::make_shared<Response>(); response->data = "Demo"; cache.put({ Resource::Unknown, "mapbox://test" }, response); - std::unique_ptr<Response> res = cache.get({ Resource::Unknown, "mapbox://test" }); - EXPECT_NE(nullptr, res.get()); - EXPECT_EQ("Demo", res->data); + cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr<Response> res) { + EXPECT_NE(nullptr, res.get()); + EXPECT_EQ("Demo", res->data); + }); Log::removeObserver(); } @@ -265,9 +274,10 @@ TEST_F(Storage, DatabaseDeleted) { auto response = std::make_shared<Response>(); response->data = "Demo"; cache.put({ Resource::Unknown, "mapbox://test" }, response); - std::unique_ptr<Response> res = cache.get({ Resource::Unknown, "mapbox://test" }); - EXPECT_NE(nullptr, res.get()); - EXPECT_EQ("Demo", res->data); + cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr<Response> res) { + EXPECT_NE(nullptr, res.get()); + EXPECT_EQ("Demo", res->data); + }); auto observer = Log::removeObserver(); auto flo = dynamic_cast<FixtureLogObserver*>(observer.get()); @@ -285,7 +295,7 @@ TEST_F(Storage, DatabaseInvalid) { deleteFile("test/fixtures/database/invalid.db"); writeFile("test/fixtures/database/invalid.db", "this is an invalid file"); - SQLiteCache::Impl cache(nullptr, "test/fixtures/database/invalid.db"); + SQLiteCache::Impl cache("test/fixtures/database/invalid.db"); { // Adds a file. @@ -294,9 +304,10 @@ TEST_F(Storage, DatabaseInvalid) { auto response = std::make_shared<Response>(); response->data = "Demo"; cache.put({ Resource::Unknown, "mapbox://test" }, response); - std::unique_ptr<Response> res = cache.get({ Resource::Unknown, "mapbox://test" }); - EXPECT_NE(nullptr, res.get()); - EXPECT_EQ("Demo", res->data); + cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr<Response> res) { + EXPECT_NE(nullptr, res.get()); + EXPECT_EQ("Demo", res->data); + }); auto observer = Log::removeObserver(); auto flo = dynamic_cast<FixtureLogObserver*>(observer.get()); diff --git a/test/style/glyph_store.cpp b/test/style/glyph_store.cpp new file mode 100644 index 0000000000..fe614e8c60 --- /dev/null +++ b/test/style/glyph_store.cpp @@ -0,0 +1,231 @@ +#include "../fixtures/fixture_log_observer.hpp" +#include "../fixtures/mock_file_source.hpp" +#include "../fixtures/util.hpp" + +#include <mbgl/text/font_stack.hpp> +#include <mbgl/text/glyph_store.hpp> +#include <mbgl/util/run_loop.hpp> +#include <mbgl/util/thread.hpp> + +using namespace mbgl; + +using GlyphStoreTestCallback = std::function<void(GlyphStore*, std::exception_ptr)>; + +struct GlyphStoreParams { + const std::string url; + const std::string stack; + const std::set<GlyphRange> 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> 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<uv::async>(loop.get(), [&]{ loop.stop(); }); + async_->unref(); + + const util::ThreadContext context = {"Map", util::ThreadType::Map, util::ThreadPriority::Regular}; + + util::Thread<GlyphStoreThread> 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<uv::async> 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_TRUE(fontStack->getMetrics().empty()); + ASSERT_TRUE(fontStack->getSDFs().empty()); + + 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); + + auto fontStack = store->getFontStack(params.stack); + ASSERT_TRUE(fontStack->getMetrics().empty()); + ASSERT_TRUE(fontStack->getSDFs().empty()); + + 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_TRUE(fontStack->getMetrics().empty()); + ASSERT_TRUE(fontStack->getSDFs().empty()); + + stopTest(); + }; + + MockFileSource fileSource(MockFileSource::Success, ""); + runTest(params, &fileSource, callback); +} diff --git a/test/style/pending_resources.cpp b/test/style/pending_resources.cpp index 5d13d49a7c..3ba59657de 100644 --- a/test/style/pending_resources.cpp +++ b/test/style/pending_resources.cpp @@ -1,6 +1,6 @@ #include "../fixtures/fixture_log_observer.hpp" +#include "../fixtures/mock_file_source.hpp" #include "../fixtures/util.hpp" -#include "mock_file_source.hpp" #include <mbgl/map/map.hpp> #include <mbgl/map/still_image.hpp> @@ -21,15 +21,10 @@ class PendingResources : public ::testing::TestWithParam<std::string> { // 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) { - // TODO: The glyphs test is blocked by the issue #1664. - if (GetParam() == "glyphs.pbf") { - return; - } - util::RunLoop loop(uv_default_loop()); auto display = std::make_shared<mbgl::HeadlessDisplay>(); - HeadlessView view(display); + HeadlessView view(display, 1, 1000, 1000); MockFileSource fileSource(MockFileSource::SuccessWithDelay, GetParam()); std::unique_ptr<Map> map = std::make_unique<Map>(view, fileSource, MapMode::Still); @@ -43,7 +38,6 @@ TEST_P(PendingResources, DeleteMapObjectWithPendingRequest) { fileSource.setOnRequestDelayedCallback([&endTest] { endTest.send(); }); const std::string style = util::read_file("test/fixtures/resources/style.json"); - map->resize(1000, 1000, 1.0); map->setStyleJSON(style, "."); map->renderStill([&endTest](std::exception_ptr, std::unique_ptr<const StillImage>) { @@ -55,7 +49,16 @@ TEST_P(PendingResources, DeleteMapObjectWithPendingRequest) { // 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" +// 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.json", "sprite.json", "sprite.png", "sprite", "vector.pbf", "glyphs.pbf", "resources")); + ::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 index 92d479b512..5ed09065a1 100644 --- a/test/style/resource_loading.cpp +++ b/test/style/resource_loading.cpp @@ -1,7 +1,7 @@ #include "../fixtures/fixture_log_observer.hpp" #include "../fixtures/util.hpp" +#include "../fixtures/mock_file_source.hpp" #include "../fixtures/mock_view.hpp" -#include "mock_file_source.hpp" #include <mbgl/map/map_data.hpp> #include <mbgl/map/transform.hpp> @@ -20,20 +20,20 @@ namespace { class MockMapContext : public Style::Observer { public: - MockMapContext(uv_loop_t* loop, - View& view, + MockMapContext(View& view, FileSource& fileSource, const std::function<void(std::exception_ptr error)>& callback) - : data_(MapMode::Still), + : data_(MapMode::Still, view.getPixelRatio()), transform_(view), callback_(callback) { util::ThreadContext::setFileSource(&fileSource); - transform_.resize(1000, 1000, 1.0, 1000, 1000); + transform_.resize({{ 1000, 1000 }}); transform_.setLatLngZoom({0, 0}, 16); const std::string style = util::read_file("test/fixtures/resources/style.json"); - style_ = std::make_unique<Style>(style, "", loop), + style_ = std::make_unique<Style>(data_); + style_->setJSON(style, ""); style_->setObserver(this); } @@ -47,7 +47,7 @@ public: data_.setAnimationTime(now); transform_.updateTransitions(now); - style_->update(data_, transform_.getState(), texturePool_); + style_->update(transform_.getState(), texturePool_); } // Style::Observer implementation. @@ -63,6 +63,10 @@ public: callback_(error); } + void onSpriteStoreLoaded() override { + // no-op + } + private: MapData data_; Transform transform_; @@ -98,11 +102,11 @@ void runTestCase(MockFileSource::Type type, } catch (const util::GlyphRangeLoadingException&) { EXPECT_EQ(param, "glyphs.pbf"); } catch (const util::SourceLoadingException&) { - EXPECT_EQ(param, "source.json"); + EXPECT_TRUE(param == "source_raster.json" || param == "source_vector.json"); } catch (const util::SpriteLoadingException&) { EXPECT_TRUE(param == "sprite.png" || param == "sprite.json"); } catch (const util::TileLoadingException&) { - EXPECT_EQ(param, "vector.pbf"); + EXPECT_TRUE(param == "raster.png" || param == "vector.pbf"); } catch (const std::exception&) { EXPECT_TRUE(false) << "Unhandled exception."; } @@ -124,7 +128,7 @@ void runTestCase(MockFileSource::Type type, std::vector<FixtureLogObserver::LogMessage> logMessages = log->unchecked(); for (auto& logMessage : logMessages) { - if (std::regex_match(*logMessage.msg, std::regex(message))) { + if (std::regex_match(logMessage.msg, std::regex(message))) { match++; } } @@ -138,38 +142,30 @@ void runTestCase(MockFileSource::Type type, } -class ResourceLoading : public ::testing::TestWithParam<std::string> { +class ResourceLoading : public ::testing::TestWithParam<std::pair<std::string, std::string>> { }; TEST_P(ResourceLoading, Success) { - runTestCase(MockFileSource::Success, GetParam(), std::string()); + runTestCase(MockFileSource::Success, GetParam().first, std::string()); } TEST_P(ResourceLoading, RequestFail) { std::stringstream message; - message << "Failed to load \\[test\\/fixtures\\/resources\\/" << GetParam() << "\\]\\: Failed by the test case"; + message << "Failed to load \\[test\\/fixtures\\/resources\\/" << GetParam().first << "\\]\\: Failed by the test case"; - runTestCase(MockFileSource::RequestFail, GetParam(), message.str()); + runTestCase(MockFileSource::RequestFail, GetParam().first, message.str()); } TEST_P(ResourceLoading, RequestWithCorruptedData) { - const std::string param(GetParam()); - - std::stringstream message; - message << "Failed to parse "; - - if (param == "vector.pbf") { - message << "\\[15\\/1638(3|4)\\/1638(3|4)\\]\\: pbf unknown field type exception"; - } else { - message << "\\[test\\/fixtures\\/resources\\/" << param << "\\]"; - } - - if (param.find("json") != std::string::npos) { - message << "\\: 0 - Expect either an object or array at root"; - } - - runTestCase(MockFileSource::RequestWithCorruptedData, GetParam(), message.str()); + runTestCase(MockFileSource::RequestWithCorruptedData, GetParam().first, GetParam().second); } INSTANTIATE_TEST_CASE_P(Style, ResourceLoading, - ::testing::Values("source.json", "sprite.json", "sprite.png", "vector.pbf", "glyphs.pbf")); + ::testing::Values( + std::make_pair("source_raster.json", "Failed to parse \\[test\\/fixtures\\/resources\\/source_raster.json\\]: 0 - Expect either an object or array at root"), + std::make_pair("source_vector.json", "Failed to parse \\[test\\/fixtures\\/resources\\/source_vector.json\\]: 0 - Expect either an object or array at root"), + std::make_pair("sprite.json", "Failed to parse JSON: Expect either an object or array at root at offset 0"), + std::make_pair("sprite.png", "Could not parse sprite image"), + std::make_pair("raster.png", "Failed to parse \\[17\\/6553(4|5|6|7)\\/6553(4|5|6|7)\\]\\: error parsing raster image"), + std::make_pair("vector.pbf", "Failed to parse \\[1(5|6)\\/1638(3|4)\\/1638(3|4)\\]\\: pbf unknown field type exception"), + std::make_pair("glyphs.pbf", "Failed to parse \\[test\\/fixtures\\/resources\\/glyphs.pbf\\]: pbf unknown field type exception"))); diff --git a/test/style/sprite.cpp b/test/style/sprite.cpp new file mode 100644 index 0000000000..1c3f9b2271 --- /dev/null +++ b/test/style/sprite.cpp @@ -0,0 +1,173 @@ +#include "../fixtures/fixture_log_observer.hpp" +#include "../fixtures/mock_file_source.hpp" +#include "../fixtures/util.hpp" + +#include <mbgl/map/sprite.hpp> +#include <mbgl/util/run_loop.hpp> +#include <mbgl/util/thread.hpp> + +using namespace mbgl; + +using SpriteTestCallback = std::function<void(Sprite*, const Sprites&, std::exception_ptr)>; + +struct SpriteParams { + const std::string baseUrl; + const float pixelRatio; +}; + +class SpriteThread : public Sprite::Observer { +public: + SpriteThread(FileSource* fileSource, SpriteTestCallback callback) : callback_(callback) { + util::ThreadContext::setFileSource(fileSource); + } + + void loadSprite(const SpriteParams& params) { + sprite_.reset(new Sprite(params.baseUrl, params.pixelRatio)); + sprite_->setObserver(this); + } + + void unloadSprite() { + sprite_->setObserver(nullptr); + sprite_.reset(); + } + + void onSpriteLoaded(const Sprites& sprites) override { + callback_(sprite_.get(), sprites, nullptr); + } + + void onSpriteLoadingFailed(std::exception_ptr error) override { + callback_(sprite_.get(), Sprites(), error); + } + +private: + std::unique_ptr<Sprite> sprite_; + SpriteTestCallback callback_; +}; + +class SpriteTest : public testing::Test { +protected: + void runTest(const SpriteParams& params, FileSource* fileSource, SpriteTestCallback callback) { + util::RunLoop loop(uv_default_loop()); + + async_ = std::make_unique<uv::async>(loop.get(), [&] { loop.stop(); }); + async_->unref(); + + const util::ThreadContext context = {"Map", util::ThreadType::Map, util::ThreadPriority::Regular}; + + util::Thread<SpriteThread> tester(context, fileSource, callback); + tester.invoke(&SpriteThread::loadSprite, params); + + uv_run(loop.get(), UV_RUN_DEFAULT); + + tester.invoke(&SpriteThread::unloadSprite); + } + + void stopTest() { + async_->send(); + } + +private: + std::unique_ptr<uv::async> async_; +}; + +TEST_F(SpriteTest, LoadingSuccess) { + SpriteParams params = { + "test/fixtures/resources/sprite", + 1.0, + }; + + auto callback = [this, ¶ms](Sprite* sprite, const Sprites& sprites, std::exception_ptr error) { + ASSERT_TRUE(util::ThreadContext::currentlyOn(util::ThreadType::Map)); + + ASSERT_TRUE(error == nullptr); + + ASSERT_TRUE(!sprites.empty()); + + ASSERT_EQ(sprite->pixelRatio, params.pixelRatio); + ASSERT_NE(sprite->pixelRatio, 1.5); + ASSERT_NE(sprite->pixelRatio, 2.0); + + ASSERT_TRUE(sprite->isLoaded()); + + stopTest(); + }; + + MockFileSource fileSource(MockFileSource::Success, ""); + runTest(params, &fileSource, callback); +} + +TEST_F(SpriteTest, LoadingFail) { + SpriteParams params = { + "test/fixtures/resources/sprite", + 1.0, + }; + + auto callback = [this, ¶ms](Sprite* sprite, const Sprites&, std::exception_ptr error) { + ASSERT_TRUE(util::ThreadContext::currentlyOn(util::ThreadType::Map)); + + ASSERT_TRUE(error != nullptr); + + ASSERT_EQ(sprite->pixelRatio, params.pixelRatio); + ASSERT_NE(sprite->pixelRatio, 1.5); + ASSERT_NE(sprite->pixelRatio, 2.0); + + ASSERT_FALSE(sprite->isLoaded()); + + stopTest(); + }; + + MockFileSource fileSourceFailSpriteJSON(MockFileSource::RequestFail, "sprite.json"); + runTest(params, &fileSourceFailSpriteJSON, callback); + + MockFileSource fileSourceFailSpriteImage(MockFileSource::RequestFail, "sprite.png"); + runTest(params, &fileSourceFailSpriteImage, callback); + + MockFileSource fileSourceCorruptedSpriteJSON(MockFileSource::RequestWithCorruptedData, "sprite.json"); + runTest(params, &fileSourceCorruptedSpriteJSON, callback); + + MockFileSource fileSourceCorruptedSpriteImage(MockFileSource::RequestWithCorruptedData, "sprite.png"); + runTest(params, &fileSourceCorruptedSpriteImage, callback); +} + +TEST_F(SpriteTest, LoadingCancel) { + SpriteParams params = { + "test/fixtures/resources/sprite", + 1.0, + }; + + auto callback = [this](Sprite*, const Sprites&, std::exception_ptr) { + FAIL() << "Should never be called"; + }; + + MockFileSource fileSourceDelaySpriteJSON(MockFileSource::SuccessWithDelay, "sprite.json"); + fileSourceDelaySpriteJSON.setOnRequestDelayedCallback([this]{ + stopTest(); + }); + runTest(params, &fileSourceDelaySpriteJSON, callback); + + MockFileSource fileSourceDelaySpriteImage(MockFileSource::SuccessWithDelay, "sprite.png"); + fileSourceDelaySpriteImage.setOnRequestDelayedCallback([this]{ + stopTest(); + }); + runTest(params, &fileSourceDelaySpriteImage, callback); +} + +TEST_F(SpriteTest, InvalidURL) { + SpriteParams params = { + "foo bar", + 1.0, + }; + + auto callback = [this](Sprite* sprite, const Sprites&, std::exception_ptr error) { + ASSERT_TRUE(util::ThreadContext::currentlyOn(util::ThreadType::Map)); + + ASSERT_TRUE(error != nullptr); + + ASSERT_EQ(sprite->isLoaded(), false); + + stopTest(); + }; + + MockFileSource fileSource(MockFileSource::Success, ""); + runTest(params, &fileSource, callback); +} diff --git a/test/test.gypi b/test/test.gypi index f3d6976dd2..a35b00133b 100644 --- a/test/test.gypi +++ b/test/test.gypi @@ -30,22 +30,32 @@ ], 'sources': [ 'fixtures/main.cpp', + 'fixtures/mock_file_source.cpp', + 'fixtures/mock_file_source.hpp', + 'fixtures/mock_view.hpp', 'fixtures/util.hpp', 'fixtures/util.cpp', 'fixtures/fixture_log_observer.hpp', 'fixtures/fixture_log_observer.cpp', + 'miscellaneous/assert.cpp', + + 'annotations/sprite_atlas.cpp', + 'annotations/sprite_image.cpp', + 'annotations/sprite_store.cpp', + 'annotations/sprite_parser.cpp', + + 'api/annotations.cpp', 'api/api_misuse.cpp', 'api/repeated_render.cpp', 'api/set_style.cpp', - 'headless/headless.cpp', - 'miscellaneous/assert.cpp', 'miscellaneous/clip_ids.cpp', 'miscellaneous/binpack.cpp', 'miscellaneous/bilinear.cpp', 'miscellaneous/comparisons.cpp', + 'miscellaneous/custom_sprites.cpp', 'miscellaneous/enums.cpp', 'miscellaneous/functions.cpp', 'miscellaneous/geo.cpp', @@ -58,8 +68,8 @@ 'miscellaneous/thread.cpp', 'miscellaneous/tile.cpp', 'miscellaneous/transform.cpp', + 'miscellaneous/work_queue.cpp', 'miscellaneous/variant.cpp', - 'miscellaneous/worker.cpp', 'storage/storage.hpp', 'storage/storage.cpp', @@ -77,26 +87,25 @@ 'storage/http_other_loop.cpp', 'storage/http_reading.cpp', - 'style/mock_file_source.cpp', - 'style/mock_file_source.hpp', - 'style/mock_view.hpp', + 'style/glyph_store.cpp', 'style/pending_resources.cpp', 'style/resource_loading.cpp', + 'style/sprite.cpp', ], 'libraries': [ - '<@(uv_static_libs)', - '<@(sqlite3_static_libs)', + '<@(libuv_static_libs)', + '<@(sqlite_static_libs)', ], 'variables': { 'cflags_cc': [ - '<@(uv_cflags)', + '<@(libuv_cflags)', '<@(opengl_cflags)', '<@(boost_cflags)', - '<@(sqlite3_cflags)', + '<@(sqlite_cflags)', ], 'ldflags': [ - '<@(uv_ldflags)', - '<@(sqlite3_ldflags)', + '<@(libuv_ldflags)', + '<@(sqlite_ldflags)', ], }, 'conditions': [ |