mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-03-28 18:52:42 -04:00
Refactor command handling, enhance tests, and improve discovery logic (#4878)
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025-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
|
||||
|
||||
class NodeInfoTest {
|
||||
/*
|
||||
|
||||
private val model = HardwareModel.ANDROID_SIM
|
||||
private val node =
|
||||
listOf(
|
||||
NodeInfo(4, MeshUser("+zero", "User Zero", "U0", model)),
|
||||
NodeInfo(5, MeshUser("+one", "User One", "U1", model), Position(37.1, 121.1, 35)),
|
||||
NodeInfo(6, MeshUser("+two", "User Two", "U2", model), Position(37.11, 121.1, 40)),
|
||||
NodeInfo(7, MeshUser("+three", "User Three", "U3", model), Position(37.101, 121.1, 40)),
|
||||
NodeInfo(8, MeshUser("+four", "User Four", "U4", model), Position(37.116, 121.1, 40)),
|
||||
)
|
||||
|
||||
private val currentDefaultLocale = LocaleListCompat.getDefault().get(0) ?: Locale.US
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
Locale.setDefault(Locale.US)
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
Locale.setDefault(currentDefaultLocale)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun distanceGood() {
|
||||
assertEquals(1111, node[1].distance(node[2]))
|
||||
assertEquals(111, node[1].distance(node[3]))
|
||||
assertEquals(1779, node[1].distance(node[4]))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun distanceStrGood() {
|
||||
assertEquals("1.1 km", node[1].distanceStr(node[2], Config.DisplayConfig.DisplayUnits.METRIC.value))
|
||||
assertEquals("111 m", node[1].distanceStr(node[3], Config.DisplayConfig.DisplayUnits.METRIC.value))
|
||||
assertEquals("1.1 mi", node[1].distanceStr(node[4], Config.DisplayConfig.DisplayUnits.IMPERIAL.value))
|
||||
assertEquals("364 ft", node[1].distanceStr(node[3], Config.DisplayConfig.DisplayUnits.IMPERIAL.value))
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025-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
|
||||
|
||||
class PositionTest {
|
||||
/*
|
||||
|
||||
@Test
|
||||
fun degGood() {
|
||||
assertEquals(Position.degI(89.0), 890000000)
|
||||
assertEquals(Position.degI(-89.0), -890000000)
|
||||
|
||||
assertEquals(89.0, Position.degD(Position.degI(89.0)), 0.01)
|
||||
assertEquals(-89.0, Position.degD(Position.degI(-89.0)), 0.01)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun givenPositionCreatedWithoutTime_thenTimeIsSet() {
|
||||
val position = Position(37.1, 121.1, 35)
|
||||
assertTrue(position.time != 0)
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -76,7 +76,7 @@ private fun formatTraceroutePath(nodesList: List<String>, snrList: List<Int>): S
|
||||
.joinToString("\n")
|
||||
}
|
||||
|
||||
private fun RouteDiscovery.getTracerouteResponse(
|
||||
fun RouteDiscovery.getTracerouteResponse(
|
||||
getUser: (nodeNum: Int) -> String,
|
||||
headerTowards: String = "Route traced toward destination:\n\n",
|
||||
headerBack: String = "Route traced back to us:\n\n",
|
||||
@@ -98,15 +98,6 @@ fun MeshPacket.getTracerouteResponse(
|
||||
headerBack: String = "Route traced back to us:\n\n",
|
||||
): String? = fullRouteDiscovery?.getTracerouteResponse(getUser, headerTowards, headerBack)
|
||||
|
||||
/** Returns a traceroute response string only when the result is complete (both directions). */
|
||||
fun MeshPacket.getFullTracerouteResponse(
|
||||
getUser: (nodeNum: Int) -> String,
|
||||
headerTowards: String = "Route traced toward destination:\n\n",
|
||||
headerBack: String = "Route traced back to us:\n\n",
|
||||
): String? = fullRouteDiscovery
|
||||
?.takeIf { it.route.isNotEmpty() && it.route_back.isNotEmpty() }
|
||||
?.getTracerouteResponse(getUser, headerTowards, headerBack)
|
||||
|
||||
enum class TracerouteMapAvailability {
|
||||
Ok,
|
||||
MissingEndpoints,
|
||||
|
||||
@@ -16,81 +16,83 @@
|
||||
*/
|
||||
package org.meshtastic.core.model
|
||||
|
||||
class CapabilitiesTest {
|
||||
/*
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class CapabilitiesTest {
|
||||
|
||||
private fun caps(version: String?) = Capabilities(version, forceEnableAll = false)
|
||||
|
||||
@Test
|
||||
fun canMuteNodeRequiresV2718() {
|
||||
fun canMuteNode_requires_V2_7_18() {
|
||||
assertFalse(caps("2.7.15").canMuteNode)
|
||||
assertTrue(caps("2.7.18").canMuteNode)
|
||||
assertTrue(caps("2.8.0").canMuteNode)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun canRequestNeighborInfoIsCurrentlyDisabled() {
|
||||
fun canRequestNeighborInfo_is_currently_disabled() {
|
||||
assertFalse(caps("2.7.14").canRequestNeighborInfo)
|
||||
assertFalse(caps("3.0.0").canRequestNeighborInfo)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun canSendVerifiedContactsRequiresV2712() {
|
||||
fun canSendVerifiedContacts_requires_V2_7_12() {
|
||||
assertFalse(caps("2.7.11").canSendVerifiedContacts)
|
||||
assertTrue(caps("2.7.12").canSendVerifiedContacts)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun canToggleTelemetryEnabledRequiresV2712() {
|
||||
fun canToggleTelemetryEnabled_requires_V2_7_12() {
|
||||
assertFalse(caps("2.7.11").canToggleTelemetryEnabled)
|
||||
assertTrue(caps("2.7.12").canToggleTelemetryEnabled)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun canToggleUnmessageableRequiresV269() {
|
||||
fun canToggleUnmessageable_requires_V2_6_9() {
|
||||
assertFalse(caps("2.6.8").canToggleUnmessageable)
|
||||
assertTrue(caps("2.6.9").canToggleUnmessageable)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun supportsQrCodeSharingRequiresV268() {
|
||||
fun supportsQrCodeSharing_requires_V2_6_8() {
|
||||
assertFalse(caps("2.6.7").supportsQrCodeSharing)
|
||||
assertTrue(caps("2.6.8").supportsQrCodeSharing)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun supportsSecondaryChannelLocationRequiresV2610() {
|
||||
fun supportsSecondaryChannelLocation_requires_V2_6_10() {
|
||||
assertFalse(caps("2.6.9").supportsSecondaryChannelLocation)
|
||||
assertTrue(caps("2.6.10").supportsSecondaryChannelLocation)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun supportsStatusMessageRequiresV2717() {
|
||||
fun supportsStatusMessage_requires_V2_7_17() {
|
||||
assertFalse(caps("2.7.16").supportsStatusMessage)
|
||||
assertTrue(caps("2.7.17").supportsStatusMessage)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun supportsTrafficManagementConfigRequiresV300() {
|
||||
fun supportsTrafficManagementConfig_requires_V3_0_0() {
|
||||
assertFalse(caps("2.7.18").supportsTrafficManagementConfig)
|
||||
assertTrue(caps("3.0.0").supportsTrafficManagementConfig)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun supportsTakConfigRequiresV2719() {
|
||||
fun supportsTakConfig_requires_V2_7_19() {
|
||||
assertFalse(caps("2.7.18").supportsTakConfig)
|
||||
assertTrue(caps("2.7.19").supportsTakConfig)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun supportsEsp32OtaRequiresV2718() {
|
||||
fun supportsEsp32Ota_requires_V2_7_18() {
|
||||
assertFalse(caps("2.7.17").supportsEsp32Ota)
|
||||
assertTrue(caps("2.7.18").supportsEsp32Ota)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun nullFirmwareReturnsAllFalse() {
|
||||
fun nullFirmware_returns_all_false() {
|
||||
val c = caps(null)
|
||||
assertFalse(c.canMuteNode)
|
||||
assertFalse(c.canRequestNeighborInfo)
|
||||
@@ -106,7 +108,7 @@ class CapabilitiesTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun forceEnableAllReturnsTrueForEverythingRegardlessOfVersion() {
|
||||
fun forceEnableAll_returns_true_regardless_of_version() {
|
||||
val c = Capabilities(firmwareVersion = null, forceEnableAll = true)
|
||||
assertTrue(c.canMuteNode)
|
||||
assertTrue(c.canSendVerifiedContacts)
|
||||
@@ -114,23 +116,4 @@ class CapabilitiesTest {
|
||||
assertTrue(c.supportsTrafficManagementConfig)
|
||||
assertTrue(c.supportsTakConfig)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deviceVersionParsingIsRobust() {
|
||||
assertEquals(20712, DeviceVersion("2.7.12").asInt)
|
||||
assertEquals(20712, DeviceVersion("2.7.12-beta").asInt)
|
||||
assertEquals(30000, DeviceVersion("3.0.0").asInt)
|
||||
assertEquals(20700, DeviceVersion("2.7").asInt) // Handles 2-part versions
|
||||
assertEquals(0, DeviceVersion("invalid").asInt)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deviceVersionComparisonIsCorrect() {
|
||||
assertTrue(DeviceVersion("2.7.12") >= DeviceVersion("2.7.11"))
|
||||
assertTrue(DeviceVersion("3.0.0") > DeviceVersion("2.8.1"))
|
||||
assertTrue(DeviceVersion("2.7.12") == DeviceVersion("2.7.12"))
|
||||
assertFalse(DeviceVersion("2.6.9") >= DeviceVersion("2.7.0"))
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -16,62 +16,55 @@
|
||||
*/
|
||||
package org.meshtastic.core.model
|
||||
|
||||
class ChannelOptionTest {
|
||||
/*
|
||||
import org.meshtastic.proto.Config.LoRaConfig.ModemPreset
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
class ChannelOptionTest {
|
||||
|
||||
/**
|
||||
* This test ensures that every `ModemPreset` defined in the protobufs has a corresponding entry in our
|
||||
* `ChannelOption` enum.
|
||||
* Ensures that every [ModemPreset] defined in the protobufs has a corresponding entry in [ChannelOption].
|
||||
*
|
||||
* If this test fails, it means a `ModemPreset` was added or changed in the firmware/protobufs, and you must update
|
||||
* the `ChannelOption` enum to match.
|
||||
* If this test fails, a ModemPreset was added or changed in the firmware/protobufs and you must update the
|
||||
* [ChannelOption] enum to match.
|
||||
*/
|
||||
@Test
|
||||
fun `ensure every ModemPreset is mapped in ChannelOption`() {
|
||||
// Get all possible ModemPreset values.
|
||||
val unmappedPresets =
|
||||
Config.LoRaConfig.ModemPreset.entries.filter { it.name != "UNSET" && it.name != "UNRECOGNIZED" }
|
||||
fun ensure_every_ModemPreset_is_mapped_in_ChannelOption() {
|
||||
val unmappedPresets = ModemPreset.entries.filter { it.name != "UNSET" && it.name != "UNRECOGNIZED" }
|
||||
|
||||
unmappedPresets.forEach { preset ->
|
||||
// Attempt to find the corresponding ChannelOption
|
||||
val channelOption = ChannelOption.from(preset)
|
||||
|
||||
// Assert that a mapping exists, with a detailed failure message.
|
||||
assertNotNull(
|
||||
channelOption,
|
||||
"Missing ChannelOption mapping for ModemPreset: '${preset.name}'. " +
|
||||
"Please add a corresponding entry to the ChannelOption enum class.",
|
||||
channelOption,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This test ensures that there are no extra entries in `ChannelOption` that don't correspond to a valid
|
||||
* `ModemPreset`.
|
||||
* Ensures that there are no extra entries in [ChannelOption] that don't correspond to a valid [ModemPreset].
|
||||
*
|
||||
* If this test fails, it means a `ModemPreset` was removed from the protobufs, and you must remove the
|
||||
* corresponding entry from the `ChannelOption` enum.
|
||||
* If this test fails, a ModemPreset was removed from the protobufs and you must remove the corresponding entry from
|
||||
* the [ChannelOption] enum.
|
||||
*/
|
||||
@Test
|
||||
fun `ensure no extra mappings exist in ChannelOption`() {
|
||||
val protoPresets =
|
||||
Config.LoRaConfig.ModemPreset.entries.filter { it.name != "UNSET" && it.name != "UNRECOGNIZED" }.toSet()
|
||||
fun ensure_no_extra_mappings_exist_in_ChannelOption() {
|
||||
val protoPresets = ModemPreset.entries.filter { it.name != "UNSET" && it.name != "UNRECOGNIZED" }.toSet()
|
||||
val mappedPresets = ChannelOption.entries.map { it.modemPreset }.toSet()
|
||||
|
||||
assertEquals(
|
||||
"The set of ModemPresets in protobufs does not match the set of ModemPresets mapped in ChannelOption. " +
|
||||
"Check for removed presets in protobufs or duplicate mappings in ChannelOption.",
|
||||
protoPresets,
|
||||
mappedPresets,
|
||||
"The set of ModemPresets in protobufs does not match the set of ModemPresets mapped in ChannelOption. " +
|
||||
"Check for removed presets in protobufs or duplicate mappings in ChannelOption.",
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
"Each ChannelOption must map to a unique ModemPreset.",
|
||||
protoPresets.size,
|
||||
ChannelOption.entries.size,
|
||||
"Each ChannelOption must map to a unique ModemPreset.",
|
||||
)
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -16,10 +16,11 @@
|
||||
*/
|
||||
package org.meshtastic.core.model
|
||||
|
||||
class DeviceVersionTest {
|
||||
/*
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class DeviceVersionTest {
|
||||
|
||||
/** make sure we match the python and device code behavior */
|
||||
@Test
|
||||
fun canParse() {
|
||||
assertEquals(10000, DeviceVersion("1.0.0").asInt)
|
||||
@@ -28,5 +29,21 @@ class DeviceVersionTest {
|
||||
assertEquals(12357, DeviceVersion("1.23.57.abde123").asInt)
|
||||
}
|
||||
|
||||
*/
|
||||
@Test
|
||||
fun twoPartVersionAppends_zero() {
|
||||
assertEquals(20700, DeviceVersion("2.7").asInt)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun invalidVersionReturns_zero() {
|
||||
assertEquals(0, DeviceVersion("invalid").asInt)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun comparisonIsCorrect() {
|
||||
kotlin.test.assertTrue(DeviceVersion("2.7.12") >= DeviceVersion("2.7.11"))
|
||||
kotlin.test.assertTrue(DeviceVersion("3.0.0") > DeviceVersion("2.8.1"))
|
||||
assertEquals(DeviceVersion("2.7.12"), DeviceVersion("2.7.12"))
|
||||
kotlin.test.assertFalse(DeviceVersion("2.6.9") >= DeviceVersion("2.7.0"))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user