mpy-coverage 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mpy_coverage-1.0.0/.github/workflows/ci.yml +48 -0
- mpy_coverage-1.0.0/.github/workflows/release.yml +30 -0
- mpy_coverage-1.0.0/.gitignore +9 -0
- mpy_coverage-1.0.0/.python-version +1 -0
- mpy_coverage-1.0.0/LICENSE +21 -0
- mpy_coverage-1.0.0/PKG-INFO +9 -0
- mpy_coverage-1.0.0/README.md +293 -0
- mpy_coverage-1.0.0/pyproject.toml +27 -0
- mpy_coverage-1.0.0/src/mpy_coverage/__init__.py +14 -0
- mpy_coverage-1.0.0/src/mpy_coverage/__main__.py +3 -0
- mpy_coverage-1.0.0/src/mpy_coverage/_vendor/__init__.py +0 -0
- mpy_coverage-1.0.0/src/mpy_coverage/_vendor/makeqstrdata.py +460 -0
- mpy_coverage-1.0.0/src/mpy_coverage/_vendor/mpy_tool.py +2203 -0
- mpy_coverage-1.0.0/src/mpy_coverage/_version.py +34 -0
- mpy_coverage-1.0.0/src/mpy_coverage/cli.py +583 -0
- mpy_coverage-1.0.0/src/mpy_coverage/mpy_analysis.py +283 -0
- mpy_coverage-1.0.0/src/mpy_coverage/report.py +419 -0
- mpy_coverage-1.0.0/src/mpy_coverage/tracer.py +162 -0
- mpy_coverage-1.0.0/tests/conftest.py +34 -0
- mpy_coverage-1.0.0/tests/test_coverage.py +1315 -0
- mpy_coverage-1.0.0/tests/test_mpy_analysis.py +127 -0
- mpy_coverage-1.0.0/tests/test_target.py +43 -0
- mpy_coverage-1.0.0/tests/test_target_xval.py +168 -0
- mpy_coverage-1.0.0/uv.lock +319 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: astral-sh/setup-uv@v5
|
|
15
|
+
- run: uv sync --only-group dev
|
|
16
|
+
- run: uv run ruff check src/ tests/
|
|
17
|
+
- run: uv run ruff format --check src/ tests/
|
|
18
|
+
|
|
19
|
+
test:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
- uses: astral-sh/setup-uv@v5
|
|
24
|
+
- run: uv sync
|
|
25
|
+
- run: uv run pytest tests/test_mpy_analysis.py -v
|
|
26
|
+
|
|
27
|
+
build:
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@v4
|
|
31
|
+
- uses: astral-sh/setup-uv@v5
|
|
32
|
+
- run: uv build
|
|
33
|
+
- run: uv run mpy-coverage --help
|
|
34
|
+
- run: uv run python -m mpy_coverage --help
|
|
35
|
+
|
|
36
|
+
integration:
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
needs: test
|
|
39
|
+
container:
|
|
40
|
+
image: micropython/unix:coverage_v1.27.0
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/checkout@v4
|
|
43
|
+
- run: apt-get update && apt-get install -y python3 python3-pip python3-venv curl
|
|
44
|
+
- uses: astral-sh/setup-uv@v5
|
|
45
|
+
- run: uv sync
|
|
46
|
+
- run: uv run pytest tests/ -v
|
|
47
|
+
env:
|
|
48
|
+
MPY_BINARY: /usr/local/bin/micropython
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
build:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v4
|
|
12
|
+
- uses: astral-sh/setup-uv@v5
|
|
13
|
+
- run: uv build
|
|
14
|
+
- uses: actions/upload-artifact@v4
|
|
15
|
+
with:
|
|
16
|
+
name: dist
|
|
17
|
+
path: dist/
|
|
18
|
+
|
|
19
|
+
publish:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
needs: build
|
|
22
|
+
environment: pypi
|
|
23
|
+
permissions:
|
|
24
|
+
id-token: write
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/download-artifact@v4
|
|
27
|
+
with:
|
|
28
|
+
name: dist
|
|
29
|
+
path: dist/
|
|
30
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Andrew Leech
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
# mpy-coverage
|
|
2
|
+
|
|
3
|
+
Code coverage for MicroPython. Lightweight on-device tracer using `sys.settrace`, with host-side reporting via coverage.py.
|
|
4
|
+
|
|
5
|
+
The tracer runs on the MicroPython target (unix port or real hardware with settrace enabled), collects executed line data, and exports it as JSON. The host-side tooling then merges multiple runs and generates reports using coverage.py's reporting engine.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install mpy-coverage
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
This pulls in `coverage` and `mpy-cross` automatically.
|
|
14
|
+
|
|
15
|
+
Dev setup:
|
|
16
|
+
```bash
|
|
17
|
+
git clone git@github.com:andrewleech/mpy-coverage.git
|
|
18
|
+
cd mpy-coverage
|
|
19
|
+
uv sync
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Getting started
|
|
23
|
+
|
|
24
|
+
You need a micropython binary with settrace support. The quickest way is the unix coverage variant:
|
|
25
|
+
```bash
|
|
26
|
+
cd ports/unix && make submodules && make VARIANT=coverage
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Say you have a module `myapp.py` and a test script `test_myapp.py` that exercises it:
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
# test_myapp.py
|
|
33
|
+
import myapp
|
|
34
|
+
myapp.run()
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Collect coverage and generate a report:
|
|
38
|
+
```bash
|
|
39
|
+
mpy-coverage run test_myapp.py --include myapp
|
|
40
|
+
mpy-coverage report --show-missing
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
That's it. The run command executes `test_myapp.py` under micropython with the tracer active, saves a JSON data file to `.mpy_coverage/`, and the report command reads it and prints a coverage summary.
|
|
44
|
+
|
|
45
|
+
For an HTML report instead:
|
|
46
|
+
```bash
|
|
47
|
+
mpy-coverage report --format html --output-dir htmlcov
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
If you have multiple test files, run each one separately then generate a single merged report:
|
|
51
|
+
```bash
|
|
52
|
+
mpy-coverage run tests/test_network.py --include myapp
|
|
53
|
+
mpy-coverage run tests/test_storage.py --include myapp
|
|
54
|
+
mpy-coverage report --show-missing
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Prerequisites
|
|
58
|
+
|
|
59
|
+
For unix-port testing you need a micropython coverage build:
|
|
60
|
+
```bash
|
|
61
|
+
cd ports/unix && make submodules && make VARIANT=coverage
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Hardware targets need firmware built with `MICROPY_PY_SYS_SETTRACE=1`.
|
|
65
|
+
|
|
66
|
+
## Usage
|
|
67
|
+
|
|
68
|
+
Each test run stores a timestamped JSON file, the report command merges all collected data.
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
mpy-coverage run test_foo.py --include myapp
|
|
72
|
+
mpy-coverage run test_bar.py --include myapp
|
|
73
|
+
mpy-coverage report --method auto --show-missing
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
micropython binary is auto-detected from PATH or `ports/unix/build-coverage/micropython` relative to CWD. Override with `--micropython`. Also works as `python -m mpy_coverage`.
|
|
77
|
+
|
|
78
|
+
### Hardware
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# deploys tracer to device automatically, runs test, collects data
|
|
82
|
+
mpy-coverage run test_foo.py --device /dev/serial/by-id/usb-... --include myapp
|
|
83
|
+
|
|
84
|
+
# skip deploy if mpy_coverage.py is already on device
|
|
85
|
+
mpy-coverage run test_foo.py --device /dev/serial/by-id/usb-... --no-deploy --include myapp
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Multi-pass
|
|
89
|
+
|
|
90
|
+
Run tests separately, accumulate data, generate one merged report:
|
|
91
|
+
```bash
|
|
92
|
+
mpy-coverage run tests/test_network.py --include myapp --branch
|
|
93
|
+
mpy-coverage run tests/test_storage.py --include myapp --branch
|
|
94
|
+
mpy-coverage run tests/test_ui.py --include myapp --branch
|
|
95
|
+
mpy-coverage report --show-missing --format html --output-dir htmlcov
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Data stored in `.mpy_coverage/` by default, override with `--data-dir`.
|
|
99
|
+
|
|
100
|
+
Other subcommands: `mpy-coverage list` and `mpy-coverage clean`.
|
|
101
|
+
|
|
102
|
+
## Branch coverage
|
|
103
|
+
|
|
104
|
+
Branch coverage follows the same pattern as [coverage.py's branch measurement](https://coverage.readthedocs.io/en/latest/branch.html): `--branch` is a collection-time flag on `run`. Report commands auto-detect branch data from the JSON files and include branch columns when arc data is present — no `--branch` flag on `report`.
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# Collect with arcs
|
|
108
|
+
mpy-coverage run test_myapp.py --include myapp --branch
|
|
109
|
+
|
|
110
|
+
# Report auto-detects arc data and shows branch columns
|
|
111
|
+
mpy-coverage report --show-missing
|
|
112
|
+
|
|
113
|
+
# Force line-only report even when arc data exists
|
|
114
|
+
mpy-coverage report --show-missing --no-branch
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
This matches [coverage.py's CLI](https://coverage.readthedocs.io/en/latest/commands/cmd_run.html) where `coverage run --branch` enables collection and `coverage report` auto-detects.
|
|
118
|
+
|
|
119
|
+
## Test map
|
|
120
|
+
|
|
121
|
+
Show which tests cover which application files:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
mpy-coverage run tests/test_a.py --include myapp
|
|
125
|
+
mpy-coverage run tests/test_b.py --include myapp
|
|
126
|
+
mpy-coverage test-map
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Output:
|
|
130
|
+
```
|
|
131
|
+
app_file , test
|
|
132
|
+
myapp.py , test_a
|
|
133
|
+
myapp.py , test_b
|
|
134
|
+
helpers.py , test_a
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
For per-line detail:
|
|
138
|
+
```bash
|
|
139
|
+
mpy-coverage test-map --line-detail
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Output:
|
|
143
|
+
```
|
|
144
|
+
app_file , line, test
|
|
145
|
+
myapp.py , 1 , test_a
|
|
146
|
+
myapp.py , 1 , test_b
|
|
147
|
+
myapp.py , 5 , test_a
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Test names are extracted from `_metadata.test_script` in the JSON (set automatically by the CLI), with a filename-based fallback for older data files.
|
|
151
|
+
|
|
152
|
+
## Report formats
|
|
153
|
+
|
|
154
|
+
Reports are generated by coverage.py's reporting infrastructure, so all standard formats are supported with identical output to `coverage report`, `coverage html`, etc. See coverage.py's documentation for format details:
|
|
155
|
+
|
|
156
|
+
| Format | Flag | Description | coverage.py docs |
|
|
157
|
+
|--------|------|-------------|-----------------|
|
|
158
|
+
| `text` | `--format text` (default) | Terminal summary table | [cmd_report](https://coverage.readthedocs.io/en/latest/commands/cmd_report.html) |
|
|
159
|
+
| `html` | `--format html` | Annotated source HTML | [cmd_html](https://coverage.readthedocs.io/en/latest/commands/cmd_html.html) |
|
|
160
|
+
| `json` | `--format json` | Machine-readable JSON | [cmd_json](https://coverage.readthedocs.io/en/latest/commands/cmd_json.html) |
|
|
161
|
+
| `xml` | `--format xml` | Cobertura XML for CI | [cmd_xml](https://coverage.readthedocs.io/en/latest/commands/cmd_xml.html) |
|
|
162
|
+
| `lcov` | `--format lcov` | LCOV tracefile | [cmd_lcov](https://coverage.readthedocs.io/en/latest/commands/cmd_lcov.html) |
|
|
163
|
+
|
|
164
|
+
Multiple formats in one invocation: `--format text --format html --format xml`
|
|
165
|
+
|
|
166
|
+
## Tracer API
|
|
167
|
+
|
|
168
|
+
For direct use without the CLI wrapper. This runs on the MicroPython device, not the host.
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
import mpy_coverage
|
|
172
|
+
|
|
173
|
+
# Functional API
|
|
174
|
+
mpy_coverage.start(
|
|
175
|
+
include=['mymod'], # filename substring filters (list or None)
|
|
176
|
+
exclude=['test_'], # exclusion filters (list or None)
|
|
177
|
+
collect_executable=False, # collect co_lines data for pathway A
|
|
178
|
+
collect_arcs=False, # collect line-to-line arcs for branch coverage
|
|
179
|
+
)
|
|
180
|
+
# ... run code under test ...
|
|
181
|
+
mpy_coverage.stop()
|
|
182
|
+
data = mpy_coverage.get_data() # returns dict
|
|
183
|
+
mpy_coverage.export_json('out.json') # to file
|
|
184
|
+
mpy_coverage.export_json() # to stdout with serial delimiters
|
|
185
|
+
|
|
186
|
+
# Context manager
|
|
187
|
+
with mpy_coverage.coverage(include=['mymod'], collect_arcs=True):
|
|
188
|
+
import mymod
|
|
189
|
+
mymod.run()
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Filtering uses substring matching on filenames. The tracer always excludes itself.
|
|
193
|
+
|
|
194
|
+
Then on the host:
|
|
195
|
+
```bash
|
|
196
|
+
python -m mpy_coverage.report coverage.json --method ast --show-missing
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Executable line detection
|
|
200
|
+
|
|
201
|
+
Three methods for determining which lines are executable:
|
|
202
|
+
|
|
203
|
+
| Method | Where | Pros | Cons |
|
|
204
|
+
|--------|-------|------|------|
|
|
205
|
+
| `co_lines` | On-device | No host tools needed, exact MicroPython view | Only sees called functions; uncalled code is invisible |
|
|
206
|
+
| `ast` | Host CPython | Sees all code, same parser as coverage.py | May differ from MicroPython's view on edge cases |
|
|
207
|
+
| `mpy` | Host via mpy-cross | Exact MicroPython bytecode view, sees all code | Requires mpy-cross binary |
|
|
208
|
+
|
|
209
|
+
`--method auto` (default) tries `mpy` first, falling back through the others. For cross-validation against coverage.py, `ast` is preferred since both sides use the same `PythonParser` — any differences in executed/missing lines reveal genuine tracing divergences rather than parser disagreements.
|
|
210
|
+
|
|
211
|
+
## JSON format
|
|
212
|
+
|
|
213
|
+
```json
|
|
214
|
+
{
|
|
215
|
+
"_metadata": {"test_script": "test_myapp"},
|
|
216
|
+
"executed": {"filename.py": [1, 3, 5, 7]},
|
|
217
|
+
"executable": {"filename.py": [1, 2, 3, 5, 6, 7, 10]},
|
|
218
|
+
"arcs": {"filename.py": [[-1, 1], [1, 3], [3, 5], [5, -1]]}
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
- `executed`: always present — lines traced during execution
|
|
223
|
+
- `executable`: only present when `collect_executable=True` (co_lines pathway)
|
|
224
|
+
- `arcs`: only present when `collect_arcs=True` or `--branch` was used — each arc is `[from_line, to_line]` where negative values encode function entry/exit boundaries
|
|
225
|
+
- `_metadata`: added by the CLI wrapper, includes `test_script` name for test-map
|
|
226
|
+
|
|
227
|
+
## Data directory structure
|
|
228
|
+
|
|
229
|
+
```
|
|
230
|
+
.mpy_coverage/
|
|
231
|
+
20260213_143022_test_foo.json
|
|
232
|
+
20260213_143025_test_bar.json
|
|
233
|
+
20260213_143030_test_baz.json
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Files are named `<YYYYMMDD_HHMMSS>_<script_basename>.json`. The `report` command merges all JSON files in the directory.
|
|
237
|
+
|
|
238
|
+
## Divergences from CPython / coverage.py
|
|
239
|
+
|
|
240
|
+
### `--branch` flag placement
|
|
241
|
+
|
|
242
|
+
In [coverage.py](https://coverage.readthedocs.io/en/latest/branch.html), `--branch` is a collection-time flag on `coverage run`. Report commands auto-detect branch data. This toolchain follows the same pattern: `--branch` on `run`, auto-detect on `report`.
|
|
243
|
+
|
|
244
|
+
### except-header line tracing
|
|
245
|
+
|
|
246
|
+
MicroPython's compiler emits `set_source_line` for `except XxxError:` handler lines even when the exception path is not taken. CPython only traces these lines when the handler actually executes. This causes MicroPython to report one extra "executed" line per unvisited except clause.
|
|
247
|
+
|
|
248
|
+
In practice this means slightly higher coverage percentages for code with exception handlers that aren't exercised. The cross-validation tests document this as a known divergence.
|
|
249
|
+
|
|
250
|
+
### Arc encoding conventions
|
|
251
|
+
|
|
252
|
+
MicroPython's settrace produces raw line-to-line transition arcs recorded as `(previous_line, current_line)` pairs. coverage.py's `PythonParser` models arcs using AST-derived conventions where negative line numbers encode function entry (`-co_firstlineno -> first_body_line`) and exit (`last_line -> -co_firstlineno`).
|
|
253
|
+
|
|
254
|
+
These representations don't map 1:1:
|
|
255
|
+
- Function entry arcs: MicroPython records `(def_line, first_body_line)` as a positive transition; coverage.py uses `(-def_line, first_body_line)`
|
|
256
|
+
- While/for loop arcs: MicroPython's bytecode produces different line-to-line transitions than CPython's AST-derived arc model
|
|
257
|
+
- Class body: Sequential class body lines appear as interior arcs in MicroPython but are modeled as entry arcs by coverage.py
|
|
258
|
+
|
|
259
|
+
Line-level coverage metrics (statements, executed, missing, percentage) are unaffected by these arc convention differences and match exactly between the two systems when using the `ast` method.
|
|
260
|
+
|
|
261
|
+
### Constructs not cross-validated
|
|
262
|
+
|
|
263
|
+
The following constructs are excluded from cross-validation tests due to known irreconcilable differences:
|
|
264
|
+
|
|
265
|
+
- **List comprehensions**: CPython 3.12+ creates a hidden function scope; MicroPython doesn't. This produces different arc structures.
|
|
266
|
+
- **Generators/yield**: arc encoding for yield vs return differs between VMs.
|
|
267
|
+
- **Boolean short-circuit**: arc differences within a single line aren't detectable at line-level coverage.
|
|
268
|
+
|
|
269
|
+
## Cross-validation tests
|
|
270
|
+
|
|
271
|
+
`test_coverage.py` includes 4 cross-validation trials that run the same code under both CPython+coverage.py and MicroPython+mpy_coverage, then compare metrics:
|
|
272
|
+
|
|
273
|
+
| Trial | Description |
|
|
274
|
+
|-------|-------------|
|
|
275
|
+
| `trial_xval_lines` | Line coverage with partial path execution (~66%) |
|
|
276
|
+
| `trial_xval_lines_full` | Line coverage with full path execution (100%) |
|
|
277
|
+
| `trial_xval_branches` | Branch data collection with partial paths |
|
|
278
|
+
| `trial_xval_branches_full` | Branch data collection with full paths |
|
|
279
|
+
|
|
280
|
+
The test target (`test_target_xval.py`) covers: if/elif/else, for+break+else, while, try/except/finally, nested closures, classes with methods, ternary expressions, multiple return points, and with-statements.
|
|
281
|
+
|
|
282
|
+
Run all trials:
|
|
283
|
+
```bash
|
|
284
|
+
MPY_BINARY=path/to/micropython python tests/test_coverage.py
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## Limitations
|
|
288
|
+
|
|
289
|
+
- settrace adds significant runtime overhead, not suitable for timing-sensitive code
|
|
290
|
+
- `co_lines` method only reports executable lines for functions that were entered, uncalled functions are invisible rather than showing 0%
|
|
291
|
+
- native/viper functions are not traced by settrace
|
|
292
|
+
- large `_executed` dicts may hit memory limits on constrained devices
|
|
293
|
+
- report integration overrides `Coverage._get_file_reporter()` which is a private API and may change across coverage.py versions
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling", "hatch-vcs"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "mpy-coverage"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Code coverage for MicroPython"
|
|
9
|
+
requires-python = ">=3.10"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
dependencies = ["coverage", "mpy-cross"]
|
|
12
|
+
|
|
13
|
+
[project.scripts]
|
|
14
|
+
mpy-coverage = "mpy_coverage.cli:main"
|
|
15
|
+
|
|
16
|
+
[dependency-groups]
|
|
17
|
+
dev = ["pytest", "ruff"]
|
|
18
|
+
|
|
19
|
+
[tool.hatch.version]
|
|
20
|
+
source = "vcs"
|
|
21
|
+
|
|
22
|
+
[tool.hatch.build.hooks.vcs]
|
|
23
|
+
version-file = "src/mpy_coverage/_version.py"
|
|
24
|
+
|
|
25
|
+
[tool.ruff]
|
|
26
|
+
line-length = 99
|
|
27
|
+
extend-exclude = ["src/mpy_coverage/_vendor"]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""MicroPython code coverage toolchain."""
|
|
2
|
+
|
|
3
|
+
from mpy_coverage._version import __version__ # noqa: F401
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def __getattr__(name):
|
|
7
|
+
if name in ("merge_coverage_data", "run_report"):
|
|
8
|
+
from mpy_coverage.report import merge_coverage_data, run_report
|
|
9
|
+
|
|
10
|
+
return {"merge_coverage_data": merge_coverage_data, "run_report": run_report}[name]
|
|
11
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
__all__ = ["merge_coverage_data", "run_report", "__version__"]
|
|
File without changes
|