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

Last change on this file since 12990 was 12990, checked in by bastiK, 21 months ago

see #15410 - update tests + minor fixes (2)

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