func TestDeferEval(t *testing.T) { d := New() err := d.ParseString(`@foo: red; @bar1: @foo; @foo: blue; @bar2: @foo;`) assert.NoError(t, err) err = d.Evaluate() assert.NoError(t, err) // with deferred evaluation bar1 and bar2 reference latest @foo value assert.Equal(t, color.MustParse("blue"), d.vars.getKey(key{name: "bar1"})) assert.Equal(t, color.MustParse("blue"), d.vars.getKey(key{name: "bar2"})) d = New() err = d.ParseString(` @foo: red; #foo { line-color: @foo; line-width: 1; } @foo: blue;`) assert.NoError(t, err) err = d.Evaluate() assert.NoError(t, err) // with deferred evaluation line-color references latest @foo value r := d.MSS().LayerRules("foo") c, _ := r[0].Properties.get("line-color") assert.Equal(t, color.MustParse("blue"), c) }
func TestFmtColor(t *testing.T) { assert.Nil(t, fmtColor(color.Color{}, false)) assert.Equal(t, `"#ff0000"`, *fmtColor(color.MustParse("red"), true)) c := color.MustParse("red") c.A = 0.5 assert.Equal(t, `"#ff000080"`, *fmtColor(c, true)) }
func TestPolygonLayer(t *testing.T) { m := New(&locator) m.SetNoMapBlock(true) m.AddLayer(mml.Layer{ID: "test", SRS: "4326", Type: mml.Polygon}, []mss.Rule{ {Layer: "test", Properties: mss.NewProperties( "line-width", 1.0, "line-color", color.MustParse("red"), "line-opacity", 0.5, "line-dasharray", []mss.Value{3.0, 5.0}, "polygon-fill", color.MustParse("blue"), "polygon-opacity", 0.2, "text-size", 10.0, "text-name", []mss.Value{mss.Field("name")}, )}, }) result := m.String() assert.Contains(t, result, "WIDTH 1\n") assert.Contains(t, result, "OUTLINECOLOR \"#ff000080\"\n") assert.Regexp(t, `PATTERN\s+3\s+5\s+END`, result) assert.Contains(t, result, "COLOR \"#0000ff\"\n") assert.Contains(t, result, "OPACITY 20\n") assert.Regexp(t, `LABEL\s+ SIZE 7.4\d+`, result) assert.Regexp(t, `TEXT 'name'`, result) }
func TestScaledLineStringLayer(t *testing.T) { m := New(&locator) m.SetNoMapBlock(true) m.AddLayer(mml.Layer{ID: "test", SRS: "4326", Type: mml.LineString, ScaleFactor: 2.0}, []mss.Rule{ {Layer: "test", Properties: mss.NewProperties( "line-width", 3.0, "line-opacity", 0.2, "line-dasharray", []mss.Value{2.0, 7.0}, )}, }) m.AddLayer(mml.Layer{ID: "test", SRS: "4326", Type: mml.LineString}, []mss.Rule{ {Layer: "test", Properties: mss.NewProperties( "line-width", 1.0, "line-color", color.MustParse("red"), "line-opacity", 0.5, "line-dasharray", []mss.Value{3.0, 5.0}, )}, }) result := m.String() assert.Contains(t, result, "WIDTH 6\n") assert.Contains(t, result, "OPACITY 20\n") assert.Regexp(t, `PATTERN\s+4\s+14\s+END`, result) assert.Contains(t, result, "WIDTH 1\n") assert.Contains(t, result, "COLOR \"#ff0000\"\n") assert.Contains(t, result, "OPACITY 50\n") assert.Regexp(t, `PATTERN\s+3\s+5\s+END`, result) }
func TestEmptyStyle(t *testing.T) { // check that instance with line-width produces empty STYLE block m := New(&locator) m.SetNoMapBlock(true) m.AddLayer(mml.Layer{ID: "test", SRS: "4326", Type: mml.Polygon}, []mss.Rule{ {Layer: "test", Properties: mss.NewPropertiesInstance( "line-width", "a", 0.0, "line-color", "a", color.MustParse("red"), "line-width", "b", 1.0, "line-color", "b", color.MustParse("blue"), )}, }) result := m.String() assert.Regexp(t, `STYLE\s+END\s+STYLE\n`, result) }
func TestParseColor(t *testing.T) { var err error var d *Decoder _, err = decodeString(`@foo: #zzz;`) assert.Error(t, err) assert.Contains(t, err.Error(), "invalid hex color") _, err = decodeString(`@foo: #abcde;`) assert.Error(t, err) assert.Contains(t, err.Error(), "invalid hex color") _, err = decodeString(`@foo: #zzzzzz;`) assert.Error(t, err) assert.Contains(t, err.Error(), "invalid hex color") d, err = decodeString(`@foo: #abcdef;`) assert.NoError(t, err) assert.Equal(t, color.MustParse("#abcdef"), d.vars.getKey(key{name: "foo"})) d, err = decodeString(`@foo: #fff;`) assert.NoError(t, err) assert.Equal(t, color.RGBA{1, 1, 1, 1}, d.vars.getKey(key{name: "foo"})) d, err = decodeString(`@foo: #000;`) assert.NoError(t, err) assert.Equal(t, color.RGBA{0, 0, 0, 1}, d.vars.getKey(key{name: "foo"})) d, err = decodeString(`@foo: #00ff00;`) assert.NoError(t, err) assert.Equal(t, color.RGBA{0, 1, 0, 1}, d.vars.getKey(key{name: "foo"})) d, err = decodeString(`@foo: rgb(255, 102, 0);`) assert.NoError(t, err) assert.Equal(t, color.RGBA{1, 0.4, 0, 1}, d.vars.getKey(key{name: "foo"})) d, err = decodeString(`@foo: rgb(255, 102, 0, 50);`) assert.Error(t, err) assert.Contains(t, err.Error(), "rgb takes exactly three arguments") d, err = decodeString(`@foo: rgba(255, 102, 0, 102);`) assert.NoError(t, err) assert.Equal(t, color.RGBA{1, 0.4, 0, 0.4}, d.vars.getKey(key{name: "foo"})) d, err = decodeString(`@foo: rgb(100%, 40%, 0);`) assert.NoError(t, err) assert.Equal(t, color.RGBA{1, 0.4, 0, 1}, d.vars.getKey(key{name: "foo"})) d, err = decodeString(`@foo: rgba(100%, 102, 0, 20%);`) assert.NoError(t, err) assert.Equal(t, color.RGBA{1, 0.4, 0, 0.2}, d.vars.getKey(key{name: "foo"})) d, err = decodeString(`@foo: -mc-set-hue(#996644, red); `) assert.NoError(t, err) assert.Equal(t, "#c64444", d.vars.getKey(key{name: "foo"}).(color.RGBA).Hex()) }
func TestParseMapBlock(t *testing.T) { d, err := decodeString(` #foo {line-width: 1} Map {background-color: red} #bar {line-width: 1} `) assert.NoError(t, err) rules := allRules(d.MSS()) assertRulesEq(t, rules, []Rule{ Rule{Layer: "foo", Attachment: "", Properties: NewProperties("line-width", float64(1)), Zoom: AllZoom, order: 0}, Rule{Layer: "bar", Attachment: "", Properties: NewProperties("line-width", float64(1)), Zoom: AllZoom, order: 1}, }) assert.Equal(t, color.MustParse("red"), d.MSS().Map().getKey(key{name: "background-color"})) }
func TestLayerZoomRules(t *testing.T) { d := New() err := d.ParseString(` #foo[zoom=13] { line-color: red; line-width: 1; }`) assert.NoError(t, err) err = d.Evaluate() assert.NoError(t, err) r := d.MSS().LayerZoomRules("foo", NewZoomRange(EQ, 13)) assert.Len(t, r, 1) c, _ := r[0].Properties.get("line-color") assert.Equal(t, color.MustParse("red"), c) assert.Empty(t, d.MSS().LayerZoomRules("foo", NewZoomRange(EQ, 12))) assert.Empty(t, d.MSS().LayerZoomRules("foo", NewZoomRange(GT, 13))) assert.Empty(t, d.MSS().LayerZoomRules("foo", NewZoomRange(LT, 13))) }
func TestRecursiveDeferEval(t *testing.T) { d := New() err := d.ParseString(` @foo: red; @bar: @foo; #foo { line-color: @baz; line-width: 1; } @baz: @bar; @foo: blue;`) assert.NoError(t, err) err = d.Evaluate() assert.NoError(t, err) // with deferred evaluation line-color references latest @foo value r := d.MSS().LayerRules("foo") c, _ := r[0].Properties.get("line-color") assert.Equal(t, color.MustParse("blue"), c) }
func TestParseColor(t *testing.T) { var err error var d *Decoder _, err = decodeString(`@foo: #zzz;`) assert.Error(t, err) assert.Contains(t, err.Error(), "invalid hex color") _, err = decodeString(`@foo: #abcde;`) assert.Error(t, err) assert.Contains(t, err.Error(), "invalid hex color") _, err = decodeString(`@foo: #zzzzzz;`) assert.Error(t, err) assert.Contains(t, err.Error(), "invalid hex color") d, err = decodeString(`@foo: #abcdef;`) assert.NoError(t, err) assert.Equal(t, color.MustParse("#abcdef"), d.vars.getKey(key{name: "foo"})) d, err = decodeString(`@foo: #fff;`) assert.NoError(t, err) assert.Equal(t, color.Color{0.0, 0.0, 1.0, 1.0, false}, d.vars.getKey(key{name: "foo"})) d, err = decodeString(`@foo: #000;`) assert.NoError(t, err) assert.Equal(t, color.Color{0.0, 0.0, 0.0, 1.0, false}, d.vars.getKey(key{name: "foo"})) d, err = decodeString(`@foo: #00ff00;`) assert.NoError(t, err) assert.Equal(t, color.Color{120.0, 1.0, 0.5, 1.0, false}, d.vars.getKey(key{name: "foo"})) d, err = decodeString(`@foo: rgb(255, 102, 0);`) assert.NoError(t, err) assert.Equal(t, color.Color{24.0, 1.0, 0.5, 1.0, false}, d.vars.getKey(key{name: "foo"})) d, err = decodeString(`@foo: rgb(255, 102, 0, 50);`) assert.Error(t, err) assert.Contains(t, err.Error(), "rgb takes exactly three arguments") d, err = decodeString(`@foo: rgba(255, 102, 0, 102);`) assert.NoError(t, err) assert.Equal(t, color.Color{24.0, 1.0, 0.5, 0.4, false}, d.vars.getKey(key{name: "foo"})) d, err = decodeString(`@foo: rgb(100%, 40%, 0);`) assert.NoError(t, err) assert.Equal(t, color.Color{24.0, 1.0, 0.5, 1.0, false}, d.vars.getKey(key{name: "foo"})) d, err = decodeString(`@foo: rgba(100%, 102, 0, 20%);`) assert.NoError(t, err) assert.Equal(t, color.Color{24.0, 1.0, 0.5, 0.2, false}, d.vars.getKey(key{name: "foo"})) d, err = decodeString(`@foo: hsl(125, 55%, 25%);`) assert.NoError(t, err) assert.Equal(t, color.Color{125.0, 0.55, 0.25, 1.0, false}, d.vars.getKey(key{name: "foo"})) d, err = decodeString(`@foo: hsla(125, 0.55, 0.25, 20%);`) assert.NoError(t, err) assert.Equal(t, color.Color{125.0, 0.55, 0.25, 0.2, false}, d.vars.getKey(key{name: "foo"})) d, err = decodeString(`@foo: husl(125, 55%, 25%);`) assert.NoError(t, err) assert.Equal(t, color.Color{125.0, 0.55, 0.25, 1.0, true}, d.vars.getKey(key{name: "foo"})) d, err = decodeString(`@foo: husla(125, 0.55, 0.25, 20%);`) assert.NoError(t, err) assert.Equal(t, color.Color{125.0, 0.55, 0.25, 0.2, true}, d.vars.getKey(key{name: "foo"})) d, err = decodeString(`@foo: -mc-set-hue(#996644, red); `) assert.NoError(t, err) assert.Equal(t, "#c64545", d.vars.getKey(key{name: "foo"}).(color.Color).String()) }
func TestParseExpression(t *testing.T) { tests := []struct { expr string err string value interface{} }{ {`@foo: __echo__(1);`, "", 1.0}, {`@foo: fadeout(rgba(255, 255, 255, 255), 50%);`, "", color.Color{0.0, 0.0, 1.0, 0.5, false}}, {`@foo: fadein(rgba(255, 255, 255, 0), 50%);`, "", color.Color{0.0, 0.0, 1.0, 0.5, false}}, {`@foo: 2 + 2 * 3;`, "", float64(8)}, {`@foo: (2 + 2) * 3;`, "", float64(12)}, {`@foo: 2 + 2 * 3 - 2 * 2;`, "", float64(4)}, {`@foo: ((2 + 2) * 3 - 2) * 2;`, "", float64(20)}, {`@foo: -(-(2 + 2) * 3 - 2) * 2;`, "", float64(28)}, {`@one: "one"; @two: "two"; @foo: @one + " + " + @two;`, "", "one + two"}, {`@one: "one"; @two: [field]; @foo: @one + " + " + @two;`, "", "one + [field]"}, {`@foo: lighten(red, 10%);`, "", color.Color{0, 1, 0.6, 1, false}}, {`@foo: lighten(red, 10%, 20%);`, "function lighten takes exactly two arguments", nil}, {`@foo: lighten(122, 10%);`, "function lighten requires color as first argument", nil}, {`@foo: lighten(red, red);`, "function lighten requires number/percent as second argument", nil}, {`@foo: hue(red);`, "", 0.0}, {`@foo: hue(red, red);`, "function hue takes exactly one argument", nil}, {`@foo: hue(123);`, "function hue requires color as argument", nil}, {`@foo: mix(red, blue, 0%);`, "", color.MustParse("blue")}, {`@foo: mix(red, blue);`, "function mix takes exactly three arguments", nil}, {`@foo: mix(red, blue);`, "function mix takes exactly three arguments", nil}, {`@foo: mix(red, blue, red, blue);`, "function mix takes exactly three arguments", nil}, {`@foo: mix(123, blue, 0%);`, "function mix requires color as first and second argument", nil}, {`@foo: mix(red, 123, 0%);`, "function mix requires color as first and second argument", nil}, {`@foo: mix(red, blue, red);`, "function mix requires number/percent as third argument", nil}, {`@foo: rgb(0, 0, 0, 0);`, "rgb takes exactly three arguments", nil}, {`@foo: rgba(0, 0, 0);`, "rgba takes exactly four arguments", nil}, {`@foo: [field1] + " " + [field2] + "!";`, "", []Value{Field("[field1]"), " ", Field("[field2]"), "!"}}, {`@foo: [field1] + [field2];`, "", []Value{Field("[field1]"), Field("[field2]")}}, {`@foo: "hello " + [field2];`, "", []Value{"hello ", Field("[field2]")}}, {`@foo: red * 0.5;`, "", color.Color{0, 1.0, 0.25, 1, false}}, {`@foo: red * blue;`, "unsupported operation", nil}, } for _, tt := range tests { d, err := decodeString(tt.expr) if tt.err == "" { assert.NoError(t, err, "expr %q returnd error %q", tt.expr, err) } else if err == nil { t.Errorf("expected error %q for %q", tt.err, tt.expr) } else { assert.Contains(t, err.Error(), tt.err) } if tt.value != nil { assert.Equal(t, tt.value, d.vars.getKey(key{name: "foo"})) } } }