test(android): cover discovery and startup gates (#102758)

* test(android): cover discovery and startup gates

Co-authored-by: NianJiuZst <180004567+users.noreply.github.com>

* test(android): keep startup gate tests keystore-free
This commit is contained in:
Peter Steinberger 2026-07-09 13:25:40 +01:00 committed by GitHub
parent 64e80888d4
commit 411bf2ad4b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 144 additions and 26 deletions

View file

@ -139,7 +139,7 @@
},
{
"kind": "ui-state-text",
"line": 138,
"line": 143,
"path": "apps/android/app/src/main/java/ai/openclaw/app/MainViewModel.kt",
"source": "Searching…",
"surface": "android",
@ -147,7 +147,7 @@
},
{
"kind": "ui-state-text",
"line": 238,
"line": 243,
"path": "apps/android/app/src/main/java/ai/openclaw/app/MainViewModel.kt",
"source": "Mic off",
"surface": "android",
@ -155,7 +155,7 @@
},
{
"kind": "ui-state-text",
"line": 248,
"line": 253,
"path": "apps/android/app/src/main/java/ai/openclaw/app/MainViewModel.kt",
"source": "Off",
"surface": "android",
@ -379,7 +379,7 @@
},
{
"kind": "ui-state-text",
"line": 86,
"line": 106,
"path": "apps/android/app/src/main/java/ai/openclaw/app/gateway/GatewayDiscovery.kt",
"source": "Searching…",
"surface": "android",

View file

@ -46,6 +46,11 @@ data class ChatDraft(
val placement: ChatDraftPlacement,
)
internal fun shouldStartRuntimeOnForeground(
foreground: Boolean,
onboardingCompleted: Boolean,
): Boolean = foreground && onboardingCompleted
/**
* UI-facing bridge that exposes NodeRuntime and preference state as Compose-friendly StateFlows.
*/
@ -298,7 +303,12 @@ class MainViewModel(
*/
fun setForeground(value: Boolean) {
foreground = value
if (value && prefs.onboardingCompleted.value) {
if (
shouldStartRuntimeOnForeground(
foreground = value,
onboardingCompleted = prefs.onboardingCompleted.value,
)
) {
queueRuntimeStartup()
}
runtimeRef.value?.setForeground(value)

View file

@ -62,6 +62,26 @@ private fun createContextDnsResolver(context: Context): DnsResolver = DnsResolve
@Suppress("DEPRECATION")
private fun createLegacyDnsResolver(): DnsResolver = DnsResolver.getInstance()
internal fun gatewayDiscoveryStatusText(
localCount: Int,
wideAreaRcode: Int?,
wideAreaCount: Int,
): String {
val wide =
when (wideAreaRcode) {
null -> "Wide: ?"
Rcode.NOERROR -> "Wide: $wideAreaCount"
Rcode.NXDOMAIN -> "Wide: NXDOMAIN"
else -> "Wide: ${Rcode.string(wideAreaRcode)}"
}
return when {
localCount == 0 && wideAreaRcode == null -> "Searching for gateways…"
localCount == 0 -> wide
else -> "Local: $localCount$wide"
}
}
/**
* Watches local DNS-SD and optional wide-area DNS-SD for reachable OpenClaw gateways.
*/
@ -306,27 +326,12 @@ class GatewayDiscovery(
_gateways.value =
// Merge local and wide-area results deterministically for stable UI selection.
(localById.values + unicastById.values).sortedBy { it.name.lowercase() }
_statusText.value = buildStatusText()
}
private fun buildStatusText(): String {
val localCount = localById.size
val wideRcode = lastWideAreaRcode
val wideCount = lastWideAreaCount
val wide =
when (wideRcode) {
null -> "Wide: ?"
Rcode.NOERROR -> "Wide: $wideCount"
Rcode.NXDOMAIN -> "Wide: NXDOMAIN"
else -> "Wide: ${Rcode.string(wideRcode)}"
}
return when {
localCount == 0 && wideRcode == null -> "Searching for gateways…"
localCount == 0 -> "$wide"
else -> "Local: $localCount$wide"
}
_statusText.value =
gatewayDiscoveryStatusText(
localCount = localById.size,
wideAreaRcode = lastWideAreaRcode,
wideAreaCount = lastWideAreaCount,
)
}
private fun stableId(

View file

@ -0,0 +1,35 @@
package ai.openclaw.app
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
class MainViewModelTest {
@Test
fun foregroundStartupRequiresForegroundAndCompletedOnboarding() {
assertFalse(
shouldStartRuntimeOnForeground(
foreground = false,
onboardingCompleted = true,
),
)
assertFalse(
shouldStartRuntimeOnForeground(
foreground = true,
onboardingCompleted = false,
),
)
assertFalse(
shouldStartRuntimeOnForeground(
foreground = false,
onboardingCompleted = false,
),
)
assertTrue(
shouldStartRuntimeOnForeground(
foreground = true,
onboardingCompleted = true,
),
)
}
}

View file

@ -0,0 +1,68 @@
package ai.openclaw.app.gateway
import org.junit.Assert.assertEquals
import org.junit.Test
import org.xbill.DNS.Rcode
class GatewayDiscoveryTest {
@Test
fun statusTextFormatsLocalAndWideAreaDiscoveryStates() {
val cases =
listOf(
StatusCase(
localCount = 0,
wideAreaRcode = null,
wideAreaCount = 0,
expected = "Searching for gateways…",
),
StatusCase(
localCount = 0,
wideAreaRcode = Rcode.NOERROR,
wideAreaCount = 2,
expected = "Wide: 2",
),
StatusCase(
localCount = 1,
wideAreaRcode = Rcode.NOERROR,
wideAreaCount = 2,
expected = "Local: 1 • Wide: 2",
),
StatusCase(
localCount = 1,
wideAreaRcode = null,
wideAreaCount = 0,
expected = "Local: 1 • Wide: ?",
),
StatusCase(
localCount = 0,
wideAreaRcode = Rcode.NXDOMAIN,
wideAreaCount = 0,
expected = "Wide: NXDOMAIN",
),
StatusCase(
localCount = 2,
wideAreaRcode = Rcode.SERVFAIL,
wideAreaCount = 0,
expected = "Local: 2 • Wide: SERVFAIL",
),
)
for (case in cases) {
assertEquals(
case.expected,
gatewayDiscoveryStatusText(
localCount = case.localCount,
wideAreaRcode = case.wideAreaRcode,
wideAreaCount = case.wideAreaCount,
),
)
}
}
}
private data class StatusCase(
val localCount: Int,
val wideAreaRcode: Int?,
val wideAreaCount: Int,
val expected: String,
)