#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace mbgl; using namespace mbgl::style; using namespace mbgl::style::conversion; using namespace expression; using namespace expression::dsl; using namespace std::literals::string_literals; namespace { const auto color = Color{1, 0, 0, 1}; const auto opacity = 1.0f; const auto radius = 1.0f; const auto blur = 1.0f; const auto pattern = PropertyValue{"foo"}; const auto antialias = false; const auto translate = std::array{{0, 0}}; const auto translateAnchor = TranslateAnchorType::Map; const auto lineCap = LineCapType::Round; const auto lineJoin = LineJoinType::Miter; const auto miterLimit = 1.0f; const auto roundLimit = 1.0f; const auto width = 1.0f; const auto gapWidth = 1.0f; const auto offset = 1.0f; const auto dashArray = std::vector{}; const auto hueRotate = 1.0f; const auto brightness = 1.0f; const auto saturation = 1.0f; const auto contrast = 1.0f; const auto duration = 1.0f; class MockLayoutProperties : public Properties {}; class MockPaintProperties : public Properties {}; using MockOverrides = FormatSectionOverrides; mbgl::style::Filter parseFilter(const std::string& expression) { Error error; return *convertJSON(expression, error); } } // namespace TEST(Layer, BackgroundProperties) { auto layer = std::make_unique("background"); ASSERT_STREQ("background", layer->getTypeInfo()->type); // Paint properties layer->setBackgroundColor(color); EXPECT_EQ(layer->getBackgroundColor(), color); layer->setBackgroundOpacity(opacity); EXPECT_EQ(layer->getBackgroundOpacity(), opacity); layer->setBackgroundPattern(pattern); EXPECT_EQ(layer->getBackgroundPattern(), pattern); } TEST(Layer, CircleProperties) { auto layer = std::make_unique("circle", "source"); ASSERT_STREQ("circle", layer->getTypeInfo()->type); // Paint properties layer->setCircleColor(color); EXPECT_EQ(layer->getCircleColor(), color); layer->setCircleOpacity(opacity); EXPECT_EQ(layer->getCircleOpacity(), opacity); layer->setCircleRadius(radius); EXPECT_EQ(layer->getCircleRadius(), radius); layer->setCircleBlur(blur); EXPECT_EQ(layer->getCircleBlur(), blur); layer->setCircleTranslate(translate); EXPECT_EQ(layer->getCircleTranslate(), translate); layer->setCircleTranslateAnchor(translateAnchor); EXPECT_EQ(layer->getCircleTranslateAnchor(), translateAnchor); } TEST(Layer, RasterParticleCountProperties) { RasterParticleLayer layer{"particle", "raster-array"}; EXPECT_EQ(256u, layer.getParticleCount()); layer.setParticleCount(65536); EXPECT_EQ(65536u, layer.getParticleCount()); // Production-like validation also supports the explicit non-square // presets used by the Android visual tuner. The Renderer rounds its State // texture grid up and only draws the requested particle count. layer.setParticleCount(2048); EXPECT_EQ(2048u, layer.getParticleCount()); // Other values remain rejected by the Core Layer. layer.setParticleCount(1000); EXPECT_EQ(2048u, layer.getParticleCount()); layer.setParticleCount(1048577); EXPECT_EQ(2048u, layer.getParticleCount()); } TEST(Layer, RasterParticleVisualProperties) { RasterParticleLayer layer{"particle", "raster-array"}; EXPECT_FLOAT_EQ(2.0f, layer.getLineWidth()); EXPECT_FLOAT_EQ(0.018f, layer.getParticleLength()); EXPECT_FLOAT_EQ(1.0f, layer.getParticleOpacity()); EXPECT_FLOAT_EQ(1.0f, layer.getTrailOpacity()); EXPECT_FLOAT_EQ(0.92f, layer.getTrailFade()); EXPECT_TRUE(layer.isSpeedColorEnabled()); layer.setLineWidth(4.0f); layer.setParticleLength(0.030f); layer.setParticleOpacity(0.45f); layer.setTrailOpacity(0.60f); layer.setTrailFade(0.80f); layer.setSpeedColorEnabled(false); EXPECT_FLOAT_EQ(4.0f, layer.getLineWidth()); EXPECT_FLOAT_EQ(0.030f, layer.getParticleLength()); EXPECT_FLOAT_EQ(0.45f, layer.getParticleOpacity()); EXPECT_FLOAT_EQ(0.60f, layer.getTrailOpacity()); EXPECT_FLOAT_EQ(0.80f, layer.getTrailFade()); EXPECT_FALSE(layer.isSpeedColorEnabled()); // Visual properties are bounded, and cannot affect the existing Field or // simulation resource contracts through invalid values. layer.setLineWidth(100.0f); layer.setParticleLength(-1.0f); layer.setTrailFade(2.0f); EXPECT_FLOAT_EQ(8.0f, layer.getLineWidth()); EXPECT_FLOAT_EQ(0.0005f, layer.getParticleLength()); EXPECT_FLOAT_EQ(0.995f, layer.getTrailFade()); const std::array particleColor{0.2f, 0.3f, 0.4f, 0.5f}; const std::array trailColor{0.7f, 0.6f, 0.5f, 0.4f}; layer.setParticleColor(particleColor); layer.setTrailColor(trailColor); EXPECT_EQ(layer.getParticleColor(), particleColor); EXPECT_EQ(layer.getTrailColor(), trailColor); layer.setParticleColor({-1.0f, 0.0f, 0.0f, 1.0f}); EXPECT_EQ(layer.getParticleColor(), particleColor); } TEST(Layer, WeatherColorUsesIndependentFixedOpacity) { WeatherColorLayer layer{"weather-color", "raster-array"}; ASSERT_STREQ("weather-color", layer.getTypeInfo()->type); EXPECT_FLOAT_EQ(0.62f, layer.getOpacity()); layer.setOpacity(0.35f); EXPECT_FLOAT_EQ(0.35f, layer.getOpacity()); layer.setOpacity(4.0f); EXPECT_FLOAT_EQ(1.0f, layer.getOpacity()); std::vector valid{{0.0f, {0, 0, 1, 1}}, {10.0f, {0, 1, 0, 1}}, {20.0f, {1, 0, 0, 1}}}; EXPECT_TRUE(layer.setColorStops(valid)); EXPECT_EQ(layer.getColorStops(), valid); EXPECT_FALSE(layer.setColorStops({{0.0f, {0, 0, 1, 1}}})); EXPECT_FALSE(layer.setColorStops({{0.0f, {0, 0, 1, 1}}, {0.0f, {1, 0, 0, 1}}})); EXPECT_FALSE(layer.setColorStops({{0.0f, {0, 0, 1, 1}}, {10.0f, {std::numeric_limits::quiet_NaN(), 0, 0, 1}}})); EXPECT_FALSE(layer.setColorStops({{0.0f, {0, 0, 1, 1}}, {1.0f, {0, 0, 1, 1}}, {2.0f, {0, 0, 1, 1}}, {3.0f, {0, 0, 1, 1}}, {4.0f, {0, 0, 1, 1}}, {5.0f, {0, 0, 1, 1}}, {6.0f, {0, 0, 1, 1}}, {7.0f, {0, 0, 1, 1}}, {8.0f, {0, 0, 1, 1}}})); EXPECT_EQ(layer.getColorStops(), valid); } TEST(Layer, FillProperties) { auto layer = std::make_unique("fill", "source"); ASSERT_STREQ("fill", layer->getTypeInfo()->type); // Paint properties layer->setFillColor(color); EXPECT_EQ(layer->getFillColor(), color); layer->setFillOutlineColor(color); EXPECT_EQ(layer->getFillOutlineColor(), color); layer->setFillOpacity(opacity); EXPECT_EQ(layer->getFillOpacity(), opacity); layer->setFillPattern(pattern); EXPECT_EQ(layer->getFillPattern(), pattern); layer->setFillAntialias(antialias); EXPECT_EQ(layer->getFillAntialias(), antialias); layer->setFillTranslate(translate); EXPECT_EQ(layer->getFillTranslate(), translate); layer->setFillTranslateAnchor(translateAnchor); EXPECT_EQ(layer->getFillTranslateAnchor(), translateAnchor); } TEST(Layer, LineProperties) { auto layer = std::make_unique("line", "source"); ASSERT_STREQ("line", layer->getTypeInfo()->type); // Layout properties layer->setLineCap(lineCap); EXPECT_EQ(layer->getLineCap(), lineCap); layer->setLineJoin(lineJoin); EXPECT_EQ(layer->getLineJoin(), lineJoin); layer->setLineMiterLimit(miterLimit); EXPECT_EQ(layer->getLineMiterLimit(), miterLimit); layer->setLineRoundLimit(roundLimit); EXPECT_EQ(layer->getLineRoundLimit(), roundLimit); // Paint properties layer->setLineColor(color); EXPECT_EQ(layer->getLineColor(), color); layer->setLineOpacity(opacity); EXPECT_EQ(layer->getLineOpacity(), opacity); layer->setLineTranslate(translate); EXPECT_EQ(layer->getLineTranslate(), translate); layer->setLineTranslateAnchor(translateAnchor); EXPECT_EQ(layer->getLineTranslateAnchor(), translateAnchor); layer->setLineWidth(width); EXPECT_EQ(layer->getLineWidth(), width); layer->setLineGapWidth(gapWidth); EXPECT_EQ(layer->getLineGapWidth(), gapWidth); layer->setLineOffset(offset); EXPECT_EQ(layer->getLineOffset(), offset); layer->setLineBlur(blur); EXPECT_EQ(layer->getLineBlur(), blur); layer->setLineDasharray(dashArray); EXPECT_EQ(layer->getLineDasharray(), dashArray); layer->setLinePattern(pattern); EXPECT_EQ(layer->getLinePattern(), pattern); } TEST(Layer, RasterProperties) { auto layer = std::make_unique("raster", "source"); ASSERT_STREQ("raster", layer->getTypeInfo()->type); // Paint properties layer->setRasterOpacity(opacity); EXPECT_EQ(layer->getRasterOpacity(), opacity); layer->setRasterHueRotate(hueRotate); EXPECT_EQ(layer->getRasterHueRotate(), hueRotate); layer->setRasterBrightnessMin(brightness); EXPECT_EQ(layer->getRasterBrightnessMin(), brightness); layer->setRasterBrightnessMax(brightness); EXPECT_EQ(layer->getRasterBrightnessMax(), brightness); layer->setRasterSaturation(saturation); EXPECT_EQ(layer->getRasterSaturation(), saturation); layer->setRasterContrast(contrast); EXPECT_EQ(layer->getRasterContrast(), contrast); layer->setRasterFadeDuration(duration); EXPECT_EQ(layer->getRasterFadeDuration(), duration); } TEST(Layer, Observer) { auto layer = std::make_unique("line", "source"); StubLayerObserver observer; layer->setObserver(&observer); // Notifies observer on filter change. bool filterChanged = false; observer.layerChanged = [&](Layer& layer_) { EXPECT_EQ(layer.get(), &layer_); filterChanged = true; }; layer->setFilter(parseFilter(R"(["==", "foo", "bar"])")); EXPECT_TRUE(filterChanged); // Notifies observer on visibility change. bool visibilityChanged = false; observer.layerChanged = [&](Layer& layer_) { EXPECT_EQ(layer.get(), &layer_); visibilityChanged = true; }; layer->setVisibility(VisibilityType::None); EXPECT_TRUE(visibilityChanged); // Notifies observer on paint property change. bool paintPropertyChanged = false; observer.layerChanged = [&](Layer& layer_) { EXPECT_EQ(layer.get(), &layer_); paintPropertyChanged = true; }; layer->setLineColor(color); EXPECT_TRUE(paintPropertyChanged); // Notifies observer on layout property change. bool layoutPropertyChanged = false; observer.layerChanged = [&](Layer& layer_) { EXPECT_EQ(layer.get(), &layer_); layoutPropertyChanged = true; }; layer->setLineCap(lineCap); EXPECT_TRUE(layoutPropertyChanged); // Does not notify observer on no-op visibility change. visibilityChanged = false; observer.layerChanged = [&](Layer& layer_) { EXPECT_EQ(layer.get(), &layer_); visibilityChanged = true; }; layer->setVisibility(VisibilityType::None); EXPECT_FALSE(visibilityChanged); // Does not notify observer on no-op paint property change. paintPropertyChanged = false; observer.layerChanged = [&](Layer& layer_) { EXPECT_EQ(layer.get(), &layer_); paintPropertyChanged = true; }; layer->setLineColor(color); EXPECT_FALSE(paintPropertyChanged); // Does not notify observer on no-op layout property change. layoutPropertyChanged = false; observer.layerChanged = [&](Layer& layer_) { EXPECT_EQ(layer.get(), &layer_); layoutPropertyChanged = true; }; layer->setLineCap(lineCap); EXPECT_FALSE(layoutPropertyChanged); // Notifies observer on source-layer change. // This is required so that the render orchestrator re-evaluates already-loaded // tiles when a layer's source-layer is changed at runtime (e.g. after addLayer). bool sourceLayerChanged = false; observer.layerChanged = [&](Layer& layer_) { EXPECT_EQ(layer.get(), &layer_); sourceLayerChanged = true; }; layer->setSourceLayer("roads"); EXPECT_TRUE(sourceLayerChanged); // Does not notify observer on no-op source-layer change. sourceLayerChanged = false; observer.layerChanged = [&](Layer& layer_) { EXPECT_EQ(layer.get(), &layer_); sourceLayerChanged = true; }; layer->setSourceLayer("roads"); EXPECT_FALSE(sourceLayerChanged); // Notifies observer on source ID change. bool sourceIDChanged = false; observer.layerChanged = [&](Layer& layer_) { EXPECT_EQ(layer.get(), &layer_); sourceIDChanged = true; }; layer->setSourceID("other-source"); EXPECT_TRUE(sourceIDChanged); // Does not notify observer on no-op source ID change. sourceIDChanged = false; observer.layerChanged = [&](Layer& layer_) { EXPECT_EQ(layer.get(), &layer_); sourceIDChanged = true; }; layer->setSourceID("other-source"); EXPECT_FALSE(sourceIDChanged); } TEST(Layer, DuplicateLayer) { util::RunLoop loop; // Setup style auto fileSource = std::make_shared(); Style::Impl style{fileSource, 1.0, {Scheduler::GetBackground(), {}}}; style.loadJSON(util::read_file("test/fixtures/resources/style-unused-sources.json")); // Add initial layer style.addLayer(std::make_unique("line", "unusedsource")); // Try to add duplicate try { style.addLayer(std::make_unique("line", "unusedsource")); FAIL() << "Should not have been allowed to add a duplicate layer id"; } catch (const std::runtime_error& e) { // Expected ASSERT_STREQ("Layer line already exists", e.what()); } } TEST(Layer, IncompatibleLayer) { util::RunLoop loop; // Setup style auto fileSource = std::make_shared(); Style::Impl style{fileSource, 1.0, {Scheduler::GetBackground(), {}}}; style.loadJSON(util::read_file("test/fixtures/resources/style-unused-sources.json")); // Try to add duplicate try { style.addLayer(std::make_unique("raster", "unusedsource")); FAIL() << "Should not have been allowed to add an incompatible layer " "to the source"; } catch (const std::runtime_error& e) { // Expected ASSERT_STREQ("Layer 'raster' is not compatible with source 'unusedsource'", e.what()); } } namespace { template