Files
seedsigner/tests
ruiva 9a679ff3ae Serialize the Bytewords CRC as a fixed 4 bytes and re-enable the check
crc32n() sized its output to the CRC value's bit length, so a CRC below 2**24
was emitted as 3 bytes instead of 4 (~1 in 256 payloads; 2 bytes below 2**16).
bytewords.decode() unconditionally strips buf[-4:] as the checksum, so a short
CRC costs the payload its last byte.

This corrupts data the device *emits*. Both UREncoder.encode() (single-part)
and UREncoder.encode_part() (fountain frames) go through Bytewords.encode(),
so roughly 0.4% of every UR2 QR frame SeedSigner displays is not spec
compliant. For a single-part UR the frame is deterministic, so an affected
wallet's xpub QR is broken on every export, permanently: measured 2 broken
exports out of 600 randomly generated single-sig wallets. A spec-compliant
reader rejects the frame on the checksum; a lax reader silently drops the
payload's final byte.

The bug is silent because the checksum comparison in decode() was commented
out. Re-enabled here, which is only possible once crc32n emits a fixed width:
otherwise `checksum` (variable length) and `body_checksum` (always buf[-4:])
could not be compared.

Note utils.int_to_bytes() already carries the fixed-width form with the
bit-length version commented out above it.

Vendored from Foundation Devices' ur-py; I have not checked whether upstream
shares this.
2026-08-01 10:33:30 +01:00
..
2025-10-23 14:23:27 -05:00
2024-11-06 14:00:36 -06:00
2025-02-27 10:53:57 -06:00
2024-11-05 12:19:35 -06:00
2025-12-17 21:11:14 +01:00
2025-12-26 07:13:00 -06:00
2024-03-02 07:46:35 -06:00
2025-12-09 20:17:18 +01:00
2024-07-14 13:21:23 -05:00
2025-01-02 18:38:03 -06:00

Running Tests

The tests are designed to be run on non-Raspi hardware.

Setup

On your testing machine you'll have to install:

# general dependencies
pip3 install -r requirements.txt

# test suite dependencies
pip3 install -r tests/requirements.txt

Then make the seedsigner python module visible/importable to the tests by installing it:

pip3 install -e .

Running all tests, calculating overall test coverage

tldr: just run the convenience script from the project root:

./tests/run_full_coverage.sh

Running tests manually

Run the whole test suite:

pytest

Run a specific test file:

pytest tests/test_this_file.py

Run a specific test:

pytest tests/test_this_file.py::test_this_specific_test

Force pytest to show logging output:

pytest tests/test_this_file.py::test_this_specific_test -o log_cli=1

# or (same result)

pytest tests/test_this_file.py::test_this_specific_test --log-cli-level=DEBUG

Annoying complications:

  • If you want to see print() statements that are in a test file, add -s
  • Better idea: use a proper logger in the test file and use one of the above options to display logs

Screenshot generator

The screenshot generator is meant to mostly be a utility and not really part of the test suite. However, it is actually implemented to be run by pytest.

see: Screenshot generator README

Generate coverage manually

Run tests and generate test coverage

coverage run -m pytest

The screenshots can generate their own separate coverage report:

coverage run -m pytest tests/screenshot_generator/generator.py --locale es

Show the resulting test coverage details:

coverage report

Generate the interactive html report:

coverage html