Browse Source

Rewrite advert data tests with raw payloads

pull/2459/head
Michael Lynch 2 months ago
parent
commit
a3600d94a4
  1. 5
      src/helpers/AdvertDataHelpers.cpp
  2. 124
      test/test_utils/test_advert_data.cpp

5
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_
}
}
}
}
}

124
test/test_utils/test_advert_data.cpp

@ -1,15 +1,47 @@
#include <cstddef>
#include <cstdint>
#include <gtest/gtest.h>
#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<uint32_t>(value);
dest[(*offset)++] = static_cast<uint8_t>(raw & 0xFF);
dest[(*offset)++] = static_cast<uint8_t>((raw >> 8) & 0xFF);
dest[(*offset)++] = static_cast<uint8_t>((raw >> 16) & 0xFF);
dest[(*offset)++] = static_cast<uint8_t>((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 <size_t N>
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<const uint8_t*>(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());
}

Loading…
Cancel
Save