Completed
Push — develop ( 7d38f7...827c8b )
by Jace
02:04
created

SampleClass2   A

Complexity

Total Complexity 1

Size/Duplication

Total Lines 4
Duplicated Lines 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 1
c 3
b 0
f 0
dl 0
loc 4
rs 10
1
# pylint: disable=missing-docstring,no-self-use,no-member,misplaced-comparison-constant,expression-not-assigned
2
3
import logging
4
from unittest.mock import patch, Mock
5
6
import pytest
7
from expecter import expect
8
9
import yorm
10
from yorm import common
11
from yorm.decorators import attr
12
from yorm.types import Dictionary, List
13
from yorm.types import String, Integer
14
15
from . import strip
16
17
log = logging.getLogger(__name__)
18
19
20
# CLASSES ######################################################################
21
22
23
@attr(abc=Integer)
24
class SampleDictionary(Dictionary):
25
    """Sample dictionary container."""
26
27
28
@attr(var1=Integer)
29
@attr(var2=String)
30
class SampleDictionaryWithInitialization(Dictionary):
31
    """Sample dictionary container with initialization."""
32
33
    def __init__(self, var1, var2, var3):
34
        super().__init__()
35
        self.var1 = var1
36
        self.var2 = var2
37
        self.var3 = var3
38
39
40
@attr(all=String)
41
class StringList(List):
42
    """Sample list container."""
43
44
45
class UnknownList(List):
46
    """Sample list container."""
47
48
49
# TESTS ########################################################################
50
51
52
class TestDictionary:
53
    """Unit tests for the `Dictionary` container."""
54
55
    obj = {'abc': 123}
56
57
    class SampleClass:
58
59
        def __init__(self):
60
            self.abc = 42
61
62
    class SampleClass2:
63
64
        def __init__(self):
65
            self.unmapped = Mock()
66
67
    data_value = [
68
        (obj, obj),
69
        (None, {'abc': 0}),
70
        ("key=value", {'key': "value", 'abc': 0}),
71
        ("key=", {'key': "", 'abc': 0}),
72
        ("key", {'key': None, 'abc': 0}),
73
    ]
74
75
    value_data = [
76
        (obj, obj),
77
        (SampleClass(), {'abc': 42}),
78
        (SampleClass2(), {'abc': 0}),
79
        ([], {'abc': 0}),
80
    ]
81
82
    def setup_method(self, _):
83
        """Reset the class' mapped attributes before each test."""
84
        common.attrs[SampleDictionary] = {'abc': Integer}
85
86
    @pytest.mark.parametrize("data,value", data_value)
87
    def test_to_value(self, data, value):
88
        """Verify input data is converted to values."""
89
        assert value == SampleDictionary.to_value(data)
90
91
    @pytest.mark.parametrize("value,data", value_data)
92
    def test_to_data(self, value, data):
93
        """Verify values are converted to output data."""
94
        assert data == SampleDictionary.to_data(value)
95
96
    def test_not_implemented(self):
97
        """Verify `Dictionary` cannot be used directly."""
98
        with pytest.raises(NotImplementedError):
99
            Dictionary()
100
101
    def test_dict_as_object(self):
102
        """Verify a `Dictionary` can be used as an attribute."""
103
        dictionary = SampleDictionaryWithInitialization(1, 2, 3.0)
104
        value = {'var1': 1, 'var2': '2'}
105
        value2 = dictionary.to_value(dictionary)
106
        assert value == value2
107
        # keys are not accessible as attributes
108
        assert not hasattr(value2, 'var1')
109
        assert not hasattr(value2, 'var2')
110
        assert not hasattr(value2, 'var3')
111
112
    def test_unknown_attrributes_are_ignored(self):
113
        obj = SampleDictionary.create_default()
114
        obj.update_value({'key': "value", 'abc': 7}, auto_track=False)
115
        assert {'abc': 7} == obj
116
117
118
class TestList:
119
    """Unit tests for the `List` container."""
120
121
    obj = ["a", "b", "c"]
122
123
    data_value = [
124
        (obj, obj),
125
        (None, []),
126
        ([None], []),
127
        ("a b c", ["a", "b", "c"]),
128
        ("a,b,c", ["a", "b", "c"]),
129
        ("abc", ["abc"]),
130
        ("a\nb\nc", ["a", "b", "c"]),
131
        (4.2, ['4.2']),
132
    ]
133
134
    value_data = [
135
        (obj, obj),
136
        ([], [None]),
137
    ]
138
139
    @pytest.mark.parametrize("data,value", data_value)
140
    def test_to_value(self, data, value):
141
        """Verify input data is converted to values."""
142
        assert value == StringList.to_value(data)
143
144
    @pytest.mark.parametrize("value,data", value_data)
145
    def test_to_data(self, value, data):
146
        """Verify values are converted to output data."""
147
        assert data == StringList.to_data(value)
148
149
    def test_item_type(self):
150
        """Verify list item type can be determined."""
151
        assert String == StringList.item_type
152
153
    def test_item_type_none(self):
154
        """Verify list item type defaults to None."""
155
        assert None is UnknownList.item_type
156
157
    def test_not_implemented(self):
158
        """Verify `List` cannot be used directly."""
159
        with pytest.raises(NotImplementedError):
160
            List()
161
        with pytest.raises(NotImplementedError):
162
            UnknownList()
163
164
    def test_shortened_syntax(self):
165
        cls = List.of_type(Integer)
166
        expect(cls.__name__) == "IntegerList"
167
        expect(common.attrs[cls]) == {'all': Integer}
168
169
170
class TestExtensions:
171
    """Unit tests for extensions to the container classes."""
172
173
    class FindMixin:
174
175
        def find(self, value):
176
            for value2 in self:
177
                if value.lower() == value2.lower():
178
                    return value2
179
            return None
180
181
    @yorm.attr(a=yorm.types.String)
182
    class MyDictionary(Dictionary, FindMixin):
183
        pass
184
185
    @yorm.attr(all=yorm.types.String)
186
    class MyList(List, FindMixin):
187
        pass
188
189
    def test_converted_dict_keeps_type(self):
190
        my_dict = self.MyDictionary()
191
        my_dict['a'] = 1
192
        my_dict2 = self.MyDictionary.to_value(my_dict)
193
        assert 'a' == my_dict2.find('A')
194
        assert None is my_dict2.find('B')
195
196
    def test_converted_list_keeps_type(self):
197
        my_list = self.MyList()
198
        my_list.append('a')
199
        my_list2 = self.MyList.to_value(my_list)
200
        assert 'a' == my_list2.find('A')
201
        assert None is my_list2.find('B')
202
203
204
@patch('yorm.settings.fake', True)
205
class TestReservedNames:
206
207
    class MyObject:
208
209
        def __init__(self, items=None):
210
            self.items = items or []
211
212
        def __repr__(self):
213
            return "<my_object>"
214
215
    def test_list_named_items(self):
216
        my_object = self.MyObject()
217
        yorm.sync_object(my_object, "fake/path", {'items': StringList})
218
219
        log.info("Appending value to list of items...")
220
        my_object.items.append('foo')
221
222
        log.info("Checking object contents...")
223
        assert strip("""
224
        items:
225
        - foo
226
        """) == my_object.__mapper__.text
227
228
        log.info("Writing new file contents...")
229
        my_object.__mapper__.text = strip("""
230
        items:
231
        - bar
232
        """)
233
234
        log.info("Checking file contents...")
235
        assert ['bar'] == my_object.items
236