- CitrineOS core extracted (CSMS OCPP 2.0.1) - OpenOCPP extracted (firmware OCPP 1.6J/2.0.1) - ShapeShifter library installed (pip install -e) - ShapeShifter specification extracted - EVerest extracted TODO updated with progress
211 lines
9.2 KiB
Markdown
211 lines
9.2 KiB
Markdown
# EVerest tests
|
|
|
|
This folder contains the basic test functions for EVerest's e2e testing. You can either extend the '*\*_tests.py*' files in the "EVerest/**tests/core_tests**" folder or write your own!
|
|
|
|
## Prerequisites
|
|
|
|
In order to run any of the e2e tests, you need to have EVerest compiled
|
|
and installed on your system. Please also make sure to install the python
|
|
requirements.
|
|
|
|
```bash
|
|
cd EVerest/
|
|
cmake -S . -B build -DBUILD_TESTING=ON
|
|
cmake --build build --target install --parallel -j$(nproc)
|
|
. build/venv/bin/activate
|
|
cmake --build build --target everestpy_pip_install_dist # install everestpy
|
|
cmake --build build --target everest-testing_pip_install_dist # install everest-testing
|
|
cmake --build build --target iso15118_pip_install_dist # install iso15118 for ev side simulation
|
|
python3 -m pip install "aiofile>=3.7.4"
|
|
python3 -m pip install "netifaces>=0.11.0"
|
|
```
|
|
|
|
## Execute locally
|
|
|
|
All test suites are run via the unified `run-tests.sh` script:
|
|
|
|
```bash
|
|
cd ~/checkout/everest-workspace/EVerest/tests
|
|
|
|
./run-tests.sh all # all tests (core, framework, async, OCPP)
|
|
./run-tests.sh core # core tests only
|
|
./run-tests.sh framework # framework tests only
|
|
./run-tests.sh asyncapi # async API tests only
|
|
./run-tests.sh integration # core + framework + async
|
|
./run-tests.sh ocpp # all OCPP tests (1.6, 2.0.1, 2.1)
|
|
./run-tests.sh --serial all # run serially (no parallelism)
|
|
./run-tests.sh -j4 all # limit to 4 parallel workers
|
|
./run-tests.sh --help # show all options
|
|
```
|
|
|
|
The script automatically sets up network isolation for parallel ISO 15118 tests
|
|
(requires sudo or CAP_NET_ADMIN) and falls back to sequential execution otherwise.
|
|
|
|
After execution `result.xml` and `report.html` are written to the `tests/` directory.
|
|
|
|
### Running individual tests directly
|
|
|
|
For a single test file or test case, invoke `pytest` directly from the `tests/` directory:
|
|
|
|
```bash
|
|
cd ~/checkout/everest-workspace/EVerest/tests
|
|
|
|
# Run a single test file
|
|
python3 -m pytest core_tests/smoke_tests.py
|
|
|
|
# Run a single test by name
|
|
python3 -m pytest core_tests/smoke_tests.py::test_iso15118_ac_session
|
|
```
|
|
|
|
Network isolation is not set up automatically in this mode — ISO 15118 tests will
|
|
run sequentially via their `xdist_group` marker.
|
|
|
|
## Add own test sets
|
|
|
|
To create own test sets, you need to write a new python file in the "EVerest/**tests/core_tests/**" folder. A basic file template would look like this:
|
|
|
|
```python
|
|
import logging
|
|
import pytest
|
|
|
|
from everest.testing.core_utils.fixtures import *
|
|
from validations.base_functions import wait_for_and_validate_event
|
|
from everest.testing.core_utils.test_control_module import TestControlModule
|
|
from everest.testing.core_utils.everest_core import EverestCore
|
|
from validations.user_functions import *
|
|
|
|
@ pytest.mark.asyncio
|
|
async def test_001_my_first_test(everest_core: EverestCore,
|
|
test_control_module: TestControlModule):
|
|
logging.info(">>>>>>>>> test_001_my_first_test <<<<<<<<<")
|
|
everest_core.start()
|
|
|
|
assert await wait_for_and_validate_event(test_control_module,
|
|
exp_event='my_event',
|
|
exp_data={"my_data":"test"},
|
|
validation_function=my_validation_function,
|
|
timeout=30)
|
|
```
|
|
|
|
\*Note: All test functions' names NEED to start with "test" (see pytest documentation: https://docs.pytest.org/)
|
|
|
|
In the above example you then would also have to write a (user-)validation function by the name of "my_validation_function()" in the "EVerest/**tests/core_tests/validations/user_functions.py**" file.
|
|
|
|
For this, you can utilize helper functions from the "EVerest/**tests/core_tests/validations/base_functions.py**" file (e.g. "get_key_if_exists()" to traverse and retrieve event data from an event-object).
|
|
|
|
**Attention**: When you change something in the test- or validation files, please do not forget to run "*make install*" on EVerest before starting the tests, as otherwise the changes are not reflected in the test run!
|
|
|
|
### Controlling more functionality with PyTestControlModule
|
|
|
|
If you would like to receive other everest-internal data, send commands to other EVerest modules or would like to change the received events for everest-testing, you can modify the **PyTestControlModule** in the "EVerest/**modules/PyTestControlModule**" folder.
|
|
|
|
**Attention:** Please be aware, though, that the use of this module is preliminary and changes in the *everest-framework* may render this module **obsolete** in the future!
|
|
|
|
## Troubleshooting
|
|
|
|
**1. Problem:** Multiple concatenated tests that use the *PyTestControlModule* (in standalone configuration) result in an exception and subsequent crash of everest-testing.
|
|
|
|
**Cause:** There is currently no way to unload a module in *EVerest*. But because pytest runs out of the same context for all tests in a testset, a once loaded *PyTestControlModule* stays active for the rest of the pytest/everest-testing run. When a new test tries to load a second instance of the *PyTestControlModule* it collides with the already running instance and causes everest-testing to crash.
|
|
|
|
**Solution:** Unfortunately, this requires a change in the everest-framework. This change is currently under development, but not yet merged into main.
|
|
For the moment, there is a *workaround available:* Use a new test-set (another python file containing a new set of tests) for every test that requires control via the *PyTestControlModule*. As soon as a fix for the underlying issue is merged, this will be updated!
|
|
|
|
**2. Problem:** On an unmodified run of "basic_charging_tests.py" the test *test_001_charge_defined_ammount* fails and reports an amount of 0.0kWh charged.
|
|
|
|
**Cause:** Currently there is a bug in the EVSE manager implementation in EVerest, causing a race-condition between resetting the "amount charged" parameter and the event reporting for a "*transaction_finished*" event. Sometimes it reports correctly, other times it reports a value of 0.0kWh charged.
|
|
|
|
**Solution:** This problem is known and a fix is under way. For the moment, just re-run the test. Usually, this should then produce a passing result.
|
|
|
|
|
|
# Code coverage
|
|
|
|
To activate code coverage, you need to configure the project with
|
|
`BUILD_TESTING` enabled. Furthermore, `gcovr` needs to be installed.
|
|
|
|
## Generating coverage reports
|
|
|
|
Before generating a coverage report you will typically need to run some
|
|
executables or tests, which contain or link to code, that generates coverage
|
|
statistics.
|
|
|
|
After that, just run:
|
|
```bash
|
|
cmake --build build --target everest-core_create_coverage
|
|
# or ninja -C build everest-core_create_coverage
|
|
```
|
|
|
|
The generated `index.html` will be available at
|
|
`build/everest-core_create_coverage/index.html`.
|
|
|
|
**Note**: the code coverage for a specific compilation unit (cpp file) will
|
|
accumulate if you run multiple executables that use this compilation unit. The
|
|
code coverage is recorded in `gcda` files. If you want to get separate coverage
|
|
reports for different executables, these `gcda` files need to be deleted for
|
|
each new *run*.
|
|
|
|
|
|
## Adding code/targets to code coverage
|
|
|
|
By default, all *cpp* modules are added automatically to a list of targets that
|
|
will be compiled with coverage flags. If additional libraries or tests need to
|
|
be added to code coverage, you should use the following functions in your
|
|
*cmake* code:
|
|
```cmake
|
|
ev_register_library_target(LIBRARY_TARGET_NAME)
|
|
ev_register_test_target(TEST_TARGET_NAME)
|
|
```
|
|
|
|
## Conventions
|
|
|
|
For the `ev_register_*_target` functions, call these function close to their
|
|
scope of detail - meaning near `add_library` for libraries like:
|
|
|
|
```cmake
|
|
add_library(evse_security_conversions STATIC)
|
|
add_library(everest::evse_security_conversions ALIAS evse_security_conversions)
|
|
ev_register_library_target(evse_security_conversions)
|
|
```
|
|
|
|
and near `add_test` for tests like:
|
|
|
|
```cmake
|
|
add_test(${TLS_GTEST_NAME} ${TLS_GTEST_NAME})
|
|
ev_register_test_target(${TLS_GTEST_NAME})
|
|
```
|
|
|
|
and not scattered around.
|
|
|
|
---
|
|
|
|
By default, all directories ending with `tests` will be excluded from the code
|
|
coverage report (because we're not interested in the code coverage of the unit
|
|
test code itself). So when adding unit tests for code coverage, place them into
|
|
a `tests` folder.
|
|
|
|
## Troubleshooting
|
|
|
|
`no_working_dir_found`: while developing code (e.g. an `example.cpp`), it might
|
|
happen, that the created object file `example.cpp.o` will get out of sync with
|
|
its corresponding `example.cpp.gcno` (`gcov` note file) and/or
|
|
`example.cpp.gcda` (`gcov` data file). When creating a report, `gcovr` will
|
|
complain, because it can't find the corresponding `example.cpp.o` file for a
|
|
stale `example.cpp.gcno` file. It might also not be able to find the
|
|
`example.cpp.gcno` file for a stale `example.cpp.gcda` file or an older `gcda`
|
|
file might got out of sync due to a newer `gcno` file and so forth. Do not
|
|
simply ignore these warnings/errors, as the report might not be accurate
|
|
anymore.
|
|
|
|
The current sledgehammer approach is to manually delete the files in question or
|
|
just do (**be careful** with `rm` commands and wildcards!):
|
|
```bash
|
|
find ./build -name "*.gcno" -delete
|
|
find ./build -name "*.gcda" -delete
|
|
ninja -C build clean
|
|
ninja -C build
|
|
# run code
|
|
# create coverage
|
|
```
|
|
|
|
We're currently trying to write some tooling around this to make this more
|
|
usable.
|