source: josm/trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParserTest.groovy @ 8775

Last change on this file since 8775 was 8775, checked in by simon04, 4 years ago

fix #8170 - MapCSS: add functions parent_tags(key) and join_list(sep, list)

The method parent_tags(key) returns all parent's values for key as a list ordered by a natural ordering.
The method join_list(sep, list) joins the elements of list to one string separated by sep.

  • Property svn:eol-style set to native
File size: 19.1 KB
Line 
1package org.openstreetmap.josm.gui.mappaint.mapcss
2
3import java.awt.Color
4
5import org.junit.Before
6import org.junit.Test
7import org.openstreetmap.josm.JOSMFixture
8import org.openstreetmap.josm.Main
9import org.openstreetmap.josm.data.coor.LatLon
10import org.openstreetmap.josm.data.osm.DataSet
11import org.openstreetmap.josm.data.osm.OsmUtils
12import org.openstreetmap.josm.data.osm.Way
13import org.openstreetmap.josm.gui.mappaint.Environment
14import org.openstreetmap.josm.gui.mappaint.MultiCascade
15import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.MapCSSParser
16import org.openstreetmap.josm.tools.ColorHelper
17
18class MapCSSParserTest {
19
20    protected static Environment getEnvironment(String key, String value) {
21        return new Environment(OsmUtils.createPrimitive("way " + key + "=" + value))
22    }
23
24    protected static MapCSSParser getParser(String stringToParse) {
25        return new MapCSSParser(new StringReader(stringToParse));
26    }
27
28    @Before
29    public void setUp() throws Exception {
30        JOSMFixture.createUnitTestFixture().init();
31    }
32
33    @Test
34    public void testKothicStylesheets() throws Exception {
35        new MapCSSParser(new URL("http://kothic.googlecode.com/hg/src/styles/default.mapcss").openStream(), "UTF-8")
36        new MapCSSParser(new URL("http://kothic.googlecode.com/hg/src/styles/mapink.mapcss").openStream(), "UTF-8")
37    }
38
39    @Test
40    public void testDeclarations() {
41        getParser("{ opacity: 0.5; color: rgb(1.0, 0.0, 0.0); }").declaration()
42        getParser("{ set tag=value; }").declaration() //set a tag
43        getParser("{ set tag; }").declaration() // set a tag to 'yes'
44        getParser("{ opacity: eval(\"tag('population')/100000\"); }").declaration()
45        getParser("{ set width_in_metres=eval(\"tag('lanes')*3\"); }").declaration()
46    }
47
48    @Test
49    public void testClassCondition() throws Exception {
50        def conditions = ((Selector.GeneralSelector) getParser("way[name=X].highway:closed").selector()).conds
51        assert conditions.get(0) instanceof Condition.SimpleKeyValueCondition
52        assert conditions.get(0).applies(getEnvironment("name", "X"))
53        assert conditions.get(1) instanceof Condition.ClassCondition
54        assert conditions.get(2) instanceof Condition.PseudoClassCondition
55        assert !conditions.get(2).applies(getEnvironment("name", "X"))
56    }
57
58    @Test
59    public void testPseudoClassCondition() throws Exception {
60        def c1 = ((Selector.GeneralSelector) getParser("way!:area-style").selector()).conds.get(0)
61        def c2 = ((Selector.GeneralSelector) getParser("way!:areaStyle").selector()).conds.get(0)
62        def c3 = ((Selector.GeneralSelector) getParser("way!:area_style").selector()).conds.get(0)
63        assert c1.toString() == "!:areaStyle"
64        assert c2.toString() == "!:areaStyle"
65        assert c3.toString() == "!:areaStyle"
66    }
67
68    @Test
69    public void testClassMatching() throws Exception {
70        def css = new MapCSSStyleSource("" +
71                "way[highway=footway] { set .path; color: #FF6644; width: 2; }\n" +
72                "way[highway=path]    { set path; color: brown; width: 2; }\n" +
73                "way[\"set\"=escape]  {  }\n" +
74                "way.path             { text:auto; text-color: green; text-position: line; text-offset: 5; }\n" +
75                "way!.path            { color: orange; }\n"
76        )
77        css.loadStyleSource()
78        assert css.getErrors().isEmpty()
79        def mc1 = new MultiCascade()
80        css.apply(mc1, OsmUtils.createPrimitive("way highway=path"), 1, false);
81        assert "green".equals(mc1.getCascade("default").get("text-color", null, String.class))
82        assert "brown".equals(mc1.getCascade("default").get("color", null, String.class))
83        def mc2 = new MultiCascade()
84        css.apply(mc2, OsmUtils.createPrimitive("way highway=residential"), 1, false);
85        assert "orange".equals(mc2.getCascade("default").get("color", null, String.class))
86        assert mc2.getCascade("default").get("text-color", null, String.class) == null
87        def mc3 = new MultiCascade()
88        css.apply(mc3, OsmUtils.createPrimitive("way highway=footway"), 1, false);
89        assert ColorHelper.html2color("#FF6644").equals(mc3.getCascade("default").get("color", null, Color.class))
90    }
91
92    @Test
93    public void testEqualCondition() throws Exception {
94        def condition = (Condition.SimpleKeyValueCondition) getParser("[surface=paved]").condition(Condition.Context.PRIMITIVE)
95        assert condition instanceof Condition.SimpleKeyValueCondition
96        assert "surface".equals(condition.k)
97        assert "paved".equals(condition.v)
98        assert condition.applies(getEnvironment("surface", "paved"))
99        assert !condition.applies(getEnvironment("surface", "unpaved"))
100    }
101
102    @Test
103    public void testNotEqualCondition() throws Exception {
104        def condition = (Condition.KeyValueCondition) getParser("[surface!=paved]").condition(Condition.Context.PRIMITIVE)
105        assert Condition.Op.NEQ.equals(condition.op)
106        assert !condition.applies(getEnvironment("surface", "paved"))
107        assert condition.applies(getEnvironment("surface", "unpaved"))
108    }
109
110    @Test
111    public void testRegexCondition() throws Exception {
112        def condition = (Condition.KeyValueCondition) getParser("[surface=~/paved|unpaved/]").condition(Condition.Context.PRIMITIVE)
113        assert Condition.Op.REGEX.equals(condition.op)
114        assert condition.applies(getEnvironment("surface", "unpaved"))
115        assert !condition.applies(getEnvironment("surface", "grass"))
116    }
117
118    @Test
119    public void testRegexConditionParenthesis() throws Exception {
120        def condition = (Condition.KeyValueCondition) getParser("[name =~ /^\\(foo\\)/]").condition(Condition.Context.PRIMITIVE)
121        assert condition.applies(getEnvironment("name", "(foo)"))
122        assert !condition.applies(getEnvironment("name", "foo"))
123        assert !condition.applies(getEnvironment("name", "((foo))"))
124    }
125
126    @Test
127    public void testNegatedRegexCondition() throws Exception {
128        def condition = (Condition.KeyValueCondition) getParser("[surface!~/paved|unpaved/]").condition(Condition.Context.PRIMITIVE)
129        assert Condition.Op.NREGEX.equals(condition.op)
130        assert !condition.applies(getEnvironment("surface", "unpaved"))
131        assert condition.applies(getEnvironment("surface", "grass"))
132    }
133
134    @Test
135    public void testBeginsEndsWithCondition() throws Exception {
136        def condition = (Condition.KeyValueCondition) getParser('[foo ^= bar]').condition(Condition.Context.PRIMITIVE)
137        assert Condition.Op.BEGINS_WITH.equals(condition.op)
138        assert condition.applies(getEnvironment("foo", "bar123"))
139        assert !condition.applies(getEnvironment("foo", "123bar"))
140        assert !condition.applies(getEnvironment("foo", "123bar123"))
141        condition = (Condition.KeyValueCondition) getParser('[foo $= bar]').condition(Condition.Context.PRIMITIVE)
142        assert Condition.Op.ENDS_WITH.equals(condition.op)
143        assert !condition.applies(getEnvironment("foo", "bar123"))
144        assert condition.applies(getEnvironment("foo", "123bar"))
145        assert !condition.applies(getEnvironment("foo", "123bar123"))
146    }
147
148    @Test
149    public void testOneOfCondition() throws Exception {
150        def condition = getParser('[vending~=stamps]').condition(Condition.Context.PRIMITIVE)
151        assert condition.applies(getEnvironment("vending", "stamps"))
152        assert condition.applies(getEnvironment("vending", "bar;stamps;foo"))
153        assert !condition.applies(getEnvironment("vending", "every;thing;else"))
154        assert !condition.applies(getEnvironment("vending", "or_nothing"))
155    }
156
157    @Test
158    public void testStandardKeyCondition() throws Exception {
159        def c1 = (Condition.KeyCondition) getParser("[ highway ]").condition(Condition.Context.PRIMITIVE)
160        assert Condition.KeyMatchType.EQ.equals(c1.matchType)
161        assert c1.applies(getEnvironment("highway", "unclassified"))
162        assert !c1.applies(getEnvironment("railway", "rail"))
163        def c2 = (Condition.KeyCondition) getParser("[\"/slash/\"]").condition(Condition.Context.PRIMITIVE)
164        assert Condition.KeyMatchType.EQ.equals(c2.matchType)
165        assert c2.applies(getEnvironment("/slash/", "yes"))
166        assert !c2.applies(getEnvironment("\"slash\"", "no"))
167    }
168
169    @Test
170    public void testYesNoKeyCondition() throws Exception {
171        def c1 = (Condition.KeyCondition) getParser("[oneway?]").condition(Condition.Context.PRIMITIVE)
172        def c2 = (Condition.KeyCondition) getParser("[oneway?!]").condition(Condition.Context.PRIMITIVE)
173        def c3 = (Condition.KeyCondition) getParser("[!oneway?]").condition(Condition.Context.PRIMITIVE)
174        def c4 = (Condition.KeyCondition) getParser("[!oneway?!]").condition(Condition.Context.PRIMITIVE)
175        def yes = getEnvironment("oneway", "yes")
176        def no = getEnvironment("oneway", "no")
177        def none = getEnvironment("no-oneway", "foo")
178        assert c1.applies(yes)
179        assert !c1.applies(no)
180        assert !c1.applies(none)
181        assert !c2.applies(yes)
182        assert c2.applies(no)
183        assert !c2.applies(none)
184        assert !c3.applies(yes)
185        assert c3.applies(no)
186        assert c3.applies(none)
187        assert c4.applies(yes)
188        assert !c4.applies(no)
189        assert c4.applies(none)
190    }
191
192    @Test
193    public void testRegexKeyCondition() throws Exception {
194        def c1 = (Condition.KeyCondition) getParser("[/.*:(backward|forward)\$/]").condition(Condition.Context.PRIMITIVE)
195        assert Condition.KeyMatchType.REGEX.equals(c1.matchType)
196        assert !c1.applies(getEnvironment("lanes", "3"))
197        assert c1.applies(getEnvironment("lanes:forward", "3"))
198        assert c1.applies(getEnvironment("lanes:backward", "3"))
199        assert !c1.applies(getEnvironment("lanes:foobar", "3"))
200    }
201
202    @Test
203    public void testNRegexKeyConditionSelector() throws Exception {
204        def s1 = getParser("*[sport][tourism != hotel]").selector()
205        assert s1.matches(new Environment(OsmUtils.createPrimitive("node sport=foobar")))
206        assert !s1.matches(new Environment(OsmUtils.createPrimitive("node sport=foobar tourism=hotel")))
207        def s2 = getParser("*[sport][tourism != hotel][leisure !~ /^(sports_centre|stadium|)\$/]").selector()
208        assert s2.matches(new Environment(OsmUtils.createPrimitive("node sport=foobar")))
209        assert !s2.matches(new Environment(OsmUtils.createPrimitive("node sport=foobar tourism=hotel")))
210        assert !s2.matches(new Environment(OsmUtils.createPrimitive("node sport=foobar leisure=stadium")))
211    }
212
213    @Test
214    public void testKeyKeyCondition() throws Exception {
215        def c1 = (Condition.KeyValueCondition) getParser("[foo = *bar]").condition(Condition.Context.PRIMITIVE)
216        def w1 = new Way()
217        w1.put("foo", "123")
218        w1.put("bar", "456")
219        assert !c1.applies(new Environment(w1))
220        w1.put("bar", "123")
221        assert c1.applies(new Environment(w1))
222        def c2 = (Condition.KeyValueCondition) getParser("[foo =~ */bar/]").condition(Condition.Context.PRIMITIVE)
223        def w2 = new Way(w1)
224        w2.put("bar", "[0-9]{3}")
225        assert c2.applies(new Environment(w2))
226        w2.put("bar", "[0-9]")
227        assert c2.applies(new Environment(w2))
228        w2.put("bar", "^[0-9]\$")
229        assert !c2.applies(new Environment(w2))
230    }
231
232    @Test
233    public void testTicket8568() throws Exception {
234        def sheet = new MapCSSStyleSource("" +
235                "way { width: 5; }\n" +
236                "way[keyA], way[keyB] { width: eval(prop(width)+10); }")
237        sheet.loadStyleSource()
238        def mc = new MultiCascade()
239        sheet.apply(mc, OsmUtils.createPrimitive("way foo=bar"), 20, false)
240        assert mc.getCascade(Environment.DEFAULT_LAYER).get("width") == 5
241        sheet.apply(mc, OsmUtils.createPrimitive("way keyA=true"), 20, false)
242        assert mc.getCascade(Environment.DEFAULT_LAYER).get("width") == 15
243        sheet.apply(mc, OsmUtils.createPrimitive("way keyB=true"), 20, false)
244        assert mc.getCascade(Environment.DEFAULT_LAYER).get("width") == 15
245        sheet.apply(mc, OsmUtils.createPrimitive("way keyA=true keyB=true"), 20, false)
246        assert mc.getCascade(Environment.DEFAULT_LAYER).get("width") == 15
247    }
248
249    @Test
250    public void testTicket8071() throws Exception {
251        def sheet = new MapCSSStyleSource("" +
252                "*[rcn_ref], *[name] {text: concat(tag(rcn_ref), \" \", tag(name)); }")
253        sheet.loadStyleSource()
254        def mc = new MultiCascade()
255        sheet.apply(mc, OsmUtils.createPrimitive("way name=Foo"), 20, false)
256        assert mc.getCascade(Environment.DEFAULT_LAYER).get("text") == " Foo"
257        sheet.apply(mc, OsmUtils.createPrimitive("way rcn_ref=15"), 20, false)
258        assert mc.getCascade(Environment.DEFAULT_LAYER).get("text") == "15 "
259        sheet.apply(mc, OsmUtils.createPrimitive("way rcn_ref=15 name=Foo"), 20, false)
260        assert mc.getCascade(Environment.DEFAULT_LAYER).get("text") == "15 Foo"
261
262        sheet = new MapCSSStyleSource("" +
263                "*[rcn_ref], *[name] {text: join(\" - \", tag(rcn_ref), tag(ref), tag(name)); }")
264        sheet.loadStyleSource()
265        sheet.apply(mc, OsmUtils.createPrimitive("way rcn_ref=15 ref=1.5 name=Foo"), 20, false)
266        assert mc.getCascade(Environment.DEFAULT_LAYER).get("text") == "15 - 1.5 - Foo"
267    }
268
269    @Test
270    public void testColorNameTicket9191() throws Exception {
271        def e = new Environment(null, new MultiCascade(), Environment.DEFAULT_LAYER, null)
272        getParser("{color: testcolour1#88DD22}").declaration().instructions.get(0).execute(e)
273        def expected = new Color(0x88DD22)
274        assert e.getCascade(Environment.DEFAULT_LAYER).get("color") == expected
275        assert Main.pref.getDefaultColor("mappaint.mapcss.testcolour1") == expected
276    }
277
278    @Test
279    public void testColorNameTicket9191Alpha() throws Exception {
280        def e = new Environment(null, new MultiCascade(), Environment.DEFAULT_LAYER, null)
281        getParser("{color: testcolour2#12345678}").declaration().instructions.get(0).execute(e)
282        def expected = new Color(0x12, 0x34, 0x56, 0x78)
283        assert e.getCascade(Environment.DEFAULT_LAYER).get("color") == expected
284        assert Main.pref.getDefaultColor("mappaint.mapcss.testcolour2") == expected
285    }
286
287    @Test
288    public void testColorParsing() throws Exception {
289        assert ColorHelper.html2color("#12345678") == new Color(0x12, 0x34, 0x56, 0x78)
290    }
291
292    @Test
293    public void testChildSelectorGreaterThanSignIsOptional() throws Exception {
294        assert getParser("relation[type=route] way[highway]").child_selector().toString() ==
295                getParser("relation[type=route] > way[highway]").child_selector().toString()
296    }
297
298    @Test
299    public void testSiblingSelector() throws Exception {
300        def s1 = (Selector.ChildOrParentSelector) getParser("*[a?][parent_tag(\"highway\")=\"unclassified\"] + *[b?]").child_selector()
301        def ds = new DataSet()
302        def n1 = new org.openstreetmap.josm.data.osm.Node(new LatLon(1, 2))
303        n1.put("a", "true")
304        def n2 = new org.openstreetmap.josm.data.osm.Node(new LatLon(1.1, 2.2))
305        n2.put("b", "true")
306        def w = new Way()
307        w.put("highway", "unclassified")
308        ds.addPrimitive(n1)
309        ds.addPrimitive(n2)
310        ds.addPrimitive(w)
311        w.addNode(n1)
312        w.addNode(n2)
313
314        def e = new Environment(n2)
315        assert s1.matches(e)
316        assert e.osm == n2
317        assert e.child == n1
318        assert e.parent == w
319        assert !s1.matches(new Environment(n1))
320        assert !s1.matches(new Environment(w))
321    }
322
323    @Test
324    public void testParentTags() throws Exception {
325        def ds = new DataSet()
326        def n = new org.openstreetmap.josm.data.osm.Node(new LatLon(1, 2))
327        n.put("foo", "bar")
328        def w1 = new Way()
329        w1.put("ref", "x10")
330        def w2 = new Way()
331        w2.put("ref", "x2")
332        def w3 = new Way()
333        ds.addPrimitive(n)
334        ds.addPrimitive(w1)
335        ds.addPrimitive(w2)
336        ds.addPrimitive(w3)
337        w1.addNode(n)
338        w2.addNode(n)
339        w3.addNode(n)
340
341        MapCSSStyleSource source = new MapCSSStyleSource("node[foo=bar] {refs: join_list(\";\", parent_tags(\"ref\"));}")
342        source.loadStyleSource()
343        assert source.rules.size() == 1
344        def e = new Environment(n, new MultiCascade(), Environment.DEFAULT_LAYER, null)
345        assert source.rules.get(0).selector.matches(e)
346        source.rules.get(0).declaration.execute(e)
347        assert e.getCascade(Environment.DEFAULT_LAYER).get("refs", null, String.class) == "x2;x10"
348    }
349
350    @Test
351    public void testSiblingSelectorInterpolation() throws Exception {
352        def s1 = (Selector.ChildOrParentSelector) getParser(
353                "*[tag(\"addr:housenumber\") > child_tag(\"addr:housenumber\")][regexp_test(\"even|odd\", parent_tag(\"addr:interpolation\"))]" +
354                        " + *[addr:housenumber]").child_selector()
355        def ds = new DataSet()
356        def n1 = new org.openstreetmap.josm.data.osm.Node(new LatLon(1, 2))
357        n1.put("addr:housenumber", "10")
358        def n2 = new org.openstreetmap.josm.data.osm.Node(new LatLon(1.1, 2.2))
359        n2.put("addr:housenumber", "100")
360        def n3 = new org.openstreetmap.josm.data.osm.Node(new LatLon(1.2, 2.3))
361        n3.put("addr:housenumber", "20")
362        def w = new Way()
363        w.put("addr:interpolation", "even")
364        ds.addPrimitive(n1)
365        ds.addPrimitive(n2)
366        ds.addPrimitive(n3)
367        ds.addPrimitive(w)
368        w.addNode(n1)
369        w.addNode(n2)
370        w.addNode(n3)
371
372        assert s1.right.matches(new Environment(n3))
373        assert s1.left.matches(new Environment(n2).withChild(n3).withParent(w))
374        assert s1.matches(new Environment(n3))
375        assert !s1.matches(new Environment(n1))
376        assert !s1.matches(new Environment(n2))
377        assert !s1.matches(new Environment(w))
378    }
379
380    @Test
381    public void testInvalidBaseSelector() throws Exception {
382        def css = new MapCSSStyleSource("invalid_base[key=value] {}")
383        css.loadStyleSource()
384        assert !css.getErrors().isEmpty()
385        assert css.getErrors().iterator().next().toString().contains("Unknown MapCSS base selector invalid_base")
386    }
387
388    @Test
389    public void testMinMaxFunctions() throws Exception {
390        def sheet = new MapCSSStyleSource("* {" +
391                "min_value: min(tag(x), tag(y), tag(z)); " +
392                "max_value: max(tag(x), tag(y), tag(z)); " +
393                "max_split: max(split(\";\", tag(widths))); " +
394                "}")
395        sheet.loadStyleSource()
396        def mc = new MultiCascade()
397
398        sheet.apply(mc, OsmUtils.createPrimitive("way x=4 y=6 z=8 u=100"), 20, false)
399        assert mc.getCascade(Environment.DEFAULT_LAYER).get("min_value", Float.NaN, Float.class) == 4.0f
400        assert mc.getCascade(Environment.DEFAULT_LAYER).get("max_value", Float.NaN, Float.class) == 8.0f
401
402        sheet.apply(mc, OsmUtils.createPrimitive("way x=4 y=6 widths=1;2;8;56;3;a"), 20, false)
403        assert mc.getCascade(Environment.DEFAULT_LAYER).get("min_value", -777f, Float.class) == 4
404        assert mc.getCascade(Environment.DEFAULT_LAYER).get("max_value", -777f, Float.class) == 6
405        assert mc.getCascade(Environment.DEFAULT_LAYER).get("max_split", -777f, Float.class) == 56
406    }
407}
Note: See TracBrowser for help on using the repository browser.