From a3600d94a4e6c24d0f7ac11b23e0bf1273597928 Mon Sep 17 00:00:00 2001 From: Michael Lynch Date: Fri, 24 Apr 2026 23:42:36 +0000 Subject: [PATCH] Rewrite advert data tests with raw payloads --- src/helpers/AdvertDataHelpers.cpp | 5 +- test/test_utils/test_advert_data.cpp | 124 ++++++++++++++++++++++----- 2 files changed, 106 insertions(+), 23 deletions(-) diff --git a/src/helpers/AdvertDataHelpers.cpp b/src/helpers/AdvertDataHelpers.cpp index 0e05620ec..0802cac95 100644 --- a/src/helpers/AdvertDataHelpers.cpp +++ b/src/helpers/AdvertDataHelpers.cpp @@ -37,6 +37,9 @@ if (_flags & ADV_LATLON_MASK) { memcpy(&_lat, &app_data[i], 4); i += 4; memcpy(&_lon, &app_data[i], 4); i += 4; + if (_lat < -90000000 || _lat > 90000000 || _lon < -180000000 || _lon > 180000000) { + return; + } } if (_flags & ADV_FEAT1_MASK) { memcpy(&_extra1, &app_data[i], 2); i += 2; @@ -84,4 +87,4 @@ void AdvertTimeHelper::formatRelativeTimeDiff(char dest[], int32_t seconds_from_ } } } -} \ No newline at end of file +} diff --git a/test/test_utils/test_advert_data.cpp b/test/test_utils/test_advert_data.cpp index 9645a516e..0de8e92aa 100644 --- a/test/test_utils/test_advert_data.cpp +++ b/test/test_utils/test_advert_data.cpp @@ -1,15 +1,47 @@ +#include +#include + #include #include "helpers/AdvertDataHelpers.h" namespace { +void WriteU8(uint8_t* dest, size_t* offset, uint8_t value) { + dest[(*offset)++] = value; +} + +void WriteI32Le(uint8_t* dest, size_t* offset, int32_t value) { + const uint32_t raw = static_cast(value); + dest[(*offset)++] = static_cast(raw & 0xFF); + dest[(*offset)++] = static_cast((raw >> 8) & 0xFF); + dest[(*offset)++] = static_cast((raw >> 16) & 0xFF); + dest[(*offset)++] = static_cast((raw >> 24) & 0xFF); +} + +void WriteBytes(uint8_t* dest, size_t* offset, const uint8_t* bytes, size_t length) { + for (size_t i = 0; i < length; ++i) { + dest[*offset + i] = bytes[i]; + } + *offset += length; +} + +template +void WriteStringLiteral(uint8_t* dest, size_t* offset, const char (&value)[N]) { + static_assert(N > 0, "string literal must include a null terminator"); + WriteBytes(dest, offset, reinterpret_cast(value), N - 1); +} + TEST(AdvertData, RoundTripsNameOnly) { uint8_t app_data[MAX_ADVERT_DATA_SIZE] = {}; - AdvertDataBuilder builder(ADV_TYPE_CHAT, "alice"); + size_t offset = 0; + + // flags/type byte: chat advert with a trailing name field. + WriteU8(app_data, &offset, ADV_TYPE_CHAT | ADV_NAME_MASK); + // name field: raw bytes for "alice", consuming the rest of app_data. + WriteStringLiteral(app_data, &offset, "alice"); - uint8_t len = builder.encodeTo(app_data); - AdvertDataParser parser(app_data, len); + AdvertDataParser parser(app_data, offset); ASSERT_TRUE(parser.isValid()); EXPECT_EQ(ADV_TYPE_CHAT, parser.getType()); @@ -20,10 +52,18 @@ TEST(AdvertData, RoundTripsNameOnly) { TEST(AdvertData, RoundTripsNameAndCoordinates) { uint8_t app_data[MAX_ADVERT_DATA_SIZE] = {}; - AdvertDataBuilder builder(ADV_TYPE_REPEATER, "node", 37.7749, -122.4194); + size_t offset = 0; - uint8_t len = builder.encodeTo(app_data); - AdvertDataParser parser(app_data, len); + // flags/type byte: repeater advert with lat/lon followed by a name. + WriteU8(app_data, &offset, ADV_TYPE_REPEATER | ADV_LATLON_MASK | ADV_NAME_MASK); + // latitude field: signed little-endian microdegrees for 37.7749. + WriteI32Le(app_data, &offset, 37774900); + // longitude field: signed little-endian microdegrees for -122.4194. + WriteI32Le(app_data, &offset, -122419400); + // name field: raw bytes for "node" after the coordinate fields. + WriteStringLiteral(app_data, &offset, "node"); + + AdvertDataParser parser(app_data, offset); ASSERT_TRUE(parser.isValid()); EXPECT_EQ(ADV_TYPE_REPEATER, parser.getType()); @@ -38,10 +78,18 @@ TEST(AdvertData, RoundTripsNameAndCoordinates) { TEST(AdvertData, RoundTripsCoordinateExtremes) { uint8_t app_data[MAX_ADVERT_DATA_SIZE] = {}; - AdvertDataBuilder builder(ADV_TYPE_SENSOR, "edge", -90.0, 180.0); + size_t offset = 0; + + // flags/type byte: sensor advert with both location fields and a name. + WriteU8(app_data, &offset, ADV_TYPE_SENSOR | ADV_LATLON_MASK | ADV_NAME_MASK); + // latitude field: minimum supported latitude, -90.000000 degrees. + WriteI32Le(app_data, &offset, -90000000); + // longitude field: maximum supported longitude, 180.000000 degrees. + WriteI32Le(app_data, &offset, 180000000); + // name field: raw bytes for "edge". + WriteStringLiteral(app_data, &offset, "edge"); - uint8_t len = builder.encodeTo(app_data); - AdvertDataParser parser(app_data, len); + AdvertDataParser parser(app_data, offset); ASSERT_TRUE(parser.isValid()); EXPECT_TRUE(parser.hasLatLon()); @@ -51,40 +99,72 @@ TEST(AdvertData, RoundTripsCoordinateExtremes) { TEST(AdvertData, RejectsLongitudeOutsideValidRange) { uint8_t app_data[MAX_ADVERT_DATA_SIZE] = {}; - AdvertDataBuilder builder(ADV_TYPE_CHAT, "node", 37.7749, 180.000001); + size_t offset = 0; - uint8_t len = builder.encodeTo(app_data); - AdvertDataParser parser(app_data, len); + // flags/type byte: chat advert with location and name fields present. + WriteU8(app_data, &offset, ADV_TYPE_CHAT | ADV_LATLON_MASK | ADV_NAME_MASK); + // latitude field: valid latitude so the failure comes from longitude. + WriteI32Le(app_data, &offset, 37774900); + // longitude field: one microdegree above +180.0, which is invalid. + WriteI32Le(app_data, &offset, 180000001); + // name field: parser should reject before the trailing name matters. + WriteStringLiteral(app_data, &offset, "node"); + + AdvertDataParser parser(app_data, offset); EXPECT_FALSE(parser.isValid()); } TEST(AdvertData, RejectsLongitudeBelowValidRange) { uint8_t app_data[MAX_ADVERT_DATA_SIZE] = {}; - AdvertDataBuilder builder(ADV_TYPE_CHAT, "node", 37.7749, -180.000001); + size_t offset = 0; + + // flags/type byte: chat advert with location and name fields present. + WriteU8(app_data, &offset, ADV_TYPE_CHAT | ADV_LATLON_MASK | ADV_NAME_MASK); + // latitude field: valid latitude so the failure comes from longitude. + WriteI32Le(app_data, &offset, 37774900); + // longitude field: one microdegree below -180.0, which is invalid. + WriteI32Le(app_data, &offset, -180000001); + // name field: included to keep the payload shape consistent. + WriteStringLiteral(app_data, &offset, "node"); - uint8_t len = builder.encodeTo(app_data); - AdvertDataParser parser(app_data, len); + AdvertDataParser parser(app_data, offset); EXPECT_FALSE(parser.isValid()); } TEST(AdvertData, RejectsLatitudeOutsideValidRange) { uint8_t app_data[MAX_ADVERT_DATA_SIZE] = {}; - AdvertDataBuilder builder(ADV_TYPE_CHAT, "node", 90.000001, -122.4194); + size_t offset = 0; - uint8_t len = builder.encodeTo(app_data); - AdvertDataParser parser(app_data, len); + // flags/type byte: chat advert with location and name fields present. + WriteU8(app_data, &offset, ADV_TYPE_CHAT | ADV_LATLON_MASK | ADV_NAME_MASK); + // latitude field: one microdegree above +90.0, which is invalid. + WriteI32Le(app_data, &offset, 90000001); + // longitude field: valid longitude so the failure comes from latitude. + WriteI32Le(app_data, &offset, -122419400); + // name field: included to keep the payload shape consistent. + WriteStringLiteral(app_data, &offset, "node"); + + AdvertDataParser parser(app_data, offset); EXPECT_FALSE(parser.isValid()); } TEST(AdvertData, RejectsLatitudeBelowValidRange) { uint8_t app_data[MAX_ADVERT_DATA_SIZE] = {}; - AdvertDataBuilder builder(ADV_TYPE_CHAT, "node", -90.000001, -122.4194); - - uint8_t len = builder.encodeTo(app_data); - AdvertDataParser parser(app_data, len); + size_t offset = 0; + + // flags/type byte: chat advert with location and name fields present. + WriteU8(app_data, &offset, ADV_TYPE_CHAT | ADV_LATLON_MASK | ADV_NAME_MASK); + // latitude field: one microdegree below -90.0, which is invalid. + WriteI32Le(app_data, &offset, -90000001); + // longitude field: valid longitude so the failure comes from latitude. + WriteI32Le(app_data, &offset, -122419400); + // name field: included to keep the payload shape consistent. + WriteStringLiteral(app_data, &offset, "node"); + + AdvertDataParser parser(app_data, offset); EXPECT_FALSE(parser.isValid()); }