From b637a128575ca1a512c0167f14f1cd6b31f3af94 Mon Sep 17 00:00:00 2001 From: David Cozens Date: Tue, 26 May 2026 15:21:42 +0100 Subject: [PATCH 01/12] chore: S24.11 bump container images to IWYU-enabled SHAs cpputest now ships include-what-you-use 0.23 (clang_19), matching the cpputest-clang build. cpputest-freertos and its cross sibling inherit IWYU by chaining FROM the rebased base, enabling the freertos-aware analyze-iwyu lane that subsequent commits on this branch add. - ghcr.io/davidcozens/cpputest: sha-18f19e1 -> sha-2c7b76b - ghcr.io/davidcozens/cpputest-freertos: sha-24de138 -> sha-a0c1c0a - ghcr.io/davidcozens/cpputest-freertos-cross: sha-24de138 -> sha-a0c1c0a --- .devcontainer/docker-compose.yml | 6 +++--- .github/workflows/ci.yml | 22 +++++++++++----------- ci/docker-compose.bdd.yml | 2 +- docs/containers.md | 6 +++--- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index f2c860b9..eaf8b849 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -1,6 +1,6 @@ services: gcc: - image: ghcr.io/davidcozens/cpputest:sha-18f19e1 + image: ghcr.io/davidcozens/cpputest:sha-2c7b76b volumes: - ..:/workspaces/SolidSyslog:cached - ${SSH_AUTH_SOCK:-/dev/null}:/ssh-agent @@ -102,7 +102,7 @@ services: command: sleep infinity freertos-host: - image: ghcr.io/davidcozens/cpputest-freertos:sha-24de138 + image: ghcr.io/davidcozens/cpputest-freertos:sha-a0c1c0a volumes: - ..:/workspaces/SolidSyslog:cached - ${SSH_AUTH_SOCK:-/dev/null}:/ssh-agent @@ -153,7 +153,7 @@ services: - syslog-ng freertos-target: - image: ghcr.io/davidcozens/cpputest-freertos-cross:sha-24de138 + image: ghcr.io/davidcozens/cpputest-freertos-cross:sha-a0c1c0a volumes: - ..:/workspaces/SolidSyslog:cached - syslog-ng-ctl-freertos:/var/lib/syslog-ng diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 93ef11e1..d96fbc4b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: contents: read checks: write container: - image: ghcr.io/davidcozens/cpputest:sha-18f19e1 + image: ghcr.io/davidcozens/cpputest:sha-2c7b76b options: --user root env: GIT_CONFIG_COUNT: 1 @@ -76,7 +76,7 @@ jobs: contents: read checks: write container: - image: ghcr.io/davidcozens/cpputest:sha-18f19e1 + image: ghcr.io/davidcozens/cpputest:sha-2c7b76b options: --user root env: GIT_CONFIG_COUNT: 1 @@ -119,7 +119,7 @@ jobs: # cpputest-freertos image carries /opt/mbedtls (and exports MBEDTLS_DIR); # Tests/MbedTlsIntegration/CMakeLists.txt requires that env var. container: - image: ghcr.io/davidcozens/cpputest-freertos:sha-24de138 + image: ghcr.io/davidcozens/cpputest-freertos:sha-a0c1c0a options: --user root env: GIT_CONFIG_COUNT: 1 @@ -224,7 +224,7 @@ jobs: contents: read checks: write container: - image: ghcr.io/davidcozens/cpputest:sha-18f19e1 + image: ghcr.io/davidcozens/cpputest:sha-2c7b76b options: --user root env: GIT_CONFIG_COUNT: 1 @@ -300,7 +300,7 @@ jobs: contents: read checks: write container: - image: ghcr.io/davidcozens/cpputest:sha-18f19e1 + image: ghcr.io/davidcozens/cpputest:sha-2c7b76b options: --user root env: GIT_CONFIG_COUNT: 1 @@ -340,7 +340,7 @@ jobs: pages: write id-token: write container: - image: ghcr.io/davidcozens/cpputest:sha-18f19e1 + image: ghcr.io/davidcozens/cpputest:sha-2c7b76b options: --user root env: GIT_CONFIG_COUNT: 1 @@ -418,7 +418,7 @@ jobs: analyze-tidy: runs-on: ubuntu-latest container: - image: ghcr.io/davidcozens/cpputest:sha-18f19e1 + image: ghcr.io/davidcozens/cpputest:sha-2c7b76b options: --user root env: GIT_CONFIG_COUNT: 1 @@ -474,7 +474,7 @@ jobs: analyze-format: runs-on: ubuntu-latest container: - image: ghcr.io/davidcozens/cpputest:sha-18f19e1 + image: ghcr.io/davidcozens/cpputest:sha-2c7b76b options: --user root env: GIT_CONFIG_COUNT: 1 @@ -492,7 +492,7 @@ jobs: analyze-cppcheck: runs-on: ubuntu-latest container: - image: ghcr.io/davidcozens/cpputest:sha-18f19e1 + image: ghcr.io/davidcozens/cpputest:sha-2c7b76b options: --user root env: GIT_CONFIG_COUNT: 1 @@ -857,7 +857,7 @@ jobs: contents: read checks: write container: - image: ghcr.io/davidcozens/cpputest-freertos:sha-24de138 + image: ghcr.io/davidcozens/cpputest-freertos:sha-a0c1c0a options: --user root env: GIT_CONFIG_COUNT: 1 @@ -901,7 +901,7 @@ jobs: permissions: contents: read container: - image: ghcr.io/davidcozens/cpputest-freertos-cross:sha-24de138 + image: ghcr.io/davidcozens/cpputest-freertos-cross:sha-a0c1c0a options: --user root env: GIT_CONFIG_COUNT: 1 diff --git a/ci/docker-compose.bdd.yml b/ci/docker-compose.bdd.yml index 9da870ae..8e0cfd4e 100644 --- a/ci/docker-compose.bdd.yml +++ b/ci/docker-compose.bdd.yml @@ -87,7 +87,7 @@ services: behave-freertos: # Cross image carries qemu-system-arm + python3 + behave. - image: ghcr.io/davidcozens/cpputest-freertos-cross:sha-24de138 + image: ghcr.io/davidcozens/cpputest-freertos-cross:sha-a0c1c0a user: "0" volumes: - ..:/workspaces/SolidSyslog diff --git a/docs/containers.md b/docs/containers.md index f743d9b8..77d14843 100644 --- a/docs/containers.md +++ b/docs/containers.md @@ -4,10 +4,10 @@ | Image | Tag | Used by | |---|---|---| -| `ghcr.io/davidcozens/cpputest` | `sha-18f19e1` | devcontainer (`gcc` service), most CI jobs | +| `ghcr.io/davidcozens/cpputest` | `sha-2c7b76b` | devcontainer (`gcc` service), most CI jobs. Now ships `include-what-you-use 0.23 (clang_19)` matching the `cpputest-clang` build, so local `iwyu` runs work in the gcc container | | `ghcr.io/davidcozens/cpputest-clang` | `sha-7eac3ab` | `clang` compose service, `build-linux-clang` CI job, `analyze-iwyu` CI job | -| `ghcr.io/davidcozens/cpputest-freertos` | `sha-24de138` | `freertos-host` compose service, `build-freertos-host-tdd-plustcp` CI job — adds FreeRTOS-Kernel / Plus-TCP / lwIP / FatFs / Mbed TLS sources for host-TDD of FreeRTOS adapters against fakes | -| `ghcr.io/davidcozens/cpputest-freertos-cross` | `sha-24de138` | `freertos-target` compose service, `build-freertos-target-plustcp` CI job, `behave-freertos` BDD service, `bdd-freertos-qemu-plustcp` CI job — adds `gcc-arm-none-eabi`, `libnewlib-arm-none-eabi`, `gdb-multiarch` (aliased as `arm-none-eabi-gdb`), `qemu-system-arm`, `python3` + `behave`, FreeRTOS-Kernel / Plus-TCP / lwIP / FatFs sources at `/opt/freertos-kernel` / `/opt/freertos-plus-tcp` / `/opt/lwip` / `/opt/fatfs` for cross builds, on-QEMU runs, and BDD scenarios driving a QEMU target | +| `ghcr.io/davidcozens/cpputest-freertos` | `sha-a0c1c0a` | `freertos-host` compose service, `build-freertos-host-tdd-plustcp` CI job — adds FreeRTOS-Kernel / Plus-TCP / lwIP / FatFs / Mbed TLS sources for host-TDD of FreeRTOS adapters against fakes; inherits IWYU from the rebased `cpputest` base, enabling the freertos-aware `analyze-iwyu` lane | +| `ghcr.io/davidcozens/cpputest-freertos-cross` | `sha-a0c1c0a` | `freertos-target` compose service, `build-freertos-target-plustcp` CI job, `behave-freertos` BDD service, `bdd-freertos-qemu-plustcp` CI job — adds `gcc-arm-none-eabi`, `libnewlib-arm-none-eabi`, `gdb-multiarch` (aliased as `arm-none-eabi-gdb`), `qemu-system-arm`, `python3` + `behave`, FreeRTOS-Kernel / Plus-TCP / lwIP / FatFs sources at `/opt/freertos-kernel` / `/opt/freertos-plus-tcp` / `/opt/lwip` / `/opt/fatfs` for cross builds, on-QEMU runs, and BDD scenarios driving a QEMU target | | `balabit/syslog-ng` | `4.8.2` | `syslog-ng-linux` and `syslog-ng-freertos` services — BDD test oracles, one per target pair. Pinned to the 4.8 LTS line; 4.11.0 (`latest` as of 2026-02-24) regressed by aborting on `STATS` over the control socket, which crashed the oracle and cascaded to the dev-container network when `freertos-target` shares the namespace. | | `ghcr.io/davidcozens/behave` | `sha-3faff14` | `behave-linux` service — Debian trixie + Python 3.12 + Behave for Linux BDD scenarios. The FreeRTOS BDD runner uses the `cpputest-freertos-cross` image instead (which carries QEMU + Behave). | From da9c3a9d64111112cf90a875b78868652ecd4968 Mon Sep 17 00:00:00 2001 From: David Cozens Date: Tue, 26 May 2026 16:16:11 +0100 Subject: [PATCH 02/12] ci: S24.11 add analyze-tidy-freertos-plustcp + analyze-iwyu-freertos-plustcp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors the existing analyze-tidy / analyze-iwyu lanes but runs on cpputest-freertos so the Platform/{FreeRtos,PlusTcp,LwipRaw,MbedTls, FatFs} trees and their Tests/* siblings enter the CMake configure. The base lanes on cpputest silently skip those trees because FREERTOS_KERNEL_PATH / MBEDTLS_DIR / LWIP_PATH / FATFS_PATH are not set. The freertos iwyu lane overrides CMAKE_C_COMPILER explicitly to clang-19 — cpputest-freertos ships the clang-19 binary but no `clang` alternative (only cpputest-clang creates that alias). Could be cleaned up later by adding the alternative to the cpputest gcc base. Findings these lanes surface are addressed in subsequent commits on this branch. --- .github/workflows/ci.yml | 69 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d96fbc4b..953b141c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -471,6 +471,73 @@ jobs: path: build/iwyu/iwyu-output.txt retention-days: 1 + # Mirrors analyze-tidy but runs on cpputest-freertos so the Platform/{FreeRtos, + # PlusTcp,LwipRaw,MbedTls,FatFs} trees and their Tests/* siblings enter the + # CMake configure (gated by FREERTOS_KERNEL_PATH / MBEDTLS_DIR / LWIP_PATH / + # FATFS_PATH, all set in the freertos image). The base lane on cpputest skips + # those trees because the env vars are absent. + analyze-tidy-freertos-plustcp: + runs-on: ubuntu-latest + container: + image: ghcr.io/davidcozens/cpputest-freertos:sha-a0c1c0a + options: --user root + env: + GIT_CONFIG_COUNT: 1 + GIT_CONFIG_KEY_0: safe.directory + GIT_CONFIG_VALUE_0: '*' + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Configure + run: cmake --preset tidy + + - name: Build with clang-tidy + shell: bash + run: set -o pipefail && cmake --build --preset tidy 2>&1 | tee build/tidy/clang-tidy-output.txt + + - name: Upload clang-tidy output + if: success() || failure() + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + with: + name: clang-tidy-report-freertos-plustcp + path: build/tidy/clang-tidy-output.txt + retention-days: 1 + + # Same freertos coverage rationale as analyze-tidy-freertos-plustcp. Overrides + # CMAKE_C_COMPILER explicitly to clang-19 because the cpputest-freertos image + # (chained from the cpputest gcc base) ships clang-19/clang++-19 but no + # `clang`/`clang++` alternative — only the cpputest-clang image creates that + # alias. A future image bump could add the alternative to the gcc base and + # drop these overrides. + analyze-iwyu-freertos-plustcp: + runs-on: ubuntu-latest + container: + image: ghcr.io/davidcozens/cpputest-freertos:sha-a0c1c0a + options: --user root + env: + GIT_CONFIG_COUNT: 1 + GIT_CONFIG_KEY_0: safe.directory + GIT_CONFIG_VALUE_0: '*' + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Configure + run: cmake --preset iwyu -DCMAKE_C_COMPILER=clang-19 -DCMAKE_CXX_COMPILER=clang++-19 + + - name: Run include-what-you-use + shell: bash + run: set -o pipefail && cmake --build --preset iwyu --target iwyu 2>&1 | tee build/iwyu/iwyu-output.txt + + - name: Upload iwyu output + if: success() || failure() + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + with: + name: iwyu-report-freertos-plustcp + path: build/iwyu/iwyu-output.txt + retention-days: 1 + analyze-format: runs-on: ubuntu-latest container: @@ -986,7 +1053,7 @@ jobs: summary: if: always() && github.event_name == 'pull_request' - needs: [build-linux-gcc, build-linux-tunable-override, build-linux-clang, sanitize-linux-gcc, coverage-linux-gcc, analyze-tidy, analyze-cppcheck, analyze-format, analyze-iwyu, bdd-linux-syslog-ng, build-windows-msvc, bdd-windows-otel, integration-linux-openssl, integration-linux-mbedtls, integration-windows-openssl, build-freertos-host-tdd-plustcp, build-freertos-target-plustcp, bdd-freertos-qemu-plustcp] + needs: [build-linux-gcc, build-linux-tunable-override, build-linux-clang, sanitize-linux-gcc, coverage-linux-gcc, analyze-tidy, analyze-tidy-freertos-plustcp, analyze-cppcheck, analyze-format, analyze-iwyu, analyze-iwyu-freertos-plustcp, bdd-linux-syslog-ng, build-windows-msvc, bdd-windows-otel, integration-linux-openssl, integration-linux-mbedtls, integration-windows-openssl, build-freertos-host-tdd-plustcp, build-freertos-target-plustcp, bdd-freertos-qemu-plustcp] runs-on: ubuntu-latest permissions: contents: read From 6e914df777db6d2e5a9fc9a7dcd7e87b79fbee76 Mon Sep 17 00:00:00 2001 From: David Cozens Date: Tue, 26 May 2026 17:35:23 +0100 Subject: [PATCH 03/12] chore: S24.11 drop /opt/* paths from IWYU report MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new analyze-iwyu-freertos-plustcp lane compiles mbedTLS / lwIP / FreeRTOS-Plus-TCP / FatFs INTERFACE-library TUs from /opt/* in the cpputest-freertos image, so include-what-you-use surfaces ~500 findings on those third-party trees. They are not actionable here — we don't own those includes — and they drown out the ~37 in-tree files where the report does want changes. Extends scripts/iwyu_filter.py with an EXTERNAL_PATH_PREFIXES tuple (currently /opt/) that drops whole file blocks before per-block filtering. The cpputest-clang IWYU lane is unaffected: it never sees /opt/* paths because that image doesn't carry the upstream sources. --- scripts/iwyu_filter.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/scripts/iwyu_filter.py b/scripts/iwyu_filter.py index 60e26c49..e453a9f7 100755 --- a/scripts/iwyu_filter.py +++ b/scripts/iwyu_filter.py @@ -35,6 +35,14 @@ so it asks for forward declarations no test file ever writes. Same shape of false positive as (3) — IWYU not modelling CppUTest macros. +WHOLE-FILE blocks we drop because the file is third-party: + +5. Anything under /opt/... — upstream sources mounted into the container + image (mbedTLS, lwIP, FreeRTOS-Kernel / Plus-TCP, FatFs). These appear + in compile_commands.json because the freertos-aware analyze-iwyu lane + compiles INTERFACE-library TUs from those trees. We don't own the + includes there; suggestions on those files are noise. + Reads IWYU output (typically the stdout of iwyu_tool.py) on stdin, emits filtered output on stdout. File blocks whose only findings are filtered findings are suppressed entirely, so the report shows only actionable @@ -58,6 +66,21 @@ re.compile(r"^class TEST_\w+_Test;\s*$"), ) +# Whole file paths to drop entirely — third-party trees mounted into the +# container image and not part of this project. See module docstring (5). +EXTERNAL_PATH_PREFIXES = ( + "/opt/", +) + +_FILE_HEADER = re.compile(r"^(\S+) should add these lines:") + + +def _is_external_path(header_line): + match = _FILE_HEADER.match(header_line) + if not match: + return False + return match.group(1).startswith(EXTERNAL_PATH_PREFIXES) + def _is_filtered_removal(line): return any(pat.match(line) for pat in FILTERED_REMOVALS) @@ -87,7 +110,10 @@ def filter_iwyu(stream_in, stream_out): block_end += 1 block = lines[i:block_end] - kept_block, has_finding = _filter_block(block) + if _is_external_path(block[0]): + kept_block, has_finding = [], False + else: + kept_block, has_finding = _filter_block(block) if has_finding: files_with_findings += 1 stream_out.writelines(kept_block) From cc5686b77630f9f1d1bea61ee9d7a2959233db0d Mon Sep 17 00:00:00 2001 From: David Cozens Date: Tue, 26 May 2026 17:49:05 +0100 Subject: [PATCH 04/12] chore: S24.11 silence clang-tidy on CmsdkUartFake register macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses the 11 findings analyze-tidy-freertos-plustcp surfaced on Tests/FreeRtos/CmsdkUartFake.c. Two distinct rule categories, two distinct treatments per discussion with David: * 10 × cppcoreguidelines-macro-to-enum / modernize-macro-to-enum on the CMSDK UART register-offset and status-bit defines — wrapped in NOLINTBEGIN/END with a rationale comment. The #define form mirrors the ARM datasheet so a future reader can grep `DATA_OFFSET` against vendor docs. Kept the rule active at tier level so real test code that should use enums still gets caught. * 1 × bugprone-easily-swappable-parameters on Fake_Write32 — disabled tier-wide in Tests/.clang-tidy. The rule fires routinely on legitimate patterns in test helpers (uintptr_t address + uint32_t value, pairs of comparison values) where the types carry semantic distinction the rule can't see. Real argument-order bugs in tests would be caught by the tests themselves. --- Tests/.clang-tidy | 31 +++++++++++++++++++++---------- Tests/FreeRtos/CmsdkUartFake.c | 8 ++++++++ 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/Tests/.clang-tidy b/Tests/.clang-tidy index 1137ec01..009671dc 100644 --- a/Tests/.clang-tidy +++ b/Tests/.clang-tidy @@ -1,15 +1,26 @@ --- # Consistency-only tier per docs/NAMING.md. # -# Inherits the root .clang-tidy and disables readability-identifier-naming -# outright — CppUTest's TEST_GROUP / TEST / CHECK_* macros expand to -# external-linkage identifiers (TEST_GroupName_TestName_TestShell etc.) -# that routinely exceed any reasonable identifier window, and test code -# uses a documented set of relaxations (lowerCamelCase preferred but not -# enforced; PascalCase helpers; SolidSyslog prefix dropped on fakes / -# spies; SCREAMING_SNAKE on test-only macros without a project prefix). -# -# The other safety checks (bugprone-*, clang-analyzer-*, cppcoreguidelines-*, +# Inherits the root .clang-tidy and disables a documented set of rules. +# Other safety checks (bugprone-*, clang-analyzer-*, cppcoreguidelines-*, # etc.) continue to apply — they catch real bugs in test code too. +# +# -readability-identifier-naming +# CppUTest's TEST_GROUP / TEST / CHECK_* macros expand to external-linkage +# identifiers (TEST_GroupName_TestName_TestShell etc.) that routinely +# exceed any reasonable identifier window, and test code uses a documented +# set of relaxations (lowerCamelCase preferred but not enforced; +# PascalCase helpers; SolidSyslog prefix dropped on fakes / spies; +# SCREAMING_SNAKE on test-only macros without a project prefix). +# +# -bugprone-easily-swappable-parameters +# Test helpers and hardware fakes routinely take adjacent parameters of +# convertible types — `uintptr_t address + uint32_t value` in MMIO writes, +# pairs of similarly-typed values in equality / range assertions — where +# the types carry semantic distinction the rule can't see. Disabled tier- +# wide because the rule fires too often for legitimate patterns; real +# argument-order bugs in tests get caught by the tests themselves. InheritParentConfig: true -Checks: '-readability-identifier-naming' +Checks: > + -readability-identifier-naming, + -bugprone-easily-swappable-parameters diff --git a/Tests/FreeRtos/CmsdkUartFake.c b/Tests/FreeRtos/CmsdkUartFake.c index 952501b3..64b56c89 100644 --- a/Tests/FreeRtos/CmsdkUartFake.c +++ b/Tests/FreeRtos/CmsdkUartFake.c @@ -2,6 +2,12 @@ #include +// The CMSDK UART register-offset and status-bit macros below mirror the +// ARM CMSDK UART hardware datasheet exactly. The macro form is intentional — +// a future reader can grep for `DATA_OFFSET` against vendor docs and land on +// these definitions verbatim. Converting them to enums would obscure that +// mapping, so we keep them as #defines. +// NOLINTBEGIN(cppcoreguidelines-macro-to-enum,modernize-macro-to-enum) #define DATA_OFFSET 0x000U #define STATE_OFFSET 0x004U #define CTRL_OFFSET 0x008U @@ -12,6 +18,8 @@ #define RX_FULL_BIT 0x02U #define TX_OVRE_BIT 0x04U +// NOLINTEND(cppcoreguidelines-macro-to-enum,modernize-macro-to-enum) + static struct { uintptr_t base; From 6de160b52454f16251b5dbc0b325f5976c53830e Mon Sep 17 00:00:00 2001 From: David Cozens Date: Tue, 26 May 2026 18:01:23 +0100 Subject: [PATCH 05/12] chore: S24.11 keep FreeRTOS.h / FreeRTOS_IP.h umbrellas in IWYU output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extends scripts/iwyu_filter.py with three new filter rules for FreeRTOS ecosystem conventions that IWYU doesn't model: * Never remove FreeRTOS.h or FreeRTOS_IP.h. Both are umbrella headers that set up the build environment (configMACRO_X from FreeRTOSConfig.h) before any sub-header is parsed. IWYU sees specific types in task.h / portmacro.h / FreeRTOS_DNS_Globals.h and decides the umbrella is redundant, but removing it breaks the upstream convention. * Never add FreeRTOSConfig.h. Direct inclusion from Platform/FreeRtos/ is a LayerGuard violation — the config is reached transitively via FreeRTOS.h. The freertos-aware analyze-iwyu lane surfaced this as the immediate post-apply LayerGuard failure on Platform/FreeRtos/Source/SolidSyslogFreeRtosSysUpTime.c. * Never add portmacro.h or FreeRTOS_DNS_Globals.h. Both are private to the FreeRTOS-Plus-TCP implementation; consumers reach BaseType_t and freertos_addrinfo via the umbrellas. Without these rules, iwyu-apply against the freertos image either breaks LayerGuard (FreeRTOSConfig.h add) or produces files that fail to parse (FreeRTOS_*.h sub-headers ahead of FreeRTOS.h). With them, the freertos-aware analyze-iwyu lane settles cleanly. --- scripts/iwyu_filter.py | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/scripts/iwyu_filter.py b/scripts/iwyu_filter.py index e453a9f7..be990eea 100755 --- a/scripts/iwyu_filter.py +++ b/scripts/iwyu_filter.py @@ -35,9 +35,39 @@ so it asks for forward declarations no test file ever writes. Same shape of false positive as (3) — IWYU not modelling CppUTest macros. +REMOVALS that we keep as-is (FreeRTOS umbrella convention): + +5. '- #include "FreeRTOS.h"' and '- #include "FreeRTOS_IP.h"'. + The FreeRTOS-Kernel and FreeRTOS-Plus-TCP APIs both follow an + umbrella-header convention: FreeRTOS.h must be included first because + it sets up the build environment from FreeRTOSConfig.h before any + other FreeRTOS header is parsed. IWYU sees specific types in + task.h / portmacro.h / FreeRTOS_DNS_Globals.h and decides the + umbrella isn't directly used, but removing it breaks the upstream + convention. The same applies to FreeRTOS_IP.h for the Plus-TCP stack. + +ADDITIONS that IWYU asks for but we never write by hand (cont.): + +6. '#include "FreeRTOSConfig.h"' — direct config include. + Platform/FreeRtos/ source files must not include FreeRTOSConfig.h + directly — that's a LayerGuard violation (Platform reaching into a + consumer-supplied / Tests-tier file). The config is reached + transitively through FreeRTOS.h, which is the contract. + +7. '#include "portmacro.h"' — private FreeRTOS header. + portmacro.h is a port-specific internal header that must come in via + the FreeRTOS.h umbrella, not be included directly. Same as (6) on + layering grounds. + +7a. '#include "FreeRTOS_DNS_Globals.h"' — internal FreeRTOS-Plus-TCP header. + The `_Globals` suffix marks this as the implementation's internal + shared declarations; consumers reach `freertos_addrinfo` via + FreeRTOS_DNS.h, which the FreeRTOS-Plus-TCP API documents as the + entry point. IWYU doesn't model that public/private split. + WHOLE-FILE blocks we drop because the file is third-party: -5. Anything under /opt/... — upstream sources mounted into the container +8. Anything under /opt/... — upstream sources mounted into the container image (mbedTLS, lwIP, FreeRTOS-Kernel / Plus-TCP, FatFs). These appear in compile_commands.json because the freertos-aware analyze-iwyu lane compiles INTERFACE-library TUs from those trees. We don't own the @@ -60,10 +90,15 @@ re.compile(r"^- struct \w+;\s*//"), re.compile(r"^- #include \s*//"), re.compile(r"^- #include \"CppUTest/TestHarness\.h\"\s*//"), + re.compile(r"^- #include \"FreeRTOS\.h\"\s*//"), + re.compile(r"^- #include \"FreeRTOS_IP\.h\"\s*//"), ) FILTERED_ADDITIONS = ( re.compile(r"^class TEST_\w+_Test;\s*$"), + re.compile(r"^#include \"FreeRTOSConfig\.h\"\s*//"), + re.compile(r"^#include \"portmacro\.h\"\s*//"), + re.compile(r"^#include \"FreeRTOS_DNS_Globals\.h\"\s*//"), ) # Whole file paths to drop entirely — third-party trees mounted into the From b3d15d64ec77b02af58b69c7646d19e844e77dae Mon Sep 17 00:00:00 2001 From: David Cozens Date: Tue, 26 May 2026 18:01:38 +0100 Subject: [PATCH 06/12] chore: S24.11 apply IWYU suggestions to FreeRTOS / mbedTLS / lwIP trees Drives include hygiene to clean in the previously-unanalysed trees that analyze-iwyu-freertos-plustcp now sees. Generated via the iwyu-apply target running with the FreeRTOS-umbrella filter rules in the previous commit, then verified via: cmake --preset debug -B build/iwyu-verify cmake --build build/iwyu-verify --target junit BddTargetTests inside cpputest-freertos:sha-a0c1c0a. All tests pass; analyze-iwyu lane settles cleanly. Touches 31 files across Platform/{FreeRtos,PlusTcp,LwipRaw}/Source/, Tests/{FreeRtos,Lwip,MbedTls,MbedTlsIntegration}/, and Tests/Support/ fakes. The dominant pattern is adding stdint / stdbool / standard-C headers that were transitively pulled in before, plus moving "#include "X.h"" up to the natural sort position relative to system headers. No source-level behaviour change: the binary set produced is bit-for- bit identical except for line-number debug info shifts. --- .../Source/SolidSyslogLwipRawResolver.c | 5 +- .../Source/SolidSyslogPlusTcpAddress.c | 5 +- .../Source/SolidSyslogPlusTcpDatagram.c | 4 +- .../Source/SolidSyslogPlusTcpTcpStream.c | 4 +- Tests/FreeRtos/CmsdkUartFake.c | 2 - Tests/FreeRtos/CmsdkUartTest.cpp | 17 ++++++ .../FreeRtos/SolidSyslogFreeRtosMutexTest.cpp | 15 +++++- .../SolidSyslogFreeRtosSysUpTimeTest.cpp | 8 ++- .../SolidSyslogPlusTcpAddressTest.cpp | 14 +++-- .../SolidSyslogPlusTcpDatagramTest.cpp | 35 +++++++++++- .../SolidSyslogPlusTcpTcpStreamTest.cpp | 54 ++++++++++++++++++- Tests/Lwip/SolidSyslogLwipRawAddressTest.cpp | 14 ++++- Tests/Lwip/SolidSyslogLwipRawResolverTest.cpp | 18 ++++++- .../MbedTls/SolidSyslogMbedTlsStreamTest.cpp | 53 +++++++++++++++++- Tests/MbedTlsIntegration/MbedTlsTestCert.c | 2 +- Tests/MbedTlsIntegration/MbedTlsTestServer.c | 2 + Tests/MbedTlsIntegration/SocketStream.c | 1 + ...olidSyslogMbedTlsStreamIntegrationTest.cpp | 9 +++- .../Interface/FreeRTOSIPConfig.h | 1 + .../FreeRtosFakes/Interface/FreeRtosDnsFake.h | 4 +- .../Interface/FreeRtosSemaphoreFake.h | 2 - .../Interface/FreeRtosSocketsFake.h | 5 +- .../Interface/FreeRtosTaskFake.h | 2 - .../FreeRtosFakes/Interface/portmacro.h | 1 - .../FreeRtosFakes/Source/FreeRtosArpFake.c | 1 + .../FreeRtosFakes/Source/FreeRtosDnsFake.c | 4 +- .../Source/FreeRtosSemaphoreFake.c | 6 +++ .../Source/FreeRtosSocketsFake.c | 2 + .../FreeRtosFakes/Source/FreeRtosTaskFake.c | 2 + Tests/Support/LwipFakes/Interface/arch/cc.h | 3 -- Tests/Support/MbedTlsFake.c | 4 ++ 31 files changed, 258 insertions(+), 41 deletions(-) diff --git a/Platform/LwipRaw/Source/SolidSyslogLwipRawResolver.c b/Platform/LwipRaw/Source/SolidSyslogLwipRawResolver.c index 041fa740..15aae81e 100644 --- a/Platform/LwipRaw/Source/SolidSyslogLwipRawResolver.c +++ b/Platform/LwipRaw/Source/SolidSyslogLwipRawResolver.c @@ -1,13 +1,12 @@ -#include "SolidSyslogLwipRawResolverPrivate.h" - #include #include +#include "SolidSyslogLwipRawResolverPrivate.h" #include "lwip/ip_addr.h" - #include "SolidSyslogLwipRawAddressPrivate.h" #include "SolidSyslogNullResolver.h" #include "SolidSyslogResolverDefinition.h" +#include "SolidSyslogTransport.h" struct SolidSyslogAddress; diff --git a/Platform/PlusTcp/Source/SolidSyslogPlusTcpAddress.c b/Platform/PlusTcp/Source/SolidSyslogPlusTcpAddress.c index 2acc23c4..9fdaf6a9 100644 --- a/Platform/PlusTcp/Source/SolidSyslogPlusTcpAddress.c +++ b/Platform/PlusTcp/Source/SolidSyslogPlusTcpAddress.c @@ -1,7 +1,8 @@ -#include "SolidSyslogPlusTcpAddressPrivate.h" - #include +#include "SolidSyslogPlusTcpAddressPrivate.h" +#include "FreeRTOS_Sockets.h" + struct SolidSyslogAddress; void PlusTcpAddress_Initialise(struct SolidSyslogAddress* base) diff --git a/Platform/PlusTcp/Source/SolidSyslogPlusTcpDatagram.c b/Platform/PlusTcp/Source/SolidSyslogPlusTcpDatagram.c index 3c4ab1fb..cbea5812 100644 --- a/Platform/PlusTcp/Source/SolidSyslogPlusTcpDatagram.c +++ b/Platform/PlusTcp/Source/SolidSyslogPlusTcpDatagram.c @@ -11,12 +11,14 @@ #include "FreeRTOS_ARP.h" #include "FreeRTOS_Sockets.h" #include "task.h" - #include "SolidSyslogDatagramDefinition.h" #include "SolidSyslogPlusTcpAddressPrivate.h" #include "SolidSyslogPlusTcpDatagramPrivate.h" #include "SolidSyslogNullDatagram.h" #include "SolidSyslogUdpPayload.h" +#include "SolidSyslogDatagram.h" + +struct SolidSyslogAddress; static bool PlusTcpDatagram_Open(struct SolidSyslogDatagram* base); static enum SolidSyslogDatagramSendResult PlusTcpDatagram_SendTo( diff --git a/Platform/PlusTcp/Source/SolidSyslogPlusTcpTcpStream.c b/Platform/PlusTcp/Source/SolidSyslogPlusTcpTcpStream.c index 40f03880..88369d6e 100644 --- a/Platform/PlusTcp/Source/SolidSyslogPlusTcpTcpStream.c +++ b/Platform/PlusTcp/Source/SolidSyslogPlusTcpTcpStream.c @@ -12,14 +12,14 @@ #include "FreeRTOS_IP.h" #include "FreeRTOS_Sockets.h" #include "task.h" - #include "SolidSyslogPlusTcpAddressPrivate.h" #include "SolidSyslogPlusTcpTcpStreamPrivate.h" #include "SolidSyslogNullStream.h" #include "SolidSyslogStream.h" -#include "SolidSyslogStreamDefinition.h" #include "SolidSyslogTunables.h" +struct SolidSyslogAddress; + /* SolidSyslogStream_Read returns < 0 to signal EOF/error (socket closed * internally); -1 is the in-tree convention shared with Posix/Winsock. */ static const SolidSyslogSsize READ_FAILED = -1; diff --git a/Tests/FreeRtos/CmsdkUartFake.c b/Tests/FreeRtos/CmsdkUartFake.c index 64b56c89..0de06731 100644 --- a/Tests/FreeRtos/CmsdkUartFake.c +++ b/Tests/FreeRtos/CmsdkUartFake.c @@ -1,7 +1,5 @@ #include "CmsdkUartFake.h" -#include - // The CMSDK UART register-offset and status-bit macros below mirror the // ARM CMSDK UART hardware datasheet exactly. The macro form is intentional — // a future reader can grep for `DATA_OFFSET` against vendor docs and land on diff --git a/Tests/FreeRtos/CmsdkUartTest.cpp b/Tests/FreeRtos/CmsdkUartTest.cpp index 3e561ab2..d4bb30d8 100644 --- a/Tests/FreeRtos/CmsdkUartTest.cpp +++ b/Tests/FreeRtos/CmsdkUartTest.cpp @@ -1,3 +1,5 @@ +#include + #include "TestUtils.h" #include "CppUTest/TestHarness.h" @@ -21,33 +23,39 @@ TEST_GROUP(CmsdkUart) // clang-format on TEST(CmsdkUart, InitWritesBaudDivisor) + { LONGS_EQUAL(16, CmsdkUartFake_GetBaudDiv()); } TEST(CmsdkUart, InitEnablesTransmitter) + { LONGS_EQUAL(0x01, CmsdkUartFake_GetCtrl() & 0x01); } TEST(CmsdkUart, InitEnablesReceiver) + { LONGS_EQUAL(0x02, CmsdkUartFake_GetCtrl() & 0x02); } TEST(CmsdkUart, PutCharWritesByteToDataRegister) + { CmsdkUart_PutChar('A'); LONGS_EQUAL('A', CmsdkUartFake_GetData()); } TEST(CmsdkUart, PutCharWritesTheGivenByte) + { CmsdkUart_PutChar('B'); LONGS_EQUAL('B', CmsdkUartFake_GetData()); } TEST(CmsdkUart, PutCharSpinsForTxFullToClearBeforeWritingNextByte) + { CmsdkUart_PutChar('A'); CmsdkUart_PutChar('B'); @@ -56,6 +64,7 @@ TEST(CmsdkUart, PutCharSpinsForTxFullToClearBeforeWritingNextByte) } TEST(CmsdkUart, PutCharCallsSleepWhileSpinningForTxFull) + { CmsdkUart_PutChar('A'); CmsdkUart_PutChar('B'); @@ -63,6 +72,7 @@ TEST(CmsdkUart, PutCharCallsSleepWhileSpinningForTxFull) } TEST(CmsdkUart, PutCharWritesImmediatelyWhenTransmitterIsAlwaysReady) + { CmsdkUartFake_SetReadsBeforeTxReady(0); CmsdkUart_PutChar('X'); @@ -72,12 +82,14 @@ TEST(CmsdkUart, PutCharWritesImmediatelyWhenTransmitterIsAlwaysReady) } TEST(CmsdkUart, WriteOfSingleByteEmitsThatByte) + { CmsdkUart_Write("X", 1); LONGS_EQUAL('X', CmsdkUartFake_GetData()); } TEST(CmsdkUart, WriteOfMultipleBytesEmitsAllByteWithoutOverrun) + { CmsdkUart_Write("AB", 2); LONGS_EQUAL('B', CmsdkUartFake_GetData()); @@ -85,12 +97,14 @@ TEST(CmsdkUart, WriteOfMultipleBytesEmitsAllByteWithoutOverrun) } TEST(CmsdkUart, GetCharReturnsByteFromDataRegister) + { CmsdkUartFake_SetReceivedByte('Q'); LONGS_EQUAL('Q', CmsdkUart_GetChar()); } TEST(CmsdkUart, GetCharSpinsForRxFullToBecomeSetBeforeReadingDataRegister) + { CmsdkUartFake_SetReadsBeforeRxReady(2); CmsdkUartFake_SetReceivedByte('Z'); @@ -98,6 +112,7 @@ TEST(CmsdkUart, GetCharSpinsForRxFullToBecomeSetBeforeReadingDataRegister) } TEST(CmsdkUart, GetCharCallsSleepWhileSpinningForRxFull) + { CmsdkUartFake_SetReadsBeforeRxReady(2); CmsdkUartFake_SetReceivedByte('Z'); @@ -106,6 +121,7 @@ TEST(CmsdkUart, GetCharCallsSleepWhileSpinningForRxFull) } TEST(CmsdkUart, GetCharReturnsImmediatelyWhenReceiverHasByte) + { CmsdkUartFake_SetReadsBeforeRxReady(0); CmsdkUartFake_SetReceivedByte('X'); @@ -114,6 +130,7 @@ TEST(CmsdkUart, GetCharReturnsImmediatelyWhenReceiverHasByte) } TEST(CmsdkUart, GetCharSpinsAfterReArmFromImmediateReadyToDelayedReady) + { CmsdkUartFake_SetReadsBeforeRxReady(0); CmsdkUartFake_SetReceivedByte('A'); diff --git a/Tests/FreeRtos/SolidSyslogFreeRtosMutexTest.cpp b/Tests/FreeRtos/SolidSyslogFreeRtosMutexTest.cpp index 7a321760..85594fe4 100644 --- a/Tests/FreeRtos/SolidSyslogFreeRtosMutexTest.cpp +++ b/Tests/FreeRtos/SolidSyslogFreeRtosMutexTest.cpp @@ -13,9 +13,7 @@ using namespace CososoTesting; // NOLINT(google-build-using-namespace) -- test-f #include "SolidSyslogMutexDefinition.h" #include "SolidSyslogPrival.h" #include "SolidSyslogTunables.h" - #include "FreeRTOS.h" -#include "semphr.h" // NOLINTBEGIN(cppcoreguidelines-macro-usage,cppcoreguidelines-avoid-do-while) -- macros preserve __FILE__/__LINE__ at the call site @@ -54,11 +52,13 @@ TEST_GROUP(SolidSyslogFreeRtosMutex) // clang-format on TEST(SolidSyslogFreeRtosMutex, CreateCallsCreateMutexStaticOnce) + { CALLED_FAKE(FreeRtosSemaphoreFake_CreateMutexStatic, ONCE); } TEST(SolidSyslogFreeRtosMutex, LockCallsSemaphoreTakeOnce) + { SolidSyslogMutex_Lock(mutex); @@ -66,6 +66,7 @@ TEST(SolidSyslogFreeRtosMutex, LockCallsSemaphoreTakeOnce) } TEST(SolidSyslogFreeRtosMutex, UnlockCallsSemaphoreGiveOnce) + { SolidSyslogMutex_Unlock(mutex); @@ -73,6 +74,7 @@ TEST(SolidSyslogFreeRtosMutex, UnlockCallsSemaphoreGiveOnce) } TEST(SolidSyslogFreeRtosMutex, DestroyCallsSemaphoreDeleteOnce) + { SolidSyslogFreeRtosMutex_Destroy(mutex); mutex = nullptr; @@ -121,6 +123,7 @@ TEST_GROUP(SolidSyslogFreeRtosMutexPool) // clang-format on TEST(SolidSyslogFreeRtosMutexPool, FillingPoolThenOverflowReturnsDistinctFallback) + { FillPool(); @@ -130,6 +133,7 @@ TEST(SolidSyslogFreeRtosMutexPool, FillingPoolThenOverflowReturnsDistinctFallbac } TEST(SolidSyslogFreeRtosMutexPool, ExhaustedCreateReportsError) + { ErrorHandlerFake_Install(nullptr); FillPool(); @@ -143,6 +147,7 @@ TEST(SolidSyslogFreeRtosMutexPool, ExhaustedCreateReportsError) } TEST(SolidSyslogFreeRtosMutexPool, FallbackLockUnlockAreNoOps) + { FillPool(); FreeRtosSemaphoreFake_Reset(); @@ -156,6 +161,7 @@ TEST(SolidSyslogFreeRtosMutexPool, FallbackLockUnlockAreNoOps) } TEST(SolidSyslogFreeRtosMutexPool, CreateAcquiresAndReleasesConfigLockOnFirstFreeSlot) + { ConfigLockFake_Install(); @@ -166,6 +172,7 @@ TEST(SolidSyslogFreeRtosMutexPool, CreateAcquiresAndReleasesConfigLockOnFirstFre } TEST(SolidSyslogFreeRtosMutexPool, CreateLocksOncePerSlotProbedWhenPoolIsFull) + { FillPool(); ConfigLockFake_Install(); @@ -177,6 +184,7 @@ TEST(SolidSyslogFreeRtosMutexPool, CreateLocksOncePerSlotProbedWhenPoolIsFull) } TEST(SolidSyslogFreeRtosMutexPool, DestroyOfPooledHandleLocksOnce) + { pooled[0] = SolidSyslogFreeRtosMutex_Create(); ConfigLockFake_Install(); @@ -189,6 +197,7 @@ TEST(SolidSyslogFreeRtosMutexPool, DestroyOfPooledHandleLocksOnce) } TEST(SolidSyslogFreeRtosMutexPool, DestroyOfUnknownHandleDoesNotLock) + { ConfigLockFake_Install(); struct SolidSyslogMutex stranger = {}; @@ -200,6 +209,7 @@ TEST(SolidSyslogFreeRtosMutexPool, DestroyOfUnknownHandleDoesNotLock) } TEST(SolidSyslogFreeRtosMutexPool, DestroyOfUnknownHandleReportsWarning) + { ErrorHandlerFake_Install(nullptr); struct SolidSyslogMutex stranger = {}; @@ -213,6 +223,7 @@ TEST(SolidSyslogFreeRtosMutexPool, DestroyOfUnknownHandleReportsWarning) } TEST(SolidSyslogFreeRtosMutexPool, DestroyOfStaleHandleReportsWarning) + { pooled[0] = SolidSyslogFreeRtosMutex_Create(); SolidSyslogFreeRtosMutex_Destroy(pooled[0]); diff --git a/Tests/FreeRtos/SolidSyslogFreeRtosSysUpTimeTest.cpp b/Tests/FreeRtos/SolidSyslogFreeRtosSysUpTimeTest.cpp index a6a7239b..9f206fe7 100644 --- a/Tests/FreeRtos/SolidSyslogFreeRtosSysUpTimeTest.cpp +++ b/Tests/FreeRtos/SolidSyslogFreeRtosSysUpTimeTest.cpp @@ -1,8 +1,8 @@ -#include "CppUTest/TestHarness.h" +#include +#include "CppUTest/TestHarness.h" #include "FreeRtosTaskFake.h" #include "SolidSyslogFreeRtosSysUpTime.h" - #include "FreeRTOS.h" // clang-format off @@ -17,6 +17,7 @@ TEST_GROUP(SolidSyslogFreeRtosSysUpTime) // clang-format on TEST(SolidSyslogFreeRtosSysUpTime, ReturnsZeroWhenTicksAreZero) + { FreeRtosTaskFake_SetTickCount(0); @@ -24,6 +25,7 @@ TEST(SolidSyslogFreeRtosSysUpTime, ReturnsZeroWhenTicksAreZero) } TEST(SolidSyslogFreeRtosSysUpTime, ReturnsOneWhenTicksAreOne) + { FreeRtosTaskFake_SetTickCount(1); @@ -31,6 +33,7 @@ TEST(SolidSyslogFreeRtosSysUpTime, ReturnsOneWhenTicksAreOne) } TEST(SolidSyslogFreeRtosSysUpTime, ReturnsTickCountAtMidRange) + { FreeRtosTaskFake_SetTickCount(12345U); @@ -38,6 +41,7 @@ TEST(SolidSyslogFreeRtosSysUpTime, ReturnsTickCountAtMidRange) } TEST(SolidSyslogFreeRtosSysUpTime, ReturnsUint32MaxWhenTicksAreUint32Max) + { FreeRtosTaskFake_SetTickCount(UINT32_MAX); diff --git a/Tests/FreeRtos/SolidSyslogPlusTcpAddressTest.cpp b/Tests/FreeRtos/SolidSyslogPlusTcpAddressTest.cpp index a4504f5a..e6949957 100644 --- a/Tests/FreeRtos/SolidSyslogPlusTcpAddressTest.cpp +++ b/Tests/FreeRtos/SolidSyslogPlusTcpAddressTest.cpp @@ -4,8 +4,6 @@ using namespace CososoTesting; // NOLINT(google-build-using-namespace) -- test-file scope only; brings NEVER/ONCE/TWICE/THRICE into scope for the CALLED_* // macros -#include - #include "ConfigLockFake.h" #include "ErrorHandlerFake.h" #include "SolidSyslogPlusTcpAddress.h" @@ -13,7 +11,6 @@ using namespace CososoTesting; // NOLINT(google-build-using-namespace) -- test-f #include "SolidSyslogPlusTcpAddressPrivate.h" #include "SolidSyslogPrival.h" #include "SolidSyslogTunables.h" - #include "FreeRTOS.h" #include "FreeRTOS_IP.h" #include "FreeRTOS_Sockets.h" @@ -53,11 +50,13 @@ TEST_GROUP(SolidSyslogPlusTcpAddress) // clang-format on TEST(SolidSyslogPlusTcpAddress, CreateReturnsNonNull) + { CHECK(address != nullptr); } TEST(SolidSyslogPlusTcpAddress, AsFreertosSockaddrRoundTripsBytes) + { struct freertos_sockaddr expected = {}; expected.sin_family = FREERTOS_AF_INET; @@ -71,6 +70,7 @@ TEST(SolidSyslogPlusTcpAddress, AsFreertosSockaddrRoundTripsBytes) } TEST(SolidSyslogPlusTcpAddress, CreateZeroesTheSockaddrFromAnyPriorSlotContents) + { struct freertos_sockaddr dirty = {}; dirty.sin_family = FREERTOS_AF_INET; @@ -121,6 +121,7 @@ TEST_GROUP(SolidSyslogPlusTcpAddressPool) // clang-format on TEST(SolidSyslogPlusTcpAddressPool, FillingPoolThenOverflowReturnsDistinctFallback) + { FillPool(); @@ -130,6 +131,7 @@ TEST(SolidSyslogPlusTcpAddressPool, FillingPoolThenOverflowReturnsDistinctFallba } TEST(SolidSyslogPlusTcpAddressPool, ExhaustedCreateReportsError) + { ErrorHandlerFake_Install(nullptr); FillPool(); @@ -143,6 +145,7 @@ TEST(SolidSyslogPlusTcpAddressPool, ExhaustedCreateReportsError) } TEST(SolidSyslogPlusTcpAddressPool, CreateAcquiresAndReleasesConfigLockOnFirstFreeSlot) + { ConfigLockFake_Install(); @@ -153,6 +156,7 @@ TEST(SolidSyslogPlusTcpAddressPool, CreateAcquiresAndReleasesConfigLockOnFirstFr } TEST(SolidSyslogPlusTcpAddressPool, CreateLocksOncePerSlotProbedWhenPoolIsFull) + { FillPool(); ConfigLockFake_Install(); @@ -164,6 +168,7 @@ TEST(SolidSyslogPlusTcpAddressPool, CreateLocksOncePerSlotProbedWhenPoolIsFull) } TEST(SolidSyslogPlusTcpAddressPool, DestroyOfPooledHandleLocksOnce) + { pooled[0] = SolidSyslogPlusTcpAddress_Create(); ConfigLockFake_Install(); @@ -176,6 +181,7 @@ TEST(SolidSyslogPlusTcpAddressPool, DestroyOfPooledHandleLocksOnce) } TEST(SolidSyslogPlusTcpAddressPool, DestroyOfUnknownHandleDoesNotLock) + { ConfigLockFake_Install(); char stranger = 0; @@ -187,6 +193,7 @@ TEST(SolidSyslogPlusTcpAddressPool, DestroyOfUnknownHandleDoesNotLock) } TEST(SolidSyslogPlusTcpAddressPool, DestroyOfUnknownHandleReportsWarning) + { ErrorHandlerFake_Install(nullptr); char stranger = 0; @@ -200,6 +207,7 @@ TEST(SolidSyslogPlusTcpAddressPool, DestroyOfUnknownHandleReportsWarning) } TEST(SolidSyslogPlusTcpAddressPool, DestroyOfStaleHandleReportsWarning) + { pooled[0] = SolidSyslogPlusTcpAddress_Create(); SolidSyslogPlusTcpAddress_Destroy(pooled[0]); diff --git a/Tests/FreeRtos/SolidSyslogPlusTcpDatagramTest.cpp b/Tests/FreeRtos/SolidSyslogPlusTcpDatagramTest.cpp index 8f3d91c6..ad7e1245 100644 --- a/Tests/FreeRtos/SolidSyslogPlusTcpDatagramTest.cpp +++ b/Tests/FreeRtos/SolidSyslogPlusTcpDatagramTest.cpp @@ -1,3 +1,6 @@ +#include +#include + #include "TestUtils.h" #include "CppUTest/TestHarness.h" @@ -15,11 +18,9 @@ using namespace CososoTesting; // NOLINT(google-build-using-namespace) -- test-f #include "SolidSyslogPrival.h" #include "SolidSyslogTunables.h" #include "SolidSyslogUdpPayload.h" - #include "FreeRtosArpFake.h" #include "FreeRtosSocketsFake.h" #include "FreeRtosTaskFake.h" - #include "FreeRTOS.h" #include "FreeRTOS_IP.h" #include "FreeRTOS_Sockets.h" @@ -74,11 +75,13 @@ TEST_GROUP(SolidSyslogPlusTcpDatagram) }; TEST(SolidSyslogPlusTcpDatagram, CreateReturnsNonNullDatagram) + { CHECK(datagram != nullptr); } TEST(SolidSyslogPlusTcpDatagram, OpenCreatesUdpSocket) + { SolidSyslogDatagram_Open(datagram); CALLED_FAKE(FreeRtosSocketsFake_Socket, ONCE); @@ -88,17 +91,20 @@ TEST(SolidSyslogPlusTcpDatagram, OpenCreatesUdpSocket) } TEST(SolidSyslogPlusTcpDatagram, OpenReturnsTrueOnSuccess) + { CHECK_TRUE(SolidSyslogDatagram_Open(datagram)); } TEST(SolidSyslogPlusTcpDatagram, OpenReturnsFalseWhenSocketFails) + { FreeRtosSocketsFake_SetSocketFails(true); CHECK_FALSE(SolidSyslogDatagram_Open(datagram)); } TEST(SolidSyslogPlusTcpDatagram, OpenIsIdempotent) + { SolidSyslogDatagram_Open(datagram); CHECK_TRUE(SolidSyslogDatagram_Open(datagram)); @@ -106,11 +112,13 @@ TEST(SolidSyslogPlusTcpDatagram, OpenIsIdempotent) } TEST(SolidSyslogPlusTcpDatagram, MaxPayloadReturnsIpv6SafeDefault) + { LONGS_EQUAL(SOLIDSYSLOG_UDP_IPV6_SAFE_PAYLOAD, SolidSyslogDatagram_MaxPayload(datagram)); } TEST(SolidSyslogPlusTcpDatagram, SendToFailsBeforeOpen) + { enum SolidSyslogDatagramSendResult result = SolidSyslogDatagram_SendTo(datagram, "x", 1, addr); LONGS_EQUAL(SOLIDSYSLOG_DATAGRAM_SEND_RESULT_FAILED, result); @@ -118,6 +126,7 @@ TEST(SolidSyslogPlusTcpDatagram, SendToFailsBeforeOpen) } TEST(SolidSyslogPlusTcpDatagram, SendToFailsWhenSendtoErrors) + { SolidSyslogDatagram_Open(datagram); FreeRtosSocketsFake_SetSendtoFails(true); @@ -125,6 +134,7 @@ TEST(SolidSyslogPlusTcpDatagram, SendToFailsWhenSendtoErrors) } TEST(SolidSyslogPlusTcpDatagram, CloseClosesSocket) + { SolidSyslogDatagram_Open(datagram); SolidSyslogDatagram_Close(datagram); @@ -133,6 +143,7 @@ TEST(SolidSyslogPlusTcpDatagram, CloseClosesSocket) } TEST(SolidSyslogPlusTcpDatagram, SendToFailsAfterClose) + { SolidSyslogDatagram_Open(datagram); SolidSyslogDatagram_Close(datagram); @@ -142,6 +153,7 @@ TEST(SolidSyslogPlusTcpDatagram, SendToFailsAfterClose) } TEST(SolidSyslogPlusTcpDatagram, DestroyClosesOpenSocket) + { SolidSyslogDatagram_Open(datagram); SolidSyslogPlusTcpDatagram_Destroy(datagram); @@ -150,6 +162,7 @@ TEST(SolidSyslogPlusTcpDatagram, DestroyClosesOpenSocket) } TEST(SolidSyslogPlusTcpDatagram, CloseIsIdempotent) + { SolidSyslogDatagram_Open(datagram); SolidSyslogDatagram_Close(datagram); @@ -158,12 +171,14 @@ TEST(SolidSyslogPlusTcpDatagram, CloseIsIdempotent) } TEST(SolidSyslogPlusTcpDatagram, CloseWithoutOpenIsNoOp) + { SolidSyslogDatagram_Close(datagram); CALLED_FAKE(FreeRtosSocketsFake_Closesocket, NEVER); } TEST(SolidSyslogPlusTcpDatagram, DestroyAfterCloseDoesNotCloseAgain) + { SolidSyslogDatagram_Open(datagram); SolidSyslogDatagram_Close(datagram); @@ -173,6 +188,7 @@ TEST(SolidSyslogPlusTcpDatagram, DestroyAfterCloseDoesNotCloseAgain) } TEST(SolidSyslogPlusTcpDatagram, SendToSendsBufferToDestinationAfterOpen) + { static const char TEST_MESSAGE[] = "hello"; static const size_t TEST_MESSAGE_LEN = sizeof(TEST_MESSAGE) - 1; @@ -195,6 +211,7 @@ TEST(SolidSyslogPlusTcpDatagram, SendToSendsBufferToDestinationAfterOpen) } TEST(SolidSyslogPlusTcpDatagram, SendToChecksIfIpIsInArpCache) + { openAndSendOnce(); @@ -203,6 +220,7 @@ TEST(SolidSyslogPlusTcpDatagram, SendToChecksIfIpIsInArpCache) } TEST(SolidSyslogPlusTcpDatagram, SendToFiresArpProbeOnCacheMiss) + { openAndSendOnce(); @@ -211,6 +229,7 @@ TEST(SolidSyslogPlusTcpDatagram, SendToFiresArpProbeOnCacheMiss) } TEST(SolidSyslogPlusTcpDatagram, SendToYieldsAfterArpProbeOnCacheMiss) + { openAndSendOnce(); @@ -218,6 +237,7 @@ TEST(SolidSyslogPlusTcpDatagram, SendToYieldsAfterArpProbeOnCacheMiss) } TEST(SolidSyslogPlusTcpDatagram, SendToSkipsArpProbeAndYieldOnCacheHit) + { SolidSyslogDatagram_Open(datagram); FreeRtosArpFake_SetCacheHit(true); @@ -230,6 +250,7 @@ TEST(SolidSyslogPlusTcpDatagram, SendToSkipsArpProbeAndYieldOnCacheHit) } TEST(SolidSyslogPlusTcpDatagram, SendToReChecksArpCacheOnEachCall) + { SolidSyslogDatagram_Open(datagram); @@ -247,6 +268,7 @@ TEST(SolidSyslogPlusTcpDatagram, SendToReChecksArpCacheOnEachCall) } TEST(SolidSyslogPlusTcpDatagram, SendToForwardsLengthVerbatim) + { static const char TEST_BUFFER[1024] = {}; SolidSyslogDatagram_Open(datagram); @@ -299,6 +321,7 @@ TEST_GROUP(SolidSyslogPlusTcpDatagramPool) // clang-format on TEST(SolidSyslogPlusTcpDatagramPool, FillingPoolThenOverflowReturnsDistinctFallback) + { FillPool(); @@ -308,6 +331,7 @@ TEST(SolidSyslogPlusTcpDatagramPool, FillingPoolThenOverflowReturnsDistinctFallb } TEST(SolidSyslogPlusTcpDatagramPool, ExhaustedCreateReportsError) + { ErrorHandlerFake_Install(nullptr); FillPool(); @@ -321,6 +345,7 @@ TEST(SolidSyslogPlusTcpDatagramPool, ExhaustedCreateReportsError) } TEST(SolidSyslogPlusTcpDatagramPool, FallbackVtableMethodsAreNoOps) + { FillPool(); overflow = SolidSyslogPlusTcpDatagram_Create(); @@ -341,6 +366,7 @@ TEST(SolidSyslogPlusTcpDatagramPool, FallbackVtableMethodsAreNoOps) } TEST(SolidSyslogPlusTcpDatagramPool, CreateAcquiresAndReleasesConfigLockOnFirstFreeSlot) + { ConfigLockFake_Install(); @@ -351,6 +377,7 @@ TEST(SolidSyslogPlusTcpDatagramPool, CreateAcquiresAndReleasesConfigLockOnFirstF } TEST(SolidSyslogPlusTcpDatagramPool, CreateLocksOncePerSlotProbedWhenPoolIsFull) + { FillPool(); ConfigLockFake_Install(); @@ -362,6 +389,7 @@ TEST(SolidSyslogPlusTcpDatagramPool, CreateLocksOncePerSlotProbedWhenPoolIsFull) } TEST(SolidSyslogPlusTcpDatagramPool, DestroyOfPooledHandleLocksOnce) + { pooled[0] = SolidSyslogPlusTcpDatagram_Create(); ConfigLockFake_Install(); @@ -374,6 +402,7 @@ TEST(SolidSyslogPlusTcpDatagramPool, DestroyOfPooledHandleLocksOnce) } TEST(SolidSyslogPlusTcpDatagramPool, DestroyOfUnknownHandleDoesNotLock) + { ConfigLockFake_Install(); struct SolidSyslogDatagram stranger = {}; @@ -385,6 +414,7 @@ TEST(SolidSyslogPlusTcpDatagramPool, DestroyOfUnknownHandleDoesNotLock) } TEST(SolidSyslogPlusTcpDatagramPool, DestroyOfUnknownHandleReportsWarning) + { ErrorHandlerFake_Install(nullptr); struct SolidSyslogDatagram stranger = {}; @@ -398,6 +428,7 @@ TEST(SolidSyslogPlusTcpDatagramPool, DestroyOfUnknownHandleReportsWarning) } TEST(SolidSyslogPlusTcpDatagramPool, DestroyOfStaleHandleReportsWarning) + { pooled[0] = SolidSyslogPlusTcpDatagram_Create(); SolidSyslogPlusTcpDatagram_Destroy(pooled[0]); diff --git a/Tests/FreeRtos/SolidSyslogPlusTcpTcpStreamTest.cpp b/Tests/FreeRtos/SolidSyslogPlusTcpTcpStreamTest.cpp index 9ddc9fdc..1dac769a 100644 --- a/Tests/FreeRtos/SolidSyslogPlusTcpTcpStreamTest.cpp +++ b/Tests/FreeRtos/SolidSyslogPlusTcpTcpStreamTest.cpp @@ -1,3 +1,6 @@ +#include +#include + #include "TestUtils.h" #include "CppUTest/TestHarness.h" @@ -14,11 +17,9 @@ using namespace CososoTesting; // NOLINT(google-build-using-namespace) -- test-f #include "SolidSyslogStream.h" #include "SolidSyslogStreamDefinition.h" #include "SolidSyslogTunables.h" - #include "FreeRtosArpFake.h" #include "FreeRtosSocketsFake.h" #include "FreeRtosTaskFake.h" - #include "FreeRTOS.h" #include "FreeRTOS_IP.h" #include "FreeRTOS_Sockets.h" @@ -134,11 +135,13 @@ TEST_GROUP(SolidSyslogPlusTcpTcpStream) // NOLINTEND(cppcoreguidelines-macro-usage,cppcoreguidelines-avoid-do-while) TEST(SolidSyslogPlusTcpTcpStream, CreateReturnsNonNullStream) + { CHECK(stream != nullptr); } TEST(SolidSyslogPlusTcpTcpStream, OpenCreatesTcpSocket) + { openStream(); CALLED_FAKE(FreeRtosSocketsFake_Socket, ONCE); @@ -148,17 +151,20 @@ TEST(SolidSyslogPlusTcpTcpStream, OpenCreatesTcpSocket) } TEST(SolidSyslogPlusTcpTcpStream, OpenReturnsTrueOnSuccess) + { CHECK_TRUE(SolidSyslogStream_Open(stream, addr)); } TEST(SolidSyslogPlusTcpTcpStream, OpenReturnsFalseWhenSocketFails) + { FreeRtosSocketsFake_SetSocketFails(true); CHECK_FALSE(SolidSyslogStream_Open(stream, addr)); } TEST(SolidSyslogPlusTcpTcpStream, OpenChecksIfDestinationIsInArpCache) + { openStream(); CALLED_FAKE(FreeRtosArpFake_IsIpInArpCache, ONCE); @@ -166,6 +172,7 @@ TEST(SolidSyslogPlusTcpTcpStream, OpenChecksIfDestinationIsInArpCache) } TEST(SolidSyslogPlusTcpTcpStream, OpenFiresArpProbeOnCacheMiss) + { openStream(); CALLED_FAKE(FreeRtosArpFake_OutputArpRequest, ONCE); @@ -173,12 +180,14 @@ TEST(SolidSyslogPlusTcpTcpStream, OpenFiresArpProbeOnCacheMiss) } TEST(SolidSyslogPlusTcpTcpStream, OpenYieldsAfterArpProbeOnCacheMiss) + { openStream(); CALLED_FAKE(FreeRtosTaskFake_VTaskDelay, ONCE); } TEST(SolidSyslogPlusTcpTcpStream, OpenSkipsArpProbeAndYieldOnCacheHit) + { FreeRtosArpFake_SetCacheHit(true); openStream(); @@ -187,18 +196,21 @@ TEST(SolidSyslogPlusTcpTcpStream, OpenSkipsArpProbeAndYieldOnCacheHit) } TEST(SolidSyslogPlusTcpTcpStream, OpenSetsConnectTimeoutBeforeConnect) + { openStream(); LONGS_EQUAL(pdMS_TO_TICKS(SOLIDSYSLOG_TCP_CONNECT_TIMEOUT_MS), FreeRtosSocketsFake_SndTimeoAtConnect()); } TEST(SolidSyslogPlusTcpTcpStream, OpenSetsRecvTimeoutBeforeConnect) + { openStream(); LONGS_EQUAL(pdMS_TO_TICKS(SOLIDSYSLOG_TCP_CONNECT_TIMEOUT_MS), FreeRtosSocketsFake_RcvTimeoAtConnect()); } TEST(SolidSyslogPlusTcpTcpStream, OpenInvokesConfiguredConnectTimeoutGetter) + { openStreamWithFakeGetter(); @@ -206,6 +218,7 @@ TEST(SolidSyslogPlusTcpTcpStream, OpenInvokesConfiguredConnectTimeoutGetter) } TEST(SolidSyslogPlusTcpTcpStream, OpenUsesGetterReturnValueAsConnectTimeout) + { FakeGetConnectTimeoutMs_ReturnValue = 1234U; @@ -215,6 +228,7 @@ TEST(SolidSyslogPlusTcpTcpStream, OpenUsesGetterReturnValueAsConnectTimeout) } TEST(SolidSyslogPlusTcpTcpStream, GetterReceivesNullContextWhenContextNotConfigured) + { openStreamWithFakeGetter(); @@ -222,6 +236,7 @@ TEST(SolidSyslogPlusTcpTcpStream, GetterReceivesNullContextWhenContextNotConfigu } TEST(SolidSyslogPlusTcpTcpStream, OpenCallsConnectWithSocketAndAddress) + { openStream(); CALLED_FAKE(FreeRtosSocketsFake_Connect, ONCE); @@ -231,12 +246,14 @@ TEST(SolidSyslogPlusTcpTcpStream, OpenCallsConnectWithSocketAndAddress) } TEST(SolidSyslogPlusTcpTcpStream, OpenClearsSendTimeoutAfterConnect) + { openStream(); LONGS_EQUAL(0, FreeRtosSocketsFake_LastSndTimeoSet()); } TEST(SolidSyslogPlusTcpTcpStream, OpenClearsRecvTimeoutAfterConnect) + { openStream(); LONGS_EQUAL(0, FreeRtosSocketsFake_LastRcvTimeoSet()); @@ -244,6 +261,7 @@ TEST(SolidSyslogPlusTcpTcpStream, OpenClearsRecvTimeoutAfterConnect) } TEST(SolidSyslogPlusTcpTcpStream, OpenCallsSetsockoptWithReturnedSocketAndLevelZero) + { openStream(); POINTERS_EQUAL(FreeRtosSocketsFake_LastSocketReturned(), FreeRtosSocketsFake_LastSetsockoptSocket()); @@ -251,18 +269,21 @@ TEST(SolidSyslogPlusTcpTcpStream, OpenCallsSetsockoptWithReturnedSocketAndLevelZ } TEST(SolidSyslogPlusTcpTcpStream, OpenPassesTickTypeSizedOptionLengthToSetsockopt) + { openStream(); LONGS_EQUAL(sizeof(TickType_t), FreeRtosSocketsFake_LastSetsockoptOptionLength()); } TEST(SolidSyslogPlusTcpTcpStream, OpenReturnsFalseOnConnectFailure) + { FreeRtosSocketsFake_SetConnectFails(true); CHECK_FALSE(SolidSyslogStream_Open(stream, addr)); } TEST(SolidSyslogPlusTcpTcpStream, OpenClosesSocketOnConnectFailure) + { FreeRtosSocketsFake_SetConnectFails(true); openStream(); @@ -270,6 +291,7 @@ TEST(SolidSyslogPlusTcpTcpStream, OpenClosesSocketOnConnectFailure) } TEST(SolidSyslogPlusTcpTcpStream, OpenIsIdempotent) + { openStream(); CHECK_TRUE(SolidSyslogStream_Open(stream, addr)); @@ -278,12 +300,14 @@ TEST(SolidSyslogPlusTcpTcpStream, OpenIsIdempotent) } TEST(SolidSyslogPlusTcpTcpStream, SendFailsBeforeOpen) + { CHECK_FALSE(SolidSyslogStream_Send(stream, "x", 1)); CALLED_FAKE(FreeRtosSocketsFake_Send, NEVER); } TEST(SolidSyslogPlusTcpTcpStream, SendCallsFreeRtosSendWithSocketBufferAndLength) + { openStream(); SolidSyslogStream_Send(stream, TEST_MESSAGE, TEST_MESSAGE_LEN); @@ -295,12 +319,14 @@ TEST(SolidSyslogPlusTcpTcpStream, SendCallsFreeRtosSendWithSocketBufferAndLength } TEST(SolidSyslogPlusTcpTcpStream, SendReturnsTrueOnFullWrite) + { openStream(); CHECK_TRUE(SolidSyslogStream_Send(stream, TEST_MESSAGE, TEST_MESSAGE_LEN)); } TEST(SolidSyslogPlusTcpTcpStream, SendReturnsFalseOnShortWrite) + { openStream(); FreeRtosSocketsFake_SetSendReturn(TEST_SHORT_WRITE_BYTES); @@ -308,6 +334,7 @@ TEST(SolidSyslogPlusTcpTcpStream, SendReturnsFalseOnShortWrite) } TEST(SolidSyslogPlusTcpTcpStream, SendDoesNotRetryAfterShortWrite) + { openStream(); FreeRtosSocketsFake_SetSendReturn(TEST_SHORT_WRITE_BYTES); @@ -316,6 +343,7 @@ TEST(SolidSyslogPlusTcpTcpStream, SendDoesNotRetryAfterShortWrite) } TEST(SolidSyslogPlusTcpTcpStream, SendClosesSocketOnShortWrite) + { openStream(); FreeRtosSocketsFake_SetSendReturn(TEST_SHORT_WRITE_BYTES); @@ -324,6 +352,7 @@ TEST(SolidSyslogPlusTcpTcpStream, SendClosesSocketOnShortWrite) } TEST(SolidSyslogPlusTcpTcpStream, SendReturnsFalseOnError) + { openStream(); FreeRtosSocketsFake_SetSendFails(true); @@ -331,6 +360,7 @@ TEST(SolidSyslogPlusTcpTcpStream, SendReturnsFalseOnError) } TEST(SolidSyslogPlusTcpTcpStream, SendClosesSocketOnError) + { openStream(); FreeRtosSocketsFake_SetSendFails(true); @@ -339,12 +369,14 @@ TEST(SolidSyslogPlusTcpTcpStream, SendClosesSocketOnError) } TEST(SolidSyslogPlusTcpTcpStream, ReadReturnsNegativeOneBeforeOpen) + { LONGS_EQUAL(-1, readIntoBuffer()); CALLED_FAKE(FreeRtosSocketsFake_Recv, NEVER); } TEST(SolidSyslogPlusTcpTcpStream, ReadCallsFreeRtosRecvWithSocketBufferAndLength) + { openStream(); readIntoBuffer(); @@ -356,6 +388,7 @@ TEST(SolidSyslogPlusTcpTcpStream, ReadCallsFreeRtosRecvWithSocketBufferAndLength } TEST(SolidSyslogPlusTcpTcpStream, ReadReturnsBytesOnSuccess) + { openStream(); FreeRtosSocketsFake_SetRecvReturn(TEST_READ_BYTES); @@ -363,6 +396,7 @@ TEST(SolidSyslogPlusTcpTcpStream, ReadReturnsBytesOnSuccess) } TEST(SolidSyslogPlusTcpTcpStream, ReadReturnsZeroWhenWouldBlock) + { openStream(); FreeRtosSocketsFake_SetRecvReturn(0); @@ -370,6 +404,7 @@ TEST(SolidSyslogPlusTcpTcpStream, ReadReturnsZeroWhenWouldBlock) } TEST(SolidSyslogPlusTcpTcpStream, ReadLeavesSocketOpenWhenWouldBlock) + { openStream(); FreeRtosSocketsFake_SetRecvReturn(0); @@ -378,6 +413,7 @@ TEST(SolidSyslogPlusTcpTcpStream, ReadLeavesSocketOpenWhenWouldBlock) } TEST(SolidSyslogPlusTcpTcpStream, ReadReturnsNegativeOneOnErrorAndClosesSocket) + { openStream(); FreeRtosSocketsFake_SetRecvFails(true); @@ -386,12 +422,14 @@ TEST(SolidSyslogPlusTcpTcpStream, ReadReturnsNegativeOneOnErrorAndClosesSocket) } TEST(SolidSyslogPlusTcpTcpStream, CloseWithoutOpenIsNoOp) + { SolidSyslogStream_Close(stream); CALLED_FAKE(FreeRtosSocketsFake_Closesocket, NEVER); } TEST(SolidSyslogPlusTcpTcpStream, CloseClosesOpenSocket) + { openStream(); SolidSyslogStream_Close(stream); @@ -399,6 +437,7 @@ TEST(SolidSyslogPlusTcpTcpStream, CloseClosesOpenSocket) } TEST(SolidSyslogPlusTcpTcpStream, CloseIsIdempotent) + { openStream(); SolidSyslogStream_Close(stream); @@ -407,6 +446,7 @@ TEST(SolidSyslogPlusTcpTcpStream, CloseIsIdempotent) } TEST(SolidSyslogPlusTcpTcpStream, DestroyClosesOpenSocket) + { openStream(); SolidSyslogPlusTcpTcpStream_Destroy(stream); @@ -415,6 +455,7 @@ TEST(SolidSyslogPlusTcpTcpStream, DestroyClosesOpenSocket) } TEST(SolidSyslogPlusTcpTcpStream, DestroyAfterCloseDoesNotCloseAgain) + { openStream(); SolidSyslogStream_Close(stream); @@ -464,6 +505,7 @@ TEST_GROUP(SolidSyslogPlusTcpTcpStreamPool) // clang-format on TEST(SolidSyslogPlusTcpTcpStreamPool, FillingPoolThenOverflowReturnsDistinctFallback) + { FillPool(); @@ -473,6 +515,7 @@ TEST(SolidSyslogPlusTcpTcpStreamPool, FillingPoolThenOverflowReturnsDistinctFall } TEST(SolidSyslogPlusTcpTcpStreamPool, ExhaustedCreateReportsError) + { ErrorHandlerFake_Install(nullptr); FillPool(); @@ -486,6 +529,7 @@ TEST(SolidSyslogPlusTcpTcpStreamPool, ExhaustedCreateReportsError) } TEST(SolidSyslogPlusTcpTcpStreamPool, FallbackVtableMethodsAreNoOps) + { FillPool(); overflow = SolidSyslogPlusTcpTcpStream_Create(nullptr); @@ -509,6 +553,7 @@ TEST(SolidSyslogPlusTcpTcpStreamPool, FallbackVtableMethodsAreNoOps) } TEST(SolidSyslogPlusTcpTcpStreamPool, CreateAcquiresAndReleasesConfigLockOnFirstFreeSlot) + { ConfigLockFake_Install(); @@ -519,6 +564,7 @@ TEST(SolidSyslogPlusTcpTcpStreamPool, CreateAcquiresAndReleasesConfigLockOnFirst } TEST(SolidSyslogPlusTcpTcpStreamPool, CreateLocksOncePerSlotProbedWhenPoolIsFull) + { FillPool(); ConfigLockFake_Install(); @@ -530,6 +576,7 @@ TEST(SolidSyslogPlusTcpTcpStreamPool, CreateLocksOncePerSlotProbedWhenPoolIsFull } TEST(SolidSyslogPlusTcpTcpStreamPool, DestroyOfPooledHandleLocksOnce) + { pooled[0] = SolidSyslogPlusTcpTcpStream_Create(nullptr); ConfigLockFake_Install(); @@ -542,6 +589,7 @@ TEST(SolidSyslogPlusTcpTcpStreamPool, DestroyOfPooledHandleLocksOnce) } TEST(SolidSyslogPlusTcpTcpStreamPool, DestroyOfUnknownHandleDoesNotLock) + { ConfigLockFake_Install(); struct SolidSyslogStream stranger = {}; @@ -553,6 +601,7 @@ TEST(SolidSyslogPlusTcpTcpStreamPool, DestroyOfUnknownHandleDoesNotLock) } TEST(SolidSyslogPlusTcpTcpStreamPool, DestroyOfUnknownHandleReportsWarning) + { ErrorHandlerFake_Install(nullptr); struct SolidSyslogStream stranger = {}; @@ -566,6 +615,7 @@ TEST(SolidSyslogPlusTcpTcpStreamPool, DestroyOfUnknownHandleReportsWarning) } TEST(SolidSyslogPlusTcpTcpStreamPool, DestroyOfStaleHandleReportsWarning) + { pooled[0] = SolidSyslogPlusTcpTcpStream_Create(nullptr); SolidSyslogPlusTcpTcpStream_Destroy(pooled[0]); diff --git a/Tests/Lwip/SolidSyslogLwipRawAddressTest.cpp b/Tests/Lwip/SolidSyslogLwipRawAddressTest.cpp index edd7cf54..c4998396 100644 --- a/Tests/Lwip/SolidSyslogLwipRawAddressTest.cpp +++ b/Tests/Lwip/SolidSyslogLwipRawAddressTest.cpp @@ -1,5 +1,6 @@ #include "TestUtils.h" #include "CppUTest/TestHarness.h" +#include "lwip/ip4_addr.h" using namespace CososoTesting; // NOLINT(google-build-using-namespace) -- test-file scope only; brings NEVER/ONCE/TWICE/THRICE into scope for the CALLED_* // macros @@ -11,7 +12,6 @@ using namespace CososoTesting; // NOLINT(google-build-using-namespace) -- test-f #include "SolidSyslogLwipRawAddressPrivate.h" #include "SolidSyslogPrival.h" #include "SolidSyslogTunables.h" - #include "lwip/ip_addr.h" // NOLINTBEGIN(cppcoreguidelines-macro-usage,cppcoreguidelines-avoid-do-while) -- macros preserve __FILE__/__LINE__ at the call site @@ -60,11 +60,13 @@ TEST_GROUP(SolidSyslogLwipRawAddress) // clang-format on TEST(SolidSyslogLwipRawAddress, CreateReturnsNonNull) + { CHECK(address != nullptr); } TEST(SolidSyslogLwipRawAddress, IpFieldRoundTripsBytes) + { struct SolidSyslogLwipRawAddress* self = SolidSyslogLwipRawAddress_As(address); IP4_ADDR(&self->Ip, 127, 0, 0, 1); @@ -78,6 +80,7 @@ TEST(SolidSyslogLwipRawAddress, IpFieldRoundTripsBytes) } TEST(SolidSyslogLwipRawAddress, PortFieldRoundTripsValue) + { SolidSyslogLwipRawAddress_As(address)->Port = 514; @@ -85,6 +88,7 @@ TEST(SolidSyslogLwipRawAddress, PortFieldRoundTripsValue) } TEST(SolidSyslogLwipRawAddress, CreateZeroesIpAndPortFromAnyPriorSlotContents) + { struct SolidSyslogLwipRawAddress* self = SolidSyslogLwipRawAddress_As(address); IP4_ADDR(&self->Ip, 1, 2, 3, 4); @@ -134,6 +138,7 @@ TEST_GROUP(SolidSyslogLwipRawAddressPool) // clang-format on TEST(SolidSyslogLwipRawAddressPool, FillingPoolThenOverflowReturnsDistinctFallback) + { FillPool(); @@ -143,6 +148,7 @@ TEST(SolidSyslogLwipRawAddressPool, FillingPoolThenOverflowReturnsDistinctFallba } TEST(SolidSyslogLwipRawAddressPool, ExhaustedCreateReportsError) + { ErrorHandlerFake_Install(nullptr); FillPool(); @@ -153,6 +159,7 @@ TEST(SolidSyslogLwipRawAddressPool, ExhaustedCreateReportsError) } TEST(SolidSyslogLwipRawAddressPool, CreateAcquiresAndReleasesConfigLockOnFirstFreeSlot) + { ConfigLockFake_Install(); @@ -163,6 +170,7 @@ TEST(SolidSyslogLwipRawAddressPool, CreateAcquiresAndReleasesConfigLockOnFirstFr } TEST(SolidSyslogLwipRawAddressPool, CreateLocksOncePerSlotProbedWhenPoolIsFull) + { FillPool(); ConfigLockFake_Install(); @@ -174,6 +182,7 @@ TEST(SolidSyslogLwipRawAddressPool, CreateLocksOncePerSlotProbedWhenPoolIsFull) } TEST(SolidSyslogLwipRawAddressPool, DestroyOfPooledHandleLocksOnce) + { pooled[0] = SolidSyslogLwipRawAddress_Create(); ConfigLockFake_Install(); @@ -186,6 +195,7 @@ TEST(SolidSyslogLwipRawAddressPool, DestroyOfPooledHandleLocksOnce) } TEST(SolidSyslogLwipRawAddressPool, DestroyOfUnknownHandleDoesNotLock) + { ConfigLockFake_Install(); char stranger = 0; @@ -197,6 +207,7 @@ TEST(SolidSyslogLwipRawAddressPool, DestroyOfUnknownHandleDoesNotLock) } TEST(SolidSyslogLwipRawAddressPool, DestroyOfUnknownHandleReportsWarning) + { ErrorHandlerFake_Install(nullptr); char stranger = 0; @@ -207,6 +218,7 @@ TEST(SolidSyslogLwipRawAddressPool, DestroyOfUnknownHandleReportsWarning) } TEST(SolidSyslogLwipRawAddressPool, DestroyOfStaleHandleReportsWarning) + { pooled[0] = SolidSyslogLwipRawAddress_Create(); SolidSyslogLwipRawAddress_Destroy(pooled[0]); diff --git a/Tests/Lwip/SolidSyslogLwipRawResolverTest.cpp b/Tests/Lwip/SolidSyslogLwipRawResolverTest.cpp index c4d707d0..797c2bcb 100644 --- a/Tests/Lwip/SolidSyslogLwipRawResolverTest.cpp +++ b/Tests/Lwip/SolidSyslogLwipRawResolverTest.cpp @@ -1,5 +1,6 @@ #include "TestUtils.h" #include "CppUTest/TestHarness.h" +#include "lwip/ip4_addr.h" using namespace CososoTesting; // NOLINT(google-build-using-namespace) -- test-file scope only; brings NEVER/ONCE/TWICE/THRICE into scope for the CALLED_* // macros @@ -17,7 +18,6 @@ using namespace CososoTesting; // NOLINT(google-build-using-namespace) -- test-f #include "SolidSyslogResolverDefinition.h" #include "SolidSyslogTransport.h" #include "SolidSyslogTunables.h" - #include "lwip/ip_addr.h" // NOLINTBEGIN(cppcoreguidelines-macro-usage,cppcoreguidelines-avoid-do-while) -- macros preserve __FILE__/__LINE__ at the call site @@ -77,16 +77,19 @@ TEST_GROUP(SolidSyslogLwipRawResolver) // clang-format on TEST(SolidSyslogLwipRawResolver, CreateSucceeds) + { CHECK(resolver != nullptr); } TEST(SolidSyslogLwipRawResolver, ResolveReturnsTrueOnLiteralIpv4) + { CHECK_TRUE(Resolve()); } TEST(SolidSyslogLwipRawResolver, ResolvePopulatesIpFromLiteralIpv4) + { Resolve("127.0.0.1"); @@ -99,6 +102,7 @@ TEST(SolidSyslogLwipRawResolver, ResolvePopulatesIpFromLiteralIpv4) } TEST(SolidSyslogLwipRawResolver, ResolvePopulatesPortFromArg) + { Resolve(TEST_HOST, 9999); @@ -106,6 +110,7 @@ TEST(SolidSyslogLwipRawResolver, ResolvePopulatesPortFromArg) } TEST(SolidSyslogLwipRawResolver, ResolveReturnsFalseOnNonLiteralHost) + { // Locks in the no-DNS contract: a hostname that isn't a literal dotted-quad // is rejected. Real DNS would land as the future SolidSyslogLwipRawDnsResolver @@ -114,6 +119,7 @@ TEST(SolidSyslogLwipRawResolver, ResolveReturnsFalseOnNonLiteralHost) } TEST(SolidSyslogLwipRawResolver, ResolveReturnsFalseWhenIpaddrAtonRejectsHost) + { // We defer to lwIP's ipaddr_aton — whatever the parser accepts, we accept; // whatever it rejects, we reject. We do not enforce any specific shape on @@ -160,6 +166,7 @@ TEST_GROUP(SolidSyslogLwipRawResolverPool) // clang-format on TEST(SolidSyslogLwipRawResolverPool, FillingPoolThenOverflowReturnsDistinctFallback) + { FillPool(); @@ -169,6 +176,7 @@ TEST(SolidSyslogLwipRawResolverPool, FillingPoolThenOverflowReturnsDistinctFallb } TEST(SolidSyslogLwipRawResolverPool, ExhaustedCreateReportsError) + { ErrorHandlerFake_Install(nullptr); FillPool(); @@ -179,6 +187,7 @@ TEST(SolidSyslogLwipRawResolverPool, ExhaustedCreateReportsError) } TEST(SolidSyslogLwipRawResolverPool, FallbackResolveReturnsFalse) + { FillPool(); overflow = SolidSyslogLwipRawResolver_Create(); @@ -190,6 +199,7 @@ TEST(SolidSyslogLwipRawResolverPool, FallbackResolveReturnsFalse) } TEST(SolidSyslogLwipRawResolverPool, CreateAcquiresAndReleasesConfigLockOnFirstFreeSlot) + { ConfigLockFake_Install(); @@ -200,6 +210,7 @@ TEST(SolidSyslogLwipRawResolverPool, CreateAcquiresAndReleasesConfigLockOnFirstF } TEST(SolidSyslogLwipRawResolverPool, CreateLocksOncePerSlotProbedWhenPoolIsFull) + { FillPool(); ConfigLockFake_Install(); @@ -211,6 +222,7 @@ TEST(SolidSyslogLwipRawResolverPool, CreateLocksOncePerSlotProbedWhenPoolIsFull) } TEST(SolidSyslogLwipRawResolverPool, DestroyOfPooledHandleLocksOnce) + { pooled[0] = SolidSyslogLwipRawResolver_Create(); ConfigLockFake_Install(); @@ -223,6 +235,7 @@ TEST(SolidSyslogLwipRawResolverPool, DestroyOfPooledHandleLocksOnce) } TEST(SolidSyslogLwipRawResolverPool, DestroyOfUnknownHandleDoesNotLock) + { ConfigLockFake_Install(); struct SolidSyslogResolver stranger = {}; @@ -234,6 +247,7 @@ TEST(SolidSyslogLwipRawResolverPool, DestroyOfUnknownHandleDoesNotLock) } TEST(SolidSyslogLwipRawResolverPool, DestroyOfUnknownHandleReportsWarning) + { ErrorHandlerFake_Install(nullptr); struct SolidSyslogResolver stranger = {}; @@ -244,6 +258,7 @@ TEST(SolidSyslogLwipRawResolverPool, DestroyOfUnknownHandleReportsWarning) } TEST(SolidSyslogLwipRawResolverPool, DestroyOfStaleHandleReportsWarning) + { pooled[0] = SolidSyslogLwipRawResolver_Create(); SolidSyslogLwipRawResolver_Destroy(pooled[0]); @@ -256,6 +271,7 @@ TEST(SolidSyslogLwipRawResolverPool, DestroyOfStaleHandleReportsWarning) } TEST(SolidSyslogLwipRawResolver, UdpTransportResolvesIdenticallyToTcp) + { // Locks in that the literal-IPv4 resolver does not dispatch on transport — // a future reader must not add transport-typed pcb selection here. diff --git a/Tests/MbedTls/SolidSyslogMbedTlsStreamTest.cpp b/Tests/MbedTls/SolidSyslogMbedTlsStreamTest.cpp index 5ac5c8c2..6520f6cc 100644 --- a/Tests/MbedTls/SolidSyslogMbedTlsStreamTest.cpp +++ b/Tests/MbedTls/SolidSyslogMbedTlsStreamTest.cpp @@ -1,9 +1,12 @@ #include "CppUTest/TestHarness.h" +#include "mbedtls/pk.h" +#include "mbedtls/x509_crt.h" extern "C" { #include #include +#include #include "ErrorHandlerFake.h" #include "MbedTlsFake.h" @@ -12,7 +15,6 @@ extern "C" #include "SolidSyslogPrival.h" #include "AddressFake.h" #include "SolidSyslogStream.h" -#include "SolidSyslogStreamDefinition.h" #include "SolidSyslogTunables.h" #include "StreamFake.h" } @@ -144,6 +146,7 @@ TEST_GROUP(SolidSyslogMbedTlsStream) // clang-format on TEST(SolidSyslogMbedTlsStream, OpenDelegatesToInjectedTransport) + { SolidSyslogStream_Open(handle, addr); @@ -152,6 +155,7 @@ TEST(SolidSyslogMbedTlsStream, OpenDelegatesToInjectedTransport) } TEST(SolidSyslogMbedTlsStream, CreateInitialisesSslConfigForSafeFree) + { /* Init happens eagerly in Create (via MbedTlsStream_Initialise) so the * symmetric *_free in Close is always safe — whether Open was reached, @@ -160,6 +164,7 @@ TEST(SolidSyslogMbedTlsStream, CreateInitialisesSslConfigForSafeFree) } TEST(SolidSyslogMbedTlsStream, OpenAppliesClientStreamDefaultsToSslConfig) + { SolidSyslogStream_Open(handle, addr); @@ -171,12 +176,14 @@ TEST(SolidSyslogMbedTlsStream, OpenAppliesClientStreamDefaultsToSslConfig) } TEST(SolidSyslogMbedTlsStream, CreateInitialisesSslContextForSafeFree) + { /* Same eager-init invariant as the SslConfig case above. */ LONGS_EQUAL(1, MbedTlsFake_SslInitCallCount()); } TEST(SolidSyslogMbedTlsStream, OpenBindsContextToConfig) + { SolidSyslogStream_Open(handle, addr); @@ -186,6 +193,7 @@ TEST(SolidSyslogMbedTlsStream, OpenBindsContextToConfig) } TEST(SolidSyslogMbedTlsStream, OpenWiresBioWithNonNullSendRecvAndNullRecvTimeout) + { /* mbedTLS's set_bio takes both a recv and a recv_timeout callback; * we install the former (non-blocking would-block via WANT_READ) and @@ -202,6 +210,7 @@ TEST(SolidSyslogMbedTlsStream, OpenWiresBioWithNonNullSendRecvAndNullRecvTimeout } TEST(SolidSyslogMbedTlsStream, OpenDrivesHandshakeOnTheSslContext) + { SolidSyslogStream_Open(handle, addr); @@ -210,6 +219,7 @@ TEST(SolidSyslogMbedTlsStream, OpenDrivesHandshakeOnTheSslContext) } TEST(SolidSyslogMbedTlsStream, OpenReturnsTrueWhenHandshakeSucceeds) + { MbedTlsFake_SetSslHandshakeReturn(0); @@ -217,6 +227,7 @@ TEST(SolidSyslogMbedTlsStream, OpenReturnsTrueWhenHandshakeSucceeds) } TEST(SolidSyslogMbedTlsStream, OpenReturnsFalseWhenHandshakeFails) + { MbedTlsFake_SetSslHandshakeReturn(-1); @@ -232,6 +243,7 @@ TEST(SolidSyslogMbedTlsStream, OpenReturnsFalseWhenHandshakeFails) * ------------------------------------------------------------------------- */ TEST(SolidSyslogMbedTlsStream, OpenRetriesHandshakeOnWantRead) + { ArrangeHandshakeRetryThenSucceed(MBEDTLS_ERR_SSL_WANT_READ); @@ -240,6 +252,7 @@ TEST(SolidSyslogMbedTlsStream, OpenRetriesHandshakeOnWantRead) } TEST(SolidSyslogMbedTlsStream, OpenSleepsBetweenHandshakeRetries) + { ArrangeHandshakeRetryThenSucceed(MBEDTLS_ERR_SSL_WANT_READ); @@ -248,6 +261,7 @@ TEST(SolidSyslogMbedTlsStream, OpenSleepsBetweenHandshakeRetries) } TEST(SolidSyslogMbedTlsStream, OpenRetriesHandshakeOnWantWrite) + { /* WANT_WRITE arises when mbedTLS needs to send (e.g. ClientFinished * under non-blocking transport with a temporarily-full send buffer). @@ -259,6 +273,7 @@ TEST(SolidSyslogMbedTlsStream, OpenRetriesHandshakeOnWantWrite) } TEST(SolidSyslogMbedTlsStream, OpenClosesTransportAndFreesSslStateWhenHandshakeBudgetExhausts) + { /* mbedtls_ssl_handshake always returns WANT_READ — handshake never makes * progress, so the bounded budget should expire and Open returns false. */ @@ -269,6 +284,7 @@ TEST(SolidSyslogMbedTlsStream, OpenClosesTransportAndFreesSslStateWhenHandshakeB } TEST(SolidSyslogMbedTlsStream, OpenInvokesConfiguredHandshakeTimeoutGetter) + { RecreateHandleWithFakeHandshakeGetter(); SolidSyslogStream_Open(handle, addr); @@ -277,6 +293,7 @@ TEST(SolidSyslogMbedTlsStream, OpenInvokesConfiguredHandshakeTimeoutGetter) } TEST(SolidSyslogMbedTlsStream, OpenUsesGetterReturnValueAsHandshakeBudget) + { /* 5 ms budget against the 1 ms poll interval → loop should sleep 5 times before declaring HANDSHAKE_TIMEOUT and unwinding. */ @@ -290,6 +307,7 @@ TEST(SolidSyslogMbedTlsStream, OpenUsesGetterReturnValueAsHandshakeBudget) } TEST(SolidSyslogMbedTlsStream, GetterReceivesNullContextWhenContextNotConfigured) + { RecreateHandleWithFakeHandshakeGetter(); SolidSyslogStream_Open(handle, addr); @@ -298,6 +316,7 @@ TEST(SolidSyslogMbedTlsStream, GetterReceivesNullContextWhenContextNotConfigured } TEST(SolidSyslogMbedTlsStream, SecondOpenAfterFailedFirstOpenSucceeds) + { /* The recovery contract that the per-failure-point unwinds enable: once * Open's failure tail Closes the transport and frees the SSL state, the @@ -315,6 +334,7 @@ TEST(SolidSyslogMbedTlsStream, SecondOpenAfterFailedFirstOpenSucceeds) } TEST(SolidSyslogMbedTlsStream, OpenClosesTransportAndFreesSslStateWhenHandshakeFailsHard) + { /* Non-WANT error (e.g. a verify/connection failure) is fail-fast — no * retry budget burn, no Sleep. */ @@ -334,6 +354,7 @@ TEST(SolidSyslogMbedTlsStream, OpenClosesTransportAndFreesSslStateWhenHandshakeF * ------------------------------------------------------------------------- */ TEST(SolidSyslogMbedTlsStream, OpenClosesTransportAndFreesSslStateWhenSslConfigDefaultsFails) + { MbedTlsFake_SetSslConfigDefaultsReturn(-1); @@ -342,6 +363,7 @@ TEST(SolidSyslogMbedTlsStream, OpenClosesTransportAndFreesSslStateWhenSslConfigD } TEST(SolidSyslogMbedTlsStream, OpenClosesTransportAndFreesSslStateWhenSslSetupFails) + { MbedTlsFake_SetSslSetupReturn(-1); @@ -350,6 +372,7 @@ TEST(SolidSyslogMbedTlsStream, OpenClosesTransportAndFreesSslStateWhenSslSetupFa } TEST(SolidSyslogMbedTlsStream, OpenClosesTransportAndFreesSslStateWhenSetHostnameFails) + { /* ServerName must be set for ConfigureExpectedHostname to invoke * mbedtls_ssl_set_hostname — otherwise the helper short-circuits to true. */ @@ -362,6 +385,7 @@ TEST(SolidSyslogMbedTlsStream, OpenClosesTransportAndFreesSslStateWhenSetHostnam } TEST(SolidSyslogMbedTlsStream, SendForwardsBufferToSslWrite) + { const unsigned char payload[] = {0x10, 0x20, 0x30}; @@ -373,6 +397,7 @@ TEST(SolidSyslogMbedTlsStream, SendForwardsBufferToSslWrite) } TEST(SolidSyslogMbedTlsStream, SendReturnsTrueWhenSslWriteWritesAllBytes) + { const unsigned char payload[] = {0x10, 0x20, 0x30}; MbedTlsFake_SetSslWriteReturn((int) sizeof(payload)); @@ -381,6 +406,7 @@ TEST(SolidSyslogMbedTlsStream, SendReturnsTrueWhenSslWriteWritesAllBytes) } TEST(SolidSyslogMbedTlsStream, SendReturnsFalseWhenSslWriteWritesPartial) + { const unsigned char payload[] = {0x10, 0x20, 0x30}; MbedTlsFake_SetSslWriteReturn(1); @@ -389,6 +415,7 @@ TEST(SolidSyslogMbedTlsStream, SendReturnsFalseWhenSslWriteWritesPartial) } TEST(SolidSyslogMbedTlsStream, SendReturnsFalseWhenSslWriteFails) + { const unsigned char payload[] = {0x10, 0x20, 0x30}; MbedTlsFake_SetSslWriteReturn(-1); @@ -397,6 +424,7 @@ TEST(SolidSyslogMbedTlsStream, SendReturnsFalseWhenSslWriteFails) } TEST(SolidSyslogMbedTlsStream, SendClosesSslAndTransportOnWriteFailure) + { /* Fail-fast: a TLS-level write failure means the session state is * unrecoverable. Mirror the OpenSSL TlsStream contract — close internally @@ -411,6 +439,7 @@ TEST(SolidSyslogMbedTlsStream, SendClosesSslAndTransportOnWriteFailure) } TEST(SolidSyslogMbedTlsStream, SendClosesSslAndTransportOnShortWrite) + { /* mbedtls_ssl_write returning fewer bytes than requested is treated the * same as outright failure — the application boundary requires @@ -425,6 +454,7 @@ TEST(SolidSyslogMbedTlsStream, SendClosesSslAndTransportOnShortWrite) } TEST(SolidSyslogMbedTlsStream, ReadForwardsBufferToSslRead) + { unsigned char buffer[8]; MbedTlsFake_SetSslReadReturn(3); @@ -437,6 +467,7 @@ TEST(SolidSyslogMbedTlsStream, ReadForwardsBufferToSslRead) } TEST(SolidSyslogMbedTlsStream, ReadReturnsByteCountWhenSslReadReturnsPositive) + { unsigned char buffer[8]; MbedTlsFake_SetSslReadReturn(5); @@ -445,6 +476,7 @@ TEST(SolidSyslogMbedTlsStream, ReadReturnsByteCountWhenSslReadReturnsPositive) } TEST(SolidSyslogMbedTlsStream, ReadReturnsZeroOnWantRead) + { unsigned char buffer[8]; MbedTlsFake_SetSslReadReturn(MBEDTLS_ERR_SSL_WANT_READ); @@ -453,6 +485,7 @@ TEST(SolidSyslogMbedTlsStream, ReadReturnsZeroOnWantRead) } TEST(SolidSyslogMbedTlsStream, ReadReturnsNegativeOnSslReadError) + { unsigned char buffer[8]; MbedTlsFake_SetSslReadReturn(-1); @@ -461,6 +494,7 @@ TEST(SolidSyslogMbedTlsStream, ReadReturnsNegativeOnSslReadError) } TEST(SolidSyslogMbedTlsStream, ReadClosesSslAndTransportOnHardError) + { /* Same fail-fast contract as Send: any read result other than positive * bytes or WANT_READ (e.g. peer close_notify, fatal alert, transport @@ -475,6 +509,7 @@ TEST(SolidSyslogMbedTlsStream, ReadClosesSslAndTransportOnHardError) } TEST(SolidSyslogMbedTlsStream, ReadDoesNotCloseOnWantRead) + { /* WANT_READ is steady-state would-block, not a connection failure — * leave the session intact so the caller can retry. */ @@ -488,6 +523,7 @@ TEST(SolidSyslogMbedTlsStream, ReadDoesNotCloseOnWantRead) } TEST(SolidSyslogMbedTlsStream, CloseAfterInternalCloseFromSendFailureDoesNotDoubleFree) + { /* Send and Read may close internally on failure; the subsequent Close * from the StreamSender reconnect path or Destroy must not crash or @@ -506,6 +542,7 @@ TEST(SolidSyslogMbedTlsStream, CloseAfterInternalCloseFromSendFailureDoesNotDoub } TEST(SolidSyslogMbedTlsStream, CloseSendsSslCloseNotifyOnTheSslContextFromOpen) + { SolidSyslogStream_Open(handle, addr); @@ -516,6 +553,7 @@ TEST(SolidSyslogMbedTlsStream, CloseSendsSslCloseNotifyOnTheSslContextFromOpen) } TEST(SolidSyslogMbedTlsStream, CloseFreesSslContextAndSslConfigFromOpen) + { SolidSyslogStream_Open(handle, addr); @@ -528,6 +566,7 @@ TEST(SolidSyslogMbedTlsStream, CloseFreesSslContextAndSslConfigFromOpen) } TEST(SolidSyslogMbedTlsStream, CloseDelegatesToInjectedTransport) + { SolidSyslogStream_Close(handle); @@ -535,6 +574,7 @@ TEST(SolidSyslogMbedTlsStream, CloseDelegatesToInjectedTransport) } TEST(SolidSyslogMbedTlsStream, BioSendCallbackForwardsBufferToTransport) + { SolidSyslogStream_Open(handle, addr); auto* bioSend = MbedTlsFake_LastSslSetBioSendCallback(); @@ -550,6 +590,7 @@ TEST(SolidSyslogMbedTlsStream, BioSendCallbackForwardsBufferToTransport) } TEST(SolidSyslogMbedTlsStream, BioRecvCallbackForwardsBufferToTransport) + { SolidSyslogStream_Open(handle, addr); auto* bioRecv = MbedTlsFake_LastSslSetBioRecvCallback(); @@ -566,6 +607,7 @@ TEST(SolidSyslogMbedTlsStream, BioRecvCallbackForwardsBufferToTransport) } TEST(SolidSyslogMbedTlsStream, BioRecvReturnsWantReadWhenTransportWouldBlock) + { /* Stream contract: transport Read returns 0 to signal would-block. mbedTLS * needs MBEDTLS_ERR_SSL_WANT_READ to drive its retry loop; any other @@ -583,6 +625,7 @@ TEST(SolidSyslogMbedTlsStream, BioRecvReturnsWantReadWhenTransportWouldBlock) } TEST(SolidSyslogMbedTlsStream, BioRecvReturnsFatalWhenTransportFails) + { /* Stream contract: negative is fatal. mbedTLS treats any negative other * than its own WANT_* sentinels as a transport error and aborts. */ @@ -599,6 +642,7 @@ TEST(SolidSyslogMbedTlsStream, BioRecvReturnsFatalWhenTransportFails) } TEST(SolidSyslogMbedTlsStream, OpenSetsAuthmodeRequired) + { SolidSyslogStream_Open(handle, addr); @@ -608,6 +652,7 @@ TEST(SolidSyslogMbedTlsStream, OpenSetsAuthmodeRequired) } TEST(SolidSyslogMbedTlsStream, OpenWiresCaChainFromConfigAndNullCrl) + { /* Use a non-null marker pointer; the fake captures it without dereferencing. */ static mbedtls_x509_crt caChainMarker; @@ -622,6 +667,7 @@ TEST(SolidSyslogMbedTlsStream, OpenWiresCaChainFromConfigAndNullCrl) } TEST(SolidSyslogMbedTlsStream, OpenWiresRngFromConfigUsingCtrDrbgRandom) + { static mbedtls_ctr_drbg_context rngMarker; config.Rng = &rngMarker; @@ -635,6 +681,7 @@ TEST(SolidSyslogMbedTlsStream, OpenWiresRngFromConfigUsingCtrDrbgRandom) } TEST(SolidSyslogMbedTlsStream, OpenSetsHostnameWhenServerNameProvided) + { config.ServerName = "syslog.example.com"; ReCreateHandleWithUpdatedConfig(); @@ -646,6 +693,7 @@ TEST(SolidSyslogMbedTlsStream, OpenSetsHostnameWhenServerNameProvided) } TEST(SolidSyslogMbedTlsStream, OpenSkipsHostnameWhenServerNameIsNull) + { /* setup() left config.ServerName at NULL. */ SolidSyslogStream_Open(handle, addr); @@ -661,6 +709,7 @@ TEST(SolidSyslogMbedTlsStream, OpenSkipsHostnameWhenServerNameIsNull) * ------------------------------------------------------------------------- */ TEST(SolidSyslogMbedTlsStream, OpenWiresOwnCertWhenClientCertAndKeyProvided) + { static mbedtls_x509_crt clientCertMarker; static mbedtls_pk_context clientKeyMarker; @@ -676,6 +725,7 @@ TEST(SolidSyslogMbedTlsStream, OpenWiresOwnCertWhenClientCertAndKeyProvided) } TEST(SolidSyslogMbedTlsStream, OpenSkipsOwnCertWhenClientCertChainIsNull) + { /* Key provided, cert NULL — caller hasn't fully opted in to mTLS, so * the adapter must not tell mbedTLS anything. setup() leaves @@ -689,6 +739,7 @@ TEST(SolidSyslogMbedTlsStream, OpenSkipsOwnCertWhenClientCertChainIsNull) } TEST(SolidSyslogMbedTlsStream, OpenSkipsOwnCertWhenClientKeyIsNull) + { /* Cert provided, key NULL — still incomplete; same skip. */ static mbedtls_x509_crt clientCertMarker; diff --git a/Tests/MbedTlsIntegration/MbedTlsTestCert.c b/Tests/MbedTlsIntegration/MbedTlsTestCert.c index a964eb38..b46d6123 100644 --- a/Tests/MbedTlsIntegration/MbedTlsTestCert.c +++ b/Tests/MbedTlsIntegration/MbedTlsTestCert.c @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -12,6 +11,7 @@ #include #include "SafeString.h" +#include "mbedtls/md.h" enum { diff --git a/Tests/MbedTlsIntegration/MbedTlsTestServer.c b/Tests/MbedTlsIntegration/MbedTlsTestServer.c index 484560ca..afb69ac0 100644 --- a/Tests/MbedTlsIntegration/MbedTlsTestServer.c +++ b/Tests/MbedTlsIntegration/MbedTlsTestServer.c @@ -9,8 +9,10 @@ #include #include #include +#include #include "MbedTlsTestCert.h" +#include "mbedtls/pk.h" struct MbedTlsTestServer { diff --git a/Tests/MbedTlsIntegration/SocketStream.c b/Tests/MbedTlsIntegration/SocketStream.c index 3621bbc1..4365a263 100644 --- a/Tests/MbedTlsIntegration/SocketStream.c +++ b/Tests/MbedTlsIntegration/SocketStream.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "SolidSyslogStream.h" #include "SolidSyslogStreamDefinition.h" diff --git a/Tests/MbedTlsIntegration/SolidSyslogMbedTlsStreamIntegrationTest.cpp b/Tests/MbedTlsIntegration/SolidSyslogMbedTlsStreamIntegrationTest.cpp index 60c9bbfd..4eb64f57 100644 --- a/Tests/MbedTlsIntegration/SolidSyslogMbedTlsStreamIntegrationTest.cpp +++ b/Tests/MbedTlsIntegration/SolidSyslogMbedTlsStreamIntegrationTest.cpp @@ -4,11 +4,9 @@ extern "C" { #include #include -#include #include #include #include -#include #include "MbedTlsTestCert.h" #include "MbedTlsTestServer.h" @@ -154,6 +152,7 @@ TEST_GROUP(SolidSyslogMbedTlsStreamIntegration) // clang-format on TEST(SolidSyslogMbedTlsStreamIntegration, HandshakeSucceedsWhenServerCertSignedByTrustedCaAndHostnameMatches) + { struct SolidSyslogStream* transport = StartServerWithCert(&serverCert); struct SolidSyslogMbedTlsStreamConfig config = BuildBaseConfig(transport); @@ -169,6 +168,7 @@ TEST(SolidSyslogMbedTlsStreamIntegration, HandshakeSucceedsWhenServerCertSignedB } TEST(SolidSyslogMbedTlsStreamIntegration, HandshakeFailsWhenServerCertSignedByUntrustedCa) + { /* Trust an unrelated CA: we hand the *client* a different CA chain than * the one that signed the server's cert, so the chain validation fails. */ @@ -191,6 +191,7 @@ TEST(SolidSyslogMbedTlsStreamIntegration, HandshakeFailsWhenServerCertSignedByUn } TEST(SolidSyslogMbedTlsStreamIntegration, HandshakeFailsWhenServerNameDoesNotMatchCert) + { struct SolidSyslogStream* transport = StartServerWithCert(&serverCert); struct SolidSyslogMbedTlsStreamConfig config = BuildBaseConfig(transport); @@ -203,6 +204,7 @@ TEST(SolidSyslogMbedTlsStreamIntegration, HandshakeFailsWhenServerNameDoesNotMat } TEST(SolidSyslogMbedTlsStreamIntegration, MutualTlsHandshakeSucceedsWithClientCertSignedByTrustedCa) + { /* Build per-test mTLS material: a client CA + a leaf cert signed by it. * Server is told to require + verify client certs against this CA. */ @@ -233,6 +235,7 @@ TEST(SolidSyslogMbedTlsStreamIntegration, MutualTlsHandshakeSucceedsWithClientCe } TEST(SolidSyslogMbedTlsStreamIntegration, MutualTlsHandshakeRejectedWhenClientSendsNoCert) + { /* Server requires a client cert but the integrator hasn't opted in to * mTLS — ClientCertChain / ClientKey are NULL. Server-side verify must @@ -255,6 +258,7 @@ TEST(SolidSyslogMbedTlsStreamIntegration, MutualTlsHandshakeRejectedWhenClientSe } TEST(SolidSyslogMbedTlsStreamIntegration, MutualTlsHandshakeRejectedWhenClientCertSignedByUntrustedCa) + { /* Client cert is signed by a CA the server doesn't trust. Server-side * chain validation fails and Open returns false on the client. */ @@ -291,6 +295,7 @@ TEST(SolidSyslogMbedTlsStreamIntegration, MutualTlsHandshakeRejectedWhenClientCe } TEST(SolidSyslogMbedTlsStreamIntegration, BinaryLinksAgainstRealLibMbedTls) + { /* mbedtls_version_get_number() is a constant, side-effect-free symbol * present in every mbedTLS build — a successful link plus a return diff --git a/Tests/Support/FreeRtosFakes/Interface/FreeRTOSIPConfig.h b/Tests/Support/FreeRtosFakes/Interface/FreeRTOSIPConfig.h index 61f2a2d2..b998fb92 100644 --- a/Tests/Support/FreeRtosFakes/Interface/FreeRTOSIPConfig.h +++ b/Tests/Support/FreeRtosFakes/Interface/FreeRTOSIPConfig.h @@ -1,6 +1,7 @@ #ifndef FREERTOS_IP_CONFIG_H #define FREERTOS_IP_CONFIG_H +#include "FreeRTOS.h" // NOLINTBEGIN(cppcoreguidelines-macro-usage) -- FreeRTOS-Plus-TCP API requires these to be #defines /* Host-suitable FreeRTOS-Plus-TCP config for unit-test fakes. The IP stack diff --git a/Tests/Support/FreeRtosFakes/Interface/FreeRtosDnsFake.h b/Tests/Support/FreeRtosFakes/Interface/FreeRtosDnsFake.h index d6a7e605..3c48608c 100644 --- a/Tests/Support/FreeRtosFakes/Interface/FreeRtosDnsFake.h +++ b/Tests/Support/FreeRtosFakes/Interface/FreeRtosDnsFake.h @@ -1,12 +1,10 @@ #ifndef FREERTOSDNSFAKE_H #define FREERTOSDNSFAKE_H -#include "ExternC.h" - #include +#include "ExternC.h" #include "FreeRTOS.h" -#include "FreeRTOS_Sockets.h" EXTERN_C_BEGIN diff --git a/Tests/Support/FreeRtosFakes/Interface/FreeRtosSemaphoreFake.h b/Tests/Support/FreeRtosFakes/Interface/FreeRtosSemaphoreFake.h index 42898834..2ae32c87 100644 --- a/Tests/Support/FreeRtosFakes/Interface/FreeRtosSemaphoreFake.h +++ b/Tests/Support/FreeRtosFakes/Interface/FreeRtosSemaphoreFake.h @@ -2,9 +2,7 @@ #define FREERTOSSEMAPHOREFAKE_H #include "ExternC.h" - #include "FreeRTOS.h" -#include "semphr.h" EXTERN_C_BEGIN diff --git a/Tests/Support/FreeRtosFakes/Interface/FreeRtosSocketsFake.h b/Tests/Support/FreeRtosFakes/Interface/FreeRtosSocketsFake.h index f92e7140..6c6607c8 100644 --- a/Tests/Support/FreeRtosFakes/Interface/FreeRtosSocketsFake.h +++ b/Tests/Support/FreeRtosFakes/Interface/FreeRtosSocketsFake.h @@ -1,11 +1,12 @@ #ifndef FREERTOSSOCKETSFAKE_H #define FREERTOSSOCKETSFAKE_H -#include "ExternC.h" - #include #include +#include +#include +#include "ExternC.h" #include "FreeRTOS.h" #include "FreeRTOS_Sockets.h" diff --git a/Tests/Support/FreeRtosFakes/Interface/FreeRtosTaskFake.h b/Tests/Support/FreeRtosFakes/Interface/FreeRtosTaskFake.h index 7144f39e..7f68f460 100644 --- a/Tests/Support/FreeRtosFakes/Interface/FreeRtosTaskFake.h +++ b/Tests/Support/FreeRtosFakes/Interface/FreeRtosTaskFake.h @@ -2,9 +2,7 @@ #define FREERTOSTASKFAKE_H #include "ExternC.h" - #include "FreeRTOS.h" -#include "task.h" EXTERN_C_BEGIN diff --git a/Tests/Support/FreeRtosFakes/Interface/portmacro.h b/Tests/Support/FreeRtosFakes/Interface/portmacro.h index a8d4933d..97448162 100644 --- a/Tests/Support/FreeRtosFakes/Interface/portmacro.h +++ b/Tests/Support/FreeRtosFakes/Interface/portmacro.h @@ -19,7 +19,6 @@ */ #include -#include /* Kernel scalar types. BaseType_t / UBaseType_t are typedef'd to long / * unsigned long so they match the host compiler's long width (64-bit on x86_64 diff --git a/Tests/Support/FreeRtosFakes/Source/FreeRtosArpFake.c b/Tests/Support/FreeRtosFakes/Source/FreeRtosArpFake.c index 9aa0416c..599eca56 100644 --- a/Tests/Support/FreeRtosFakes/Source/FreeRtosArpFake.c +++ b/Tests/Support/FreeRtosFakes/Source/FreeRtosArpFake.c @@ -1,6 +1,7 @@ #include "FreeRtosArpFake.h" #include "FreeRTOS_ARP.h" +#include "FreeRTOS.h" static unsigned isIpInArpCacheCallCount = 0; static uint32_t lastIsIpInArpCacheArg = 0; diff --git a/Tests/Support/FreeRtosFakes/Source/FreeRtosDnsFake.c b/Tests/Support/FreeRtosFakes/Source/FreeRtosDnsFake.c index 84979025..5c631dfa 100644 --- a/Tests/Support/FreeRtosFakes/Source/FreeRtosDnsFake.c +++ b/Tests/Support/FreeRtosFakes/Source/FreeRtosDnsFake.c @@ -7,8 +7,10 @@ #include "FreeRTOS_DNS.h" #include "FreeRTOS_IP.h" - #include "SafeString.h" +#include "FreeRTOS.h" +#include "FreeRTOS_IPv4.h" +#include "FreeRTOS_Sockets.h" static unsigned getAddrInfoCallCount = 0; static unsigned freeAddrInfoCallCount = 0; diff --git a/Tests/Support/FreeRtosFakes/Source/FreeRtosSemaphoreFake.c b/Tests/Support/FreeRtosFakes/Source/FreeRtosSemaphoreFake.c index 8247a8a8..88e65d4b 100644 --- a/Tests/Support/FreeRtosFakes/Source/FreeRtosSemaphoreFake.c +++ b/Tests/Support/FreeRtosFakes/Source/FreeRtosSemaphoreFake.c @@ -1,5 +1,11 @@ #include "FreeRtosSemaphoreFake.h" +#include + +#include "FreeRTOS.h" +#include "queue.h" +#include "semphr.h" + static unsigned createMutexStaticCallCount = 0; static unsigned semaphoreTakeCallCount = 0; static unsigned semaphoreGiveCallCount = 0; diff --git a/Tests/Support/FreeRtosFakes/Source/FreeRtosSocketsFake.c b/Tests/Support/FreeRtosFakes/Source/FreeRtosSocketsFake.c index 763efec6..bcb24610 100644 --- a/Tests/Support/FreeRtosFakes/Source/FreeRtosSocketsFake.c +++ b/Tests/Support/FreeRtosFakes/Source/FreeRtosSocketsFake.c @@ -2,6 +2,8 @@ #include "FreeRtosSocketsFake.h" +#include "FreeRTOS.h" + static unsigned socketCallCount = 0; static BaseType_t lastSocketDomain = 0; static BaseType_t lastSocketType = 0; diff --git a/Tests/Support/FreeRtosFakes/Source/FreeRtosTaskFake.c b/Tests/Support/FreeRtosFakes/Source/FreeRtosTaskFake.c index e4875813..7d192e68 100644 --- a/Tests/Support/FreeRtosFakes/Source/FreeRtosTaskFake.c +++ b/Tests/Support/FreeRtosFakes/Source/FreeRtosTaskFake.c @@ -1,5 +1,7 @@ #include "FreeRtosTaskFake.h" +#include "task.h" + static unsigned vTaskDelayCallCount = 0; static TickType_t lastVTaskDelayTicks = 0; static TickType_t tickCount = 0; diff --git a/Tests/Support/LwipFakes/Interface/arch/cc.h b/Tests/Support/LwipFakes/Interface/arch/cc.h index f4928160..6f854651 100644 --- a/Tests/Support/LwipFakes/Interface/arch/cc.h +++ b/Tests/Support/LwipFakes/Interface/arch/cc.h @@ -6,9 +6,6 @@ #ifndef LWIP_ARCH_CC_H #define LWIP_ARCH_CC_H -#include -#include - // NOLINTBEGIN(cppcoreguidelines-macro-usage,bugprone-macro-parentheses) -- lwIP API requires these to be #defines #define LWIP_PLATFORM_DIAG(x) \ do \ diff --git a/Tests/Support/MbedTlsFake.c b/Tests/Support/MbedTlsFake.c index 5c1042a6..5d4847ee 100644 --- a/Tests/Support/MbedTlsFake.c +++ b/Tests/Support/MbedTlsFake.c @@ -4,6 +4,10 @@ #include #include +#include "mbedtls/pk.h" +#include "mbedtls/x509_crl.h" +#include "mbedtls/x509_crt.h" + /* ------------------------------------------------------------------------- * Captured state — one section per mbedTLS API call. Tests read these via * accessors below; production reaches libmbedtls through the link-interposed From 85efed3f3517ca49e1c23ba0468fce503b84cbdf Mon Sep 17 00:00:00 2001 From: David Cozens Date: Tue, 26 May 2026 19:53:56 +0100 Subject: [PATCH 07/12] =?UTF-8?q?feat:=20S24.11=20scripts/misra=5Frenumber?= =?UTF-8?q?.py=20=E2=80=94=20rejuggle=20suppression=20line=20numbers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a refactor or IWYU pass shifts source lines, the line-anchored entries in misra_suppressions.txt go stale and the cppcheck-misra gate fails on findings it has always passed — just at a new line. The clerical fix-up (re-edit the suppressions file by hand) has been a recurring tax on pre-PR time. scripts/misra_renumber.py automates it. Two passes: 1. Audit against unsuppressed findings (cppcheck --addon=misra without the suppressions list). For each (rule, file) pair where the suppression entries match the findings in count but not in lines, remap ascending-by-ascending — handles bulk refactors that touch many sites in one file. 2. Residual failures with suppressions in place. cppcheck dedupes per location, so when 11.2 and 11.3 both fire at the same line and only 11.2 is reported, pass 1 can't see 11.3. Pass 2 runs cppcheck WITH the suppressions list, picks up the residual failures, and updates single-entry (rule, file) suppressions. Conservative on ambiguity — bails on (rule, file) pairs whose suppression count and finding count diverge, so a real new finding or deleted suppression doesn't get silently mis-mapped. This commit also applies the tool's output to misra_suppressions.txt for the three shifts the IWYU apply in the previous commits introduced: misra-c2012-11.2:.../SolidSyslogPlusTcpAddress.c :9 -> :10 misra-c2012-11.3:.../SolidSyslogPlusTcpAddress.c :9 -> :10 misra-c2012-11.3:.../SolidSyslogPlusTcpDatagram.c :47 -> :49 cppcheck-misra now passes locally with the updated suppressions list. --- misra_suppressions.txt | 6 +- scripts/misra_renumber.py | 266 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 269 insertions(+), 3 deletions(-) create mode 100755 scripts/misra_renumber.py diff --git a/misra_suppressions.txt b/misra_suppressions.txt index 050fa7e1..c28469d4 100644 --- a/misra_suppressions.txt +++ b/misra_suppressions.txt @@ -12,7 +12,7 @@ # D.002 — Rules 11.2 / 11.3 / 11.5: vtable downcasts + Formatter # See docs/misra-deviations.md#d002 misra-c2012-11.2:Core/Interface/SolidSyslogFormatter.h:30 -misra-c2012-11.2:Platform/PlusTcp/Source/SolidSyslogPlusTcpAddress.c:9 +misra-c2012-11.2:Platform/PlusTcp/Source/SolidSyslogPlusTcpAddress.c:10 misra-c2012-11.2:Platform/PlusTcp/Source/SolidSyslogPlusTcpAddressPrivate.h:20 misra-c2012-11.2:Platform/PlusTcp/Source/SolidSyslogPlusTcpAddressPrivate.h:28 misra-c2012-11.2:Platform/Posix/Source/SolidSyslogPosixAddress.c:10 @@ -35,12 +35,12 @@ misra-c2012-11.3:Core/Source/SolidSyslogTimeQualitySd.c:59 misra-c2012-11.3:Core/Source/SolidSyslogUdpSender.c:101 misra-c2012-11.3:Platform/Atomics/Source/SolidSyslogStdAtomicCounter.c:25 misra-c2012-11.3:Platform/FatFs/Source/SolidSyslogFatFsFile.c:45 -misra-c2012-11.3:Platform/PlusTcp/Source/SolidSyslogPlusTcpAddress.c:9 +misra-c2012-11.3:Platform/PlusTcp/Source/SolidSyslogPlusTcpAddress.c:10 misra-c2012-11.3:Platform/PlusTcp/Source/SolidSyslogPlusTcpAddressPrivate.h:20 misra-c2012-11.3:Platform/PlusTcp/Source/SolidSyslogPlusTcpAddressPrivate.h:28 misra-c2012-11.3:Platform/PlusTcp/Source/SolidSyslogPlusTcpAddressStatic.c:34 misra-c2012-11.3:Platform/PlusTcp/Source/SolidSyslogPlusTcpAddressStatic.c:54 -misra-c2012-11.3:Platform/PlusTcp/Source/SolidSyslogPlusTcpDatagram.c:47 +misra-c2012-11.3:Platform/PlusTcp/Source/SolidSyslogPlusTcpDatagram.c:49 misra-c2012-11.3:Platform/FreeRtos/Source/SolidSyslogFreeRtosMutex.c:40 misra-c2012-11.3:Platform/PlusTcp/Source/SolidSyslogPlusTcpResolver.c:44 misra-c2012-11.3:Platform/PlusTcp/Source/SolidSyslogPlusTcpTcpStream.c:114 diff --git a/scripts/misra_renumber.py b/scripts/misra_renumber.py new file mode 100755 index 00000000..9f72bb22 --- /dev/null +++ b/scripts/misra_renumber.py @@ -0,0 +1,266 @@ +#!/usr/bin/env python3 +"""Rejuggle stale line numbers in misra_suppressions.txt. + +Production-code edits that shift line numbers (typical: an include reorder +from IWYU, a refactor that adds a helper) leave entries in +misra_suppressions.txt pointing at the wrong source line. The cppcheck-misra +gate then fails on the same finding it has always passed — just at a new +line — and the human has to re-do clerical work. + +This script automates the clerical bit. It runs the CI's cppcheck-misra +invocation and, for each (rule, file) pair where misra_suppressions.txt +has stale lines and the report shows the same finding at different lines, +proposes (or applies) the renumbering. + +Usage: + scripts/misra_renumber.py # show proposed updates + scripts/misra_renumber.py --apply # write back updated suppressions + +Exit code: + 0 no actionable updates + 1 proposed updates exist (default 'check' mode) — re-run with --apply + 2 ambiguous mismatch needing human attention + +Algorithm (two passes): + Pass 1 — run cppcheck WITHOUT --suppressions-list and audit. For each + (rule, file) pair where the suppression entries match the findings + in count but not in lines, remap ascending-by-ascending. This catches + bulk line drifts from refactors that touch many sites in one file. + + Pass 2 — run cppcheck WITH --suppressions-list to see which + suppressions are still failing. cppcheck dedupes findings per + location: if 11.2 and 11.3 both fire on the same line and the 11.2 + suppression is correct (or absent), the 11.3 finding gets shadowed in + pass 1's view. Pass 2's residual failures resolve those cases — for + each failure (rule, file, line) we look for a suppression with the + same (rule, file) and update its line if there's exactly one match. + +Conservatism: + - Pass 1: only renumber when the (rule, file) pair has the same + NUMBER of suppression entries as findings. Mismatched counts mean + a real finding was added or removed; the script bails on that + pair so a human looks at it. + - Pass 2: only renumber when the (rule, file) pair has exactly one + suppression entry. Multiple-entry cases (e.g. several casts in one + file all needing different new lines) need pass 1 to handle them. + - The cppcheck command list must match .github/workflows/ci.yml's + cppcheck-misra step. If the CI lane changes its arguments, this + script needs the same update — flagged as a deviation from + single-source-of-truth (acceptable for a one-off helper). +""" + +import argparse +import re +import subprocess +import sys +import xml.etree.ElementTree as ET +from collections import defaultdict +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parent.parent +SUPPRESSIONS = REPO_ROOT / "misra_suppressions.txt" + +# Mirror of .github/workflows/ci.yml `Run cppcheck-misra` step minus the +# --suppressions-list flag (we want to see every finding, including the +# ones a current suppression is silencing). +CPPCHECK_CMD = [ + "cppcheck", + "--addon=misra", + "--inline-suppr", + "--std=c11", + "-ICore/Interface", + "-IPlatform/Atomics/Interface", + "-IPlatform/Posix/Interface", + "-IPlatform/Windows/Interface", + "-IPlatform/OpenSsl/Interface", + "-IPlatform/MbedTls/Interface", + "-IPlatform/FreeRtos/Interface", + "-IPlatform/PlusTcp/Interface", + "-IPlatform/FatFs/Interface", + "--xml", + "--xml-version=2", + "Core/Source/", + "Platform/Atomics/Source/", + "Platform/Posix/Source/", + "Platform/Windows/Source/", + "Platform/OpenSsl/Source/", + "Platform/MbedTls/Source/", + "Platform/FreeRtos/Source/", + "Platform/PlusTcp/Source/", + "Platform/FatFs/Source/", +] + +SUPPRESSION_LINE = re.compile(r"^(misra-c2012-[0-9.]+):([^:]+):(\d+)\s*$") + + +def run_cppcheck(with_suppressions=False): + """Run cppcheck-misra and return XML stderr as a string.""" + cmd = list(CPPCHECK_CMD) + if with_suppressions: + cmd.insert(1, f"--suppressions-list={SUPPRESSIONS}") + proc = subprocess.run( + cmd, cwd=REPO_ROOT, capture_output=True, text=True, check=False + ) + return proc.stderr + + +def findings_by_rule_file(xml_text): + """Parse cppcheck XML; return dict (rule, file) -> sorted list of lines.""" + root = ET.fromstring(xml_text) + by_pair = defaultdict(list) + for err in root.findall(".//error"): + rule = err.get("id", "") + if not rule.startswith("misra-c2012-"): + continue + loc = err.find("location") + if loc is None: + continue + file_path = loc.get("file", "") + line = int(loc.get("line", "0")) + by_pair[(rule, file_path)].append(line) + return {pair: sorted(lines) for pair, lines in by_pair.items()} + + +def parse_suppressions(): + """Return list of (raw_line, rule, file, line_or_None). Comments/blanks + keep line_or_None=None and are passed through untouched on rewrite.""" + parsed = [] + for raw in SUPPRESSIONS.read_text().splitlines(keepends=True): + stripped = raw.strip() + if not stripped or stripped.startswith("#"): + parsed.append((raw, None, None, None)) + continue + match = SUPPRESSION_LINE.match(stripped) + if match: + rule, path, line = match.group(1), match.group(2), int(match.group(3)) + parsed.append((raw, rule, path, line)) + else: + parsed.append((raw, None, None, None)) + return parsed + + +def propose_updates(parsed, findings): + """For each (rule, file) group, if counts match but line sets differ, + propose remapping ascending-by-ascending. Returns dict + (index_in_parsed -> new_line_number) and a list of ambiguous pairs.""" + suppressions_by_pair = defaultdict(list) # (rule, file) -> [index_in_parsed] + for i, (_, rule, path, _) in enumerate(parsed): + if rule is not None: + suppressions_by_pair[(rule, path)].append(i) + + updates = {} + ambiguous = [] + for pair, indices in suppressions_by_pair.items(): + old_lines = sorted(parsed[i][3] for i in indices) + new_lines = findings.get(pair, []) + if old_lines == new_lines: + continue + if len(old_lines) != len(new_lines): + ambiguous.append((pair, old_lines, new_lines)) + continue + # Same count, different lines: map ascending-by-ascending. + indices_sorted = sorted(indices, key=lambda i: parsed[i][3]) + for i, new_line in zip(indices_sorted, new_lines): + old_line = parsed[i][3] + if new_line != old_line: + updates[i] = new_line + return updates, ambiguous + + +def apply_updates(parsed, updates): + """Rewrite misra_suppressions.txt with the proposed updates applied.""" + lines = [] + for i, (raw, rule, path, old_line) in enumerate(parsed): + if i in updates: + new_line = updates[i] + replacement = f"{rule}:{path}:{new_line}" + # Preserve trailing whitespace / newline from the original line. + trailing = raw[raw.rstrip().__len__():] + lines.append(replacement + trailing) + else: + lines.append(raw) + SUPPRESSIONS.write_text("".join(lines)) + + +def propose_pass2_updates(parsed, residual): + """Pass 2: for each residual (rule, file, line) failure, find the lone + suppression entry with the same (rule, file) and propose updating its line. + Skips (rule, file) pairs that have multiple suppression entries.""" + suppressions_by_pair = defaultdict(list) + for i, (_, rule, path, _) in enumerate(parsed): + if rule is not None: + suppressions_by_pair[(rule, path)].append(i) + + updates = {} + ambiguous = [] + for pair, new_lines in residual.items(): + indices = suppressions_by_pair.get(pair, []) + if len(indices) == 0: + ambiguous.append((pair, [], new_lines)) + continue + if len(indices) != 1 or len(new_lines) != 1: + ambiguous.append( + (pair, sorted(parsed[i][3] for i in indices), new_lines) + ) + continue + i = indices[0] + if parsed[i][3] != new_lines[0]: + updates[i] = new_lines[0] + return updates, ambiguous + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--apply", action="store_true", help="Write the proposed updates back." + ) + args = parser.parse_args() + + # Pass 1: full audit against unsuppressed findings. + findings = findings_by_rule_file(run_cppcheck(with_suppressions=False)) + parsed = parse_suppressions() + updates, ambiguous = propose_updates(parsed, findings) + + # Pass 2: residual failures with current suppressions applied — handles + # cppcheck's per-location dedup that hides one rule behind another. + if args.apply and updates: + apply_updates(parsed, updates) + parsed = parse_suppressions() + residual = findings_by_rule_file(run_cppcheck(with_suppressions=True)) + pass2_updates, pass2_ambiguous = propose_pass2_updates(parsed, residual) + + for pair, old, new in ambiguous + pass2_ambiguous: + rule, path = pair + print(f"AMBIGUOUS {rule}:{path} old={old} new={new}", file=sys.stderr) + + all_updates = list(sorted(updates.items())) + if args.apply: + if updates: + print(f"Applied {len(updates)} pass-1 update(s).") + all_updates = list(sorted(pass2_updates.items())) + + for i, new_line in all_updates: + _, rule, path, old_line = parsed[i] + print(f" {rule}:{path}:{old_line} -> {new_line}") + + if args.apply: + if pass2_updates: + apply_updates(parsed, pass2_updates) + print(f"Applied {len(pass2_updates)} pass-2 update(s) to " + f"{SUPPRESSIONS.relative_to(REPO_ROOT)}.") + return 0 if not (ambiguous or pass2_ambiguous) else 2 + + total = len(updates) + len(pass2_updates) + if total == 0: + if ambiguous or pass2_ambiguous: + print("No simple renumbers proposed; ambiguous pairs need human review.") + return 2 + print("No stale suppression lines found.") + return 0 + + print(f"\nRun with --apply to write {total} update(s).") + return 1 if not (ambiguous or pass2_ambiguous) else 2 + + +if __name__ == "__main__": + sys.exit(main()) From 668afeafcbcdab8fa67aee6d16a2673bd3f427a8 Mon Sep 17 00:00:00 2001 From: David Cozens Date: Tue, 26 May 2026 19:54:47 +0100 Subject: [PATCH 08/12] chore: S24.11 silence clang-tidy on Bdd CmsdkUart register macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors the suppression already in place on the host-side fake (Tests/FreeRtos/CmsdkUartFake.c) — same NOLINTBEGIN/END bracket, same rationale: register-offset, control-bit, and status-bit macros that mirror the ARM CMSDK UART hardware datasheet keep their #define form so a future reader can grep against vendor docs. Surfaced by the new analyze-tidy-freertos-plustcp lane on the cross- compiled BDD target build. Audit of inline cppcheck-suppress / NOLINT comments tier-wide is scheduled as a separate story under E10 (not S24.11). --- Bdd/Targets/FreeRtos/Common/CmsdkUart.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Bdd/Targets/FreeRtos/Common/CmsdkUart.c b/Bdd/Targets/FreeRtos/Common/CmsdkUart.c index a3074b03..2597c8d9 100644 --- a/Bdd/Targets/FreeRtos/Common/CmsdkUart.c +++ b/Bdd/Targets/FreeRtos/Common/CmsdkUart.c @@ -3,6 +3,14 @@ #include #include +// The CMSDK UART register-offset, control-bit and status-bit macros below +// mirror the ARM CMSDK UART hardware datasheet exactly, plus the integer- +// valued YIELD_MILLISECONDS yield interval. The macro form is intentional — +// a future reader can grep `DATA_OFFSET` against vendor docs and land on +// these definitions verbatim. Converting them to enums would obscure that +// mapping, so we keep them as #defines. Mirrors the suppression in +// Tests/FreeRtos/CmsdkUartFake.c. +// NOLINTBEGIN(cppcoreguidelines-macro-to-enum,modernize-macro-to-enum) #define DATA_OFFSET 0x000U #define STATE_OFFSET 0x004U #define CTRL_OFFSET 0x008U @@ -15,6 +23,7 @@ #define RX_FULL_BIT 0x02U #define YIELD_MILLISECONDS 1 +// NOLINTEND(cppcoreguidelines-macro-to-enum,modernize-macro-to-enum) static const CmsdkUartMemoryAccess* memoryAccess = NULL; static uintptr_t base = 0U; From c039b822af5fbef1346fe07c32d45a9b9eb84dbb Mon Sep 17 00:00:00 2001 From: David Cozens Date: Tue, 26 May 2026 19:54:47 +0100 Subject: [PATCH 09/12] fix: S24.11 drop dead `LONG previous = 0` init in WindowsAtomicCounter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The unconditional first iteration of the CAS loop assigns `previous` via InterlockedCompareExchange before reading it, so the initial 0 is never used. Moves the declaration into the loop body, which both satisfies cppcheck's unreadVariable rule and limits the variable's scope to where it's actually live. Pre-existing finding sitting silently in the cppcheck-report.xml artifact across many prior CI runs — S24.11 promoted reading the whole cppcheck output to a habit, which surfaced it. --- Platform/Windows/Source/SolidSyslogWindowsAtomicCounter.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Platform/Windows/Source/SolidSyslogWindowsAtomicCounter.c b/Platform/Windows/Source/SolidSyslogWindowsAtomicCounter.c index 9af53b67..f45f4fd7 100644 --- a/Platform/Windows/Source/SolidSyslogWindowsAtomicCounter.c +++ b/Platform/Windows/Source/SolidSyslogWindowsAtomicCounter.c @@ -46,11 +46,10 @@ static uint32_t WindowsAtomicCounter_Increment(struct SolidSyslogAtomicCounter* struct SolidSyslogWindowsAtomicCounter* self = WindowsAtomicCounter_SelfFromBase(base); LONG current = InterlockedCompareExchange(&self->Value, 0, 0); LONG next = 0; - LONG previous = 0; do { next = ((uint32_t) current >= SOLIDSYSLOG_SEQUENCE_ID_MAX) ? 1 : (current + 1); - previous = InterlockedCompareExchange(&self->Value, next, current); + LONG previous = InterlockedCompareExchange(&self->Value, next, current); if (previous == current) { break; From 6a319191fec474e1ac1e929c283e0385c7df1cf3 Mon Sep 17 00:00:00 2001 From: David Cozens Date: Tue, 26 May 2026 20:06:40 +0100 Subject: [PATCH 10/12] chore: S24.11 accept Mbed TLS struct names in MbedTls clang-tidy scope MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SolidSyslogMbedTlsStreamConfig API takes integrator-supplied handles of upstream Mbed TLS types — `mbedtls_ctr_drbg_context`, `mbedtls_x509_crt`, `mbedtls_pk_context` — see project_mbedtls_di_handles. The Interface/ header forward-declares those structs to keep public consumers free of mbedTLS includes, which trips the project's StructCase + StructPrefix rules (snake_case `mbedtls_*` is neither CamelCase nor SolidSyslog-prefixed). Renaming is impossible — they're third-party. Adds Platform/MbedTls/.clang-tidy with a StructIgnoredRegexp override that re-states the root whitelist plus the mbedtls_ namespace. The config applies to both Interface/ and Source/ via clang-tidy's walk-up-the-tree resolution. Surfaced by analyze-tidy-freertos-plustcp; verified locally with `cmake --preset tidy && cmake --build`. clang-tidy CheckOption values do not merge across the hierarchy (latest-encountered wins), so the platform-level file restates the root pattern verbatim. Re-stating beats relying on regex-OR with the root, which would be ambiguous to read at the override site. --- Platform/MbedTls/.clang-tidy | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 Platform/MbedTls/.clang-tidy diff --git a/Platform/MbedTls/.clang-tidy b/Platform/MbedTls/.clang-tidy new file mode 100644 index 00000000..e6461154 --- /dev/null +++ b/Platform/MbedTls/.clang-tidy @@ -0,0 +1,22 @@ +--- +# Pragmatic tier per docs/NAMING.md — Platform/MbedTls. +# +# Inherits the root .clang-tidy. SolidSyslogMbedTlsStream.h (Interface/) and +# SolidSyslogMbedTlsStream.c (Source/) both refer to upstream Mbed TLS struct +# types — `mbedtls_ctr_drbg_context`, `mbedtls_x509_crt`, `mbedtls_pk_context`, +# `mbedtls_ssl_context`, … — because the integrator-supplied handles in +# SolidSyslogMbedTlsStreamConfig are those types (see +# project_mbedtls_di_handles). The upstream names use snake_case prefixed with +# `mbedtls_`, which violates the project's StructCase + StructPrefix rule, but +# renaming is impossible (they're third-party). Extend StructIgnoredRegexp +# locally so the rule exempts the mbedTLS namespace. +# +# clang-tidy CheckOption values do not merge across the hierarchy — the +# latest-encountered value wins. The pattern below therefore re-states the +# root .clang-tidy whitelist plus the mbedtls_ namespace. Keep in sync. +InheritParentConfig: true +CheckOptions: + - { + key: readability-identifier-naming.StructIgnoredRegexp, + value: "^(SolidSyslog|EscapedContext|BlockPresence|OpenHandle|mbedtls_[a-z0-9_]+)$", + } From 9e6956072fa8e9774aca0ba8c31f6dffa04624f2 Mon Sep 17 00:00:00 2001 From: David Cozens Date: Tue, 26 May 2026 20:11:59 +0100 Subject: [PATCH 11/12] docs: S24.11 tiered pre-PR check budget MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codifies the agreement reached at the start of S24.11: full CI suite takes 10-15 minutes; running every lane locally before pushing burns human time for confidence we mostly already have from the previous push's CI result. Split into: - Tier A (per-commit, ~30-60 s): debug build + tests for matching preset - Tier B (pre-push, ~3-5 min, scoped): IWYU + format reflow + misra_renumber - CI (everything else): tidy, sanitize, coverage, Windows, BDD, integration Tier B runs only when production source changed — tests-only, docs-only or CMake-only diffs push immediately and let CI run. The path-gating is the engineer's judgement; the doc spells out the rule. The doc also captures the two non-obvious mechanics from this session: - `analyze-iwyu-freertos-plustcp` lane needs CMAKE_C_COMPILER=clang-19 override (the gcc base image carries clang-19 with no `clang` alias) - IWYU output should be read whole, not head/tail — easy to miss findings that sit in the middle of a long report Updates CLAUDE.md "Before raising a PR" section to point at the new doc rather than recite the (now-stale) preset list. --- CLAUDE.md | 15 +++++--- docs/local-checks.md | 85 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 5 deletions(-) create mode 100644 docs/local-checks.md diff --git a/CLAUDE.md b/CLAUDE.md index f36c01a5..88547d1d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -17,11 +17,16 @@ All changes to `main` must go via a pull request — direct pushes are blocked b **Merge strategy:** Squash merge only. This keeps a linear history on `main` and means the PR title becomes the single commit message — so the PR title must follow Conventional Commits format (see below). -**Before raising a PR — run locally:** -- build-linux-gcc, build-linux-clang, sanitize-linux-gcc, coverage-linux-gcc, analyze-tidy, analyze-cppcheck, analyze-format -- Windows, BDD, and OpenSSL integration jobs are CI's responsibility — running them locally would slow development too much -- Commits on the branch can be informal (work-in-progress messages are fine) -- The PR title is what matters — it becomes the permanent commit message on `main` +**Before raising a PR — see [docs/local-checks.md](docs/local-checks.md)** for the +full tiered pre-PR check budget. One-line summary: + +- Per-commit: `debug` build + tests for the matching preset (~30–60 s) +- Pre-push (only when production source changed): IWYU + `clang-format` reflow + `scripts/misra_renumber.py` (~3–5 min) +- Everything else (`tidy`, `sanitize`, `coverage`, Windows, BDD, integration, FreeRTOS host/cross) — CI's job; do not run locally +- If CI surfaces a finding you missed, fix in another commit on the same branch rather than re-running every lane locally + +Commits on the branch can be informal (WIP messages are fine). The PR title is +what matters — it becomes the permanent commit message on `main` on squash merge. **Branch protection rules (configured on GitHub):** - Direct pushes to `main` are blocked diff --git a/docs/local-checks.md b/docs/local-checks.md new file mode 100644 index 00000000..b3fcc11f --- /dev/null +++ b/docs/local-checks.md @@ -0,0 +1,85 @@ +# Pre-PR local checks + +The full CI suite takes ~10–15 minutes wall-clock. Running every check +locally before pushing trades human time for one extra reviewer-confidence +margin we mostly don't need. This document defines what to run locally and +when, so the wait stays under a few minutes and CI catches the rest. + +## Tiers + +| Tier | When | What | Wall-clock | +|---|---|---|---| +| **A** — fast feedback | Every commit on the branch | `cmake --build --preset debug --target junit` for whatever preset matches the diff (gcc / clang / freertos-host) | ~30–60 s | +| **B** — pre-push | First push to the branch and any push that changes production source | A + `iwyu` + format reflowed includes + `misra_renumber.py` | ~3–5 min | +| **C** — none | — | — | — | +| **CI** — everything else | After push | `tidy`, `sanitize`, `coverage`, Windows, BDD, integration, FreeRTOS host/cross, IWYU on cpputest-clang, MISRA on cpputest | runs in parallel; results in ~10–15 min | + +Format-on-save in the editor handles formatting per-edit, so no separate +`analyze-format` step locally. If you skip an editor with format-on-save, +add a `clang-format -i` sweep over touched files to Tier A. + +## Path-gating Tier B + +Tier B does the expensive bits (IWYU, MISRA), so scope it to what changed: + +- **Touched only `Tests/`, `Bdd/Targets/`, `docs/`, `cmake/`, or `*.md`** — + skip Tier B entirely. Push and let CI run. +- **Touched any `Core/Source/`, `Platform/*/Source/`, or public-header file** + — run Tier B against the container that compiles that tree (see below). + +## Running Tier B + +The IWYU lane runs against clang's parser. cpputest-freertos and cpputest +both ship the same IWYU binary now (clang_19 branch), so pick the container +whose env vars include the trees you touched: + +### Touched only Core / Posix / Windows / OpenSSL trees + +```bash +# Inside the gcc devcontainer (Ctrl+Shift+B-able), or: +docker compose -f .devcontainer/docker-compose.yml run --rm clang \ + bash -c 'cmake --preset iwyu && cmake --build --preset iwyu --target iwyu' +``` + +Read the full IWYU output — head/tail truncation hides findings. After +fixing IWYU's complaints, format the file (`clang-format -i path`) — the +include reorder can put forward-decls on lines that need reflowing. + +### Touched any FreeRTOS / Plus-TCP / lwIP / MbedTLS / FatFs tree + +```bash +docker compose -f .devcontainer/docker-compose.yml run --rm freertos-host \ + bash -c 'cmake --preset iwyu \ + -DCMAKE_C_COMPILER=clang-19 -DCMAKE_CXX_COMPILER=clang++-19 \ + && cmake --build --preset iwyu --target iwyu' +``` + +The `-DCMAKE_C_COMPILER=clang-19` overrides match the +`analyze-iwyu-freertos-plustcp` CI lane — the gcc base image carries +`clang-19` but no `clang` alternative. + +### MISRA — after IWYU, fix line-number drift + +When IWYU's include reorder shifts production lines, `misra_suppressions.txt` +entries go stale. Fix in one step: + +```bash +# In any container that has cppcheck (all of them do): +scripts/misra_renumber.py # show proposed renumbers +scripts/misra_renumber.py --apply # write back updated suppressions +``` + +The script bails on genuine new findings (mismatched counts per +rule+file) — those need manual review. See the script's docstring. + +## What CI runs and you should not run locally + +- `tidy`, `sanitize`, `coverage` — minutes each, all gated by CI +- Windows MSVC + BDD + integration — depend on tools you may not have +- BDD-linux-syslog-ng, BDD-windows-otel, BDD-freertos-qemu — heavy + multi-container stacks +- The cpputest-clang IWYU lane and the cpputest-freertos IWYU/tidy lanes — + CI runs both; you only need one locally for the trees you touched + +If CI surfaces a finding you missed locally, fix in another commit on the +same branch — cheaper than running every CI lane on every push. From e5689da8d7964199abf96344db5029e29948ef38 Mon Sep 17 00:00:00 2001 From: David Cozens Date: Tue, 26 May 2026 20:15:26 +0100 Subject: [PATCH 12/12] docs: update DEVLOG for S24.11 freertos analyzer coverage --- DEVLOG.md | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/DEVLOG.md b/DEVLOG.md index f51d22a8..705f6f85 100644 --- a/DEVLOG.md +++ b/DEVLOG.md @@ -12323,3 +12323,101 @@ MISRA rule — different category, doesn't set precedent. clang container) but needs re-applying via the IDE before the next Rebuild Container. Format pipeline going forward: filter `git diff --name-only` for `.c|.cpp|.h|.hpp$` extensions. + +## 2026-05-26 — S24.11: freertos-aware analyze-tidy + analyze-iwyu, misra_renumber tool, tiered pre-PR doc + +### Decisions + +- **Extend analyzer CI rather than add new gates.** S24.11's scope was + narrowed to "wire the existing `tidy` / `iwyu` presets through the + freertos image" — single `analyze-tidy-freertos-plustcp` + + `analyze-iwyu-freertos-plustcp` pair. Avoids per-backend duplication + (LWIP deferred to S28.06). + +- **Single image bump cascades through the chain.** Built IWYU 0.23 from + the `clang_19` upstream branch into `cpputest` (gcc base) so it + matches the version in `cpputest-clang`. `cpputest-freertos` chains + `FROM cpputest`, so the rebase inherits IWYU automatically. New SHAs: + `cpputest:sha-2c7b76b`, `cpputest-freertos:sha-a0c1c0a`, + `cpputest-freertos-cross:sha-a0c1c0a`. + +- **FreeRTOS umbrella-header convention codified in `scripts/iwyu_filter.py`.** + IWYU sees specific types in `task.h` / `portmacro.h` / + `FreeRTOS_DNS_Globals.h` and suggests including them directly, + removing `FreeRTOS.h` / `FreeRTOS_IP.h`. Both umbrellas must stay first + to set up the build environment from `FreeRTOSConfig.h`. Filter now + drops removals of those umbrellas and adds of the private sub-headers. + +- **Tidy treatment: A2 (per-file NOLINTBEGIN/END) over A1 (tier-wide).** + David's preference shifted mid-session — first picked A2 for + `Tests/FreeRtos/CmsdkUartFake.c` register macros, then flagged + dislike of inline suppression generally. We kept the A2 form for + consistency with the BDD-side `Bdd/Targets/FreeRtos/Common/CmsdkUart.c` + (mirrors the test fake) and queued S24.12 (#461) as the tier-wide + audit follow-up. + +- **`bugprone-easily-swappable-parameters` disabled tier-wide in + `Tests/.clang-tidy`.** The rule fires too often for legitimate + MMIO-helper / equality-assertion patterns; real arg-swap bugs in tests + get caught by the test itself. + +- **MbedTls struct names exempted at `Platform/MbedTls/.clang-tidy`.** + Forward declarations of `mbedtls_ctr_drbg_context`, `mbedtls_x509_crt`, + `mbedtls_pk_context` violate `StructCase` + `StructPrefix` rules but + renaming is impossible. New per-platform-component `.clang-tidy` with + a `StructIgnoredRegexp` override (re-stating the root whitelist plus + `mbedtls_[a-z0-9_]+`). clang-tidy CheckOption values don't merge + across the hierarchy — the override file restates the root pattern + verbatim. + +- **`scripts/misra_renumber.py` shipped early.** Originally planned as the + S24.11 closing commit; brought forward when the IWYU apply shifted + line numbers in `Platform/PlusTcp/Source/SolidSyslogPlusTcpAddress.c` + and `SolidSyslogPlusTcpDatagram.c`, breaking + `misra_suppressions.txt`. Two-pass design: pass 1 audits against + unsuppressed findings; pass 2 catches per-location dedup + shadowing (cppcheck only reports the first rule fired at a line, so + a 11.3 hiding behind a 11.2 surfaces only when run with the existing + suppressions list). + +- **Tiered pre-PR check budget written to `docs/local-checks.md`.** + Replaces the old "run 8 presets locally" guidance (30+ min) with: + per-commit `debug` build + tests (~30–60s); pre-push IWYU + format + + `misra_renumber.py` only when production source changed (~3–5 min); + CI owns everything else. + +### Deferred + +- **S24.12 (#461) — inline-suppression audit.** Promote tests-only + rules to tier-level, fix where possible, move MISRA cases to + `misra_suppressions.txt`, keep inline only as last resort. David's + open question: how many non-MISRA cppcheck inline suppressions + remain after that cleanup? + +- **LWIP analyzer lane.** S28.06 (when the lwIP backend is feature- + complete) is where adding an `analyze-tidy-freertos-lwip` / + `analyze-iwyu-freertos-lwip` lane belongs naturally. Today the + freertos image carries both backends, but only PLUSTCP is wired into + the new lanes. + +- **Removing the `-DCMAKE_C_COMPILER=clang-19` override.** The + `cpputest` gcc base image carries `clang-19` with no `clang` + alternative, so the freertos-aware IWYU lane spells the version + explicitly. Adding `update-alternatives --install /usr/bin/clang + clang /usr/bin/clang-19 100` to the gcc Dockerfile in a future image + bump would let the override drop. + +### Open questions + +- The dead `cppcheck-suppress` entries that `misra_renumber.py`'s pass-1 + flagged (`new=[]`) — are they stale (rule no longer fires at all) or + shadowed by inline suppressions? S24.12 should resolve case by case. + +- `Platform/Windows/Source/SolidSyslogWindowsAtomicCounter.c`'s + `unreadVariable` finding had been sitting silently in + `cppcheck-report.xml` for many CI runs (the XML-report step doesn't + carry `--error-exitcode=1`). Should we tighten that step to + `--error-exitcode=1` so quiet style findings can't accumulate? Risk: + every untriaged style finding becomes a hard gate. Worth E24 + discussion. +