kothic/tests/testCondition.py

300 lines
14 KiB
Python

import re
import unittest
import sys
from pathlib import Path
# Add `src` directory to the import paths
sys.path.insert(0, str(Path(__file__).parent.parent / 'src'))
from mapcss import parseCondition
from mapcss.Condition import Condition
class ConditionTest(unittest.TestCase):
def test_parser_eq(self):
cond:Condition = parseCondition("natural=coastline")
self.assertEqual(cond.type, "eq")
self.assertEqual(cond.params, ("natural", "coastline"))
self.assertTrue(cond.test({'natural': 'coastline'}))
self.assertFalse(cond.test({'Natural': 'Coastline'}))
cond = parseCondition(" highway\t=\tprimary")
self.assertEqual(cond.type, "eq")
self.assertEqual(cond.params, ("highway", "primary"))
self.assertTrue(cond.test({'highway': 'primary'}))
self.assertFalse(cond.test({'highway': 'secondary'}))
cond = parseCondition(" admin_level = 3")
self.assertEqual(cond.type, "eq")
self.assertEqual(cond.params, ("admin_level", "3"))
self.assertTrue(cond.test({'admin_level': '3'}))
self.assertFalse(cond.test({'admin_level': '32'}))
cond = Condition('eq', ("::class", "::*"))
self.assertEqual(cond.type, "eq")
self.assertEqual(cond.params, ("::class", "::*"))
self.assertEqual(cond.extract_tag(), "*")
self.assertEqual(cond.test({'any_key': 'any_value'}), "::*")
self.assertTrue(cond.test({'any_key': 'any_value'}))
cond = Condition('eq', ("::class", "::int_name"))
self.assertEqual(cond.type, "eq")
self.assertEqual(cond.params, ("::class", "::int_name"))
self.assertEqual(cond.extract_tag(), "*")
self.assertEqual(cond.test({'any_key': 'any_value'}), "::int_name")
self.assertTrue(cond.test({'any_key': 'any_value'}))
def test_parser_regex(self):
""" Test conditions in format natural =~/water.+/
Note that such conditions are not used by Organic Maps styles.
"""
cond:Condition = parseCondition("natural =~/water.+/")
self.assertEqual(cond.type, "regex")
self.assertEqual(cond.params, ("natural", "water.+"))
self.assertEqual(type(cond.regex), re.Pattern)
self.assertTrue(cond.test({"natural": "waterway"}))
self.assertTrue(cond.test({"natural": "water123"}))
self.assertFalse(cond.test({"natural": "water"}))
self.assertFalse(cond.test({"natural": " waterway "}))
def test_parser_ge(self):
cond:Condition = parseCondition("population>=0")
self.assertEqual(cond.type, ">=")
self.assertEqual(cond.params, ("population", "0"))
self.assertTrue(cond.test({"population": "0"}))
self.assertTrue(cond.test({"population": "100000"}))
self.assertFalse(cond.test({"highway": "secondary"}))
self.assertFalse(cond.test({"population": "-1"}))
cond:Condition = parseCondition("population >= 150000")
self.assertEqual(cond.type, ">=")
self.assertEqual(cond.params, ("population", "150000"))
self.assertTrue(cond.test({"population": "150000"}))
self.assertTrue(cond.test({"population": "250000"}))
self.assertFalse(cond.test({"highway": "secondary"}))
self.assertFalse(cond.test({"population": "10000"}))
cond:Condition = parseCondition("\tbbox_area >= 4000000")
self.assertEqual(cond.type, ">=")
self.assertEqual(cond.params, ("bbox_area", "4000000"))
self.assertTrue(cond.test({"bbox_area": "4000000"}))
self.assertTrue(cond.test({"bbox_area": "8000000"}))
self.assertFalse(cond.test({"highway": "secondary"}))
self.assertFalse(cond.test({"bbox_area": "999"}))
def test_parser_gt(self):
""" Test conditions in format population > 100000
Note that such conditions are not used by Organic Maps styles.
"""
cond:Condition = parseCondition("population>0")
self.assertEqual(cond.type, ">")
self.assertEqual(cond.params, ("population", "0"))
self.assertTrue(cond.test({"population": "100"}))
self.assertFalse(cond.test({"population": "000"}))
self.assertFalse(cond.test({"highway": "secondary"}))
self.assertFalse(cond.test({"population": "-1"}))
cond:Condition = parseCondition("population > 150000")
self.assertEqual(cond.type, ">")
self.assertEqual(cond.params, ("population", "150000"))
self.assertTrue(cond.test({"population": "250000"}))
self.assertFalse(cond.test({"population": "150000"}))
self.assertFalse(cond.test({"highway": "secondary"}))
self.assertFalse(cond.test({"population": "10000"}))
cond:Condition = parseCondition("\tbbox_area > 4000000 ")
self.assertEqual(cond.type, ">")
self.assertEqual(cond.params, ("bbox_area", "4000000 ")) # TODO fix parser to exclude trailing space
self.assertTrue(cond.test({"bbox_area": "8000000"}))
self.assertFalse(cond.test({"bbox_area": "4000000"}))
self.assertFalse(cond.test({"highway": "secondary"}))
self.assertFalse(cond.test({"bbox_area": "999"}))
def test_parser_lt(self):
cond:Condition = parseCondition("population<40000")
self.assertEqual(cond.type, "<")
self.assertEqual(cond.params, ("population", "40000"))
self.assertTrue(cond.test({"population": "100"}))
self.assertTrue(cond.test({"population": "-1"}))
self.assertFalse(cond.test({"population": "40000"}))
self.assertFalse(cond.test({"highway": "secondary"}))
self.assertFalse(cond.test({"population": "500000"}))
cond:Condition = parseCondition("\tbbox_area < 4000000\n")
self.assertEqual(cond.type, "<")
self.assertEqual(cond.params, ("bbox_area", "4000000\n")) # TODO fix parser to exclude trailing \n
self.assertTrue(cond.test({"bbox_area": "100"}))
self.assertTrue(cond.test({"bbox_area": "-1"}))
self.assertTrue(cond.test({"bbox_area": "000"}))
self.assertFalse(cond.test({"highway": "secondary"}))
self.assertFalse(cond.test({"bbox_area": "4000000"}))
self.assertFalse(cond.test({"bbox_area": "8000000"}))
def test_parser_le(self):
""" Test conditions in format population <= 100000
Note that such conditions are not used by Organic Maps styles.
"""
cond:Condition = parseCondition("population<=40000")
self.assertEqual(cond.type, "<=")
self.assertEqual(cond.params, ("population", "40000"))
self.assertTrue(cond.test({"population": "100"}))
self.assertTrue(cond.test({"population": "-1"}))
self.assertTrue(cond.test({"population": "40000"}))
self.assertFalse(cond.test({"highway": "secondary"}))
self.assertFalse(cond.test({"population": "500000"}))
cond:Condition = parseCondition("\tbbox_area <= 4000000\n")
self.assertEqual(cond.type, "<=")
self.assertEqual(cond.params, ("bbox_area", "4000000\n")) # TODO fix parser to exclude trailing \n
self.assertTrue(cond.test({"bbox_area": "100"}))
self.assertTrue(cond.test({"bbox_area": "-1"}))
self.assertTrue(cond.test({"bbox_area": "000"}))
self.assertTrue(cond.test({"bbox_area": "4000000"}))
self.assertFalse(cond.test({"highway": "secondary"}))
self.assertFalse(cond.test({"bbox_area": "8000000"}))
def test_parser_ne(self):
cond:Condition = parseCondition("capital!=2")
self.assertEqual(cond.type, "ne")
self.assertEqual(cond.params, ("capital", "2"))
self.assertTrue(cond.test({"capital": "1"}))
self.assertTrue(cond.test({"capital": "22"}))
self.assertTrue(cond.test({"highway": "secondary"}))
self.assertFalse(cond.test({"capital": "2"}))
cond:Condition = parseCondition("\tcapital != 2")
self.assertEqual(cond.type, "ne")
self.assertEqual(cond.params, ("capital", "2"))
self.assertTrue(cond.test({"capital": "1"}))
self.assertTrue(cond.test({"capital": "22"}))
self.assertTrue(cond.test({"highway": "secondary"}))
self.assertFalse(cond.test({"capital": "2"}))
cond:Condition = parseCondition("garden:type != residential")
self.assertEqual(cond.type, "ne")
self.assertEqual(cond.params, ("garden:type", "residential"))
self.assertTrue(cond.test({"garden:type": "public"}))
self.assertTrue(cond.test({"garden:type": "res"}))
self.assertTrue(cond.test({"garden:type": "residential_plus"}))
self.assertTrue(cond.test({"highway": "secondary"}))
self.assertFalse(cond.test({"garden:type": "residential"}))
def test_parser_set(self):
cond:Condition = parseCondition("tunnel")
self.assertEqual(cond.type, "set")
self.assertEqual(cond.params, ("tunnel", ))
self.assertTrue(cond.test({"tunnel": "yes"}))
self.assertTrue(cond.test({"tunnel": "maybe"}))
self.assertTrue(cond.test({"tunnel": "+1"}))
self.assertFalse(cond.test({"highway": "secondary"}))
cond:Condition = parseCondition("building\t")
self.assertEqual(cond.type, "set")
self.assertEqual(cond.params, ("building", ))
self.assertTrue(cond.test({"building": "yes"}))
self.assertTrue(cond.test({"building": "apartment"}))
self.assertTrue(cond.test({"building": "1"}))
self.assertFalse(cond.test({"highway": "secondary"}))
self.assertFalse(cond.test({"building:part": "yes"}))
cond:Condition = parseCondition(" addr:housenumber ")
self.assertEqual(cond.type, "set")
self.assertEqual(cond.params, ("addr:housenumber", ))
self.assertTrue(cond.test({"addr:housenumber": "1"}))
self.assertTrue(cond.test({"addr:housenumber": "yes"}))
self.assertFalse(cond.test({"highway": "secondary"}))
self.assertFalse(cond.test({"addr:street": "Baker st"}))
cond:Condition = parseCondition(" some-tag ")
self.assertEqual(cond.type, "set")
self.assertEqual(cond.params, ("some-tag", ))
self.assertTrue(cond.test({"some-tag": "1"}))
self.assertTrue(cond.test({"some-tag": "yes"}))
self.assertFalse(cond.test({"highway": "secondary"}))
self.assertFalse(cond.test({"some": "tag"}))
def test_parser_unset(self):
cond:Condition = parseCondition("!tunnel")
self.assertEqual(cond.type, "unset")
self.assertEqual(cond.params, ("tunnel", ))
self.assertTrue(cond.test({"capital": "1"}))
self.assertFalse(cond.test({"tunnel": "yes"}))
self.assertFalse(cond.test({"tunnel": "no"}))
cond:Condition = parseCondition("\t!name ")
self.assertEqual(cond.type, "unset")
self.assertEqual(cond.params, ("name", ))
self.assertTrue(cond.test({"capital": "1"}))
self.assertTrue(cond.test({"int_name": "1"}))
self.assertFalse(cond.test({"name": "London"}))
def test_parser_false(self):
""" Test conditions in format some_tag = no
Note that such conditions are not used by Organic Maps styles.
"""
cond:Condition = parseCondition("access=no")
self.assertEqual(cond.type, "false")
self.assertEqual(cond.params, ("access", ))
#self.assertTrue(cond.test({"access": "no"})) # test is not implemented for `false` condition
#self.assertTrue(cond.test({"access": "private"})) # test is not implemented for `false` condition
self.assertFalse(cond.test({"tunnel": "yes"}))
def test_parser_invTrue(self):
""" Test conditions in format [!some_tag?] It works the same way as [some_tag != yes]
Note that such conditions are not used by Organic Maps styles.
"""
cond:Condition = parseCondition("!oneway?")
self.assertEqual(cond.type, "ne")
self.assertEqual(cond.params, ("oneway", "yes"))
self.assertTrue(cond.test({"oneway": "no"}))
self.assertTrue(cond.test({"oneway": "nobody_knows"}))
self.assertTrue(cond.test({"access": "private"}))
self.assertFalse(cond.test({"oneway": "yes"}))
cond:Condition = parseCondition("\t! intermittent ?\n")
self.assertEqual(cond.type, "ne")
self.assertEqual(cond.params, ("intermittent", "yes"))
self.assertTrue(cond.test({"intermittent": "no"}))
self.assertTrue(cond.test({"intermittent": "maybe"}))
self.assertTrue(cond.test({"access": "private"}))
self.assertFalse(cond.test({"intermittent": "yes"}))
def test_parser_true(self):
""" Test conditions in format [some_tag?] It works the same way as [some_tag = yes] """
cond:Condition = parseCondition("area?")
self.assertEqual(cond.type, "true")
self.assertEqual(cond.params, ("area", ))
self.assertTrue(cond.test({"area": "yes"}))
self.assertFalse(cond.test({"area": "no"}))
self.assertFalse(cond.test({"access": "private"}))
self.assertFalse(cond.test({"oneway": "nobody_knows"}))
cond:Condition = parseCondition("\tbridge ? ")
self.assertEqual(cond.type, "true")
self.assertEqual(cond.params, ("bridge", ))
self.assertTrue(cond.test({"bridge": "yes"}))
self.assertFalse(cond.test({"bridge": "no"}))
self.assertFalse(cond.test({"access": "private"}))
self.assertFalse(cond.test({"bridge": "maybe"}))
def test_untrue(self):
""" parseCondition(...) doesn't support this type of condition.
Not sure if it's ever used.
"""
cond:Condition = Condition("untrue", "access")
self.assertEqual(cond.type, "untrue")
self.assertEqual(cond.params, ("access", ))
self.assertTrue(cond.test({"access": "no"}))
self.assertFalse(cond.test({"access": "private"}))
self.assertFalse(cond.test({"oneway": "yes"}))
def test_parser_errors(self):
with self.assertRaises(Exception):
parseCondition("! tunnel")
with self.assertRaises(Exception):
""" Symbol '-' is only supported in simple 'set' rule. E.g. [key-with-dash]
But not in 'unset' rule [!key-with-dash] """
parseCondition("key-with-dash?")
if __name__ == '__main__':
unittest.main()