/*
* 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.Reader;
import java.io.StringReader;
import java.util.List;
import java.util.Map;
import uk.me.parabola.mkgmap.osmstyle.actions.Action;
import uk.me.parabola.mkgmap.osmstyle.actions.ActionReader;
import uk.me.parabola.mkgmap.reader.osm.Element;
import uk.me.parabola.mkgmap.reader.osm.GeneralRelation;
import uk.me.parabola.mkgmap.reader.osm.Relation;
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 uk.me.parabola.mkgmap.scan.TokenScanner;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Test the possible actions that can appear in an action block.
* These are run before any rule is finally matched.
*/
public class ActionReaderTest {
@Test
public void testSimpleSet() {
List<Action> actions = readActionsFromString("{set park=yes}");
assertEquals("one action", 1, actions.size());
Element el = stdElementRun(actions);
assertEquals("park overwritten", "yes", el.getTag("park"));
}
@Test
public void testSimpleAdd() {
List<Action> actions = readActionsFromString("{add park=yes}");
assertEquals("one action", 1, actions.size());
// add does not overwrite existing tags.
Element el = stdElementRun(actions);
assertEquals("park not overwritten", "no", el.getTag("park"));
}
@Test
public void testRename() {
List<Action> actions = readActionsFromString("{rename park landarea}");
assertEquals("one action", 1, actions.size());
Element el = stdElementRun(actions);
assertNull("park should be gone", el.getTag("park"));
assertEquals("park renamed", "no", el.getTag("landarea"));
}
/**
* Test with embedded comment, newlines, semicolon used as separator.
*/
@Test
public void testFreeForm() {
List<Action> actions = readActionsFromString(" { set web='world wide';" +
"set \nribbon = 'yellow' \n# a comment } ");
assertEquals("number of actions", 2, actions.size());
Element el = stdElementRun(actions);
assertEquals("park not overwritten", "no", el.getTag("park"));
assertEquals("word with spaces", "world wide", el.getTag("web"));
assertEquals("yellow ribbon", "yellow", el.getTag("ribbon"));
}
/**
* Test several commands in the block. They should all be executed.
*/
@Test
public void testMultipleCommands() {
List<Action> actions = readActionsFromString(
"{set park=yes; add fred=other;" +
"set pooh=bear}");
assertEquals("number of actions", 3, actions.size());
Element el = stdElementRun(actions);
assertEquals("park set to yes", "yes", el.getTag("park"));
assertEquals("fred set", "other", el.getTag("fred"));
assertEquals("pooh set", "bear", el.getTag("pooh"));
}
@Test(expected = SyntaxException.class)
public void testInvalidCommand() {
readActionsFromString("{bad }");
}
/**
* The name action set the element-name (not the 'name' tag).
* The first value to set it counts, later matches are ignored.
*/
@Test
public void testName() {
List<Action> actions = readActionsFromString("{name '${name} (${ref})' |" +
" '${ref}' | '${name}' ; }");
Element el = makeElement();
el.addTag("name", "Main St");
Rule rule = new ActionRule(null, actions);
rule.resolveType(el, TypeResult.NULL_RESULT);
assertEquals("just name", "Main St", el.getName());
}
/**
* Test with two name actions. This works just the same as having several
* name options on the same name command, in that it is still the
* first one to match that counts.
*/
@Test
public void testDoubleName() {
List<Action> actions = readActionsFromString("{name '${name} (${ref})' |" +
" '${ref}' | '${name}' ; " +
" name 'fred';}");
// Something that matches nothing in the first name command.
Element el = makeElement();
Rule rule = new ActionRule(null, actions);
rule.resolveType(el, TypeResult.NULL_RESULT);
assertEquals("no tags, second action matches", "fred", el.getName());
el = makeElement();
el.addTag("ref", "A1");
rule.resolveType(el, TypeResult.NULL_RESULT);
assertEquals("just a ref tag", "A1", el.getName());
el = makeElement();
el.addTag("ref", "A1");
el.addTag("name", "Main St");
rule.resolveType(el, TypeResult.NULL_RESULT);
assertEquals("ref and name", "Main St (A1)", el.getName());
}
/**
* The apply action works on the members of relations.
*/
@Test
public void testApplyAction() {
List<Action> actions = readActionsFromString("{apply {" +
"add route=bike;" +
"set foo=bar; }" +
"}\n");
Relation rel = makeRelation();
Rule rule = new ActionRule(null, actions);
rule.resolveType(rel, TypeResult.NULL_RESULT);
assertNull("Tag not set on relation", rel.getTag("route"));
// Will be set on all members as there is no role filter.
List<Map.Entry<String,Element>> elements = rel.getElements();
Element el1 = elements.get(0).getValue();
assertEquals("route tag added to first", "bike", el1.getTag("route"));
assertEquals("foo tag set to first", "bar", el1.getTag("foo"));
Element el2 = elements.get(1).getValue();
assertEquals("route tag added to second", "bike", el2.getTag("route"));
assertEquals("foo tag set to second", "bar", el2.getTag("foo"));
}
/**
* You can have a role filter, so that the actions are only applied
* to members with the given role.
*/
@Test
public void testApplyWithRole() {
List<Action> actions = readActionsFromString("{apply role=bar {" +
"add route=bike;" +
"set foo=bar; }}");
Relation rel = makeRelation();
Rule rule = new ActionRule(null, actions);
rule.resolveType(rel, TypeResult.NULL_RESULT);
List<Map.Entry<String,Element>> elements = rel.getElements();
Element el1 = elements.get(0).getValue();
assertEquals("route tag added to first", "bike", el1.getTag("route"));
assertEquals("foo tag set to first", "bar", el1.getTag("foo"));
// Wrong role, so not applied.
Element el2 = elements.get(1).getValue();
assertNull("route tag not added to second element (role=foo)", el2.getTag("route"));
assertNull("foo tag not set in second element (role=foo)", el2.getTag("foo"));
}
/**
* When an apply statement runs, then substitutions on the value use
* the tags of the relation and not of the sub element.
*/
@Test
public void testApplyWithSubst() {
List<Action> actions = readActionsFromString("{apply {" +
"add route='${route_no}';" +
"}}");
Relation rel = makeRelation();
rel.addTag("route_no", "66");
Element el1 = rel.getElements().get(0).getValue();
el1.addTag("route_no", "42");
Rule rule = new ActionRule(null, actions);
rule.resolveType(rel, TypeResult.NULL_RESULT);
assertEquals("route_no taken from relation tags", "66", el1.getTag("route"));
}
@Test
public void testEmptyActionList() {
List<Action> actions = readActionsFromString("{}");
assertEquals("no actions found", 0, actions.size());
}
@Test
public void testAlternatives() {
List<Action> actions = readActionsFromString(
"{set fred = '${park}' | 'default value'}");
Element el = makeElement();
Rule rule = new ActionRule(null, actions);
rule.resolveType(el, TypeResult.NULL_RESULT);
assertEquals("first alternative", "no", el.getTag("fred"));
}
@Test
public void testSecondAlternative() {
List<Action> actions = readActionsFromString(
"{set fred = '${notset}' | 'default value'}");
Element el = makeElement();
el.addTag("fred", "origvalue");
Rule rule = new ActionRule(null, actions);
rule.resolveType(el, TypeResult.NULL_RESULT);
assertEquals("second alternative", "default value", el.getTag("fred"));
}
private Element stdElementRun(List<Action> actions) {
Rule rule = new ActionRule(null, actions);
Element el = makeElement();
rule.resolveType(el, TypeResult.NULL_RESULT);
return el;
}
/**
* Make a standard element for the tests.
*/
private Element makeElement() {
Element el = new Way(0);
el.addTag("park", "no");
el.addTag("test", "1");
return el;
}
private Relation makeRelation() {
Relation rel = new GeneralRelation(23);
rel.addElement("bar", makeElement());
rel.addElement("foo", makeElement());
return rel;
}
/**
* Read a action list from a string.
*/
private List<Action> readActionsFromString(String in) {
Reader sr = new StringReader(in);
TokenScanner ts = new TokenScanner("string", sr);
ActionReader ar = new ActionReader(ts);
return ar.readActions().getList();
}
}