Removed unused TestConverter.
[philipp/winterrodeln/wrpylib.git] / tests / test_wrvalidators.py
index fcce7e2b4e1f29cfc8d417c55da5a6bd843bfc59..a61e8475eeb7a2ed491d89175c113752bcb35425 100644 (file)
-#!/usr/bin/python2.7
+#!/usr/bin/python3.4
 # -*- coding: iso-8859-15 -*-
 import collections
 import wrpylib.wrvalidators
 import formencode
-
-
-def test_NoneValidator():
-    v =  wrpylib.wrvalidators.NoneValidator(wrpylib.wrvalidators.Unicode())
-    assert v.to_python(u'') == None
-    assert v.from_python(None) == u''
-
-
-# test_NeinValidator
-
-
-# test_Unicode
-
-
-# test_UnicodeNone
-
-
-# test_Unsigned
-
-
-def test_UnsignedNone():
-    v = wrpylib.wrvalidators.UnsignedNone()
-    assert v.to_python(u'42') == 42
-    assert v.to_python(u'') == None
-    assert v.from_python(42) == u'42'
-    assert v.from_python(None) == u''
-
-
-# test_UnsignedNeinNone
-
-
-# test_Loop
-
-
-# test_DictValidator
-
-
-# test_GermanBoolNone
-
-
-def test_GermanTristateTuple():
-    v = wrpylib.wrvalidators.GermanTristateTuple()
-    assert v.to_python(u'') == (None, None)
-    assert v.to_python(u'Ja') == (True, False)
-    assert v.to_python(u'Nein') == (False, True)
-    assert v.to_python(u'Teilweise') == (True, True)
-    assert v.from_python((None, None)) == u''
-    assert v.from_python((False, True)) == u'Nein'
-    assert v.from_python((True, False)) == u'Ja'
-    assert v.from_python((True, True)) == u'Teilweise'
-
-
-def test_GermanTristateFloat():
-    v = wrpylib.wrvalidators.GermanTristateFloat()
-    assert v.to_python(u'') == None
-    assert v.to_python(u'Ja') == 1.0
-    assert v.to_python(u'Nein') == 0.0
-    assert v.to_python(u'Teilweise') == 0.5
-    assert v.from_python(None) == u''
-    assert v.from_python(0.0) == u'Nein'
-    assert v.from_python(1.0) == u'Ja'
-    assert v.from_python(0.5) == u'Teilweise'
-
-
-# test_ValueComment
-
-
-# test_SemicolonList
-
-
-def test_ValueCommentList():
-    v = wrpylib.wrvalidators.ValueCommentList()
-    assert v.to_python(u'abc') == [(u'abc', None)]
-    assert v.to_python(u'abc def') == [(u'abc def', None)]
-    assert v.to_python(u'value (comment)') == [(u'value', u'comment')]
-    assert v.to_python(u'value (comment)') == [(u'value', u'comment')]
-    assert v.to_python(u'value1 (comment); value2') == [(u'value1', u'comment'), (u'value2', None)]
-    assert v.to_python(u'value1 (comment1); value2; value3 (comment3)') == [(u'value1', u'comment1'), (u'value2', None), ('value3', 'comment3')]
-    assert v.to_python(u'value1 (comment1); [[link (linkcomment)]] (not easy)') == [(u'value1', u'comment1'), (u'[[link (linkcomment)]]', u'not easy')]
-
-
-# test_GenericDateTime
-
-
-# test_DateTimeNoSec
-
-
-# test_DateNone
-
-
-# test_Geo
-
-
-def test_GeoNone():
-    coord = u'47.076207 N 11.453553 E'
-    v = wrpylib.wrvalidators.GeoNone()
-    (lat, lon) = v.to_python(coord)
-    assert lat == 47.076207
-    assert lon == 11.453553
-    assert v.to_python(u'') == (None, None)
-
-    assert v.from_python((lat, lon)) == coord
-    assert v.from_python((None, None)) == u''
-
-
-# test_MultiGeo
-
-
-# test_AustrianPhoneNumber
-
-
-# test_AustrianPhoneNumberNone
-
-
-# test_AustrianPhoneNumberCommentLoop
-
-
-# test_GermanDifficulty
-
-
-# test_GermanAvalanches
-
-
-def test_GermanPublicTransport():
-    v = wrpylib.wrvalidators.GermanPublicTransport()
-    assert v.to_python(u'') is None
-    assert v.to_python(u'Sehr gut') == 1
-    assert v.to_python(u'Gut') == 2
-    assert v.to_python(u'Mittelmäßig') == 3
-    assert v.to_python(u'Schlecht') == 4
-    assert v.to_python(u'Nein') == 5
-    assert v.to_python(u'Ja') == 6
-
-    assert v.from_python(None) == u''
-    assert v.from_python(1) == u'Sehr gut'
-    assert v.from_python(2) == u'Gut'
-    assert v.from_python(3) == u'Mittelmäßig'
-    assert v.from_python(4) == u'Schlecht'
-    assert v.from_python(5) == u'Nein'
-    assert v.from_python(6) == u'Ja'
-    assert v.from_python(1l) == u'Sehr gut'
-
-
-# test_GermanTristateFloatComment
-
-
-# test_UnsignedCommentNone
-
-
-# test_GermanCachet
-
-
-# test_url
-
-
-def test_UrlNeinNone():
-    v = wrpylib.wrvalidators.UrlNeinNone()
-    assert v.to_python(u'') == None
-    assert v.to_python(u'Nein') == u'Nein'
-    assert v.to_python(u'http://www.höttingeralm.at') == u'http://www.höttingeralm.at'
-    assert v.from_python(None) == u''
-    assert v.from_python(u'Nein') == u'Nein'
-    assert v.from_python(u'http://www.höttingeralm.at') == u'http://www.höttingeralm.at'
-
-
-def test_ValueCommentListNeinLoopNone():
-    v = wrpylib.wrvalidators.ValueCommentListNeinLoopNone()
-    assert v.to_python(u'') == None
-    assert v.to_python(u'Nein') == u'Nein'
-    assert v.to_python(u'T-Mobile (gut); A1') == u'T-Mobile (gut); A1'
-    assert v.from_python(None) == u''
-    assert v.from_python(u'Nein') == u'Nein'
-    assert v.from_python(u'T-Mobile (gut); A1') == u'T-Mobile (gut); A1'
-
-
-# test_PhoneNumber
-    
-
-def test_PhoneCommentListNeinLoopNone():
-    v = wrpylib.wrvalidators.PhoneCommentListNeinLoopNone(comments_are_optional=True)
-    assert v.to_python(u'') == None
-    assert v.to_python(u'Nein') == u'Nein'
-    assert v.to_python(u'+43-699-1234567 (nicht nach 20:00 Uhr); +43-512-123456') == u'+43-699-1234567 (nicht nach 20:00 Uhr); +43-512-123456'
-    assert v.from_python(None) == u''
-    assert v.from_python(u'Nein') == u'Nein'
-    assert v.from_python(u'+43-699-1234567 (nicht nach 20:00 Uhr); +43-512-123456') == u'+43-699-1234567 (nicht nach 20:00 Uhr); +43-512-123456'
-
-
-def test_MaskedEmail():
-    v = wrpylib.wrvalidators.MaskedEmail()
-    assert v.to_python(u'') == (None, None)
-    assert v.to_python(u'abc.def@example.com') == (u'abc.def@example.com', False)
-    assert v.to_python(u'abc.def(at)example.com') == (u'abc.def@example.com', True)
-    assert v.from_python((None, None)) == u''
-    assert v.from_python((u'abc.def@example.com', False)) == u'abc.def@example.com'
-    assert v.from_python((u'abc.def@example.com', True)) == u'abc.def(at)example.com'
-
-
-def test_EmailCommentListNeinLoopNone():
-    v = wrpylib.wrvalidators.EmailCommentListNeinLoopNone()
-    assert v.to_python(u'') == None
-    assert v.to_python(u'Nein') == u'Nein'
-    assert v.to_python(u'first@example.com') == u'first@example.com'
-    assert v.to_python(u'first@example.com (Nur Winter); second@example.com') == u'first@example.com (Nur Winter); second@example.com'
-    assert v.from_python(None) == u''
-    assert v.from_python(u'Nein') == u'Nein'
-    assert v.from_python(u'first@example.com') == u'first@example.com'
-    assert v.from_python(u'first@example.com (Nur Winter); second@example.com') == u'first@example.com (Nur Winter); second@example.com'
-    testvalue = u'abc.def(at)example.com (comment)'
-    try:
-        v.to_python(testvalue)
-        assert False
-    except formencode.Invalid:
-        pass
-    try:
-        v.from_python(testvalue)
-        assert False
-    except formencode.Invalid:
-        pass
-    v = wrpylib.wrvalidators.EmailCommentListNeinLoopNone(allow_masked_email=True)
-    assert v.to_python(testvalue) == testvalue
-    assert v.from_python(testvalue) == testvalue
-
-
-# test_WikiPage
-
-
-# test_WikiPageList
-
-
-def test_WikiPageListLoopNone():
-    v = wrpylib.wrvalidators.WikiPageListLoopNone()
-    assert v.to_python(u'') == None
-    assert v.to_python(u'[[Birgitzer Alm]]; [[Kemater Alm]]') == u'[[Birgitzer Alm]]; [[Kemater Alm]]'
-    assert v.from_python(None) == u''
-    assert v.from_python(u'[[Birgitzer Alm]]; [[Kemater Alm]]') == u'[[Birgitzer Alm]]; [[Kemater Alm]]'
-
-
-# test_TupleSecondValidator
-
-
-def test_BoolUnicodeTupleValidator():
-    v = wrpylib.wrvalidators.BoolUnicodeTupleValidator()
-    assert v.to_python(u'') == (None, None)
-    assert v.to_python(u'Nein') == (False, None)
-    assert v.to_python(u'any text') == (True, u'any text')
-    assert v.from_python((None, None)) == u''
-    assert v.from_python((False, None)) == u'Nein'
-    assert v.from_python((True, u'any text')) == u'any text'
-
-
-
-
-def test_GermanLift():
-    v = wrpylib.wrvalidators.GermanLift()
-    assert v.to_python(u'') == (None, None)
-    assert v.to_python(u'Nein') == (False, None)
-    assert v.to_python(u'Sessellift (4 Euro)') == (True, u'Sessellift (4 Euro)')
-    assert v.from_python((None, None)) == u''
-    assert v.from_python((False, None)) == u'Nein'
-    assert v.from_python((True, u'Sessellift (4 Euro)')) == u'Sessellift (4 Euro)'
-
-
-def test_SledRental():
-    v = wrpylib.wrvalidators.SledRental()
-    assert v.to_python(u'') == (None, None)
-    assert v.to_python(u'Nein') == (False, None)
-    assert v.to_python(u'Ja') == (True, u'Ja')
-    assert v.to_python(u'Talstation (nur mit Ticket); Schneealm') == (True, u'Talstation (nur mit Ticket); Schneealm')
-    assert v.from_python((None, None)) == u''
-    assert v.from_python((False, None)) == u'Nein'
-    assert v.from_python((True, u'Talstation (nur mit Ticket); Schneealm')) == u'Talstation (nur mit Ticket); Schneealm'
-    assert v.from_python((True, u'Ja')) == u'Ja'
-
-
-def test_RodelbahnboxDictValidator():
-    v = wrpylib.wrvalidators.RodelbahnboxDictValidator()
-    other = collections.OrderedDict([
-        (u'Position', u'47.309820 N 9.986508 E'),
-        (u'Position oben', u''),
-        (u'Höhe oben', u'1244'),
-        (u'Position unten', u''),
-        (u'Höhe unten', u'806'),
-        (u'Länge', u'5045'),
-        (u'Schwierigkeit', u''),
-        (u'Lawinen', u'gelegentlich'),
-        (u'Betreiber', u''),
-        (u'Öffentliche Anreise', u'Ja'),
-        (u'Aufstieg möglich', u'Ja'),
-        (u'Aufstieg getrennt', u'Nein'),
-        (u'Gehzeit', u'105'),
-        (u'Aufstiegshilfe', u'Nein'),
-        (u'Beleuchtungsanlage', u'Nein'),
-        (u'Beleuchtungstage', u''),
-        (u'Rodelverleih', u'Ja'),
-        (u'Gütesiegel', u''),
-        (u'Webauskunft', u''),
-        (u'Telefonauskunft', u'+43-664-1808482 (Bergkristallhütte)'),
-        (u'Bild', u'Rodelbahn Bergkristallhütte 2009-03-03.jpg'),
-        (u'In Übersichtskarte', u'Ja'),
-        (u'Forumid', u'72')])
-    python = v.to_python(other, None)
-    other2 = v.from_python(python, None)
-    assert other == other2
-
+import unittest
+from wrpylib.wrvalidators import *
+
+
+class TestInt(unittest.TestCase):
+    def test_from_str(self):
+        self.assertEqual(int_from_str('42'), 42)
+        self.assertEqual(int_from_str('+42'), 42)
+        self.assertEqual(int_from_str('-20'), -20)
+        self.assertEqual(int_from_str('0', min=0), 0)
+        self.assertEqual(int_from_str('10', max=10), 10)
+        with self.assertRaises(ValueError):
+            int_from_str('abc')
+        with self.assertRaises(ValueError):
+            int_from_str('')
+        with self.assertRaises(ValueError):
+            int_from_str('-1', min=0)
+        with self.assertRaises(ValueError):
+            int_from_str('11', max=10)
+        with self.assertRaises(ValueError):
+            int_from_str('10.0')
+        with self.assertRaises(ValueError):
+            int_from_str('0d')
+
+    def test_to_str(self):
+        self.assertEqual(int_to_str(20), '20')
+        self.assertEqual(int_to_str(-20), '-20')
+        self.assertEqual(int_to_str(0), '0')
+
+
+class TestOptInt(unittest.TestCase):
+    def test_from_str(self):
+        self.assertEqual(opt_int_from_str('42'), 42)
+        self.assertEqual(opt_int_from_str('+42'), 42)
+        self.assertEqual(opt_int_from_str('-20'), -20)
+        self.assertEqual(opt_int_from_str(''), None)
+        with self.assertRaises(ValueError):
+            opt_int_from_str('abc')
+        with self.assertRaises(ValueError):
+            opt_int_from_str('10.0')
+        with self.assertRaises(ValueError):
+            opt_int_from_str('0d')
+
+    def test_to_str(self):
+        self.assertEqual(opt_int_to_str(20), '20')
+        self.assertEqual(opt_int_to_str(-20), '-20')
+        self.assertEqual(opt_int_to_str(0), '0')
+        self.assertEqual(opt_int_to_str(None), '')
+
+
+class TestEnumConverter(unittest.TestCase):
+    def test_from_str(self):
+        self.assertEqual(enum_from_str(''), [])
+        self.assertEqual(enum_from_str('abc'), ['abc'])
+        self.assertEqual(enum_from_str('abc; def'), ['abc', 'def'])
+        self.assertEqual(enum_from_str('abc; def;ghi'), ['abc', 'def', 'ghi'])
+
+    def test_to_str(self):
+        self.assertEqual(enum_to_str(['abc', 'def', 'ghi']), 'abc; def; ghi')
+        self.assertEqual(enum_to_str(['abc']), 'abc')
+        self.assertEqual(enum_to_str(['']), '')
+        self.assertEqual(enum_to_str([]), '')
+
+
+class TestDateTime(unittest.TestCase):
+    def test_from_str(self):
+        self.assertEqual(DateTime.from_str('2015-12-31 23:07:42'), datetime.datetime(2015, 12, 31, 23, 7, 42))
+
+    def test_to_str(self):
+        self.assertEqual(DateTime.to_str(datetime.datetime(2015, 12, 31, 23, 7, 42)), '2015-12-31 23:07:42')
+
+
+class TestDifficultyGerman(unittest.TestCase):
+    def test_from_str(self):
+        self.assertEqual(difficulty_german_from_str('leicht'), 1)
+        self.assertEqual(difficulty_german_from_str('mittel'), 2)
+        with self.assertRaises(ValueError):
+            difficulty_german_from_str('dontknow')
+        with self.assertRaises(ValueError):
+            difficulty_german_from_str('')
+
+    def test_to_str(self):
+        self.assertEqual(difficulty_german_to_str(1), 'leicht')
+
+
+class TestTristateGerman(unittest.TestCase):
+    def test_from_str(self):
+        self.assertEqual(tristate_german_from_str('Ja'), 1.0)
+        self.assertEqual(tristate_german_from_str('Teilweise'), 0.5)
+        self.assertEqual(tristate_german_from_str('Nein'), 0)
+        with self.assertRaises(ValueError):
+            tristate_german_from_str('')
+        with self.assertRaises(ValueError):
+            tristate_german_from_str('Vielleicht')
+
+    def test_to_str(self):
+        self.assertEqual(tristate_german_to_str(1.0), 'Ja')
+        self.assertEqual(tristate_german_to_str(0.5), 'Teilweise')
+        self.assertEqual(tristate_german_to_str(0.0), 'Nein')
+
+
+class TestOptTristateGerman(unittest.TestCase):
+    def test_from_str(self):
+        self.assertEqual(opt_tristate_german_from_str('Ja'), 1.0)
+        self.assertEqual(opt_tristate_german_from_str('Teilweise'), 0.5)
+        self.assertEqual(opt_tristate_german_from_str('Nein'), 0)
+        self.assertEqual(opt_tristate_german_from_str(''), None)
+        with self.assertRaises(ValueError):
+            opt_tristate_german_from_str('Vielleicht')
+
+    def test_to_str(self):
+        self.assertEqual(opt_tristate_german_to_str(1.0), 'Ja')
+        self.assertEqual(opt_tristate_german_to_str(0.5), 'Teilweise')
+        self.assertEqual(opt_tristate_german_to_str(0.0), 'Nein')
+        self.assertEqual(opt_tristate_german_to_str(None), '')
+
+
+class TestOptTristateGermanComment(unittest.TestCase):
+    def test_from_str(self):
+        self.assertEqual(opt_tristate_german_comment_from_str('Ja'), (1.0, None))
+        self.assertEqual(opt_tristate_german_comment_from_str('Teilweise'), (0.5, None))
+        self.assertEqual(opt_tristate_german_comment_from_str('Nein'), (0, None))
+        self.assertEqual(opt_tristate_german_comment_from_str('Teilweise (nur ganz oben nicht)'), (0.5, 'nur ganz oben nicht'))
+        self.assertEqual(opt_tristate_german_comment_from_str(''), (None, None))
+        with self.assertRaises(ValueError):
+            opt_tristate_german_from_str('Vielleicht')
+        with self.assertRaises(ValueError):
+            opt_tristate_german_from_str('(Ja)')
+
+    def test_to_str(self):
+        self.assertEqual(opt_tristate_german_comment_to_str((1.0, None)), 'Ja')
+        self.assertEqual(opt_tristate_german_comment_to_str((0.5, None)), 'Teilweise')
+        self.assertEqual(opt_tristate_german_comment_to_str((0.0, None)), 'Nein')
+        self.assertEqual(opt_tristate_german_comment_to_str((None, None)), '')
+
+
+class TestLonLat(unittest.TestCase):
+    def test_from_str(self):
+        self.assertEqual(lonlat_from_str('47.076207 N 11.453553 E'), LonLat(11.453553, 47.076207))
+        with self.assertRaises(ValueError):
+            lonlat_from_str('47.076207 N 11.453553')
+
+    def test_to_str(self):
+        self.assertEqual(lonlat_to_str(LonLat(11.453553, 47.076207)), '47.076207 N 11.453553 E')
+
+
+class TestValueCommentConverter(unittest.TestCase):
+    def test_from_str(self):
+        self.assertEqual(value_comment_from_str('abc (defg)'), ('abc', 'defg'))
+        self.assertEqual(value_comment_from_str('abc ()'), ('abc', ''))
+        self.assertEqual(value_comment_from_str('(def)'), ('', 'def'))
+        self.assertEqual(value_comment_from_str('ab((cd))'), ('ab', '(cd)'))
+        self.assertEqual(value_comment_from_str('ab((c(d)[(])))'), ('ab', '(c(d)[(]))'))
+        self.assertEqual(value_comment_from_str('ab((cd)'), ('ab(', 'cd'))
+        self.assertEqual(value_comment_from_str('abcd  (ef) '), ('abcd', 'ef'))
+        self.assertEqual(value_comment_from_str('abc', comment_optional=True), ('abc', ''))
+        with self.assertRaises(ValueError):
+            value_comment_from_str('abc (')
+        with self.assertRaises(ValueError):
+            value_comment_from_str('abc )')
+        with self.assertRaises(ValueError):
+            value_comment_from_str('abc (def)g')
+        with self.assertRaises(ValueError):
+            value_comment_from_str('abc (b))')
+        with self.assertRaises(ValueError):
+            value_comment_from_str('abc')
+
+    def test_to_str(self):
+        self.assertEqual(value_comment_to_str(('abc', 'defg')), 'abc (defg)')
+        self.assertEqual(value_comment_to_str(('abc', '')), 'abc ()')
+        self.assertEqual(value_comment_to_str(('', 'def')), '(def)')
+        self.assertEqual(value_comment_to_str(('ab', '(cd)')), 'ab ((cd))')
+        self.assertEqual(value_comment_to_str(('ab', '(c(d)[(]))')), 'ab ((c(d)[(])))')
+        self.assertEqual(value_comment_to_str(('ab(', 'cd')), 'ab( (cd)')
+        self.assertEqual(value_comment_to_str(('abcd', 'ef')), 'abcd (ef)')
+        self.assertEqual(value_comment_to_str(('abc', ''), comment_optional=True), 'abc')
+
+
+class TestNoGermanConverter(unittest.TestCase):
+    def test_from_str(self):
+        self.assertEqual(no_german_from_str('abc'), (True, 'abc'))
+        self.assertEqual(no_german_from_str('Nein'), (False, None))
+        with self.assertRaises(ValueError):
+            no_german_from_str('')
+
+    def test_to_str(self):
+        self.assertEqual(no_german_to_str((True, 'abc')), 'abc')
+        self.assertEqual(no_german_to_str((False, None)), 'Nein')
+
+
+class TestOptNoGerman(unittest.TestCase):
+    def test_from_str(self):
+        self.assertEqual(opt_no_german_from_str('abc'), (True, 'abc'))
+        self.assertEqual(opt_no_german_from_str('Nein'), (False, None))
+        self.assertEqual(opt_no_german_from_str(''), (None, None))
+
+    def test_to_str(self):
+        self.assertEqual(opt_no_german_to_str((True, 'abc')), 'abc')
+        self.assertEqual(opt_no_german_to_str((False, None)), 'Nein')
+        self.assertEqual(opt_no_german_to_str((None, None)), '')
+
+
+class TestLiftGermanValidator(unittest.TestCase):
+    def test_from_str(self):
+        self.assertEqual(lift_german_from_str(''), None)
+        self.assertEqual(lift_german_from_str('Nein'), [])
+        self.assertEqual(lift_german_from_str('Sessellift'), [('Sessellift', None)])
+        self.assertEqual(lift_german_from_str('Gondel (nur bis zur Hälfte)'), [('Gondel', 'nur bis zur Hälfte')])
+        self.assertEqual(lift_german_from_str('Sessellift; Taxi'), [('Sessellift', None), ('Taxi', None)])
+        self.assertEqual(lift_german_from_str('Sessellift (Wochenende); Taxi (6 Euro)'), [('Sessellift', 'Wochenende'), ('Taxi', '6 Euro')])
+
+    def test_to_str(self):
+        self.assertEqual(lift_german_to_str(None), '')
+        self.assertEqual(lift_german_to_str([]), 'Nein')
+        self.assertEqual(lift_german_to_str([('Sessellift', None)]), 'Sessellift')
+        self.assertEqual(lift_german_to_str([('Gondel', 'nur bis zur Hälfte')]), 'Gondel (nur bis zur Hälfte)')
+        self.assertEqual(lift_german_to_str([('Sessellift', None), ('Taxi', None)]), 'Sessellift; Taxi')
+        self.assertEqual(lift_german_to_str([('Sessellift', 'Wochenende'), ('Taxi', '6 Euro')]), 'Sessellift (Wochenende); Taxi (6 Euro)')
+
+
+class TestNightLightDays(unittest.TestCase):
+    def test_from_str(self):
+        self.assertEqual(nightlightdays_from_str(''), (None, None))
+        self.assertEqual(nightlightdays_from_str('2 (Mo, Di)'), (2, 'Mo, Di'))
+        self.assertEqual(nightlightdays_from_str('7'), (7, None))
+        self.assertEqual(nightlightdays_from_str('0'), (0, None))
+        self.assertEqual(nightlightdays_from_str('(keine Ahnung)'), (None, 'keine Ahnung'))
+        with self.assertRaises(ValueError):
+            nightlightdays_from_str('8')
+        with self.assertRaises(ValueError):
+            nightlightdays_from_str('5 (Montag')
+        with self.assertRaises(ValueError):
+            nightlightdays_from_str('5.2')
+
+    def test_to_str(self):
+        self.assertEqual(nightlightdays_to_str((None, None)), '')
+        self.assertEqual(nightlightdays_to_str((2, 'Mo, Di')), '2 (Mo, Di)')
+        self.assertEqual(nightlightdays_to_str((7, None)), '7')
+        self.assertEqual(nightlightdays_to_str((0, None)), '0')
+
+
+class TestSledRental(unittest.TestCase):
+    def test_from_str(self):
+        self.assertEqual(sledrental_from_str(''), None)
+        self.assertEqual(sledrental_from_str('Nein'), [])
+        self.assertEqual(sledrental_from_str('Talstation'), [('Talstation', None)])
+        self.assertEqual(sledrental_from_str('Talstation (unten)'), [('Talstation', 'unten')])
+        self.assertEqual(sledrental_from_str('Talstation (unten); Mittelstation'), [('Talstation', 'unten'), ('Mittelstation', None)])
+        with self.assertRaises(ValueError):
+            sledrental_from_str('(unten)')
+        with self.assertRaises(ValueError):
+            sledrental_from_str('Talstation (unten); ; Mittelstation')
+
+    def test_to_str(self):
+        self.assertEqual(sledrental_to_str(None), '')
+        self.assertEqual(sledrental_to_str([]), 'Nein')
+        self.assertEqual(sledrental_to_str([('Talstation', None)]), 'Talstation')
+        self.assertEqual(sledrental_to_str([('Talstation', 'unten')]), 'Talstation (unten)')
+        self.assertEqual(sledrental_to_str([('Talstation', 'unten'), ('Mittelstation', None)]), 'Talstation (unten); Mittelstation')
+
+
+class TestSingleCachet(unittest.TestCase):
+    def test_from_str(self):
+        self.assertEqual(single_cachet_german_from_str('Tiroler Naturrodelbahn-Gütesiegel 2009 mittel'), ('Tiroler Naturrodelbahn-Gütesiegel', '2009', 'mittel'))
+        self.assertEqual(single_cachet_german_from_str('Tiroler Naturrodelbahn-Gütesiegel 2013 schwer'), ('Tiroler Naturrodelbahn-Gütesiegel', '2013', 'schwer'))
+        with self.assertRaises(ValueError):
+            single_cachet_german_from_str('')
+        with self.assertRaises(ValueError):
+            single_cachet_german_from_str('Salzburger Naturrodelbahn-Gütesiegel 2013 schwer')
+        with self.assertRaises(ValueError):
+            single_cachet_german_from_str('Tiroler Naturrodelbahn-Gütesiegel 4013 schwer')
+        with self.assertRaises(ValueError):
+            single_cachet_german_from_str('Tiroler Naturrodelbahn-Gütesiegel 13 schwer')
+        with self.assertRaises(ValueError):
+            single_cachet_german_from_str('Tiroler Naturrodelbahn-Gütesiegel 2013 schwerer')
+
+    def test_to_str(self):
+        self.assertEqual(single_cachet_german_to_str(('Tiroler Naturrodelbahn-Gütesiegel', '2009', 'mittel')), 'Tiroler Naturrodelbahn-Gütesiegel 2009 mittel')
+        self.assertEqual(single_cachet_german_to_str(('Tiroler Naturrodelbahn-Gütesiegel', '2013', 'schwer')), 'Tiroler Naturrodelbahn-Gütesiegel 2013 schwer')
+
+
+class TestCachetGerman(unittest.TestCase):
+    def test_from_str(self):
+        self.assertEqual(cachet_german_from_str(''), None)
+        self.assertEqual(cachet_german_from_str('Nein'), [])
+        self.assertEqual(cachet_german_from_str('Tiroler Naturrodelbahn-Gütesiegel 2009 mittel'), [('Tiroler Naturrodelbahn-Gütesiegel', '2009', 'mittel')])
+        self.assertEqual(cachet_german_from_str('Tiroler Naturrodelbahn-Gütesiegel 2013 schwer; Tiroler Naturrodelbahn-Gütesiegel 2009 mittel'),
+            [('Tiroler Naturrodelbahn-Gütesiegel', '2013', 'schwer'), ('Tiroler Naturrodelbahn-Gütesiegel', '2009', 'mittel')])
+        with self.assertRaises(ValueError):
+            cachet_german_from_str('Ja')
+        with self.assertRaises(ValueError):
+            cachet_german_from_str('Tiroler Naturrodelbahn-Gütesiegel 2013 schwer Tiroler Naturrodelbahn-Gütesiegel 2009 mittel')
+
+    def test_to_str(self):
+        self.assertEqual(cachet_german_to_str(None), '')
+        self.assertEqual(cachet_german_to_str([]), 'Nein')
+        self.assertEqual(cachet_german_to_str([('Tiroler Naturrodelbahn-Gütesiegel', '2009', 'mittel')]), 'Tiroler Naturrodelbahn-Gütesiegel 2009 mittel')
+        self.assertEqual(cachet_german_to_str([('Tiroler Naturrodelbahn-Gütesiegel', '2013', 'schwer'), ('Tiroler Naturrodelbahn-Gütesiegel', '2009', 'mittel')]),
+            'Tiroler Naturrodelbahn-Gütesiegel 2013 schwer; Tiroler Naturrodelbahn-Gütesiegel 2009 mittel')
+
+
+class TestUrl(unittest.TestCase):
+    def test_from_str(self):
+        self.assertEqual(url_from_str('http://www.winterrodeln.org/wiki/Arzler_Alm/'), 'http://www.winterrodeln.org/wiki/Arzler_Alm/')
+        self.assertEqual(url_from_str('http://www.winterrodeln.org/wiki/Nösslachhütte/'), 'http://www.winterrodeln.org/wiki/Nösslachhütte/')
+        self.assertEqual(url_from_str('https://www.winterrodeln.org/wiki/Nösslachhütte/'), 'https://www.winterrodeln.org/wiki/Nösslachhütte/')
+        with self.assertRaises(ValueError):
+            url_from_str('mailto:office@example.com')
+        with self.assertRaises(ValueError):
+            url_from_str('/wiki/Arzler_Alm/')
+
+    def test_to_str(self):
+        self.assertEqual(url_to_str('http://www.winterrodeln.org/wiki/Arzler_Alm/'), 'http://www.winterrodeln.org/wiki/Arzler_Alm/')
+        self.assertEqual(url_to_str('http://www.winterrodeln.org/wiki/Nösslachhütte/'), 'http://www.winterrodeln.org/wiki/Nösslachhütte/')
+        self.assertEqual(url_to_str('https://www.winterrodeln.org/wiki/Nösslachhütte/'), 'https://www.winterrodeln.org/wiki/Nösslachhütte/')
+
+
+class TestWebauskunft(unittest.TestCase):
+    def test_from_str(self):
+        self.assertEqual(webauskunft_from_str('http://www.example.com/current'), (True, 'http://www.example.com/current'))
+        self.assertEqual(webauskunft_from_str(''), (None, None))
+        self.assertEqual(webauskunft_from_str('Nein'), (False, None))
+
+    def test_to_str(self):
+        self.assertEqual(webauskunft_to_str((True, 'http://www.example.com/current')), 'http://www.example.com/current')
+        self.assertEqual(webauskunft_to_str((None, None)), '')
+        self.assertEqual(webauskunft_to_str((False, None)), 'Nein')
+
+
+class TestPhoneNumber(unittest.TestCase):
+    def test_from_str(self):
+        self.assertEqual(phone_number_from_str('+43-699-123456789'), '+43-699-123456789')
+        self.assertEqual(phone_number_from_str('+43-69945'), '+43-69945')
+        self.assertEqual(phone_number_from_str('+43-512-507-6418'), '+43-512-507-6418')
+        with self.assertRaises(ValueError):
+            phone_number_from_str('+43-')
+        with self.assertRaises(ValueError):
+            phone_number_from_str('0512123456789')
+
+    def test_to_str(self):
+        self.assertEqual(phone_number_to_str('+43-699-123456789'), '+43-699-123456789')
+        self.assertEqual(phone_number_to_str('+43-69945'), '+43-69945')
+        self.assertEqual(phone_number_to_str('+43-512-507-6418'), '+43-512-507-6418')
+
+
+class TestTelefonauskunft(unittest.TestCase):
+    def test_from_str(self):
+        self.assertEqual(telefonauskunft_from_str(''), None)
+        self.assertEqual(telefonauskunft_from_str('Nein'), [])
+        self.assertEqual(telefonauskunft_from_str('+43-512-123456 (untertags)'), [('+43-512-123456', 'untertags')])
+        self.assertEqual(telefonauskunft_from_str('+43-512-1234 (untertags); +43-664-123456 (Alm)'), [('+43-512-1234', 'untertags'), ('+43-664-123456', 'Alm')])
+        with self.assertRaises(ValueError):
+            telefonauskunft_from_str('+43-512-123456+ (untertags)')
+        with self.assertRaises(ValueError):
+            telefonauskunft_from_str('+43-512-123456')
+
+    def test_to_str(self):
+        self.assertEqual(telefonauskunft_to_str(None), '')
+        self.assertEqual(telefonauskunft_to_str([]), 'Nein')
+        self.assertEqual(telefonauskunft_to_str([('+43-512-123456', 'untertags')]), '+43-512-123456 (untertags)')
+        self.assertEqual(telefonauskunft_to_str([('+43-512-1234', 'untertags'), ('+43-664-123456', 'Alm')]), '+43-512-1234 (untertags); +43-664-123456 (Alm)')
+
+
+class TestEmail(unittest.TestCase):
+    def setUp(self):
+        self.good_addresses = ['office@example.com', 'winter+rodeln@localhost', 'joe.doe@exämple.com']
+        self.bad_addresses = ['öffice@example.com', 'winter rodeln@localhost', 'www.winterrodeln.org', 'mailto:info@example.com', 'info@example.com.']
+
+    def test_from_str(self):
+        for value in self.good_addresses:
+            self.assertEqual(value, email_from_str(value))
+        for value in self.bad_addresses:
+            with self.assertRaises(ValueError):
+                email_from_str(value)
+
+    def test_to_str(self):
+        for value in self.good_addresses:
+            self.assertEqual(value, email_to_str(value))
+
+
+class TestMaskedEmail(unittest.TestCase):
+    def test_from_str(self):
+        self.assertEqual(('office@example.com', False), masked_email_from_str('office@example.com'))
+        self.assertEqual(('office@example.com', True), masked_email_from_str('office(at)example.com'))
+        with self.assertRaises(ValueError):
+            masked_email_from_str('office@example.com', masked_only=True)
+        with self.assertRaises(ValueError):
+            masked_email_from_str('off ice@example.com')
+
+    def test_to_str(self):
+        self.assertEqual('office@example.com', masked_email_to_str(('office@example.com', False)))
+        self.assertEqual('office(at)example.com', masked_email_to_str(('office@example.com', True)))
+        self.assertEqual('office()example.com', masked_email_to_str(('office@example.com', True), '()'))
+
+
+class TestBox(unittest.TestCase):
+    def test_from_str(self):
+        value = '{{MyTemplate|apple=2|banana=5}}'
+        converter_dict = OrderedDict([('apple', opt_int_converter), ('banana', opt_int_converter)])
+        result = box_from_str(value, 'MyTemplate', converter_dict)
+        self.assertEqual(result['apple'], 2)
+        self.assertEqual(result['banana'], 5)
+
+        value = '{{MyTemplate\n | apple = 2 \n| banana = 5 }}'
+        result = box_from_str(value, 'MyTemplate', converter_dict)
+        self.assertEqual(result['apple'], 2)
+        self.assertEqual(result['banana'], 5)
+
+        with self.assertRaises(ValueError):
+            box_from_str(value, 'myTemplate', converter_dict)
+        with self.assertRaises(ValueError):
+            value = '{{MyTemplate|apple=2|banana=five}}'
+            box_from_str(value, 'MyTemplate', converter_dict)
+        with self.assertRaises(ValueError):
+            value = '{{MyTemplate|apple=2}}'
+            box_from_str(value, 'MyTemplate', converter_dict)
+        with self.assertRaises(ValueError):
+            value = '{{MyTemplate|apple=2|banana=5|cherry=6}}'
+            box_from_str(value, 'MyTemplate', converter_dict)
+
+    def test_to_str(self):
+        value = OrderedDict([('apple', 2), ('banana', 5)])
+        converter_dict = OrderedDict([('apple', opt_int_converter), ('banana', opt_int_converter)])
+        result = box_to_str(value, 'MyTemplate', converter_dict)
+        self.assertEqual(result, '{{MyTemplate|apple=2|banana=5}}')
+
+
+class TestRodelbahnbox(unittest.TestCase):
+    def setUp(self):
+        self.maxDiff = None
+        self.value = \
+'''{{Rodelbahnbox
+| Position             = 46.807218 N 12.806522 E
+| Position oben        = 46.799014 N 12.818658 E
+| Höhe oben            = 1046
+| Position unten       =
+| Höhe unten           =
+| Länge                = 3500
+| Schwierigkeit        = mittel
+| Lawinen              = kaum
+| Betreiber            = Bringungsgemeinschaft Kreithof-Dolomitenhütte
+| Öffentliche Anreise  = Schlecht
+| Aufstieg möglich     = Ja
+| Aufstieg getrennt    = Teilweise
+| Gehzeit              = 75
+| Aufstiegshilfe       = Taxi; Sonstige (PKW bis Kreithof)
+| Beleuchtungsanlage   = Ja
+| Beleuchtungstage     = 7
+| Rodelverleih         = Nein
+| Gütesiegel           = Tiroler Naturrodelbahn-Gütesiegel 2009 mittel
+| Webauskunft          = http://www.lienzerdolomiten.info/at/tobogorpt.html
+| Telefonauskunft      = +43-664-2253782 (Dolomitenhütte)
+| Bild                 = Dolomitenrodelbahn Tristach 2011-12-22 oberer Bereich.jpg
+| In Übersichtskarte   = Ja
+| Forumid              = 139
+}}'''
+
+    def test_from_str(self):
+        value = rodelbahnbox_from_str(self.value)
+        self.assertEqual(value['Position'], LonLat(12.806522, 46.807218))
+        self.assertEqual(value['Position oben'], LonLat(12.818658, 46.799014))
+        self.assertEqual(value['Höhe oben'], 1046)
+        self.assertEqual(value['Position unten'], LonLat(None, None))
+        self.assertEqual(value['Höhe unten'], None)
+        self.assertEqual(value['Länge'], 3500)
+        self.assertEqual(value['Schwierigkeit'], 2)
+        self.assertEqual(value['Lawinen'], 1)
+        self.assertEqual(value['Betreiber'], 'Bringungsgemeinschaft Kreithof-Dolomitenhütte')
+        self.assertEqual(value['Öffentliche Anreise'], 4)
+        self.assertEqual(value['Aufstieg möglich'], True)
+        self.assertEqual(value['Aufstieg getrennt'], (0.5, None))
+        self.assertEqual(value['Gehzeit'], 75)
+        self.assertEqual(value['Aufstiegshilfe'], [('Taxi', None), ('Sonstige', 'PKW bis Kreithof')])
+        self.assertEqual(value['Beleuchtungsanlage'], (1.0, None))
+        self.assertEqual(value['Beleuchtungstage'], (7, None))
+        self.assertEqual(value['Rodelverleih'], [])
+        self.assertEqual(value['Gütesiegel'], [('Tiroler Naturrodelbahn-Gütesiegel', '2009', 'mittel')])
+        self.assertEqual(value['Webauskunft'], (True, 'http://www.lienzerdolomiten.info/at/tobogorpt.html'))
+        self.assertEqual(value['Telefonauskunft'], [('+43-664-2253782', 'Dolomitenhütte')])
+        self.assertEqual(value['Bild'], 'Dolomitenrodelbahn Tristach 2011-12-22 oberer Bereich.jpg')
+        self.assertEqual(value['In Übersichtskarte'], True)
+        self.assertEqual(value['Forumid'], 139)
+
+    def test_to_str(self):
+        value = OrderedDict([
+            ('Position', LonLat(12.806522, 46.807218)),
+            ('Position oben', LonLat(12.818658, 46.799014)),
+            ('Höhe oben', 1046),
+            ('Position unten', LonLat(None, None)),
+            ('Höhe unten', None),
+            ('Länge', 3500),
+            ('Schwierigkeit', 2),
+            ('Lawinen', 1),
+            ('Betreiber', 'Bringungsgemeinschaft Kreithof-Dolomitenhütte'),
+            ('Öffentliche Anreise', 4),
+            ('Aufstieg möglich', True),
+            ('Aufstieg getrennt', (0.5, None)),
+            ('Gehzeit', 75),
+            ('Aufstiegshilfe', [('Taxi', None), ('Sonstige', 'PKW bis Kreithof')]),
+            ('Beleuchtungsanlage', (1.0, None)),
+            ('Beleuchtungstage', (7, None)),
+            ('Rodelverleih', []),
+            ('Gütesiegel', [('Tiroler Naturrodelbahn-Gütesiegel', '2009', 'mittel')]),
+            ('Webauskunft', (True, 'http://www.lienzerdolomiten.info/at/tobogorpt.html')),
+            ('Telefonauskunft', [('+43-664-2253782', 'Dolomitenhütte')]),
+            ('Bild', 'Dolomitenrodelbahn Tristach 2011-12-22 oberer Bereich.jpg'),
+            ('In Übersichtskarte', True),
+            ('Forumid', 139)])
+        self.assertEqual(rodelbahnbox_to_str(value), self.value)
+
+
+class TestWrValidators(unittest.TestCase):
+    def test_ValueCommentListNeinLoopNone(self):
+        v = wrpylib.wrvalidators.ValueCommentListNeinLoopNone()
+        assert v.to_python('') == None
+        assert v.to_python('Nein') == 'Nein'
+        assert v.to_python('T-Mobile (gut); A1') == 'T-Mobile (gut); A1'
+        assert v.from_python(None) == ''
+        assert v.from_python('Nein') == 'Nein'
+        assert v.from_python('T-Mobile (gut); A1') == 'T-Mobile (gut); A1'
+
+
+    def test_PhoneCommentListNeinLoopNone(self):
+        v = wrpylib.wrvalidators.PhoneCommentListNeinLoopNone(comments_are_optional=True)
+        assert v.to_python('') == None
+        assert v.to_python('Nein') == 'Nein'
+        assert v.to_python('+43-699-1234567 (nicht nach 20:00 Uhr); +43-512-123456') == '+43-699-1234567 (nicht nach 20:00 Uhr); +43-512-123456'
+        assert v.from_python(None) == ''
+        assert v.from_python('Nein') == 'Nein'
+        assert v.from_python('+43-699-1234567 (nicht nach 20:00 Uhr); +43-512-123456') == '+43-699-1234567 (nicht nach 20:00 Uhr); +43-512-123456'
+
+
+    def test_MaskedEmail(self):
+        v = wrpylib.wrvalidators.MaskedEmail()
+        assert v.to_python('') == (None, None)
+        assert v.to_python('abc.def@example.com') == ('abc.def@example.com', False)
+        assert v.to_python('abc.def(at)example.com') == ('abc.def@example.com', True)
+        assert v.from_python((None, None)) == ''
+        assert v.from_python(('abc.def@example.com', False)) == 'abc.def@example.com'
+        assert v.from_python(('abc.def@example.com', True)) == 'abc.def(at)example.com'
+
+
+    def test_EmailCommentListNeinLoopNone(self):
+        v = wrpylib.wrvalidators.EmailCommentListNeinLoopNone()
+        assert v.to_python('') == None
+        assert v.to_python('Nein') == 'Nein'
+        assert v.to_python('first@example.com') == 'first@example.com'
+        assert v.to_python('first@example.com (Nur Winter); second@example.com') == 'first@example.com (Nur Winter); second@example.com'
+        assert v.from_python(None) == ''
+        assert v.from_python('Nein') == 'Nein'
+        assert v.from_python('first@example.com') == 'first@example.com'
+        assert v.from_python('first@example.com (Nur Winter); second@example.com') == 'first@example.com (Nur Winter); second@example.com'
+        testvalue = 'abc.def(at)example.com (comment)'
+        try:
+            v.to_python(testvalue)
+            assert False
+        except formencode.Invalid:
+            pass
+        try:
+            v.from_python(testvalue)
+            assert False
+        except formencode.Invalid:
+            pass
+        v = wrpylib.wrvalidators.EmailCommentListNeinLoopNone(allow_masked_email=True)
+        assert v.to_python(testvalue) == testvalue
+        assert v.from_python(testvalue) == testvalue
+
+
+    def test_WikiPageListLoopNone(self):
+        v = wrpylib.wrvalidators.WikiPageListLoopNone()
+        assert v.to_python('') == None
+        assert v.to_python('[[Birgitzer Alm]]; [[Kemater Alm]]') == '[[Birgitzer Alm]]; [[Kemater Alm]]'
+        assert v.from_python(None) == ''
+        assert v.from_python('[[Birgitzer Alm]]; [[Kemater Alm]]') == '[[Birgitzer Alm]]; [[Kemater Alm]]'
+
+
+    def test_GasthausboxDictValidator(self):
+        v = wrpylib.wrvalidators.GasthausboxDictValidator()
+        other = collections.OrderedDict([
+            ('Position', '47.295549 N 9.986970 E'),
+            ('Höhe', '1250'),
+            ('Betreiber', ''),
+            ('Sitzplätze', ''),
+            ('Übernachtung', ''),
+            ('Rauchfrei', 'Nein'),
+            ('Rodelverleih', ''),
+            ('Handyempfang', 'A1; T-Mobile/Telering'),
+            ('Homepage', 'http://www.bergkristallhuette.com/'),
+            ('E-Mail', 'bergkristallhuette@gmx.at'),
+            ('Telefon', '+43-664-1808482'),
+            ('Bild', 'Bergkritsallhütte 2009-02-07.JPG'),
+            ('Rodelbahnen', '[[Bergkristallhütte]]')])
+        python = v.to_python(other, None)
+        other2 = v.from_python(python, None)
+        assert other == other2