Skip to content

Commit b9a6e82

Browse files
committed
New lingo for tracking sequential tasks
1 parent f1f26b1 commit b9a6e82

File tree

3 files changed

+334
-294
lines changed

3 files changed

+334
-294
lines changed
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
import asyncio
2+
from enum import Enum, auto
3+
4+
from eth_utils import (
5+
ValidationError,
6+
)
7+
from eth_utils.toolz import identity
8+
import pytest
9+
10+
from trinity.utils.datastructures import (
11+
OrderedTaskPreparation,
12+
)
13+
14+
DEFAULT_TIMEOUT = 0.05
15+
16+
17+
async def wait(coro, timeout=DEFAULT_TIMEOUT):
18+
return await asyncio.wait_for(coro, timeout=timeout)
19+
20+
21+
class OnePrereq(Enum):
22+
one = auto()
23+
24+
25+
class TwoPrereqs(Enum):
26+
Prereq1 = auto()
27+
Prereq2 = auto()
28+
29+
30+
@pytest.mark.asyncio
31+
async def test_simplest_path():
32+
ti = OrderedTaskPreparation(TwoPrereqs, identity, lambda x: x - 1)
33+
ti.set_finished_dependency(3)
34+
ti.register_tasks((4, ))
35+
ti.finish_prereq(TwoPrereqs.Prereq1, (4, ))
36+
ti.finish_prereq(TwoPrereqs.Prereq2, (4, ))
37+
ready = await wait(ti.ready_tasks())
38+
assert ready == (4, )
39+
40+
41+
@pytest.mark.asyncio
42+
async def test_cannot_finish_before_prepare():
43+
ti = OrderedTaskPreparation(TwoPrereqs, identity, lambda x: x - 1)
44+
ti.set_finished_dependency(3)
45+
with pytest.raises(ValidationError):
46+
ti.finish_prereq(TwoPrereqs.Prereq1, (4, ))
47+
48+
49+
@pytest.mark.asyncio
50+
async def test_two_steps_simultaneous_complete():
51+
ti = OrderedTaskPreparation(OnePrereq, identity, lambda x: x - 1)
52+
ti.set_finished_dependency(3)
53+
ti.register_tasks((4, 5))
54+
ti.finish_prereq(OnePrereq.one, (4, ))
55+
ti.finish_prereq(OnePrereq.one, (5, ))
56+
57+
completed = await wait(ti.ready_tasks())
58+
assert completed == (4, 5)
59+
60+
61+
@pytest.mark.asyncio
62+
async def test_pruning():
63+
# make a number task depend on the mod10, so 4 and 14 both depend on task 3
64+
ti = OrderedTaskPreparation(OnePrereq, identity, lambda x: (x % 10) - 1, max_depth=2)
65+
ti.set_finished_dependency(3)
66+
ti.register_tasks((4, 5, 6))
67+
ti.finish_prereq(OnePrereq.one, (4, 5, 6))
68+
69+
# it's fine to prepare a task that depends up to two back in history
70+
# this depends on 5
71+
ti.register_tasks((16, ))
72+
# this depends on 4
73+
ti.register_tasks((15, ))
74+
75+
# but depending 3 back in history should raise a validation error, because it's pruned
76+
with pytest.raises(ValidationError):
77+
# this depends on 3
78+
ti.register_tasks((14, ))
79+
80+
# test the same concept, but after pruning more than just the starting task...
81+
ti.register_tasks((7, ))
82+
ti.finish_prereq(OnePrereq.one, (7, ))
83+
84+
ti.register_tasks((16, ))
85+
ti.register_tasks((17, ))
86+
with pytest.raises(ValidationError):
87+
ti.register_tasks((15, ))
88+
89+
90+
@pytest.mark.asyncio
91+
async def test_wait_forever():
92+
ti = OrderedTaskPreparation(OnePrereq, identity, lambda x: x - 1)
93+
try:
94+
finished = await wait(ti.ready_tasks())
95+
except asyncio.TimeoutError:
96+
pass
97+
else:
98+
assert False, f"No steps should complete, but got {finished!r}"
99+
100+
101+
def test_finish_same_task_twice():
102+
ti = OrderedTaskPreparation(TwoPrereqs, identity, lambda x: x - 1)
103+
ti.set_finished_dependency(1)
104+
ti.register_tasks((2, ))
105+
ti.finish_prereq(TwoPrereqs.Prereq1, (2,))
106+
with pytest.raises(ValidationError):
107+
ti.finish_prereq(TwoPrereqs.Prereq1, (2,))
108+
109+
110+
@pytest.mark.asyncio
111+
async def test_finish_different_entry_at_same_step():
112+
113+
def previous_even_number(num):
114+
return ((num - 1) // 2) * 2
115+
116+
ti = OrderedTaskPreparation(OnePrereq, identity, previous_even_number)
117+
118+
ti.set_finished_dependency(2)
119+
120+
ti.register_tasks((3, 4))
121+
122+
# depends on 2
123+
ti.finish_prereq(OnePrereq.one, (3,))
124+
125+
# also depends on 2
126+
ti.finish_prereq(OnePrereq.one, (4,))
127+
128+
completed = await wait(ti.ready_tasks())
129+
assert completed == (3, 4)
130+
131+
132+
@pytest.mark.asyncio
133+
async def test_return_original_entry():
134+
# for no particular reason, the id is 3 before the number
135+
ti = OrderedTaskPreparation(OnePrereq, lambda x: x - 3, lambda x: x - 4)
136+
137+
# translates to id -1
138+
ti.set_finished_dependency(2)
139+
140+
ti.register_tasks((3, 4))
141+
142+
# translates to id 0
143+
ti.finish_prereq(OnePrereq.one, (3,))
144+
145+
# translates to id 1
146+
ti.finish_prereq(OnePrereq.one, (4,))
147+
148+
entries = await wait(ti.ready_tasks())
149+
150+
# make sure that the original task is returned, not the id
151+
assert entries == (3, 4)
152+
153+
154+
def test_finish_with_unrecognized_task():
155+
ti = OrderedTaskPreparation(TwoPrereqs, identity, lambda x: x - 1)
156+
ti.set_finished_dependency(1)
157+
with pytest.raises(ValidationError):
158+
ti.finish_prereq('UNRECOGNIZED_TASK', (2,))
159+
160+
161+
def test_finish_before_setting_start_val():
162+
ti = OrderedTaskPreparation(TwoPrereqs, identity, lambda x: x - 1)
163+
with pytest.raises(ValidationError):
164+
ti.finish_prereq(TwoPrereqs.Prereq1, (2,))
165+
166+
167+
def test_finish_too_early():
168+
ti = OrderedTaskPreparation(TwoPrereqs, identity, lambda x: x - 1)
169+
ti.set_finished_dependency(3)
170+
with pytest.raises(ValidationError):
171+
ti.finish_prereq(TwoPrereqs.Prereq1, (3,))
172+
173+
174+
def test_empty_completion():
175+
ti = OrderedTaskPreparation(TwoPrereqs, identity, lambda x: x - 1)
176+
with pytest.raises(ValidationError):
177+
ti.finish_prereq(TwoPrereqs.Prereq1, tuple())
178+
179+
180+
def test_empty_enum():
181+
182+
class NoPrerequisites(Enum):
183+
pass
184+
185+
with pytest.raises(ValidationError):
186+
OrderedTaskPreparation(NoPrerequisites, identity, lambda x: x - 1)

tests/trinity/utils/test_task_integration.py

Lines changed: 0 additions & 194 deletions
This file was deleted.

0 commit comments

Comments
 (0)