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, 9 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.