From e145d738527dc2793559f57216eba5db10eb4d0b Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 23 Jul 2015 23:53:00 +0200 Subject: [PATCH] configtypes: Add a MappingType base class. --- qutebrowser/config/configtypes.py | 62 ++++++++++++++++--------------- tests/config/test_configtypes.py | 56 ++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 29 deletions(-) diff --git a/qutebrowser/config/configtypes.py b/qutebrowser/config/configtypes.py index 9b6014167..5008ca83d 100644 --- a/qutebrowser/config/configtypes.py +++ b/qutebrowser/config/configtypes.py @@ -160,6 +160,31 @@ class BaseType: return out +class MappingType(BaseType): + + """Base class for any setting which has a mapping to the given values. + + Attributes: + MAPPING: The mapping to use. + """ + + MAPPING = {} + + def __init__(self, none_ok=False): + super().__init__(none_ok) + if list(sorted(self.MAPPING)) != list(sorted(self.valid_values)): + raise ValueError("Mapping {!r} doesn't match valid values " + "{!r}".format(self.MAPPING, self.valid_values)) + + def validate(self, value): + super().validate(value.lower()) + + def transform(self, value): + if not value: + return None + return self.MAPPING[value.lower()] + + class String(BaseType): """Base class for a string setting (case-insensitive). @@ -557,7 +582,7 @@ class Command(BaseType): return out -class ColorSystem(BaseType): +class ColorSystem(MappingType): """Color systems for interpolation.""" @@ -565,19 +590,11 @@ class ColorSystem(BaseType): ('hsv', "Interpolate in the HSV color system."), ('hsl', "Interpolate in the HSL color system.")) - def validate(self, value): - super().validate(value.lower()) - - def transform(self, value): - if not value: - return None - else: - mapping = { - 'rgb': QColor.Rgb, - 'hsv': QColor.Hsv, - 'hsl': QColor.Hsl, - } - return mapping[value.lower()] + MAPPING = { + 'rgb': QColor.Rgb, + 'hsv': QColor.Hsv, + 'hsl': QColor.Hsl, + } class QtColor(BaseType): @@ -1265,7 +1282,7 @@ class AutoSearch(BaseType): return False -class Position(BaseType): +class Position(MappingType): """The position of the tab bar.""" @@ -1278,14 +1295,6 @@ class Position(BaseType): 'east': QTabWidget.East, } - def validate(self, value): - super().validate(value.lower()) - - def transform(self, value): - if not value: - return None - return self.MAPPING[value.lower()] - class VerticalPosition(BaseType): @@ -1340,7 +1349,7 @@ class SessionName(BaseType): raise configexc.ValidationError(value, "may not start with '_'!") -class SelectOnRemove(BaseType): +class SelectOnRemove(MappingType): """Which tab to select when the focused tab is removed.""" @@ -1355,11 +1364,6 @@ class SelectOnRemove(BaseType): 'previous': QTabBar.SelectPreviousTab, } - def transform(self, value): - if not value: - return None - return self.MAPPING[value] - class LastClose(BaseType): diff --git a/tests/config/test_configtypes.py b/tests/config/test_configtypes.py index acc1277fe..66e7e8a8a 100644 --- a/tests/config/test_configtypes.py +++ b/tests/config/test_configtypes.py @@ -184,6 +184,62 @@ class TestBaseType: assert basetype.complete() == completions +class GoodMappingSubclass(configtypes.MappingType): + + """A MappingType we use in TestMappingType which is valid/good.""" + + valid_values = configtypes.ValidValues('one', 'two') + + MAPPING = { + 'one': 1, + 'two': 2, + } + + +class BadMappingSubclass(configtypes.MappingType): + + """A MappingType which is missing a value in MAPPING.""" + + valid_values = configtypes.ValidValues('one', 'two') + + MAPPING = { + 'one': 1, + } + + +class TestMappingType: + + """Test MappingType.""" + + TESTS = { + '': None, + 'one': 1, + 'two': 2, + 'ONE': 1, + } + + @pytest.fixture + def klass(self): + return GoodMappingSubclass + + @pytest.mark.parametrize('val', TESTS.keys()) + def test_validate_valid(self, klass, val): + klass(none_ok=True).validate(val) + + @pytest.mark.parametrize('val', ['', 'one!', 'blah']) + def test_validate_invalid(self, klass, val): + with pytest.raises(configexc.ValidationError): + klass().validate(val) + + @pytest.mark.parametrize('val, expected', TESTS.items()) + def test_transform(self, klass, val, expected): + assert klass().transform(val) == expected + + def test_bad_subclass_init(self): + with pytest.raises(ValueError): + BadMappingSubclass() + + class TestString: """Test String."""