Skip to content

Commit 166cdb7

Browse files
jamesarichclaude
andcommitted
test(car): cover the buildNodeUi snr resolver path
Address review feedback on #6523. The car signal-quality tests called determineSignalQuality() directly, so nothing proved buildNodeUi() routes through Node.snrOrNull rather than reading node.snr. Add two buildNodeUi() cases: an unset node resolves to UNKNOWN (this fails if the call reverts to node.snr, which would feed Float.MAX_VALUE into the bands and rate a node with no reading as EXCELLENT), and a 0 dB node still rates EXCELLENT. Also document the nullable snr/rssi fields in the AppFunction schema and correct RSSI's unit from dB to dBm to match the NodeDetails contract, and record in SnrExtensionsTest why the proto-absent case is not asserted there: rx_snr is still a non-null float upstream, so snrOrNull() cannot return null for any packet the test could construct. Null handling is covered where a null is representable, in MetricFormatterTest and LoraSignalIndicatorUiTest. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent ae69463 commit 166cdb7

3 files changed

Lines changed: 29 additions & 2 deletions

File tree

androidApp/src/google/kotlin/org/meshtastic/app/ai/appfunctions/AppFunctionModels.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,9 @@ data class GetNodeDetailsResponse(
126126
val hardwareModel: String,
127127
/** Firmware version string. */
128128
val firmwareVersion: String,
129-
/** Signal-to-noise ratio of strongest signal. */
129+
/** Signal-to-noise ratio in dB of the strongest signal, or null if this node has no reading. */
130130
val snr: Float?,
131-
/** Received signal strength indicator in dB. */
131+
/** Received signal strength indicator in dBm, or null if this node has no reading. */
132132
val rssi: Int?,
133133
/** Number of hops away from local node (-1 if unknown). */
134134
val hopsAway: Int,

core/model/src/commonTest/kotlin/org/meshtastic/core/model/util/SnrExtensionsTest.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ import kotlin.test.assertNotNull
2424
/**
2525
* Guards the presence policy for `rx_snr`: absent means null and nothing else. Unlike [rxTimeOrNull], a zero must never
2626
* be folded into "unknown" — 0 dB is a real measurement. See [snrOrNull].
27+
*
28+
* The proto-absent case is not asserted here because it is not yet constructible: `rx_snr` is still a non-null `float`
29+
* upstream, so [snrOrNull] cannot return null for any packet this test could build. What these tests do lock down is
30+
* the half that can regress today — that a zero is never folded — which is exactly what breaks if the [rxTimeOrNull]
31+
* pattern is copied over. Null *handling* is covered where a null is representable: `MetricFormatterTest`
32+
* (`snrAbsentIsUnknown`) and `LoraSignalIndicatorUiTest` (`snrRendersNothingWhenAbsent`,
33+
* `loraSignalIndicatorShowsUnknownWhenSnrIsAbsent`).
2734
*/
2835
class SnrExtensionsTest {
2936

feature/car/src/test/kotlin/org/meshtastic/feature/car/util/CarScreenDataBuilderTest.kt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,26 @@ class CarScreenDataBuilderTest {
7070
assertEquals(SignalQuality.EXCELLENT, quality)
7171
}
7272

73+
@Test
74+
fun `buildNodeUi resolves an unset node snr to unknown`() {
75+
// Exercises the production path: fails if buildNodeUi reverts to reading node.snr, which would feed the
76+
// Float.MAX_VALUE sentinel into the bands and rate a node with no reading as EXCELLENT.
77+
val node = Node(num = 1)
78+
79+
val ui = CarScreenDataBuilder.buildNodeUi(node, ModemPreset.LONG_FAST)
80+
81+
assertEquals(SignalQuality.UNKNOWN, ui.signalQuality)
82+
}
83+
84+
@Test
85+
fun `buildNodeUi rates a zero node snr reading`() {
86+
val node = Node(num = 1, snr = 0f)
87+
88+
val ui = CarScreenDataBuilder.buildNodeUi(node, ModemPreset.LONG_FAST)
89+
90+
assertEquals(SignalQuality.EXCELLENT, ui.signalQuality)
91+
}
92+
7393
@Test
7494
fun `determineSignalQuality returns excellent well above the preset floor`() {
7595
// LongFast floor -17.5; -10 is 7.5 dB above it (> floor + 5.5 margin).

0 commit comments

Comments
 (0)