From 749ee1e2e5672f9965a138ae948b62c255b32d65 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Wed, 28 Oct 2026 03:18:45 -0700 Subject: [PATCH] Keep unused Companion preset staging out of repeater compilation --- src/helpers/RadioProfileCLI.cpp | 3 +++ test/fixtures/radio_profiles/cli_test.cpp | 5 +++++ test/test_radio_profile_cli.py | 26 +++++++++++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/src/helpers/RadioProfileCLI.cpp b/src/helpers/RadioProfileCLI.cpp index 60facf08a..04c171d30 100644 --- a/src/helpers/RadioProfileCLI.cpp +++ b/src/helpers/RadioProfileCLI.cpp @@ -746,6 +746,8 @@ bool RadioProfileCLI::finishReplyMutation(bool delivered) { || !fs_ || !fs_->exists(ReplyPath) || fs_->remove(ReplyPath); } +#if !MESH_CLIENT_REPEATER_ONLY +// Companion channel joins use this transaction; repeaters use the CLI below. bool RadioProfileCLI::stageChannelPreset(const char* name) { if (!name || !cli::radio2PresetTuple(name) || remote_command_ || !secondaryRuntimeAvailable()) return false; @@ -768,6 +770,7 @@ bool RadioProfileCLI::finishChannelPreset(bool commit) { publish_after_ms_ = millis() + 2000; return true; } +#endif #if MESH_ENABLE_FLEET_CONTROL bool RadioProfileCLI::stageRemoteSchedule(unsigned slot, const Schedule& schedule) { diff --git a/test/fixtures/radio_profiles/cli_test.cpp b/test/fixtures/radio_profiles/cli_test.cpp index 953cf35b5..f6a836f3e 100644 --- a/test/fixtures/radio_profiles/cli_test.cpp +++ b/test/fixtures/radio_profiles/cli_test.cpp @@ -548,6 +548,7 @@ static void mediaMeshPresets() { assert(!invalid.radio.p.enabled() && invalid.fs.files.empty()); } +#if !MESH_CLIENT_REPEATER_ONLY static void channelPresetTransaction() { for (bool range : {false, true}) { Fixture f; @@ -710,6 +711,8 @@ static void channelPresetTransaction() { } } +#endif + struct MediaScope { bool available = true, fail_save = false, enabled = false, durable = false; unsigned checks = 0, commits = 0; @@ -893,7 +896,9 @@ int main(int argc, char** argv) { else if (!strcmp(argv[1], "relative_schedule")) relativeSchedules(); else if (!strcmp(argv[1], "spectrum_overlap")) spectrumOverlap(); else if (!strcmp(argv[1], "mediamesh")) mediaMeshPresets(); +#if !MESH_CLIENT_REPEATER_ONLY else if (!strcmp(argv[1], "channel_preset")) channelPresetTransaction(); +#endif else if (!strcmp(argv[1], "media_scope")) mediaMeshScopeLifecycle(); else return 2; return 0; diff --git a/test/test_radio_profile_cli.py b/test/test_radio_profile_cli.py index 2b5000893..be966e186 100644 --- a/test/test_radio_profile_cli.py +++ b/test/test_radio_profile_cli.py @@ -9,6 +9,32 @@ import unittest ROOT = Path(__file__).resolve().parents[1] class RadioProfileCLITest(unittest.TestCase): + def test_repeater_retains_radio2_presets_schedules_and_transactions(self): + compiler = shutil.which('g++') or shutil.which('clang++') + self.assertIsNotNone(compiler) + with tempfile.TemporaryDirectory(prefix='profile-repeater-') as work: + binary = Path(work) / 'repeater' + compiled = subprocess.run([ + compiler, '-std=c++17', '-Os', '-Wall', '-Wextra', + '-DMESH_CLIENT_REPEATER_ONLY=1', '-DMESH_ENABLE_FLEET_CONTROL=0', + '-DMESH_ENABLE_CARRIER_TOOLS=0', + '-I', str(ROOT/'test/fixtures/radio_profiles/mocks'), + '-I', str(ROOT/'test/mocks'), '-I', str(ROOT/'src'), + str(ROOT/'src/helpers/RadioProfileCLI.cpp'), + str(ROOT/'test/fixtures/radio_profiles/cli_test.cpp'), + '-o', str(binary)], capture_output=True, text=True, timeout=60) + self.assertEqual(compiled.returncode, 0, compiled.stderr) + for scenario in (None, 'permanent_order', 'temporary_boundary', 'lease_creation', + 'save_failure', 'schedule_permutations', 'storage_isolation', + 'relative_schedule', 'spectrum_overlap', 'mediamesh', 'media_scope'): + with self.subTest(scenario=scenario): + ran = subprocess.run([str(binary)] + ([scenario] if scenario else []), + capture_output=True, text=True, timeout=10) + self.assertEqual(ran.returncode, 0, ran.stdout + ran.stderr) + symbols = subprocess.check_output(['nm', '-C', str(binary)], text=True) + self.assertNotIn('RadioProfileCLI::stageChannelPreset(', symbols) + self.assertNotIn('RadioProfileCLI::finishChannelPreset(', symbols) + def test_channel_preset_preserves_secondary_owners(self): compiler = shutil.which('g++') or shutil.which('clang++') self.assertIsNotNone(compiler)