/*
* Copyright (C) 2008 Steve Ratcliffe
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License 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.
*
*
* Author: Steve Ratcliffe
* Create date: 29-Nov-2008
*/
package uk.me.parabola.mkgmap.osmstyle;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.mkgmap.general.LevelInfo;
import uk.me.parabola.mkgmap.reader.osm.Element;
import uk.me.parabola.mkgmap.reader.osm.FeatureKind;
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 uk.me.parabola.mkgmap.scan.SyntaxException;
import func.lib.StringStyleFileLoader;
import org.junit.Test;
import static func.lib.TestUtils.makeRuleSet;
import static org.junit.Assert.*;
public class RuleFileReaderTest
{
/**
* Test of a file containing a number of different rules, with varying
* formatting and including comments.
*/
@Test
public void testLoad
() {
RuleSet rs = makeRuleSet
("highway=footway & type=rough [0x2 level 2]\n" +
"highway=footway | highway = path\n" +
" [0x3]\n# comment here\n" +
"foo=\nbar & bar=two [0x4]\n" +
"highway=* & oneway=true [0x6 level 1]\n" +
"");
Element el =
new Way
(1);
el.
addTag("highway",
"footway");
GType type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
("plain footway",
"[0x3 level 0]", type.
toString());
el.
addTag("type",
"rough");
type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
("rough footway",
"[0x2 level 2]", type.
toString());
}
/**
* Test for non-standard level specification. You can give a range
* of levels, rather than defaulting the max end to 0.
*/
@Test
public void testLevel
() {
RuleSet rs = makeRuleSet
(
"highway=primary [0x1 level 1-3]"
);
Element el =
new Way
(1);
el.
addTag("highway",
"primary");
GType type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
("min level",
1, type.
getMinLevel());
assertEquals
("max level",
3, type.
getMaxLevel());
}
/**
* Try out arithmetic comparisons and mixtures of 'and' and 'or'.
*/
@Test
public void testComplexExpressions
() {
String str =
"a=b & (c=d | e=f) & x>10 [0x1]\n";
RuleSet rs = makeRuleSet
(str
);
Element el =
new Way
(1);
el.
addTag("a",
"b");
el.
addTag("c",
"d");
el.
addTag("x",
"11");
GType type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
("expression ok",
1, type.
getType());
// fails with x less than 10
el.
addTag("x",
"9");
type = getFirstType
(rs, el
);
assertNull
("x too low", type
);
// also fails with x equal to 10
el.
addTag("x",
"10");
type = getFirstType
(rs, el
);
assertNull
("x too low", type
);
// OK with x > 10
el.
addTag("x",
"100");
el.
addTag("e",
"f");
type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
("c and e set",
1, type.
getType());
el.
addTag("c",
"");
el.
addTag("e",
"");
type = getFirstType
(rs, el
);
assertNull
("none of c and e set", type
);
el.
addTag("e",
"f");
type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
("e is set to f",
1, type.
getType());
}
/**
* Test based on email on the mailing list at:
* http://www.mkgmap.org.uk/pipermail/mkgmap-dev/2009q3/003009.html
* See that email for an explanation.
*/
@Test
public void testComparasons
() {
String str =
"highway=null_null & layer<0 [0x01 resolution 10]\n" +
"highway=null_null & layer=0 [0x02 resolution 10]\n" +
"highway=null_null & layer>0 [0x03 resolution 10]\n" +
"highway=null_null & layer='-1' [0x04 resolution 10]\n" +
"highway=null_null & layer='0' [0x05 resolution 10]\n" +
"highway=null_null & layer='1' [0x06 resolution 10]\n" +
"highway=null_null & layer='+1' [0x07 resolution 10]\n" +
"highway=null_null [0x08 resolution 10]";
RuleSet rs = makeRuleSet
(str
);
// 9902
Element el =
new Way
(1);
el.
addTag("highway",
"null_null");
el.
addTag("layer",
"-1");
GType type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
("9902 layer = -1", 0x1, type.
getType());
// 9912
el.
addTag("layer",
"0");
type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
("9912 layer = 0", 0x2, type.
getType());
// 9922
el.
deleteTag("layer");
type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
("9922 no layer tag", 0x8, type.
getType());
// 9932
el.
addTag("layer",
"1");
type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
("9932 layer is 1", 0x3, type.
getType());
// 9952
el.
addTag("layer",
"+1");
type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
("9952 layer is +1", 0x3, type.
getType());
}
@Test
public void testMultipleActions
() {
String rstr =
"highway=footway {add access = no; add foot = yes} [0x16 road_class=0 road_speed=0 resolution 23]";
RuleSet rs = makeRuleSet
(rstr
);
Element el =
new Way
(1);
el.
addTag("highway",
"footway");
getFirstType
(rs, el
);
assertEquals
("access set",
"no", el.
getTag("access"));
assertEquals
("access set",
"yes", el.
getTag("foot"));
}
/**
* You can now have a wild card at the top level.
*/
@Test
public void testWildcardTop
() {
RuleSet rs = makeRuleSet
("highway=* {set a=fred} [0x1]\n");
assertNotNull
("rule found", rs
);
Element el =
new Way
(1);
el.
addTag("highway",
"secondary");
GType type = getFirstType
(rs, el
);
assertNotNull
("can find match", type
);
assertEquals
("correct type",
1, type.
getType());
assertEquals
("tag set",
"fred", el.
getTag("a"));
}
/**
* Deal with cases such as
* (a = b | a = c) & d!=*
* where there is no key at the top level. This gets converted
* to: (a=b & d!=*) | (a=c & d!= *) which can then be used.
*
* This is applied recursively, so you can have chains of any length.
*/
@Test
public void testLeftSideOr
() {
RuleSet rs = makeRuleSet
("(a = b | a = c | a=d) & e!=* [0x2]" +
"a=c & e!=* [0x1]");
assertNotNull
("a=b chain", rs
);
assertNotNull
("a=c chain", rs
);
assertNotNull
("a=d chain", rs
);
// get the a=c chain and look at it more closely
Element el =
new Way
(1);
el.
addTag("a",
"c");
GType type = getFirstType
(rs, el
);
assertNotNull
("match e not existing", type
);
assertEquals
("correct type",
2, type.
getType());
el =
new Way
(2);
el.
addTag("a",
"d");
assertNotNull
("match e not existing", type
);
assertEquals
("correct type",
2, type.
getType());
}
/**
* You can now have a wild card at the top level, here we have & between
* two of them.
*/
@Test
public void testWildcard2
() {
RuleSet rs = makeRuleSet
("highway=* & z=* {set a=square} [0x1]\n");
assertNotNull
("rule found", rs
);
Element el =
new Way
(1);
el.
addTag("highway",
"secondary");
GType type = getFirstType
(rs, el
);
assertNull
("type not found with no z tag", type
);
// now add z
el.
addTag("z",
"1");
type = getFirstType
(rs, el
);
assertNotNull
("found match", type
);
assertEquals
("correct type",
1, type.
getType());
assertEquals
("tag set",
"square", el.
getTag("a"));
}
/**
* Tests for the road classification and other parts of the GType.
*/
@Test
public void testGType
() {
RuleSet rs = makeRuleSet
("highway=motorway " +
"[0x1 road_class=4 road_speed=7 default_name='motor way']\n");
Element el =
new Way
(1);
el.
addTag("highway",
"motorway");
GType type = getFirstType
(rs, el
);
assertNotNull
(type
);
// Check that the correct class and speed are returned.
assertEquals
("class",
4, type.
getRoadClass());
assertEquals
("class",
7, type.
getRoadSpeed());
assertEquals
("default name",
"motor way", type.
getDefaultName());
}
/**
* Check for the regexp handling.
*/
@Test
public void testRegexp
() {
RuleSet rs = makeRuleSet
("highway=* & name ~ 'blue.*' [0x2]\n");
assertNotNull
("rule found", rs
);
// Set up element with matching name
Element el =
new Way
(1);
el.
addTag("highway",
"secondary");
el.
addTag("name",
"blue sq");
GType type = getFirstType
(rs, el
);
assertNotNull
("matched regexp", type
);
assertEquals
("matched type",
2, type.
getType());
// change name to one that should not match
el.
addTag("name",
"yellow");
type = getFirstType
(rs, el
);
assertNull
("no match for yello", type
);
}
@Test
public void testRegex2
() {
RuleSet rs = makeRuleSet
("a=b & (smoothness ~ '.*(bad|horrible|impassable)' | sac_scale ~ '.*(mountain|alpine)_hiking') [0x1]" +
"a = '>=' & b = '>' [0x2]");
assertNotNull
(rs
);
Element el =
new Way
(1);
el.
addTag("a",
"b");
el.
addTag("smoothness",
"zzzbad");
GType type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
("matched .*bad",
1, type.
getType());
el =
new Way
(1);
el.
addTag("a",
"b");
el.
addTag("sac_scale",
"zzz alpine_hiking");
type = getFirstType
(rs, el
);
assertNotNull
(type
);
el =
new Way
(1);
el.
addTag("a",
"b");
el.
addTag("sac_scale",
"zzz alp_hiking");
type = getFirstType
(rs, el
);
assertNull
(type
);
el =
new Way
(1);
el.
addTag("a",
">=");
el.
addTag("b",
">");
type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
("match string that is the same as an operator",
2, type.
getType());
}
/**
* Some operations could not originally be used by themselves but now they are converted
* into expressions that can be handled automatically. The following few tests verify this.
*/
@Test
public void testRegexAtTop
() {
RuleSet rs = makeRuleSet
("QUOTA ~ ' [05]00\\.0+' [0x2]");
Element el =
new Way
(1);
el.
addTag("QUOTA",
" 500.0");
GType type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
(2, type.
getType());
}
@Test
(expected = SyntaxException.
class)
public void testNEAtTop
() {
RuleSet rs = makeRuleSet
("QUOTA != 'fred' [0x2]");
Element el =
new Way
(1);
el.
addTag("QUOTA",
"tom");
GType type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
(2, type.
getType());
}
@Test
public void testNEAtTopWithRE
() {
RuleSet rs = makeRuleSet
("a != 'fred' & a ~ '.*' [0x2]");
Element el =
new Way
(1);
el.
addTag("a",
"tom");
GType type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
(2, type.
getType());
}
@Test
public void testNumberOpAtTop
() {
RuleSet rs = makeRuleSet
("QUOTA > 10 [0x1] QUOTA < 6 [0x2]");
Element el =
new Way
(1);
el.
addTag("QUOTA",
"2");
GType type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
(2, type.
getType());
}
/**
* Failure of the optimiser to promote the correct term to the front.
* Example from mailing list.
*/
@Test
public void testOptimizeWithOr
() {
String s =
"highway ~ '(secondary|tertiary|unclassified|residential|minor|living_street|service)' " +
"& oneway=* " +
"& (cycleway=opposite | cycleway=opposite_lane | cycleway=opposite_track )" +
"[0x2 ]";
RuleSet rs = makeRuleSet
(s
);
Element el =
new Way
(1);
el.
addTag("highway",
"tertiary");
el.
addTag("oneway",
"1");
el.
addTag("cycleway",
"opposite_track");
GType type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
(2, type.
getType());
el.
addTag("cycleway",
"fred");
type = getFirstType
(rs, el
);
assertNull
(type
);
el.
addTag("cycleway",
"opposite");
type = getFirstType
(rs, el
);
assertNotNull
(type
);
el = el.
copy(); // Copy for LinkedOp which remembers the last matched element
el.
addTag("cycleway",
"opposite_lane");
type = getFirstType
(rs, el
);
assertNotNull
(type
);
el.
addTag("highway",
"fred");
type = getFirstType
(rs, el
);
assertNull
(type
);
}
/**
* Test is a simplified version of a rule in the floodblocker style.
*/
@Test
public void testOptimizeWithOr2
() {
String s =
"highway=*" +
"& tunnel!=*" +
"& (layer!=* | layer=0)" +
" [0x02]\n"
;
RuleSet rs = makeRuleSet
(s
);
Element el =
new Way
(1);
el.
addTag("highway",
"primary");
GType type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
(2, type.
getType());
el.
addTag("layer",
"0");
type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
(2, type.
getType());
el.
addTag("layer",
"1");
type = getFirstType
(rs, el
);
assertNull
(type
);
}
@Test
public void testOptimizeWithOr3
() throws Exception {
String s =
"highway=* & bridge!=* & " +
" (mtb:scale>0 | mtb:scale='0+' | tracktype ~ 'grade[2-6]' |" +
" sac_scale ~ '.*(mountain|alpine)_hiking' |" +
" sport=via_ferrata) [0x3]";
RuleSet rs = makeRuleSet
(s
);
Element el =
new Way
(1);
el.
addTag("highway",
"primary");
el.
addTag("mtb:scale",
"0+");
GType type = getFirstType
(rs, el
);
assertNotNull
(type
);
}
/**
* This simply is to make sure that actions that affect their own
* conditions do not hang. There are no defined semantics for this.
*/
@Test
public void testSelfReference
() {
RuleSet rs = makeRuleSet
("iii=* { set iii=no }");
//Rule rule = rs.getMap().get("foot=*");
Way el =
new Way
(1);
el.
addTag("foot",
"yes");
el.
addTag("iii",
"xyz");
getFirstType
(rs, el
);
}
/**
* Test the not operator.
*/
@Test
public void testNot
() {
RuleSet rs = makeRuleSet
("tunnel=yes & !(route=mtb | route=bicycle) [0x1]");
//RuleSet rs = makeRuleSet("tunnel=yes & (route!=mtb & route!=bicycle) [0x1]");
Way el =
new Way
(1);
el.
addTag("tunnel",
"yes");
el.
addTag("route",
"abc");
getFirstType
(rs, el
);
}
@Test
public void testGTR
() {
RuleSet rs = makeRuleSet
("z=0 & a >= 10 [0x1]");
Way el =
new Way
(1);
el.
addTag("z",
"0");
el.
addTag("a",
"9");
GType type = getFirstType
(rs, el
);
assertNull
("a less that 10, no result", type
);
el.
addTag("a",
"10");
type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
("Valid type returned",
1, type.
getType());
el.
addTag("a",
"11");
type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
("Valid type returned",
1, type.
getType());
}
@Test
public void testLTE
() {
RuleSet rs = makeRuleSet
("z=0 & a <= 10 [0x1]");
Way el =
new Way
(1);
el.
addTag("z",
"0");
el.
addTag("a",
"9");
GType type = getFirstType
(rs, el
);
assertNotNull
("a less that 10", type
);
assertEquals
("found type for a <= 10",
1, type.
getType());
el.
addTag("a",
"10");
type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
("Found type for a == 10",
1, type.
getType());
el.
addTag("a",
"11");
type = getFirstType
(rs, el
);
assertNull
("a is 11, a <= 10 is false", type
);
}
@Test
public void testNE
() {
RuleSet rs = makeRuleSet
("z=0 & a != 10 [0x1]");
Way el =
new Way
(1);
el.
addTag("z",
"0");
el.
addTag("a",
"9");
GType type = getFirstType
(rs, el
);
assertNotNull
("a is 9 so a!=10 is true", type
);
el.
addTag("a",
"10");
type = getFirstType
(rs, el
);
assertNull
("a is 10, so a!=10 is false", type
);
}
/**
* Test values such as 3.5 in comparisons.
* Originally non-integer values were not allowed and were not even recognised.
*/
@Test
public void testDecimalValues
() {
RuleSet rs = makeRuleSet
("z=yes & a < 3.5 [0x1]");
Way el =
new Way
(1);
el.
addTag("z",
"yes");
// Is less than so
el.
addTag("a",
"2");
GType type = getFirstType
(rs, el
);
assertNotNull
("a is less than 3.5", type
);
el.
addTag("a",
"4");
type = getFirstType
(rs, el
);
assertNull
("a is greater than 3.5", type
);
}
@Test
public void testDecimalAndDecimalCompare
() {
RuleSet rs = makeRuleSet
("z=yes & a < 3.5 [0x1]");
Way el =
new Way
(1);
el.
addTag("z",
"yes");
// Is less than so
el.
addTag("a",
"3.49");
GType type = getFirstType
(rs, el
);
assertNotNull
("a is less than 3.5", type
);
el.
addTag("a",
"3.55");
type = getFirstType
(rs, el
);
assertNull
("a is greater than 3.5", type
);
}
/**
* A moderately complex set of conditions and substitutions.
*/
@Test
public void testMtbRules
() {
RuleSet rs = makeRuleSet
(
"(mtb:scale=* | mtb:scale:uphill=*) & route=mtb" +
"{ name 'mtbrt${mtb:scale|def:.}${mtb:scale:uphill|def:.} ${name}' " +
" | 'mtbrt${mtb:scale|def:.}${mtb:scale:uphill|def:.}' }" +
" (mtb:scale=* | mtb:scale:uphill=*) & route!=mtb " +
"{ name 'mtb${mtb:scale|def:.}${mtb:scale:uphill|def:.} ${name}' " +
" | 'mtb${mtb:scale|def:.}${mtb:scale:uphill|def:.}' }"
);
Way el =
new Way
(1);
el.
addTag("route",
"mtb");
el.
addTag("mtb:scale",
"2");
getFirstType
(rs, el
);
assertEquals
("mtbrt2.", el.
getName());
el =
new Way
(1);
el.
addTag("route",
"mtb");
el.
addTag("mtb:scale:uphill",
"3");
getFirstType
(rs, el
);
assertEquals
("mtbrt.3", el.
getName());
el =
new Way
(1);
el.
addTag("name",
"myname");
el.
addTag("route",
"mtb");
el.
addTag("mtb:scale:uphill",
"3");
getFirstType
(rs, el
);
assertEquals
("mtbrt.3 myname", el.
getName());
el =
new Way
(1);
el.
addTag("mtb:scale:uphill",
"3");
getFirstType
(rs, el
);
assertEquals
("mtb.3", el.
getName());
}
/**
* Appending to an existing tag.
*/
@Test
public void testTagAppend
() {
RuleSet rs = makeRuleSet
(
"highway=*{set fullname='${ref}';" +
"set fullname='${fullname} ${name}';" +
"set fullname='${fullname} ${name1}';" +
"set fullname='${fullname} ${name2}';" +
"name '${fullname}'}"
);
Way el =
new Way
(1);
el.
addTag("highway",
"road");
el.
addTag("ref",
"A1");
el.
addTag("name",
"long lane");
el.
addTag("name1",
"foo");
el.
addTag("name2",
"bar");
getFirstType
(rs, el
);
assertEquals
("appended name",
"A1 long lane foo bar", el.
getName());
}
@Test
public void testExists
() {
RuleSet rs = makeRuleSet
("highway=* & maxspeed=40 {set mcssl=40}" +
"highway=primary & mcssl=40 [0x2 ]" +
"highway=* & mcssl=40 [0x3]");
Way el =
new Way
(1);
el.
addTag("ref",
"A123");
el.
addTag("name",
"Long Lane");
el.
addTag("highway",
"primary");
el.
addTag("maxspeed",
"40");
GType type = getFirstType
(rs, el
);
assertNotNull
("finds the type", type
);
assertEquals
("resulting type",
2, type.
getType());
}
/**
* Test the continue keyword. If a type is marked with this word, then
* further matches are performed and this might result in more types
* being added.
*/
@Test
public void testContinue
() {
RuleSet rs = makeRuleSet
("highway=primary [0x1 continue]" +
"highway=primary [0x2 continue]" +
"highway=primary [0x3]" +
"highway=primary [0x4]"
);
Way el =
new Way
(1);
el.
addTag("highway",
"primary");
final List<GType
> list =
new ArrayList<>();
rs.
resolveType(el,
new TypeResult
() {
public void add
(Element el, GType type
) {
list.
add(type
);
}
});
GType type = list.
get(0);
assertEquals
("first type",
1, type.
getType());
assertEquals
("continue search",
true, type.
isContinueSearch());
assertEquals
("number of result types",
3, list.
size());
assertEquals
("type of first",
1, list.
get(0).
getType());
assertEquals
("type of second",
2, list.
get(1).
getType());
assertEquals
("type of third",
3, list.
get(2).
getType());
}
@Test
public void testContinueRepeat
() {
RuleSet rs = makeRuleSet
("highway=primary [0x1 continue]" +
"highway=primary [0x2 continue]" +
"highway=primary [0x3]" +
"highway=primary [0x4]"
);
Way el =
new Way
(1);
el.
addTag("highway",
"primary");
for (int i =
0; i
< 3; i++
) {
GType type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
("first type",
1, type.
getType());
assertEquals
("continue search",
true, type.
isContinueSearch());
}
}
/**
* The main point of this test is to ensure that all the examples compile.
*/
@Test
public void testComplexRegex
() {
RuleSet rs = makeRuleSet
(
//"a~b [0x0]" +
"a~b & c=d [0x1]" +
"a~b & c~d & e=f [0x2]" +
"(a~b | c~d) & e=f [0x3]" +
"(a~b | c~d) & e=f & g=h [0x4]" +
"((a~b | c~d) & e=f) & g=h [0x5]" +
"e=f & g=h & (a~b | c~'d.*') [0x6]" +
"(e=f & g=h) & (a~b | c~'d.*') [0x7]" +
"a=* & b=* & c=d [0x8]" +
"a=* & (b=* | c=d) [0x9]" +
""
);
Way el =
new Way
(1);
el.
addTag("c",
"df");
el.
addTag("g",
"h");
el.
addTag("e",
"f");
GType type = getFirstType
(rs, el
);
assertNotNull
("matches a rule", type
);
}
@Test
public void testTagsUsed
() {
RuleSet rs = makeRuleSet
("highway=primary & surface=good [0x1]" +
"A=B | C=D & E~'f.*' & G!=9 & K=* & L!=* [0x2]");
Set<String> tags = rs.
getUsedTags();
assertEquals
("number of tags used",
8, tags.
size());
assertTrue
("has highway", tags.
contains("highway"));
assertTrue
("has surface", tags.
contains("surface"));
assertTrue
("has A", tags.
contains("A"));
assertTrue
("has C", tags.
contains("C"));
assertTrue
("has E", tags.
contains("E"));
assertTrue
("has G", tags.
contains("G"));
assertTrue
("has K", tags.
contains("K"));
assertTrue
("has L", tags.
contains("L"));
}
/**
* There is a case where a tag is only used in an action but not in any
* expression. If we dropped the tags it would not be available for the
* action. A typical example might be name.
*/
@Test
public void testTagsUsedInActions
() {
RuleSet rs = makeRuleSet
("A=B { set t='${C}'; add t='${D} p ${E}'; name '${F} ${G}'; rename K L");
Set<String> tags = rs.
getUsedTags();
assertTrue
("has A", tags.
contains("A"));
assertTrue
("has C", tags.
contains("C"));
assertTrue
("has D", tags.
contains("D"));
assertTrue
("has E", tags.
contains("E"));
assertTrue
("has F", tags.
contains("F"));
assertTrue
("has G", tags.
contains("G"));
assertTrue
("has K", tags.
contains("K"));
}
@Test
public void testIncludeAsTagName
() {
RuleSet rs = makeRuleSet
("include=yes [0x2]");
Way way =
new Way
(1);
way.
addTag("include",
"yes");
GType type = getFirstType
(rs, way
);
assertNotNull
(type
);
assertEquals
(2, type.
getType());
}
@Test
public void testIncludeAsTagName2
() {
RuleSet rs = makeRuleSet
("include = yes [0x2]");
Way way =
new Way
(1);
way.
addTag("include",
"yes");
GType type = getFirstType
(rs, way
);
assertNotNull
(type
);
assertEquals
(2, type.
getType());
}
@Test
public void testIncludeFile
() {
StyleFileLoader loader =
new StringStyleFileLoader
(new String[][] {
{"lines",
"include incfile;"},
{"incfile",
"highway=secondary [0x3]"},
});
RuleSet rs = makeRuleSet
(loader
);
Element el =
new Way
(1);
el.
addTag("highway",
"secondary");
GType type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
(3, type.
getType());
}
@Test
public void testIncludeFileQuoted
() {
StyleFileLoader loader =
new StringStyleFileLoader
(new String[][] {
{"lines",
"include \n 'inc file' \n;"},
{"inc file",
"highway=secondary [0x3]"},
});
RuleSet rs = makeRuleSet
(loader
);
Element el =
new Way
(1);
el.
addTag("highway",
"secondary");
GType type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
(3, type.
getType());
}
/**
* Test an include file within an include file.
*/
@Test
public void testNestedIncludes
() {
StyleFileLoader loader =
new StringStyleFileLoader
(new String[][] {
{"lines",
"a=1 [0x1] include 'first'; a=2 [0x2]"},
{"first",
"b=1 [0x1] include 'second'; b=2 [0x2 ]"},
{"second",
"c=1 [0x1] c=2 [0x2 ]"},
});
RuleSet rs = makeRuleSet
(loader
);
Element el =
new Way
(1);
el.
addTag("a",
"2");
GType type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
(2, type.
getType());
el =
new Way
(2);
el.
addTag("c",
"1");
type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
(1, type.
getType());
el =
new Way
(2);
el.
addTag("c",
"2");
type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
(2, type.
getType());
}
/**
* Bug when the first statement of an include file is itself an include statement.
* As luck would have the test tested the supposedly more difficult case of an
* include statement in the middle of the file.
*/
@Test
public void testNestedIncludeAndImmediateInclude
() {
StyleFileLoader loader =
new StringStyleFileLoader
(new String[][] {
{"lines",
"a=1 [0x1] include 'first'; a=2 [0x2]"},
{"first",
"include 'second'; b=2 [0x2 ]"},
{"second",
"c=1 [0x1] c=2 [0x2 ]"},
});
RuleSet rs = makeRuleSet
(loader
);
Element el =
new Way
(1);
el.
addTag("a",
"2");
GType type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
(2, type.
getType());
el =
new Way
(2);
el.
addTag("c",
"1");
type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
(1, type.
getType());
el =
new Way
(2);
el.
addTag("c",
"2");
type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
(2, type.
getType());
}
@Test
public void testIncludeFrom
() {
// NOTE: this test uses the default style, which could change.
StyleFileLoader loader =
new StringStyleFileLoader
(new String[][] {
{"lines",
"include 'lines' from default;\n"},
});
RuleSet rs = makeRuleSet
(loader
);
Way way =
new Way
(1);
way.
addTag("highway",
"motorway");
GType type = getFirstType
(rs, way
);
assertNotNull
("Check type not null", type
);
assertEquals
(1, type.
getType());
}
@Test
public void testLengthFunction
() {
// Its less than 92m
RuleSet rs = makeRuleSet
("A=B & length() < 92 [0x5]");
Way el = getWayWithLength
();
el.
addTag("A",
"B");
GType type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
(5, type.
getType());
}
@Test
public void testLengthFunction2
() {
// Its more than 91m
RuleSet rs = makeRuleSet
("A=B & length() > 91 [0x5]");
Way el = getWayWithLength
();
el.
addTag("A",
"B");
GType type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
(5, type.
getType());
}
@Test
public void testFunctionWithSpaces
() {
RuleSet rs = makeRuleSet
("A=B & length ( \n) > 91 & length\n()\n < 92 [0x5]");
Way el = getWayWithLength
();
el.
addTag("A",
"B");
GType type = getFirstType
(rs, el
);
assertNotNull
(type
);
}
@Test
(expected = SyntaxException.
class)
public void testFunctionWithParameters
() {
// a parameter in a function is not allowed yet
// this should throw a SyntaxException
makeRuleSet
("A=B & length(a) > 91 [0x5]");
assertTrue
("Function with parameters are not allowed",
false);
}
@Test
public void testIsClosedFunction
() {
RuleSet rs = makeRuleSet
("A=B & is_closed() = true [0x5]");
Way el = getWayForClosedCompleteCheck
(true,
true);
GType type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
(5, type.
getType());
Way el2 = getWayForClosedCompleteCheck
(false,
true);
RuleSet rs2 = makeRuleSet
("A=B & is_closed() = false [0x5]");
GType type2 = getFirstType
(rs2, el2
);
assertNotNull
(type2
);
assertEquals
(5, type2.
getType());
}
@Test
public void testIsCompleteFunction
() {
RuleSet rs = makeRuleSet
("A=B & is_complete() = true [0x5]");
Way el = getWayForClosedCompleteCheck
(false,
true);
GType type = getFirstType
(rs, el
);
assertNotNull
(type
);
assertEquals
(5, type.
getType());
Way el2 = getWayForClosedCompleteCheck
(false,
false);
RuleSet rs2 = makeRuleSet
("A=B & is_complete() = false [0x5]");
GType type2 = getFirstType
(rs2, el2
);
assertNotNull
(type2
);
assertEquals
(5, type2.
getType());
}
@Test
(expected=SyntaxException.
class)
public void testNoFunctionParameters
() {
// a parameter in a function is not allowed for the length() function
// this should throw a SyntaxException
makeRuleSet
("A=B & length(a) > 91 [0x5]");
assertTrue
("Function with parameters are not allowed",
false);
}
/** You can't use length as the only term */
@Test
(expected=SyntaxException.
class)
public void testStandAloneLength
() {
// The length function is not allowed by itself, should be a syntax exception
makeRuleSet
("length() > 91 [0x5]");
}
@Test
(expected = SyntaxException.
class)
public void testFunctionDoesNotExist
() {
makeRuleSet
("A=B & non_existing_function() > 10 [0x5]");
}
/**
* Functions can be restricted to certain files. Eg length() does not make sense on a point.
*/
@Test
(expected = SyntaxException.
class)
public void testLengthInPoints
() {
StringStyleFileLoader loader =
new StringStyleFileLoader
(new String[][] {
{"points",
"A=B & length() < 100"}
});
RuleSet rs =
new RuleSet
();
RuleFileReader rr =
new RuleFileReader
(FeatureKind.
POINT,
LevelInfo.
createFromString("0:24 1:20 2:18 3:16 4:14"),
rs,
false,
null);
try {
rr.
load(loader,
"points");
} catch (FileNotFoundException e
) {
throw new AssertionError("Failed to open file: lines");
}
}
/**
* A test between something that is not not a value should be caught as a syntax
* error.
*/
@Test
(expected = SyntaxException.
class)
public void testWithNonValue
() {
RuleSet rs = makeRuleSet
("c=b & a=!* [0x5]");
Way w = getWayWithLength
();
w.
addTag("c",
"b");
getFirstType
(rs, w
);
}
@Test
(expected = SyntaxException.
class)
public void testLessThanWithNonValue
() {
RuleSet rs = makeRuleSet
("c=b & a<!* [0x5]");
Way w = getWayWithLength
();
w.
addTag("c",
"b");
getFirstType
(rs, w
);
}
/**
* Test the syntax to get a tag value on the RHS of the expression.
*/
@Test
public void testGetTagValueEquality
() {
RuleSet rs = makeRuleSet
("a=b & a=$c [0x5] a=b [0x6]");
Way w =
new Way
(1);
w.
addTag("a",
"b");
w.
addTag("c",
"b");
GType type = getFirstType
(rs, w
);
assertNotNull
(type
);
assertEquals
(5, type.
getType());
w.
addTag("c",
"x");
type = getFirstType
(rs, w
);
assertNotNull
(type
);
assertEquals
(6, type.
getType());
}
@Test
public void testGetTagValueNotFound
() {
RuleSet rs = makeRuleSet
("a=b & b<$c [0x5] a=b [0x6]");
Way w =
new Way
(1);
w.
addTag("a",
"b");
w.
addTag("b",
"50");
GType type = getFirstType
(rs, w
);
assertNotNull
(type
);
assertEquals
(6, type.
getType());
}
@Test
public void testGetTagValueAlone
() {
RuleSet rs = makeRuleSet
("a<$b [0x5] a=b [0x6]");
Way w =
new Way
(1);
w.
addTag("a",
"1");
w.
addTag("b",
"2");
GType type = getFirstType
(rs, w
);
assertNotNull
(type
);
assertEquals
(5, type.
getType());
}
@Test
public void testValueTagValue
() {
RuleSet rs = makeRuleSet
("a=$b [0x5]");
Way w =
new Way
(1);
w.
addTag("a",
"2");
w.
addTag("b",
"2");
GType type = getFirstType
(rs, w
);
assertNotNull
(type
);
assertEquals
(5, type.
getType());
}
@Test
public void test_X3NOT_Error
() {
// Bug caused an Error: X3:NOT syntax exception to be thrown.
RuleSet rs = makeRuleSet
("(a=1 | b=2) & !(c=1) & d!=3 [0x8]");
Way w =
new Way
(1);
w.
addTag("b",
"1");
GType type = getFirstType
(rs, w
);
assertNull
(type
);
w.
addTag("b",
"2");
type = getFirstType
(rs, w
);
assertNotNull
(type
);
w.
addTag("d",
"3");
type = getFirstType
(rs, w
);
assertNull
(type
);
w.
addTag("d",
"2");
type = getFirstType
(rs, w
);
assertNotNull
(type
);
}
@Test
public void testBugOrWithAndOnLeft
() {
RuleSet rs = makeRuleSet
("((a=1&b=2) | a=2) & c!=4 [0x2]");
Way w =
new Way
(1);
w.
addTag("a",
"2");
GType type = getFirstType
(rs, w
);
assertNotNull
(type
);
}
@Test
public void testBugOrWithAndOnLeft2
() {
RuleSet rs = makeRuleSet
("(((a=1 | a=5)&b=2) | a=2) & c!=4 [0x2]");
Way w =
new Way
(1);
w.
addTag("a",
"2");
GType type = getFirstType
(rs, w
);
assertNotNull
(type
);
assertEquals
(type.
getType(),
2);
}
@Test
public void testBugOr
() {
String s =
"maxspeed=*\n" +
" & ( maxspeedkmh()>120 | maxspeed = none )\n" +
" & ( highway = motorway | highway = trunk )\n" +
"[0x4]\n";
RuleSet rs = makeRuleSet
(s
);
Way w =
new Way
(1);
w.
addTag("highway",
"trunk");
w.
addTag("maxspeed",
"122");
GType type = getFirstType
(rs, w
);
assertNotNull
(type
);
}
/**
* Get a way with a few points for testing length.
*
* The length of this segment was independently confirmed to be around 91m.
*/
private Way getWayWithLength
() {
Way el =
new Way
(1);
el.
addPoint(new Coord
(51.6124376, -
0.1777185));
el.
addPoint(new Coord
(51.6127816, -
0.1775029));
el.
addPoint(new Coord
(51.6132048, -
0.1772467));
return el
;
}
/**
* Get a way with a few points for testing the closed and complete flag.
* @param closed way should be closed
* @param complete way should not be complete
*/
private Way getWayForClosedCompleteCheck
(boolean closed,
boolean complete
) {
Way el =
new Way
(1);
el.
addTag("A",
"B");
el.
addPoint(new Coord
(1000,
1000));
el.
addPoint(new Coord
(1000,
2000));
el.
addPoint(new Coord
(2000,
2000));
el.
addPoint(new Coord
(2000,
1000));
if (closed
)
el.
addPoint(el.
getFirstPoint());
el.
setComplete(complete
);
el.
setClosedInOSM(true);
return el
;
}
/**
* Resolve the rule set with the given element and get the first
* resolved type.
*/
private GType getFirstType
(Rule rs,
Element el
) {
final List<GType
> types =
new ArrayList<>();
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);
}
}