forked from jon-jacky/PyModel
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstepper.txt
102 lines (82 loc) · 4.92 KB
/
stepper.txt
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
PyModel Test Harness, or Stepper
To actually execute tests on the implemention, from offline test
suites or on-the-fly, you must write a test harness in Python that
couples your implementation to the model through the test runner, pmt.
In PyModel, the test harness is called a "stepper" because it steps
the implementation one action at a time.
A stepper is written in its own module. To use that stepper, name that
module on the pmt command line with the -i or --iut option.
The WebApplication sample includes a stepper called Stepper.py. The
Socket sample includes several different steppers: stepper.py, which
is recommended, and also some experiments: stepper_d.py, stepper_o.py,
and stepper_a.py. See Socket/README and notes/socket_experiments.txt
for explanations.
Each stepper must provide a TestAction (or testaction or test_action)
method, which is called by the test runner like this:
result = stepper.TestAction(aname, args, modelResult)
The TestAction method takes three arguments: the model's action name
string, the model's action arguments tuple, and the value returned by
the model (when executing that action with those arguments), which
might be None.
The TestAction method calls the corresponding implementation action
with the corresponding implementation arguments. So, the stepper must
translate from the model action name string to an implementation
action and from the model arguments to the implementation arguments.
The stepper cooperates with pmt to determine whether the
implementation passed or failed the test. The pmt program supports
two quite different techniques for this.
In the first technique, code in the stepper TestAction method checks
whether the test passed or failed. Usually the stepper performs this
check by comparing the return value computed by the model (which is
passed in the third argument to TestAction) to the return value
returned by the implementation. Usually the test passes when these
two return values are equal (when tested by the Python == operator),
and fails if they are not equal. The stepper indicates that the test
passed by returning None, and indicates the test failed by returning a
string, which usually describes the reason for the test failure.
In our samples, WebApplication/Stepper.py and Socket/stepper_d.py both
use this first technique.
This first technique is the more obvious of the two, but less
flexible. It is only recommended for deterministic, synchronous
systems, where it is easy to determine whether the test passed by
doing a simple comparison of the return values from the model and the
implementation.
In the second technique, the model's enabling conditions check whether
the test passed or failed. This technique is a bit more complicated,
but more flexible and versatile. It is necessary for systems that are
nondeterministic or asynchronous. It requires that the model be coded
with split actions (with pairs of separate _call and _return, or
_start and _finish actions). The stepper's TestAction method uses the
first half of the split action (for example recv_call) to construct a
call to the implementation, then collects any return values from the
implementation, and uses them to construct the second half of the
split action (recv_return), which it appends to observation_queue. It
always returns None from the TestAction method. At some later time,
the pmt program pops the recv_return action from the observation queue
and applies the corresponding enabling condition
(recv_return_enabled). If the enabling condition returns True, the
test passes, but if it returns False, the test fails.
In the Socket sample, stepper.py, stepper_o.py and stepper_a.py use
this second technique.
This second technique supports nondeterminism easily because the
enabling condition for the _return action can check any condition on
the returned values and the state variables. This condition is coded
in the model along with other knowledge about the intended behavior.
(it is not coded in the stepper). In effect, the enabling condition
for the _return action is the postcondition for the call/return pair.
This second technique supports asynchrony because the _return action
does not need to be ready when TestAction returns. Instead, the
implementation's action can return later. Then pmt can continue
running; the stepper captures the _return action when it eventually
appears. To achieve this, the stepper can monitor the implementation
with the standard library select function (as in Socket/stepper.py),
or can call the implementation action in a separate thread (as in
Socket/stepper_a.py).
The test_action method in the stepper may also handle exceptions
raised by the implementation. Any exceptions that are raised by the
implementation that are not handled by the stepper are reported as
test failures by pmt.
Each stepper must also provide a Reset (or reset) function (with no
arguments) that resets the implementation, to support multiple test
runs in a single pmt session.
Revised Mar 2013