-
Notifications
You must be signed in to change notification settings - Fork 0
/
fsmvalidator.py
104 lines (82 loc) · 3.13 KB
/
fsmvalidator.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
import argparse
import importlib.util
from functools import partial
from hypothesis import strategies as st, settings
from hypothesis.reporting import reporter, report
from hypothesis.stateful import (
RuleBasedStateMachine,
Bundle,
rule,
run_state_machine_as_test,
)
import config
from fsmgenerator import generate
def create_reference_fsm(states, inputs, outputs, seed):
return generate(states, inputs, outputs, seed)
def create_concrete_fsm(concrete_implementation_file_path):
module_name = "concrete_fsm"
spec = importlib.util.spec_from_file_location(
module_name, concrete_implementation_file_path
)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
assert hasattr(
module, "FiniteStateMachine"
), "File don't have FiniteStateMachine class"
return module.FiniteStateMachine()
def state_machine_factory(path, seed, states, inputs, outputs):
create_concrete_fsm_partial = partial(create_concrete_fsm, path)
create_reference_fsm_partial = partial(
create_reference_fsm, states, inputs, outputs, seed
)
class FiniteStateMachine(RuleBasedStateMachine):
output = Bundle("output")
def __init__(self):
super().__init__()
self.concrete_fsm = create_concrete_fsm_partial()
self.reference_fsm = create_reference_fsm_partial()
@rule(target=output, input=st.sampled_from(inputs))
def tick(self, input):
concrete_output = self.concrete_fsm.tick(input)
reference_output = self.reference_fsm.tick(input)
return reference_output, concrete_output
@rule(output=output)
def check_output(self, output):
reference_output, concrete_output = output
assert (
reference_output == concrete_output
), f"State machine produce wrong output, expected: {reference_output}, got: {concrete_output}"
return FiniteStateMachine()
def parser():
parser = argparse.ArgumentParser(
description="Validate finite state machine implementation"
)
parser.add_argument("seed", type=str, help="random seed used to generate fsm")
parser.add_argument("path", type=str, help="path to fsm implementation")
return parser
def custom_reporter(value):
"""
Custom reporter used to slightly modify hypothesis output
"""
textified = f"{value}".replace("state", "machine")
if "teardown" not in textified and "check" not in textified:
print(textified)
if __name__ == "__main__":
args = parser().parse_args()
with reporter.with_value(custom_reporter):
try:
run_state_machine_as_test(
lambda: state_machine_factory(
args.path,
args.seed,
config.states,
config.inputs,
config.outputs,
),
settings=settings(max_examples=1000),
)
except AssertionError as e:
report(e)
report(
"Implementation contains errors, correct them and try again!",
)