mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-06-16 17:59:07 -04:00
test: pin ModemPreset->channel-name contract; drop vacuous name check
Add ChannelPresetNameTest (commonTest, so it gates every PR) pinning every ModemPreset to its exact firmware-canonical channel name from DisplayFormatters::getModemPresetDisplayName(preset, useShortName=false). The name is interop-critical (it feeds the channel hash, channel number/frequency and MQTT topic), so the test: - locks the mapping against accidental edits (e.g. "LongMod" -> "LongModerate"), - asserts set-completeness vs ModemPreset.entries, so a new preset that was added to Channel.name's `when` but not pinned here fails loudly (also catches someone silencing the intentional compile tripwire with an `else`), - covers the deprecated VERY_LONG_SLOW, which still has a real name. This backstops Channel.name's exhaustive `when` without weakening it. The numeric on-air anchors (hash/channelNum/radioFreq) stay in ChannelTest. Remove the old androidDeviceTest allModemPresetsHaveValidNames, which only asserted name != "Invalid" — a value the no-else `when` can never produce, so it gave the illusion of a name guard while testing nothing, and ran only on-device rather than on every PR. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -24,7 +24,6 @@ import org.meshtastic.core.model.util.CHANNEL_URL_PREFIX
|
||||
import org.meshtastic.core.model.util.getChannelUrl
|
||||
import org.meshtastic.core.model.util.toChannelSet
|
||||
import org.meshtastic.proto.ChannelSet
|
||||
import org.meshtastic.proto.Config
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ChannelTest {
|
||||
@@ -64,18 +63,4 @@ class ChannelTest {
|
||||
|
||||
Assert.assertEquals(906.875f, ch.radioFreq)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun allModemPresetsHaveValidNames() {
|
||||
Config.LoRaConfig.ModemPreset.entries.forEach { preset ->
|
||||
// Skip UNRECOGNIZED if it exists (Wire generates it sometimes) or generic UNSET values if applicable
|
||||
if (preset.name == "UNSET" || preset.name == "UNRECOGNIZED") return@forEach
|
||||
|
||||
val loraConfig = Channel.default.loraConfig.copy(use_preset = true, modem_preset = preset)
|
||||
val channel = Channel(loraConfig = loraConfig)
|
||||
|
||||
// We want to ensure it is NOT "Invalid"
|
||||
Assert.assertNotEquals("Preset ${preset.name} should typically have a valid name", "Invalid", channel.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.core.model
|
||||
|
||||
import org.meshtastic.proto.Config.LoRaConfig.ModemPreset
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
/**
|
||||
* Contract test pinning the preset -> channel-name mapping in [Channel.name].
|
||||
*
|
||||
* This name is INTEROP-CRITICAL: for a channel with an empty name and `use_preset = true`, the name is hashed into the
|
||||
* channel hash, the channel number / radio frequency, and the MQTT topic, so it must byte-match the firmware. Source of
|
||||
* truth is firmware `DisplayFormatters::getModemPresetDisplayName(preset, useShortName = false)`. Two names are
|
||||
* deliberately abbreviated (LONG_MODERATE -> "LongMod", VERY_LONG_SLOW -> "VLongSlow") and must NOT be auto-derived
|
||||
* from the enum name.
|
||||
*
|
||||
* When firmware adds a preset, [Channel.name]'s exhaustive `when` fails to compile first (by design). This test is the
|
||||
* backstop: it forces the new branch to carry the EXACT firmware name, guards the mapping against accidental edits, and
|
||||
* fails if a preset is left unpinned (e.g. someone silenced the compile error with an `else`). It deliberately covers
|
||||
* the deprecated VERY_LONG_SLOW, which still has a real firmware name. The numeric anchors (hash/channelNum/radioFreq)
|
||||
* live in ChannelTest and are the genuine on-air interop guard — keep both.
|
||||
*/
|
||||
class ChannelPresetNameTest {
|
||||
|
||||
// Firmware-canonical names: DisplayFormatters::getModemPresetDisplayName(preset, useShortName = false).
|
||||
private val expectedNames =
|
||||
mapOf(
|
||||
ModemPreset.SHORT_TURBO to "ShortTurbo",
|
||||
ModemPreset.SHORT_FAST to "ShortFast",
|
||||
ModemPreset.SHORT_SLOW to "ShortSlow",
|
||||
ModemPreset.MEDIUM_FAST to "MediumFast",
|
||||
ModemPreset.MEDIUM_SLOW to "MediumSlow",
|
||||
ModemPreset.LONG_FAST to "LongFast",
|
||||
ModemPreset.LONG_SLOW to "LongSlow",
|
||||
ModemPreset.LONG_MODERATE to "LongMod",
|
||||
ModemPreset.VERY_LONG_SLOW to "VLongSlow",
|
||||
ModemPreset.LONG_TURBO to "LongTurbo",
|
||||
ModemPreset.LITE_FAST to "LiteFast",
|
||||
ModemPreset.LITE_SLOW to "LiteSlow",
|
||||
ModemPreset.NARROW_FAST to "NarrowFast",
|
||||
ModemPreset.NARROW_SLOW to "NarrowSlow",
|
||||
ModemPreset.TINY_FAST to "TinyFast",
|
||||
ModemPreset.TINY_SLOW to "TinySlow",
|
||||
)
|
||||
|
||||
private fun presetChannelName(preset: ModemPreset): String =
|
||||
Channel(loraConfig = Channel.default.loraConfig.copy(use_preset = true, modem_preset = preset)).name
|
||||
|
||||
@Test
|
||||
fun every_preset_maps_to_its_exact_firmware_name() {
|
||||
expectedNames.forEach { (preset, expected) ->
|
||||
assertEquals(expected, presetChannelName(preset), "Channel name for $preset must match firmware exactly")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun every_ModemPreset_is_pinned() {
|
||||
val protoPresets = ModemPreset.entries.filter { it.name != "UNSET" && it.name != "UNRECOGNIZED" }.toSet()
|
||||
assertEquals(
|
||||
protoPresets,
|
||||
expectedNames.keys,
|
||||
"Every ModemPreset must be pinned to its firmware name here. A new preset was added to the protos " +
|
||||
"(and to Channel.name's `when`) but not recorded in this contract — add it with its exact " +
|
||||
"DisplayFormatters::getModemPresetDisplayName string.",
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user