Skip to content

Commit fcd58b1

Browse files
authored
Merge pull request #47: Algorithm update - Combine targeted exploration with randomisation
2 parents 9b23c68 + 39965a4 commit fcd58b1

15 files changed

Lines changed: 397 additions & 111 deletions

.github/workflows/run-tests.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,13 @@ jobs:
2525
- name: Install dependencies (without extra visualisation dependencies)
2626
run: |
2727
python -m pip install --upgrade pip # upgrade pip to latest version
28-
pip install . # no additional [visualisation] dependencies
28+
pip install . # using the minimum installation
2929
3030
- name: Run tests (without visualisation dependencies)
3131
run: python run_tests.py
3232

3333
- name: Install extra visualisation dependencies
34-
run: pip install ".[visualisation]" # extra [visualisation] dependencies in pyproject.toml
34+
run: pip install .[full]
3535

3636
- name: Run Tests (with visualisation dependencies)
3737
run: python run_tests.py
38-

README.md

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,28 @@ RobotMBT offers features to cover both _when_ and _what_ variations.
2424

2525
RobotMBT is suitable for sequencing complete scenarios, including action refinement for when-steps. Concrete example scenarios can be generalised for added data-driven variation. When all steps are properly annotated with modelling info, the library can resolve their dependencies and figure out the correct execution order. Each run a new test sequence is generated from the available options.
2626

27-
To be successful, the set of scenarios in the model must (for now) be composable into a single complete sequence, without leftovers. The same scenario can be inserted multiple times if repetition helps to reach the entry condition for later scenarios.
27+
To be successful, the set of scenarios in the model must (for now) be composable into a single complete sequence, without leftovers. The same scenario can be inserted into the trace multiple times, creating loops, if repetition helps to reach the entry condition for later scenarios. Dead ends should be prevented, i.e., sequences from which there is no way forward and no way to loop back.
2828

2929
## Getting started
3030

31-
To get a feel for what this library can do, have a look at our [Titanic themed demo](https://github.com/JFoederer/robotframeworkMBT/tree/main/demo/Titanic), which is executable as a [Robot framework](https://robotframework.org/) test suite.
32-
3331
The recommended installation method is using [pip](http://pip-installer.org)
3432

33+
pip install --upgrade robotframework-mbt[full]
34+
35+
The full installation includes additional graphical dependencies to help during model creation. These are optional. The minimum installation includes everything you need to create and run model-based test suites.
36+
3537
pip install --upgrade robotframework-mbt
3638

37-
After installation include `robotmbt` as library in your robot file to get access to the new functionality. To run your test suite model-based, use the __Treat this test suite model-based__ keyword as suite setup. Check the _How to model_ section to learn how to make your scenarios suitable for running model-based.
39+
After installation, include `robotmbt` as library in your robot file to get access to the new functionality. To run your test suite model-based, use the __Treat this test suite model-based__ keyword as suite setup. Check the _How to model_ section to learn how to make your scenarios suitable for running model-based.
3840

3941
```robotframework
4042
*** Settings ***
4143
Library robotmbt
4244
Suite Setup Treat this test suite model-based
4345
```
4446

47+
To get a feel for what this library can do, have a look at our [Titanic themed demo](https://github.com/JFoederer/robotframeworkMBT/tree/main/demo/Titanic), which is executable as a [Robot framework](https://robotframework.org/) test suite.
48+
4549
## How to model
4650

4751
Modelling can be done directly from [Robot framework](https://robotframework.org/), without the need for additional tooling besides [RobotMBT](https://github.com/JFoederer/robotframeworkMBT).
@@ -211,14 +215,14 @@ Using `seed=new` will force generation of a new reusable seed and is identical t
211215

212216
A graph can be included in the log file to visualise how scenarios are linked. This helps in understanding a test suite's structure and reveals alternative paths that did not make it into the final trace.
213217

214-
To enable graph generation, some extra dependencies must be installed: `pip install robotframework-mbt[visualisation]`
215-
216218
Generate the graph by setting the graph style for the model-based suite. The graph will be included in the Robot log file as part of the keyword's logging.
217219

218220
```robotframework
219221
Treat this test suite Model-based graph=scenario
220222
```
221223

224+
_Note_: The graphing functionality requires the _full_ installation package: `pip install robotframework-mbt[full]`
225+
222226
Available graph styles:
223227

224228
* scenario

atest/robotMBT tests/07__processor_options/random_seeds/02__reusing_seed_reproduces_trace.robot

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Documentation This suite uses the `seed` argument to reproduce a previously
55
... indicated by the number string.
66
Suite Setup Run keywords Set suite variable ${trace} ${empty}
77
... AND Treat this test suite Model-based seed=aqmou-eelcuu-sniu-ugsyek-jyhoor
8-
Suite Teardown Should be equal ${trace} 6930142758
8+
Suite Teardown Should be equal ${trace} 6781950324
99
Library robotmbt
1010

1111
*** Test Cases ***

atest/robotMBT tests/07__processor_options/random_seeds/03__retrace_with_refinement.robot

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Documentation This suite uses the `seed` argument to reproduce a previously
99
... traces possible of varying length.
1010
Suite Setup Run keywords Set suite variable ${trace} ${empty}
1111
... AND Treat this test suite Model-based seed=xou-pumj-ihj-oibiyc-surer
12-
Suite Teardown Should be equal ${trace} B1A5BY3B4BX6B2
12+
Suite Teardown Should be equal ${trace} AY3B2B4B5BX1A6
1313
Library robotmbt
1414

1515
*** Test Cases ***

atest/robotMBT tests/07__processor_options/random_seeds/04__retrace_with_step_modifiers.robot

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Documentation This suite uses the `seed` argument to reproduce a previously
66
... included, the modifers will number the scenarios at random. The reproduced trace
77
... must match for both the scenario order and the data order.
88
Suite Setup Treat this test suite Model-based seed=iulr-vih-esycu-eyl-yfa
9-
Suite Teardown Should be equal ${trace} H6G3E5I4D9F8J2B1A7C0
9+
Suite Teardown Should be equal ${trace} H0J3G7F9E5C8A1I2D4B6
1010
Library robotmbt
1111

1212
*** Test Cases ***

atest/robotMBT tests/07__processor_options/random_seeds/05__retrace_combined.robot

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ Documentation This suite uses the `seed` argument to reproduce a previously
1414
... The looping part is enhanced with refinement. The high-level scenario includes
1515
... a data choice by using step modifiers. The lower level includes a path choice.
1616
... Both low-level scenarios are equally valid, the only difference is that the data
17-
... choice is included either once or twice.
18-
Suite Setup Treat this test suite Model-based seed=gujuqt-iakm-oexo-xnu-huba
19-
Suite Teardown Should be equal ${trace} ATQQPRSPRPXY
17+
... choice is included either once (single letter) or twice (letter repeated).
18+
Suite Setup Treat this test suite Model-based seed=efyuti-oan-naimfe-noelua-ler
19+
Suite Teardown Should be equal ${trace} ATTSPRRPPXY
2020
Library robotmbt
2121

2222
*** Test Cases ***

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
77

88
[project]
99
name = "robotframework-mbt"
10-
version = "0.11.0"
10+
version = "0.12.0"
1111
description = "Model-Based Testing in Robot framework with test case generation"
1212
readme = "README.md"
1313
authors = [{ name = "Johan Foederer", email = "github@famfoe.nl" }]
@@ -30,4 +30,4 @@ include = ["robotmbt*"]
3030
Homepage = "https://github.com/JFoederer/robotframeworkMBT"
3131

3232
[project.optional-dependencies]
33-
visualisation = ["networkx", "bokeh", "grandalf", "jsonpickle"]
33+
full = ["networkx", "bokeh", "grandalf", "jsonpickle"]

robotmbt/modeller.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,11 @@ def try_to_fit_in_scenario(candidate: Scenario, tracestate: TraceState):
5757
elif not remainder: # the scenario processed in full
5858
model.end_scenario_scope()
5959
tracestate.confirm_full_scenario(inserted.src_id, inserted, model)
60-
logger.debug(f"Inserted scenario {inserted.src_id}, {inserted.name}")
60+
logger.debug(f"Scenario {inserted.src_id} inserted: {inserted.name}")
6161
if tracestate.is_refinement_active():
6262
handle_refinement_exit(inserted, tracestate)
6363
else: # the scenario is split into two parts, ready for refinement
64-
logger.debug(f"Partially inserted scenario {inserted.src_id}, {inserted.name}\n"
64+
logger.debug(f"Scenario {inserted.src_id} partially inserted: {inserted.name}\n"
6565
f"Refinement needed at step: {remainder.steps[1]}")
6666
inserted.name = f"{inserted.name} (part {tracestate.highest_part(inserted.src_id)+1})"
6767
tracestate.push_partial_scenario(inserted.src_id, inserted, model, remainder)
@@ -79,12 +79,12 @@ def process_scenario(scenario: Scenario, model: ModelSpace) -> tuple[Scenario, S
7979
part1, part2 = split_for_refinement(scenario, step)
8080
return part1, part2, dict()
8181
else:
82-
return None, None, dict(fail_msg=f"Unable to insert scenario {scenario.src_id}, "
82+
return None, None, dict(fail_msg=f"Rejecting scenario {scenario.src_id}, "
8383
f"{scenario.name}, due to step '{step}': [{expr}] is False")
8484
except TimeoutExceeded:
8585
raise
8686
except Exception as err:
87-
return None, None, dict(fail_msg=f"Unable to insert scenario {scenario.src_id}, "
87+
return None, None, dict(fail_msg=f"Rejecting scenario {scenario.src_id}, "
8888
f"{scenario.name}, due to step '{step}': [{expr}] {err}")
8989
return scenario.copy(), None, dict()
9090

@@ -141,8 +141,8 @@ def handle_refinement_exit(inserted_refinement: Scenario, tracestate: TraceState
141141

142142
if not exit_conditions_processed:
143143
rewind(tracestate) # Reject insterted scenario. Even though it fits, it is not a refinement.
144-
logger.debug(f"Reconsidering scenario {inserted_refinement.src_id}, {inserted_refinement.name}, "
145-
f"did not meet refinement exit condition: {exit_conditions}")
144+
logger.debug(f"Reconsidering scenario {inserted_refinement.src_id}, "
145+
f"refinement exit condition not met for scenario {refinement_tail.src_id}: {exit_conditions}")
146146
return
147147

148148
model = tracestate.model
@@ -157,7 +157,7 @@ def handle_refinement_exit(inserted_refinement: Scenario, tracestate: TraceState
157157
elif not remainder:
158158
model.end_scenario_scope()
159159
tracestate.confirm_full_scenario(tail_inserted.src_id, tail_inserted, model)
160-
logger.debug(f"Scenario '{tail_inserted.name}' completed after refinement")
160+
logger.debug(f"Scenario {tail_inserted.src_id} completed after refinement: {tail_inserted.name}")
161161
if tracestate.is_refinement_active():
162162
handle_refinement_exit(tail_inserted, tracestate)
163163
else:
@@ -219,20 +219,20 @@ def generate_scenario_variant(scenario: Scenario, model: ModelSpace) -> Scenario
219219
except TimeoutExceeded:
220220
raise
221221
except Exception as err:
222-
logger.debug(f"Unable to insert scenario {scenario.src_id}, {scenario.name}, due to modifier\n"
222+
logger.debug(f"Rejecting scenario {scenario.src_id}, {scenario.name}, due to modifier\n"
223223
f" In step {step}: {err}")
224224
return None
225225

226226
try:
227227
subs.solve()
228228
except ValueError as err:
229-
logger.debug(f"Unable to insert scenario {scenario.src_id}, {scenario.name}, due to modifier\n"
229+
logger.debug(f"Rejecting scenario {scenario.src_id}, {scenario.name}, due to modifier\n"
230230
f" {err}: {subs}")
231231
return None
232232

233233
# Update scenario with generated values
234234
if subs.solution:
235-
logger.debug(f"Example variant generated with argument substitution: {subs}")
235+
logger.debug(f"Example variant generated with argument substitution (↦ replaced by): {subs}")
236236
scenario.data_choices = subs
237237
for step in scenario.steps:
238238
if 'MOD' in step.model_info:

robotmbt/substitutionmap.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def __init__(self):
4949

5050
def __str__(self):
5151
src = self.solution or self.substitutions
52-
return ", ".join([f"{k} {v}" for k, v in src.items()])
52+
return ", ".join([f"{k} {v}" for k, v in src.items()])
5353

5454
def copy(self):
5555
new = SubstitutionMap()

0 commit comments

Comments
 (0)