Skip to content

Commit f067277

Browse files
committed
reuse scenario indexes for TraceState
1 parent 4af34b9 commit f067277

4 files changed

Lines changed: 233 additions & 230 deletions

File tree

robotmbt/suiteprocessors.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ def process_test_suite(self, in_suite, *, seed='new'):
105105
return self.out_suite
106106

107107
def _try_to_reach_full_coverage(self, allow_duplicate_scenarios):
108-
self.tracestate = TraceState(len(self.scenarios))
108+
self.tracestate = TraceState(range(len(self.scenarios)))
109109
self.active_model = ModelSpace()
110110
while not self.tracestate.coverage_reached():
111111
i_candidate = self.tracestate.next_candidate(retry=allow_duplicate_scenarios)

robotmbt/tracestate.py

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -31,58 +31,63 @@
3131
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3232

3333
class TraceState:
34-
def __init__(self, n_scenarios):
35-
self._c_pool = [False] * n_scenarios # coverage pool: True means scenario is in trace
34+
def __init__(self, scenario_indexes):
35+
if len(scenario_indexes) != len(set(scenario_indexes)):
36+
raise ValueError("Scenarios must be uniquely identifiable")
37+
self.c_pool = {index: 0 for index in scenario_indexes}
3638
self._tried = [[]] # Keeps track of the scenarios already tried at each step in the trace
37-
self._trace = [] # Choice trace, when was which scenario inserted (e.g. ['1', '2.1', '3', '2.0'])
3839
self._snapshots = [] # Keeps details for elements in trace
3940
self._open_refinements = []
4041

4142
@property
4243
def model(self):
4344
"""returns the model as it is at the end of the current trace"""
44-
return self._snapshots[-1].model if self._trace else None
45+
return self._snapshots[-1].model if self._snapshots else None
4546

4647
@property
4748
def tried(self):
4849
"""returns the indices that were rejected or previously inserted at the current position"""
4950
return tuple(self._tried[-1])
5051

5152
def coverage_reached(self):
52-
return all(self._c_pool)
53+
return all(self.c_pool.values())
5354

5455
@property
5556
def coverage_drought(self):
5657
"""Number of scenarios since last new coverage"""
5758
return self._snapshots[-1].coverage_drought if self._snapshots else 0
5859

60+
@property
61+
def id_trace(self):
62+
return [snap.id for snap in self._snapshots]
63+
5964
def get_trace(self):
6065
return [snap.scenario for snap in self._snapshots]
6166

6267
def next_candidate(self, retry=False):
63-
for i in range(len(self._c_pool)):
68+
for i in self.c_pool:
6469
if i not in self._tried[-1] and not self._is_refinement_active(i) and self.count(i) == 0:
6570
return i
6671
if not retry:
6772
return None
68-
for i in range(len(self._c_pool)):
73+
for i in self.c_pool:
6974
if i not in self._tried[-1] and not self._is_refinement_active(i):
7075
return i
7176
return None
7277

7378
def count(self, index):
7479
"""Count the number of times the index is present in the trace.
7580
unfinished partial scenarios are excluded."""
76-
return self._trace.count(str(index)) + self._trace.count(str(f"{index}.0"))
81+
return self.c_pool[index]
7782

7883
def highest_part(self, index):
7984
"""Given the current trace and an index, returns the highest part number of an ongoing
8085
refinement for the related scenario. Returns 0 when there is no refinement active."""
81-
for i in range(1, len(self._trace)+1):
82-
if self._trace[-i] == f'{index}':
86+
for i in range(1, len(self.id_trace)+1):
87+
if self.id_trace[-i] == f'{index}':
8388
return 0
84-
if self._trace[-i].startswith(f'{index}.'):
85-
return int(self._trace[-i].split('.')[1])
89+
if self.id_trace[-i].startswith(f'{index}.'):
90+
return int(self.id_trace[-i].split('.')[1])
8691
return 0
8792

8893
def _is_refinement_active(self, index):
@@ -91,7 +96,7 @@ def _is_refinement_active(self, index):
9196
def find_scenarios_with_active_refinement(self):
9297
scenarios = []
9398
for i in self._open_refinements:
94-
index = -self._trace[::-1].index(f'{i}.1')-1
99+
index = -self.id_trace[::-1].index(f'{i}.1')-1
95100
scenarios.append(self._snapshots[index].scenario)
96101
return scenarios
97102

@@ -100,19 +105,15 @@ def reject_scenario(self, i_scenario):
100105
self._tried[-1].append(i_scenario)
101106

102107
def confirm_full_scenario(self, index, scenario, model):
103-
if not self._c_pool[index]:
104-
self._c_pool[index] = True
105-
c_drought = 0
106-
else:
107-
c_drought = self.coverage_drought+1
108+
c_drought = 0 if self.c_pool[index] == 0 else self.coverage_drought+1
109+
self.c_pool[index] += 1
108110
if self._is_refinement_active(index):
109111
id = f"{index}.0"
110112
self._open_refinements.pop()
111113
else:
112114
id = str(index)
113115
self._tried[-1].append(index)
114116
self._tried.append([])
115-
self._trace.append(id)
116117
self._snapshots.append(TraceSnapShot(id, scenario, model, c_drought))
117118

118119
def push_partial_scenario(self, index, scenario, model):
@@ -123,29 +124,28 @@ def push_partial_scenario(self, index, scenario, model):
123124
self._tried[-1].append(index)
124125
self._tried.append([])
125126
self._open_refinements.append(index)
126-
self._trace.append(id)
127127
self._snapshots.append(TraceSnapShot(id, scenario, model, self.coverage_drought))
128128

129129
def can_rewind(self):
130-
return len(self._trace) > 0
130+
return len(self._snapshots) > 0
131131

132132
def rewind(self):
133-
id = self._trace.pop()
133+
id = self._snapshots[-1].id
134134
index = int(id.split('.')[0])
135+
self._snapshots.pop()
135136
if id.endswith('.0'):
136-
self._snapshots.pop()
137+
self.c_pool[index] -= 1
137138
self._open_refinements.append(index)
138-
while self._trace[-1] != f"{index}.1":
139+
while self._snapshots[-1].id != f"{index}.1":
139140
self.rewind()
140141
return self.rewind()
141142

142-
self._snapshots.pop()
143-
if '.' not in id or id.endswith('.1'):
144-
if self.count(index) == 0:
145-
self._c_pool[index] = False
143+
if '.' not in id:
144+
self._tried.pop()
145+
self.c_pool[index] -= 1
146+
if id.endswith('.1'):
146147
self._tried.pop()
147-
if id.endswith('.1'):
148-
self._open_refinements.pop()
148+
self._open_refinements.pop()
149149
return self._snapshots[-1] if self._snapshots else None
150150

151151
def __iter__(self):

0 commit comments

Comments
 (0)