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

Last change on this file since 10837 was 10837, checked in by Don-vip, 9 years ago

see #11924 - extract MapCSS conditions to new class ConditionFactory (on the same model than ExpressionFactory) - should workaround Groovy bug with Java 9 (https://issues.apache.org/jira/browse/GROOVY-7879 ?)

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