mirror of
https://github.com/mikecarper/MeshCore.git
synced 2026-09-02 13:13:45 +00:00
Append mqtt_neighbors_enabled(u8) + mqtt_neighbors_interval(u32) to the observer tail of MQTTPrefs. The layout is kept byte-identical to the flex neighbors build: the enable flag lands in the old struct's zeroed trailing padding (offset 2857) and the interval begins exactly at the former baseline (2860), so sizeof grows 2860 -> 2864 (net +4 bytes). offsetof static_asserts lock the layout so a mismatch fails the build. The codec now accepts three v1 payload sizes: register 2860 as a "pre-neighbors" Current payload so an in-lineage upgrade reads its existing /mqtt_prefs and defaults the neighbors tail (off / 24h). Because 2864 is the shared Current baseline, a /mqtt_prefs written by either the flex build or this firmware is interchangeable. Add the 12/24/336h interval constants, neighbors defaults, and a load-time interval clamp that keeps persisted values inside the signed-delta millis() scheduling window. Extend the host codec suite with a pre-neighbors migration case and neighbors round-trip coverage.
400 lines
17 KiB
C++
400 lines
17 KiB
C++
#include <gtest/gtest.h>
|
|
|
|
#include <cstring>
|
|
#include <vector>
|
|
|
|
#define WITH_MQTT_BRIDGE 1
|
|
#include "helpers/MQTTPrefsCodec.h"
|
|
|
|
namespace Codec = MQTTPrefsCodec;
|
|
|
|
namespace {
|
|
|
|
MQTTPrefs defaults() {
|
|
MQTTPrefs prefs = {};
|
|
prefs.mqtt_status_enabled = 1;
|
|
prefs.mqtt_packets_enabled = 1;
|
|
prefs.mqtt_tx_enabled = 2;
|
|
prefs.mqtt_rx_enabled = 1;
|
|
prefs.mqtt_status_interval = 300000;
|
|
prefs.wifi_power_save = 1;
|
|
for (int i = 0; i < MQTT_PREFS_SLOT_COUNT; ++i) {
|
|
strncpy(prefs.mqtt_slot_preset[i], "none", sizeof(prefs.mqtt_slot_preset[i]) - 1);
|
|
}
|
|
strncpy(prefs.snmp_community, "public", sizeof(prefs.snmp_community) - 1);
|
|
prefs.radio_watchdog_minutes = 5;
|
|
prefs.alert_wifi_minutes = 30;
|
|
prefs.alert_mqtt_minutes = 240;
|
|
prefs.alert_min_interval_min = 60;
|
|
return prefs;
|
|
}
|
|
|
|
void writeText(std::vector<uint8_t>* bytes, size_t offset, const char* value) {
|
|
const size_t length = strlen(value);
|
|
ASSERT_LE(offset + length, bytes->size());
|
|
memcpy(bytes->data() + offset, value, length);
|
|
}
|
|
|
|
void writeLe16(std::vector<uint8_t>* bytes, size_t offset, uint16_t value) {
|
|
ASSERT_LE(offset + 2, bytes->size());
|
|
(*bytes)[offset] = static_cast<uint8_t>(value & 0xff);
|
|
(*bytes)[offset + 1] = static_cast<uint8_t>(value >> 8);
|
|
}
|
|
|
|
void writeHeader(std::vector<uint8_t>* bytes, uint16_t version, uint16_t payload_len) {
|
|
ASSERT_GE(bytes->size(), sizeof(MQTTPrefsHeader));
|
|
(*bytes)[0] = MQTT_PREFS_MAGIC[0];
|
|
(*bytes)[1] = MQTT_PREFS_MAGIC[1];
|
|
(*bytes)[2] = MQTT_PREFS_MAGIC[2];
|
|
(*bytes)[3] = MQTT_PREFS_MAGIC[3];
|
|
writeLe16(bytes, 4, version);
|
|
writeLe16(bytes, 6, payload_len);
|
|
}
|
|
|
|
Codec::DecodePlan classify(const std::vector<uint8_t>& bytes) {
|
|
const size_t prefix_size = bytes.size() < sizeof(MQTTPrefsHeader)
|
|
? bytes.size() : sizeof(MQTTPrefsHeader);
|
|
return Codec::classify(bytes.data(), prefix_size, bytes.size());
|
|
}
|
|
|
|
void fillHighEntropy(std::vector<uint8_t>* bytes) {
|
|
uint32_t state = 0x89abcdef;
|
|
for (size_t i = 0; i < bytes->size(); ++i) {
|
|
state = state * 1664525u + 1013904223u;
|
|
// Keep every byte non-NUL so this is a useful corruption fixture rather
|
|
// than a sparse/default-like legacy file.
|
|
(*bytes)[i] = static_cast<uint8_t>((state >> 24) | 0x80);
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST(MQTTPrefsCodec, MigratesPostWifiPowerPreSlotFixture) {
|
|
// Frozen post-WiFi-power pre-slot offsets: status=44, ssid=48, power=144,
|
|
// timezone=145, server=178, port=242. Do not derive this fixture from structs.
|
|
std::vector<uint8_t> bytes(472, 0);
|
|
writeText(&bytes, 0, "legacy-node");
|
|
writeText(&bytes, 32, "YYZ");
|
|
bytes[40] = 1;
|
|
bytes[41] = 1;
|
|
bytes[43] = 2;
|
|
bytes[144] = 2;
|
|
writeText(&bytes, 145, "UTC");
|
|
writeText(&bytes, 178, "broker.example");
|
|
writeLe16(&bytes, 242, 1883);
|
|
writeText(&bytes, 244, "alice");
|
|
writeText(&bytes, 276, "secret");
|
|
bytes[340] = 1;
|
|
|
|
const Codec::DecodePlan plan = classify(bytes);
|
|
ASSERT_EQ(Codec::Source::LegacyPreSlot, plan.source);
|
|
ASSERT_TRUE(plan.rewrite_legacy);
|
|
ASSERT_FALSE(Codec::looksLikePreWifiPower(bytes.data(), bytes.size()));
|
|
ASSERT_TRUE(Codec::isPlausibleLegacy(plan.source, bytes.data(), bytes.size()));
|
|
|
|
OldMQTTPrefs old_prefs = {};
|
|
memcpy(&old_prefs, bytes.data(), sizeof(old_prefs));
|
|
MQTTPrefs prefs = defaults();
|
|
Codec::migratePreSlot(old_prefs, &prefs);
|
|
|
|
EXPECT_STREQ("legacy-node", prefs.mqtt_origin);
|
|
EXPECT_STREQ("analyzer-us", prefs.mqtt_slot_preset[0]);
|
|
EXPECT_STREQ("custom", prefs.mqtt_slot_preset[2]);
|
|
EXPECT_STREQ("broker.example", prefs.mqtt_slot_host[2]);
|
|
EXPECT_EQ(1883, prefs.mqtt_slot_port[2]);
|
|
EXPECT_STREQ("secret", prefs.mqtt_slot_password[2]);
|
|
}
|
|
|
|
TEST(MQTTPrefsCodec, MigratesPreWifiPowerFixtureWithConservativeHeuristic) {
|
|
// In the pre-WiFi-power variant timezone starts at 144 and server at 177;
|
|
// port still aligns at 242. A non-empty timezone makes the layout unambiguous.
|
|
std::vector<uint8_t> bytes(472, 0);
|
|
writeText(&bytes, 0, "pre-power");
|
|
writeText(&bytes, 144, "PST8PDT");
|
|
writeText(&bytes, 177, "old-broker");
|
|
writeLe16(&bytes, 242, 8883);
|
|
|
|
ASSERT_TRUE(Codec::looksLikePreWifiPower(bytes.data(), bytes.size()));
|
|
ASSERT_TRUE(Codec::isPlausibleLegacy(Codec::Source::LegacyPreSlot,
|
|
bytes.data(), bytes.size()));
|
|
PreWifiPowerOldMQTTPrefs old_prefs = {};
|
|
memcpy(&old_prefs, bytes.data(), sizeof(old_prefs));
|
|
MQTTPrefs prefs = defaults();
|
|
Codec::migratePreWifiPower(old_prefs, &prefs);
|
|
|
|
EXPECT_STREQ("PST8PDT", prefs.timezone_string);
|
|
EXPECT_STREQ("old-broker", prefs.mqtt_slot_host[2]);
|
|
EXPECT_EQ(8883, prefs.mqtt_slot_port[2]);
|
|
EXPECT_EQ(1, prefs.wifi_power_save); // retained current default; old layout had none
|
|
}
|
|
|
|
TEST(MQTTPrefsCodec, DetectsPreWifiPowerFixtureWithOnlyUtcOffsetConfigured) {
|
|
std::vector<uint8_t> bytes(472, 0);
|
|
bytes[176] = static_cast<uint8_t>(-8);
|
|
|
|
EXPECT_TRUE(Codec::looksLikePreWifiPower(bytes.data(), bytes.size()));
|
|
}
|
|
|
|
TEST(MQTTPrefsCodec, MigratesThreeSlotBaseAndExtendedFixtures) {
|
|
// Frozen 3-slot offsets: presets=178, hosts=250, ports=442, users=448,
|
|
// passwords=544, owner=736, tokens=1030, topics=1174. The base file has
|
|
// two tail-padding bytes, so its frozen size is 1032.
|
|
std::vector<uint8_t> base(1032, 0);
|
|
writeText(&base, 0, "three-base");
|
|
writeText(&base, 178, "meshmapper");
|
|
writeText(&base, 250, "three.example");
|
|
writeLe16(&base, 442, 1884);
|
|
const Codec::DecodePlan base_plan = classify(base);
|
|
ASSERT_EQ(Codec::Source::LegacyThreeSlotBase, base_plan.source);
|
|
ASSERT_TRUE(Codec::isPlausibleLegacy(base_plan.source, base.data(), base.size()));
|
|
ThreeSlotBaseMQTTPrefs old_base = {};
|
|
memcpy(&old_base, base.data(), sizeof(old_base));
|
|
MQTTPrefs base_prefs = defaults();
|
|
Codec::migrateThreeSlot(old_base, &base_prefs);
|
|
EXPECT_STREQ("three-base", base_prefs.mqtt_origin);
|
|
EXPECT_STREQ("meshmapper", base_prefs.mqtt_slot_preset[0]);
|
|
EXPECT_EQ(1884, base_prefs.mqtt_slot_port[0]);
|
|
EXPECT_STREQ("none", base_prefs.mqtt_slot_preset[3]);
|
|
|
|
std::vector<uint8_t> extended(1464, 0);
|
|
writeText(&extended, 0, "three-extended");
|
|
writeText(&extended, 178 + 24, "custom");
|
|
writeText(&extended, 250 + 64, "slot-two.example");
|
|
writeText(&extended, 1030 + 48, "token-two");
|
|
writeText(&extended, 1174 + 96, "custom/{type}");
|
|
const Codec::DecodePlan extended_plan = classify(extended);
|
|
ASSERT_EQ(Codec::Source::LegacyThreeSlot, extended_plan.source);
|
|
ASSERT_TRUE(Codec::isPlausibleLegacy(extended_plan.source, extended.data(), extended.size()));
|
|
ThreeSlotMQTTPrefs old_extended = {};
|
|
memcpy(&old_extended, extended.data(), sizeof(old_extended));
|
|
MQTTPrefs extended_prefs = defaults();
|
|
Codec::migrateThreeSlot(old_extended, &extended_prefs);
|
|
EXPECT_STREQ("custom", extended_prefs.mqtt_slot_preset[1]);
|
|
EXPECT_STREQ("token-two", extended_prefs.mqtt_slot_token[1]);
|
|
EXPECT_STREQ("custom/{type}", extended_prefs.mqtt_slot_topic[1]);
|
|
}
|
|
|
|
TEST(MQTTPrefsCodec, MigratesAllLegacySixSlotPrefixesWithoutClobberingDefaults) {
|
|
// Frozen 6-slot offsets: presets=178, hosts=322, ports=706, tokens=1588,
|
|
// topics=1876, audience=2452, rx=2836, ntp=2837.
|
|
for (const size_t size : {size_t(2452), size_t(2836), size_t(2840), size_t(2904)}) {
|
|
std::vector<uint8_t> bytes(size, 0);
|
|
writeText(&bytes, 0, "six-slot");
|
|
writeText(&bytes, 178 + 5 * 24, "custom");
|
|
writeText(&bytes, 322 + 5 * 64, "six.example");
|
|
writeText(&bytes, 1588 + 5 * 48, "token-six");
|
|
writeText(&bytes, 1876 + 5 * 96, "six/{type}");
|
|
if (size >= 2836) writeText(&bytes, 2452 + 5 * 64, "audience-six");
|
|
if (size >= 2840) bytes[2836] = 0;
|
|
if (size >= 2904) writeText(&bytes, 2837, "time.example");
|
|
|
|
const Codec::DecodePlan plan = classify(bytes);
|
|
const Codec::Source expected_source = size == 2452 ? Codec::Source::LegacySixSlotBase
|
|
: size == 2836 ? Codec::Source::LegacySixSlotAudience
|
|
: size == 2840 ? Codec::Source::LegacySixSlotAudienceRx
|
|
: Codec::Source::LegacySixSlot;
|
|
EXPECT_EQ(expected_source, plan.source) << size;
|
|
ASSERT_TRUE(plan.rewrite_legacy);
|
|
ASSERT_TRUE(Codec::isPlausibleLegacy(plan.source, bytes.data(), bytes.size())) << size;
|
|
Legacy6SlotMQTTPrefs old_prefs = {};
|
|
memcpy(&old_prefs, bytes.data(), bytes.size());
|
|
MQTTPrefs prefs = defaults();
|
|
Codec::migrateLegacySixSlot(old_prefs, plan.source, &prefs);
|
|
|
|
EXPECT_STREQ("custom", prefs.mqtt_slot_preset[5]);
|
|
EXPECT_STREQ("token-six", prefs.mqtt_slot_token[5]);
|
|
if (size < 2836) {
|
|
EXPECT_EQ('\0', prefs.mqtt_slot_audience[5][0]);
|
|
} else {
|
|
EXPECT_STREQ("audience-six", prefs.mqtt_slot_audience[5]);
|
|
}
|
|
if (size < 2840) {
|
|
EXPECT_EQ(1, prefs.mqtt_rx_enabled) << size;
|
|
} else {
|
|
EXPECT_EQ(0, prefs.mqtt_rx_enabled);
|
|
}
|
|
if (size < 2904) {
|
|
EXPECT_EQ('\0', prefs.mqtt_ntp_server[0]);
|
|
} else {
|
|
EXPECT_STREQ("time.example", prefs.mqtt_ntp_server);
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(MQTTPrefsCodec, CurrentVersionedPayloadRoundTripsExactly) {
|
|
MQTTPrefs source = defaults();
|
|
strncpy(source.mqtt_origin, "current-node", sizeof(source.mqtt_origin) - 1);
|
|
strncpy(source.mqtt_slot_password[2], "preserve-me", sizeof(source.mqtt_slot_password[2]) - 1);
|
|
strncpy(source.alert_region, "PNW", sizeof(source.alert_region) - 1);
|
|
source.mqtt_neighbors_enabled = 1;
|
|
source.mqtt_neighbors_interval = MQTT_NEIGHBORS_MAX_INTERVAL_MS;
|
|
std::vector<uint8_t> bytes(Codec::kEncodedSize);
|
|
ASSERT_EQ(Codec::kEncodedSize, Codec::encode(source, bytes.data(), bytes.size()));
|
|
|
|
const Codec::DecodePlan plan = classify(bytes);
|
|
ASSERT_EQ(Codec::Source::Current, plan.source);
|
|
ASSERT_FALSE(plan.preserve_file);
|
|
ASSERT_TRUE(plan.observer_fields_present);
|
|
MQTTPrefs loaded = defaults();
|
|
memcpy(&loaded, bytes.data() + sizeof(MQTTPrefsHeader), plan.payload_len);
|
|
EXPECT_EQ(0, memcmp(&source, &loaded, sizeof(source)));
|
|
}
|
|
|
|
TEST(MQTTPrefsCodec, CompatibleShortV1PayloadPreservesDefaultsBeyondObserverBoundary) {
|
|
MQTTPrefs source = defaults();
|
|
strncpy(source.mqtt_origin, "short-v1-node", sizeof(source.mqtt_origin) - 1);
|
|
strncpy(source.mqtt_ntp_server, "ntp.short.example", sizeof(source.mqtt_ntp_server) - 1);
|
|
source.snmp_enabled = 1;
|
|
strncpy(source.snmp_community, "do-not-copy", sizeof(source.snmp_community) - 1);
|
|
source.radio_watchdog_minutes = 99;
|
|
|
|
std::vector<uint8_t> bytes(sizeof(MQTTPrefsHeader) + Codec::kV1PreObserverPayloadSize, 0);
|
|
writeHeader(&bytes, MQTT_PREFS_VERSION,
|
|
static_cast<uint16_t>(Codec::kV1PreObserverPayloadSize));
|
|
memcpy(bytes.data() + sizeof(MQTTPrefsHeader), &source, Codec::kV1PreObserverPayloadSize);
|
|
|
|
const Codec::DecodePlan plan = classify(bytes);
|
|
ASSERT_EQ(Codec::Source::Current, plan.source);
|
|
ASSERT_EQ(Codec::kV1PreObserverPayloadSize, plan.payload_len);
|
|
ASSERT_FALSE(plan.preserve_file);
|
|
ASSERT_FALSE(plan.observer_fields_present);
|
|
|
|
MQTTPrefs loaded = defaults();
|
|
memcpy(&loaded, bytes.data() + sizeof(MQTTPrefsHeader), plan.payload_len);
|
|
EXPECT_STREQ("short-v1-node", loaded.mqtt_origin);
|
|
EXPECT_STREQ("ntp.short.example", loaded.mqtt_ntp_server);
|
|
EXPECT_EQ(0, loaded.snmp_enabled);
|
|
EXPECT_STREQ("public", loaded.snmp_community);
|
|
EXPECT_EQ(5, loaded.radio_watchdog_minutes);
|
|
}
|
|
|
|
TEST(MQTTPrefsCodec, PreNeighborsV1PayloadLoadsObserverFieldsAndDefaultsNeighborsTail) {
|
|
// A /mqtt_prefs written by observer/webconfig firmware before the neighbors
|
|
// tail existed: full observer fields, 2860-byte v1 payload. It must still load
|
|
// as Current (observer fields present) with the neighbors tail defaulted.
|
|
MQTTPrefs source = defaults();
|
|
strncpy(source.mqtt_origin, "pre-neighbors-node", sizeof(source.mqtt_origin) - 1);
|
|
strncpy(source.alert_region, "PNW", sizeof(source.alert_region) - 1);
|
|
source.snmp_enabled = 1;
|
|
source.alert_enabled = 1;
|
|
source.mqtt_neighbors_enabled = 0; // old struct's byte 2857 was zero padding
|
|
source.mqtt_neighbors_interval = 0x11223344; // must NOT survive a 2860-byte read
|
|
|
|
std::vector<uint8_t> bytes(sizeof(MQTTPrefsHeader) + Codec::kV1PreNeighborsPayloadSize, 0);
|
|
writeHeader(&bytes, MQTT_PREFS_VERSION,
|
|
static_cast<uint16_t>(Codec::kV1PreNeighborsPayloadSize));
|
|
memcpy(bytes.data() + sizeof(MQTTPrefsHeader), &source, Codec::kV1PreNeighborsPayloadSize);
|
|
|
|
const Codec::DecodePlan plan = classify(bytes);
|
|
ASSERT_EQ(Codec::Source::Current, plan.source);
|
|
ASSERT_EQ(Codec::kV1PreNeighborsPayloadSize, plan.payload_len);
|
|
ASSERT_FALSE(plan.preserve_file);
|
|
ASSERT_TRUE(plan.observer_fields_present);
|
|
|
|
MQTTPrefs loaded = defaults();
|
|
loaded.mqtt_neighbors_enabled = 1; // pretend stale
|
|
loaded.mqtt_neighbors_interval = MQTT_NEIGHBORS_DEFAULT_INTERVAL_MS; // caller's defaulted tail
|
|
memcpy(&loaded, bytes.data() + sizeof(MQTTPrefsHeader), plan.payload_len);
|
|
|
|
EXPECT_STREQ("pre-neighbors-node", loaded.mqtt_origin);
|
|
EXPECT_STREQ("PNW", loaded.alert_region);
|
|
EXPECT_EQ(1, loaded.snmp_enabled);
|
|
EXPECT_EQ(1, loaded.alert_enabled);
|
|
// Enable flag sits at offset 2857 (inside the 2860 read) -> takes the file's 0.
|
|
// Interval begins at 2860 (beyond the read) -> keeps the caller's default.
|
|
EXPECT_EQ(0u, loaded.mqtt_neighbors_enabled);
|
|
EXPECT_EQ(MQTT_NEIGHBORS_DEFAULT_INTERVAL_MS, loaded.mqtt_neighbors_interval);
|
|
}
|
|
|
|
TEST(MQTTPrefsCodec, CorruptOrShortVersionedInputsArePreserved) {
|
|
Codec::DecodePlan plan = Codec::classify(nullptr, 0, 0);
|
|
EXPECT_EQ(Codec::Source::Corrupt, plan.source);
|
|
EXPECT_TRUE(plan.preserve_file);
|
|
|
|
std::vector<uint8_t> partial_magic = {MQTT_PREFS_MAGIC[0], MQTT_PREFS_MAGIC[1], MQTT_PREFS_MAGIC[2]};
|
|
plan = classify(partial_magic);
|
|
EXPECT_EQ(Codec::Source::Corrupt, plan.source);
|
|
EXPECT_TRUE(plan.preserve_file);
|
|
|
|
std::vector<uint8_t> short_payload(sizeof(MQTTPrefsHeader) + 4, 0);
|
|
writeHeader(&short_payload, MQTT_PREFS_VERSION,
|
|
static_cast<uint16_t>(Codec::kV1BaselinePayloadSize - 1));
|
|
plan = classify(short_payload);
|
|
EXPECT_EQ(Codec::Source::Corrupt, plan.source);
|
|
EXPECT_TRUE(plan.preserve_file);
|
|
|
|
std::vector<uint8_t> declared_short_with_full_body(
|
|
sizeof(MQTTPrefsHeader) + Codec::kV1BaselinePayloadSize, 0);
|
|
writeHeader(&declared_short_with_full_body, MQTT_PREFS_VERSION,
|
|
static_cast<uint16_t>(Codec::kV1PreObserverPayloadSize));
|
|
plan = classify(declared_short_with_full_body);
|
|
EXPECT_EQ(Codec::Source::Corrupt, plan.source);
|
|
EXPECT_TRUE(plan.preserve_file);
|
|
|
|
std::vector<uint8_t> declared_full_with_short_body(
|
|
sizeof(MQTTPrefsHeader) + Codec::kV1PreObserverPayloadSize, 0);
|
|
writeHeader(&declared_full_with_short_body, MQTT_PREFS_VERSION,
|
|
static_cast<uint16_t>(Codec::kV1BaselinePayloadSize));
|
|
plan = classify(declared_full_with_short_body);
|
|
EXPECT_EQ(Codec::Source::Corrupt, plan.source);
|
|
EXPECT_TRUE(plan.preserve_file);
|
|
|
|
std::vector<uint8_t> truncated(sizeof(MQTTPrefsHeader) + Codec::kV1BaselinePayloadSize - 1, 0);
|
|
writeHeader(&truncated, MQTT_PREFS_VERSION,
|
|
static_cast<uint16_t>(Codec::kV1BaselinePayloadSize));
|
|
plan = classify(truncated);
|
|
EXPECT_EQ(Codec::Source::Corrupt, plan.source);
|
|
EXPECT_TRUE(plan.preserve_file);
|
|
|
|
std::vector<uint8_t> trailing(sizeof(MQTTPrefsHeader) + Codec::kV1BaselinePayloadSize + 1, 0);
|
|
writeHeader(&trailing, MQTT_PREFS_VERSION,
|
|
static_cast<uint16_t>(Codec::kV1BaselinePayloadSize));
|
|
plan = classify(trailing);
|
|
EXPECT_EQ(Codec::Source::Corrupt, plan.source);
|
|
EXPECT_TRUE(plan.preserve_file);
|
|
}
|
|
|
|
TEST(MQTTPrefsCodec, LegacyPlausibilityRejectsHighEntropyBytesAtEveryWhitelistedSize) {
|
|
// A headerless raw struct has no checksum, so this only reduces false
|
|
// migrations; it cannot prove that a plausible-looking file is authentic.
|
|
for (const size_t size : {size_t(472), size_t(1032), size_t(1464), size_t(2452),
|
|
size_t(2836), size_t(2840), size_t(2904)}) {
|
|
std::vector<uint8_t> bytes(size, 0);
|
|
fillHighEntropy(&bytes);
|
|
const Codec::DecodePlan plan = classify(bytes);
|
|
ASSERT_TRUE(plan.rewrite_legacy) << size;
|
|
EXPECT_FALSE(Codec::isPlausibleLegacy(plan.source, bytes.data(), bytes.size())) << size;
|
|
}
|
|
}
|
|
|
|
TEST(MQTTPrefsCodec, UnsupportedHeaderlessSizesArePreserved) {
|
|
// 3024 was produced briefly before versioning, but repository history says
|
|
// that raw observer-tail form was not shipped. Preserve it rather than guess.
|
|
for (const size_t size : {size_t(471), size_t(473), size_t(1465), size_t(2905), size_t(3024)}) {
|
|
std::vector<uint8_t> bytes(size, 0);
|
|
const Codec::DecodePlan plan = classify(bytes);
|
|
EXPECT_EQ(Codec::Source::Corrupt, plan.source) << size;
|
|
EXPECT_TRUE(plan.preserve_file) << size;
|
|
}
|
|
}
|
|
|
|
TEST(MQTTPrefsCodec, NewerAndSameVersionExtendedPayloadsAreHeld) {
|
|
std::vector<uint8_t> newer(sizeof(MQTTPrefsHeader), 0);
|
|
writeHeader(&newer, MQTT_PREFS_VERSION + 1, 0);
|
|
Codec::DecodePlan plan = classify(newer);
|
|
EXPECT_EQ(Codec::Source::UnsupportedVersion, plan.source);
|
|
EXPECT_TRUE(plan.preserve_file);
|
|
|
|
std::vector<uint8_t> extended(sizeof(MQTTPrefsHeader) + Codec::kV1BaselinePayloadSize + 1, 0);
|
|
writeHeader(&extended, MQTT_PREFS_VERSION,
|
|
static_cast<uint16_t>(Codec::kV1BaselinePayloadSize + 1));
|
|
plan = classify(extended);
|
|
EXPECT_EQ(Codec::Source::UnsupportedVersion, plan.source);
|
|
EXPECT_TRUE(plan.preserve_file);
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
return RUN_ALL_TESTS();
|
|
}
|