/*
* Copyright (c) 2009.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 or
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
package uk.me.parabola.mkgmap.osmstyle;
import java.util.ArrayList;
import java.util.List;
import uk.me.parabola.mkgmap.reader.osm.Element;
import uk.me.parabola.mkgmap.reader.osm.GType;
import uk.me.parabola.mkgmap.reader.osm.Rule;
import uk.me.parabola.mkgmap.reader.osm.TypeResult;
import uk.me.parabola.mkgmap.reader.osm.Way;
import org.junit.Test;
import static func.lib.TestUtils.makeRuleSet;
import static org.junit.Assert.*;
/**
* More tests for rule sets. Mostly concentrating on ordering issues and
* not on the resulting type.
*
* @see RuleFileReaderTest
*/
public class RuleSetTest {
private final String MAXSPEED_EXAMPLE = "highway=* & maxspeed=40mph {set mcssl=40}" +
"highway=primary & mcssl=40 [0x01]" +
"highway=* & mcssl=40 [0x02]" +
"highway=primary [0x3]";
/**
* A test for matching in the correct order with a simple set
* of tags. See also the next test.
*/
@Test
public void testFirstMatch1() {
RuleSet rs = makeRuleSet("c=d & a=b [0x1]" +
"a=b & c=d [0x2]" +
"a=b [0x3]");
Way el = new Way(1);
el.addTag("a", "b");
el.addTag("c", "d");
GType type = getFirstType(rs, el);
assertNotNull("should be found", type);
assertEquals("first matching rule wins", 1, type.getType());
}
/**
* As previous test but with order reversed. Depending on the order
* that the tags iterate from the way, you might get different results.
*/
@Test
public void testFirstMatch2() {
RuleSet rs = makeRuleSet("a=b & c=d [0x1]" +
"c=d & a=b [0x2]" +
"a=b [0x3]");
Way el = new Way(1);
el.addTag("a", "b");
el.addTag("c", "d");
GType type = getFirstType(rs, el);
assertNotNull("should be found", type);
assertEquals("first matching rule wins", 1, type.getType());
}
/**
* An action variable is set on a rule that starts with an exists clause.
* We then attempt to match on value that it is
* @throws Exception
*/
@Test
public void testActionVarSetOnExistsRule1() throws Exception {
RuleSet rs = makeRuleSet(MAXSPEED_EXAMPLE);
Way el = new Way(1);
el.addTag("highway", "primary");
el.addTag("maxspeed", "40mph");
el.addTag("ref", "A123");
el.addTag("name", "Long Lane");
GType type = getFirstType(rs, el);
assertEquals("should match first", 1, type.getType());
}
@Test
public void testActionVarSetOnExistsRule2() {
RuleSet rs = makeRuleSet(MAXSPEED_EXAMPLE);
Way el = new Way(1);
el.addTag("highway", "unclassified");
el.addTag("maxspeed", "40mph");
el.addTag("ref", "A123");
el.addTag("name", "Long Lane");
GType type = getFirstType(rs, el);
assertEquals("should match first", 2, type.getType());
}
/**
* Check that actions are run in the order given. Use the add command
* to set a variable. The first add that is run will be the value of
* the variable.
*/
@Test
public void testActionOrder() {
RuleSet rs = makeRuleSet("b=c {add fred=1}" +
"a=b {add fred=2}" +
"c=d {add fred=3}" +
"a=b [0x1]");
// All of the conditions are set.
Way el = new Way(1);
el.addTag("a", "b");
el.addTag("b", "c");
el.addTag("c", "d");
getFirstType(rs, el);
assertEquals("b=c was first action", "1", el.getTag("fred"));
}
/**
* Match on a tag that was set in a previous action rule and was not
* on the original element.
*/
@Test
public void testMatchOnSetTag() {
RuleSet rs = makeRuleSet("highway=yes {set abcxyz = 1}" +
"abcxyz=1 [0x1]");
Way el = new Way(1);
el.addTag("highway", "yes");
GType type = getFirstType(rs, el);
assertNotNull("type matched on previously set tag", type);
}
/**
* A chain of rules, some of which contain tags from the element and
* some that contain only tags that are set in previous rules.
*/
@Test
public void testOrderChain() {
RuleSet rs = makeRuleSet("z=1 {add fred=1;}" +
"fred=1 {add abba=1}" +
"z=1 & abba=1 {add destiny=1}" +
"destiny=1 [0x1]" +
"z=1 [0x2]");
Way el = new Way(1);
el.addTag("z", "1");
GType type = getFirstType(rs, el);
assertNotNull("chain of commands", type);
assertEquals("'destiny' should be selected", 1, type.getType());
}
/**
* A chain of rules, some of which contain tags from the element and
* some that contain only tags that are set in previous rules.
*/
@Test
public void testOrderChain2() {
RuleSet rs = makeRuleSet("z=1 {add fred=1;}" +
"fred=1 {add abba=1}" +
"abba=1 {add destiny=1}" +
"destiny=1 [0x1]");
Way el = new Way(1);
el.addTag("z", "1");
GType type = getFirstType(rs, el);
assertNotNull("chain of commands", type);
}
/**
* Append to a variable in the correct order as in the rule set.
*/
@Test
public void testAppendInOrder() {
RuleSet rs = makeRuleSet("highway=primary {set R='${R} a'}" +
"ref=A1 {set R='${R} b'}" +
"z=1 {set R='${R} c'}" +
"a=1 {set R='${R} d'}");
Way el = new Way(1);
el.addTag("R", "init");
el.addTag("highway", "primary");
el.addTag("ref", "A1");
el.addTag("z", "1");
el.addTag("a", "1");
getFirstType(rs, el);
String s = el.getTag("R");
assertEquals("appended value", "init a b c d", s);
}
/**
* Rules should only be evaluated once for an element. Because of the
* way that we handle rules that may get run after tags are set in actions
* it is possible that a rule would get run twice if not careful.
*
* It is not that easy to trigger, as this is the second attempt at
* showing it is possible...
*/
@Test
public void testRuleEvaluatedOnce() {
RuleSet rs = makeRuleSet("highway=primary " +
" {set highway=primary; set result='${result} 1';}" +
"highway='primary' {set result='${result} 2'");
Way el = new Way(1);
el.addTag("highway", "primary");
el.addTag("result", "0");
getFirstType(rs, el);
assertEquals("rules run once", "0 1 2", el.getTag("result"));
}
/**
* The example that was in the check in comment, make sure it actually
* does work ;)
*/
@Test
public void testCheckinExample() {
RuleSet rs = makeRuleSet("highway=motorway {set blue=true;}\n" +
"blue=true [0x1 ]\n" +
"highway=motorway [0x2]");
Way el = new Way(1);
el.addTag("highway", "motorway");
GType type = getFirstType(rs, el);
assertEquals("first match is on blue", 1, type.getType());
}
@Test
public void testActionsMixedWithTypes() {
RuleSet rs = makeRuleSet("highway=primary {set marker=1}" +
"marker=2 [0x1]" +
"highway=primary {set marker=2}" +
"marker=2 [0x2]");
Way el = new Way(1);
el.addTag("highway", "primary");
GType type = getFirstType(rs, el);
assertEquals("second marker rule", 2, type.getType());
}
@Test
public void testContinueDefault() {
RuleSet rs = makeRuleSet("highway=footway {set surface=good;} [0x1 continue]" +
"surface=good [0x20]" +
"surface=bad [0x30]");
Way el = new Way(1);
el.addTag("highway", "footway");
el.addTag("surface", "bad");
List<GType> list = resolveList(rs, el);
assertEquals("number of lines returned", 2, list.size());
assertEquals("surface setting not propagated", "bad", el.getTag("surface"));
assertEquals("result type", 0x30, list.get(1).getType());
}
@Test
public void testContinuePropagate() {
RuleSet rs = makeRuleSet("highway=footway {set surface=good;} [0x1 continue propagate]" +
"surface=good [0x20]" +
"surface=bad [0x30]");
Way el = new Way(1);
el.addTag("highway", "footway");
el.addTag("surface", "bad");
List<GType> list = resolveList(rs, el);
assertEquals("number of lines returned", 2, list.size());
assertEquals("surface setting is propagated", "good", el.getTag("surface"));
assertEquals("result type", 0x20, list.get(1).getType());
}
@Test
public void testContinueNoPropagate() {
RuleSet rs = makeRuleSet("highway=footway {set surface=good;} [0x1 continue no_propagate]" +
"surface=good [0x20]" +
"surface=bad [0x30]");
Way el = new Way(1);
el.addTag("highway", "footway");
el.addTag("surface", "bad");
List<GType> list = resolveList(rs, el);
assertEquals("number of lines returned", 2, list.size());
assertEquals("surface setting is not propagated", "bad", el.getTag("surface"));
assertEquals("result type", 0x30, list.get(1).getType());
}
@Test
public void testContinueWithActions() {
RuleSet rs = makeRuleSet("highway=footway {set surface=good;} [0x1 continue with_actions]" +
"surface=good [0x20]" +
"surface=bad [0x30]");
Way el = new Way(1);
el.addTag("highway", "footway");
el.addTag("surface", "bad");
List<GType> list = resolveList(rs, el);
assertEquals("number of lines returned", 2, list.size());
assertEquals("surface setting is propagated", "good", el.getTag("surface"));
assertEquals("result type", 0x20, list.get(1).getType());
}
@Test
public void testContinueChangesTag() {
RuleSet rs = makeRuleSet("highway=crossing & crossing=zebra_crossing" +
" {set highway=deleted_crossing} [0x10404 resolution 24 continue propagate]" +
"highway=crossing [0x1010f resolution 24 continue]" +
"highway=deleted_crossing [0x6 resolution 24 continue]"
);
Way el = new Way(1);
el.addTag("highway", "crossing");
el.addTag("crossing", "zebra_crossing");
List<GType> list = resolveList(rs, el);
assertEquals("first element", 0x10404, list.get(0).getType());
assertEquals("second element", 0x6, list.get(1).getType());
}
private List<GType> resolveList(RuleSet rs, Way el) {
final List<GType> list = new ArrayList<GType>();
rs.resolveType(el, new TypeResult() {
public void add(Element el, GType type) {
list.add(type);
}
});
return list;
}
private GType getFirstType(Rule rs, Element el) {
final List<GType> types = new ArrayList<GType>();
rs.resolveType(el, new TypeResult() {
public void add(Element el, GType type) {
types.add(type);
}
});
if (types.isEmpty())
return null;
else
return types.get(0);
}
}