mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-03-27 18:21:58 -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:
@@ -20,6 +20,7 @@ import kotlinx.serialization.json.Json
|
||||
import org.meshtastic.core.model.MqttJsonPayload
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class MQTTRepositoryImplTest {
|
||||
|
||||
@@ -67,8 +68,8 @@ class MQTTRepositoryImplTest {
|
||||
val json = Json { ignoreUnknownKeys = true }
|
||||
val jsonStr = json.encodeToString(MqttJsonPayload.serializer(), payload)
|
||||
|
||||
assert(jsonStr.contains("\"type\":\"text\""))
|
||||
assert(jsonStr.contains("\"from\":12345678"))
|
||||
assert(jsonStr.contains("\"payload\":\"Hello World\""))
|
||||
assertTrue(jsonStr.contains("\"type\":\"text\""))
|
||||
assertTrue(jsonStr.contains("\"from\":12345678"))
|
||||
assertTrue(jsonStr.contains("\"payload\":\"Hello World\""))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import kotlinx.coroutines.flow.flowOn
|
||||
import org.koin.core.annotation.Single
|
||||
import java.io.IOException
|
||||
import java.net.InetAddress
|
||||
import java.net.NetworkInterface
|
||||
import javax.jmdns.JmDNS
|
||||
import javax.jmdns.ServiceEvent
|
||||
import javax.jmdns.ServiceListener
|
||||
@@ -34,9 +35,14 @@ class JvmServiceDiscovery : ServiceDiscovery {
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
override val resolvedServices: Flow<List<DiscoveredService>> =
|
||||
callbackFlow {
|
||||
trySend(emptyList()) // Emit initial empty list so downstream combine() is not blocked
|
||||
|
||||
val bindAddress = findLanAddress() ?: InetAddress.getLocalHost()
|
||||
Logger.i { "JmDNS binding to ${bindAddress.hostAddress}" }
|
||||
|
||||
val jmdns =
|
||||
try {
|
||||
JmDNS.create(InetAddress.getLocalHost())
|
||||
JmDNS.create(bindAddress)
|
||||
} catch (e: IOException) {
|
||||
Logger.e(e) { "Failed to create JmDNS" }
|
||||
null
|
||||
@@ -93,4 +99,24 @@ class JvmServiceDiscovery : ServiceDiscovery {
|
||||
}
|
||||
}
|
||||
.flowOn(Dispatchers.IO)
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Finds a non-loopback, up, IPv4 LAN address for JmDNS to bind to. On many systems (especially Windows),
|
||||
* [InetAddress.getLocalHost] resolves to `127.0.0.1` or `::1`, which prevents JmDNS from seeing multicast
|
||||
* traffic on the actual LAN interface.
|
||||
*/
|
||||
@Suppress("TooGenericExceptionCaught", "LoopWithTooManyJumpStatements")
|
||||
internal fun findLanAddress(): InetAddress? = try {
|
||||
NetworkInterface.getNetworkInterfaces()
|
||||
?.toList()
|
||||
.orEmpty()
|
||||
.filter { it.isUp && !it.isLoopback }
|
||||
.flatMap { it.inetAddresses.toList() }
|
||||
.firstOrNull { !it.isLoopbackAddress && it is java.net.Inet4Address }
|
||||
} catch (e: Exception) {
|
||||
Logger.w(e) { "Failed to enumerate network interfaces, falling back to getLocalHost()" }
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.network.repository
|
||||
|
||||
import app.cash.turbine.test
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class JvmServiceDiscoveryTest {
|
||||
|
||||
@Test
|
||||
fun `resolvedServices emits initial empty list immediately`() = runTest {
|
||||
val discovery = JvmServiceDiscovery()
|
||||
discovery.resolvedServices.test {
|
||||
val first = awaitItem()
|
||||
assertNotNull(first, "First emission should not be null")
|
||||
assertTrue(first.isEmpty(), "First emission should be an empty list")
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `findLanAddress returns non-loopback address or null`() {
|
||||
val address = JvmServiceDiscovery.findLanAddress()
|
||||
// On CI machines there may be no LAN interface, so null is acceptable
|
||||
if (address != null) {
|
||||
assertTrue(!address.isLoopbackAddress, "Address should not be loopback")
|
||||
assertTrue(address is java.net.Inet4Address, "Address should be IPv4")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `findLanAddress does not throw`() {
|
||||
// Ensure the method handles exceptions gracefully
|
||||
val result = runCatching { JvmServiceDiscovery.findLanAddress() }
|
||||
assertTrue(result.isSuccess, "findLanAddress should not throw: ${result.exceptionOrNull()}")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user