|
1 | 1 | .. _writingtests:
|
2 | 2 |
|
3 |
| -Writing tests |
4 |
| -============= |
| 3 | +Test Suite |
| 4 | +========== |
5 | 5 |
|
6 |
| -Tests in MicroPython are located at the path ``tests/``. The following is a listing of |
7 |
| -key directories and the run-tests.py runner script: |
| 6 | +The MicroPython test suite resides in the ``tests/`` directory and consists of multiple components: |
8 | 7 |
|
9 |
| -.. code-block:: bash |
| 8 | +1. **Standard Tests**: The majority of tests that run on a single instance and verify functionality against CPython or expected outputs. |
| 9 | +2. **Multi-instance Tests**: Tests that require multiple MicroPython instances to interact with each other (e.g., networking, Bluetooth). |
| 10 | +3. **Native Module Tests**: Tests for dynamic native modules compiled to ``.mpy`` files. |
| 11 | +4. **Performance Benchmarks**: Python-level benchmarks for measuring execution speed. |
| 12 | +5. **Internal Benchmarks**: Low-level benchmarks for core VM operations and C code. |
| 13 | + |
| 14 | +This document primarily focuses on the standard tests and multi-instance tests. |
| 15 | + |
| 16 | +Directory Structure |
| 17 | +------------------- |
| 18 | + |
| 19 | +The ``tests/`` directory is organized into subfolders by functionality: |
| 20 | + |
| 21 | +* ``basics/``: Core Python language features |
| 22 | +* ``extmod/``: Extended modules (like ``ujson``, ``ure``) |
| 23 | +* ``float/``: Floating-point arithmetic specifics |
| 24 | +* ``micropython/``: MicroPython-specific features and behaviors |
| 25 | +* ``import/``: Import mechanisms |
| 26 | +* ``io/``: Input/Output operations |
| 27 | +* ``stress/``: Tests designed to push resource limits (memory, recursion) |
| 28 | +* ``thread/``: Threading module tests |
| 29 | +* ``cmdline/``: Tests for the command-line interface and REPL |
| 30 | +* ``ports/<port_name>/``: Tests specific to a particular port |
| 31 | +* ``feature_check/``: Scripts used to detect target capabilities |
| 32 | +* ``multi_bluetooth/``, ``multi_network/``: Tests involving multiple instances |
| 33 | +* ``perf_bench/``: Performance benchmarking scripts |
| 34 | +* ``internal_bench/``: Low-level internal benchmarks |
| 35 | + |
| 36 | +Key Test Runner Scripts: |
10 | 37 |
|
11 |
| - . |
12 |
| - ├── basics |
13 |
| - ├── extmod |
14 |
| - ├── float |
15 |
| - ├── micropython |
16 |
| - ├── run-tests.py |
17 |
| - ... |
| 38 | +* ``run-tests.py``: Main script for standard tests |
| 39 | +* ``run-multitests.py``: For tests requiring multiple interacting instances |
| 40 | +* ``run-natmodtests.py``: For testing dynamic native modules |
| 41 | +* ``run-perfbench.py``: For performance benchmarks |
| 42 | +* ``run-internalbench.py``: For low-level internal benchmarks |
18 | 43 |
|
19 |
| -There are subfolders maintained to categorize the tests. Add a test by creating a new file in one of the |
20 |
| -existing folders or in a new folder. It's also possible to make custom tests outside this tests folder, |
21 |
| -which would be recommended for a custom port. |
| 44 | +Writing Standard Tests |
| 45 | +---------------------- |
22 | 46 |
|
23 |
| -For example, add the following code in a file ``print.py`` in the ``tests/unix/`` subdirectory: |
| 47 | +Standard tests are the most common type. They verify functionality by comparing output against expected results. |
| 48 | + |
| 49 | +1. **Choose a Directory:** Select the appropriate subfolder for your test based on the functionality it tests. |
| 50 | +2. **Create a ``.py`` file:** Add your test code to a new Python file. |
| 51 | +3. **Use ``print()``:** The test harness works by comparing standard output. Use ``print()`` statements to output results. |
| 52 | +4. **Expected Output:** |
| 53 | + * **CPython Comparison:** By default, tests are run on both CPython and MicroPython with outputs compared. |
| 54 | + * **``.exp`` Files:** For MicroPython-specific features, create a ``<testname>.py.exp`` file with the expected output. |
| 55 | + |
| 56 | +Example: |
| 57 | + |
| 58 | +Create ``tests/basics/simple_print.py``: |
24 | 59 |
|
25 | 60 | .. code-block:: python
|
26 | 61 |
|
27 |
| - def print_one(): |
28 |
| - print(1) |
| 62 | + # tests/basics/simple_print.py |
| 63 | + print("Hello") |
| 64 | + a = 1 + 2 |
| 65 | + print(a) |
| 66 | +
|
| 67 | +When run, MicroPython's output will be compared to CPython's output (or ``simple_print.py.exp`` if it exists). |
| 68 | + |
| 69 | +Types of Tests |
| 70 | +-------------- |
| 71 | + |
| 72 | +The test system supports three main testing approaches: |
29 | 73 |
|
30 |
| - print_one() |
| 74 | +1. **CPython comparison tests**: |
| 75 | + * Used for behavior that should match CPython |
| 76 | + * Tests are run on both CPython and MicroPython |
| 77 | + * Passes if outputs match exactly |
31 | 78 |
|
32 |
| -If you run your tests, this test should appear in the test output: |
| 79 | +2. **Expected output (``.exp``) tests**: |
| 80 | + * Used for MicroPython-specific features or when CPython behavior differs |
| 81 | + * MicroPython output is compared against contents of a ``.py.exp`` file |
| 82 | + * Passes if outputs match exactly |
| 83 | + |
| 84 | +3. **``unittest``-based tests**: |
| 85 | + * Requires ``unittest`` module to be available on the target |
| 86 | + * Used for hardware testing or other MicroPython-specific behavior |
| 87 | + * Passes if unittest summary shows "OK" |
| 88 | + |
| 89 | +Running Tests (``run-tests.py``) |
| 90 | +-------------------------------- |
| 91 | + |
| 92 | +The primary script is ``run-tests.py``, executed from the ``tests/`` directory. |
| 93 | + |
| 94 | +**Basic Usage:** |
33 | 95 |
|
34 | 96 | .. code-block:: bash
|
35 | 97 |
|
36 |
| - $ cd ports/unix |
37 |
| - $ make tests |
38 |
| - skip unix/extra_coverage.py |
39 |
| - pass unix/ffi_callback.py |
40 |
| - pass unix/ffi_float.py |
41 |
| - pass unix/ffi_float2.py |
42 |
| - pass unix/print.py |
43 |
| - pass unix/time.py |
44 |
| - pass unix/time2.py |
| 98 | + # Run default tests for the Unix port |
| 99 | + cd tests/ |
| 100 | + ./run-tests.py |
| 101 | +
|
| 102 | +**Running on Hardware Targets:** |
| 103 | + |
| 104 | +.. code-block:: bash |
45 | 105 |
|
46 |
| -Tests are run by comparing the output from the test target against the output from CPython. |
47 |
| -So any test should use print statements to indicate test results. |
| 106 | + # Run on a specific serial port |
| 107 | + ./run-tests.py -t /dev/ttyACM0 |
| 108 | + ./run-tests.py -t COM3 |
48 | 109 |
|
49 |
| -For tests that can't be compared to CPython (i.e. micropython-specific functionality), |
50 |
| -you can provide a ``.py.exp`` file which will be used as the truth for comparison. |
| 110 | + # Use shortcuts for common serial ports |
| 111 | + ./run-tests.py -t a0 # Maps to /dev/ttyACM0 |
| 112 | + ./run-tests.py -t u1 # Maps to /dev/ttyUSB1 |
| 113 | + ./run-tests.py -t c3 # Maps to COM3 |
51 | 114 |
|
52 |
| -The other way to run tests, which is useful when running on targets other than the Unix port, is: |
| 115 | +**Filtering Tests:** |
53 | 116 |
|
54 | 117 | .. code-block:: bash
|
55 | 118 |
|
56 |
| - $ cd tests |
57 |
| - $ ./run-tests.py |
| 119 | + # Run all tests in specific directories |
| 120 | + ./run-tests.py -d basics |
| 121 | + ./run-tests.py -d extmod -d float |
| 122 | +
|
| 123 | + # Run specific files |
| 124 | + ./run-tests.py basics/int*.py |
| 125 | + ./run-tests.py float/float_parse.py |
| 126 | +
|
| 127 | + # Filter by regex |
| 128 | + ./run-tests.py -e viper # Exclude tests matching 'viper' |
| 129 | + ./run-tests.py -i asyncio # Include only tests matching 'asyncio' |
| 130 | +
|
| 131 | +**Other Useful Options:** |
| 132 | + |
| 133 | +* ``--emit {bytecode | native | viper}``: Run tests using a specific code emitter |
| 134 | +* ``--via-mpy``: Compile tests to ``.mpy`` files first before running |
| 135 | +* ``-j N`` or ``--jobs N``: Run N tests in parallel (for PC targets) |
| 136 | +* ``--print-failures``: Show the diff for tests that failed in the last run |
| 137 | +* ``--run-failures``: Re-run only the tests that failed in the last run |
| 138 | +* ``--clean-failures``: Remove the ``.exp`` and ``.out`` files from failures |
| 139 | + |
| 140 | +How The Test System Works |
| 141 | +------------------------- |
58 | 142 |
|
59 |
| -Then to run on a board: |
| 143 | +1. **Target Detection:** Determines the platform and architecture. |
| 144 | +2. **Feature Detection:** Identifies capabilities of the target MicroPython build. |
| 145 | +3. **Test Selection:** Gathers test files based on command-line arguments or defaults. |
| 146 | +4. **Skipping Tests:** Excludes tests based on: |
| 147 | + * Command-line filters |
| 148 | + * Missing features |
| 149 | + * Platform-specific skip lists |
| 150 | + * Emitter type restrictions |
| 151 | + * Tests explicitly printing ``SKIP`` |
| 152 | +5. **Execution:** |
| 153 | + * PC targets: Runs MicroPython as a subprocess |
| 154 | + * Board targets: Uses ``pyboard.py`` to connect via serial/network |
| 155 | +6. **Output Comparison:** Compares target's output with expected output |
| 156 | +7. **Reporting:** Shows results and summarizes pass/fail statistics |
| 157 | + |
| 158 | +Writing Advanced Tests |
| 159 | +---------------------- |
| 160 | + |
| 161 | +**Skipping Tests Conditionally** |
| 162 | + |
| 163 | +If a test needs to skip itself conditionally: |
| 164 | + |
| 165 | +.. code-block:: python |
| 166 | +
|
| 167 | + import sys |
| 168 | + if not hasattr(sys, 'feature_needed'): |
| 169 | + print('SKIP') |
| 170 | + sys.exit() |
| 171 | +
|
| 172 | +**Special Test Handling** |
| 173 | + |
| 174 | +Some tests need special options: |
| 175 | + |
| 176 | +* Add comments in the test file: ``# cmdline: -X heapsize=...`` |
| 177 | +* Tests needing redirection are listed in ``special_tests`` in ``run-tests.py`` |
| 178 | + |
| 179 | +**Platform Considerations** |
| 180 | + |
| 181 | +* **Floating Point:** Be mindful of precision differences. Use ``math.isclose()`` with appropriate tolerances. |
| 182 | +* **Endianness:** Check ``sys.byteorder`` for byte order sensitive operations. |
| 183 | +* **Memory/Stack:** Avoid large allocations or deep recursion on constrained devices. |
| 184 | + |
| 185 | +Multi-instance Tests |
| 186 | +-------------------- |
| 187 | + |
| 188 | +For testing interactions like networking or Bluetooth, ``run-multitests.py`` orchestrates multiple instances. |
| 189 | + |
| 190 | +**Writing Multi-instance Tests:** |
| 191 | + |
| 192 | +1. Define functions named ``instance0()``, ``instance1()``, etc. for each instance's code. |
| 193 | +2. Use the ``multitest`` helper for coordination: |
| 194 | + * ``multitest.next()``: Complete a stage and wait for other instances |
| 195 | + * ``multitest.broadcast(msg)``: Send a message to all other instances |
| 196 | + * ``multitest.wait(msg)``: Wait for a specific broadcast message |
| 197 | + * ``multitest.skip()``: Indicate the test should be skipped |
| 198 | + * ``multitest.globals(var=value, ...)``: Set global variables |
| 199 | + * ``multitest.get_network_ip()``: Get the IP address |
| 200 | + * ``multitest.expect_reboot(resume_func_name, delay_ms)``: Handle device reboots |
| 201 | + |
| 202 | +**Running Multi-instance Tests:** |
60 | 203 |
|
61 | 204 | .. code-block:: bash
|
62 | 205 |
|
63 |
| - $ ./run-tests.py -t /dev/ttyACM0 |
| 206 | + # Run a network test using two unix instances |
| 207 | + ./run-multitests.py -i micropython -i micropython multi_network/tcp_connect.py |
| 208 | +
|
| 209 | + # Run a bluetooth test using two pyboards |
| 210 | + ./run-multitests.py -i pyb:/dev/ttyACM0 -i pyb:/dev/ttyACM1 multi_bluetooth/ble_connect.py |
| 211 | +
|
| 212 | +Provide as many ``-i`` arguments as instances required by the tests. |
64 | 213 |
|
65 |
| -And to run only a certain set of tests (eg a directory): |
| 214 | +Test Certificates for SSL/TLS Tests |
| 215 | +----------------------------------- |
| 216 | + |
| 217 | +Network tests using SSL/TLS require test certificates to be available on the device. These certificates are included in the repository in ``multi_network/`` and ``net_inet/`` directories. |
| 218 | + |
| 219 | +**Preparing a Device for SSL/TLS Tests:** |
| 220 | + |
| 221 | +Use ``mpremote`` to set the device's RTC and copy the certificate files: |
66 | 222 |
|
67 | 223 | .. code-block:: bash
|
68 | 224 |
|
69 |
| - $ ./run-tests.py -d basics |
70 |
| - $ ./run-tests.py float/builtin*.py |
| 225 | + # From the micropython/tests/ directory: |
| 226 | + mpremote rtc --set cp multi_net/*.der net_inet/*.der :/ |
| 227 | +
|
| 228 | +This sets the device's Real Time Clock (using the host's time) and copies all ``.der`` files to the root directory of the device's filesystem. |
| 229 | + |
| 230 | +These certificates are self-signed and generated for testing purposes only. |
0 commit comments