fix(test): assert the rejected packet is released, not that malloc reuses its address

test_beginSending_oversizedPayloadAbortsSafely checked that the allocation
following beginSending()'s rejection path returned the same address as the
packet it released. That holds for the fixed-slot MemoryPool used on embedded
targets, but native builds bind packetPool to MemoryDynamic (plain malloc and
free), and the coverage env compiles with -fsanitize=address, whose quarantine
withholds a freed chunk from the next malloc. The address always differs, so
the test failed on every CI run that reached it, and the ASan abort also
tripped the suite attribution check with an unsourced coverage:test_radio case.

Check the invariant the test is named for instead: packetPool is tagged
"pktpool(live)" for memaudit, so comparing live bytes across the call proves
the rejected packet went back to the pool, independent of the allocator.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Ben MeadorsandClaude Opus 5 committed 2026-08-24 06:59:02 -05:00
1 parent ee48094ea8
commit 770875e08a
1 file changed
+20 -5
+20 -5
View File
@@ -3,6 +3,8 @@
#include "MeshService.h"
#include "RadioInterface.h"
#include "TestUtil.h"
#include "memory/MemAudit.h"
#include <cstring>
#include <unity.h>
#include "meshtastic/config.pb.h"
@@ -417,8 +419,21 @@ static void test_regionPresetMap_unsetCarriesUserprefsIntent()
#endif
}
// Live bytes packetPool reports under its memaudit tag, which tracks every alloc/release.
static int32_t livePacketPoolBytes()
{
memaudit::Tag rows[memaudit::kMaxTags];
size_t n = memaudit::snapshot(rows, memaudit::kMaxTags);
for (size_t i = 0; i < n; i++)
if (rows[i].tag && strcmp(rows[i].tag, "pktpool(live)") == 0)
return rows[i].bytes;
return 0;
}
static void test_beginSending_oversizedPayloadAbortsSafely()
{
const int32_t liveBefore = livePacketPoolBytes();
meshtastic_MeshPacket *p = packetPool.allocZeroed();
TEST_ASSERT_NOT_NULL(p);
p->from = 0x12345678;
@@ -434,11 +449,11 @@ static void test_beginSending_oversizedPayloadAbortsSafely()
TEST_ASSERT_EQUAL_UINT(0, result);
TEST_ASSERT_NULL(testRadio->getSendingPacket());
// Verify rejected packet was released to packetPool and its slot is reusable
meshtastic_MeshPacket *reallocated = packetPool.allocZeroed();
TEST_ASSERT_NOT_NULL(reallocated);
TEST_ASSERT_EQUAL_PTR(p, reallocated);
packetPool.release(reallocated);
// beginSending() owns the packet it rejects, so the pool's live byte count has to land back
// where it started. Do not assert the next alloc hands the same address back: on native
// packetPool is a malloc/free MemoryDynamic and the coverage build's ASan quarantine holds the
// freed chunk, so the address always differs.
TEST_ASSERT_EQUAL_INT32(liveBefore, livePacketPoolBytes());
}
void setUp(void)