Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .github/workflows/autopep8.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: autopep8 Check

on: [pull_request]

jobs:
autopep8-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.10"

- name: Install autopep8
run: pip install autopep8

- name: Check formatting with autopep8
id: check
run: |
# Check if autopep8 would make changes
formatting_issues=$(autopep8 --diff --recursive --max-line-length 120 .)
if [[ formatting_issues ]] then
echo "Formatting issues found:"
printf "%s\n" "$formatting_issues"
echo "------------------------------"
echo "-- Formatting issues found! --"
echo "------------------------------"
exit 1
else
echo "No formatting issues found."
fi

35 changes: 35 additions & 0 deletions .github/workflows/run-demo.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# This workflow installs required Python dependencies and then runs the demo.
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python

name: Run Demo

on:
pull_request:
branches: [ "main" ]

permissions:
contents: read

defaults:
run:
working-directory: ./demo/Titanic

jobs:
build:

runs-on: windows-latest # Deliberately different OS compared to the test run.

steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: "3.14" # Deliberately different Python version compared to the test run.
- name: Install dependencies
run: |
python -m pip install --upgrade pip # upgrade pip to latest version
pip install -r requirements.txt # install demo dependencies
- name: Run basic demo (no repeating scenarios)
run: python run_demo.py miss
- name: Run extended demo
run: python run_demo.py extended
30 changes: 30 additions & 0 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# This workflow installs required Python dependencies and then runs the available tests.
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python

name: Run Acceptance and Unit tests
Comment thread
osingaatje marked this conversation as resolved.

on:
pull_request:
branches: [ "main" ]

permissions:
contents: read

jobs:
build:

runs-on: ubuntu-latest
Comment thread
JFoederer marked this conversation as resolved.

steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: "3.10" # Only the oldest supported Python version is included here
- name: Install dependencies
run: |
python -m pip install --upgrade pip # upgrade pip to latest version
pip install . # install pyproject.toml dependencies - excludes optional dependencies (such as visualisation)
- name: Run tests
run: |
python run_tests.py
Comment thread
osingaatje marked this conversation as resolved.
116 changes: 116 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Contribution guidelines RobotMBT

Welcome! Thank you for considering to contribute to this project. If you haven't already, because your contribution already starts when you use this software and share your experiences with the people around you. These guidelines will help you to further connect with the online community.

## Communication channels

### Slack

If you want to ask or answer questions and participate in discussions, then the [Robot Framework Slack](http://slack.robotframework.org/) channels are a good place to do so.

### GitHub

If you want to get involved on GitHub, you can so by submitting issues or offering code improvements. These guidelines will help you to find your way. These guidelines expect readers to have a basic knowledge about open source as well as why and how to contribute to an open source project. If you are new to these topics, please have a look at the generic [Open Source Guides](https://opensource.guide/) first.

## Code of Conduct

If you want to be part of this community, then we expect you to respect our norms and values. These are in line with the [GitHub Code of Conduct](https://docs.github.com/en/site-policy/github-terms/github-community-code-of-conduct) and the [Slack Code of Conduct](https://docs.slack.dev/community-code-of-conduct/). In short, we expect you to:

- Be welcoming.
- Be kind.
- Look out for each other.

## Submitting issues

Defects and enhancements are tracked in [GitHub Issues](https://github.com/JFoederer/robotframeworkMBT/issues). Before submitting an issue here, please make sure the issue is caused by this project in particular. If you are unsure if something is worth submitting, you can first ask on [Slack](http://slack.robotframework.org/). Before submitting a new issue, it is always a good idea to check if something similar was already reported. If it is, please add your comments to the existing issue instead of creating a new one. Communication in issues on GitHub is done in English.

Take notice that issues do not get resolved by themselves. Someone will need to spend time on the topic. Be prepared to wait, contribute yourself or arrange budget to hire someone for the job.

### Reporting defects

When reporting a defect, be precise and concise in your description. Write in way that helps others understand and reproduce the issue. Screenshots can be very helpful, but when adding logging or other textual information, please keep the textual form.

Note that all information in the issue tracker is public. *Do not include any confidential information there*.

Be sure to add information about:

- The applicable version(s) of RobotMBT (use `pip list` and check for `robotframework-mbt`)
- Your Robot Framework version (use `pip list` and check for `robotframework`)
- Your Python version (check using `python --version`)
- Your operating system
- Your custom settings for RobotMBT (at the library and test suite level)

Version information about Robot Framework, Python and the operating system are also reported at the start of Robot's `output.xml` file.

### Enhancement requests

When proposing an enhancement, a feature request, be clear about the use cases. Who will benefit from the enhancement and in what way? Describe the expected behaviour and use concrete examples to illustrate the intent.

## Code contributions

If you have fixed a defect or implemented an enhancement, you can contribute your changes via a [GitHub Pull Request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request). This is not restricted to implementation code: on the contrary, fixes and enhancements to documentation and tests alone are also very valuable!

### First steps

- [Clone](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) and/or [Fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/about-forks) the RobotMBT repo. If you are not a fan of command line tools, [GitHub Desktop](https://github.com/apps/desktop) can help you.
- [Run the tests](#running-tests) to check your starting point.
- Write new failing tests to cover your intended changes.
- Implement your changes.
- Verify that your tests pass with your implementation.

### Definition of Done

The Definition of Done for RobotMBT is when a pull request is merged. This is to ensure that pull requests are fully self-contained, and leave no open ends.

In other words: when the pull request is merged, it is 100% done. This keeps the main branch ready for release at all times.

This means that for each pull request you need to ensure that:

- [No regression](#non-regression-criteria) is introduced.
- New functionality is covered by [tests](#guidelines-for-writing-new-tests).
- [Code style](#code-style) follows the standard.
- Documentation is up to date.
- The PR branch is 0 commits behind the main branch.

### Running tests

Tests can be executed from the command line by running `python run_tests.py`. This will run all unit tests, followed by the Robot acceptance tests. Use `--help` for additional info.

### Non-regression criteria

The criteria for proving non-regression are:

- All automated regression tests pass
- All supported Python, Robot Framework and OS versions still work (see `pyproject.toml` for supported versions).
- The [demo](https://github.com/JFoederer/robotframeworkMBT/tree/main/demo/Titanic) still works.
- Manual checks are executed to cover the automation's blind spots and subjective elements (e.g. some visual inspection on layout and assessing overall look and feel).

### Guidelines for writing new tests

For this project, we are not maintaining separate requirements documentation. The user documentation explains the software's purpose and scope, while tests further specify its concrete behaviour. Keep this in mind when writing tests and pay extra attention to documenting your test cases: they are more than just bug catchers. If code exists due to a technical limitation rather than a requirement, be sure to document your design decision.

Tests are located in the `atest` and `utest` folders, which stands for _acceptance test_ and _unit test_ respectively. The acceptance tests are Robot tests that cover user-visible behaviour using black-box testing techniques. They typically do not cover all details, unless some Robot Framework interaction is involved. The unit tests go more in-depth, including white box techniques to cover the _dark corners_ of the code. Choose the right type of test for what you are covering.

A specific challenge for this project is that there is a lot of test case generation going on. Be wary that variations in the generation process do not alter the intended coverage of a test and do not yield false positives (passing results without proof for passing), such as checking "_all_" results in an empty list. Lastly: keep the resulting total number of test cases in a run deterministic. This allows for a quick check that all test cases are still being generated.

### Code style

Maintainability is the main driver for coding style. Always write your code with the mindset that you are writing it for someone else, and that this person's experience level is slightly below the average in the project. Code is written following the [PEP 8](https://peps.python.org/pep-0008) Style guide and [SOLID](https://en.wikipedia.org/wiki/SOLID) principles.

#### Formatting

Formatting follows the default rules of [autopep8](https://pypi.org/project/autopep8/) with the exception of the maximum line length (see https://github.com/JFoederer/robotframeworkMBT/tree/main/.github/workflows/autopep8.yml). Note however, that the extended line length is not an invite to always write long lines.

Researchers have suggested that longer lines are better suited for cases when the information will likely be scanned, while shorter lines (45-75 characters) are appropriate when the information is meant to be read thoroughly [[ref.](https://www.academia.edu/6232736/The_influence_of_font_type_and_line_length_on_visual_search_and_information_retrieval_in_web_pages)]. Keep this in mind when writing code and documentation, taking the current indentation level into account.

#### Docstrings, comments and logging

- Docstrings are written using a black-box approach. One should not need to know the inside of a class or function in order to use it.
- Use comments to annotate code for maintainers.
- Prevent trivial comments and use descriptive names to make your code self-explanatory.
- When documenting external interfaces, also check whether the user documentation requires an update.
- Log useful information that is runtime-dependent.
- Information that is useful after a passing test run is logged at info-level.
- Information that is useful for analysing failed tests is logged at debug-level.

- Be careful not to make assumptions in what you log: Recheck log statements if your changes affect the context in which the code is run, and only report about what you know to be true.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,10 @@ If you want to set configuration options for use in multiple test suites without

Tip: [Robot dictionaries](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#dictionary-variable) (`&{ }`) can be used to group related options and pass them as one set.

## Contributing

If you have feedback, ideas, or want to get involved in coding, then check out the [Contribution guidelines](https://github.com/JFoederer/robotframeworkMBT/blob/main/CONTRIBUTING.md).

## Disclaimer

Please note that this library is in a premature state and hasn't reached its first official (1.0) release yet. Developments are ongoing within the context of the [TiCToC](https://tictoc.cs.ru.nl/) research project. Interface changes are still frequent, and no deprecation warnings are being issued yet.
2 changes: 1 addition & 1 deletion atest/robotMBT tests/03__parse_model_info/MyProcessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ def _fail_on_step_errors(self):
if self.in_suite.has_error():
msg = "\n".join(["Error(s) detected in at least one step"] +
[f"{step.kw_wo_gherkin} FAILED: {step.model_info['error']}"
for step in self.in_suite.steps_with_errors()])
for step in self.in_suite.steps_with_errors()])
raise Exception(msg)
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@

from robot.api.deco import library


@library(auto_keywords=None, listener=True)
class SuiteRepeater:
"""
Given a test suite, repeats all scenarios 'repeat' times (default=1)
Setting bonus_scenario=${True} repeats 1 additional time
sub-suites are ignored
"""

def process_test_suite(self, in_suite, repeat=1, **kwargs):
n_repeats = int(repeat)
if kwargs.get('bonus_scenario', False):
n_repeats +=1
n_repeats += 1
out_suite = copy.deepcopy(in_suite)
out_suite.scenarios = n_repeats*out_suite.scenarios
for i in range(len(out_suite.scenarios)):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
from robot.api.deco import keyword


class traces:
ROBOT_LIBRARY_SCOPE = 'GLOBAL'

def reset_traces(self):
self.traces = {}

@keyword("Trace '${trace}', scenario number ${test_id} is executed")
def add_test(self, trace, test_id:str):
def add_test(self, trace, test_id: str):
"""*model info*
:IN: None
:OUT: None
Expand Down
8 changes: 4 additions & 4 deletions demo/Titanic/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# robotMBT Titanic demo
# RobotMBT Titanic demo

## What is it?

The purpose of this demo is to showcase the Model-Based Testing concepts available from the [robotMBT](https://github.com/JFoederer/robotframeworkMBT) library using a [BDD](https://en.wikipedia.org/wiki/Behavior-driven_development) style project. It is based on the principle of [specification by example](https://en.wikipedia.org/wiki/Specification_by_example), using _given-when-then_ style scenarios.
The purpose of this demo is to showcase the Model-Based Testing concepts available from the [RobotMBT](https://github.com/JFoederer/robotframeworkMBT) library using a [BDD](https://en.wikipedia.org/wiki/Behavior-driven_development) style project. It is based on the principle of [specification by example](https://en.wikipedia.org/wiki/Specification_by_example), using _given-when-then_ style scenarios.

Given-steps typically describe preconditions, i.e. state, but classically given-steps are implemented as actions to get to that desired precondition. Now, consider using [specification by example](https://en.wikipedia.org/wiki/Specification_by_example). If your specification is complete, and your examples are consistent, then any given-state must be reachable by operating the system within specification, following the examples. In this demo we use [robotMBT](https://github.com/JFoederer/robotframeworkMBT) to specify a complete story, on varying levels of detail, using small consise scenarios. Then we let [robotMBT](https://github.com/JFoederer/robotframeworkMBT) construct a complete storyline, so we don't have to worry about how to reach all the correct preconditions.
Given-steps typically describe preconditions, i.e. state, but classically given-steps are implemented as actions to get to that desired precondition. Now, consider using [specification by example](https://en.wikipedia.org/wiki/Specification_by_example). If your specification is complete, and your examples are consistent, then any given-state must be reachable by operating the system within specification, following the examples. In this demo we use [RobotMBT](https://github.com/JFoederer/robotframeworkMBT) to specify a complete story, on varying levels of detail, using small consise scenarios. Then we let [RobotMBT](https://github.com/JFoederer/robotframeworkMBT) construct a complete storyline, so we don't have to worry about how to reach all the correct preconditions.

Please keep in mind that the library and this demo are still in the early development phases and offered functionality is still limited. However, in good agile spirit, we still wanted to publish the results.

Expand All @@ -18,7 +18,7 @@ There are a total of 7 scenarios in this demo, 10 if you use the extended varian

It might seem odd at first, seeing the [story of Titanic](https://en.wikipedia.org/wiki/Sinking_of_the_Titanic) in a context that is typically used in more technical environments. However, the [BDD](https://en.wikipedia.org/wiki/Behavior-driven_development) process is mostly non-technical and using a topic like Titanic helps to prevent technical bias. Another important point is that we want to stick to writing and maintaining short, to-the-point scenarios. From these, we want to compose larger scenarios, describing behaviour of complex systems, start to end, like telling a story. What better use for that than a well known story?

The [story of Titanic](https://en.wikipedia.org/wiki/Sinking_of_the_Titanic) fits these criteria and, since it already happened, there should be little discussion on the specification. It will be interesting to see if the test case generation mechanism from [robotMBT](https://github.com/JFoederer/robotframeworkMBT) can reconstruct the familiar story, and then some variations thereof. After all, the maiden voyage of Titanic was just one example of what could have happened...
The [story of Titanic](https://en.wikipedia.org/wiki/Sinking_of_the_Titanic) fits these criteria and, since it already happened, there should be little discussion on the specification. It will be interesting to see if the test case generation mechanism from [RobotMBT](https://github.com/JFoederer/robotframeworkMBT) can reconstruct the familiar story, and then some variations thereof. After all, the maiden voyage of Titanic was just one example of what could have happened...

## Running the demo

Expand Down
1 change: 1 addition & 0 deletions demo/Titanic/domain_lib/JourneyLib.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from simulation.titanic_in_ocean import TitanicInOcean
from simulation.journey import Journey


class JourneyLib:
_journey = None

Expand Down
18 changes: 10 additions & 8 deletions demo/Titanic/run_demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,16 @@
OUTPUT_ROOT = os.path.join(THIS_DIR, 'results')
SCENARIO_FOLDER = os.path.join(THIS_DIR, 'Titanic_scenarios')
HIT_MISS_TAG = 'hit' if len(sys.argv) == 1 or sys.argv[1].casefold() != 'hit' else 'miss'
EXTENDED_TAG = 'extended' if len(sys.argv) == 1 or sys.argv[1].casefold() != 'extended' else 'dummy'
EXTENDED_TAG = 'extended' if len(sys.argv) == 1 or sys.argv[1].casefold() != 'extended' else 'dummy'

# The base folder needs to be added to the python path to resolve the dependencies. You
# will also need to add this path to your IDE options when running from there.
robot.run_cli(['--outputdir', OUTPUT_ROOT,
'--pythonpath', THIS_DIR,
'--exclude', HIT_MISS_TAG,
'--exclude', EXTENDED_TAG,
'--loglevel', 'DEBUG:INFO',
SCENARIO_FOLDER],
exit=False)
exitcode = robot.run_cli(['--outputdir', OUTPUT_ROOT,
'--pythonpath', THIS_DIR,
'--exclude', HIT_MISS_TAG,
'--exclude', EXTENDED_TAG,
'--loglevel', 'DEBUG:INFO',
SCENARIO_FOLDER],
exit=False)

sys.exit(exitcode)
Loading