Skip to content

test(bq27441): Add a test seam for the writeExtendedData block-boundary guard. #185

@nedseb

Description

@nedseb

Context

PR #171 added a defense-in-depth guard in BQ27441::writeExtendedData:

if (static_cast<uint16_t>(offset % 32) + length > 32) {
    return false;
}

The check is correct — without it, an offset = 31, length = 2 write would spill past the 32-byte block boundary into the checksum / extended-command region — but there's currently no regression test for it, because writeExtendedData is private and every public setter that wraps it uses fixed (offset, length) pairs that all stay inside one block:

Setter offset length
setCapacity 10 2
setSoc1Thresholds 0 2
setSocfThresholds 2 2
setSociDelta 26 1

So a regression that removes the guard wouldn't be caught by any current test.

Goal

Make the boundary guard testable without making writeExtendedData part of the public driver API.

Options

  1. friend test class — declare friend class BQ27441BoundaryTest in BQ27441.h and have the test reach into the private method. Minimal driver-side change, but tightly couples a test name to the header.
  2. Static helper extraction — pull the boundary check into a static bool isExtendedDataWriteInRange(uint16_t offset, uint16_t length) accessible at namespace scope (or as a static member). Test the helper directly without needing access to the writeExtendedData state machine.
  3. Protected + derived test hook — change private:protected: for writeExtendedData only, then make a class BQ27441TestHook : public BQ27441 { using BQ27441::writeExtendedData; }; accessible to tests. Keeps the public surface unchanged but exposes via inheritance.

Option (2) is the cleanest: no visibility shenanigans, the rule is a pure function with no side effects, and the test reads like TEST_ASSERT_FALSE(BQ27441::isExtendedDataWriteInRange(31, 2));.

Acceptance

  • Native test verifies the guard rejects offset = 31, length = 2, offset = 0, length = 33, and accepts offset = 31, length = 1 / offset = 0, length = 32.
  • Native test runs as part of make test-native.
  • No public driver API change beyond the (optionally exposed) helper.

Why this matters

The guard is defense-in-depth today — no current caller triggers it. But the helper is generic by design and the next driver / sketch that uses it directly (e.g. a tooling utility that calibrates a fresh BQ27441 from a CSV of (class_id, offset, value) triples) could very plausibly cross the boundary. Locking the guard with a test means a future refactor can't accidentally drop it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions