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
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.
- 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.
- 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
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.
Context
PR #171 added a defense-in-depth guard in
BQ27441::writeExtendedData:The check is correct — without it, an
offset = 31, length = 2write would spill past the 32-byte block boundary into the checksum / extended-command region — but there's currently no regression test for it, becausewriteExtendedDatais private and every public setter that wraps it uses fixed(offset, length)pairs that all stay inside one block:offsetlengthsetCapacitysetSoc1ThresholdssetSocfThresholdssetSociDeltaSo a regression that removes the guard wouldn't be caught by any current test.
Goal
Make the boundary guard testable without making
writeExtendedDatapart of the public driver API.Options
friendtest class — declarefriend class BQ27441BoundaryTestinBQ27441.hand have the test reach into the private method. Minimal driver-side change, but tightly couples a test name to the header.static bool isExtendedDataWriteInRange(uint16_t offset, uint16_t length)accessible at namespace scope (or as astaticmember). Test the helper directly without needing access to the writeExtendedData state machine.private:→protected:forwriteExtendedDataonly, then make aclass 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
offset = 31, length = 2,offset = 0, length = 33, and acceptsoffset = 31, length = 1/offset = 0, length = 32.make test-native.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.