|
| 1 | +# Testing |
| 2 | + |
| 3 | +Testing is done inside MicroPython Docker container |
| 4 | + |
| 5 | +--------------- |
| 6 | + |
| 7 | +## Basics |
| 8 | + |
| 9 | +This library is as of now tested with the `v1.18` version of MicroPython |
| 10 | + |
| 11 | +### Pull container |
| 12 | + |
| 13 | +Checkout the available |
| 14 | +[MicroPython containers](https://hub.docker.com/r/micropython/unix/tags) and |
| 15 | +pull the `v1.18` locally. |
| 16 | + |
| 17 | +```bash |
| 18 | +docker pull micropython/unix:v1.18 |
| 19 | +``` |
| 20 | + |
| 21 | +### Spin up container |
| 22 | + |
| 23 | +#### Simple container |
| 24 | + |
| 25 | +Use this command for your first tests or to run some MicroPython commands in |
| 26 | +a simple REPL |
| 27 | + |
| 28 | +```bash |
| 29 | +docker run -it --name micropython-1.18 --network=host --entrypoint bash micropython/unix:v1.18 |
| 30 | +``` |
| 31 | + |
| 32 | +#### Enter MicroPython REPL |
| 33 | + |
| 34 | +Inside the container enter the REPL by running `micropython-dev`. The console |
| 35 | +should now look similar to this |
| 36 | + |
| 37 | +``` |
| 38 | +root@debian:/home# |
| 39 | +MicroPython v1.18 on 2022-01-17; linux version |
| 40 | +Use Ctrl-D to exit, Ctrl-E for paste mode |
| 41 | +>>> |
| 42 | +``` |
| 43 | + |
| 44 | +## Testing |
| 45 | + |
| 46 | +All tests are automatically executed on a push to GitHub and have to be passed |
| 47 | +in order to be allowed to merge to the main development branch, see also [CONTRIBUTING](CONTRIBUTING.md). |
| 48 | + |
| 49 | +### Run unittests manually |
| 50 | + |
| 51 | +First build and run the docker image with the following command |
| 52 | + |
| 53 | +```bash |
| 54 | +docker build -t micropython-test-manually -f Dockerfile.tests_manually . |
| 55 | +docker run -it --name micropython-test-manually micropython-test-manually |
| 56 | +``` |
| 57 | + |
| 58 | +Run all unittests defined in the `tests` directory and exit with status result |
| 59 | + |
| 60 | +```bash |
| 61 | +micropython-dev -c "import mpy_unittest as unittest; unittest.main('tests')" |
| 62 | +``` |
| 63 | + |
| 64 | +In order to execute only a specific set of tests use the following command |
| 65 | +inside the built and running MicroPython container |
| 66 | + |
| 67 | +```bash |
| 68 | +# run all tests of "TestAbsoluteTruth" defined in tests/test_absolute_truth.py |
| 69 | +# and exit with status result |
| 70 | +micropython-dev -c "import mpy_unittest as unittest; unittest.main(name='tests.test_absolute_truth', fromlist=['TestAbsoluteTruth'])" |
| 71 | +``` |
| 72 | + |
| 73 | +### Custom container for unittests |
| 74 | + |
| 75 | +```bash |
| 76 | +docker build --tag micropython-test --file Dockerfile.tests . |
| 77 | +``` |
| 78 | + |
| 79 | +As soon as the built image is executed all unittests are executed. The |
| 80 | +container will exit with a non-zero status in case of a unittest failure. |
| 81 | + |
| 82 | +The return value can be collected by `echo $?` (on Linux based systems) or |
| 83 | +`echo %errorlevel%` (on Windows), which will be either `0` in case all tests |
| 84 | +passed, or `1` if one or multiple tests failed. |
| 85 | + |
| 86 | +### Docker compose |
| 87 | + |
| 88 | +For more complex unittests and integration tests, several Dockerfiles are |
| 89 | +combined into a docker-compose file. |
| 90 | + |
| 91 | +The option `--build` can be skipped on the second run, to avoid rebuilds of |
| 92 | +the containers. All "dynamic" data is shared via `volumes`. |
| 93 | + |
| 94 | +#### TCP integration tests |
| 95 | + |
| 96 | +##### Overview |
| 97 | + |
| 98 | +The TCP integration tests defined by `test_tcp_example.py` uses the |
| 99 | +`Dockerfile.client_tcp` and `Dockerfile.test_examples`. |
| 100 | + |
| 101 | +A network bridge is created with fixed IPv4 addresses in order to bind the |
| 102 | +client to a known IP and let the host reach it on that predefined IP. |
| 103 | + |
| 104 | +The port defined in `tcp_client_example.py`, executed by |
| 105 | +`Dockerfile.client_tcp` has to be open, exposed to the `micropython-host` |
| 106 | +service running `Dockerfile.test_examples` and optionally exposed in the |
| 107 | +`docker-compose-tcp-test.yaml` file. |
| 108 | + |
| 109 | +Finally the tests defined in `TestTcpExample` are executed. The tests read and |
| 110 | +write all available register types on the client and check the response with |
| 111 | +the expected data. |
| 112 | + |
| 113 | +##### Usage |
| 114 | + |
| 115 | +```bash |
| 116 | +docker compose -f docker-compose-tcp-test.yaml up --build --exit-code-from micropython-host --remove-orphans |
| 117 | +``` |
| 118 | + |
| 119 | +#### RTU integration tests |
| 120 | + |
| 121 | +##### UART interface |
| 122 | + |
| 123 | +As the [MicroPython containers](https://hub.docker.com/r/micropython/unix/tags) |
| 124 | +do not have a UART interface, which is additionally anyway not connectable via |
| 125 | +two containers, a [`UART fake`](fakes.machine.UART) has been implemented. |
| 126 | + |
| 127 | +The fake [`UART`](fakes.machine.UART) class provides all required functions |
| 128 | +like [`any()`](fakes.machine.UART.any), [`read()`](fakes.machine.UART.read) and |
| 129 | +[`write()`](fakes.machine.UART.write) to simulate a UART interface. |
| 130 | + |
| 131 | +During the initialisation of the fake UART a simple and very basic socket |
| 132 | +request is made to `172.25.0.2`, a predefined IP address, see |
| 133 | +`docker-compose-rtu-test.yaml`. In case no response is received, a socket based |
| 134 | +server is started. It is thereby important to start the RTU client before the |
| 135 | +RTU host. The RTU host will perform the same check during the UART init, but |
| 136 | +will reach the (already running) socket server and connect to it. |
| 137 | + |
| 138 | +The data provided to the [`write()`](fakes.machine.UART.write) call of the RTU |
| 139 | +host, will be sent to the background socket server of the RTU client and be |
| 140 | +read inside the [`get_request()`](umodbus.serial.Serial.get_request) function |
| 141 | +which is constantly called by the [`process()`](umodbus.modbus.Modbus.process) |
| 142 | +function. |
| 143 | + |
| 144 | +After it has been processed from Modbus perspective, the RTU client response |
| 145 | +will then be put by the [`write()`](fakes.machine.UART.write) function into a |
| 146 | +queue on RTU client side, picked up by the RTU client background socket server |
| 147 | +thread and sent back to the RTU host where it is made available via the |
| 148 | +[`read()`](fakes.machine.UART.read) function. |
| 149 | + |
| 150 | +##### Overview |
| 151 | + |
| 152 | +The RTU integration tests defined by `test_rtu_example.py` uses the |
| 153 | +`Dockerfile.client_rtu` and `Dockerfile.test_examples`. |
| 154 | + |
| 155 | +A network bridge is created with fixed IPv4 addresses in order to bind the |
| 156 | +client to a known IP and let the host reach it on that predefined IP. |
| 157 | + |
| 158 | +The port defined in `rtu_client_example.py`, executed by |
| 159 | +`Dockerfile.client_rtu` has to be open, exposed to the `micropython-host` |
| 160 | +service running `Dockerfile.test_examples` and optionally exposed in the |
| 161 | +`docker-compose-rtu-test.yaml` file. |
| 162 | + |
| 163 | +Finally the tests defined in `TestRtuExample` are executed. The tests read and |
| 164 | +write all available register types on the client and check the response with |
| 165 | +the expected data. |
| 166 | + |
| 167 | +##### Usage |
| 168 | + |
| 169 | +```bash |
| 170 | +docker compose -f docker-compose-rtu-test.yaml up --build --exit-code-from micropython-host-rtu --remove-orphans |
| 171 | +``` |
| 172 | + |
| 173 | +<!-- Links --> |
| 174 | +[ref-fakes]: https://github.com/brainelectronics/micropython-modbus/blob/develop/fakes/machine.py |
0 commit comments