diff options
Diffstat (limited to 'test')
151 files changed, 3405 insertions, 569 deletions
diff --git a/test/api/annotations.test.cpp b/test/api/annotations.test.cpp index b777615b4c..07257851ac 100644 --- a/test/api/annotations.test.cpp +++ b/test/api/annotations.test.cpp @@ -32,7 +32,7 @@ public: float pixelRatio { 1 }; HeadlessFrontend frontend { pixelRatio, fileSource, threadPool }; Map map { frontend, MapObserver::nullObserver(), frontend.getSize(), pixelRatio, fileSource, - threadPool, MapMode::Still }; + threadPool, MapMode::Static}; void checkRendering(const char * name) { test::checkImage(std::string("test/fixtures/annotations/") + name, @@ -366,8 +366,8 @@ TEST(Annotations, QueryFractionalZoomLevels) { test.map.addAnnotationImage(namedMarker("default_marker")); std::vector<mbgl::AnnotationID> ids; - for (int longitude = 0; longitude < 10; ++longitude) { - for (int latitude = 0; latitude < 10; ++latitude) { + for (int longitude = 0; longitude < 10; longitude += 2) { + for (int latitude = 0; latitude < 10; latitude += 2) { ids.push_back(test.map.addAnnotation(SymbolAnnotation { { double(latitude), double(longitude) }, "default_marker" })); } } @@ -399,8 +399,8 @@ TEST(Annotations, VisibleFeatures) { test.map.setLatLngZoom({ 5, 5 }, 3); std::vector<mbgl::AnnotationID> ids; - for (int longitude = 0; longitude < 10; ++longitude) { - for (int latitude = 0; latitude <= 10; ++latitude) { + for (int longitude = 0; longitude < 10; longitude += 2) { + for (int latitude = 0; latitude <= 10; latitude += 2) { ids.push_back(test.map.addAnnotation(SymbolAnnotation { { double(latitude), double(longitude) }, "default_marker" })); } } diff --git a/test/api/api_misuse.test.cpp b/test/api/api_misuse.test.cpp index 690c1548e5..363958451b 100644 --- a/test/api/api_misuse.test.cpp +++ b/test/api/api_misuse.test.cpp @@ -27,7 +27,7 @@ TEST(API, RenderWithoutCallback) { HeadlessFrontend frontend { pixelRatio, fileSource, threadPool }; auto map = std::make_unique<Map>(frontend, MapObserver::nullObserver(), frontend.getSize(), - pixelRatio, fileSource, threadPool, MapMode::Still); + pixelRatio, fileSource, threadPool, MapMode::Static); map->renderStill(nullptr); // Force Map thread to join. diff --git a/test/api/custom_geometry_source.test.cpp b/test/api/custom_geometry_source.test.cpp new file mode 100644 index 0000000000..83d1543a0a --- /dev/null +++ b/test/api/custom_geometry_source.test.cpp @@ -0,0 +1,71 @@ +#include <mbgl/test/util.hpp> + +#include <mbgl/gl/gl.hpp> +#include <mbgl/map/map.hpp> +#include <mbgl/util/shared_thread_pool.hpp> +#include <mbgl/storage/default_file_source.hpp> +#include <mbgl/gl/headless_frontend.hpp> +#include <mbgl/style/style.hpp> +#include <mbgl/style/sources/custom_geometry_source.hpp> +#include <mbgl/style/layers/fill_layer.hpp> +#include <mbgl/style/layers/line_layer.hpp> +#include <mbgl/util/geojson.hpp> +#include <mbgl/util/io.hpp> +#include <mbgl/util/mat4.hpp> +#include <mbgl/util/run_loop.hpp> + +using namespace mbgl; +using namespace mbgl::style; + +TEST(CustomGeometrySource, Grid) { + util::RunLoop loop; + + DefaultFileSource fileSource(":memory:", "test/fixtures/api/assets"); + auto threadPool = sharedThreadPool(); + float pixelRatio { 1 }; + HeadlessFrontend frontend { pixelRatio, fileSource, *threadPool }; + Map map(frontend, MapObserver::nullObserver(), frontend.getSize(), pixelRatio, fileSource, + *threadPool, MapMode::Static); + map.getStyle().loadJSON(util::read_file("test/fixtures/api/water.json")); + map.setLatLngZoom({ 37.8, -122.5 }, 10); + + CustomGeometrySource::Options options; + options.fetchTileFunction = [&map](const mbgl::CanonicalTileID& tileID) { + double gridSpacing = 0.1; + FeatureCollection features; + const LatLngBounds bounds(tileID); + for (double y = ceil(bounds.north() / gridSpacing) * gridSpacing; y >= floor(bounds.south() / gridSpacing) * gridSpacing; y -= gridSpacing) { + + mapbox::geojson::line_string gridLine; + gridLine.emplace_back(bounds.west(), y); + gridLine.emplace_back(bounds.east(), y); + + features.emplace_back(gridLine); + } + + for (double x = floor(bounds.west() / gridSpacing) * gridSpacing; x <= ceil(bounds.east() / gridSpacing) * gridSpacing; x += gridSpacing) { + mapbox::geojson::line_string gridLine; + gridLine.emplace_back(x, bounds.south()); + gridLine.emplace_back(x, bounds.north()); + + features.emplace_back(gridLine); + } + auto source = static_cast<CustomGeometrySource *>(map.getStyle().getSource("custom")); + if (source) { + source->setTileData(tileID, features); + } + }; + + map.getStyle().addSource(std::make_unique<CustomGeometrySource>("custom", options)); + + auto fillLayer = std::make_unique<FillLayer>("landcover", "mapbox"); + fillLayer->setSourceLayer("landcover"); + fillLayer->setFillColor(Color{ 1.0, 1.0, 0.0, 1.0 }); + map.getStyle().addLayer(std::move(fillLayer)); + + auto layer = std::make_unique<LineLayer>("grid", "custom"); + layer->setLineColor(Color{ 1.0, 1.0, 1.0, 1.0 }); + map.getStyle().addLayer(std::move(layer)); + + test::checkImage("test/fixtures/custom_geometry_source/grid", frontend.render(map), 0.0006, 0.1); +} diff --git a/test/api/custom_layer.test.cpp b/test/api/custom_layer.test.cpp index 1c514aeca2..eb1d7e0d3a 100644 --- a/test/api/custom_layer.test.cpp +++ b/test/api/custom_layer.test.cpp @@ -90,7 +90,7 @@ TEST(CustomLayer, Basic) { float pixelRatio { 1 }; HeadlessFrontend frontend { pixelRatio, fileSource, threadPool }; Map map(frontend, MapObserver::nullObserver(), frontend.getSize(), pixelRatio, fileSource, - threadPool, MapMode::Still); + threadPool, MapMode::Static); map.getStyle().loadJSON(util::read_file("test/fixtures/api/water.json")); map.setLatLngZoom({ 37.8, -122.5 }, 10); map.getStyle().addLayer(std::make_unique<CustomLayer>( diff --git a/test/api/query.test.cpp b/test/api/query.test.cpp index 3f3825eb72..c67ff9064c 100644 --- a/test/api/query.test.cpp +++ b/test/api/query.test.cpp @@ -32,7 +32,7 @@ public: float pixelRatio { 1 }; HeadlessFrontend frontend { pixelRatio, fileSource, threadPool }; Map map { frontend, MapObserver::nullObserver(), frontend.getSize(), pixelRatio, fileSource, - threadPool, MapMode::Still }; + threadPool, MapMode::Static}; }; } // end namespace diff --git a/test/api/recycle_map.cpp b/test/api/recycle_map.cpp index 8cd622fe79..ca6abac8c1 100644 --- a/test/api/recycle_map.cpp +++ b/test/api/recycle_map.cpp @@ -29,7 +29,7 @@ TEST(API, RecycleMapUpdateImages) { HeadlessFrontend frontend { pixelRatio, fileSource, threadPool }; auto map = std::make_unique<Map>(frontend, MapObserver::nullObserver(), frontend.getSize(), - pixelRatio, fileSource, threadPool, MapMode::Still); + pixelRatio, fileSource, threadPool, MapMode::Static); EXPECT_TRUE(map); diff --git a/test/api/zoom_history.cpp b/test/api/zoom_history.cpp index 1b1159bf52..df9b6ff2a3 100644 --- a/test/api/zoom_history.cpp +++ b/test/api/zoom_history.cpp @@ -30,7 +30,7 @@ TEST(API, ZoomHistory) { HeadlessFrontend frontend { pixelRatio, fileSource, threadPool }; auto map = std::make_unique<Map>(frontend, MapObserver::nullObserver(), frontend.getSize(), - pixelRatio, fileSource, threadPool, MapMode::Still); + pixelRatio, fileSource, threadPool, MapMode::Static); EXPECT_TRUE(map); diff --git a/test/fixtures/annotations/debug_sparse/expected.png b/test/fixtures/annotations/debug_sparse/expected.png Binary files differindex 3c1ad1599c..493928998d 100644 --- a/test/fixtures/annotations/debug_sparse/expected.png +++ b/test/fixtures/annotations/debug_sparse/expected.png diff --git a/test/fixtures/api/query_style.json b/test/fixtures/api/query_style.json index 2e499c383d..fd147f7899 100644 --- a/test/fixtures/api/query_style.json +++ b/test/fixtures/api/query_style.json @@ -67,7 +67,8 @@ "type": "symbol", "source": "source1", "layout": { - "icon-image": "test-icon" + "icon-image": "test-icon", + "icon-allow-overlap": true } }, { @@ -75,7 +76,8 @@ "type": "symbol", "source": "source2", "layout": { - "icon-image": "test-icon" + "icon-image": "test-icon", + "icon-allow-overlap": true } }, { @@ -83,7 +85,8 @@ "type": "symbol", "source": "source3", "layout": { - "icon-image": "test-icon" + "icon-image": "test-icon", + "icon-allow-overlap": true } }, { @@ -91,7 +94,8 @@ "type": "symbol", "source": "source4", "layout": { - "icon-image": "test-icon" + "icon-image": "test-icon", + "icon-allow-overlap": true } } ] diff --git a/test/fixtures/custom_geometry_source/grid/expected.png b/test/fixtures/custom_geometry_source/grid/expected.png Binary files differnew file mode 100644 index 0000000000..628a3b11fb --- /dev/null +++ b/test/fixtures/custom_geometry_source/grid/expected.png diff --git a/test/fixtures/expression_equality/acos.a.json b/test/fixtures/expression_equality/acos.a.json new file mode 100644 index 0000000000..1e9bb752ca --- /dev/null +++ b/test/fixtures/expression_equality/acos.a.json @@ -0,0 +1,4 @@ +[ + "acos", + 0.5 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/acos.b.json b/test/fixtures/expression_equality/acos.b.json new file mode 100644 index 0000000000..54e035cb7e --- /dev/null +++ b/test/fixtures/expression_equality/acos.b.json @@ -0,0 +1,4 @@ +[ + "acos", + 1.5 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/all.a.json b/test/fixtures/expression_equality/all.a.json new file mode 100644 index 0000000000..ec7154b7b9 --- /dev/null +++ b/test/fixtures/expression_equality/all.a.json @@ -0,0 +1,17 @@ +[ + "all", + [ + "boolean", + [ + "get", + "x" + ] + ], + [ + "boolean", + [ + "get", + "y" + ] + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/all.b.json b/test/fixtures/expression_equality/all.b.json new file mode 100644 index 0000000000..8eab839bb0 --- /dev/null +++ b/test/fixtures/expression_equality/all.b.json @@ -0,0 +1,17 @@ +[ + "all", + [ + "boolean", + [ + "get", + "x" + ] + ], + [ + "boolean", + [ + "get", + "y_other" + ] + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/any.a.json b/test/fixtures/expression_equality/any.a.json new file mode 100644 index 0000000000..3f044c1f79 --- /dev/null +++ b/test/fixtures/expression_equality/any.a.json @@ -0,0 +1,17 @@ +[ + "any", + [ + "boolean", + [ + "get", + "x" + ] + ], + [ + "boolean", + [ + "get", + "y" + ] + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/any.b.json b/test/fixtures/expression_equality/any.b.json new file mode 100644 index 0000000000..720662751f --- /dev/null +++ b/test/fixtures/expression_equality/any.b.json @@ -0,0 +1,17 @@ +[ + "any", + [ + "boolean", + [ + "get", + "x" + ] + ], + [ + "boolean", + [ + "get", + "y_other" + ] + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/array.a.json b/test/fixtures/expression_equality/array.a.json new file mode 100644 index 0000000000..3c31303ca3 --- /dev/null +++ b/test/fixtures/expression_equality/array.a.json @@ -0,0 +1,11 @@ +[ + "array", + [ + "literal", + [ + 1, + 2, + 3 + ] + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/array.b.json b/test/fixtures/expression_equality/array.b.json new file mode 100644 index 0000000000..7606794d56 --- /dev/null +++ b/test/fixtures/expression_equality/array.b.json @@ -0,0 +1,11 @@ +[ + "array", + [ + "literal", + [ + 1, + 2, + 4 + ] + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/asin.a.json b/test/fixtures/expression_equality/asin.a.json new file mode 100644 index 0000000000..3cd730ccbf --- /dev/null +++ b/test/fixtures/expression_equality/asin.a.json @@ -0,0 +1,4 @@ +[ + "asin", + 0.5 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/asin.b.json b/test/fixtures/expression_equality/asin.b.json new file mode 100644 index 0000000000..2c862c8cbe --- /dev/null +++ b/test/fixtures/expression_equality/asin.b.json @@ -0,0 +1,4 @@ +[ + "asin", + 1.5 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/at.a.json b/test/fixtures/expression_equality/at.a.json new file mode 100644 index 0000000000..c69b0d933b --- /dev/null +++ b/test/fixtures/expression_equality/at.a.json @@ -0,0 +1,20 @@ +[ + "number", + [ + "at", + [ + "number", + [ + "get", + "i" + ] + ], + [ + "array", + [ + "get", + "arr" + ] + ] + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/at.b.json b/test/fixtures/expression_equality/at.b.json new file mode 100644 index 0000000000..6e19c28606 --- /dev/null +++ b/test/fixtures/expression_equality/at.b.json @@ -0,0 +1,20 @@ +[ + "number", + [ + "at", + [ + "number", + [ + "get", + "i" + ] + ], + [ + "array", + [ + "get", + "arr_other" + ] + ] + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/atan.a.json b/test/fixtures/expression_equality/atan.a.json new file mode 100644 index 0000000000..b76406bc44 --- /dev/null +++ b/test/fixtures/expression_equality/atan.a.json @@ -0,0 +1,4 @@ +[ + "atan", + 1 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/atan.b.json b/test/fixtures/expression_equality/atan.b.json new file mode 100644 index 0000000000..aafbbb0594 --- /dev/null +++ b/test/fixtures/expression_equality/atan.b.json @@ -0,0 +1,4 @@ +[ + "atan", + 2 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/boolean.a.json b/test/fixtures/expression_equality/boolean.a.json new file mode 100644 index 0000000000..1230a2a926 --- /dev/null +++ b/test/fixtures/expression_equality/boolean.a.json @@ -0,0 +1,7 @@ +[ + "boolean", + [ + "get", + "x" + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/boolean.b.json b/test/fixtures/expression_equality/boolean.b.json new file mode 100644 index 0000000000..1ae91ef60c --- /dev/null +++ b/test/fixtures/expression_equality/boolean.b.json @@ -0,0 +1,7 @@ +[ + "boolean", + [ + "get", + "x_other" + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/case.a.json b/test/fixtures/expression_equality/case.a.json new file mode 100644 index 0000000000..84049294f5 --- /dev/null +++ b/test/fixtures/expression_equality/case.a.json @@ -0,0 +1,14 @@ +[ + "case", + [ + "get", + "x" + ], + "x", + [ + "get", + "y" + ], + "y", + "otherwise" +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/case.b.json b/test/fixtures/expression_equality/case.b.json new file mode 100644 index 0000000000..038806043f --- /dev/null +++ b/test/fixtures/expression_equality/case.b.json @@ -0,0 +1,14 @@ +[ + "case", + [ + "get", + "x" + ], + "x", + [ + "get", + "y" + ], + "y", + "otherwise_other" +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/coalesce.a.json b/test/fixtures/expression_equality/coalesce.a.json new file mode 100644 index 0000000000..8fae579e7c --- /dev/null +++ b/test/fixtures/expression_equality/coalesce.a.json @@ -0,0 +1,16 @@ +[ + "coalesce", + [ + "get", + "x" + ], + [ + "get", + "y" + ], + [ + "get", + "z" + ], + 0 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/coalesce.b.json b/test/fixtures/expression_equality/coalesce.b.json new file mode 100644 index 0000000000..4e0af8baa0 --- /dev/null +++ b/test/fixtures/expression_equality/coalesce.b.json @@ -0,0 +1,16 @@ +[ + "coalesce", + [ + "get", + "x" + ], + [ + "get", + "y" + ], + [ + "get", + "z" + ], + 1 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/concat.a.json b/test/fixtures/expression_equality/concat.a.json new file mode 100644 index 0000000000..08c95d7f49 --- /dev/null +++ b/test/fixtures/expression_equality/concat.a.json @@ -0,0 +1,6 @@ +[ + "concat", + "a", + "b", + "c" +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/concat.b.json b/test/fixtures/expression_equality/concat.b.json new file mode 100644 index 0000000000..e3396d4fc0 --- /dev/null +++ b/test/fixtures/expression_equality/concat.b.json @@ -0,0 +1,6 @@ +[ + "concat", + "a", + "b", + "c_other" +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/cos.a.json b/test/fixtures/expression_equality/cos.a.json new file mode 100644 index 0000000000..e41430de53 --- /dev/null +++ b/test/fixtures/expression_equality/cos.a.json @@ -0,0 +1,4 @@ +[ + "cos", + 0 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/cos.b.json b/test/fixtures/expression_equality/cos.b.json new file mode 100644 index 0000000000..5ba4424dae --- /dev/null +++ b/test/fixtures/expression_equality/cos.b.json @@ -0,0 +1,4 @@ +[ + "cos", + 1 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/divide.a.json b/test/fixtures/expression_equality/divide.a.json new file mode 100644 index 0000000000..40a67a871c --- /dev/null +++ b/test/fixtures/expression_equality/divide.a.json @@ -0,0 +1,5 @@ +[ + "/", + 10, + 5 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/divide.b.json b/test/fixtures/expression_equality/divide.b.json new file mode 100644 index 0000000000..e3f7b155b2 --- /dev/null +++ b/test/fixtures/expression_equality/divide.b.json @@ -0,0 +1,5 @@ +[ + "/", + 10, + 6 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/downcase.a.json b/test/fixtures/expression_equality/downcase.a.json new file mode 100644 index 0000000000..ca367218c4 --- /dev/null +++ b/test/fixtures/expression_equality/downcase.a.json @@ -0,0 +1,4 @@ +[ + "downcase", + "StRiNg" +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/downcase.b.json b/test/fixtures/expression_equality/downcase.b.json new file mode 100644 index 0000000000..fd9ea9881d --- /dev/null +++ b/test/fixtures/expression_equality/downcase.b.json @@ -0,0 +1,4 @@ +[ + "downcase", + "StRiNg_other" +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/get.a.json b/test/fixtures/expression_equality/get.a.json new file mode 100644 index 0000000000..57c3df48e7 --- /dev/null +++ b/test/fixtures/expression_equality/get.a.json @@ -0,0 +1,7 @@ +[ + "number", + [ + "get", + "x" + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/get.b.json b/test/fixtures/expression_equality/get.b.json new file mode 100644 index 0000000000..d1843362d3 --- /dev/null +++ b/test/fixtures/expression_equality/get.b.json @@ -0,0 +1,7 @@ +[ + "number", + [ + "get", + "x_other" + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/has.a.json b/test/fixtures/expression_equality/has.a.json new file mode 100644 index 0000000000..8326754107 --- /dev/null +++ b/test/fixtures/expression_equality/has.a.json @@ -0,0 +1,4 @@ +[ + "has", + "x" +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/has.b.json b/test/fixtures/expression_equality/has.b.json new file mode 100644 index 0000000000..20b6072303 --- /dev/null +++ b/test/fixtures/expression_equality/has.b.json @@ -0,0 +1,4 @@ +[ + "has", + "x_other" +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/heatmap-density.a.json b/test/fixtures/expression_equality/heatmap-density.a.json new file mode 100644 index 0000000000..90bd396f54 --- /dev/null +++ b/test/fixtures/expression_equality/heatmap-density.a.json @@ -0,0 +1,23 @@ +[ + "interpolate", + [ + "linear" + ], + [ + "heatmap-density" + ], + 0, + [ + "rgb", + 0, + 0, + 255 + ], + 1, + [ + "rgb", + 255, + 0, + 0 + ] +] diff --git a/test/fixtures/expression_equality/heatmap-density.b.json b/test/fixtures/expression_equality/heatmap-density.b.json new file mode 100644 index 0000000000..bce8ab03a1 --- /dev/null +++ b/test/fixtures/expression_equality/heatmap-density.b.json @@ -0,0 +1,23 @@ +[ + "interpolate", + [ + "linear" + ], + [ + "heatmap-density" + ], + 0, + [ + "rgb", + 0, + 0, + 255 + ], + 1, + [ + "rgb", + 255, + 255, + 255 + ] +] diff --git a/test/fixtures/expression_equality/let.a.json b/test/fixtures/expression_equality/let.a.json new file mode 100644 index 0000000000..fb24e50cfb --- /dev/null +++ b/test/fixtures/expression_equality/let.a.json @@ -0,0 +1,25 @@ +[ + "let", + "a", + 1, + "b", + 2, + [ + "+", + [ + "+", + [ + "var", + "a" + ], + [ + "var", + "b" + ] + ], + [ + "var", + "a" + ] + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/let.b.json b/test/fixtures/expression_equality/let.b.json new file mode 100644 index 0000000000..26813cb6ff --- /dev/null +++ b/test/fixtures/expression_equality/let.b.json @@ -0,0 +1,25 @@ +[ + "let", + "a", + 1, + "b", + 3, + [ + "+", + [ + "+", + [ + "var", + "a" + ], + [ + "var", + "b" + ] + ], + [ + "var", + "b" + ] + ] +] diff --git a/test/fixtures/expression_equality/ln.a.json b/test/fixtures/expression_equality/ln.a.json new file mode 100644 index 0000000000..30d80f36ae --- /dev/null +++ b/test/fixtures/expression_equality/ln.a.json @@ -0,0 +1,4 @@ +[ + "ln", + 2 +] diff --git a/test/fixtures/expression_equality/ln.b.json b/test/fixtures/expression_equality/ln.b.json new file mode 100644 index 0000000000..9bc04ad586 --- /dev/null +++ b/test/fixtures/expression_equality/ln.b.json @@ -0,0 +1,6 @@ +[ + "ln", + [ + "e" + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/log10.a.json b/test/fixtures/expression_equality/log10.a.json new file mode 100644 index 0000000000..32e4c18807 --- /dev/null +++ b/test/fixtures/expression_equality/log10.a.json @@ -0,0 +1,4 @@ +[ + "log10", + 100 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/log10.b.json b/test/fixtures/expression_equality/log10.b.json new file mode 100644 index 0000000000..8f32c204f9 --- /dev/null +++ b/test/fixtures/expression_equality/log10.b.json @@ -0,0 +1,4 @@ +[ + "log10", + 101 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/log2.a.json b/test/fixtures/expression_equality/log2.a.json new file mode 100644 index 0000000000..95cdc15373 --- /dev/null +++ b/test/fixtures/expression_equality/log2.a.json @@ -0,0 +1,4 @@ +[ + "log2", + 1024 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/log2.b.json b/test/fixtures/expression_equality/log2.b.json new file mode 100644 index 0000000000..2fffaeb32a --- /dev/null +++ b/test/fixtures/expression_equality/log2.b.json @@ -0,0 +1,4 @@ +[ + "log2", + 1025 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/match.a.json b/test/fixtures/expression_equality/match.a.json new file mode 100644 index 0000000000..ba8afc4126 --- /dev/null +++ b/test/fixtures/expression_equality/match.a.json @@ -0,0 +1,12 @@ +[ + "match", + [ + "get", + "x" + ], + "a", + "Apple", + "b", + "Banana", + "Kumquat" +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/match.b.json b/test/fixtures/expression_equality/match.b.json new file mode 100644 index 0000000000..2404b8e2e7 --- /dev/null +++ b/test/fixtures/expression_equality/match.b.json @@ -0,0 +1,12 @@ +[ + "match", + [ + "get", + "x" + ], + "a", + "Apple", + "b", + "Banana", + "Kumquat_other" +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/max.a.json b/test/fixtures/expression_equality/max.a.json new file mode 100644 index 0000000000..09a8f82bd7 --- /dev/null +++ b/test/fixtures/expression_equality/max.a.json @@ -0,0 +1,6 @@ +[ + "max", + 0, + -1, + 100 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/max.b.json b/test/fixtures/expression_equality/max.b.json new file mode 100644 index 0000000000..1b0beb20d6 --- /dev/null +++ b/test/fixtures/expression_equality/max.b.json @@ -0,0 +1,6 @@ +[ + "max", + 0, + -1, + 101 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/min.a.json b/test/fixtures/expression_equality/min.a.json new file mode 100644 index 0000000000..38cc90f1cd --- /dev/null +++ b/test/fixtures/expression_equality/min.a.json @@ -0,0 +1,5 @@ +[ + "min", + ["get", "x"], + 0 +] diff --git a/test/fixtures/expression_equality/min.b.json b/test/fixtures/expression_equality/min.b.json new file mode 100644 index 0000000000..84a5f66842 --- /dev/null +++ b/test/fixtures/expression_equality/min.b.json @@ -0,0 +1,5 @@ +[ + "min", + ["get", "x"], + 1 +] diff --git a/test/fixtures/expression_equality/minus.a.json b/test/fixtures/expression_equality/minus.a.json new file mode 100644 index 0000000000..9eb4f954e7 --- /dev/null +++ b/test/fixtures/expression_equality/minus.a.json @@ -0,0 +1,5 @@ +[ + "-", + 5, + 7 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/minus.b.json b/test/fixtures/expression_equality/minus.b.json new file mode 100644 index 0000000000..87042b98ef --- /dev/null +++ b/test/fixtures/expression_equality/minus.b.json @@ -0,0 +1,5 @@ +[ + "-", + 5, + 8 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/mod.a.json b/test/fixtures/expression_equality/mod.a.json new file mode 100644 index 0000000000..8439bafcd1 --- /dev/null +++ b/test/fixtures/expression_equality/mod.a.json @@ -0,0 +1,5 @@ +[ + "%", + 18, + 12 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/mod.b.json b/test/fixtures/expression_equality/mod.b.json new file mode 100644 index 0000000000..362e1721c1 --- /dev/null +++ b/test/fixtures/expression_equality/mod.b.json @@ -0,0 +1,5 @@ +[ + "%", + 18, + 13 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/not.a.json b/test/fixtures/expression_equality/not.a.json new file mode 100644 index 0000000000..b5f03e0ac0 --- /dev/null +++ b/test/fixtures/expression_equality/not.a.json @@ -0,0 +1,10 @@ +[ + "!", + [ + "boolean", + [ + "get", + "x" + ] + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/not.b.json b/test/fixtures/expression_equality/not.b.json new file mode 100644 index 0000000000..a4d77adf2e --- /dev/null +++ b/test/fixtures/expression_equality/not.b.json @@ -0,0 +1,10 @@ +[ + "!", + [ + "boolean", + [ + "get", + "x_other" + ] + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/number.a.json b/test/fixtures/expression_equality/number.a.json new file mode 100644 index 0000000000..57c3df48e7 --- /dev/null +++ b/test/fixtures/expression_equality/number.a.json @@ -0,0 +1,7 @@ +[ + "number", + [ + "get", + "x" + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/number.b.json b/test/fixtures/expression_equality/number.b.json new file mode 100644 index 0000000000..d1843362d3 --- /dev/null +++ b/test/fixtures/expression_equality/number.b.json @@ -0,0 +1,7 @@ +[ + "number", + [ + "get", + "x_other" + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/object.a.json b/test/fixtures/expression_equality/object.a.json new file mode 100644 index 0000000000..7551cfdbb2 --- /dev/null +++ b/test/fixtures/expression_equality/object.a.json @@ -0,0 +1,7 @@ +[ + "object", + [ + "get", + "x" + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/object.b.json b/test/fixtures/expression_equality/object.b.json new file mode 100644 index 0000000000..8444d40c0e --- /dev/null +++ b/test/fixtures/expression_equality/object.b.json @@ -0,0 +1,7 @@ +[ + "object", + [ + "get", + "x_other" + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/plus.a.json b/test/fixtures/expression_equality/plus.a.json new file mode 100644 index 0000000000..a00c4409fa --- /dev/null +++ b/test/fixtures/expression_equality/plus.a.json @@ -0,0 +1,7 @@ +[ + "+", + 1, + 2, + 3, + 4 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/plus.b.json b/test/fixtures/expression_equality/plus.b.json new file mode 100644 index 0000000000..87c071123f --- /dev/null +++ b/test/fixtures/expression_equality/plus.b.json @@ -0,0 +1,7 @@ +[ + "+", + 1, + 2, + 3, + 5 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/pow.a.json b/test/fixtures/expression_equality/pow.a.json new file mode 100644 index 0000000000..c1a1e67f86 --- /dev/null +++ b/test/fixtures/expression_equality/pow.a.json @@ -0,0 +1,11 @@ +[ + "^", + 4, + [ + "number", + [ + "get", + "x" + ] + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/pow.b.json b/test/fixtures/expression_equality/pow.b.json new file mode 100644 index 0000000000..ca5331b92a --- /dev/null +++ b/test/fixtures/expression_equality/pow.b.json @@ -0,0 +1,11 @@ +[ + "^", + 4, + [ + "number", + [ + "get", + "x_other" + ] + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/rgb.a.json b/test/fixtures/expression_equality/rgb.a.json new file mode 100644 index 0000000000..ce6c5e5dd0 --- /dev/null +++ b/test/fixtures/expression_equality/rgb.a.json @@ -0,0 +1,6 @@ +[ + "rgb", + 0, + 0, + 255 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/rgb.b.json b/test/fixtures/expression_equality/rgb.b.json new file mode 100644 index 0000000000..577c19748b --- /dev/null +++ b/test/fixtures/expression_equality/rgb.b.json @@ -0,0 +1,6 @@ +[ + "rgb", + 0, + 0, + 0 +] diff --git a/test/fixtures/expression_equality/rgba.a.json b/test/fixtures/expression_equality/rgba.a.json new file mode 100644 index 0000000000..e8ad7326c1 --- /dev/null +++ b/test/fixtures/expression_equality/rgba.a.json @@ -0,0 +1,7 @@ +[ + "rgba", + 0, + 0, + 255, + 1 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/rgba.b.json b/test/fixtures/expression_equality/rgba.b.json new file mode 100644 index 0000000000..81d442eaae --- /dev/null +++ b/test/fixtures/expression_equality/rgba.b.json @@ -0,0 +1,7 @@ +[ + "rgba", + 0, + 0, + 255, + 0.5 +] diff --git a/test/fixtures/expression_equality/sin.a.json b/test/fixtures/expression_equality/sin.a.json new file mode 100644 index 0000000000..0f7ae2966f --- /dev/null +++ b/test/fixtures/expression_equality/sin.a.json @@ -0,0 +1,4 @@ +[ + "sin", + 0 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/sin.b.json b/test/fixtures/expression_equality/sin.b.json new file mode 100644 index 0000000000..90f309b80f --- /dev/null +++ b/test/fixtures/expression_equality/sin.b.json @@ -0,0 +1,4 @@ +[ + "sin", + 1 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/sqrt.a.json b/test/fixtures/expression_equality/sqrt.a.json new file mode 100644 index 0000000000..56dd85bc1a --- /dev/null +++ b/test/fixtures/expression_equality/sqrt.a.json @@ -0,0 +1,7 @@ +[ + "sqrt", + [ + "get", + "x" + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/sqrt.b.json b/test/fixtures/expression_equality/sqrt.b.json new file mode 100644 index 0000000000..ab05d5084c --- /dev/null +++ b/test/fixtures/expression_equality/sqrt.b.json @@ -0,0 +1,7 @@ +[ + "sqrt", + [ + "get", + "x_other" + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/step.a.json b/test/fixtures/expression_equality/step.a.json new file mode 100644 index 0000000000..4fee85cd03 --- /dev/null +++ b/test/fixtures/expression_equality/step.a.json @@ -0,0 +1,18 @@ +[ + "number", + [ + "step", + [ + "number", + [ + "get", + "x" + ] + ], + 11, + 0, + 111, + 1, + 1111 + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/step.b.json b/test/fixtures/expression_equality/step.b.json new file mode 100644 index 0000000000..0a591a84df --- /dev/null +++ b/test/fixtures/expression_equality/step.b.json @@ -0,0 +1,18 @@ +[ + "number", + [ + "step", + [ + "number", + [ + "get", + "x" + ] + ], + 11, + 0, + 111, + 1, + 1112 + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/string.a.json b/test/fixtures/expression_equality/string.a.json new file mode 100644 index 0000000000..a79344f338 --- /dev/null +++ b/test/fixtures/expression_equality/string.a.json @@ -0,0 +1,7 @@ +[ + "string", + [ + "get", + "x" + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/string.b.json b/test/fixtures/expression_equality/string.b.json new file mode 100644 index 0000000000..6f77f3c3cf --- /dev/null +++ b/test/fixtures/expression_equality/string.b.json @@ -0,0 +1,7 @@ +[ + "string", + [ + "get", + "x_other" + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/tan.a.json b/test/fixtures/expression_equality/tan.a.json new file mode 100644 index 0000000000..c78e47e492 --- /dev/null +++ b/test/fixtures/expression_equality/tan.a.json @@ -0,0 +1,4 @@ +[ + "tan", + 0.7853981633974483 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/tan.b.json b/test/fixtures/expression_equality/tan.b.json new file mode 100644 index 0000000000..c22e64cf8a --- /dev/null +++ b/test/fixtures/expression_equality/tan.b.json @@ -0,0 +1,4 @@ +[ + "tan", + 1.7853981633974483 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/times.a.json b/test/fixtures/expression_equality/times.a.json new file mode 100644 index 0000000000..ce6d9b46e0 --- /dev/null +++ b/test/fixtures/expression_equality/times.a.json @@ -0,0 +1,7 @@ +[ + "*", + 3, + 2, + 0.5, + 2 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/times.b.json b/test/fixtures/expression_equality/times.b.json new file mode 100644 index 0000000000..147e011172 --- /dev/null +++ b/test/fixtures/expression_equality/times.b.json @@ -0,0 +1,7 @@ +[ + "*", + 3, + 2, + 0.5, + 3 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/to-boolean.a.json b/test/fixtures/expression_equality/to-boolean.a.json new file mode 100644 index 0000000000..ccf48149ec --- /dev/null +++ b/test/fixtures/expression_equality/to-boolean.a.json @@ -0,0 +1,7 @@ +[ + "to-boolean", + [ + "get", + "x" + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/to-boolean.b.json b/test/fixtures/expression_equality/to-boolean.b.json new file mode 100644 index 0000000000..7896261241 --- /dev/null +++ b/test/fixtures/expression_equality/to-boolean.b.json @@ -0,0 +1,7 @@ +[ + "to-boolean", + [ + "get", + "x_other" + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/to-color.a.json b/test/fixtures/expression_equality/to-color.a.json new file mode 100644 index 0000000000..de9ab59eec --- /dev/null +++ b/test/fixtures/expression_equality/to-color.a.json @@ -0,0 +1,7 @@ +[ + "to-color", + [ + "get", + "x" + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/to-color.b.json b/test/fixtures/expression_equality/to-color.b.json new file mode 100644 index 0000000000..c0566ef6a7 --- /dev/null +++ b/test/fixtures/expression_equality/to-color.b.json @@ -0,0 +1,7 @@ +[ + "to-color", + [ + "get", + "x_other" + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/to-number.a.json b/test/fixtures/expression_equality/to-number.a.json new file mode 100644 index 0000000000..65b2df5014 --- /dev/null +++ b/test/fixtures/expression_equality/to-number.a.json @@ -0,0 +1,7 @@ +[ + "to-number", + [ + "get", + "x" + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/to-number.b.json b/test/fixtures/expression_equality/to-number.b.json new file mode 100644 index 0000000000..b38dc5a455 --- /dev/null +++ b/test/fixtures/expression_equality/to-number.b.json @@ -0,0 +1,7 @@ +[ + "to-number", + [ + "get", + "x_other" + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/to-string.a.json b/test/fixtures/expression_equality/to-string.a.json new file mode 100644 index 0000000000..66f9a9caa1 --- /dev/null +++ b/test/fixtures/expression_equality/to-string.a.json @@ -0,0 +1,7 @@ +[ + "to-string", + [ + "get", + "x" + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/to-string.b.json b/test/fixtures/expression_equality/to-string.b.json new file mode 100644 index 0000000000..977a9d7769 --- /dev/null +++ b/test/fixtures/expression_equality/to-string.b.json @@ -0,0 +1,7 @@ +[ + "to-string", + [ + "get", + "x_other" + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/typeof.a.json b/test/fixtures/expression_equality/typeof.a.json new file mode 100644 index 0000000000..7843ff8c7f --- /dev/null +++ b/test/fixtures/expression_equality/typeof.a.json @@ -0,0 +1,7 @@ +[ + "typeof", + [ + "get", + "x" + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/typeof.b.json b/test/fixtures/expression_equality/typeof.b.json new file mode 100644 index 0000000000..412482347a --- /dev/null +++ b/test/fixtures/expression_equality/typeof.b.json @@ -0,0 +1,7 @@ +[ + "typeof", + [ + "get", + "x_other" + ] +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/upcase.a.json b/test/fixtures/expression_equality/upcase.a.json new file mode 100644 index 0000000000..d12ca7b08d --- /dev/null +++ b/test/fixtures/expression_equality/upcase.a.json @@ -0,0 +1,4 @@ +[ + "upcase", + "string" +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/upcase.b.json b/test/fixtures/expression_equality/upcase.b.json new file mode 100644 index 0000000000..ddeeb0300c --- /dev/null +++ b/test/fixtures/expression_equality/upcase.b.json @@ -0,0 +1,4 @@ +[ + "upcase", + "string_other" +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/zoom.a.json b/test/fixtures/expression_equality/zoom.a.json new file mode 100644 index 0000000000..fc675721ab --- /dev/null +++ b/test/fixtures/expression_equality/zoom.a.json @@ -0,0 +1,13 @@ +[ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 0, + 0, + 30, + 30 +]
\ No newline at end of file diff --git a/test/fixtures/expression_equality/zoom.b.json b/test/fixtures/expression_equality/zoom.b.json new file mode 100644 index 0000000000..6314858a5e --- /dev/null +++ b/test/fixtures/expression_equality/zoom.b.json @@ -0,0 +1,13 @@ +[ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 0, + 0, + 30, + 31 +]
\ No newline at end of file diff --git a/test/fixtures/local_glyphs/droid/expected.png b/test/fixtures/local_glyphs/droid/expected.png Binary files differnew file mode 100644 index 0000000000..c0ba43bf11 --- /dev/null +++ b/test/fixtures/local_glyphs/droid/expected.png diff --git a/test/fixtures/local_glyphs/mixed.json b/test/fixtures/local_glyphs/mixed.json new file mode 100644 index 0000000000..e07d429753 --- /dev/null +++ b/test/fixtures/local_glyphs/mixed.json @@ -0,0 +1,196 @@ +{ + "version": 8, + "zoom": 0, + "center": [-14.41400, 39.09187], + "sources": { + "mapbox": { + "type": "geojson", + "data": { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": { + "name": "身什戰 1" + }, + "geometry": { + "type": "LineString", + "coordinates": [ + [ + -14.4195556640625, + 39.091699613104595 + ], + [ + 102.3046875, + 39.36827914916014 + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "name": "two 身什戰" + }, + "geometry": { + "type": "LineString", + "coordinates": [ + [ + -14.403076171875, + 39.10022600175347 + ], + [ + 103.35937499999999, + 65.80277639340238 + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "name": "身什戰33" + }, + "geometry": { + "type": "LineString", + "coordinates": [ + [ + -14.414062499999998, + 39.091699613104595 + ], + [ + -14.765625, + 82.21421714106776 + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "name": "身什戰" + }, + "geometry": { + "type": "LineString", + "coordinates": [ + [ + -14.408569335937498, + 39.091699613104595 + ], + [ + -130.78125, + 39.095962936305476 + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "name": "身什戰 five" + }, + "geometry": { + "type": "LineString", + "coordinates": [ + [ + -14.414062499999998, + 39.095962936305476 + ], + [ + -16.5234375, + -58.81374171570779 + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "name": "six 身什戰" + }, + "geometry": { + "type": "LineString", + "coordinates": [ + [ + -14.4195556640625, + 39.10022600175347 + ], + [ + -130.4296875, + 64.47279382008166 + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "name": "身什戰 seven" + }, + "geometry": { + "type": "LineString", + "coordinates": [ + [ + -14.4195556640625, + 39.0831721934762 + ], + [ + 33.75, + 81.87364125482827 + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "name": "eight 身什戰" + }, + "geometry": { + "type": "LineString", + "coordinates": [ + [ + -14.447021484374998, + 39.104488809440475 + ], + [ + -66.4453125, + 82.26169873683153 + ] + ] + } + } + ] + } + } + }, + "glyphs": "local://glyphs/{fontstack}/{range}.pbf", + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "white" + } + }, + { + "id": "lines-symbol", + "type": "symbol", + "source": "mapbox", + "layout": { + "text-field": "{name}", + "symbol-placement": "line", + "symbol-spacing": 150, + "text-allow-overlap": true, + "text-font": [ "NotoCJK" ] + } + }, { + "id": "lines", + "type": "line", + "source": "mapbox", + "paint": { + "line-opacity": 0.25 + } + } + ] +} diff --git a/test/fixtures/local_glyphs/no_local/expected.png b/test/fixtures/local_glyphs/no_local/expected.png Binary files differnew file mode 100644 index 0000000000..c7b1b828ee --- /dev/null +++ b/test/fixtures/local_glyphs/no_local/expected.png diff --git a/test/fixtures/local_glyphs/ping_fang/expected.png b/test/fixtures/local_glyphs/ping_fang/expected.png Binary files differnew file mode 100644 index 0000000000..c0ba43bf11 --- /dev/null +++ b/test/fixtures/local_glyphs/ping_fang/expected.png diff --git a/test/fixtures/map/offline/expected.png b/test/fixtures/map/offline/expected.png Binary files differindex 973c4102da..0005137ae3 100644 --- a/test/fixtures/map/offline/expected.png +++ b/test/fixtures/map/offline/expected.png diff --git a/test/fixtures/shared_context/expected.png b/test/fixtures/shared_context/expected.png Binary files differnew file mode 100644 index 0000000000..3b3fd0e315 --- /dev/null +++ b/test/fixtures/shared_context/expected.png diff --git a/test/fixtures/style_parser/expressions.info.json b/test/fixtures/style_parser/expressions.info.json new file mode 100644 index 0000000000..9e1765ecd0 --- /dev/null +++ b/test/fixtures/style_parser/expressions.info.json @@ -0,0 +1,12 @@ +{ + "default": { + "log": [ + [1, "WARNING", "ParseStyle", "Expected color but found number instead."], + [1, "WARNING", "ParseStyle", "[2]: Expected number but found string instead."], + [1, "WARNING", "ParseStyle", "\"zoom\" expression may only be used as input to a top-level \"step\" or \"interpolate\" expression."], + [1, "WARNING", "ParseStyle", "value must be a string"], + [1, "WARNING", "ParseStyle", "property expressions not supported"], + [1, "WARNING", "ParseStyle", "Type array<number> is not interpolatable."] + ] + } +} diff --git a/test/fixtures/style_parser/expressions.style.json b/test/fixtures/style_parser/expressions.style.json new file mode 100644 index 0000000000..b9b4aeac7f --- /dev/null +++ b/test/fixtures/style_parser/expressions.style.json @@ -0,0 +1,74 @@ +{ + "version": 8, + "sources": { + "source": { + "type": "vector", + "url": "mapbox://mapbox.mapbox-streets-v5" + } + }, + "layers": [ + { + "id": "valid expression", + "type": "fill", + "source": "source", + "source-layer": "layer", + "paint": { + "fill-color": ["rgba", 10, ["number", ["get", "x"]], 30, 1] + } + }, + { + "id": "invalid expression type - color", + "type": "fill", + "source": "source", + "source-layer": "layer", + "paint": { + "fill-color": ["pi"] + } + }, + { + "id": "invalid expression - fails type checking", + "type": "fill", + "source": "source", + "source-layer": "layer", + "paint": { + "fill-color": ["rgba", 1, "should be a number", 0, 1] + } + }, + { + "id": "invalid expression - nested zoom expression", + "type": "fill", + "source": "source", + "source-layer": "layer", + "paint": { + "fill-opacity": ["+", 0.5, ["interpolate", ["linear"], ["zoom"], 0, 0, 1, 1]] + } + }, + { + "id": "invalid expression - not allowed in visibility", + "type": "fill", + "source": "source", + "source-layer": "layer", + "layout": { + "visibility": ["literal", true] + } + }, + { + "id": "invalid expression - not a DDS property", + "type": "fill-extrusion", + "source": "source", + "source-layer": "layer", + "paint": { + "fill-extrusion-opacity": ["get", "opacity"] + } + }, + { + "id": "invalid expression - line-dasharray must use step interpolation", + "type": "line", + "source": "source", + "source-layer": "layer", + "paint": { + "line-dasharray": ["interpolate", ["linear"], ["zoom"], 0, ["literal", [1, 2]], 1, ["literal", [3, 4]]] + } + } + ] +} diff --git a/test/fixtures/style_parser/font_stacks.json b/test/fixtures/style_parser/font_stacks.json index 07fe223d46..0ff3e936a8 100644 --- a/test/fixtures/style_parser/font_stacks.json +++ b/test/fixtures/style_parser/font_stacks.json @@ -12,6 +12,7 @@ "source": "source", "source-layer": "source-layer", "layout": { + "text-field": "a", "text-font": ["a"] } }, { @@ -20,6 +21,7 @@ "source": "source", "source-layer": "source-layer", "layout": { + "text-field": "a", "text-font": { "stops": [[0, ["a", "b"]]] } @@ -30,6 +32,7 @@ "source": "source", "source-layer": "source-layer", "layout": { + "text-field": "a", "text-font": { "stops": [[0, ["a", "b"]], [1, ["a", "b", "c"]]] } diff --git a/test/fixtures/style_parser/text-font.info.json b/test/fixtures/style_parser/text-font.info.json new file mode 100644 index 0000000000..0fb9658269 --- /dev/null +++ b/test/fixtures/style_parser/text-font.info.json @@ -0,0 +1,14 @@ +{ + "default": { + "log": [ + [1, "WARNING", "ParseStyle", "Layer 'invalid expression - get' has an invalid value for text-font and will not work offline. Output values must be contained as literals within the expression."], + [1, "WARNING", "ParseStyle", "Layer 'invalid expression - case' has an invalid value for text-font and will not work offline. Output values must be contained as literals within the expression."], + [1, "WARNING", "ParseStyle", "Layer 'invalid expression - match' has an invalid value for text-font and will not work offline. Output values must be contained as literals within the expression."], + [1, "WARNING", "ParseStyle", "Layer 'invalid expression - at' has an invalid value for text-font and will not work offline. Output values must be contained as literals within the expression."], + [1, "WARNING", "ParseStyle", "Layer 'invalid expression - coalesce' has an invalid value for text-font and will not work offline. Output values must be contained as literals within the expression."], + [1, "WARNING", "ParseStyle", "Layer 'invalid expression - step' has an invalid value for text-font and will not work offline. Output values must be contained as literals within the expression."], + [1, "WARNING", "ParseStyle", "Layer 'invalid expression - let/var' has an invalid value for text-font and will not work offline. Output values must be contained as literals within the expression."], + [1, "WARNING", "ParseStyle", "Layer 'invalid expression - identity function' has an invalid value for text-font and will not work offline. Output values must be contained as literals within the expression."] + ] + } +} diff --git a/test/fixtures/style_parser/text-font.style.json b/test/fixtures/style_parser/text-font.style.json new file mode 100644 index 0000000000..215807ca81 --- /dev/null +++ b/test/fixtures/style_parser/text-font.style.json @@ -0,0 +1,135 @@ +{ + "version": 8, + "glyphs": "https://example.com/{fontstack}/{range}", + "sources": { + "vector": { + "type": "vector", + "url": "mapbox://mapbox.mapbox-streets-v5" + } + }, + "layers": [ + { + "id": "minimum", + "type": "symbol", + "source": "vector", + "source-layer": "layer", + "layout": { + "text-font": ["Helvetica"], + "text-field": "{foo}" + } + }, + { + "id": "valid expression - case", + "type": "symbol", + "source": "vector", + "source-layer": "layer", + "layout": { + "text-font": ["case", ["==", "a", ["string", ["get", "text-font"]]], ["literal", ["Arial"]], ["literal", ["Helvetica"]]], + "text-field": "{foo}" + } + }, + { + "id": "valid expression - match", + "type": "symbol", + "source": "vector", + "source-layer": "layer", + "layout": { + "text-font": ["match", ["get", "text-font"], "a", ["literal", ["Arial"]], ["literal", ["Helvetica"]]], + "text-field": "{foo}" + } + }, + { + "id": "valid expression - step", + "type": "symbol", + "source": "vector", + "source-layer": "layer", + "layout": { + "text-font": ["array", "string", ["step", ["get", "text-font"], ["literal", ["Arial"]], 0, ["literal", ["Helvetica"]]]], + "text-field": "{foo}" + } + }, + { + "id": "invalid expression - get", + "type": "symbol", + "source": "vector", + "source-layer": "layer", + "layout": { + "text-font": ["array", "string", ["get", "text-font"]], + "text-field": "{foo}" + } + }, + { + "id": "invalid expression - case", + "type": "symbol", + "source": "vector", + "source-layer": "layer", + "layout": { + "text-font": ["case", ["==", "a", ["string", ["get", "text-font"]]], ["array", "string", ["get", "text-font-a"]], ["literal", ["Helvetica"]]], + "text-field": "{foo}" + } + }, + { + "id": "invalid expression - match", + "type": "symbol", + "source": "vector", + "source-layer": "layer", + "layout": { + "text-font": ["match", ["get", "text-font"], "a", ["array", "string", ["get", "text-font-a"]], ["literal", ["Helvetica"]]], + "text-field": "{foo}" + } + }, + { + "id": "invalid expression - at", + "type": "symbol", + "source": "vector", + "source-layer": "layer", + "layout": { + "text-font": ["array", "string", ["at", 0, ["array", ["get", "text-font"]]]], + "text-field": "{foo}" + } + }, + { + "id": "invalid expression - coalesce", + "type": "symbol", + "source": "vector", + "source-layer": "layer", + "layout": { + "text-font": ["array", "string", ["coalesce", ["get", "text-font"]]], + "text-field": "{foo}" + } + }, + { + "id": "invalid expression - step", + "type": "symbol", + "source": "vector", + "source-layer": "layer", + "layout": { + "text-font": ["step", ["zoom"], ["array", "string", ["get", "text-font-0"]], 0, ["array", "string", ["get", "text-font-1"]]], + "text-field": "{foo}" + } + }, + { + "id": "invalid expression - let/var", + "type": "symbol", + "source": "vector", + "source-layer": "layer", + "layout": { + "text-font": ["let", "p", ["array", "string", ["get", "text-font"]], ["var", "p"]], + "text-field": "{foo}" + } + }, + { + "id": "invalid expression - identity function", + "type": "symbol", + "source": "vector", + "source-layer": "layer", + "layout": { + "text-font": { + "type": "identity", + "property": "text-font" + }, + "text-field": "{foo}" + } + } + ] +} diff --git a/test/geometry/dem_data.test.cpp b/test/geometry/dem_data.test.cpp new file mode 100644 index 0000000000..3848f028f1 --- /dev/null +++ b/test/geometry/dem_data.test.cpp @@ -0,0 +1,149 @@ +#include <mbgl/test/util.hpp> + +#include <mbgl/util/image.hpp> +#include <mbgl/util/tileset.hpp> +#include <mbgl/geometry/dem_data.hpp> + +using namespace mbgl; + +auto fakeImage = [](Size s) { + PremultipliedImage img = PremultipliedImage(s); + + for (size_t i = 0; i < img.bytes(); i ++) { + img.data[i] = (i+1) % 4 == 0 ? 1 : std::rand() % 255; + } + return img; +}; + +TEST(DEMData, ConstructorMapbox) { + PremultipliedImage image = fakeImage({16, 16}); + DEMData demdata(image, Tileset::DEMEncoding::Mapbox); + + EXPECT_EQ(demdata.dim, 16); + EXPECT_EQ(demdata.border, 8); + EXPECT_EQ(demdata.stride, 32); + EXPECT_EQ(demdata.getImage()->bytes(), size_t(32*32*4)); +}; + +TEST(DEMData, ConstructorTerrarium) { + PremultipliedImage image = fakeImage({16, 16}); + DEMData demdata(image, Tileset::DEMEncoding::Terrarium); + + EXPECT_EQ(demdata.dim, 16); + EXPECT_EQ(demdata.border, 8); + EXPECT_EQ(demdata.stride, 32); + EXPECT_EQ(demdata.getImage()->bytes(), size_t(32*32*4)); +}; + +TEST(DEMData, RoundTrip) { + PremultipliedImage image = fakeImage({16, 16}); + DEMData demdata(image, Tileset::DEMEncoding::Mapbox); + + demdata.set(4, 6, 255); + EXPECT_EQ(demdata.get(4, 6), 255); +} + +TEST(DEMData, InitialBackfill) { + + PremultipliedImage image1 = fakeImage({4, 4}); + DEMData dem1(image1, Tileset::DEMEncoding::Mapbox); + + bool nonempty = true; + // checking that a 1 px border around the fake image has been populated + // with a non-empty pixel value + for (int x = -1; x < 5; x++){ + for (int y = -1; y < 5; y ++) { + if (dem1.get(x, y) == -65536) { + nonempty = false; + break; + } + } + } + EXPECT_TRUE(nonempty); + + bool verticalBorderMatch = true; + int vertx[] = {-1, 4}; + for (int x : vertx) { + for (int y = 0; y < 4; y++) { + if (dem1.get(x, y) != dem1.get(x < 0 ? x + 1 : x - 1, y)) { + verticalBorderMatch = false; + break; + } + } + } + // vertical border of DEM data is initially equal to next column of data + EXPECT_TRUE(verticalBorderMatch); + + // horizontal borders empty + bool horizontalBorderMatch = true; + int horiz[] = {-1, 4}; + for (int y : horiz) { + for (int x = 0; x < 4; x++) { + if (dem1.get(x, y) != dem1.get(x, y < 0 ? y + 1 : y - 1)) { + horizontalBorderMatch = false; + break; + } + } + } + //horizontal border of DEM data is initially equal to next row of data + + EXPECT_TRUE(horizontalBorderMatch); + // -1, 1 corner initially equal to closest corner data + EXPECT_TRUE(dem1.get(-1, 4) == dem1.get(0, 3)); + // 1, 1 corner initially equal to closest corner data + EXPECT_TRUE(dem1.get(4, 4) == dem1.get(3, 3)); + // -1, -1 corner initially equal to closest corner data + EXPECT_TRUE(dem1.get(-1, -1) == dem1.get(0, 0)); + // -1, 1 corner initially equal to closest corner data + EXPECT_TRUE(dem1.get(4, -1) == dem1.get(3, 0)); +}; + +TEST(DEMData, BackfillNeighbor) { + PremultipliedImage image1 = fakeImage({4, 4}); + DEMData dem0(image1, Tileset::DEMEncoding::Mapbox); + + PremultipliedImage image2 = fakeImage({4, 4}); + DEMData dem1(image2, Tileset::DEMEncoding::Mapbox); + + dem0.backfillBorder(dem1, -1, 0); + for (int y = 0; y < 4; y++) { + // dx = -1, dy = 0, so the left edge of dem1 should equal the right edge of dem0 + // backfills Left neighbor + EXPECT_TRUE(dem0.get(-1, y) == dem1.get(3, y)); + + } + + dem0.backfillBorder(dem1, 0, -1); + // backfills TopCenter neighbor + for (int x = 0; x < 4; x++) { + EXPECT_TRUE(dem0.get(x, -1) == dem1.get(x, 3)); + } + + dem0.backfillBorder(dem1, 1, 0); + // backfills Right neighbor + for (int y = 0; y < 4; y++) { + EXPECT_TRUE(dem0.get(4, y) == dem1.get(0, y)); + } + + dem0.backfillBorder(dem1, 0, 1); + // backfills BottomCenter neighbor + for (int x = 0; x < 4; x++) { + EXPECT_TRUE(dem0.get(x, 4) == dem1.get(x, 0)); + } + + dem0.backfillBorder(dem1, -1, 1); + // backfulls TopRight neighbor + EXPECT_TRUE(dem0.get(-1, 4) == dem1.get(3, 0)); + + dem0.backfillBorder(dem1, 1, 1); + // backfulls BottomRight neighbor + EXPECT_TRUE(dem0.get(4, 4) == dem1.get(0, 0)); + + dem0.backfillBorder(dem1, -1, -1); + // backfulls TopLeft neighbor + EXPECT_TRUE(dem0.get(-1, -1) == dem1.get(3, 3)); + + dem0.backfillBorder(dem1, 1, -1); + // backfulls BottomLeft neighbor + EXPECT_TRUE(dem0.get(4, -1) == dem1.get(0, 3)); +}; diff --git a/test/gl/bucket.test.cpp b/test/gl/bucket.test.cpp index 5f9626bc99..8393fd130d 100644 --- a/test/gl/bucket.test.cpp +++ b/test/gl/bucket.test.cpp @@ -1,6 +1,7 @@ #include <mbgl/test/util.hpp> #include <mbgl/test/stub_geometry_tile_feature.hpp> +#include <mbgl/renderer/backend_scope.hpp> #include <mbgl/renderer/buckets/circle_bucket.hpp> #include <mbgl/renderer/buckets/fill_bucket.hpp> #include <mbgl/renderer/buckets/line_bucket.hpp> @@ -9,6 +10,7 @@ #include <mbgl/renderer/bucket_parameters.hpp> #include <mbgl/style/layers/symbol_layer_properties.hpp> #include <mbgl/gl/context.hpp> +#include <mbgl/gl/headless_backend.hpp> #include <mbgl/map/mode.hpp> @@ -41,8 +43,11 @@ PropertyMap properties; } // namespace TEST(Buckets, CircleBucket) { + HeadlessBackend backend({ 512, 256 }); + BackendScope scope { backend }; + gl::Context context; - CircleBucket bucket { { {0, 0, 0}, MapMode::Still, 1.0 }, {} }; + CircleBucket bucket { { {0, 0, 0}, MapMode::Static, 1.0 }, {} }; ASSERT_FALSE(bucket.hasData()); ASSERT_FALSE(bucket.needsUpload()); @@ -57,8 +62,11 @@ TEST(Buckets, CircleBucket) { } TEST(Buckets, FillBucket) { + HeadlessBackend backend({ 512, 256 }); + BackendScope scope { backend }; + gl::Context context; - FillBucket bucket { { {0, 0, 0}, MapMode::Still, 1.0 }, {} }; + FillBucket bucket { { {0, 0, 0}, MapMode::Static, 1.0 }, {} }; ASSERT_FALSE(bucket.hasData()); ASSERT_FALSE(bucket.needsUpload()); @@ -72,8 +80,11 @@ TEST(Buckets, FillBucket) { } TEST(Buckets, LineBucket) { + HeadlessBackend backend({ 512, 256 }); + BackendScope scope { backend }; + gl::Context context; - LineBucket bucket { { {0, 0, 0}, MapMode::Still, 1.0 }, {}, {} }; + LineBucket bucket { { {0, 0, 0}, MapMode::Static, 1.0 }, {}, {} }; ASSERT_FALSE(bucket.hasData()); ASSERT_FALSE(bucket.needsUpload()); @@ -92,12 +103,17 @@ TEST(Buckets, LineBucket) { } TEST(Buckets, SymbolBucket) { + HeadlessBackend backend({ 512, 256 }); + BackendScope scope { backend }; + style::SymbolLayoutProperties::PossiblyEvaluated layout; bool sdfIcons = false; bool iconsNeedLinear = false; + bool sortFeaturesByY = false; + std::vector<SymbolInstance> symbolInstances; gl::Context context; - SymbolBucket bucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear }; + SymbolBucket bucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(symbolInstances) }; ASSERT_FALSE(bucket.hasIconData()); ASSERT_FALSE(bucket.hasTextData()); ASSERT_FALSE(bucket.hasCollisionBoxData()); @@ -120,6 +136,9 @@ TEST(Buckets, SymbolBucket) { } TEST(Buckets, RasterBucket) { + HeadlessBackend backend({ 512, 256 }); + BackendScope scope { backend }; + gl::Context context; PremultipliedImage rgba({ 1, 1 }); diff --git a/test/gl/context.test.cpp b/test/gl/context.test.cpp new file mode 100644 index 0000000000..179ce5de53 --- /dev/null +++ b/test/gl/context.test.cpp @@ -0,0 +1,114 @@ +#include <mbgl/test/util.hpp> + +#include <mbgl/gl/gl.hpp> +#include <mbgl/gl/context.hpp> +#include <mbgl/map/map.hpp> +#include <mbgl/util/default_thread_pool.hpp> +#include <mbgl/storage/default_file_source.hpp> +#include <mbgl/gl/headless_frontend.hpp> +#include <mbgl/style/style.hpp> +#include <mbgl/style/layers/custom_layer.hpp> +#include <mbgl/style/layers/fill_layer.hpp> +#include <mbgl/style/layers/background_layer.hpp> +#include <mbgl/util/io.hpp> +#include <mbgl/util/mat4.hpp> +#include <mbgl/util/run_loop.hpp> + +using namespace mbgl; +using namespace mbgl::style; + +static const GLchar* vertexShaderSource = R"MBGL_SHADER( +#ifdef GL_ES +precision mediump float; +#endif +attribute vec2 a_pos; +void main() { + gl_Position = vec4(a_pos, 0, 1); +} +)MBGL_SHADER"; + +static const GLchar* fragmentShaderSource = R"MBGL_SHADER( +#ifdef GL_ES +precision mediump float; +#endif +void main() { + gl_FragColor = vec4(0, 1, 0, 1); +} +)MBGL_SHADER"; + +struct Shader { + Shader(const GLchar* vertex, const GLchar* fragment) { + program = MBGL_CHECK_ERROR(glCreateProgram()); + vertexShader = MBGL_CHECK_ERROR(glCreateShader(GL_VERTEX_SHADER)); + fragmentShader = MBGL_CHECK_ERROR(glCreateShader(GL_FRAGMENT_SHADER)); + MBGL_CHECK_ERROR(glShaderSource(vertexShader, 1, &vertex, nullptr)); + MBGL_CHECK_ERROR(glCompileShader(vertexShader)); + MBGL_CHECK_ERROR(glAttachShader(program, vertexShader)); + MBGL_CHECK_ERROR(glShaderSource(fragmentShader, 1, &fragment, nullptr)); + MBGL_CHECK_ERROR(glCompileShader(fragmentShader)); + MBGL_CHECK_ERROR(glAttachShader(program, fragmentShader)); + MBGL_CHECK_ERROR(glLinkProgram(program)); + a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos")); + } + + ~Shader() { + MBGL_CHECK_ERROR(glDetachShader(program, vertexShader)); + MBGL_CHECK_ERROR(glDetachShader(program, fragmentShader)); + MBGL_CHECK_ERROR(glDeleteShader(vertexShader)); + MBGL_CHECK_ERROR(glDeleteShader(fragmentShader)); + MBGL_CHECK_ERROR(glDeleteProgram(program)); + } + + GLuint program = 0; + GLuint vertexShader = 0; + GLuint fragmentShader = 0; + GLuint a_pos = 0; +}; + +struct Buffer { + Buffer(std::vector<GLfloat> data) { + MBGL_CHECK_ERROR(glGenBuffers(1, &buffer)); + MBGL_CHECK_ERROR(glBindBuffer(GL_ARRAY_BUFFER, buffer)); + MBGL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, data.size() * sizeof(GLfloat), data.data(), + GL_STATIC_DRAW)); + } + + ~Buffer() { + MBGL_CHECK_ERROR(glDeleteBuffers(1, &buffer)); + } + + GLuint buffer = 0; +}; + +TEST(GLContextMode, Shared) { + util::RunLoop loop; + + DefaultFileSource fileSource(":memory:", "test/fixtures/api/assets"); + ThreadPool threadPool(4); + float pixelRatio { 1 }; + + HeadlessFrontend frontend { pixelRatio, fileSource, threadPool, {}, GLContextMode::Shared }; + + Map map(frontend, MapObserver::nullObserver(), frontend.getSize(), pixelRatio, fileSource, threadPool, MapMode::Static); + map.getStyle().loadJSON(util::read_file("test/fixtures/api/water.json")); + map.setLatLngZoom({ 37.8, -122.5 }, 10); + + // Set transparent background layer. + map.getStyle().getLayer("background")->as<BackgroundLayer>()->setBackgroundColor( { { 1.0f, 0.0f, 0.0f, 0.5f } } ); + + { + // Custom rendering outside of GL Native render loop. + BackendScope scope { *frontend.getBackend() }; + frontend.getBackend()->bind(); + + Shader paintShader(vertexShaderSource, fragmentShaderSource); + Buffer triangleBuffer({ 0, 0.5, 0.5, -0.5, -0.5, -0.5 }); + MBGL_CHECK_ERROR(glUseProgram(paintShader.program)); + MBGL_CHECK_ERROR(glBindBuffer(GL_ARRAY_BUFFER, triangleBuffer.buffer)); + MBGL_CHECK_ERROR(glEnableVertexAttribArray(paintShader.a_pos)); + MBGL_CHECK_ERROR(glVertexAttribPointer(paintShader.a_pos, 2, GL_FLOAT, GL_FALSE, 0, nullptr)); + MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, 3)); + } + + test::checkImage("test/fixtures/shared_context", frontend.render(map), 0.5, 0.1); +} diff --git a/test/map/map.test.cpp b/test/map/map.test.cpp index 9358175297..9b34ea89b0 100644 --- a/test/map/map.test.cpp +++ b/test/map/map.test.cpp @@ -72,14 +72,14 @@ public: HeadlessFrontend frontend; Map map; - MapTest(float pixelRatio = 1, MapMode mode = MapMode::Still) + MapTest(float pixelRatio = 1, MapMode mode = MapMode::Static) : frontend(pixelRatio, fileSource, threadPool) , map(frontend, observer, frontend.getSize(), pixelRatio, fileSource, threadPool, mode) { } template <typename T = FileSource> MapTest(const std::string& cachePath, const std::string& assetRoot, - float pixelRatio = 1, MapMode mode = MapMode::Still, + float pixelRatio = 1, MapMode mode = MapMode::Static, typename std::enable_if<std::is_same<T, DefaultFileSource>::value>::type* = 0) : fileSource { cachePath, assetRoot } , frontend(pixelRatio, fileSource, threadPool) diff --git a/test/map/prefetch.test.cpp b/test/map/prefetch.test.cpp index 5c9a22d1bf..4c82b2c965 100644 --- a/test/map/prefetch.test.cpp +++ b/test/map/prefetch.test.cpp @@ -23,7 +23,7 @@ TEST(Map, PrefetchTiles) { ThreadPool threadPool(4); StubFileSource fileSource; HeadlessFrontend frontend { { 512, 512 }, 1, fileSource, threadPool }; - Map map(frontend, MapObserver::nullObserver(), frontend.getSize(), 1, fileSource, threadPool, MapMode::Still); + Map map(frontend, MapObserver::nullObserver(), frontend.getSize(), 1, fileSource, threadPool, MapMode::Static); std::vector<int> tiles; diff --git a/test/renderer/backend_scope.test.cpp b/test/renderer/backend_scope.test.cpp index 66cf88a9c6..05b82695b2 100644 --- a/test/renderer/backend_scope.test.cpp +++ b/test/renderer/backend_scope.test.cpp @@ -28,18 +28,13 @@ public: if (updateAssumedStateFunction) updateAssumedStateFunction(); } - gl::ProcAddress initializeExtension(const char* ext) override { - if (initializeExtensionFunction) { - return initializeExtensionFunction(ext); - } else { - return {}; - } + gl::ProcAddress getExtensionFunctionPointer(const char*) override { + return {}; } std::function<void ()> activateFunction; std::function<void ()> deactivateFunction; std::function<void ()> updateAssumedStateFunction; - std::function<gl::ProcAddress (const char*)> initializeExtensionFunction; }; // A scope should activate on construction diff --git a/test/src/app-info.plist b/test/src/app-info.plist index 107e4058f9..6ee059b3b4 100644 --- a/test/src/app-info.plist +++ b/test/src/app-info.plist @@ -25,7 +25,7 @@ <key>LSRequiresIPhoneOS</key> <true/> <key>NSHumanReadableCopyright</key> - <string>© 2014–2017 Mapbox</string> + <string>© 2014–2018 Mapbox</string> <key>NSLocationAlwaysUsageDescription</key> <string>The map will ALWAYS display the user's location.</string> <key>NSLocationWhenInUseUsageDescription</key> diff --git a/test/src/mbgl/test/conversion_stubs.hpp b/test/src/mbgl/test/conversion_stubs.hpp deleted file mode 100644 index 30395ddb97..0000000000 --- a/test/src/mbgl/test/conversion_stubs.hpp +++ /dev/null @@ -1,124 +0,0 @@ -#pragma once - -#include <mbgl/style/conversion.hpp> -#include <mbgl/util/feature.hpp> -#include <mbgl/util/optional.hpp> -#include <mbgl/util/variant.hpp> - -#include <string> -#include <unordered_map> - -namespace mbgl { -namespace style { -namespace conversion { - -class Value; -using ValueMap = std::unordered_map<std::string, Value>; -using ValueVector = std::vector<Value>; -class Value : public mbgl::variant<std::string, - float, - double, - bool, - mapbox::util::recursive_wrapper<ValueMap>, - mapbox::util::recursive_wrapper<ValueVector>> { - using variant::variant; -}; - -inline bool isUndefined(const Value&) { - // Variant is always intialized - return false; -} - -inline bool isArray(const Value& value) { - return value.is<mapbox::util::recursive_wrapper<ValueVector>>(); -} - -inline std::size_t arrayLength(const Value& value) { - return value.get<mapbox::util::recursive_wrapper<ValueVector>>().get().size(); -} - -inline Value arrayMember(const Value& value, std::size_t i) { - return value.get<mapbox::util::recursive_wrapper<ValueVector>>().get()[i]; -} - -inline bool isObject(const Value& value) { - return value.is<mapbox::util::recursive_wrapper<ValueMap>>(); -} - -inline optional<Value> objectMember(const Value& value, const char* key) { - auto map = value.get<ValueMap>(); - auto iter = map.find(key); - - if (iter != map.end()) { - return iter->second; - } else { - return {}; - } -} - -using EachMemberFn = std::function<optional<Error>(const std::string&, const Value&)>; - -optional<Error> eachMember(const Value& value, EachMemberFn&& fn) { - auto map = value.get<ValueMap>(); - auto iter = map.begin(); - - while (iter != map.end()) { - optional<Error> result = fn(iter->first, iter->second); - if (result) { - return result; - } - - ++iter; - } - - return {}; -} - -inline optional<bool> toBool(const Value& value) { - if (value.is<bool>()) { - return value.get<bool>(); - } else { - return {}; - } -} - -inline optional<float> toNumber(const Value& value) { - if (value.is<float>()) { - return value.get<float>(); - } else { - return {}; - } - return {}; -} - - -inline optional<double> toDouble(const Value& value) { - if (value.is<double>()) { - return value.get<double>(); - } - return {}; -} - -inline optional<std::string> toString(const Value& value) { - if (value.is<std::string>()) { - return value.get<std::string>(); - } else { - return {}; - } -} - -inline optional<mbgl::Value> toValue(const Value& value) { - if (value.is<bool>()) { - return { value.get<bool>() }; - } else if (value.is<std::string>()) { - return { value.get<std::string>() }; - } else if (value.is<float>()) { - return { double(value.get<float>()) }; - } else { - return {}; - } -} - -} // namespace conversion -} // namespace style -} // namespace mbgl diff --git a/test/src/mbgl/test/stub_file_source.cpp b/test/src/mbgl/test/stub_file_source.cpp index 7891d5d907..0bbff84ff3 100644 --- a/test/src/mbgl/test/stub_file_source.cpp +++ b/test/src/mbgl/test/stub_file_source.cpp @@ -17,7 +17,12 @@ public: StubFileSource& fileSource; }; -StubFileSource::StubFileSource() { +StubFileSource::StubFileSource(ResponseType type_) + : type(type_) { + if (type == ResponseType::Synchronous) { + return; + } + timer.start(1ms, 1ms, [this] { // Explicit copy to avoid iterator invalidation if ~StubFileRequest gets called within the loop. auto pending_ = pending; @@ -46,7 +51,14 @@ StubFileSource::~StubFileSource() = default; std::unique_ptr<AsyncRequest> StubFileSource::request(const Resource& resource, Callback callback) { auto req = std::make_unique<StubFileRequest>(*this); - pending.emplace(req.get(), std::make_tuple(resource, response, callback)); + if (type == ResponseType::Synchronous) { + optional<Response> res = response(resource); + if (res) { + callback(*res); + } + } else { + pending.emplace(req.get(), std::make_tuple(resource, response, callback)); + } return std::move(req); } diff --git a/test/src/mbgl/test/stub_file_source.hpp b/test/src/mbgl/test/stub_file_source.hpp index 85118e1a77..6cee8377c6 100644 --- a/test/src/mbgl/test/stub_file_source.hpp +++ b/test/src/mbgl/test/stub_file_source.hpp @@ -9,7 +9,12 @@ namespace mbgl { class StubFileSource : public FileSource { public: - StubFileSource(); + enum class ResponseType { + Asynchronous = 0, + Synchronous + }; + + StubFileSource(ResponseType = ResponseType::Asynchronous); ~StubFileSource() override; std::unique_ptr<AsyncRequest> request(const Resource&, Callback) override; @@ -36,6 +41,7 @@ private: optional<Response> defaultResponse(const Resource&); std::unordered_map<AsyncRequest*, std::tuple<Resource, ResponseFunction, Callback>> pending; + ResponseType type; util::Timer timer; }; diff --git a/test/storage/asset_file_source.test.cpp b/test/storage/asset_file_source.test.cpp index a39d2963d2..978a41a306 100644 --- a/test/storage/asset_file_source.test.cpp +++ b/test/storage/asset_file_source.test.cpp @@ -69,6 +69,15 @@ TEST(AssetFileSource, Load) { loop.run(); } +TEST(AssetFileSource, AcceptsURL) { + EXPECT_TRUE(AssetFileSource::acceptsURL("asset://empty")); + EXPECT_TRUE(AssetFileSource::acceptsURL("asset:///test")); + EXPECT_FALSE(AssetFileSource::acceptsURL("assds://foo")); + EXPECT_FALSE(AssetFileSource::acceptsURL("asset:")); + EXPECT_FALSE(AssetFileSource::acceptsURL("style.json")); + EXPECT_FALSE(AssetFileSource::acceptsURL("")); +} + TEST(AssetFileSource, EmptyFile) { util::RunLoop loop; @@ -118,6 +127,23 @@ TEST(AssetFileSource, NonExistentFile) { loop.run(); } +TEST(AssetFileSource, InvalidURL) { + util::RunLoop loop; + + AssetFileSource fs("test/fixtures/storage/assets"); + + std::unique_ptr<AsyncRequest> req = fs.request({ Resource::Unknown, "test://wrong-scheme" }, [&](Response res) { + req.reset(); + ASSERT_NE(nullptr, res.error); + EXPECT_EQ(Response::Error::Reason::Other, res.error->reason); + EXPECT_EQ("Invalid asset URL", res.error->message); + ASSERT_FALSE(res.data.get()); + loop.stop(); + }); + + loop.run(); +} + TEST(AssetFileSource, ReadDirectory) { util::RunLoop loop; diff --git a/test/storage/local_file_source.test.cpp b/test/storage/local_file_source.test.cpp index 4d509e6c7d..e1756f8e7d 100644 --- a/test/storage/local_file_source.test.cpp +++ b/test/storage/local_file_source.test.cpp @@ -20,6 +20,15 @@ std::string toAbsoluteURL(const std::string& fileName) { using namespace mbgl; +TEST(LocalFileSource, AcceptsURL) { + EXPECT_TRUE(LocalFileSource::acceptsURL("file://empty")); + EXPECT_TRUE(LocalFileSource::acceptsURL("file:///test")); + EXPECT_FALSE(LocalFileSource::acceptsURL("flie://foo")); + EXPECT_FALSE(LocalFileSource::acceptsURL("file:")); + EXPECT_FALSE(LocalFileSource::acceptsURL("style.json")); + EXPECT_FALSE(LocalFileSource::acceptsURL("")); +} + TEST(LocalFileSource, EmptyFile) { util::RunLoop loop; @@ -69,6 +78,23 @@ TEST(LocalFileSource, NonExistentFile) { loop.run(); } +TEST(LocalFileSource, InvalidURL) { + util::RunLoop loop; + + LocalFileSource fs; + + std::unique_ptr<AsyncRequest> req = fs.request({ Resource::Unknown, "test://wrong-scheme" }, [&](Response res) { + req.reset(); + ASSERT_NE(nullptr, res.error); + EXPECT_EQ(Response::Error::Reason::Other, res.error->reason); + EXPECT_EQ("Invalid file URL", res.error->message); + ASSERT_FALSE(res.data.get()); + loop.stop(); + }); + + loop.run(); +} + TEST(LocalFileSource, ReadDirectory) { util::RunLoop loop; diff --git a/test/storage/offline.test.cpp b/test/storage/offline.test.cpp index e7ebe5199f..59aebebaba 100644 --- a/test/storage/offline.test.cpp +++ b/test/storage/offline.test.cpp @@ -4,6 +4,7 @@ #include <gtest/gtest.h> using namespace mbgl; +using SourceType = mbgl::style::SourceType; static const LatLngBounds sanFrancisco = LatLngBounds::hull({ 37.6609, -122.5744 }, { 37.8271, -122.3204 }); diff --git a/test/storage/offline_database.test.cpp b/test/storage/offline_database.test.cpp index d99c1f946f..94daf59c02 100644 --- a/test/storage/offline_database.test.cpp +++ b/test/storage/offline_database.test.cpp @@ -9,7 +9,6 @@ #include <gtest/gtest.h> #include <sqlite3.hpp> -#include <sqlite3.h> #include <thread> #include <random> @@ -39,45 +38,6 @@ void writeFile(const char* name, const std::string& data) { mbgl::util::write_file(name, data); } -class FileLock { -public: - FileLock(const std::string& path) { - const int err = sqlite3_open_v2(path.c_str(), &db, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, nullptr); - if (err != SQLITE_OK) { - throw std::runtime_error("Could not open db"); - } - lock(); - } - - void lock() { - assert(!locked); - const int err = sqlite3_exec(db, "begin exclusive transaction", nullptr, nullptr, nullptr); - if (err != SQLITE_OK) { - throw std::runtime_error("Could not lock db"); - } - locked = true; - } - - void unlock() { - assert(locked); - const int err = sqlite3_exec(db, "commit", nullptr, nullptr, nullptr); - if (err != SQLITE_OK) { - throw std::runtime_error("Could not unlock db"); - } - locked = false; - } - - ~FileLock() { - if (locked) { - unlock(); - } - } - -private: - sqlite3* db = nullptr; - bool locked = false; -}; - } // namespace TEST(OfflineDatabase, TEST_REQUIRES_WRITE(Create)) { @@ -102,10 +62,8 @@ TEST(OfflineDatabase, TEST_REQUIRES_WRITE(SchemaVersion)) { std::string path("test/fixtures/offline_database/offline.db"); { - sqlite3* db = nullptr; - sqlite3_open_v2(path.c_str(), &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr); - sqlite3_exec(db, "PRAGMA user_version = 1", nullptr, nullptr, nullptr); - sqlite3_close_v2(db); + mapbox::sqlite::Database db(path, mapbox::sqlite::Create | mapbox::sqlite::ReadWrite); + db.exec("PRAGMA user_version = 1"); } Log::setObserver(std::make_unique<FixtureLogObserver>()); diff --git a/test/style/conversion/function.test.cpp b/test/style/conversion/function.test.cpp index 1eff94d939..a48be2c075 100644 --- a/test/style/conversion/function.test.cpp +++ b/test/style/conversion/function.test.cpp @@ -1,9 +1,9 @@ #include <mbgl/test/util.hpp> -#include <mbgl/style/conversion.hpp> -#include <mbgl/style/rapidjson_conversion.hpp> +#include <mbgl/style/conversion/json.hpp> #include <mbgl/style/conversion/constant.hpp> #include <mbgl/style/conversion/function.hpp> +#include <mbgl/style/conversion/data_driven_property_value.hpp> #include <mbgl/util/rapidjson.hpp> using namespace mbgl; @@ -13,10 +13,8 @@ using namespace mbgl::style::conversion; TEST(StyleConversion, Function) { Error error; - auto parseFunction = [&](const std::string& src) { - JSDocument doc; - doc.Parse<0>(src); - return convert<CameraFunction<float>, JSValue>(doc, error); + auto parseFunction = [&](const std::string& json) { + return convertJSON<CameraFunction<float>>(json, error); }; auto fn1 = parseFunction(R"({"stops":[]})"); @@ -54,3 +52,29 @@ TEST(StyleConversion, Function) { ASSERT_FALSE(fn9); ASSERT_EQ("function base must be a number", error.message); } + +TEST(StyleConversion, CompositeFunctionExpression) { + Error error; + + auto parseFunction = [&](const std::string& src) { + JSDocument doc; + doc.Parse<0>(src); + return convert<DataDrivenPropertyValue<float>>(doc, error); + }; + + auto fn1 = parseFunction(R"(["interpolate", ["linear"], ["zoom"], 0, ["number", ["get", "x"]], 10, 10])"); + ASSERT_TRUE(fn1); + + auto fn2 = parseFunction(R"(["coalesce", ["interpolate", ["linear"], ["zoom"], 0, ["number", ["get", "x"]], 10, 10], 0])"); + ASSERT_TRUE(fn2); + + auto fn3 = parseFunction(R"(["let", "a", 0, ["interpolate", ["linear"], ["zoom"], 0, ["number", ["get", "x"]], 10, 10] ])"); + ASSERT_TRUE(fn3); + + auto fn4 = parseFunction(R"(["coalesce", ["let", "a", 0, ["interpolate", ["linear"], ["zoom"], 0, ["number", ["get", "x"]], 10, 10], 0 ])"); + ASSERT_TRUE(fn4); + + auto fn5 = parseFunction(R"(["coalesce", ["interpolate", ["linear"], ["number", ["get", "x"]], 0, ["zoom"], 10, 10], 0])"); + ASSERT_FALSE(fn5); + ASSERT_EQ(R"("zoom" expression may only be used as input to a top-level "step" or "interpolate" expression.)", error.message); +} diff --git a/test/style/conversion/geojson_options.test.cpp b/test/style/conversion/geojson_options.test.cpp index a798ad6559..4c5a0c9aa4 100644 --- a/test/style/conversion/geojson_options.test.cpp +++ b/test/style/conversion/geojson_options.test.cpp @@ -1,8 +1,7 @@ #include <mbgl/test/util.hpp> -#include <mbgl/style/conversion.hpp> +#include <mbgl/style/conversion/json.hpp> #include <mbgl/style/conversion/geojson_options.hpp> -#include <mbgl/test/conversion_stubs.hpp> #include <mbgl/util/logging.hpp> @@ -10,26 +9,22 @@ using namespace mbgl::style; using namespace mbgl::style::conversion; TEST(GeoJSONOptions, Basic) { - ValueMap map; - Value raw(map); Error error; - mbgl::optional<GeoJSONOptions> converted = convert<GeoJSONOptions>(raw, error); + mbgl::optional<GeoJSONOptions> converted = convertJSON<GeoJSONOptions>("{}", error); ASSERT_TRUE((bool) converted); } TEST(GeoJSONOptions, ErrorHandling) { - ValueMap map {{"maxzoom", std::string{"should not be a string"}}}; - Value raw(map); Error error; - mbgl::optional<GeoJSONOptions> converted = convert<GeoJSONOptions>(raw, error); + mbgl::optional<GeoJSONOptions> converted = convertJSON<GeoJSONOptions>(R"JSON({ + "maxzoom": "should not be a string" + })JSON", error); ASSERT_FALSE((bool) converted); } TEST(GeoJSONOptions, RetainsDefaults) { - ValueMap map; - Value raw(map); Error error; - GeoJSONOptions converted = *convert<GeoJSONOptions>(raw, error); + GeoJSONOptions converted = *convertJSON<GeoJSONOptions>("{}", error); GeoJSONOptions defaults; // GeoJSON-VT @@ -44,22 +39,16 @@ TEST(GeoJSONOptions, RetainsDefaults) { ASSERT_EQ(converted.clusterMaxZoom, defaults.clusterMaxZoom); } - TEST(GeoJSONOptions, FullConversion) { - ValueMap map { - // GeoJSON-VT - {"maxzoom", 1.0f}, - {"buffer", 2.0f}, - {"tolerance", 3.0f}, - - // Supercluster - {"cluster", true}, - {"clusterRadius", 4.0f}, - {"clusterMaxZoom", 5.0f} - }; - Value raw(map); Error error; - GeoJSONOptions converted = *convert<GeoJSONOptions>(raw, error); + GeoJSONOptions converted = *convertJSON<GeoJSONOptions>(R"JSON({ + "maxzoom": 1, + "buffer": 2, + "tolerance": 3, + "cluster": true, + "clusterRadius": 4, + "clusterMaxZoom": 5 + })JSON", error); // GeoJSON-VT ASSERT_EQ(converted.minzoom, 0); diff --git a/test/style/conversion/layer.test.cpp b/test/style/conversion/layer.test.cpp index d51d7d33e2..33cd329999 100644 --- a/test/style/conversion/layer.test.cpp +++ b/test/style/conversion/layer.test.cpp @@ -1,10 +1,8 @@ #include <mbgl/test/util.hpp> -#include <mbgl/style/conversion.hpp> -#include <mbgl/style/rapidjson_conversion.hpp> +#include <mbgl/style/conversion/json.hpp> #include <mbgl/style/conversion/layer.hpp> #include <mbgl/style/layers/background_layer_impl.hpp> -#include <mbgl/util/rapidjson.hpp> using namespace mbgl; using namespace mbgl::style; @@ -12,10 +10,8 @@ using namespace mbgl::style::conversion; using namespace std::literals::chrono_literals; std::unique_ptr<Layer> parseLayer(const std::string& src) { - JSDocument doc; - doc.Parse<0>(src); Error error; - return std::move(*convert<std::unique_ptr<Layer>, JSValue>(doc, error)); + return std::move(*convertJSON<std::unique_ptr<Layer>>(src, error)); } TEST(StyleConversion, LayerTransition) { diff --git a/test/style/conversion/light.test.cpp b/test/style/conversion/light.test.cpp index 28e22b3550..67e48c942e 100644 --- a/test/style/conversion/light.test.cpp +++ b/test/style/conversion/light.test.cpp @@ -1,11 +1,10 @@ #include <mbgl/test/util.hpp> #include <mbgl/style/conversion.hpp> -#include <mbgl/style/rapidjson_conversion.hpp> +#include <mbgl/style/conversion/json.hpp> #include <mbgl/style/conversion/constant.hpp> #include <mbgl/style/conversion/light.hpp> #include <mbgl/style/position.hpp> -#include <mbgl/util/rapidjson.hpp> #include <mbgl/util/color.hpp> #include <mbgl/util/chrono.hpp> @@ -19,9 +18,7 @@ TEST(StyleConversion, Light) { Error error; auto parseLight = [&](const std::string& src) { - JSDocument doc; - doc.Parse<0>(src); - return convert<Light>(doc, error); + return convertJSON<Light>(src, error); }; { diff --git a/test/style/expression/expression.test.cpp b/test/style/expression/expression.test.cpp new file mode 100644 index 0000000000..694569695c --- /dev/null +++ b/test/style/expression/expression.test.cpp @@ -0,0 +1,91 @@ +#include <mbgl/test/util.hpp> +#include <mbgl/util/io.hpp> +#include <mbgl/style/conversion.hpp> +#include <mbgl/util/rapidjson.hpp> +#include <mbgl/style/rapidjson_conversion.hpp> +#include <mbgl/style/expression/is_expression.hpp> + +#include <rapidjson/document.h> + +#include <iostream> +#include <fstream> +#include <dirent.h> + + +using namespace mbgl; +using namespace mbgl::style; + +TEST(Expression, IsExpression) { + rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> spec; + spec.Parse<0>(util::read_file("mapbox-gl-js/src/style-spec/reference/v8.json").c_str()); + ASSERT_FALSE(spec.HasParseError()); + ASSERT_TRUE(spec.IsObject() && + spec.HasMember("expression_name") && + spec["expression_name"].IsObject() && + spec["expression_name"].HasMember("values") && + spec["expression_name"]["values"].IsObject()); + + const auto& allExpressions = spec["expression_name"]["values"]; + + for(auto& entry : allExpressions.GetObject()) { + const std::string name { entry.name.GetString(), entry.name.GetStringLength() }; + JSDocument document; + document.Parse<0>(R"([")" + name + R"("])"); + + const JSValue* expression = &document; + EXPECT_TRUE(expression::isExpression(conversion::Convertible(expression))) << name; + } +} + +class ExpressionEqualityTest : public ::testing::TestWithParam<std::string> {}; + +TEST_P(ExpressionEqualityTest, ExpressionEquality) { + const std::string base = std::string("test/fixtures/expression_equality/") + GetParam(); + + std::string error; + auto parse = [&](std::string filename, std::string& error_) -> std::unique_ptr<expression::Expression> { + rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> document; + document.Parse<0>(util::read_file(filename).c_str()); + assert(!document.HasParseError()); + const JSValue* expression = &document; + expression::ParsingContext ctx; + expression::ParseResult parsed = ctx.parse(conversion::Convertible(expression)); + if (!parsed) { + error_ = ctx.getErrors().size() > 0 ? ctx.getErrors()[0].message : "failed to parse"; + }; + return std::move(*parsed); + }; + + std::unique_ptr<expression::Expression> expression_a1 = parse(base + ".a.json", error); + ASSERT_TRUE(expression_a1) << GetParam() << ": " << error; + + std::unique_ptr<expression::Expression> expression_a2 = parse(base + ".a.json", error); + ASSERT_TRUE(expression_a2) << GetParam() << ": " << error; + + std::unique_ptr<expression::Expression> expression_b = parse(base + ".b.json", error); + ASSERT_TRUE(expression_b) << GetParam() << ": " << error; + + + EXPECT_TRUE(*expression_a1 == *expression_a2); + EXPECT_TRUE(*expression_a1 != *expression_b); +} + +INSTANTIATE_TEST_CASE_P(Expression, ExpressionEqualityTest, ::testing::ValuesIn([] { + std::vector<std::string> names; + const std::string ending = ".a.json"; + + const std::string style_directory = "test/fixtures/expression_equality"; + DIR *dir = opendir(style_directory.c_str()); + if (dir != nullptr) { + for (dirent *dp = nullptr; (dp = readdir(dir)) != nullptr;) { + const std::string name = dp->d_name; + if (name.length() >= ending.length() && name.compare(name.length() - ending.length(), ending.length(), ending) == 0) { + names.push_back(name.substr(0, name.length() - ending.length())); + } + } + closedir(dir); + } + + EXPECT_GT(names.size(), 0u); + return names; +}())); diff --git a/test/style/expression/util.test.cpp b/test/style/expression/util.test.cpp new file mode 100644 index 0000000000..0337cd871f --- /dev/null +++ b/test/style/expression/util.test.cpp @@ -0,0 +1,23 @@ +#include <mbgl/test/util.hpp> +#include <mbgl/style/expression/util.hpp> + +using namespace mbgl; +using namespace mbgl::style::expression; + +TEST(Expression, Util_rgba) { + Result<Color> valid = rgba(0, 0, 0, 0); + ASSERT_TRUE(valid); + ASSERT_EQ(valid->r, 0); + ASSERT_EQ(valid->g, 0); + ASSERT_EQ(valid->b, 0); + ASSERT_EQ(valid->a, 0); + + ASSERT_FALSE(rgba(0, 0, 0, -0.1)); + ASSERT_FALSE(rgba(0, 0, 0, 1.1)); + ASSERT_FALSE(rgba(0, 0, -1, 1)); + ASSERT_FALSE(rgba(0, 0, 256, 1)); + ASSERT_FALSE(rgba(0, -1, 0, 1)); + ASSERT_FALSE(rgba(0, 256, 0, 1)); + ASSERT_FALSE(rgba(-1, 1, 0, 1)); + ASSERT_FALSE(rgba(-256, 1, 0, 1)); +} diff --git a/test/style/filter.test.cpp b/test/style/filter.test.cpp index 96de125945..73f8e7626d 100644 --- a/test/style/filter.test.cpp +++ b/test/style/filter.test.cpp @@ -4,20 +4,15 @@ #include <mbgl/style/filter.hpp> #include <mbgl/style/filter_evaluator.hpp> -#include <mbgl/style/rapidjson_conversion.hpp> -#include <mbgl/style/conversion.hpp> +#include <mbgl/style/conversion/json.hpp> #include <mbgl/style/conversion/filter.hpp> -#include <rapidjson/document.h> - using namespace mbgl; using namespace mbgl::style; Filter parse(const char * expression) { - rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc; - doc.Parse<0>(expression); conversion::Error error; - optional<Filter> filter = conversion::convert<Filter, JSValue>(doc, error); + optional<Filter> filter = conversion::convertJSON<Filter>(expression, error); EXPECT_TRUE(bool(filter)); return *filter; } diff --git a/test/style/source.test.cpp b/test/style/source.test.cpp index 919260ffe9..6cb01146f6 100644 --- a/test/style/source.test.cpp +++ b/test/style/source.test.cpp @@ -6,13 +6,17 @@ #include <mbgl/style/style.hpp> #include <mbgl/style/source_impl.hpp> #include <mbgl/style/sources/raster_source.hpp> +#include <mbgl/style/sources/raster_dem_source.hpp> #include <mbgl/style/sources/vector_source.hpp> #include <mbgl/style/sources/geojson_source.hpp> #include <mbgl/style/sources/image_source.hpp> +#include <mbgl/style/sources/custom_geometry_source.hpp> +#include <mbgl/style/layers/hillshade_layer.cpp> #include <mbgl/style/layers/raster_layer.cpp> #include <mbgl/style/layers/line_layer.hpp> #include <mbgl/renderer/sources/render_raster_source.hpp> +#include <mbgl/renderer/sources/render_raster_dem_source.hpp> #include <mbgl/renderer/sources/render_vector_source.hpp> #include <mbgl/renderer/sources/render_geojson_source.hpp> #include <mbgl/renderer/tile_parameters.hpp> @@ -24,7 +28,7 @@ #include <mbgl/util/image.hpp> #include <mbgl/util/tileset.hpp> -#include <mbgl/util/default_thread_pool.hpp> +#include <mbgl/util/shared_thread_pool.hpp> #include <mbgl/util/logging.hpp> #include <mbgl/util/optional.hpp> #include <mbgl/util/range.hpp> @@ -38,6 +42,7 @@ #include <cstdint> using namespace mbgl; +using SourceType = mbgl::style::SourceType; class SourceTest { public: @@ -171,6 +176,44 @@ TEST(Source, RasterTileEmpty) { test.run(); } +TEST(Source, RasterDEMTileEmpty) { + SourceTest test; + + test.fileSource.tileResponse = [&] (const Resource&) { + Response response; + response.noContent = true; + return response; + }; + + HillshadeLayer layer("id", "source"); + std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }}; + + Tileset tileset; + tileset.tiles = { "tiles" }; + + RasterDEMSource source("source", tileset, 512); + source.loadDescription(test.fileSource); + + test.renderSourceObserver.tileChanged = [&] (RenderSource& source_, const OverscaledTileID&) { + EXPECT_EQ("source", source_.baseImpl->id); + test.end(); + }; + + test.renderSourceObserver.tileError = [&] (RenderSource&, const OverscaledTileID&, std::exception_ptr) { + FAIL() << "Should never be called"; + }; + + auto renderSource = RenderSource::create(source.baseImpl); + renderSource->setObserver(&test.renderSourceObserver); + renderSource->update(source.baseImpl, + layers, + true, + true, + test.tileParameters); + + test.run(); +} + TEST(Source, VectorTileEmpty) { SourceTest test; @@ -249,6 +292,44 @@ TEST(Source, RasterTileFail) { test.run(); } +TEST(Source, RasterDEMTileFail) { + SourceTest test; + + test.fileSource.tileResponse = [&] (const Resource&) { + Response response; + response.error = std::make_unique<Response::Error>( + Response::Error::Reason::Other, + "Failed by the test case"); + return response; + }; + + HillshadeLayer layer("id", "source"); + std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }}; + + Tileset tileset; + tileset.tiles = { "tiles" }; + + RasterDEMSource source("source", tileset, 512); + source.loadDescription(test.fileSource); + + test.renderSourceObserver.tileError = [&] (RenderSource& source_, const OverscaledTileID& tileID, std::exception_ptr error) { + EXPECT_EQ(SourceType::RasterDEM, source_.baseImpl->type); + EXPECT_EQ(OverscaledTileID(0, 0, 0), tileID); + EXPECT_EQ("Failed by the test case", util::toString(error)); + test.end(); + }; + + auto renderSource = RenderSource::create(source.baseImpl); + renderSource->setObserver(&test.renderSourceObserver); + renderSource->update(source.baseImpl, + layers, + true, + true, + test.tileParameters); + + test.run(); +} + TEST(Source, VectorTileFail) { SourceTest test; @@ -326,6 +407,43 @@ TEST(Source, RasterTileCorrupt) { test.run(); } +TEST(Source, RasterDEMTileCorrupt) { + SourceTest test; + + test.fileSource.tileResponse = [&] (const Resource&) { + Response response; + response.data = std::make_unique<std::string>("CORRUPTED"); + return response; + }; + + HillshadeLayer layer("id", "source"); + std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }}; + + Tileset tileset; + tileset.tiles = { "tiles" }; + + RasterDEMSource source("source", tileset, 512); + source.loadDescription(test.fileSource); + + test.renderSourceObserver.tileError = [&] (RenderSource& source_, const OverscaledTileID& tileID, std::exception_ptr error) { + EXPECT_EQ(source_.baseImpl->type, SourceType::RasterDEM); + EXPECT_EQ(OverscaledTileID(0, 0, 0), tileID); + EXPECT_TRUE(bool(error)); + // Not asserting on platform-specific error text. + test.end(); + }; + + auto renderSource = RenderSource::create(source.baseImpl); + renderSource->setObserver(&test.renderSourceObserver); + renderSource->update(source.baseImpl, + layers, + true, + true, + test.tileParameters); + + test.run(); +} + TEST(Source, VectorTileCorrupt) { SourceTest test; @@ -400,6 +518,42 @@ TEST(Source, RasterTileCancel) { test.run(); } +TEST(Source, RasterDEMTileCancel) { + SourceTest test; + + test.fileSource.tileResponse = [&] (const Resource&) { + test.end(); + return optional<Response>(); + }; + + HillshadeLayer layer("id", "source"); + std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }}; + + Tileset tileset; + tileset.tiles = { "tiles" }; + + RasterDEMSource source("source", tileset, 512); + source.loadDescription(test.fileSource); + + test.renderSourceObserver.tileChanged = [&] (RenderSource&, const OverscaledTileID&) { + FAIL() << "Should never be called"; + }; + + test.renderSourceObserver.tileError = [&] (RenderSource&, const OverscaledTileID&, std::exception_ptr) { + FAIL() << "Should never be called"; + }; + + auto renderSource = RenderSource::create(source.baseImpl); + renderSource->setObserver(&test.renderSourceObserver); + renderSource->update(source.baseImpl, + layers, + true, + true, + test.tileParameters); + + test.run(); +} + TEST(Source, VectorTileCancel) { SourceTest test; @@ -482,6 +636,48 @@ TEST(Source, RasterTileAttribution) { test.run(); } +TEST(Source, RasterDEMTileAttribution) { + SourceTest test; + + HillshadeLayer layer("id", "source"); + std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }}; + + std::string mapbox = ("<a href='https://www.mapbox.com/about/maps/' target='_blank'>© Mapbox</a> "); + + test.fileSource.tileResponse = [&] (const Resource&) { + Response response; + response.noContent = true; + return response; + }; + + test.fileSource.sourceResponse = [&] (const Resource& resource) { + EXPECT_EQ("url", resource.url); + Response response; + response.data = std::make_unique<std::string>(R"TILEJSON({ "tilejson": "2.1.0", "attribution": ")TILEJSON" + + mapbox + + R"TILEJSON(", "tiles": [ "tiles" ] })TILEJSON"); + return response; + }; + + test.styleObserver.sourceChanged = [&] (Source& source) { + EXPECT_EQ(mapbox, source.getAttribution()); + test.end(); + }; + + RasterDEMSource source("source", "url", 512); + source.setObserver(&test.styleObserver); + source.loadDescription(test.fileSource); + + auto renderSource = RenderSource::create(source.baseImpl); + renderSource->update(source.baseImpl, + layers, + true, + true, + test.tileParameters); + + test.run(); +} + TEST(Source, GeoJSonSourceUrlUpdate) { SourceTest test; @@ -547,3 +743,39 @@ TEST(Source, ImageSourceImageUpdate) { test.run(); } + +TEST(Source, CustomGeometrySourceSetTileData) { + SourceTest test; + std::shared_ptr<ThreadPool> threadPool = sharedThreadPool(); + CustomGeometrySource source("source", CustomGeometrySource::Options()); + source.loadDescription(test.fileSource); + + LineLayer layer("id", "source"); + layer.setSourceLayer("water"); + std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }}; + + test.renderSourceObserver.tileChanged = [&] (RenderSource& source_, const OverscaledTileID&) { + EXPECT_EQ("source", source_.baseImpl->id); + test.end(); + }; + + test.renderSourceObserver.tileError = [&] (RenderSource&, const OverscaledTileID&, std::exception_ptr) { + FAIL() << "Should never be called"; + }; + + auto renderSource = RenderSource::create(source.baseImpl); + renderSource->setObserver(&test.renderSourceObserver); + renderSource->update(source.baseImpl, + layers, + true, + true, + test.tileParameters); + + test.loop.invoke([&] () { + // Set Tile Data + source.setTileData(CanonicalTileID(0, 0, 0), GeoJSON{ FeatureCollection{} }); + }); + + test.run(); +} + diff --git a/test/style/style_parser.test.cpp b/test/style/style_parser.test.cpp index 5fa81b47e9..43b982c3b9 100644 --- a/test/style/style_parser.test.cpp +++ b/test/style/style_parser.test.cpp @@ -102,3 +102,99 @@ TEST(StyleParser, FontStacks) { ASSERT_EQ(FontStack({"a", "b"}), result[1]); ASSERT_EQ(FontStack({"a", "b", "c"}), result[2]); } + +TEST(StyleParser, FontStacksNoTextField) { + style::Parser parser; + parser.parse(R"({ + "version": 8, + "layers": [{ + "id": "symbol", + "type": "symbol", + "source": "vector", + "layout": { + "text-font": ["a"] + } + }] + })"); + auto result = parser.fontStacks(); + ASSERT_EQ(0u, result.size()); +} + +TEST(StyleParser, FontStacksCaseExpression) { + style::Parser parser; + parser.parse(R"({ + "version": 8, + "layers": [{ + "id": "symbol", + "type": "symbol", + "source": "vector", + "layout": { + "text-field": "a", + "text-font": ["case", ["==", "a", ["string", ["get", "text-font"]]], ["literal", ["Arial"]], ["literal", ["Helvetica"]]] + } + }] + })"); + auto result = parser.fontStacks(); + ASSERT_EQ(2u, result.size()); + ASSERT_EQ(FontStack({"Arial"}), result[0]); + ASSERT_EQ(FontStack({"Helvetica"}), result[1]); +} + +TEST(StyleParser, FontStacksMatchExpression) { + style::Parser parser; + parser.parse(R"({ + "version": 8, + "layers": [{ + "id": "symbol", + "type": "symbol", + "source": "vector", + "layout": { + "text-field": "a", + "text-font": ["match", ["get", "text-font"], "a", ["literal", ["Arial"]], ["literal", ["Helvetica"]]] + } + }] + })"); + auto result = parser.fontStacks(); + ASSERT_EQ(2u, result.size()); + ASSERT_EQ(FontStack({"Arial"}), result[0]); + ASSERT_EQ(FontStack({"Helvetica"}), result[1]); +} + +TEST(StyleParser, FontStacksStepExpression) { + style::Parser parser; + parser.parse(R"({ + "version": 8, + "layers": [{ + "id": "symbol", + "type": "symbol", + "source": "vector", + "layout": { + "text-field": "a", + "text-font": ["array", "string", ["step", ["get", "text-font"], ["literal", ["Arial"]], 0, ["literal", ["Helvetica"]]]] + } + }] + })"); + auto result = parser.fontStacks(); + ASSERT_EQ(2u, result.size()); + ASSERT_EQ(FontStack({"Arial"}), result[0]); + ASSERT_EQ(FontStack({"Helvetica"}), result[1]); +} + +TEST(StyleParser, FontStacksGetExpression) { + // Invalid style, but not currently validated. + style::Parser parser; + parser.parse(R"({ + "version": 8, + "layers": [{ + "id": "symbol", + "type": "symbol", + "source": "vector", + "layout": { + "text-field": "a", + "text-font": ["array", "string", ["get", "text-font"]] + } + }] + })"); + auto result = parser.fontStacks(); + ASSERT_EQ(0u, result.size()); +} diff --git a/test/text/cross_tile_symbol_index.test.cpp b/test/text/cross_tile_symbol_index.test.cpp new file mode 100644 index 0000000000..794c2b59a1 --- /dev/null +++ b/test/text/cross_tile_symbol_index.test.cpp @@ -0,0 +1,203 @@ +#include <mbgl/text/cross_tile_symbol_index.hpp> +#include <mbgl/renderer/buckets/symbol_bucket.hpp> +#include <mbgl/test/util.hpp> + +using namespace mbgl; + +SymbolInstance makeSymbolInstance(float x, float y, std::u16string key) { + GeometryCoordinates line; + GlyphPositionMap gpm; + const std::pair<Shaping, Shaping> shaping(Shaping{}, Shaping{}); + style::SymbolLayoutProperties::Evaluated layout_; + IndexedSubfeature subfeature(0, "", "", 0); + Anchor anchor(x, y, 0, 0); + return {anchor, line, shaping, {}, layout_, 0, 0, 0, 0, style::SymbolPlacementType::Point, {{0, 0}}, 0, 0, {{0, 0}}, gpm, subfeature, 0, key, 0 }; +} + + +TEST(CrossTileSymbolLayerIndex, addBucket) { + + uint32_t maxCrossTileID = 0; + uint32_t maxBucketInstanceId = 0; + CrossTileSymbolLayerIndex index; + + style::SymbolLayoutProperties::PossiblyEvaluated layout; + bool sdfIcons = false; + bool iconsNeedLinear = false; + bool sortFeaturesByY = false; + + OverscaledTileID mainID(6, 0, 6, 8, 8); + std::vector<SymbolInstance> mainInstances; + mainInstances.push_back(makeSymbolInstance(1000, 1000, u"Detroit")); + mainInstances.push_back(makeSymbolInstance(2000, 2000, u"Toronto")); + SymbolBucket mainBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(mainInstances) }; + mainBucket.bucketInstanceId = ++maxBucketInstanceId; + index.addBucket(mainID, mainBucket, maxCrossTileID); + + // Assigned new IDs + ASSERT_EQ(mainBucket.symbolInstances.at(0).crossTileID, 1u); + ASSERT_EQ(mainBucket.symbolInstances.at(1).crossTileID, 2u); + + + OverscaledTileID childID(7, 0, 7, 16, 16); + std::vector<SymbolInstance> childInstances; + childInstances.push_back(makeSymbolInstance(2000, 2000, u"Detroit")); + childInstances.push_back(makeSymbolInstance(2000, 2000, u"Windsor")); + childInstances.push_back(makeSymbolInstance(3000, 3000, u"Toronto")); + childInstances.push_back(makeSymbolInstance(4001, 4001, u"Toronto")); + SymbolBucket childBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(childInstances) }; + childBucket.bucketInstanceId = ++maxBucketInstanceId; + index.addBucket(childID, childBucket, maxCrossTileID); + + // matched parent tile + ASSERT_EQ(childBucket.symbolInstances.at(0).crossTileID, 1u); + // does not match because of different key + ASSERT_EQ(childBucket.symbolInstances.at(1).crossTileID, 3u); + // does not match because of different location + ASSERT_EQ(childBucket.symbolInstances.at(2).crossTileID, 4u); + // matches with a slightly different location + ASSERT_EQ(childBucket.symbolInstances.at(3).crossTileID, 2u); + + OverscaledTileID parentID(5, 0, 5, 4, 4); + std::vector<SymbolInstance> parentInstances; + parentInstances.push_back(makeSymbolInstance(500, 500, u"Detroit")); + SymbolBucket parentBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(parentInstances) }; + parentBucket.bucketInstanceId = ++maxBucketInstanceId; + index.addBucket(parentID, parentBucket, maxCrossTileID); + + // matched child tile + ASSERT_EQ(parentBucket.symbolInstances.at(0).crossTileID, 1u); + + std::unordered_set<uint32_t> currentIDs; + currentIDs.insert(mainBucket.bucketInstanceId); + index.removeStaleBuckets(currentIDs); + + // grandchild + OverscaledTileID grandchildID(8, 0, 8, 32, 32); + std::vector<SymbolInstance> grandchildInstances; + grandchildInstances.push_back(makeSymbolInstance(4000, 4000, u"Detroit")); + grandchildInstances.push_back(makeSymbolInstance(4000, 4000, u"Windsor")); + SymbolBucket grandchildBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(grandchildInstances) }; + grandchildBucket.bucketInstanceId = ++maxBucketInstanceId; + index.addBucket(grandchildID, grandchildBucket, maxCrossTileID); + + // Matches the symbol in `mainBucket` + ASSERT_EQ(grandchildBucket.symbolInstances.at(0).crossTileID, 1u); + // Does not match the previous value for Windsor because that tile was removed + ASSERT_EQ(grandchildBucket.symbolInstances.at(1).crossTileID, 5u); + +} + +TEST(CrossTileSymbolLayerIndex, resetIDs) { + + uint32_t maxCrossTileID = 0; + uint32_t maxBucketInstanceId = 0; + CrossTileSymbolLayerIndex index; + + style::SymbolLayoutProperties::PossiblyEvaluated layout; + bool sdfIcons = false; + bool iconsNeedLinear = false; + bool sortFeaturesByY = false; + + OverscaledTileID mainID(6, 0, 6, 8, 8); + std::vector<SymbolInstance> mainInstances; + mainInstances.push_back(makeSymbolInstance(1000, 1000, u"Detroit")); + SymbolBucket mainBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(mainInstances) }; + mainBucket.bucketInstanceId = ++maxBucketInstanceId; + + OverscaledTileID childID(7, 0, 7, 16, 16); + std::vector<SymbolInstance> childInstances; + childInstances.push_back(makeSymbolInstance(2000, 2000, u"Detroit")); + SymbolBucket childBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(childInstances) }; + childBucket.bucketInstanceId = ++maxBucketInstanceId; + + // assigns a new id + index.addBucket(mainID, mainBucket, maxCrossTileID); + ASSERT_EQ(mainBucket.symbolInstances.at(0).crossTileID, 1u); + + // removes the tile + std::unordered_set<uint32_t> currentIDs; + index.removeStaleBuckets(currentIDs); + + // assigns a new id + index.addBucket(childID, childBucket, maxCrossTileID); + ASSERT_EQ(childBucket.symbolInstances.at(0).crossTileID, 2u); + + // overwrites the old id to match the already-added tile + index.addBucket(mainID, mainBucket, maxCrossTileID); + ASSERT_EQ(mainBucket.symbolInstances.at(0).crossTileID, 2u); +} + +TEST(CrossTileSymbolLayerIndex, noDuplicatesWithinZoomLevel) { + uint32_t maxCrossTileID = 0; + uint32_t maxBucketInstanceId = 0; + CrossTileSymbolLayerIndex index; + + style::SymbolLayoutProperties::PossiblyEvaluated layout; + bool sdfIcons = false; + bool iconsNeedLinear = false; + bool sortFeaturesByY = false; + + OverscaledTileID mainID(6, 0, 6, 8, 8); + std::vector<SymbolInstance> mainInstances; + mainInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // A + mainInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // B + SymbolBucket mainBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(mainInstances) }; + mainBucket.bucketInstanceId = ++maxBucketInstanceId; + + OverscaledTileID childID(7, 0, 7, 16, 16); + std::vector<SymbolInstance> childInstances; + childInstances.push_back(makeSymbolInstance(2000, 2000, u"")); // A' + childInstances.push_back(makeSymbolInstance(2000, 2000, u"")); // B' + childInstances.push_back(makeSymbolInstance(2000, 2000, u"")); // C' + SymbolBucket childBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(childInstances) }; + childBucket.bucketInstanceId = ++maxBucketInstanceId; + + // assigns new ids + index.addBucket(mainID, mainBucket, maxCrossTileID); + ASSERT_EQ(mainBucket.symbolInstances.at(0).crossTileID, 1u); + ASSERT_EQ(mainBucket.symbolInstances.at(1).crossTileID, 2u); + + // copies parent ids without duplicate ids in this tile + index.addBucket(childID, childBucket, maxCrossTileID); + ASSERT_EQ(childBucket.symbolInstances.at(0).crossTileID, 1u); // A' copies from A + ASSERT_EQ(childBucket.symbolInstances.at(1).crossTileID, 2u); // B' copies from B + ASSERT_EQ(childBucket.symbolInstances.at(2).crossTileID, 3u); // C' gets new ID +} + +TEST(CrossTileSymbolLayerIndex, bucketReplacement) { + uint32_t maxCrossTileID = 0; + uint32_t maxBucketInstanceId = 0; + CrossTileSymbolLayerIndex index; + + style::SymbolLayoutProperties::PossiblyEvaluated layout; + bool sdfIcons = false; + bool iconsNeedLinear = false; + bool sortFeaturesByY = false; + + OverscaledTileID tileID(6, 0, 6, 8, 8); + std::vector<SymbolInstance> firstInstances; + firstInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // A + firstInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // B + SymbolBucket firstBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(firstInstances) }; + firstBucket.bucketInstanceId = ++maxBucketInstanceId; + + std::vector<SymbolInstance> secondInstances; + secondInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // A' + secondInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // B' + secondInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // C' + SymbolBucket secondBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(secondInstances) }; + secondBucket.bucketInstanceId = ++maxBucketInstanceId; + + // assigns new ids + index.addBucket(tileID, firstBucket, maxCrossTileID); + ASSERT_EQ(firstBucket.symbolInstances.at(0).crossTileID, 1u); + ASSERT_EQ(firstBucket.symbolInstances.at(1).crossTileID, 2u); + + // copies parent ids without duplicate ids in this tile + index.addBucket(tileID, secondBucket, maxCrossTileID); + ASSERT_EQ(secondBucket.symbolInstances.at(0).crossTileID, 1u); // A' copies from A + ASSERT_EQ(secondBucket.symbolInstances.at(1).crossTileID, 2u); // B' copies from B + ASSERT_EQ(secondBucket.symbolInstances.at(2).crossTileID, 3u); // C' gets new ID +} + diff --git a/test/text/glyph_loader.test.cpp b/test/text/glyph_loader.test.cpp deleted file mode 100644 index be197ebb46..0000000000 --- a/test/text/glyph_loader.test.cpp +++ /dev/null @@ -1,216 +0,0 @@ -#include <mbgl/test/util.hpp> -#include <mbgl/test/stub_file_source.hpp> - -#include <mbgl/text/glyph_manager.hpp> -#include <mbgl/util/run_loop.hpp> -#include <mbgl/util/string.hpp> -#include <mbgl/util/io.hpp> -#include <mbgl/util/logging.hpp> - -using namespace mbgl; - -class StubGlyphManagerObserver : public GlyphManagerObserver { -public: - void onGlyphsLoaded(const FontStack& fontStack, const GlyphRange& glyphRange) override { - if (glyphsLoaded) glyphsLoaded(fontStack, glyphRange); - } - - void onGlyphsError(const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) override { - if (glyphsError) glyphsError(fontStack, glyphRange, error); - } - - std::function<void (const FontStack&, const GlyphRange&)> glyphsLoaded; - std::function<void (const FontStack&, const GlyphRange&, std::exception_ptr)> glyphsError; -}; - -class StubGlyphRequestor : public GlyphRequestor { -public: - void onGlyphsAvailable(GlyphMap glyphs) override { - if (glyphsAvailable) glyphsAvailable(std::move(glyphs)); - } - - std::function<void (GlyphMap)> glyphsAvailable; -}; - -class GlyphManagerTest { -public: - util::RunLoop loop; - StubFileSource fileSource; - StubGlyphManagerObserver observer; - StubGlyphRequestor requestor; - GlyphManager glyphManager { fileSource }; - - void run(const std::string& url, GlyphDependencies dependencies) { - // Squelch logging. - Log::setObserver(std::make_unique<Log::NullObserver>()); - - glyphManager.setURL(url); - glyphManager.setObserver(&observer); - glyphManager.getGlyphs(requestor, std::move(dependencies)); - - loop.run(); - } - - void end() { - loop.stop(); - } -}; - -TEST(GlyphManager, LoadingSuccess) { - GlyphManagerTest test; - - test.fileSource.glyphsResponse = [&] (const Resource& resource) { - EXPECT_EQ(Resource::Kind::Glyphs, resource.kind); - Response response; - response.data = std::make_shared<std::string>(util::read_file("test/fixtures/resources/glyphs.pbf")); - return response; - }; - - test.observer.glyphsError = [&] (const FontStack&, const GlyphRange&, std::exception_ptr) { - FAIL(); - test.end(); - }; - - test.observer.glyphsLoaded = [&] (const FontStack& fontStack, const GlyphRange& range) { - ASSERT_EQ(fontStack, FontStack {{"Test Stack"}}); - ASSERT_EQ(range, GlyphRange(0, 255)); - }; - - test.requestor.glyphsAvailable = [&] (GlyphMap glyphs) { - const auto& testPositions = glyphs.at({{"Test Stack"}}); - - ASSERT_EQ(testPositions.size(), 3u); - ASSERT_EQ(testPositions.count(u'a'), 1u); - ASSERT_EQ(testPositions.count(u'å'), 1u); - ASSERT_EQ(testPositions.count(u' '), 1u); - ASSERT_TRUE(bool(testPositions.at(u' '))); - - test.end(); - }; - - test.run( - "test/fixtures/resources/glyphs.pbf", - GlyphDependencies { - {{{"Test Stack"}}, {u'a', u'å', u' '}} - }); -} - -TEST(GlyphManager, LoadingFail) { - GlyphManagerTest test; - - test.fileSource.glyphsResponse = [&] (const Resource&) { - Response response; - response.error = std::make_unique<Response::Error>( - Response::Error::Reason::Other, - "Failed by the test case"); - return response; - }; - - test.observer.glyphsError = [&] (const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) { - EXPECT_EQ(fontStack, FontStack({"Test Stack"})); - EXPECT_EQ(glyphRange, GlyphRange(0, 255)); - - EXPECT_TRUE(error != nullptr); - EXPECT_EQ(util::toString(error), "Failed by the test case"); - - test.end(); - }; - - test.requestor.glyphsAvailable = [&] (GlyphMap) { - FAIL(); - test.end(); - }; - - test.run( - "test/fixtures/resources/glyphs.pbf", - GlyphDependencies { - {{{"Test Stack"}}, {u'a', u'å'}} - }); -} - -TEST(GlyphManager, LoadingCorrupted) { - GlyphManagerTest test; - - test.fileSource.glyphsResponse = [&] (const Resource&) { - Response response; - response.data = std::make_unique<std::string>("CORRUPTED"); - return response; - }; - - test.observer.glyphsError = [&] (const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) { - EXPECT_EQ(fontStack, FontStack({"Test Stack"})); - EXPECT_EQ(glyphRange, GlyphRange(0, 255)); - - EXPECT_TRUE(error != nullptr); - EXPECT_EQ(util::toString(error), "unknown pbf field type exception"); - - test.end(); - }; - - test.requestor.glyphsAvailable = [&] (GlyphMap) { - FAIL(); - test.end(); - }; - - test.run( - "test/fixtures/resources/glyphs.pbf", - GlyphDependencies { - {{{"Test Stack"}}, {u'a', u'å'}} - }); -} - -TEST(GlyphManager, LoadingCancel) { - GlyphManagerTest test; - - test.fileSource.glyphsResponse = [&] (const Resource&) { - test.end(); - return optional<Response>(); - }; - - test.observer.glyphsLoaded = [&] (const FontStack&, const GlyphRange&) { - FAIL() << "Should never be called"; - }; - - test.run( - "test/fixtures/resources/glyphs.pbf", - GlyphDependencies { - {{{"Test Stack"}}, {u'a', u'å'}} - }); -} - -TEST(GlyphManager, LoadingInvalid) { - GlyphManagerTest test; - - test.fileSource.glyphsResponse = [&] (const Resource& resource) { - EXPECT_EQ(Resource::Kind::Glyphs, resource.kind); - Response response; - response.data = std::make_shared<std::string>(util::read_file("test/fixtures/resources/fake_glyphs-0-255.pbf")); - return response; - }; - - test.observer.glyphsError = [&] (const FontStack&, const GlyphRange&, std::exception_ptr) { - FAIL(); - test.end(); - }; - - test.observer.glyphsLoaded = [&] (const FontStack& fontStack, const GlyphRange& range) { - ASSERT_EQ(fontStack, FontStack {{"Test Stack"}}); - ASSERT_EQ(range, GlyphRange(0, 255)); - }; - - test.requestor.glyphsAvailable = [&] (GlyphMap glyphs) { - const auto& testPositions = glyphs.at({{"Test Stack"}}); - - ASSERT_EQ(testPositions.size(), 2u); - ASSERT_FALSE(bool(testPositions.at(u'A'))); - ASSERT_TRUE(bool(testPositions.at(u'E'))); - - test.end(); - }; - - test.run( - "test/fixtures/resources/glyphs.pbf", - GlyphDependencies { - {{{"Test Stack"}}, {u'A', u'E'}} - }); -} diff --git a/test/text/glyph_manager.test.cpp b/test/text/glyph_manager.test.cpp new file mode 100644 index 0000000000..a96e1b970c --- /dev/null +++ b/test/text/glyph_manager.test.cpp @@ -0,0 +1,341 @@ +#include <mbgl/test/util.hpp> +#include <mbgl/test/stub_file_source.hpp> + +#include <mbgl/text/glyph_manager.hpp> +#include <mbgl/util/run_loop.hpp> +#include <mbgl/util/string.hpp> +#include <mbgl/util/i18n.hpp> +#include <mbgl/util/io.hpp> +#include <mbgl/util/logging.hpp> + +using namespace mbgl; + +// Alpha channel rendering of '中' +static constexpr const uint8_t stubBitmap[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 95, 82, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 255, 233, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 255, 233, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 255, 227, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 22, 18, 18, 18, 18, 18, 15, 55, 255, 255, 42, 14, 18, 18, 18, 18, 18, 22, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 145, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 167, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 149, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 178, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 255, 120, 8, 15, 15, 15, 11, 51, 255, 233, 32, 11, 15, 15, 15, 8, 93, 255, 178, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 255, 100, 0, 0, 0, 0, 0, 37, 255, 233, 17, 0, 0, 0, 0, 0, 84, 255, 178, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 255, 107, 0, 0, 0, 0, 0, 37, 255, 233, 17, 0, 0, 0, 0, 0, 84, 255, 178, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 255, 107, 0, 0, 0, 0, 0, 37, 255, 233, 17, 0, 0, 0, 0, 0, 84, 255, 178, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 255, 100, 0, 0, 0, 0, 0, 32, 255, 227, 10, 0, 0, 0, 0, 0, 78, 255, 178, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 255, 170, 101, 101, 101, 101, 101, 129, 255, 255, 133, 100, 101, 101, 101, 96, 191, 255, 178, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 178, 2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 156, 255, 172, 110, 116, 116, 116, 115, 140, 255, 242, 127, 116, 116, 116, 116, 110, 163, 255, 169, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 145, 255, 108, 0, 0, 0, 0, 0, 32, 255, 227, 10, 0, 0, 0, 0, 0, 89, 255, 194, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 27, 16, 0, 0, 0, 0, 0, 37, 255, 233, 17, 0, 0, 0, 0, 0, 14, 27, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 255, 233, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 255, 233, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 255, 233, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 255, 233, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 255, 233, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 205, 167, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +// SDF transformation of '中' +static constexpr const uint8_t sdfBitmap[] = {0, 0, 0, 0, 0, 0, 0, 0, 19, 48, 76, 101, 119, 127, 127, 127, 126, 118, 100, 75, 48, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 19, 28, 31, 31, 31, 31, 59, 90, 119, 145, 158, 159, 159, 156, 144, 118, 89, 59, 31, 31, 31, 31, 28, 19, 5, 0, 0, 0, 10, 31, 48, 59, 63, 63, 63, 63, 63, 95, 127, 158, 182, 187, 186, 176, 156, 126, 94, 63, 63, 63, 63, 63, 59, 48, 31, 10, 0, 5, 31, 55, 75, 89, 94, 95, 95, 95, 95, 95, 127, 158, 181, 223, 204, 177, 157, 126, 95, 95, 95, 95, 95, 95, 89, 75, 55, 31, 10, 19, 48, 75, 100, 118, 126, 126, 126, 126, 126, 126, 127, 157, 180, 223, 204, 177, 157, 126, 126, 126, 126, 126, 126, 126, 119, 100, 75, 55, 31, 28, 59, 89, 118, 144, 156, 157, 157, 157, 157, 157, 157, 157, 180, 223, 204, 177, 156, 157, 157, 157, 157, 157, 157, 156, 144, 119, 100, 75, 48, 31, 63, 94, 126, 156, 176, 178, 178, 178, 178, 178, 178, 177, 182, 223, 223, 181, 177, 178, 178, 178, 178, 178, 178, 177, 156, 144, 118, 89, 59, 32, 64, 96, 128, 159, 193, 223, 223, 223, 223, 223, 223, 223, 223, 236, 236, 223, 223, 223, 223, 223, 223, 223, 223, 196, 176, 156, 126, 94, 63, 32, 64, 96, 128, 159, 194, 223, 223, 223, 223, 223, 223, 223, 223, 236, 226, 223, 223, 223, 223, 223, 223, 223, 224, 198, 176, 156, 126, 94, 63, 32, 64, 96, 127, 159, 187, 223, 190, 176, 177, 177, 177, 177, 182, 223, 204, 179, 177, 177, 177, 177, 176, 187, 223, 198, 176, 156, 126, 94, 63, 32, 64, 95, 127, 159, 186, 223, 188, 159, 156, 156, 156, 157, 180, 223, 204, 177, 157, 156, 156, 156, 159, 186, 223, 198, 176, 156, 126, 94, 63, 32, 64, 95, 127, 159, 186, 223, 189, 159, 127, 126, 127, 157, 180, 223, 204, 177, 157, 126, 126, 127, 159, 186, 223, 198, 176, 156, 126, 94, 63, 32, 64, 95, 127, 159, 186, 223, 189, 159, 127, 127, 127, 157, 180, 223, 204, 177, 157, 127, 127, 127, 159, 186, 223, 198, 176, 156, 126, 94, 63, 32, 64, 95, 127, 159, 186, 223, 188, 159, 159, 159, 159, 159, 179, 223, 204, 177, 159, 159, 159, 159, 159, 185, 223, 198, 176, 156, 126, 94, 63, 48, 75, 100, 127, 159, 186, 223, 197, 188, 188, 188, 188, 188, 191, 223, 223, 192, 188, 188, 188, 188, 187, 199, 224, 198, 176, 156, 126, 94, 63, 59, 89, 118, 143, 159, 188, 223, 224, 223, 223, 223, 223, 223, 223, 236, 226, 223, 223, 223, 223, 223, 223, 223, 224, 198, 176, 156, 126, 94, 63, 63, 94, 126, 156, 175, 195, 223, 197, 189, 190, 190, 190, 190, 193, 223, 206, 191, 190, 190, 190, 190, 189, 196, 223, 196, 176, 156, 126, 94, 63, 59, 89, 118, 143, 159, 193, 223, 189, 159, 159, 159, 159, 159, 179, 223, 204, 177, 159, 159, 159, 159, 159, 186, 223, 200, 176, 156, 126, 94, 63, 48, 75, 100, 126, 156, 176, 179, 177, 156, 127, 127, 127, 157, 180, 223, 204, 177, 157, 127, 127, 127, 156, 177, 179, 178, 157, 144, 118, 89, 59, 31, 59, 89, 118, 144, 156, 157, 156, 144, 119, 96, 127, 157, 180, 223, 204, 177, 157, 126, 96, 119, 144, 156, 157, 157, 144, 119, 100, 75, 48, 19, 48, 75, 100, 118, 126, 126, 126, 119, 100, 95, 127, 157, 180, 223, 204, 177, 157, 126, 95, 100, 119, 126, 126, 126, 119, 100, 76, 55, 31, 5, 31, 55, 75, 89, 94, 95, 95, 89, 75, 95, 127, 157, 180, 223, 204, 177, 157, 126, 95, 75, 89, 95, 95, 95, 90, 76, 55, 31, 10, 0, 10, 31, 48, 59, 63, 63, 63, 59, 63, 95, 127, 157, 180, 223, 204, 177, 157, 126, 95, 63, 59, 63, 63, 63, 59, 48, 31, 10, 0, 0, 0, 5, 19, 28, 31, 31, 31, 31, 63, 95, 127, 157, 180, 223, 204, 177, 157, 126, 95, 63, 31, 31, 31, 31, 28, 19, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 64, 95, 127, 159, 184, 201, 196, 176, 156, 126, 94, 63, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 60, 90, 120, 146, 159, 159, 159, 156, 144, 118, 89, 59, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 49, 76, 101, 120, 127, 128, 128, 126, 118, 100, 75, 48, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 32, 56, 76, 90, 95, 96, 96, 94, 89, 75, 55, 31, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 32, 49, 60, 64, 64, 64, 63, 59, 48, 31, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 19, 29, 32, 32, 32, 31, 28, 19, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +static constexpr const size_t stubBitmapLength = 900; + +class StubLocalGlyphRasterizer : public LocalGlyphRasterizer { +public: + bool canRasterizeGlyph(const FontStack&, GlyphID glyphID) { + return util::i18n::allowsIdeographicBreaking(glyphID); + } + + Glyph rasterizeGlyph(const FontStack&, GlyphID glyphID) { + Glyph stub; + stub.id = glyphID; + + stub.metrics.width = 24; + stub.metrics.height = 24; + stub.metrics.left = 0; + stub.metrics.top = -8; + stub.metrics.advance = 24; + + stub.bitmap = AlphaImage(Size(30, 30), stubBitmap, stubBitmapLength); + + return stub; + } +}; + +class StubGlyphManagerObserver : public GlyphManagerObserver { +public: + void onGlyphsLoaded(const FontStack& fontStack, const GlyphRange& glyphRange) override { + if (glyphsLoaded) glyphsLoaded(fontStack, glyphRange); + } + + void onGlyphsError(const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) override { + if (glyphsError) glyphsError(fontStack, glyphRange, error); + } + + std::function<void (const FontStack&, const GlyphRange&)> glyphsLoaded; + std::function<void (const FontStack&, const GlyphRange&, std::exception_ptr)> glyphsError; +}; + +class StubGlyphRequestor : public GlyphRequestor { +public: + void onGlyphsAvailable(GlyphMap glyphs) override { + if (glyphsAvailable) glyphsAvailable(std::move(glyphs)); + } + + std::function<void (GlyphMap)> glyphsAvailable; +}; + +class GlyphManagerTest { +public: + util::RunLoop loop; + StubFileSource fileSource; + StubGlyphManagerObserver observer; + StubGlyphRequestor requestor; + GlyphManager glyphManager{ fileSource, std::make_unique<StubLocalGlyphRasterizer>() }; + + void run(const std::string& url, GlyphDependencies dependencies) { + // Squelch logging. + Log::setObserver(std::make_unique<Log::NullObserver>()); + + glyphManager.setURL(url); + glyphManager.setObserver(&observer); + glyphManager.getGlyphs(requestor, std::move(dependencies)); + + loop.run(); + } + + void end() { + loop.stop(); + } +}; + +TEST(GlyphManager, LoadingSuccess) { + GlyphManagerTest test; + + test.fileSource.glyphsResponse = [&] (const Resource& resource) { + EXPECT_EQ(Resource::Kind::Glyphs, resource.kind); + Response response; + response.data = std::make_shared<std::string>(util::read_file("test/fixtures/resources/glyphs.pbf")); + return response; + }; + + test.observer.glyphsError = [&] (const FontStack&, const GlyphRange&, std::exception_ptr) { + FAIL(); + test.end(); + }; + + test.observer.glyphsLoaded = [&] (const FontStack& fontStack, const GlyphRange& range) { + ASSERT_EQ(fontStack, FontStack {{"Test Stack"}}); + ASSERT_EQ(range, GlyphRange(0, 255)); + }; + + test.requestor.glyphsAvailable = [&] (GlyphMap glyphs) { + const auto& testPositions = glyphs.at({{"Test Stack"}}); + + ASSERT_EQ(testPositions.size(), 3u); + ASSERT_EQ(testPositions.count(u'a'), 1u); + ASSERT_EQ(testPositions.count(u'å'), 1u); + ASSERT_EQ(testPositions.count(u' '), 1u); + ASSERT_TRUE(bool(testPositions.at(u' '))); + + test.end(); + }; + + test.run( + "test/fixtures/resources/glyphs.pbf", + GlyphDependencies { + {{{"Test Stack"}}, {u'a', u'å', u' '}} + }); +} + +TEST(GlyphManager, LoadingFail) { + GlyphManagerTest test; + + test.fileSource.glyphsResponse = [&] (const Resource&) { + Response response; + response.error = std::make_unique<Response::Error>( + Response::Error::Reason::Other, + "Failed by the test case"); + return response; + }; + + test.observer.glyphsError = [&] (const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) { + EXPECT_EQ(fontStack, FontStack({"Test Stack"})); + EXPECT_EQ(glyphRange, GlyphRange(0, 255)); + + EXPECT_TRUE(error != nullptr); + EXPECT_EQ(util::toString(error), "Failed by the test case"); + + test.end(); + }; + + test.requestor.glyphsAvailable = [&] (GlyphMap) { + FAIL(); + test.end(); + }; + + test.run( + "test/fixtures/resources/glyphs.pbf", + GlyphDependencies { + {{{"Test Stack"}}, {u'a', u'å'}} + }); +} + +TEST(GlyphManager, LoadingCorrupted) { + GlyphManagerTest test; + + test.fileSource.glyphsResponse = [&] (const Resource&) { + Response response; + response.data = std::make_unique<std::string>("CORRUPTED"); + return response; + }; + + test.observer.glyphsError = [&] (const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) { + EXPECT_EQ(fontStack, FontStack({"Test Stack"})); + EXPECT_EQ(glyphRange, GlyphRange(0, 255)); + + EXPECT_TRUE(error != nullptr); + EXPECT_EQ(util::toString(error), "unknown pbf field type exception"); + + test.end(); + }; + + test.requestor.glyphsAvailable = [&] (GlyphMap) { + FAIL(); + test.end(); + }; + + test.run( + "test/fixtures/resources/glyphs.pbf", + GlyphDependencies { + {{{"Test Stack"}}, {u'a', u'å'}} + }); +} + +TEST(GlyphManager, LoadingCancel) { + GlyphManagerTest test; + + test.fileSource.glyphsResponse = [&] (const Resource&) { + test.end(); + return optional<Response>(); + }; + + test.observer.glyphsLoaded = [&] (const FontStack&, const GlyphRange&) { + FAIL() << "Should never be called"; + }; + + test.run( + "test/fixtures/resources/glyphs.pbf", + GlyphDependencies { + {{{"Test Stack"}}, {u'a', u'å'}} + }); +} + +TEST(GlyphManager, LoadLocalCJKGlyph) { + GlyphManagerTest test; + int glyphResponses = 0; + + test.fileSource.glyphsResponse = [&] (const Resource&) { + glyphResponses++; + return optional<Response>(); + }; + + test.observer.glyphsLoaded = [&] (const FontStack&, const GlyphRange&) { + glyphResponses++; + }; + + test.requestor.glyphsAvailable = [&] (GlyphMap glyphs) { + EXPECT_EQ(glyphResponses, 0); // Local generation should prevent requesting any glyphs + + const auto& testPositions = glyphs.at({{"Test Stack"}}); + + ASSERT_EQ(testPositions.size(), 1u); + ASSERT_EQ(testPositions.count(u'中'), 1u); + + Immutable<Glyph> glyph = *testPositions.at(u'中'); + EXPECT_EQ(glyph->id, u'中'); + EXPECT_EQ(glyph->metrics.width, 24ul); + EXPECT_EQ(glyph->metrics.height, 24ul); + EXPECT_EQ(glyph->metrics.left, 0); + EXPECT_EQ(glyph->metrics.top, -8); + EXPECT_EQ(glyph->metrics.advance, 24ul); + EXPECT_EQ(glyph->bitmap.size, Size(30, 30)); + + size_t pixelCount = glyph->bitmap.size.width * glyph->bitmap.size.height; + for (size_t i = 0; i < pixelCount; i++) { + EXPECT_EQ(glyph->bitmap.data[i], sdfBitmap[i]); + } + + test.end(); + }; + + test.run( + "test/fixtures/resources/glyphs.pbf", + GlyphDependencies { + {{{"Test Stack"}}, {u'中'}} + }); +} + + +TEST(GlyphManager, LoadingInvalid) { + GlyphManagerTest test; + + test.fileSource.glyphsResponse = [&] (const Resource& resource) { + EXPECT_EQ(Resource::Kind::Glyphs, resource.kind); + Response response; + response.data = std::make_shared<std::string>(util::read_file("test/fixtures/resources/fake_glyphs-0-255.pbf")); + return response; + }; + + test.observer.glyphsError = [&] (const FontStack&, const GlyphRange&, std::exception_ptr) { + FAIL(); + test.end(); + }; + + test.observer.glyphsLoaded = [&] (const FontStack& fontStack, const GlyphRange& range) { + ASSERT_EQ(fontStack, FontStack {{"Test Stack"}}); + ASSERT_EQ(range, GlyphRange(0, 255)); + }; + + test.requestor.glyphsAvailable = [&] (GlyphMap glyphs) { + const auto& testPositions = glyphs.at({{"Test Stack"}}); + + ASSERT_EQ(testPositions.size(), 2u); + ASSERT_FALSE(bool(testPositions.at(u'A'))); + ASSERT_TRUE(bool(testPositions.at(u'E'))); + + test.end(); + }; + + test.run( + "test/fixtures/resources/glyphs.pbf", + GlyphDependencies { + {{{"Test Stack"}}, {u'A', u'E'}} + }); +} + +TEST(GlyphManager, ImmediateFileSource) { + class GlyphManagerTestSynchronous { + public: + util::RunLoop loop; + StubFileSource fileSource = { StubFileSource::ResponseType::Synchronous }; + StubGlyphManagerObserver observer; + StubGlyphRequestor requestor; + GlyphManager glyphManager { fileSource }; + + void run(const std::string& url, GlyphDependencies dependencies) { + // Squelch logging. + Log::setObserver(std::make_unique<Log::NullObserver>()); + + glyphManager.setURL(url); + glyphManager.setObserver(&observer); + glyphManager.getGlyphs(requestor, std::move(dependencies)); + + loop.run(); + } + + void end() { + loop.stop(); + } + }; + + GlyphManagerTestSynchronous test; + + test.fileSource.glyphsResponse = [&] (const Resource&) { + Response response; + response.data = std::make_shared<std::string>(util::read_file("test/fixtures/resources/glyphs.pbf")); + return response; + }; + + test.observer.glyphsError = [&] (const FontStack&, const GlyphRange&, std::exception_ptr) { + FAIL(); + test.end(); + }; + + test.requestor.glyphsAvailable = [&] (GlyphMap) { + test.end(); + }; + + test.run( + "test/fixtures/resources/glyphs.pbf", + GlyphDependencies { + {{{"Test Stack"}}, {u'a', u'å', u' '}} + }); +} diff --git a/test/text/local_glyph_rasterizer.test.cpp b/test/text/local_glyph_rasterizer.test.cpp new file mode 100644 index 0000000000..84c685d66f --- /dev/null +++ b/test/text/local_glyph_rasterizer.test.cpp @@ -0,0 +1,86 @@ +#include <mbgl/test/util.hpp> +#include <mbgl/test/stub_file_source.hpp> +#include <mbgl/map/map.hpp> +#include <mbgl/util/io.hpp> +#include <mbgl/util/run_loop.hpp> +#include <mbgl/util/color.hpp> +#include <mbgl/renderer/renderer.hpp> +#include <mbgl/gl/headless_frontend.hpp> +#include <mbgl/util/default_thread_pool.hpp> +#include <mbgl/style/style.hpp> + +/* + LoadLocalCJKGlyph in glyph_manager.test.cpp exercises the platform-independent + part of LocalGlyphRasterizer. This test actually exercises platform-dependent + font loading code for whatever platform it runs on. Different platforms have + different default fonts, so adding a new platform requires new "expected" + fixtures. + + At the time of writing, we don't run `mbgl-test` on iOS or Android, so the only + supported test platform is macOS. Supporting Android would require adding a new + test case (probably using the "Droid" font family). iOS should theoretically + work -- the "PingFang" font family used below is expected to be available on + all iOS devices, and we use a relatively high image diff tolerance (0.05) to + account for small changes between the many possible variants of the PingFang + family. +*/ + +using namespace mbgl; + +namespace { + +class LocalGlyphRasterizerTest { +public: + LocalGlyphRasterizerTest(const optional<std::string> fontFamily) + : frontend(pixelRatio, fileSource, threadPool, optional<std::string>(), GLContextMode::Unique, fontFamily) + { + } + + util::RunLoop loop; + StubFileSource fileSource; + ThreadPool threadPool { 4 }; + float pixelRatio { 1 }; + HeadlessFrontend frontend; + Map map { frontend, MapObserver::nullObserver(), frontend.getSize(), pixelRatio, fileSource, + threadPool, MapMode::Static}; + + void checkRendering(const char * name) { + test::checkImage(std::string("test/fixtures/local_glyphs/") + name, + frontend.render(map), 0.05, 0.1); + } +}; + +} // end namespace + +#ifdef __APPLE__ + +TEST(LocalGlyphRasterizer, PingFang) { + LocalGlyphRasterizerTest test(std::string("PingFang")); + + test.fileSource.glyphsResponse = [&] (const Resource& resource) { + EXPECT_EQ(Resource::Kind::Glyphs, resource.kind); + Response response; + response.data = std::make_shared<std::string>(util::read_file("test/fixtures/resources/glyphs.pbf")); + return response; + }; + test.map.getStyle().loadJSON(util::read_file("test/fixtures/local_glyphs/mixed.json")); + test.checkRendering("ping_fang"); +} + +#endif + +TEST(LocalGlyphRasterizer, NoLocal) { + // Expectation: without any local fonts set, and without any CJK glyphs provided, + // the output should just contain basic latin characters. + LocalGlyphRasterizerTest test({}); + + test.fileSource.glyphsResponse = [&] (const Resource& resource) { + EXPECT_EQ(Resource::Kind::Glyphs, resource.kind); + Response response; + response.data = std::make_shared<std::string>(util::read_file("test/fixtures/resources/glyphs.pbf")); + return response; + }; + test.map.getStyle().loadJSON(util::read_file("test/fixtures/local_glyphs/mixed.json")); + test.checkRendering("no_local"); +} + diff --git a/test/tile/annotation_tile.test.cpp b/test/tile/annotation_tile.test.cpp deleted file mode 100644 index 8f3f903925..0000000000 --- a/test/tile/annotation_tile.test.cpp +++ /dev/null @@ -1,95 +0,0 @@ -#include <mbgl/test/util.hpp> -#include <mbgl/test/fake_file_source.hpp> - -#include <mbgl/util/default_thread_pool.hpp> -#include <mbgl/util/run_loop.hpp> -#include <mbgl/map/transform.hpp> -#include <mbgl/renderer/tile_parameters.hpp> -#include <mbgl/renderer/query.hpp> -#include <mbgl/text/collision_tile.hpp> -#include <mbgl/geometry/feature_index.hpp> -#include <mbgl/annotation/annotation_manager.hpp> -#include <mbgl/annotation/annotation_tile.hpp> -#include <mbgl/renderer/image_manager.hpp> -#include <mbgl/text/glyph_manager.hpp> -#include <mbgl/renderer/backend_scope.hpp> -#include <mbgl/gl/headless_backend.hpp> -#include <mbgl/style/style.hpp> - -#include <memory> - -using namespace mbgl; - -class AnnotationTileTest { -public: - FakeFileSource fileSource; - TransformState transformState; - util::RunLoop loop; - ThreadPool threadPool { 1 }; - style::Style style { loop, fileSource, 1 }; - AnnotationManager annotationManager { style }; - HeadlessBackend backend; - BackendScope scope { backend }; - ImageManager imageManager; - GlyphManager glyphManager { fileSource }; - - TileParameters tileParameters { - 1.0, - MapDebugOptions(), - transformState, - threadPool, - fileSource, - MapMode::Continuous, - annotationManager, - imageManager, - glyphManager, - 0 - }; -}; - -// Don't query stale collision tile -TEST(AnnotationTile, Issue8289) { - AnnotationTileTest test; - AnnotationTile tile(OverscaledTileID(0, 0, 0), test.tileParameters); - - auto data = std::make_unique<AnnotationTileData>(); - data->addLayer("test")->addFeature(0, FeatureType::Point, GeometryCollection()); - - // Simulate layout and placement of a symbol layer. - tile.onLayout(GeometryTile::LayoutResult { - std::unordered_map<std::string, std::shared_ptr<Bucket>>(), - std::make_unique<FeatureIndex>(), - std::move(data), - }, 0); - - auto collisionTile = std::make_unique<CollisionTile>(PlacementConfig()); - - IndexedSubfeature subfeature { 0, "", "", 0 }; - CollisionFeature feature(GeometryCoordinates(), Anchor(0, 0, 0, 0), -5, 5, -5, 5, 1, 0, style::SymbolPlacementType::Point, subfeature, CollisionFeature::AlignmentType::Curved); - collisionTile->insertFeature(feature, 0, true); - collisionTile->placeFeature(feature, false, false); - - tile.onPlacement(GeometryTile::PlacementResult { - std::unordered_map<std::string, std::shared_ptr<Bucket>>(), - std::move(collisionTile), - {}, - {}, - }, 0); - - // Simulate a second layout with empty data. - tile.onLayout(GeometryTile::LayoutResult { - std::unordered_map<std::string, std::shared_ptr<Bucket>>(), - std::make_unique<FeatureIndex>(), - std::make_unique<AnnotationTileData>(), - }, 0); - - std::unordered_map<std::string, std::vector<Feature>> result; - GeometryCoordinates queryGeometry {{ Point<int16_t>(0, 0) }}; - TransformState transformState; - RenderedQueryOptions options; - - tile.queryRenderedFeatures(result, queryGeometry, transformState, {}, options); - - EXPECT_TRUE(result.empty()); -} - diff --git a/test/tile/custom_geometry_tile.test.cpp b/test/tile/custom_geometry_tile.test.cpp new file mode 100644 index 0000000000..21a3dd7953 --- /dev/null +++ b/test/tile/custom_geometry_tile.test.cpp @@ -0,0 +1,130 @@ +#include <mbgl/test/util.hpp> +#include <mbgl/test/fake_file_source.hpp> +#include <mbgl/test/stub_tile_observer.hpp> +#include <mbgl/style/sources/custom_geometry_source.hpp> +#include <mbgl/tile/custom_geometry_tile.hpp> +#include <mbgl/style/custom_tile_loader.hpp> + +#include <mbgl/util/default_thread_pool.hpp> +#include <mbgl/util/run_loop.hpp> +#include <mbgl/map/transform.hpp> +#include <mbgl/renderer/tile_parameters.hpp> +#include <mbgl/style/style.hpp> +#include <mbgl/style/layers/circle_layer.hpp> +#include <mbgl/annotation/annotation_manager.hpp> +#include <mbgl/renderer/image_manager.hpp> +#include <mbgl/text/glyph_manager.hpp> + +#include <memory> + +using namespace mbgl; +using namespace mbgl::style; + +class CustomTileTest { +public: + FakeFileSource fileSource; + TransformState transformState; + util::RunLoop loop; + ThreadPool threadPool { 1 }; + style::Style style { loop, fileSource, 1 }; + AnnotationManager annotationManager { style }; + ImageManager imageManager; + GlyphManager glyphManager { fileSource }; + + TileParameters tileParameters { + 1.0, + MapDebugOptions(), + transformState, + threadPool, + fileSource, + MapMode::Continuous, + annotationManager, + imageManager, + glyphManager, + 0 + }; +}; + +TEST(CustomGeometryTile, InvokeFetchTile) { + CustomTileTest test; + + CircleLayer layer("circle", "source"); + + mapbox::geometry::feature_collection<double> features; + features.push_back(mapbox::geometry::feature<double> { + mapbox::geometry::point<double>(0, 0) + }); + CustomTileLoader loader([&](const CanonicalTileID& tileId) { + EXPECT_EQ(tileId, CanonicalTileID(0,0,0)); + test.loop.stop(); + }, [&](const CanonicalTileID&) { + + }); + auto mb =std::make_shared<Mailbox>(*Scheduler::GetCurrent()); + ActorRef<CustomTileLoader> loaderActor(loader, mb); + + CustomGeometryTile tile(OverscaledTileID(0, 0, 0), "source", test.tileParameters, CustomGeometrySource::TileOptions(), + loaderActor); + + tile.setNecessity(TileNecessity::Required); + + test.loop.run(); +} + +TEST(CustomGeometryTile, InvokeCancelTile) { + CustomTileTest test; + + CircleLayer layer("circle", "source"); + + mapbox::geometry::feature_collection<double> features; + features.push_back(mapbox::geometry::feature<double> { + mapbox::geometry::point<double>(0, 0) + }); + + CustomTileLoader loader([&](const CanonicalTileID&) { }, [&](const CanonicalTileID& tileId) { + EXPECT_EQ(tileId, CanonicalTileID(0,0,0)); + test.loop.stop(); + }); + auto mb =std::make_shared<Mailbox>(*Scheduler::GetCurrent()); + ActorRef<CustomTileLoader> loaderActor(loader, mb); + + CustomGeometryTile tile(OverscaledTileID(0, 0, 0), "source", test.tileParameters, CustomGeometrySource::TileOptions(), + loaderActor); + + tile.setNecessity(TileNecessity::Required); + tile.setNecessity(TileNecessity::Optional); + test.loop.run(); +} + +TEST(CustomGeometryTile, InvokeTileChanged) { + CustomTileTest test; + + CircleLayer layer("circle", "source"); + + mapbox::geometry::feature_collection<double> features; + features.push_back(mapbox::geometry::feature<double> { + mapbox::geometry::point<double>(0, 0) + }); + + CustomTileLoader loader(nullptr, nullptr); + auto mb =std::make_shared<Mailbox>(*Scheduler::GetCurrent()); + ActorRef<CustomTileLoader> loaderActor(loader, mb); + + CustomGeometryTile tile(OverscaledTileID(0, 0, 0), "source", test.tileParameters, CustomGeometrySource::TileOptions(), + loaderActor); + + StubTileObserver observer; + observer.tileChanged = [&] (const Tile&) { + // Once present, the bucket should never "disappear", which would cause + // flickering. + ASSERT_NE(nullptr, tile.getBucket(*layer.baseImpl)); + }; + + tile.setLayers({{ layer.baseImpl }}); + tile.setObserver(&observer); + tile.setTileData(features); + + while (!tile.isComplete()) { + test.loop.runOnce(); + } +} diff --git a/test/tile/geojson_tile.test.cpp b/test/tile/geojson_tile.test.cpp index 953c3b8a5f..c05e04bc8d 100644 --- a/test/tile/geojson_tile.test.cpp +++ b/test/tile/geojson_tile.test.cpp @@ -66,7 +66,6 @@ TEST(GeoJSONTile, Issue7648) { tile.setLayers({{ layer.baseImpl }}); tile.setObserver(&observer); - tile.setPlacementConfig({}); while (!tile.isComplete()) { test.loop.runOnce(); @@ -93,7 +92,6 @@ TEST(GeoJSONTile, Issue9927) { GeoJSONTile tile(OverscaledTileID(0, 0, 0), "source", test.tileParameters, features); tile.setLayers({{ layer.baseImpl }}); - tile.setPlacementConfig({}); while (!tile.isComplete()) { test.loop.runOnce(); diff --git a/test/tile/raster_dem_tile.test.cpp b/test/tile/raster_dem_tile.test.cpp new file mode 100644 index 0000000000..5a6f5a8c9a --- /dev/null +++ b/test/tile/raster_dem_tile.test.cpp @@ -0,0 +1,93 @@ +#include <mbgl/test/util.hpp> +#include <mbgl/test/fake_file_source.hpp> +#include <mbgl/tile/raster_dem_tile.hpp> +#include <mbgl/tile/tile_loader_impl.hpp> + +#include <mbgl/style/style.hpp> +#include <mbgl/util/default_thread_pool.hpp> +#include <mbgl/util/run_loop.hpp> +#include <mbgl/map/transform.hpp> +#include <mbgl/annotation/annotation_manager.hpp> +#include <mbgl/renderer/tile_parameters.hpp> +#include <mbgl/renderer/buckets/hillshade_bucket.hpp> +#include <mbgl/renderer/image_manager.hpp> +#include <mbgl/text/glyph_manager.hpp> + +using namespace mbgl; + +class RasterDEMTileTest { +public: + FakeFileSource fileSource; + TransformState transformState; + util::RunLoop loop; + ThreadPool threadPool { 1 }; + style::Style style { loop, fileSource, 1 }; + AnnotationManager annotationManager { style }; + ImageManager imageManager; + GlyphManager glyphManager { fileSource }; + Tileset tileset { { "https://example.com" }, { 0, 22 }, "none" }; + + TileParameters tileParameters { + 1.0, + MapDebugOptions(), + transformState, + threadPool, + fileSource, + MapMode::Continuous, + annotationManager, + imageManager, + glyphManager, + 0 + }; +}; + +TEST(RasterDEMTile, setError) { + RasterDEMTileTest test; + RasterDEMTile tile(OverscaledTileID(0, 0, 0), test.tileParameters, test.tileset); + tile.setError(std::make_exception_ptr(std::runtime_error("test"))); + EXPECT_FALSE(tile.isRenderable()); + EXPECT_TRUE(tile.isLoaded()); + EXPECT_TRUE(tile.isComplete()); +} + +TEST(RasterDEMTile, onError) { + RasterDEMTileTest test; + RasterDEMTile tile(OverscaledTileID(0, 0, 0), test.tileParameters, test.tileset); + tile.onError(std::make_exception_ptr(std::runtime_error("test")), 0); + EXPECT_FALSE(tile.isRenderable()); + EXPECT_TRUE(tile.isLoaded()); + EXPECT_TRUE(tile.isComplete()); +} + +TEST(RasterDEMTile, onParsed) { + RasterDEMTileTest test; + RasterDEMTile tile(OverscaledTileID(0, 0, 0), test.tileParameters, test.tileset); + tile.onParsed(std::make_unique<HillshadeBucket>(PremultipliedImage({16, 16}), Tileset::DEMEncoding::Mapbox), 0); + EXPECT_TRUE(tile.isRenderable()); + EXPECT_TRUE(tile.isLoaded()); + EXPECT_TRUE(tile.isComplete()); + + // Make sure that once we've had a renderable tile and then receive erroneous data, we retain + // the previously rendered data and keep the tile renderable. + tile.setError(std::make_exception_ptr(std::runtime_error("Connection offline"))); + EXPECT_TRUE(tile.isRenderable()); + EXPECT_TRUE(tile.isLoaded()); + EXPECT_TRUE(tile.isComplete()); + + // Then simulate a parsing failure and make sure that we keep it renderable in this situation + // as well. + tile.onError(std::make_exception_ptr(std::runtime_error("Parse error")), 0); + ASSERT_TRUE(tile.isRenderable()); + EXPECT_TRUE(tile.isLoaded()); + EXPECT_TRUE(tile.isComplete()); +} + +TEST(RasterDEMTile, onParsedEmpty) { + RasterDEMTileTest test; + RasterDEMTile tile(OverscaledTileID(0, 0, 0), test.tileParameters, test.tileset); + tile.onParsed(nullptr, 0); + EXPECT_FALSE(tile.isRenderable()); + EXPECT_TRUE(tile.isLoaded()); + EXPECT_TRUE(tile.isComplete()); +} + diff --git a/test/tile/vector_tile.test.cpp b/test/tile/vector_tile.test.cpp index 7e8b659b7a..9d42f7ae74 100644 --- a/test/tile/vector_tile.test.cpp +++ b/test/tile/vector_tile.test.cpp @@ -11,7 +11,6 @@ #include <mbgl/renderer/tile_parameters.hpp> #include <mbgl/renderer/buckets/symbol_bucket.hpp> #include <mbgl/renderer/query.hpp> -#include <mbgl/text/collision_tile.hpp> #include <mbgl/geometry/feature_index.hpp> #include <mbgl/annotation/annotation_manager.hpp> #include <mbgl/renderer/image_manager.hpp> @@ -71,12 +70,13 @@ TEST(VectorTile, Issue7615) { VectorTile tile(OverscaledTileID(0, 0, 0), "source", test.tileParameters, test.tileset); style::SymbolLayer symbolLayer("symbol", "source"); + std::vector<SymbolInstance> symbolInstances; auto symbolBucket = std::make_shared<SymbolBucket>( style::SymbolLayoutProperties::PossiblyEvaluated(), std::map< std::string, std::pair<style::IconPaintProperties::PossiblyEvaluated, style::TextPaintProperties::PossiblyEvaluated>>(), - 16.0f, 1.0f, 0.0f, false, false); + 16.0f, 1.0f, 0.0f, false, false, false, std::move(symbolInstances)); // Simulate placement of a symbol layer. tile.onPlacement(GeometryTile::PlacementResult { @@ -84,7 +84,6 @@ TEST(VectorTile, Issue7615) { symbolLayer.getID(), symbolBucket }}, - nullptr, {}, {}, }, 0); diff --git a/test/util/geo.test.cpp b/test/util/geo.test.cpp index d0d01b6f88..6832ba3486 100644 --- a/test/util/geo.test.cpp +++ b/test/util/geo.test.cpp @@ -220,3 +220,128 @@ TEST(LatLngBounds, FromTileID) { ASSERT_DOUBLE_EQ(util::LATITUDE_MAX, bounds.north()); } } + +TEST(LatLngBounds, ContainsPoint) { + auto bounds = LatLngBounds::hull({50.0, -100.0},{-50.0, 100.0}); + + EXPECT_FALSE(bounds.contains(LatLng{0.0, 170.0})); + EXPECT_FALSE(bounds.contains(LatLng{0.0, -170.0})); + EXPECT_TRUE(bounds.contains(LatLng{0.0, -100.0})); + EXPECT_TRUE(bounds.contains(LatLng{-50.0, 100.0})); + EXPECT_FALSE(bounds.contains(LatLng{0.0, 365.0})); +} + +TEST(LatLngBounds, ContainsPoint_Wrapped) { + auto bounds = LatLngBounds::hull({50.0, -160.0}, {-50.0, 160.0}); + EXPECT_FALSE(bounds.contains(LatLng{0.0, 170.0})); + EXPECT_FALSE(bounds.contains(LatLng{0.0, -170.0})); + + bounds = LatLngBounds::hull({50.0, -200}, {-50.0, -160.0}); + EXPECT_FALSE(bounds.contains(LatLng{0.0, 170.0})); + EXPECT_TRUE(bounds.contains(LatLng{0.0, 170.0}, LatLng::Wrapped)); + EXPECT_TRUE(bounds.contains(LatLng{0.0, -170.0})); + EXPECT_TRUE(bounds.contains(LatLng{0.0, -170.0}, LatLng::Wrapped)); + EXPECT_FALSE(bounds.contains(LatLng{0.0, 190.0})); + EXPECT_TRUE(bounds.contains(LatLng{0.0, 190.0}, LatLng::Wrapped)); + EXPECT_FALSE(bounds.contains(LatLng{0.0, 541.0})); + EXPECT_TRUE(bounds.contains(LatLng{0.0, 541.0}, LatLng::Wrapped)); +} + +TEST(LatLngBounds, ContainsBounds) { + auto bounds = LatLngBounds::hull({ 50.0, -160.0 }, {-50.0, 160.0}); + EXPECT_TRUE(bounds.contains(bounds)); + + auto innerBounds = LatLngBounds::hull({10.0, -180.0}, {-10.0, -170.0}); + EXPECT_FALSE(bounds.contains(innerBounds)); + EXPECT_FALSE(innerBounds.contains(bounds)); + + innerBounds = LatLngBounds::hull({10, 120.0}, {-60, 125.0}); + EXPECT_FALSE(bounds.contains(innerBounds)); + EXPECT_FALSE(innerBounds.contains(bounds)); + + innerBounds = LatLngBounds::hull({10, 120.0}, {-10, 125.0}); + EXPECT_TRUE(bounds.contains(innerBounds)); + EXPECT_FALSE(innerBounds.contains(bounds)); + +} + +TEST(LatLngBounds, ContainsBounds_Wrapped) { + auto bounds = LatLngBounds::hull({50.0, -200}, {-50.0, -160.0}); + + auto inner = LatLngBounds::hull({10.0, -180.0}, {-10.0, -170.0}); + EXPECT_TRUE(bounds.contains(inner)); + EXPECT_TRUE(bounds.contains(inner, LatLng::Wrapped)); + + inner = LatLngBounds::hull({10.0, 180.0}, {-10.0, 190.0}); + EXPECT_FALSE(bounds.contains(inner)); + EXPECT_TRUE(bounds.contains(inner, LatLng::Wrapped)); + + inner = LatLngBounds::hull({10.0, 190.0}, {-10.0, 220.0}); + EXPECT_FALSE(bounds.contains(inner)); + EXPECT_FALSE(bounds.contains(inner, LatLng::Wrapped)); + + auto unwrapped = LatLngBounds::hull({10.0, 170.0}, { -10.0, -175.0}); + EXPECT_FALSE(bounds.contains(unwrapped)); + EXPECT_FALSE(bounds.contains(unwrapped, LatLng::Wrapped)); + + unwrapped = LatLngBounds::hull({10.0, 0.0} , {-10.0, -10.0}); + EXPECT_FALSE(bounds.contains(unwrapped)); + EXPECT_FALSE(bounds.contains(unwrapped, LatLng::Wrapped)); + + unwrapped = LatLngBounds::hull({10.0, -165.0}, {-10.0, -180.0}); + EXPECT_TRUE(bounds.contains(unwrapped)); + EXPECT_TRUE(bounds.contains(unwrapped, LatLng::Wrapped)); + + unwrapped = LatLngBounds::hull({10.0, 180.0}, {-10.0, 160.0}); + EXPECT_FALSE(bounds.contains(unwrapped)); + EXPECT_TRUE(bounds.contains(unwrapped, LatLng::Wrapped)); + + unwrapped = LatLngBounds::hull({10.0, 540.0}, {-10.0, 560.0}); + EXPECT_FALSE(bounds.contains(unwrapped)); + EXPECT_TRUE(bounds.contains(unwrapped, LatLng::Wrapped)); +} + +TEST(LatLngBounds, ContainsTileIDs) { + LatLngBounds bounds(CanonicalTileID(4,2,6)); + LatLngBounds innerBounds(CanonicalTileID(9,82,197)); + EXPECT_TRUE(bounds.contains(innerBounds)); + EXPECT_FALSE(bounds.contains(LatLngBounds{ CanonicalTileID(3, 1, 0) })); +} + +TEST(LatLngBounds, Intersects) { + auto bounds = LatLngBounds::hull({ 50.0, -160.0 }, { -50.0, 160.0 }); + EXPECT_TRUE(bounds.intersects(bounds)); + + auto other = LatLngBounds::hull({50.0, -160.0}, {10, 160.0}); + EXPECT_TRUE(bounds.intersects(other)); + EXPECT_TRUE(other.intersects(bounds)); +} + +TEST(LatLngBounds, Intersects_Wrapped) { + auto bounds = LatLngBounds::hull({50.0, -200.0}, {-50.0, -160.0}); + EXPECT_TRUE(bounds.intersects(bounds)); + + auto other = LatLngBounds::hull({50.0, -150.0}, {10, 160.0}); + EXPECT_FALSE(bounds.intersects(other)); + EXPECT_FALSE(other.intersects(bounds)); + EXPECT_FALSE(bounds.intersects(other, LatLng::Wrapped)); + EXPECT_FALSE(other.intersects(bounds, LatLng::Wrapped)); + + other = LatLngBounds::hull({10.0, -150.0}, {-10.0, -210.0}); + EXPECT_TRUE(bounds.intersects(other)); + EXPECT_TRUE(bounds.intersects(other, LatLng::Wrapped)); + EXPECT_TRUE(other.intersects(bounds)); + EXPECT_TRUE(other.intersects(bounds, LatLng::Wrapped)); + + other = LatLngBounds::hull({10.0, 150.0}, {-10.0, 210.0}); + EXPECT_FALSE(bounds.intersects(other)); + EXPECT_FALSE(other.intersects(bounds)); + EXPECT_TRUE(bounds.intersects(other, LatLng::Wrapped)); + EXPECT_TRUE(other.intersects(bounds, LatLng::Wrapped)); + + other = LatLngBounds::hull({10.0, 195.0}, {-10.0, 300.0}); + EXPECT_FALSE(bounds.intersects(other)); + EXPECT_FALSE(other.intersects(bounds)); + EXPECT_TRUE(bounds.intersects(other, LatLng::Wrapped)); + EXPECT_TRUE(other.intersects(bounds, LatLng::Wrapped)); +} diff --git a/test/util/grid_index.test.cpp b/test/util/grid_index.test.cpp new file mode 100644 index 0000000000..b0a4e581a3 --- /dev/null +++ b/test/util/grid_index.test.cpp @@ -0,0 +1,53 @@ +#include <mbgl/util/grid_index.hpp> +#include <mbgl/util/grid_index.cpp> + +#include <mbgl/test/util.hpp> + +using namespace mbgl; + +TEST(GridIndex, IndexesFeatures) { + GridIndex<int16_t> grid(100, 100, 10); + grid.insert(0, {{4, 10}, {6, 30}}); + grid.insert(1, {{4, 10}, {30, 12}}); + grid.insert(2, {{-10, 30}, {5, 35}}); + + EXPECT_EQ(grid.query({{4, 10}, {5, 11}}), (std::vector<int16_t>{0, 1})); + EXPECT_EQ(grid.query({{24, 10}, {25, 11}}), (std::vector<int16_t>{1})); + EXPECT_EQ(grid.query({{40, 40}, {100, 100}}), (std::vector<int16_t>{})); + EXPECT_EQ(grid.query({{-6, 0}, {3, 100}}), (std::vector<int16_t>{2})); + EXPECT_EQ(grid.query({{-1000, -1000}, {1000, 1000}}), (std::vector<int16_t>{0, 1, 2})); +} + +TEST(GridIndex, DuplicateKeys) { + GridIndex<int16_t> grid(100, 100, 10); + #define KEY 123 + grid.insert(KEY, {{3, 4}, {4, 4}}); + grid.insert(KEY, {{13, 13}, {14, 14}}); + grid.insert(KEY, {{23, 23}, {24, 24}}); + + EXPECT_EQ(grid.query({{0, 0}, {30, 30}}), (std::vector<int16_t>{KEY, KEY, KEY})); +} + +TEST(GridIndex, CircleCircle) { + GridIndex<int16_t> grid(100, 100, 10); + grid.insert(0, {{50, 50}, 10}); + grid.insert(1, {{60, 60}, 15}); + grid.insert(2, {{-10, 110}, 20}); + + EXPECT_TRUE(grid.hitTest({{55, 55}, 2})); + EXPECT_FALSE(grid.hitTest({{10, 10}, 10})); + EXPECT_TRUE(grid.hitTest({{0, 100}, 10})); + EXPECT_TRUE(grid.hitTest({{80, 60}, 10})); +} + +TEST(GridIndex, CircleBox) { + GridIndex<int16_t> grid(100, 100, 10); + grid.insert(0, {{50, 50}, 10}); + grid.insert(1, {{60, 60}, 15}); + grid.insert(2, {{-10, 110}, 20}); + + EXPECT_EQ(grid.query({{45, 45}, {55, 55}}), (std::vector<int16_t>{0, 1})); + EXPECT_EQ(grid.query({{0, 0}, {30, 30}}), (std::vector<int16_t>{})); + EXPECT_EQ(grid.query({{0, 80}, {20, 100}}), (std::vector<int16_t>{2})); +} + diff --git a/test/util/mapbox.test.cpp b/test/util/mapbox.test.cpp index cdbd85118f..301475dae4 100644 --- a/test/util/mapbox.test.cpp +++ b/test/util/mapbox.test.cpp @@ -7,6 +7,7 @@ #include <stdexcept> using namespace mbgl; +using SourceType = mbgl::style::SourceType; TEST(Mapbox, SourceURL) { EXPECT_EQ( diff --git a/test/util/memory.test.cpp b/test/util/memory.test.cpp index 54763cd9db..6befb521f0 100644 --- a/test/util/memory.test.cpp +++ b/test/util/memory.test.cpp @@ -72,7 +72,7 @@ TEST(Memory, Vector) { HeadlessFrontend frontend { { 256, 256 }, ratio, test.fileSource, test.threadPool }; Map map(frontend, MapObserver::nullObserver(), frontend.getSize(), ratio, test.fileSource, - test.threadPool, MapMode::Still); + test.threadPool, MapMode::Static); map.setZoom(16); // more map features map.getStyle().loadURL("mapbox://streets"); @@ -85,7 +85,7 @@ TEST(Memory, Raster) { HeadlessFrontend frontend { { 256, 256 }, ratio, test.fileSource, test.threadPool }; Map map(frontend, MapObserver::nullObserver(), frontend.getSize(), ratio, test.fileSource, - test.threadPool, MapMode::Still); + test.threadPool, MapMode::Static); map.getStyle().loadURL("mapbox://satellite"); frontend.render(map); @@ -122,7 +122,7 @@ TEST(Memory, Footprint) { public: FrontendAndMap(MemoryTest& test_, const char* style) : frontend(Size{ 256, 256 }, 2, test_.fileSource, test_.threadPool) - , map(frontend, MapObserver::nullObserver(), frontend.getSize(), 2, test_.fileSource, test_.threadPool, MapMode::Still) { + , map(frontend, MapObserver::nullObserver(), frontend.getSize(), 2, test_.fileSource, test_.threadPool, MapMode::Static) { map.setZoom(16); map.getStyle().loadURL(style); frontend.render(map); diff --git a/test/util/run_loop.test.cpp b/test/util/run_loop.test.cpp index 57bc613f9e..4d2c704421 100644 --- a/test/util/run_loop.test.cpp +++ b/test/util/run_loop.test.cpp @@ -50,3 +50,17 @@ TEST(RunLoop, MultipleRun) { EXPECT_TRUE(secondTimeout); } + +TEST(RunLoop, Priorities) { + std::vector<int> order; + + RunLoop loop(RunLoop::Type::New); + loop.invoke([&] { order.push_back(1); }); + loop.invoke(RunLoop::Priority::High, [&] { order.push_back(2); }); + loop.invoke([&] { order.push_back(3); }); + loop.invoke(RunLoop::Priority::High, [&] { order.push_back(4); }); + loop.invoke(RunLoop::Priority::Default, [&] { loop.stop(); }); + loop.run(); + + EXPECT_EQ((std::vector<int>{ 2, 4, 1, 3 }), order); +} diff --git a/test/util/unique_any.test.cpp b/test/util/unique_any.test.cpp new file mode 100644 index 0000000000..9357b9c0ec --- /dev/null +++ b/test/util/unique_any.test.cpp @@ -0,0 +1,186 @@ +#include <mbgl/test/util.hpp> + +#include <mbgl/util/unique_any.hpp> + +using namespace mbgl::util; + +class TestType { +public: + TestType() : i1(0), i2(1) { + str[0] = 'a'; + } + + TestType(unique_any& p) : TestType() { + p = std::unique_ptr<TestType>(this); + } + + //Detect moves + TestType(TestType&& t): i1(t.i1+1), i2(t.i2+2) { + str[0] = t.str[0]+1; + } + + int i1; + int i2; + char str[256]; +}; + +bool IsStackAllocated (const unique_any& a, const void* obj1) { + uintptr_t a_ptr = (uintptr_t)(&a); + uintptr_t obj = (uintptr_t)(obj1); + return (obj >= a_ptr && obj < a_ptr + sizeof(unique_any)); +}; + +TEST(UniqueAny, Empty) { + EXPECT_FALSE(unique_any().has_value()); + EXPECT_TRUE(unique_any().type() == typeid(void)); + EXPECT_THROW(any_cast<int>(unique_any()), bad_any_cast); +} + +TEST(UniqueAny, BasicTypes) { + unique_any i = 3; + EXPECT_TRUE(i.has_value()); + EXPECT_TRUE(i.type() == typeid(int)); + EXPECT_TRUE(IsStackAllocated(i, any_cast<int>(&i))); + + auto iValue = any_cast<int>(i); + EXPECT_TRUE(iValue == 3); + + EXPECT_TRUE(unique_any(4).has_value()); + EXPECT_TRUE(unique_any(4).type() == typeid(int)); + + unique_any f = 6.2f; + EXPECT_TRUE(f.has_value()); + EXPECT_TRUE(f.type() == typeid(float)); + EXPECT_TRUE(IsStackAllocated(f, any_cast<float>(&f))); + + const float fValue = any_cast<const float>(f); + EXPECT_TRUE(fValue == 6.2f); + + EXPECT_TRUE(unique_any(1.0f).has_value()); + EXPECT_TRUE(unique_any(1.0f).type() == typeid(float)); + + unique_any c = 'z'; + EXPECT_TRUE(c.has_value()); + EXPECT_TRUE(c.type() == typeid(char)); + EXPECT_TRUE(IsStackAllocated(c, any_cast<char>(&c))); + + EXPECT_THROW(any_cast<float>(c), bad_any_cast); + + EXPECT_TRUE(unique_any('4').has_value()); + EXPECT_TRUE(unique_any('4').type() == typeid(char)); +} + +TEST(UniqueAny, BasicTypes_Move) { + unique_any i = 3; + EXPECT_TRUE(i.has_value()); + EXPECT_TRUE(i.type() == typeid(int)); + + unique_any f = 6.2f; + EXPECT_TRUE(f.has_value()); + EXPECT_TRUE(f.type() == typeid(float)); + + f = std::move(i); + EXPECT_FALSE(i.has_value()); + EXPECT_TRUE(i.type() == typeid(void)); + + EXPECT_TRUE(f.has_value()); + EXPECT_TRUE(f.type() == typeid(int)); + +} + +TEST(UniqueAny, LargeType) { + TestType t1; + unique_any u1 = unique_any(std::move(t1)); + EXPECT_TRUE(u1.has_value()); + EXPECT_TRUE(u1.type() == typeid(TestType)); + EXPECT_FALSE(IsStackAllocated(u1, any_cast<TestType>(&u1))); + + //TestType should be moved into owning unique_any + EXPECT_EQ(any_cast<TestType>(&u1)->i1, 1); + + auto u2(std::move(u1)); + EXPECT_TRUE(u2.type() == typeid(TestType)); + EXPECT_TRUE(u1.type() == typeid(void)); + + //TestType should not be moved when owning unique_any is moved; + EXPECT_EQ(any_cast<TestType>(&u2)->i1, 1); + + //TestType should be moved out of owning unique_any + // Note: two moves are involved in returning the moved value + // First out of the unique_any, and then in the return statement + auto t2 = any_cast<TestType>(std::move(u2)); + EXPECT_EQ(t2.i1, 3); + EXPECT_TRUE(u2.type() == typeid(void)); +} + +TEST(UniqueAny, Pointer) { + auto t1 = new TestType(); + + auto u1 = unique_any(std::move(t1)); + EXPECT_TRUE(u1.has_value()); + EXPECT_TRUE(u1.type() == typeid(TestType *)); + EXPECT_TRUE(IsStackAllocated(u1, any_cast<TestType *>(&u1))); + + //Only the pointer should be moved + TestType * t2 = *any_cast<TestType *>(&u1); + EXPECT_EQ(t2->i1, 0); + + unique_any u2(4); + std::swap(u2, u1); + + EXPECT_TRUE(u1.has_value()); + EXPECT_TRUE(u1.type() == typeid(int)); + + EXPECT_TRUE(u2.has_value()); + EXPECT_TRUE(u2.type() == typeid(TestType *)); + + t2 = *any_cast<TestType *>(&u2); + EXPECT_EQ(t2->i1, 0); + delete t2; +} + + +TEST(UniqueAny, UniquePtr) { + auto t1 = std::make_unique<TestType>(); + auto u1 = unique_any(std::move(t1)); + + EXPECT_EQ(t1.get(), nullptr); + EXPECT_TRUE(u1.has_value()); + EXPECT_TRUE(u1.type() == typeid(std::unique_ptr<TestType>)); + + EXPECT_TRUE(IsStackAllocated(u1, any_cast<std::unique_ptr<TestType>>(&u1))); + + auto t2 = any_cast<std::unique_ptr<TestType> >(std::move(u1)); + EXPECT_FALSE(u1.has_value()); + + unique_any u2; + TestType * t3 = new TestType(); + u2 = std::unique_ptr<TestType>(t3); + EXPECT_TRUE(u2.has_value()); + EXPECT_TRUE(any_cast<std::unique_ptr<TestType>>(&u2)->get() == t3); +} + +TEST(UniqueAny, SharedPtr) { + + std::shared_ptr<int> shared(new int(3)); + std::weak_ptr<int> weak = shared; + unique_any u1 = 0; + + EXPECT_THROW(any_cast<float>(u1), bad_any_cast); + + EXPECT_EQ(weak.use_count(), 1); + unique_any u2 = shared; + EXPECT_EQ(weak.use_count(), 2); + + EXPECT_EQ(any_cast<std::unique_ptr<int>>(&u1), nullptr); + EXPECT_FALSE(IsStackAllocated(u1, any_cast<std::shared_ptr<TestType>>(&u1))); + + u1 = std::move(u2); + EXPECT_EQ(weak.use_count(), 2); + u2.swap(u1); + EXPECT_EQ(weak.use_count(), 2); + u2 = 0; + EXPECT_EQ(weak.use_count(), 1); + shared = nullptr; + EXPECT_EQ(weak.use_count(), 0); +} |