Skip to content

Commit ec27baa

Browse files
committed
move export/import dependencies to one place
1 parent 772fa51 commit ec27baa

5 files changed

Lines changed: 43 additions & 23 deletions

File tree

atest/resources/helpers/modelgenerator.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ def check_requirements(self) -> bool:
6161
return visualisation_deps_present
6262

6363
@keyword(name='Generate Trace Information') # type:ignore
64-
def generate_trace_information(self) -> TraceInfo:
65-
return TraceInfo()
64+
def generate_trace_information(self, suite_name) -> TraceInfo:
65+
return TraceInfo(suite_name)
6666

6767
@keyword(name='The Algorithm Inserts') # type:ignore
6868
def insert_trace_info(self, trace_info: TraceInfo, scenario_name: str, state_str: str | None = None) -> TraceInfo:
@@ -103,13 +103,13 @@ def generate_networkgraph(self, graph: AbstractGraph) -> NetworkVisualiser:
103103
return NetworkVisualiser(graph=graph, suite_name="")
104104

105105
@keyword(name='Export Graph') # type:ignore
106-
def export_graph(self, suite: str, trace_info: TraceInfo) -> str:
107-
return trace_info.export_graph(suite, atest=True)
106+
def export_graph(self, trace_info: TraceInfo) -> str:
107+
return trace_info.export_graph(atest=True)
108108

109109
@keyword(name='Import Graph') # type:ignore
110110
def import_graph(self, filepath: str) -> TraceInfo:
111111
suiteprocessor = SuiteProcessors()
112-
suiteprocessor._load_graph('scenario', 'atest', filepath)
112+
suiteprocessor._load_graph('scenario', filepath)
113113
return suiteprocessor.visualiser.trace_info
114114

115115
@keyword(name='Check File Exists') # type:ignore

atest/resources/visualisation.resource

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ test suite ${suite} has trace info ${trace}
1818
... :IN: new trace_info | trace_info.current_trace=0 | trace_info.all_traces=0 |
1919
... trace_info.name = ${trace} | new graph
2020
... :OUT: None
21-
${trace_info} = Generate Trace Information
21+
${trace_info} = Generate Trace Information ${suite}
2222
Set Suite Variable ${trace_info}
2323

2424
trace info ${trace}
@@ -184,7 +184,7 @@ test suite ${suite} is exported to json
184184
Variable Should Exist ${suite}
185185
Variable Should Exist ${trace_info}
186186

187-
${filepath} = Export Graph ${suite} ${trace_info}
187+
${filepath} = Export Graph ${trace_info}
188188
Set Suite Variable ${filepath}
189189

190190
the file ${filename} exists

robotmbt/suiteprocessors.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
try:
4545
from .visualise.visualiser import Visualiser
4646
from .visualise.models import TraceInfo
47-
import jsonpickle
4847
visualisation_deps_present = True
4948
except ImportError:
5049
Visualiser = None
@@ -94,7 +93,7 @@ def process_test_suite(self, in_suite: Suite, *, seed: str | int | bytes | bytea
9493
self.flat_suite = self.flatten(in_suite)
9594

9695
if import_graph_data != '':
97-
self._load_graph(graph, in_suite.name, import_graph_data)
96+
self._load_graph(graph, import_graph_data)
9897

9998
else:
10099
self._run_test_suite(seed, graph, in_suite.name, export_graph_data)
@@ -103,16 +102,15 @@ def process_test_suite(self, in_suite: Suite, *, seed: str | int | bytes | bytea
103102

104103
return self.out_suite
105104

106-
def _load_graph(self, graph: str, suite_name: str, file_path: str):
105+
def _load_graph(self, graph: str, file_path: str):
107106
"""
108107
Imports a JSON encoding of a graph and reconstructs the graph from it. The reconstructed graph overrides the
109108
current graph instance this method is called on.
110-
file_path: the relative path to the graph JSON.
109+
file_path: the path to a previously exported graph.
111110
"""
112-
with open(file_path, "r") as f:
113-
string = f.read()
114-
traceinfo = jsonpickle.decode(string)
115-
self.visualiser = Visualiser(graph, suite_name, trace_info=traceinfo)
111+
if Visualiser:
112+
self.visualiser = Visualiser(graph)
113+
self.visualiser.load_from_file(file_path)
116114

117115
def _run_test_suite(self, seed: str | int | bytes | bytearray, graph: str, suite_name: str, export_dir: str):
118116
for id, scenario in enumerate(self.flat_suite.scenarios, start=1):
@@ -251,7 +249,7 @@ def _report_tracestate_wrapup(tracestate: TraceState):
251249
logger.debug(f"model\n{progression.model.get_status_text()}\n")
252250

253251
@staticmethod
254-
def _init_randomiser(seed: str | int | bytes | bytearray):
252+
def _init_randomiser(seed: str | int | bytes | bytearray | None):
255253
if isinstance(seed, str):
256254
seed = seed.strip()
257255
if str(seed).lower() == 'none':

robotmbt/visualise/models.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -195,8 +195,11 @@ class TraceInfo:
195195
- all_traces: all valid traces encountered in trace exploration, up until the point they could not go any further
196196
- previous_length: used to identify backtracking
197197
"""
198+
ROBOTMBT_MODEL_VERSION = '1.0.0'
198199

199-
def __init__(self):
200+
def __init__(self, name: str = ''):
201+
self.ROBOTMBT_MODEL_VERSION: str = TraceInfo.ROBOTMBT_MODEL_VERSION
202+
self.model_name: str = name
200203
self.current_trace: list[tuple[ScenarioInfo, StateInfo]] = []
201204
self.all_traces: list[list[tuple[ScenarioInfo, StateInfo]]] = []
202205
self.previous_length: int = 0
@@ -256,14 +259,14 @@ def _sanity_check(self, scen: ScenarioInfo, state: StateInfo, after: str):
256259
logger.warn(
257260
f'TraceInfo got out of sync after {after}\nExpected state: {prev_state}\nActual state: {state}')
258261

259-
def export_graph(self, suite_name: str, dir: str = '', atest: bool = False) -> str | None:
262+
def export_graph(self, dir: str = '', atest: bool = False) -> str | None:
260263
encoded_instance = jsonpickle.encode(self)
261-
name = suite_name.lower().replace(' ', '_')
264+
name = self.model_name.lower().replace(' ', '_')
262265
if atest:
263266
'''
264267
temporary file to not accidentally overwrite an existing file
265268
mkstemp() is not ideal but given Python's limitations this is the easiest solution
266-
as temporary file, a different method, is problematic on Windows
269+
as temporary file, a different method, is problematic on Windows
267270
https://stackoverflow.com/a/57015383
268271
'''
269272
fd, dir = tempfile.mkstemp()
@@ -282,6 +285,22 @@ def export_graph(self, suite_name: str, dir: str = '', atest: bool = False) -> s
282285
f.write(encoded_instance)
283286
return None
284287

288+
@staticmethod
289+
def import_graph_from_file(file_path: str) -> TraceInfo:
290+
try:
291+
with open(file_path, "r") as f:
292+
traceinfo = jsonpickle.decode(f.read())
293+
traceinfo.ROBOTMBT_MODEL_VERSION # trigger AttributeError if non-robotmbt data
294+
except OSError:
295+
raise
296+
except Exception:
297+
raise TypeError(f"Contents from '{file_path}' could not be loaded as RobotMBT graph data")
298+
299+
if str(traceinfo.ROBOTMBT_MODEL_VERSION).split('.')[0] != TraceInfo.ROBOTMBT_MODEL_VERSION.split('.')[0]:
300+
# raise if major version differs
301+
raise ValueError("Graph data is from an incompatible RobotMBT version")
302+
return traceinfo
303+
285304
@staticmethod
286305
def stringify_pair(pair: tuple[ScenarioInfo, StateInfo]) -> str:
287306
"""

robotmbt/visualise/visualiser.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,16 @@ def __init__(self, graph_type: str, suite_name: str = "", export: str = '', trac
6363
self.graph_type: str = graph_type
6464

6565
if trace_info is None:
66-
self.trace_info: TraceInfo = TraceInfo()
66+
self.trace_info: TraceInfo = TraceInfo(suite_name)
6767
else:
6868
self.trace_info = trace_info
6969

7070
self.suite_name = suite_name
7171
self.export = export
7272

73+
def load_from_file(self, file_path: str):
74+
self.trace_info = TraceInfo.import_graph_from_file(file_path)
75+
7376
def update_trace(self, trace: TraceState):
7477
"""
7578
Uses the new snapshots from trace to update the trace info.
@@ -110,14 +113,14 @@ def generate_visualisation(self) -> tuple[str, bool]:
110113
The boolean signals whether the output is in HTML format or not.
111114
"""
112115
if self.export:
113-
self.trace_info.export_graph(self.suite_name, self.export)
116+
self.trace_info.export_graph(self.export)
114117

115118
graph: AbstractGraph = self._get_graph()
116119
if graph is None and self.export:
117120
return f"Successfully exported to {self.export}!", False
118121
elif graph is None:
119122
raise ValueError(f"Unknown graph type: {self.graph_type}")
120123

121-
html_bokeh = networkvisualiser.NetworkVisualiser(graph, self.suite_name).generate_html()
124+
html_bokeh = networkvisualiser.NetworkVisualiser(graph, self.trace_info.model_name).generate_html()
122125

123126
return f'<iframe srcdoc="{html.escape(html_bokeh)}" width="{networkvisualiser.OUTER_WINDOW_WIDTH}px" height="{networkvisualiser.OUTER_WINDOW_HEIGHT}px"></iframe>', True

0 commit comments

Comments
 (0)