Files
mckero ae8b48c85a Run anywhere: no author paths left, and CI that proves it
tools/run_tests.sh defaults QEMU_SRC to a sibling of the checkout, which is where setup_qemu.sh puts it; the two defaults disagreed, so a fresh clone rebuilt nothing and reported a build that was not there. The interpreter list was also reading an empty $PY.

tools/test_bk4819_readback.sh was the last test with the author's paths, and the only one that could not run elsewhere. It now takes QEMU, GDB and ELF from the environment or PATH like the python tests, skips with a reason when one is missing, and says so on a platform whose QEMU cannot make the unix socket it uses.

tools/check_docs.py points UVK5_FW_DIR at a sibling and skips the file:line checks, with a message, when there is no firmware tree -- a fresh clone used to see thirteen failures it could do nothing about.

Added .github/workflows/unit.yml (the fast half of run_tests.sh on every push and PR), requirements-dev.txt for the one pip dependency, and a Dockerfile. Flask is not always installed, so test_webui now skips through setUpModule rather than erroring.
2026-10-01 15:12:45 +08:00

4.0 KiB

Contributing

Short version: build it, run the tests, and do not commit firmware or radio data.

Get it running

QEMU_SRC=~/src/qemu-7.2 bash tools/setup_qemu.sh   # patch a QEMU tree and build it
python3 tools/fetch_firmware.py                    # a release image to run
python3 tools/make_flash.py                        # the flash image it reads settings from
pip install -r requirements-dev.txt                # flask, for the web UI tests
bash tools/run_tests.sh -q                         # fast; no emulator needed
bash tools/run_tests.sh                            # everything; needs the tree above

tools/run_tests.sh checks the build first and stops if it failed. That matters: ninja leaves the previous binary in place, so a suite run against a broken build reports results for code that was never compiled. It has happened here twice.

The emulator tests boot their own QEMU on private ports, so they do not disturb a run-webui.ps1 or run.sh session. A missing QEMU, firmware or gdb makes a test skip with a message, never fail -- a missing prerequisite is not a regression, and a suite that fails on a fresh checkout teaches people to ignore it.

Two helpers keep that honest: tools/uvk5_socket.py gives every test an endpoint that works where the platform has unix sockets and TCP where it does not, and tools/uvk5_testenv.py finds a QEMU, a firmware and a gdb, skipping with a reason when one is absent. Use them rather than hardcoding a path or a socket family.

What runs automatically

.github/workflows/unit.yml runs tools/run_tests.sh -q on every push and pull request -- the unit tests and the documentation check, which need nothing but Python. If you add a test that needs an emulator, it belongs in the slow half of the runner, not here; if you add one that needs nothing, make sure it is picked up by the -q path so CI covers it.

Dockerfile gives you the same environment locally.

What not to commit

  • Firmware of any kind, including released images, localised builds and bootloader dumps. tools/fetch_firmware.py fetches what a test needs into assets/firmware/, which is ignored.
  • Anything from a real radio. work/data.bin is an EEPROM dump with settings and calibration; the tests build their own images from assets/pristine/.
  • Scratch under work/ -- images, logs, captures. The four .ps1 scripts there are tracked on purpose.

If you find any of that already in the history, say so before pushing: removing it in a new commit does not remove the objects.

House rules

These come from mistakes that already cost time, and AGENTS.md has the long version of each. The ones worth repeating:

  • Never edit the firmware to make the emulator work. The firmware is the reference; if something does not run, the model is wrong. A fix in firmware source makes every later test meaningless.
  • Instrument the model, not the guest. A breakpoint stops the machine and changes what you are measuring. Put a probe in qemu/py32f071.c and read the output.
  • A probe has to be able to see what it looks for. Three rounds of "the firmware never touches the flash" were a probe filtering on an address field that write-enable and erase frames do not have.
  • Check the build succeeded before believing a test. See above.
  • Document what you got wrong, in AGENTS.md and AGENTS.zh-CN.md together. The two are kept in step; tools/check_docs.py checks the heading structure and the tool names.
  • Never invent data. Audio and RF are not modelled because the MCU never sees them, not because nobody got round to it.

Documentation

Every document has a Chinese pair (README.md / README.zh-CN.md, AGENTS.md / AGENTS.zh-CN.md) with the same heading structure. python3 tools/check_docs.py checks that the tools and flags a document names exist, that the heading pairs match, that the memory-map addresses match the model, and that firmware file:line references still point at what the prose claims. It runs as part of run_tests.sh -q.