• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ilotoki0804 / fieldenum / 11192435203

05 Oct 2024 07:10AM UTC coverage: 95.905% (+0.3%) from 95.652%
11192435203

push

github

ilotoki0804
Add test for BoundResult.exit and fix bug on it

13 of 13 new or added lines in 2 files covered. (100.0%)

5 existing lines in 1 file now uncovered.

1171 of 1221 relevant lines covered (95.9%)

0.96 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.19
/tests/test_fieldenum.py
1
# type: ignore
2

3
import pickle
1✔
4
from typing import Any, Self
1✔
5

6
import pytest
1✔
7
from fieldenum import Unit, Variant, factory, fieldenum, unreachable, variant
8

9

10
class ExceptionForTest(Exception):
1✔
11
    def __init__(self, value):
1✔
12
        self.value = value
1✔
13

14

15
@fieldenum
1✔
16
class Message:
1✔
17
    Quit = Unit
1✔
18
    Move = Variant(x=int, y=int).kw_only().default(x=234569834)
1✔
19
    ArgMove = Variant(x=int, y=int).default(x=234569834)
1✔
20
    FactoryTest = Variant(x=int, y=list, z=dict).default(y=factory(list), z=factory(lambda: {"hello": "world"}))
1✔
21
    Write = Variant(str)
1✔
22
    ChangeColor = Variant(int, int, int)
1✔
23
    Pause = Variant()
1✔
24

25
    @variant
1✔
26
    class ClassVariant:
1✔
27
        x: int
1✔
28
        y: "Message"
1✔
29
        z: str = "hello"
1✔
30

31
    @variant(kw_only=True)
1✔
32
    class KwOnlyClassVariant:
1✔
33
        x: int
1✔
34
        y: "Message"
1✔
35
        z: str = "hello"
1✔
36

37
    @variant
1✔
38
    def FunctionVariantWithBody(self, a: int, b, /, c: str, d, *, f: float = 3.4, e: dict | None = None, raise_it: bool = False):
1✔
39
        if raise_it:
1✔
40
            raise ExceptionForTest((self, a, b, c, d, f, e))
1✔
41

42
    @variant
1✔
43
    def FunctionVariant(a: int, b, /, c: str, d, *, f: float = factory(float), e: dict | None = factory(lambda: {"hello": "good"})):
1✔
UNCOV
44
        raise ExceptionForTest((a, b, c, d, f, e))
×
45

46
    @variant
1✔
47
    def NotNoneReturnFunctionVariant(self, a: int, b, /, c: str, d, *, f: float = factory(float), e: dict | None = factory(lambda: {"hello": "good"})):
1✔
48
        return 123
1✔
49

50
    @variant
1✔
51
    def ArgsOnlyFuncVariant(a, b, /, c, d):
1✔
UNCOV
52
        pass
×
53

54
    @variant
1✔
55
    def KwargsOnlyFuncVariant(*, a, b, c, d=None):
1✔
UNCOV
56
        pass
×
57

58
    @variant
1✔
59
    def ParamlessFuncVariantWithBody(self):
1✔
60
        pass
1✔
61

62

63
def test_class_variant():
1✔
64
    variant = Message.ClassVariant(123, Message.Quit)
1✔
65
    assert variant.dump() == dict(x=123, y=Message.Quit, z="hello")
1✔
66

67
    variant = Message.ClassVariant(123, y=Message.ArgMove(y=34))
1✔
68
    assert variant.dump() == dict(x=123, y=Message.ArgMove(x=234569834, y=34), z="hello")
1✔
69

70
    variant = Message.KwOnlyClassVariant(x=123, y=Message.ArgMove(y=34))
1✔
71
    assert variant.dump() == dict(x=123, y=Message.ArgMove(x=234569834, y=34), z="hello")
1✔
72

73
    with pytest.raises(TypeError):
1✔
74
        Message.KwOnlyClassVariant(123, y=Message.ArgMove(y=34))
1✔
75

76
    with pytest.raises(TypeError):
1✔
77
        Message.KwOnlyClassVariant(123, Message.ArgMove(y=34))
1✔
78

79

80
def test_default_factory():
1✔
81
    message = Message.FactoryTest(x=234, y=[12, 3], z={})
1✔
82
    assert message.dump() == dict(x=234, y=[12, 3], z={})
1✔
83

84
    message = Message.FactoryTest(x=456)
1✔
85
    assert message.dump() == dict(x=456, y=[], z={"hello": "world"})
1✔
86

87

88
def test_none_kw_only():
1✔
89
    assert Message.ArgMove(1, 2) == Message.ArgMove(1, y=2) == Message.ArgMove(x=1, y=2)
1✔
90
    with pytest.raises(TypeError):
1✔
91
        Message.ArgMove(1, 2, 3)
1✔
92
    with pytest.raises(TypeError):
1✔
93
        Message.ArgMove(1, 2, x=1)
1✔
94
    with pytest.raises(TypeError):
1✔
95
        Message.ArgMove(1, 2, x=1)
1✔
96
    with pytest.raises(TypeError):
1✔
97
        Message.ArgMove(1, x=1)
1✔
98
    assert Message.ArgMove(234569834, 234) == Message.ArgMove(y=234)
1✔
99

100

101
def test_misc():
1✔
102
    with pytest.raises(TypeError):
1✔
103
        Message()
1✔
104

105
    with pytest.raises(TypeError):
1✔
106
        @fieldenum
1✔
107
        class DerivedMessage(Message):
1✔
108
            New = Unit
1✔
109

110
    with pytest.raises(TypeError):
1✔
111
        @fieldenum
1✔
112
        class Mixed:
1✔
113
            New = Variant(str, x=int)
1✔
114

115
    with pytest.raises(TypeError, match="self.name"):
1✔
116
        Unit.attach(Message, eq=True, build_hash=False, build_repr=False, frozen=False)
1✔
117

118
    with pytest.raises(TypeError):
1✔
119
        message = Message.Move(x=123, y=567)
1✔
120
        message.x = 224
1✔
121

122
    assert Message.Move(y=325) == Message.Move(x=234569834, y=325)
1✔
123

124
    with pytest.raises(TypeError):
1✔
125
        @fieldenum
1✔
126
        class TupleDefault:
1✔
127
            Move = Variant(int, int).default(a=123)
1✔
128

129
    with pytest.raises(TypeError):
1✔
130
        @fieldenum
1✔
131
        class FieldlessDefault:
1✔
132
            Move = Variant().default(a=123)
1✔
133

134

135
def test_mutable_fieldenum():
1✔
136
    @fieldenum(frozen=False)
1✔
137
    class Message:
1✔
138
        Quit = Unit
1✔
139
        Move = Variant(x=int, y=int).kw_only()
1✔
140
        Write = Variant(str)
1✔
141
        ChangeColor = Variant(int, int, int)
1✔
142
        Pause = Variant()
1✔
143

144
        @variant
1✔
145
        def FunctionVariantWithBody(self, a: int, b, /, c: str, d, *, f: float = 3.4, e: dict | None = None, raise_it: bool = False):
1✔
146
            if raise_it:
1✔
UNCOV
147
                raise ExceptionForTest((self, a, b, c, d, f, e))
×
148

149
    with pytest.raises(TypeError, match="unhashable type:"):
1✔
150
        {Message.Quit}
1✔
151

152
    with pytest.raises(TypeError, match="unhashable type:"):
1✔
153
        {Message.Move(x=123, y=345)}
1✔
154

155
    with pytest.raises(TypeError, match="unhashable type:"):
1✔
156
        {Message.Write("hello")}
1✔
157

158
    with pytest.raises(TypeError, match="unhashable type:"):
1✔
159
        {Message.ChangeColor(123, 456, 789)}
1✔
160

161
    with pytest.raises(TypeError, match="unhashable type:"):
1✔
162
        {Message.Pause()}
1✔
163

164
    with pytest.raises(TypeError, match="unhashable type:"):
1✔
165
        {Message.FunctionVariantWithBody(1, 2, "hello", d=123, f=1.3, e={"hello": "world"})}
1✔
166

167
    message = Message.Move(x=123, y=345)
1✔
168
    message.x = 654
1✔
169
    assert message == Message.Move(x=654, y=345)
1✔
170
    assert message.dump() == {"x": 654, "y": 345}
1✔
171

172
    message = Message.Write("hello")
1✔
173
    message._0 = "world"
1✔
174
    assert message == Message.Write("world")
1✔
175
    assert message.dump() == ("world",)
1✔
176

177
    message = Message.ChangeColor(123, 456, 789)
1✔
178
    message._1 = 2345
1✔
179
    assert message == Message.ChangeColor(123, 2345, 789)
1✔
180
    assert message.dump() == (123, 2345, 789)
1✔
181

182
    assert Message.Quit.dump() is None
1✔
183
    assert Message.Pause().dump() == ()
1✔
184

185
    message = Message.FunctionVariantWithBody(1, 2, "hello", d=123, f=1.3, e={"hello": "world"})
1✔
186
    message.a = 235
1✔
187
    message.e = [235, 346]
1✔
188
    assert message.dump() == dict(a=235, b=2, c="hello", d=123, f=1.3, e=[235, 346], raise_it=False)
1✔
189

190

191
def test_relationship():
1✔
192
    # Unit variants are the instances. Use fieldless variant if you want issubclass work.
193
    # Theoretically it's possible to make it to be both subclass and instance of original class,
194
    # It breaks instance and class methods.
195
    assert isinstance(Message.Quit, Message)
1✔
196

197
    assert issubclass(Message.Move, Message)
1✔
198
    assert isinstance(Message.Write("hello"), Message.Write)
1✔
199
    assert isinstance(Message.Write("hello"), Message)
1✔
200
    assert isinstance(Message.Move(x=123, y="hello"), Message.Move)
1✔
201
    assert isinstance(Message.Move(x=123, y="hello"), Message)
1✔
202
    assert isinstance(Message.ChangeColor(123, 456, 789), Message.ChangeColor)
1✔
203
    assert isinstance(Message.ChangeColor(123, 456, 789), Message)
1✔
204
    assert isinstance(Message.Pause(), Message.Pause)
1✔
205
    assert isinstance(Message.Pause(), Message)
1✔
206

207

208
def test_instancing():
1✔
209
    type MyTypeAlias = int | str | bytes
1✔
210

211
    @fieldenum
1✔
212
    class Message[T]:
1✔
213
        Quit = Unit
1✔
214
        Move = Variant(x=int, y=int).kw_only()
1✔
215
        Write = Variant(str)
1✔
216
        ChangeColor = Variant(int | str, T, Any)
1✔
217
        Pause = Variant()
1✔
218
        UseTypeAlias = Variant(MyTypeAlias)
1✔
219
        UseSelf = Variant(Self)
1✔
220

221
    Message.Move(x=123, y=567)
1✔
222
    Message.Write("hello, world!")
1✔
223
    Message.ChangeColor("hello", (), 123)
1✔
224
    Message.ChangeColor(1234, [], b"bytes")
1✔
225
    Message.Pause()
1✔
226

227
    with pytest.raises(TypeError, match=r"Expect 3 field\(s\), but received 4 argument\(s\)\."):
1✔
228
        Message.ChangeColor(1, 2, 3, 4)
1✔
229

230
    with pytest.raises(TypeError, match=r"Expect 3 field\(s\), but received 4 argument\(s\)\."):
1✔
231
        Message.ChangeColor(1, 2, 3, 4)
1✔
232

233
    with pytest.raises(TypeError, match="x"):
1✔
234
        Message.Move(y=4)
1✔
235

236
    with pytest.raises(TypeError, match="hello"):
1✔
237
        Message.Move(hello=4)
1✔
238

239

240
def test_eq_and_hash():
1✔
241
    assert Message.ChangeColor(1, ("hello",), [1, 2, 3]) == Message.ChangeColor(1, ("hello",), [1, 2, 3])
1✔
242
    assert Message.FunctionVariant(1, 2, "hello", d=123, f=1.3, e={"hello": "world"}) == Message.FunctionVariant(
1✔
243
        1, 2, "hello", d=123, f=1.3, e={"hello": "world"})
244
    my_set = {
1✔
245
        Message.ChangeColor(1, ("hello",), (1, 2, 3)),
246
        Message.Quit,
247
        Message.Move(x=234, y=(2, "hello")),
248
        Message.Pause(),
249
        Message.FunctionVariant(1, 2, "hello", d=123, f=1.3, e="world")
250
    }
251
    assert Message.ChangeColor(1, ("hello",), (1, 2, 3)) in my_set
1✔
252
    assert Message.Quit in my_set
1✔
253
    assert Message.Move(x=234, y=(2, "hello")) in my_set
1✔
254
    assert Message.Pause() in my_set
1✔
255
    assert Message.FunctionVariant(1, 2, "hello", d=123, f=1.3, e="world") in my_set
1✔
256

257
    my_set.add(Message.ChangeColor(1, ("hello",), (1, 2, 3)))
1✔
258
    my_set.add(Message.Quit)
1✔
259
    my_set.add(Message.Move(x=234, y=(2, "hello")))
1✔
260
    my_set.add(Message.Pause())
1✔
261
    my_set.add(Message.FunctionVariant(1, 2, "hello", d=123, f=1.3, e="world"))
1✔
262
    assert len(my_set) == 5
1✔
263

264

265
@pytest.mark.parametrize(
1✔
266
    "message",
267
    [
268
        Message.Move(x=123, y=456),
269
        Message.ChangeColor(1, 2, 3),
270
        Message.Pause(),
271
        Message.Write("hello"),
272
        Message.Quit,
273
        Message.FunctionVariant(1, 2, "hello", d=123, f=1.3, e={"hello": "world"}),
274
    ],
275
)
276
def test_pickling(message):
1✔
277
    dump = pickle.dumps(message)
1✔
278
    load = pickle.loads(dump)
1✔
279
    assert message == load
1✔
280

281

282
def test_complex_matching():
1✔
283
    match Message.Move(x=123, y=456):
1✔
284
        case Message.Write("hello"):
1✔
285
            assert False
286

287
        case Message.Move(x=_, y=123):
1✔
288
            assert False
289

290
        case Message.Move(x=_, y=1 | 456):
1✔
291
            assert True
1✔
292

293
        case other:
294
            assert False, other
295

296

297
@pytest.mark.parametrize(
1✔
298
    "message,expect",
299
    [
300
        (Message.Move(x=123, y=456), ("move", 456)),
301
        (Message.ChangeColor(1, 2, 3), ("color", 1, 2, 3)),
302
        (Message.Pause(), "fieldless"),
303
        (Message.Write("hello"), ("write", "hello")),
304
        (Message.Quit, "quit"),
305
        (Message.FunctionVariant(1, 2, "hello", d=123, f=1.3, e={"hello": "world"}), "function_variant"),
306
    ],
307
)
308
def test_simple_matching(message, expect):
1✔
309
    match message:
1✔
310
        case Message.ChangeColor(x, y, z):
1✔
311
            assert expect == ("color", x, y, z)
1✔
312

313
        case Message.Quit:
1✔
314
            assert expect == "quit"
1✔
315

316
        case Message.Pause():
1✔
317
            assert expect == "fieldless"
1✔
318

319
        case Message.Write(msg):
1✔
320
            assert expect == ("write", msg)
1✔
321

322
        case Message.Move(x=123, y=y):
1✔
323
            assert expect == ("move", y)
1✔
324

325
        case Message.FunctionVariant(1, 2, "hello", d=123, f=1.3, e={"hello": "world"}):
1✔
326
            assert expect == "function_variant"
1✔
327

328
    # don't do these
329
    match message:
1✔
330
        case Message.ChangeColor(x, y):
1✔
331
            assert expect[0] == "color"
1✔
332

333
        case Message.Write():
1✔
334
            assert expect[0] == "write"
1✔
335

336
        case Message.Move():
1✔
337
            assert expect[0] == "move"
1✔
338

339

340
def test_repr():
1✔
341
    assert repr(Message.Quit) == "Message.Quit"
1✔
342
    assert repr(Message.Move(x=123, y=234)) == "Message.Move(x=123, y=234)"
1✔
343
    assert repr(Message.Write("hello!")) == "Message.Write('hello!')"
1✔
344
    assert repr(Message.ChangeColor(123, 456, 789)) == "Message.ChangeColor(123, 456, 789)"
1✔
345
    assert repr(Message.Pause()) == "Message.Pause()"
1✔
346
    assert repr(Message.FunctionVariant(1, 2, "hello", d=123, f=1.3, e={"hello": "world"})) == "Message.FunctionVariant(1, 2, 'hello', 123, f=1.3, e={'hello': 'world'})"
1✔
347
    assert repr(Message.ArgsOnlyFuncVariant(1,2,3,d=4)) == "Message.ArgsOnlyFuncVariant(1, 2, 3, 4)"
1✔
348
    assert repr(Message.KwargsOnlyFuncVariant(a=1,b=2,c=3)) == "Message.KwargsOnlyFuncVariant(a=1, b=2, c=3, d=None)"
1✔
349
    assert repr(Message.ParamlessFuncVariantWithBody()) == "Message.ParamlessFuncVariantWithBody()"
1✔
350

351
    assert repr(Message.Quit) == "Message.Quit"
1✔
352
    assert repr(Message.Move) == "<class 'fieldenum._fieldenum.Message.Move'>"
1✔
353
    assert repr(Message.Write) == "<class 'fieldenum._fieldenum.Message.Write'>"
1✔
354
    assert repr(Message.ChangeColor) == "<class 'fieldenum._fieldenum.Message.ChangeColor'>"
1✔
355
    assert repr(Message.Pause) == "<class 'fieldenum._fieldenum.Message.Pause'>"
1✔
356
    assert repr(Message.FunctionVariant) == "<class 'fieldenum._fieldenum.Message.FunctionVariant'>"
1✔
357

358

359
def test_multiple_assignment():
1✔
360
    variant = Variant(x=int)
1✔
361
    @fieldenum
1✔
362
    class OneVariant:
1✔
363
        my = variant
1✔
364
    assert variant.attached
1✔
365
    with pytest.raises(TypeError, match="This variants already attached to"):
1✔
366
        @fieldenum
1✔
367
        class AnotherVariant:
1✔
368
            my = variant
1✔
369
    with pytest.raises(TypeError, match="This variants already attached to"):
1✔
370
        variant.attach(object, eq=True, build_hash=True, build_repr=True, frozen=True)
1✔
371

372

373
def test_function_variant():
1✔
374
    message = Message.FunctionVariantWithBody(1, 2, "hello", d=123, f=1.3, e={"hello": "world"})
1✔
375
    assert message.dump() == dict(a=1, b=2, c="hello", d=123, f=1.3, e={"hello": "world"}, raise_it=False)
1✔
376
    try:
1✔
377
        Message.FunctionVariantWithBody(1, 2, "hello", d=123, f=1.3, e={"hello": "world"}, raise_it=True)
1✔
378
    except ExceptionForTest as exc:
1✔
379
        message, *others = exc.value
1✔
380
        assert isinstance(message, Message)
1✔
381
        assert isinstance(message, Message.FunctionVariantWithBody)
1✔
382
        assert others == [1, 2, "hello", 123, 1.3, {"hello": "world"}]
1✔
383

384
    message = Message.FunctionVariant(1, 2, "hello", d=123, f=1.3, e={"hello": "world"})
1✔
385
    assert message.dump() == dict(a=1, b=2, c="hello", d=123, f=1.3, e={"hello": "world"})
1✔
386

387
    message = Message.FunctionVariant(1, 2, "hello", d=123)
1✔
388
    assert message.dump() == dict(a=1, b=2, c="hello", d=123, f=0.0, e={"hello": "good"})
1✔
389

390
    with pytest.raises(TypeError, match="Initializer should return None."):
1✔
391
        Message.NotNoneReturnFunctionVariant(1, 2, "hello", d=123, f=1.3, e={"hello": "world"})
1✔
392

393

394
def test_method_abuse_on_function_variant():
1✔
395
    @variant
1✔
396
    def MyVariant(self, hello, world):
1✔
UNCOV
397
        raise ValueError
×
398

399
    with pytest.raises(TypeError, match="method cannot be used in function variant"):
1✔
400
        @fieldenum
1✔
401
        class NeverGonnaBeUsed:
1✔
402
            V = MyVariant.kw_only()
1✔
403

404
    with pytest.raises(TypeError, match="method cannot be used in function variant"):
1✔
405
        @fieldenum
1✔
406
        class NeverGonnaBeUsed:
1✔
407
            V = MyVariant.default(hello=123)
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc