meshtastic-firmware/test/test_serial/SerialModule.cpp
Tom 03e6b80989
Serial config validation (#11339)
* fix(serial): validate serial module config on every platform

AdminModule guarded the serial config validation by architecture but not the
assignment beneath it:

    #if ARCH_ESP32 || ARCH_NRF52 || ARCH_RP2040
        if (!SerialModule::isValidConfig(...)) return false;
        disableBluetooth();
    #endif
        moduleConfig.serial = c.payload_variant.serial;

So on every other platform an admin "set module config: serial" stored a config
the firmware rejects on ESP32. override_console_serial_port combined with
DEFAULT, SIMPLE, TEXTMSG or PROTO is accepted and persisted today.

Two families are affected, for different reasons:

  - portduino/meshtasticd, where the validation did not exist at all:
    isValidConfig was a static member of SerialModule, and that class is inside
    the same architecture guard, so `nm` finds no such symbol in the native
    object.
  - STM32WL (rak3172, wio-e5, CDEBYTE_E77-MBL, russell), where it existed and
    was never called: the class guard includes ARCH_STM32WL and the AdminModule
    call site did not.

Validation is pure config logic with no serial hardware behind it, so it moves
out of the class and out of the guard as a free serialConfigIsValid(). Its only
external references - clientNotificationPool, service, getValidTime - are
already unguarded elsewhere, so it links on every target. AdminModule's include
of SerialModule.h is unguarded for the same reason; the class itself stays
guarded inside the header. Only disableBluetooth() remains architecture-specific.

This changes what meshtasticd and the STM32WL targets accept: a host relying on
the unvalidated path (override_console_serial_port with a mode other than NMEA,
CalTopo or MS_CONFIG) is now rejected, as it already is on ESP32.

test/test_serial has asserted nothing since it was added in 28aeb0f09e
(2025-07-26): its body is behind the same guard, so on portduino it logged a
warning and ran zero assertions while counting as one of the canonical suites.
Enabling it showed the code did not even compile - its designated initializers
list .override_console_serial_port before .mode, which is not declaration order,
and C++ requires that. PlatformIO only builds test/ for the native env, so no
build had ever compiled these lines. Reordered; all nine now run and pass.

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* I'd say gimme 5 bees for a dollar. That's what we called a nickel, because they had bees on em.

* style: wrap over-long warning string to the 120-col limit

* Cover MS_CONFIG override and correct the validator comment

serialConfigIsValid() accepts MS_CONFIG alongside NMEA and CALTOPO when
override_console_serial_port is set, but only the first two had a valid-case
test. Add the missing one.

The declaration comment described the function as pure config logic; it also
logs and, in non-test builds, sends a client notification on rejection.

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-08-05 06:55:31 +00:00

146 lines
5.4 KiB
C++

#include "DebugConfiguration.h"
#include "TestUtil.h"
#include <unity.h>
#ifdef ARCH_PORTDUINO
#include "configuration.h"
#include "modules/SerialModule.h"
// Test that empty configuration is valid.
void test_serialConfigEmptyIsValid(void)
{
meshtastic_ModuleConfig_SerialConfig config = {};
TEST_ASSERT_TRUE(serialConfigIsValid(config));
}
// Test that basic enabled configuration is valid.
void test_serialConfigEnabledIsValid(void)
{
meshtastic_ModuleConfig_SerialConfig config = {.enabled = true};
TEST_ASSERT_TRUE(serialConfigIsValid(config));
}
// Test that configuration with override_console_serial_port and NMEA mode is valid.
void test_serialConfigWithOverrideConsoleNmeaModeIsValid(void)
{
meshtastic_ModuleConfig_SerialConfig config = {
.enabled = true, .mode = meshtastic_ModuleConfig_SerialConfig_Serial_Mode_NMEA, .override_console_serial_port = true};
TEST_ASSERT_TRUE(serialConfigIsValid(config));
}
// Test that configuration with override_console_serial_port and CalTopo mode is valid.
void test_serialConfigWithOverrideConsoleCalTopoModeIsValid(void)
{
meshtastic_ModuleConfig_SerialConfig config = {
.enabled = true, .mode = meshtastic_ModuleConfig_SerialConfig_Serial_Mode_CALTOPO, .override_console_serial_port = true};
TEST_ASSERT_TRUE(serialConfigIsValid(config));
}
// Test that configuration with override_console_serial_port and MS Config mode is valid.
void test_serialConfigWithOverrideConsoleMsConfigModeIsValid(void)
{
meshtastic_ModuleConfig_SerialConfig config = {.enabled = true,
.mode = meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MS_CONFIG,
.override_console_serial_port = true};
TEST_ASSERT_TRUE(serialConfigIsValid(config));
}
// Test that configuration with override_console_serial_port and DEFAULT mode is invalid.
void test_serialConfigWithOverrideConsoleDefaultModeIsInvalid(void)
{
meshtastic_ModuleConfig_SerialConfig config = {
.enabled = true, .mode = meshtastic_ModuleConfig_SerialConfig_Serial_Mode_DEFAULT, .override_console_serial_port = true};
TEST_ASSERT_FALSE(serialConfigIsValid(config));
}
// Test that configuration with override_console_serial_port and SIMPLE mode is invalid.
void test_serialConfigWithOverrideConsoleSimpleModeIsInvalid(void)
{
meshtastic_ModuleConfig_SerialConfig config = {
.enabled = true, .mode = meshtastic_ModuleConfig_SerialConfig_Serial_Mode_SIMPLE, .override_console_serial_port = true};
TEST_ASSERT_FALSE(serialConfigIsValid(config));
}
// Test that configuration with override_console_serial_port and TEXTMSG mode is invalid.
void test_serialConfigWithOverrideConsoleTextMsgModeIsInvalid(void)
{
meshtastic_ModuleConfig_SerialConfig config = {
.enabled = true, .mode = meshtastic_ModuleConfig_SerialConfig_Serial_Mode_TEXTMSG, .override_console_serial_port = true};
TEST_ASSERT_FALSE(serialConfigIsValid(config));
}
// Test that configuration with override_console_serial_port and PROTO mode is invalid.
void test_serialConfigWithOverrideConsoleProtoModeIsInvalid(void)
{
meshtastic_ModuleConfig_SerialConfig config = {
.enabled = true, .mode = meshtastic_ModuleConfig_SerialConfig_Serial_Mode_PROTO, .override_console_serial_port = true};
TEST_ASSERT_FALSE(serialConfigIsValid(config));
}
// Test that various modes work without override_console_serial_port.
void test_serialConfigVariousModesWithoutOverrideAreValid(void)
{
meshtastic_ModuleConfig_SerialConfig config = {.enabled = true, .override_console_serial_port = false};
// Test DEFAULT mode
config.mode = meshtastic_ModuleConfig_SerialConfig_Serial_Mode_DEFAULT;
TEST_ASSERT_TRUE(serialConfigIsValid(config));
// Test SIMPLE mode
config.mode = meshtastic_ModuleConfig_SerialConfig_Serial_Mode_SIMPLE;
TEST_ASSERT_TRUE(serialConfigIsValid(config));
// Test TEXTMSG mode
config.mode = meshtastic_ModuleConfig_SerialConfig_Serial_Mode_TEXTMSG;
TEST_ASSERT_TRUE(serialConfigIsValid(config));
// Test PROTO mode
config.mode = meshtastic_ModuleConfig_SerialConfig_Serial_Mode_PROTO;
TEST_ASSERT_TRUE(serialConfigIsValid(config));
// Test NMEA mode
config.mode = meshtastic_ModuleConfig_SerialConfig_Serial_Mode_NMEA;
TEST_ASSERT_TRUE(serialConfigIsValid(config));
// Test CALTOPO mode
config.mode = meshtastic_ModuleConfig_SerialConfig_Serial_Mode_CALTOPO;
TEST_ASSERT_TRUE(serialConfigIsValid(config));
}
void setup()
{
initializeTestEnvironment();
UNITY_BEGIN();
RUN_TEST(test_serialConfigEmptyIsValid);
RUN_TEST(test_serialConfigEnabledIsValid);
RUN_TEST(test_serialConfigWithOverrideConsoleNmeaModeIsValid);
RUN_TEST(test_serialConfigWithOverrideConsoleCalTopoModeIsValid);
RUN_TEST(test_serialConfigWithOverrideConsoleMsConfigModeIsValid);
RUN_TEST(test_serialConfigWithOverrideConsoleDefaultModeIsInvalid);
RUN_TEST(test_serialConfigWithOverrideConsoleSimpleModeIsInvalid);
RUN_TEST(test_serialConfigWithOverrideConsoleTextMsgModeIsInvalid);
RUN_TEST(test_serialConfigWithOverrideConsoleProtoModeIsInvalid);
RUN_TEST(test_serialConfigVariousModesWithoutOverrideAreValid);
exit(UNITY_END());
}
#else
void setup()
{
initializeTestEnvironment();
LOG_WARN("This test requires the ARCH_PORTDUINO variant");
UNITY_BEGIN();
exit(UNITY_END());
}
#endif
void loop() {}