Statecharts · Problem 1
P1 · The Microwave Oven
Hierarchy, and one safety invariant that the engine enforces because you cannot express it any other way. Ten tests.
The system
A microwave oven. A door that can be open or shut. A timer you add minutes to. A start button, a pause button, a cancel button, and a magnetron that must be off whenever the door is not shut — not “is turned off when the door opens”, but cannot be on while the door is open.
There is one more requirement that looks trivial and is not: the countdown tick must not disturb the magnetron. Switching it off and on sixty times a minute is fine in a model and expensive in hardware.
Step 1 — draw it
Answer these on paper before you open the editor. Each has a reveal, but a guess you commit to is worth more than a correct answer you read.
idle, cooking, paused, and a door-open state. Three of those are only meaningful with the door shut — so they go inside a compound door_shut, which sits alongside door_ajar. The compound is the whole trick: it is what makes “magnetron off” a consequence of the topology rather than of an action you remember to write.cooking. Not on the start transition and not on open_door — the point is that every path out of cooking, including ones added later, runs the exit action, because the engine runs it, not you.tick event, in order: first cooking.to(idle, cond="last_second"), then an internal self-transition that decrements. First matching guard wins, so the specific case has to be declared above the general one.Step 2 — build it
exercises/p1_microwave/machine.py —
the skeleton. Its docstring is the contract: exact state ids, event names, and context attributes.exercises/p1_microwave/test_machine.py —
10 acceptance tests. Do not edit them.exercises/p1_microwave/_solution.py —
a reference solution with commentary. Open it after yours is green, not before.cd statecharts/exercises/p1_microwave pytest -q
Hints, if a test is stuck
enable_self_transition_entries. The StateChart base defaults it to True (SCXML treats a self-transition as a normal transition), and in 3.2.x that flag also suppresses the effect of internal=True. Set enable_self_transition_entries = False on the class.internal=True self-transition whose source is a compound state removes that compound from the configuration in 3.2.x. Declare add_minute on each atomic state instead — door_shut.idle, door_shut.cooking, and so on.When it is green
Look at what you did not write. There is no magnetron_off() call on
the open_door transition, and there is no test you could write that makes the oven cook
with the door open, because there is no such configuration. Then try adding a fourth child to
door_shut — a defrosting state, say — and notice how much of the
safety argument you have to redo. None of it.
Sources: Samek, Practical UML Statecharts · SCXML · RESOURCES.md