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..953b141c 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 @@ -471,10 +471,77 @@ 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: - image: ghcr.io/davidcozens/cpputest:sha-18f19e1 + image: ghcr.io/davidcozens/cpputest:sha-2c7b76b options: --user root env: GIT_CONFIG_COUNT: 1 @@ -492,7 +559,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 +924,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 +968,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 @@ -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 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; 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/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. + 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/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_]+)$", + } 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/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; 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..0de06731 100644 --- a/Tests/FreeRtos/CmsdkUartFake.c +++ b/Tests/FreeRtos/CmsdkUartFake.c @@ -1,7 +1,11 @@ #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 +// 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 +16,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; 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 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). | 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. 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/iwyu_filter.py b/scripts/iwyu_filter.py index 60e26c49..be990eea 100755 --- a/scripts/iwyu_filter.py +++ b/scripts/iwyu_filter.py @@ -35,6 +35,44 @@ 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: + +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 + 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 @@ -52,12 +90,32 @@ 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 +# 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 +145,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) 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())