Skip to content

Commit d0c55c6

Browse files
committed
refactor(tests): migrate main test files from StateMachine to StateChart
- test_statemachine.py: all classes → StateChart, current_state → is_active - test_copy.py: all classes → StateChart, current_state → is_active - test_async.py: all classes → StateChart with explicit flags where needed (allow_event_without_transition=False, error_on_execution=False)
1 parent 071b15b commit d0c55c6

3 files changed

Lines changed: 95 additions & 105 deletions

File tree

tests/test_async.py

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66

77
from statemachine import State
88
from statemachine import StateChart
9-
from statemachine import StateMachine
109

1110

1211
@pytest.fixture()
1312
def async_order_control_machine(): # noqa: C901
14-
class OrderControl(StateMachine):
13+
class OrderControl(StateChart):
14+
allow_event_without_transition = False
15+
1516
waiting_for_payment = State(initial=True)
1617
processing = State()
1718
shipping = State()
@@ -98,9 +99,11 @@ def test_async_state_from_sync_context(async_order_control_machine):
9899
assert sm.completed.is_active
99100

100101

101-
class AsyncConditionExpressionMachine(StateMachine):
102+
class AsyncConditionExpressionMachine(StateChart):
102103
"""Regression test for issue #535: async conditions in boolean expressions."""
103104

105+
allow_event_without_transition = False
106+
104107
s1 = State(initial=True)
105108

106109
go_not = s1.to.itself(cond="not cond_false")
@@ -190,17 +193,21 @@ async def test_async_state_should_be_initialized(async_order_control_machine):
190193
"""
191194

192195
sm = async_order_control_machine()
193-
with pytest.raises(
194-
InvalidStateValue,
195-
match=re.escape(
196-
r"There's no current state set. In async code, "
197-
r"did you activate the initial state? (e.g., `await sm.activate_initial_state()`)"
198-
),
199-
):
200-
assert sm.current_state == sm.waiting_for_payment
196+
import warnings
197+
198+
with warnings.catch_warnings():
199+
warnings.simplefilter("ignore", DeprecationWarning)
200+
with pytest.raises(
201+
InvalidStateValue,
202+
match=re.escape(
203+
r"There's no current state set. In async code, "
204+
r"did you activate the initial state? (e.g., `await sm.activate_initial_state()`)"
205+
),
206+
):
207+
sm.current_state # noqa: B018
201208

202209
await sm.activate_initial_state()
203-
assert sm.current_state == sm.waiting_for_payment
210+
assert sm.waiting_for_payment.is_active
204211

205212

206213
@pytest.mark.timeout(5)
@@ -303,7 +310,9 @@ def after_go(self, **kwargs):
303310
async def test_async_runtime_error_in_after_without_error_on_execution():
304311
"""RuntimeError in async after callback without error_on_execution propagates."""
305312

306-
class SM(StateMachine):
313+
class SM(StateChart):
314+
error_on_execution = False
315+
307316
s1 = State(initial=True)
308317
s2 = State(final=True)
309318

@@ -384,7 +393,9 @@ async def after_go(self, **kwargs):
384393
async def test_async_engine_runtime_error_in_after_without_error_on_execution_propagates():
385394
"""AsyncEngine: RuntimeError in async after callback without error_on_execution raises."""
386395

387-
class SM(StateMachine):
396+
class SM(StateChart):
397+
error_on_execution = False
398+
388399
s1 = State(initial=True)
389400
s2 = State(final=True)
390401

@@ -403,7 +414,7 @@ async def after_go(self, **kwargs):
403414
async def test_async_engine_start_noop_when_already_initialized():
404415
"""BaseEngine.start() is a no-op when state machine is already initialized."""
405416

406-
class SM(StateMachine):
417+
class SM(StateChart):
407418
s1 = State(initial=True)
408419
s2 = State(final=True)
409420

@@ -422,7 +433,7 @@ async def on_go(
422433

423434
class TestAsyncEnabledEvents:
424435
async def test_passing_async_condition(self):
425-
class MyMachine(StateMachine):
436+
class MyMachine(StateChart):
426437
s0 = State(initial=True)
427438
s1 = State(final=True)
428439

@@ -436,7 +447,7 @@ async def is_ready(self):
436447
assert [e.id for e in await sm.enabled_events()] == ["go"]
437448

438449
async def test_failing_async_condition(self):
439-
class MyMachine(StateMachine):
450+
class MyMachine(StateChart):
440451
s0 = State(initial=True)
441452
s1 = State(final=True)
442453

@@ -450,7 +461,7 @@ async def is_ready(self):
450461
assert await sm.enabled_events() == []
451462

452463
async def test_kwargs_forwarded_to_async_conditions(self):
453-
class MyMachine(StateMachine):
464+
class MyMachine(StateChart):
454465
s0 = State(initial=True)
455466
s1 = State(final=True)
456467

@@ -465,7 +476,7 @@ async def check_value(self, value=0):
465476
assert [e.id for e in await sm.enabled_events(value=20)] == ["go"]
466477

467478
async def test_async_condition_exception_treated_as_enabled(self):
468-
class MyMachine(StateMachine):
479+
class MyMachine(StateChart):
469480
s0 = State(initial=True)
470481
s1 = State(final=True)
471482

@@ -481,7 +492,7 @@ async def bad_cond(self):
481492
async def test_duplicate_event_across_transitions_deduplicated(self):
482493
"""Same event on multiple passing transitions appears only once."""
483494

484-
class MyMachine(StateMachine):
495+
class MyMachine(StateChart):
485496
s0 = State(initial=True)
486497
s1 = State()
487498
s2 = State(final=True)
@@ -501,7 +512,7 @@ async def cond_b(self):
501512
assert len(ids) == 1
502513

503514
async def test_mixed_enabled_and_disabled_async(self):
504-
class MyMachine(StateMachine):
515+
class MyMachine(StateChart):
505516
s0 = State(initial=True)
506517
s1 = State()
507518
s2 = State(final=True)

tests/test_copy.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from statemachine.states import States
1010

1111
from statemachine import State
12-
from statemachine import StateMachine
12+
from statemachine import StateChart
1313

1414
logger = logging.getLogger(__name__)
1515

@@ -30,7 +30,7 @@ class GameStates(str, Enum):
3030
GAME_END = auto()
3131

3232

33-
class GameStateMachine(StateMachine):
33+
class GameStateMachine(StateChart):
3434
s = States.from_enum(GameStates, initial=GameStates.GAME_START)
3535

3636
play = s.GAME_START.to(s.GAME_PLAYING)
@@ -44,7 +44,7 @@ def game_is_over(self) -> bool:
4444
advance_round = end_game | s.TURN_END.to(s.GAME_END)
4545

4646

47-
class MyStateMachine(StateMachine):
47+
class MyStateMachine(StateChart):
4848
created = State(initial=True)
4949
started = State()
5050

@@ -56,7 +56,7 @@ def __init__(self):
5656
self.value = [1, 2, 3]
5757

5858

59-
class MySM(StateMachine):
59+
class MySM(StateChart):
6060
draft = State("Draft", initial=True, value="draft")
6161
published = State("Published", value="published", final=True)
6262

@@ -89,11 +89,11 @@ def test_copy(copy_method):
8989

9090
assert sm.model is not sm2.model
9191
assert sm.model.name == sm2.model.name
92-
assert sm2.current_state == sm.current_state
92+
assert sm2.draft.is_active
9393

9494
sm2.model.let_me_be_visible = True
9595
sm2.send("publish")
96-
assert sm2.current_state == sm.published
96+
assert sm2.published.is_active
9797

9898

9999
def test_copy_with_listeners(copy_method):
@@ -120,16 +120,16 @@ def test_copy_with_listeners(copy_method):
120120
listener.let_me_be_visible = True
121121

122122
sm2.send("publish")
123-
assert sm2.current_state == sm1.published
123+
assert sm2.published.is_active
124124

125125

126126
def test_copy_with_enum(copy_method):
127127
sm = GameStateMachine()
128128
sm.play()
129-
assert sm.current_state == GameStateMachine.GAME_PLAYING
129+
assert sm.GAME_PLAYING.is_active
130130

131131
sm2 = copy_method(sm)
132-
assert sm2.current_state == GameStateMachine.GAME_PLAYING
132+
assert sm2.GAME_PLAYING.is_active
133133

134134

135135
def test_copy_with_custom_init_and_vars(copy_method):
@@ -139,10 +139,10 @@ def test_copy_with_custom_init_and_vars(copy_method):
139139
sm2 = copy_method(sm)
140140
assert sm2.custom == 1
141141
assert sm2.value == [1, 2, 3]
142-
assert sm2.current_state == MyStateMachine.started
142+
assert sm2.started.is_active
143143

144144

145-
class AsyncTrafficLightMachine(StateMachine):
145+
class AsyncTrafficLightMachine(StateChart):
146146
green = State(initial=True)
147147
yellow = State()
148148
red = State()
@@ -164,9 +164,9 @@ def test_copy_async_statemachine_before_activation(copy_method):
164164

165165
async def verify():
166166
await sm_copy.activate_initial_state()
167-
assert sm_copy.current_state == AsyncTrafficLightMachine.green
167+
assert sm_copy.green.is_active
168168
await sm_copy.cycle()
169-
assert sm_copy.current_state == AsyncTrafficLightMachine.yellow
169+
assert sm_copy.yellow.is_active
170170

171171
asyncio.run(verify())
172172

@@ -178,13 +178,13 @@ async def setup_and_verify():
178178
sm = AsyncTrafficLightMachine()
179179
await sm.activate_initial_state()
180180
await sm.cycle()
181-
assert sm.current_state == AsyncTrafficLightMachine.yellow
181+
assert sm.yellow.is_active
182182

183183
sm_copy = copy_method(sm)
184184

185185
await sm_copy.activate_initial_state()
186-
assert sm_copy.current_state == AsyncTrafficLightMachine.yellow
186+
assert sm_copy.yellow.is_active
187187
await sm_copy.cycle()
188-
assert sm_copy.current_state == AsyncTrafficLightMachine.red
188+
assert sm_copy.red.is_active
189189

190190
asyncio.run(setup_and_verify())

0 commit comments

Comments
 (0)