mirror of
https://github.com/mikecarper/MeshCore.git
synced 2026-09-15 05:55:37 +00:00
407 lines
14 KiB
C++
407 lines
14 KiB
C++
#include <gtest/gtest.h>
|
|
|
|
#include <cstring>
|
|
|
|
#include "helpers/radiolib/RXPowerSaving.h"
|
|
#include "helpers/radiolib/RXPowerSavingCLI.h"
|
|
|
|
class FakeRxPowerSavingControl : public RxPowerSavingControl {
|
|
public:
|
|
bool accept = true;
|
|
bool set_called = false;
|
|
bool requested_enabled = false;
|
|
uint32_t requested_rx_us = 0;
|
|
uint32_t requested_sleep_us = 0;
|
|
bool rf_rx_supported = false;
|
|
bool rf_rx_disabled = false;
|
|
RxPowerSavingStatus status;
|
|
|
|
bool setRxPowerSaving(bool enabled, uint32_t rx_us, uint32_t sleep_us) override {
|
|
set_called = true;
|
|
requested_enabled = enabled;
|
|
requested_rx_us = rx_us;
|
|
requested_sleep_us = sleep_us;
|
|
return accept;
|
|
}
|
|
|
|
RxPowerSavingStatus getRxPowerSavingStatus() const override { return status; }
|
|
bool supportsRxPowerSavingRfRxDisable() const override { return rf_rx_supported; }
|
|
bool setRxPowerSavingRfRxDisabled(bool disabled) override {
|
|
if (!rf_rx_supported) return false;
|
|
rf_rx_disabled = disabled;
|
|
return true;
|
|
}
|
|
bool isRxPowerSavingRfRxDisabled() const override { return rf_rx_disabled; }
|
|
};
|
|
|
|
TEST(RxPowerSaving, DefaultsKeepRepeaterDisabledWithBalancedIntent) {
|
|
const RxPowerSavingConfig config;
|
|
|
|
EXPECT_EQ(config.enabled, 0);
|
|
EXPECT_EQ(config.level, RX_POWERSAVING_BALANCED_LEVEL);
|
|
EXPECT_EQ(config.preamble, RX_POWERSAVING_PROFILE_PREAMBLE);
|
|
EXPECT_EQ(config.rx_us, RX_POWERSAVING_DEFAULT_RX_US);
|
|
EXPECT_EQ(config.sleep_us, RX_POWERSAVING_DEFAULT_SLEEP_US);
|
|
}
|
|
|
|
TEST(RxPowerSaving, BaseControlRejectsEnableAndAcceptsContinuousRx) {
|
|
RxPowerSavingControl control;
|
|
|
|
EXPECT_FALSE(control.setRxPowerSaving(true, 12345, 23456));
|
|
EXPECT_TRUE(control.setRxPowerSaving(false, 12345, 23456));
|
|
|
|
const RxPowerSavingStatus status = control.getRxPowerSavingStatus();
|
|
EXPECT_FALSE(status.supported);
|
|
EXPECT_FALSE(status.armed);
|
|
EXPECT_EQ(status.arm_failures, 0U);
|
|
EXPECT_EQ(status.effective_rx_us, 0U);
|
|
EXPECT_EQ(status.effective_sleep_us, 0U);
|
|
}
|
|
|
|
TEST(RxPowerSaving, ArmRetryStopsAfterThreeFailuresAndSuccessResetsIt) {
|
|
RxPowerSavingArmRetryState retry;
|
|
|
|
EXPECT_TRUE(retry.canAttempt());
|
|
for (uint8_t expected = 1; expected <= RX_POWERSAVING_MAX_CONSEC_ARM_FAILURES; expected++) {
|
|
retry.recordFailure();
|
|
EXPECT_EQ(retry.consecutiveFailures(), expected);
|
|
}
|
|
EXPECT_FALSE(retry.canAttempt());
|
|
|
|
// Saturate instead of wrapping if a caller records another failure.
|
|
retry.recordFailure();
|
|
EXPECT_EQ(retry.consecutiveFailures(), RX_POWERSAVING_MAX_CONSEC_ARM_FAILURES);
|
|
|
|
retry.recordSuccess();
|
|
EXPECT_EQ(retry.consecutiveFailures(), 0);
|
|
EXPECT_TRUE(retry.canAttempt());
|
|
}
|
|
|
|
TEST(RxPowerSaving, ClearingRetryStateGrantsThreeFreshAttempts) {
|
|
RxPowerSavingArmRetryState retry;
|
|
for (uint8_t i = 0; i < RX_POWERSAVING_MAX_CONSEC_ARM_FAILURES; i++) {
|
|
retry.recordFailure();
|
|
}
|
|
ASSERT_FALSE(retry.canAttempt());
|
|
|
|
retry.reset();
|
|
EXPECT_TRUE(retry.canAttempt());
|
|
EXPECT_EQ(retry.consecutiveFailures(), 0);
|
|
}
|
|
|
|
TEST(RxPowerSaving, RejectsInvalidProfileInputs) {
|
|
uint32_t rx_us = 0;
|
|
uint32_t sleep_us = 0;
|
|
|
|
EXPECT_FALSE(calcRxPowerSavingLevel(0, 10, 250.0f, 16, &rx_us, &sleep_us));
|
|
EXPECT_FALSE(calcRxPowerSavingLevel(11, 10, 250.0f, 16, &rx_us, &sleep_us));
|
|
EXPECT_FALSE(calcRxPowerSavingLevel(5, 4, 250.0f, 16, &rx_us, &sleep_us));
|
|
EXPECT_FALSE(calcRxPowerSavingLevel(5, 10, 0.0f, 16, &rx_us, &sleep_us));
|
|
EXPECT_FALSE(calcRxPowerSavingLevel(5, 10, 250.0f, 24, &rx_us, &sleep_us));
|
|
}
|
|
|
|
TEST(RxPowerSaving, AcceptsPeriodBoundariesOnly) {
|
|
EXPECT_FALSE(isValidRxPowerSavingPeriod(RX_POWERSAVING_MIN_PERIOD_US - 1));
|
|
EXPECT_TRUE(isValidRxPowerSavingPeriod(RX_POWERSAVING_MIN_PERIOD_US));
|
|
EXPECT_TRUE(isValidRxPowerSavingPeriod(RX_POWERSAVING_MAX_PERIOD_US));
|
|
EXPECT_FALSE(isValidRxPowerSavingPeriod(RX_POWERSAVING_MAX_PERIOD_US + 1));
|
|
}
|
|
|
|
TEST(RxPowerSaving, RepairsInvalidPersistedPeriodsIndependently) {
|
|
uint32_t rx_us = 0;
|
|
uint32_t sleep_us = 12345;
|
|
|
|
ensureRxPowerSavingDefaults(&rx_us, &sleep_us);
|
|
EXPECT_EQ(rx_us, RX_POWERSAVING_DEFAULT_RX_US);
|
|
EXPECT_EQ(sleep_us, 12345U);
|
|
|
|
rx_us = 23456;
|
|
sleep_us = RX_POWERSAVING_MAX_PERIOD_US + 1;
|
|
ensureRxPowerSavingDefaults(&rx_us, &sleep_us);
|
|
EXPECT_EQ(rx_us, 23456U);
|
|
EXPECT_EQ(sleep_us, RX_POWERSAVING_DEFAULT_SLEEP_US);
|
|
}
|
|
|
|
TEST(RxPowerSaving, NormalizesPersistedConfigBeforeApplying) {
|
|
RxPowerSavingConfig config;
|
|
config.enabled = 7;
|
|
config.level = 255;
|
|
config.preamble = 24;
|
|
config.rx_us = 0;
|
|
config.sleep_us = 0;
|
|
|
|
normalizeRxPowerSavingConfig(&config, 10, 250.0f);
|
|
|
|
EXPECT_EQ(config.enabled, 1);
|
|
EXPECT_EQ(config.level, RX_POWERSAVING_BALANCED_LEVEL);
|
|
EXPECT_EQ(config.preamble, RX_POWERSAVING_PROFILE_PREAMBLE);
|
|
EXPECT_EQ(config.rx_us, 41871U);
|
|
EXPECT_EQ(config.sleep_us, 26851U);
|
|
}
|
|
|
|
TEST(RxPowerSaving, NumericInputIsStrictDecimal) {
|
|
EXPECT_TRUE(isRxPowerSavingNumeric("0"));
|
|
EXPECT_TRUE(isRxPowerSavingNumeric("123456"));
|
|
EXPECT_FALSE(isRxPowerSavingNumeric(nullptr));
|
|
EXPECT_FALSE(isRxPowerSavingNumeric(""));
|
|
EXPECT_FALSE(isRxPowerSavingNumeric("-1"));
|
|
EXPECT_FALSE(isRxPowerSavingNumeric("1.5"));
|
|
EXPECT_FALSE(isRxPowerSavingNumeric("12x"));
|
|
EXPECT_FALSE(isRxPowerSavingNumeric(" 12"));
|
|
}
|
|
|
|
TEST(RxPowerSaving, CompanionProfileIsLevelFivePreambleSixteen) {
|
|
uint32_t rx_us = 0;
|
|
uint32_t sleep_us = 0;
|
|
|
|
ASSERT_TRUE(calcRxPowerSavingLevel(5, 10, 250.0f, 16, &rx_us, &sleep_us));
|
|
EXPECT_EQ(rx_us, 41871U);
|
|
EXPECT_EQ(sleep_us, 26851U);
|
|
}
|
|
|
|
TEST(RxPowerSaving, NamedProfileConstantsRemainStable) {
|
|
EXPECT_EQ(RX_POWERSAVING_CONSERVATIVE_LEVEL, 1);
|
|
EXPECT_EQ(RX_POWERSAVING_BALANCED_LEVEL, 5);
|
|
EXPECT_EQ(RX_POWERSAVING_PROFILE_PREAMBLE, 16);
|
|
}
|
|
|
|
TEST(RxPowerSaving, HigherLevelTradesListenTimeForSleepTime) {
|
|
uint32_t conservative_rx_us = 0;
|
|
uint32_t conservative_sleep_us = 0;
|
|
uint32_t aggressive_rx_us = 0;
|
|
uint32_t aggressive_sleep_us = 0;
|
|
|
|
ASSERT_TRUE(calcRxPowerSavingLevel(1, 10, 250.0f, 16,
|
|
&conservative_rx_us, &conservative_sleep_us));
|
|
ASSERT_TRUE(calcRxPowerSavingLevel(10, 10, 250.0f, 16,
|
|
&aggressive_rx_us, &aggressive_sleep_us));
|
|
EXPECT_GT(conservative_rx_us, aggressive_rx_us);
|
|
EXPECT_LT(conservative_sleep_us, aggressive_sleep_us);
|
|
}
|
|
|
|
TEST(RxPowerSaving, AutoPreambleTracksSpreadingFactor) {
|
|
EXPECT_EQ(rxPowerSavingPreambleForSF(7), 32);
|
|
EXPECT_EQ(rxPowerSavingPreambleForSF(8), 32);
|
|
EXPECT_EQ(rxPowerSavingPreambleForSF(9), 16);
|
|
EXPECT_EQ(rxPowerSavingPreambleForSF(12), 16);
|
|
}
|
|
|
|
TEST(RxPowerSaving, LevelIntentRetunesAfterRadioChange) {
|
|
uint32_t rx_us = 0;
|
|
uint32_t sleep_us = 0;
|
|
|
|
ASSERT_TRUE(recalcRxPowerSavingFromLevel(5, 8, 62.5f, 16, &rx_us, &sleep_us));
|
|
uint32_t old_rx_us = rx_us;
|
|
uint32_t old_sleep_us = sleep_us;
|
|
|
|
ASSERT_TRUE(recalcRxPowerSavingFromLevel(5, 9, 62.5f, 16, &rx_us, &sleep_us));
|
|
EXPECT_NEAR((double)rx_us, (double)old_rx_us * 2.0, 1.0);
|
|
EXPECT_NEAR((double)sleep_us, (double)old_sleep_us * 2.0, 1.0);
|
|
}
|
|
|
|
TEST(RxPowerSaving, AutomaticPreambleRetunesAcrossSfBoundary) {
|
|
uint32_t automatic_rx_us = 0;
|
|
uint32_t automatic_sleep_us = 0;
|
|
uint32_t explicit_rx_us = 0;
|
|
uint32_t explicit_sleep_us = 0;
|
|
|
|
ASSERT_TRUE(recalcRxPowerSavingFromLevel(5, 8, 250.0f, 0,
|
|
&automatic_rx_us, &automatic_sleep_us));
|
|
ASSERT_TRUE(calcRxPowerSavingLevel(5, 8, 250.0f, 32,
|
|
&explicit_rx_us, &explicit_sleep_us));
|
|
EXPECT_EQ(automatic_rx_us, explicit_rx_us);
|
|
EXPECT_EQ(automatic_sleep_us, explicit_sleep_us);
|
|
|
|
ASSERT_TRUE(recalcRxPowerSavingFromLevel(5, 9, 250.0f, 0,
|
|
&automatic_rx_us, &automatic_sleep_us));
|
|
ASSERT_TRUE(calcRxPowerSavingLevel(5, 9, 250.0f, 16,
|
|
&explicit_rx_us, &explicit_sleep_us));
|
|
EXPECT_EQ(automatic_rx_us, explicit_rx_us);
|
|
EXPECT_EQ(automatic_sleep_us, explicit_sleep_us);
|
|
}
|
|
|
|
TEST(RxPowerSaving, ManualTimingsAreNotRetuned) {
|
|
uint32_t rx_us = 12345;
|
|
uint32_t sleep_us = 23456;
|
|
|
|
EXPECT_FALSE(recalcRxPowerSavingFromLevel(0, 10, 250.0f, 16, &rx_us, &sleep_us));
|
|
EXPECT_EQ(rx_us, 12345U);
|
|
EXPECT_EQ(sleep_us, 23456U);
|
|
}
|
|
|
|
TEST(RxPowerSavingCLI, AppliesNamedAndManualProfiles) {
|
|
RxPowerSavingConfig config;
|
|
FakeRxPowerSavingControl control;
|
|
char reply[192];
|
|
|
|
ASSERT_TRUE(RXPowerSavingCLI::set("balanced", 10, 250.0f, &config, &control,
|
|
reply, sizeof(reply)));
|
|
EXPECT_TRUE(control.set_called);
|
|
EXPECT_TRUE(control.requested_enabled);
|
|
EXPECT_EQ(config.enabled, 1);
|
|
EXPECT_EQ(config.level, RX_POWERSAVING_BALANCED_LEVEL);
|
|
EXPECT_EQ(config.preamble, RX_POWERSAVING_PROFILE_PREAMBLE);
|
|
EXPECT_EQ(config.rx_us, 41871U);
|
|
EXPECT_EQ(config.sleep_us, 26851U);
|
|
|
|
control.set_called = false;
|
|
ASSERT_TRUE(RXPowerSavingCLI::set("12345 23456", 10, 250.0f, &config, &control,
|
|
reply, sizeof(reply)));
|
|
EXPECT_TRUE(control.set_called);
|
|
EXPECT_EQ(config.level, 0);
|
|
EXPECT_EQ(config.preamble, 0);
|
|
EXPECT_EQ(config.rx_us, 12345U);
|
|
EXPECT_EQ(config.sleep_us, 23456U);
|
|
}
|
|
|
|
TEST(RxPowerSavingCLI, StoresAutomaticPreambleIntentForLevel) {
|
|
RxPowerSavingConfig config;
|
|
FakeRxPowerSavingControl control;
|
|
char reply[192];
|
|
|
|
ASSERT_TRUE(RXPowerSavingCLI::set("level 5", 8, 250.0f, &config, &control,
|
|
reply, sizeof(reply)));
|
|
EXPECT_EQ(config.level, 5);
|
|
EXPECT_EQ(config.preamble, 0);
|
|
|
|
uint32_t expected_rx_us = 0;
|
|
uint32_t expected_sleep_us = 0;
|
|
ASSERT_TRUE(calcRxPowerSavingLevel(5, 8, 250.0f, 32,
|
|
&expected_rx_us, &expected_sleep_us));
|
|
EXPECT_EQ(config.rx_us, expected_rx_us);
|
|
EXPECT_EQ(config.sleep_us, expected_sleep_us);
|
|
}
|
|
|
|
TEST(RxPowerSavingCLI, DoesNotPersistRejectedOrInvalidChanges) {
|
|
RxPowerSavingConfig config;
|
|
RxPowerSavingConfig original = config;
|
|
FakeRxPowerSavingControl control;
|
|
char reply[192];
|
|
|
|
EXPECT_FALSE(RXPowerSavingCLI::set("level 0", 10, 250.0f, &config, &control,
|
|
reply, sizeof(reply)));
|
|
EXPECT_EQ(config.enabled, original.enabled);
|
|
EXPECT_EQ(config.level, original.level);
|
|
EXPECT_STREQ(reply, "ERROR: level range is 1-10; preamble is 16 or 32");
|
|
|
|
control.accept = false;
|
|
EXPECT_FALSE(RXPowerSavingCLI::set("balanced", 10, 250.0f, &config, &control,
|
|
reply, sizeof(reply)));
|
|
EXPECT_EQ(config.enabled, original.enabled);
|
|
EXPECT_EQ(config.level, original.level);
|
|
EXPECT_STREQ(reply, "ERROR: RX powersaving unsupported");
|
|
}
|
|
|
|
TEST(RxPowerSavingCLI, RejectsValuesBeforeNarrowingOrOverflow) {
|
|
RxPowerSavingConfig config;
|
|
const RxPowerSavingConfig original = config;
|
|
FakeRxPowerSavingControl control;
|
|
char reply[192];
|
|
|
|
const char* invalid_values[] = {
|
|
"261", // used to alias to level 5 after uint8_t truncation
|
|
"level 266", // used to alias to level 10
|
|
"level 5 preamble 272", // used to alias to preamble 16
|
|
"4294967296 1000", // one above UINT32_MAX
|
|
"999999999999999999999999999", // must not wrap during parsing
|
|
};
|
|
|
|
for (const char* value : invalid_values) {
|
|
control.set_called = false;
|
|
EXPECT_FALSE(RXPowerSavingCLI::set(value, 10, 250.0f, &config, &control,
|
|
reply, sizeof(reply))) << value;
|
|
EXPECT_FALSE(control.set_called) << value;
|
|
EXPECT_EQ(config.enabled, original.enabled) << value;
|
|
EXPECT_EQ(config.level, original.level) << value;
|
|
EXPECT_EQ(config.preamble, original.preamble) << value;
|
|
EXPECT_EQ(config.rx_us, original.rx_us) << value;
|
|
EXPECT_EQ(config.sleep_us, original.sleep_us) << value;
|
|
}
|
|
}
|
|
|
|
TEST(RxPowerSavingCLI, DisablingKeepsTimingIntent) {
|
|
RxPowerSavingConfig config;
|
|
config.enabled = 1;
|
|
config.level = 7;
|
|
config.preamble = 32;
|
|
config.rx_us = 34567;
|
|
config.sleep_us = 45678;
|
|
FakeRxPowerSavingControl control;
|
|
char reply[192];
|
|
|
|
ASSERT_TRUE(RXPowerSavingCLI::set("off", 10, 250.0f, &config, &control,
|
|
reply, sizeof(reply)));
|
|
EXPECT_FALSE(control.requested_enabled);
|
|
EXPECT_EQ(config.enabled, 0);
|
|
EXPECT_EQ(config.level, 7);
|
|
EXPECT_EQ(config.preamble, 32);
|
|
EXPECT_EQ(config.rx_us, 34567U);
|
|
EXPECT_EQ(config.sleep_us, 45678U);
|
|
}
|
|
|
|
TEST(RxPowerSavingCLI, FormatsDesiredAndEffectiveStateSeparately) {
|
|
RxPowerSavingConfig config;
|
|
config.enabled = 1;
|
|
FakeRxPowerSavingControl control;
|
|
control.status = {true, false, -706, 2, 0, 0};
|
|
char reply[192];
|
|
|
|
RXPowerSavingCLI::get(&config, &control, reply, sizeof(reply));
|
|
|
|
EXPECT_NE(std::strstr(reply, "desired=on,effective=continuous,supported=yes"), nullptr);
|
|
EXPECT_NE(std::strstr(reply, "err=-706,fail=2"), nullptr);
|
|
EXPECT_EQ(std::strstr(reply, "erx="), nullptr); // nothing armed, nothing to report
|
|
}
|
|
|
|
TEST(RxPowerSavingCLI, ReportsClampedPeriodsOnlyWhenTheyDiffer) {
|
|
RxPowerSavingConfig config;
|
|
config.enabled = 1;
|
|
FakeRxPowerSavingControl control;
|
|
char reply[192];
|
|
|
|
// driver armed exactly what was asked for -> no extra fields
|
|
control.status = {true, true, 0, 0, config.rx_us, config.sleep_us};
|
|
RXPowerSavingCLI::get(&config, &control, reply, sizeof(reply));
|
|
EXPECT_NE(std::strstr(reply, "effective=armed"), nullptr);
|
|
EXPECT_EQ(std::strstr(reply, "erx="), nullptr);
|
|
|
|
// driver had to stretch the RX window -> surface the real values
|
|
control.status = {true, true, 0, 0, config.rx_us + 4000, config.sleep_us};
|
|
RXPowerSavingCLI::get(&config, &control, reply, sizeof(reply));
|
|
EXPECT_NE(std::strstr(reply, "erx=69625,eslp=60000"), nullptr);
|
|
}
|
|
|
|
TEST(RxPowerSavingCLI, GetDoesNotMutateStoredConfig) {
|
|
RxPowerSavingConfig config;
|
|
config.rx_us = 0; // invalid, e.g. from an older persisted prefs file
|
|
config.sleep_us = 999;
|
|
FakeRxPowerSavingControl control;
|
|
char reply[192];
|
|
|
|
RXPowerSavingCLI::get(&config, &control, reply, sizeof(reply));
|
|
|
|
EXPECT_EQ(config.rx_us, 0U);
|
|
EXPECT_EQ(config.sleep_us, 999U);
|
|
}
|
|
|
|
TEST(RxPowerSavingCLI, RfRxDiagnosticIsRuntimeOnlyAndCapabilityGated) {
|
|
FakeRxPowerSavingControl control;
|
|
char reply[192];
|
|
|
|
RXPowerSavingCLI::setRfRxDisabled("on", &control, reply, sizeof(reply));
|
|
EXPECT_STREQ(reply, "Error: unsupported");
|
|
|
|
control.rf_rx_supported = true;
|
|
RXPowerSavingCLI::setRfRxDisabled("on", &control, reply, sizeof(reply));
|
|
EXPECT_TRUE(control.rf_rx_disabled);
|
|
EXPECT_STREQ(reply, "OK - radio.rxps.rfrx_disabled on");
|
|
|
|
RXPowerSavingCLI::getRfRxDisabled(&control, reply, sizeof(reply));
|
|
EXPECT_STREQ(reply, "> on");
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
return RUN_ALL_TESTS();
|
|
}
|