-
-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathtest_sync.py
141 lines (112 loc) · 3.43 KB
/
test_sync.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import sys
import traceback
import pytest
import outcome
from outcome import AlreadyUsedError, Error, Value
def test_Outcome():
v = Value(1)
assert v.value == 1
assert repr(v) == "Value(1)"
assert v.unwrap() == 1
assert repr(v) == "Value(<DEAD>)"
with pytest.raises(AlreadyUsedError):
v.unwrap()
v = Value(1)
exc = RuntimeError("oops")
e = Error(exc)
assert e.error is exc
assert repr(e) == f"Error({exc!r})"
with pytest.raises(RuntimeError):
e.unwrap()
with pytest.raises(AlreadyUsedError):
e.unwrap()
assert repr(e) == "Error(<DEAD>)"
e = Error(exc)
with pytest.raises(TypeError):
Error("hello")
with pytest.raises(TypeError):
Error(RuntimeError)
def expect_1():
assert (yield) == 1
yield "ok"
it = iter(expect_1())
next(it)
assert v.send(it) == "ok"
with pytest.raises(AlreadyUsedError):
v.send(it)
def expect_RuntimeError():
with pytest.raises(RuntimeError):
yield
yield "ok"
it = iter(expect_RuntimeError())
next(it)
assert e.send(it) == "ok"
with pytest.raises(AlreadyUsedError):
e.send(it)
def test_Outcome_eq_hash():
v1 = Value(["hello"])
v2 = Value(["hello"])
v3 = Value("hello")
v4 = Value("hello")
assert v1 == v2
assert v1 != v3
with pytest.raises(TypeError):
{v1}
assert {v3, v4} == {v3}
# exceptions in general compare by identity
exc1 = RuntimeError("oops")
exc2 = KeyError("foo")
e1 = Error(exc1)
e2 = Error(exc1)
e3 = Error(exc2)
e4 = Error(exc2)
assert e1 == e2
assert e3 == e4
assert e1 != e3
assert {e1, e2, e3, e4} == {e1, e3}
def test_Value_compare():
assert Value(1) < Value(2)
assert not Value(3) < Value(2)
with pytest.raises(TypeError):
Value(1) < Value("foo")
def test_capture():
def add(x, y):
return x + y
v = outcome.capture(add, 2, y=3)
assert type(v) == Value
assert v.unwrap() == 5
def raise_ValueError(x):
raise ValueError(x)
e = outcome.capture(raise_ValueError, "two")
assert type(e) == Error
assert type(e.error) is ValueError
assert e.error.args == ("two",)
def test_inheritance():
assert issubclass(Value, outcome.Outcome)
assert issubclass(Error, outcome.Outcome)
def test_traceback_frame_removal():
def raise_ValueError(x):
raise ValueError(x)
e = outcome.capture(raise_ValueError, 'abc')
with pytest.raises(ValueError) as exc_info:
e.unwrap()
frames = traceback.extract_tb(exc_info.value.__traceback__)
functions = [function for _, _, function, _ in frames]
assert functions[-2:] == ['unwrap', 'raise_ValueError']
def test_Error_unwrap_does_not_create_reference_cycles():
# See comment in Error.unwrap for why reference cycles are tricky
exc = ValueError()
err = Error(exc)
try:
err.unwrap()
except ValueError:
pass
# Top frame in the traceback is the current test function; we don't care
# about its references
assert exc.__traceback__.tb_frame is sys._getframe()
# The next frame down is the 'unwrap' frame; we want to make sure it
# doesn't reference the exception (or anything else for that matter, just
# to be thorough)
unwrap_frame = exc.__traceback__.tb_next.tb_frame
assert unwrap_frame.f_code.co_name == "unwrap"
assert unwrap_frame.f_locals == {}