Subversion Repositories mkgmap

Rev

Rev 4542 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * 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.BitSet;
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());
        }

        @Test
        public void testSetWithTagValue() {
                RuleSet rs = makeRuleSet("a=* {set b='${a}'}" +
                                "b=1 [0x10404 resolution 24]"
                );

                Way el = new Way(1);
                el.addTag("a", "1");

                List<GType> list = resolveList(rs, el);
                assertEquals("first element", 0x10404, list.get(0).getType());
        }

        @Test
        public void testSimulateIf() {
                RuleSet rs = makeRuleSet(       "boundary=administrative { set mkgmap:if:0001=1 }" +
                                "admin_level<3 & mkgmap:if:0001=1  [0x1e resolution 12]" +
                                "mkgmap:if:0001!=1 & admin_level<3 [0x1f resolution 14]"
                );

                Way el = new Way(1);
                el.addTag("boundary", "xyz"); // anything but administrative
                el.addTag("admin_level", "2");

                List<GType> list = resolveList(rs, el);
                assertEquals("number of elements", 1, list.size());
                assertEquals("first element", 0x1f, list.get(0).getType());
        }
       
        @Test
        public void testIndexWithOddOrder() {
                RuleSet rs = makeRuleSet("a=* {set b=1}" +
                                "b=1 {set c=1}" +
                                "d=2 {set c=2}" +
                                "c=* {set a=2}" +
                                "c=1 {set d=2}" +
                                "c=2 {set d=1}" +
                                "d=1 [0x10401 resolution 24]" +
                                "d=2 [0x10402 resolution 24]"
                );
               
                Way el = new Way(1);
                el.addTag("a", "1");
               
                BitSet candidates = rs.getRules(el);
                BitSet expected = new BitSet();
                expected.flip(0,8);
                expected.clear(2);
                expected.clear(5);
                expected.clear(6);
                assertEquals("candidates", expected, candidates);
                List<GType> list = resolveList(rs, el);
                assertEquals("first element", 0x10402, list.get(0).getType());
               
        }
       
        private List<GType> resolveList(RuleSet rs, Way el) {
                final List<GType> list = new ArrayList<>();
                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<>();
                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);
        }
}