Skip to content

About

Unit tests for the grblHAL core

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

3,024 Commits

Folders and files

Repository files navigation

Unit tests for grblHAL

Core

Component Compile Lines Branches
Core compile: hosted pass lines: 98.50% branches: 92.69%

Plugins

Plugin / suite Compile Lines Branches
EEPROM compile: local pass lines: not measured branches: not measured
Encoder compile: local pass lines: not measured branches: not measured
Keypad compile: local pass lines: 89.0% branches: 68.5%
Networking compile: local pass lines: 84.2% branches: 70.6%
Odometer compile: local pass lines: not measured branches: not measured
Plasma/THC compile: local pass lines: 62.9% branches: 49.5%
SD card / plugin_sdcard_stream compile: local pass lines: not measured branches: not measured
SD card / plugin_sdcard_fatfs compile: local pass lines: 75.2% branches: 55.4%
Spindle / plugin_spindle_vfd compile: local pass lines: 96.8% branches: 72.2%
Spindle / plugin_spindle_onoff compile: local pass lines: 94.9% branches: 80.0%
Spindle / plugin_spindle_pwm compile: local pass lines: 5.3% branches: 3.1%
Spindle / plugin_spindle_select compile: local pass lines: 65.3% branches: 54.8%
Spindle / plugin_spindle_offset compile: local pass lines: 96.9% branches: 93.3%
Spindle / plugin_spindle_clone compile: local pass lines: 98.6% branches: 70.8%
Spindle / plugin_spindle_stepper compile: local pass lines: 95.0% branches: 73.1%
Trinamic compile: local pass lines: 76.6% branches: 62.0%
Laser/PPI / plugin_laser_ppi compile: local pass lines: not measured branches: not measured
Laser/PPI / plugin_laser_cluster compile: local pass lines: not measured branches: not measured
Laser/PPI / plugin_laser_coolant compile: local pass lines: 87.9% branches: 70.6%
Laser/PPI / plugin_laser_overdrive compile: local pass lines: 100.0% branches: 79.2%
Fans compile: local pass lines: 96.8% branches: 85.7%
Bluetooth compile: local pass lines: 98.4% branches: 84.8%
WebUI compile: local pass lines: 91.4% branches: 71.3%
OpenPNP compile: local pass lines: not measured branches: not measured
Embroidery compile: local pass lines: 86.4% branches: 76.1%
Miscellaneous compile: hosted pass lines: 89.7% branches: 73.2%
Add status of digital aux outputs to real-time report compile: local pass lines: 100.0% branches: 100.0%
'Datron like' RGB indicator lights compile: incompatible lines: not measured branches: not measured
Control panel compile: incompatible lines: not measured branches: not measured
CRC ensured MPG communication compile: local pass lines: 93.9% branches: 72.1%
FlexGPIO I2C I/O expander compile: local pass lines: 99.3% branches: 85.3%
Homing acceleration compile: local pass lines: 100.0% branches: 97.7%
Interlock compile: local pass lines: 100.0% branches: 85.7%
PicoHAL compile: incompatible lines: not measured branches: not measured
Pneumaseal compile: local pass lines: 99.3% branches: 82.7%
Sienci ATCi compile: local pass lines: 95.7% branches: 80.8%
Sienci Lights compile: local pass lines: 97.8% branches: 94.5%
Thermistor compile: local pass lines: 99.3% branches: 93.2%
Virtual inputs compile: local pass lines: 100.0% branches: 93.0%

Drivers

Driver / suite Compile Lines Branches
STM32H7xx / driver_stm32h7_flash compile: local pass lines: 100.0% branches: 100.0%
STM32H7xx / driver_stm32h7_serial compile: hosted pass lines: 98.8% branches: 89.1%
ESP32 compile: local pass lines: not measured branches: not measured
LPC176x compile: local pass lines: 100.0% branches: 100.0%
MSP430F5529 compile: not added lines: not measured branches: not measured
MSP432E401Y compile: not added lines: not measured branches: not measured
MSP432P401R compile: local pass lines: 100.0% branches: 90.2%
PSoC5 / driver_psoc5_serial_reference compile: legacy only lines: 100.0% branches: 87.5%
RP2040 (Pi Pico & Pi Pico W) compile: local pass lines: not measured branches: not measured
SAM3X8E (Arduino Due) compile: local pass lines: 99.3% branches: 92.6%
SAMD21 compile: not added lines: not measured branches: not measured
STM32F1xx / driver_stm32f1_flash compile: local pass lines: 100.0% branches: 100.0%
STM32F1xx / driver_stm32f1_serial compile: local pass lines: 96.6% branches: 82.5%
STM32F3xx / driver_stm32f3_flash compile: local pass lines: 100.0% branches: 100.0%
STM32F3xx / driver_stm32f3_serial compile: local pass lines: 100.0% branches: 93.8%
STM32F4xx / driver_stm32f4_flash compile: local pass lines: 100.0% branches: 100.0%
STM32F4xx / driver_stm32f4_serial compile: local pass lines: 98.8% branches: 88.5%
STM32F7xx / driver_stm32f7_flash compile: local pass lines: 100.0% branches: 100.0%
STM32F7xx / driver_stm32f7_serial compile: local pass lines: 98.9% branches: 88.5%
Simulator / driver_simulator compile: local pass lines: not measured branches: not measured
Simulator / driver_simulator_vfs compile: incompatible lines: not measured branches: not measured
TM4C123 / driver_tm4c123_eeprom compile: local pass lines: 96.1% branches: 85.0%
TM4C123 / driver_tm4c123_serial compile: local pass lines: 100.0% branches: 91.7%
TM4C1294 compile: not added lines: not measured branches: not measured
iMXRT1062 (Teensy 4.x) compile: local pass lines: 100.0% branches: 83.8%
TI SimpleLink shared EEPROM and UART / driver_ti_shared_eeprom compile: local pass lines: 96.1% branches: 85.0%
TI SimpleLink shared EEPROM and UART / driver_ti_shared_serial_reference compile: legacy only lines: 97.1% branches: 80.0%

Host tests compile original source with simulated hardware callbacks. Badge percentages cover selected files and configurations. Local pass and hosted pass describe host builds; known regressions are tracked separately. Grey badges mark work not added or measured. Legacy only uses an older CORE. Component revisions and scope.

Issues and coverage gaps

GitHub Actions

Open Actions, choose Core tests, Plugin tests or Driver tests, then select Run workflow. Choose the harness branch to run; for plugins and drivers, also choose a registered suite.

Input Use
source_repository Source owner/repo or GitHub URL; blank uses the suite default
source_ref Branch, tag or commit SHA; blank uses the default branch
source_pr PR number in the selected repository; leave source_ref blank
core_ref CORE dependency branch, tag or SHA for plugin/driver suites
source_base_ref, source_base_repository Optional comparison base for changed-code coverage; available in Core tests

Defaults are in ci/suites.json; CORE defaults to grblHAL/core at master. A different source repository requires an explicit ref or PR number. PR selection tests its head commit, including fork PRs. Each run records exact harness and source commits in source-lock.json. Use commit SHAs to repeat the same source selection.

gh workflow run tests.yml --repo grblHAL/Unit_tests --ref master -f source_pr=123

gh workflow run plugins.yml --repo grblHAL/Unit_tests --ref master \
  -f suite=plugin_eeprom -f source_ref=my-fix -f core_ref=CORE_SHA

Replace CORE_SHA with a real commit, or omit core_ref to use the default. Manual runs require write access. GCC and Clang run sanitizer tests; GCC also publishes coverage. Selected ARM driver suites include module compile checks. These checks do not replace firmware builds or controller testing.

Reusable workflow inputs

Call reusable-tests.yml from another repository's workflow. Start with the CORE caller or the plugin caller template. The run appears in the calling repository's Actions and PR checks.

Input Set it to
harness_repository Tests repository, normally grblHAL/Unit_tests (required)
harness_ref Same branch, tag or SHA as the reusable workflow's uses: ...@ref (required)
suite Key in ci/suites.json; defaults to core
source_repository Repository containing the code to test; blank uses the suite default
source_ref Source branch, tag or SHA; omit when using source_pr
source_pr PR number in the selected source repository
source_base_ref Optional comparison-base branch, tag or SHA
source_base_repository Base repository; defaults to the selected source repository
core_ref CORE dependency revision for a plugin/driver suite; leave blank for CORE tests
run_label Unique artifact name suffix when calling more than once; defaults to core

The harness revision selects the tests; the source revision selects the code being tested. Keep the workflow uses ref and harness_ref on the same version. No dispatch token or inherited secrets are needed.

Run locally

Install Python 3, CMake and GCC or GNU-compatible Clang. Windows builds can use llvm-mingw with Ninja; MSVC supports only standalone modules.

git clone https://github.com/grblHAL/Unit_tests.git
cd Unit_tests
python scripts/resolve_source.py
python scripts/prepare_sources.py
python scripts/configure_suite.py --mode sanitizers --compiler clang
python scripts/build_suite.py
python scripts/run_suite.py test
python scripts/run_suite.py summary

For another suite or source, pass the selection to resolve_source.py:

python scripts/resolve_source.py --suite plugin_eeprom --ref my-fix --core-ref CORE_SHA

Use a fresh checkout when switching locked sources; preparation refuses to overwrite dirty or mismatched source directories. On Windows, put the compiler's bin directory on PATH for sanitizer DLLs and set $env:CMAKE_GENERATOR = "Ninja" before configuring.

Retest a core fix locally

The ignored core directory can be a fresh ordinary clone, an existing local checkout or a worktree. It is not staged in this test repository. You can test uncommitted fixes without publishing them. Hosted CI needs a reachable commit, but pushing to your fork is enough; no upstream merge is required.

For a fresh checkout, resolve and fetch:

python scripts/resolve_source.py --repository yourname/core --ref my-fix
python scripts/prepare_sources.py

The fetch helper refuses to overwrite dirty checkouts or silently change existing revisions. For an existing checkout, preserve local changes and switch explicitly:

git -C core status --short
git -C core fetch origin my-fix
git -C core checkout --detach FETCH_HEAD
git -C core rev-parse HEAD

Use a fresh build directory when switching revisions. Save the tested SHA with your results, then follow the regression cleanup steps below.

Run the tests before changing exceptions

Use a sanitizer build, including the summary check. For Windows llvm-mingw, replace the compiler path below with your installation and put its bin directory on PATH as described under Run locally.

cmake -S . -B build-asan -G Ninja -DCMAKE_C_COMPILER=C:/path/to/llvm-mingw/bin/clang.exe -DCMAKE_BUILD_TYPE=Debug -DENABLE_SANITIZERS=ON
cmake --build build-asan --parallel
ctest --test-dir build-asan --output-on-failure --output-junit results.xml
python scripts/summary.py tests build-asan/results.xml

On Linux, use -DCMAKE_C_COMPILER=clang or gcc instead. If an existing build uses another compiler or generator, use a new build directory. Reconfigure after changing exception registrations so CTest picks up the changes.

A fixed assertion regression may initially appear as a CTest failure because its WILL_FAIL exception expects the executable to fail. Confirm the executable now passes its correct assertions; do not change those assertions to match the old bug. For one test, inspect its output with:

ctest --test-dir build-asan -V -R '^utf8\.ascii$'

Replace utf8.ascii with the test name listed in the defect entry. Run the full suite as well: a core update can affect more than the defect being fixed.

Remove the fixed defect's exceptions

Start with its entry in tests/KNOWN_BUGS.md. Each entry links to the affected tests and says exactly which exception registrations or scripts need cleanup. Depending on the defect, cleanup is in these places:

  • Wrong result / expected assertion failure: remove the matching complete if(TEST ...) ... endif() block from tests/known_bugs.cmake. This removes WILL_FAIL and the known_bug label, making the test an ordinary passing test.
  • Correct result with an undefined-behavior warning: delete every JSON object with that defect's issue identifier from tests/known_sanitizer_diagnostics.json. These exceptions are documented in KNOWN_BUGS.md, but the actual warning allowance is stored in this JSON file. One defect can have several objects. Remove all objects for the resolved issue, keeping valid JSON (an empty manifest is []). Also remove that test's known_sanitizer_bug label block from tests/known_bugs.cmake. The label applies only to sanitizer builds: if its expected diagnostic disappears, reporting fails with cleanup guidance. An unrelated diagnostic or assertion still fails. Keep the correct output assertions; the summary must then report a clean pass.
  • Memory defect checked by a diagnostic wrapper: follow the defect's linked instructions to replace its special Python-wrapper registration in CMakeLists.txt with a normal executable test. Remove the matching exception in tests/known_bugs.cmake and any obsolete matcher. The executable must pass under sanitizers without relying on the old diagnostic.
  • Feature configuration that previously could not compile: replace the linked diagnostic-matching compile check with a normal successful compile check for that configuration. Remove its expected-failure registration and obsolete matcher, following the links in the defect entry.

Keep the regression test means keep its file in tests/cases/ and its check for the correct behavior. For example, tests/cases/utf8/ascii.c must continue checking that A encodes as byte 0x41 after the encoding bug is fixed. The test then guards against a future change reintroducing that bug. Delete the exception that tolerates the bug, rather than deleting the test that catches it.

After removing the exceptions, reconfigure, rebuild and rerun the full sanitizer suite and summary using the commands above. All affected tests must be ordinary clean passes. Also run the coverage build under Measure coverage locally. Do not weaken expectations or broaden diagnostic allowances to make an update green.

Remove the known-bug entry and commit

Once the fix passes without its exceptions, delete the entire resolved defect section from tests/KNOWN_BUGS.md. That file lists outstanding defects only. Put the fix date and core commit in the Git commit message, where history preserves them. If only part of a multi-test defect is fixed, retain an entry covering the remaining failures and their exceptions.

Check that remaining documentation links still point to the correct exception blocks and source lines; deleting blocks can shift line numbers. Refresh any remaining diagnostic source-line matches if upstream edits moved them, after confirming the same defect still reproduces.

git diff --check
git diff

Stage the documentation, exception and registration files you actually changed as well. Commit the test and exception cleanup, then push and verify both GCC and Clang sanitizer jobs and the coverage job in GitHub Actions. If the new core introduces an additional defect, add a separate correctly expected regression and dated known-bug entry; do not silently waive it.

Measure coverage locally

Use a separate build without sanitizers. Install the CI reporter: python -m pip install gcovr==8.4.

cmake -S . -B build-coverage -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=gcc -DENABLE_COVERAGE=ON
cmake --build build-coverage --parallel
ctest --test-dir build-coverage --output-on-failure
gcovr --merge-mode-functions=separate --root . --filter 'core/.*\.[ch]$' --html-details coverage.html --xml coverage.xml --print-summary build-coverage
python scripts/summary.py coverage coverage.xml

For Clang, select clang when configuring and add --gcov-executable "llvm-cov gcov" to gcovr. Coverage combines the configured feature variants, including emitted header code. Use a new build directory for a fresh measurement. On Windows, verify .gcda files are produced; long paths can prevent LLVM from writing them even when tests pass.

Adding tests

Compile original source; do not copy implementations into tests. Use public commands and APIs, shared fixtures, and assertions about observable behavior. Keep unrelated behaviors in separate files. Include useful expected/actual values when an assertion alone would be ambiguous.

  • CORE: add tests/cases/<module>/<case>.c; CMake discovers it automatically. Add required module dependencies in add_core_test().
  • Plugin or driver: add a CMake project under tests/plugins/<name> or tests/drivers/<name>, register its suite in ci/suites.json, then add the suite to the matching workflow's choices. Reuse an existing component project as a starting point.
  • Regression: retain the correct assertions. Document confirmed defects and narrow exceptions in the suite's KNOWN_BUGS.md; remove exceptions after a fix using the steps above.

Source registry

Field Purpose
repository, ref Source repository and default branch/tag
path Ignored source checkout directory
dependencies Direct registry entries to fetch; source must still be linked explicitly
source_only Dependency checkout with no selectable test suite
cmake_source, cmake_options CMake project and configure arguments
test_regex CTest names selected by the suite; zero selected tests fails
coverage_filter gcovr filter for measured source files
diagnostics_directory Suite directory containing its exception inventory
host_bits Optional 32-bit host build; defaults to 64

Register implementation repositories from the plugin and driver catalogues. Dependencies are resolved directly; arbitrary nested dependency graphs are not supported. Firmware fixes belong in the source repository.

About

Unit tests for the grblHAL core

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages