mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
fix: clean up flaky, duplicated, and misplaced tests; remove redundant deps (#5048)
This commit is contained in:
parent
e70dabe94d
commit
02f6fd67b8
23 changed files with 11 additions and 676 deletions
2
.github/workflows/reusable-check.yml
vendored
2
.github/workflows/reusable-check.yml
vendored
|
|
@ -294,8 +294,6 @@ jobs:
|
|||
tasks+=(
|
||||
"app:connectedFdroidDebugAndroidTest"
|
||||
"app:connectedGoogleDebugAndroidTest"
|
||||
"core:barcode:connectedFdroidDebugAndroidTest"
|
||||
"core:barcode:connectedGoogleDebugAndroidTest"
|
||||
)
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -53,8 +53,5 @@ dependencies {
|
|||
testRuntimeOnly(libs.junit.vintage.engine)
|
||||
testImplementation(libs.robolectric)
|
||||
testImplementation(libs.androidx.compose.ui.test.junit4)
|
||||
|
||||
androidTestImplementation(libs.androidx.test.ext.junit)
|
||||
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
|
||||
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,29 +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.barcode
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class BarcodeScannerTest {
|
||||
@Test
|
||||
fun placeholder() {
|
||||
// Placeholder for AndroidTest
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* 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.common.util
|
||||
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(sdk = [34])
|
||||
class CommonUriTest {
|
||||
|
||||
@Test
|
||||
fun testParse() {
|
||||
val uri = CommonUri.parse("https://meshtastic.org/path/to/page?param1=value1¶m2=true#fragment")
|
||||
assertEquals("meshtastic.org", uri.host)
|
||||
assertEquals("fragment", uri.fragment)
|
||||
assertEquals(listOf("path", "to", "page"), uri.pathSegments)
|
||||
assertEquals("value1", uri.getQueryParameter("param1"))
|
||||
assertTrue(uri.getBooleanQueryParameter("param2", false))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBooleanParameters() {
|
||||
val uri = CommonUri.parse("meshtastic://test?t1=true&t2=1&t3=yes&f1=false&f2=0")
|
||||
assertTrue(uri.getBooleanQueryParameter("t1", false))
|
||||
assertTrue(uri.getBooleanQueryParameter("t2", false))
|
||||
assertTrue(uri.getBooleanQueryParameter("t3", false))
|
||||
assertTrue(!uri.getBooleanQueryParameter("f1", true))
|
||||
assertTrue(!uri.getBooleanQueryParameter("f2", true))
|
||||
}
|
||||
}
|
||||
|
|
@ -57,10 +57,8 @@ kotlin {
|
|||
dependencies {
|
||||
implementation(libs.androidx.sqlite.bundled)
|
||||
implementation(libs.androidx.room.testing)
|
||||
implementation(libs.androidx.test.core)
|
||||
implementation(libs.androidx.test.ext.junit)
|
||||
implementation(libs.junit)
|
||||
implementation(libs.robolectric)
|
||||
}
|
||||
}
|
||||
val androidDeviceTest by getting {
|
||||
|
|
|
|||
|
|
@ -1,49 +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
|
||||
|
||||
import org.meshtastic.proto.HardwareModel
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class NodeTest {
|
||||
|
||||
@Test
|
||||
fun `createFallback produces expected node data`() {
|
||||
val nodeNum = 0x12345678
|
||||
val prefix = "Node"
|
||||
val node = Node.createFallback(nodeNum, prefix)
|
||||
|
||||
assertEquals(nodeNum, node.num)
|
||||
assertEquals("!12345678", node.user.id)
|
||||
assertEquals("Node 5678", node.user.long_name)
|
||||
assertEquals("5678", node.user.short_name)
|
||||
assertEquals(HardwareModel.UNSET, node.user.hw_model)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `createFallback pads short IDs with zeros`() {
|
||||
val nodeNum = 0x1
|
||||
val prefix = "Node"
|
||||
val node = Node.createFallback(nodeNum, prefix)
|
||||
|
||||
assertEquals(nodeNum, node.num)
|
||||
assertEquals("!00000001", node.user.id)
|
||||
assertEquals("Node 0001", node.user.long_name)
|
||||
assertEquals("0001", node.user.short_name)
|
||||
}
|
||||
}
|
||||
|
|
@ -52,13 +52,6 @@ kotlin {
|
|||
api(libs.androidx.annotation)
|
||||
api(libs.androidx.core.ktx)
|
||||
}
|
||||
val androidHostTest by getting {
|
||||
dependencies {
|
||||
implementation(libs.junit)
|
||||
implementation(libs.robolectric)
|
||||
implementation(libs.androidx.test.ext.junit)
|
||||
}
|
||||
}
|
||||
val androidDeviceTest by getting {
|
||||
dependencies {
|
||||
implementation(libs.androidx.test.ext.junit)
|
||||
|
|
|
|||
|
|
@ -60,8 +60,6 @@ kotlin {
|
|||
val androidHostTest by getting {
|
||||
dependencies {
|
||||
implementation(projects.core.testing)
|
||||
implementation(libs.robolectric)
|
||||
implementation(libs.androidx.test.core)
|
||||
implementation(libs.androidx.test.ext.junit)
|
||||
implementation(libs.androidx.work.testing)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,12 +16,17 @@
|
|||
*/
|
||||
package org.meshtastic.core.service
|
||||
|
||||
import org.junit.runner.RunWith
|
||||
import org.meshtastic.core.service.testing.FakeIMeshService
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
/** Test to verify that the AIDL contract is correctly implemented by our test harness. */
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(sdk = [34])
|
||||
class IMeshServiceContractTest {
|
||||
|
||||
@Test
|
||||
|
|
@ -1,143 +0,0 @@
|
|||
/*
|
||||
* 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.service
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.ServiceConnection
|
||||
import android.os.IBinder
|
||||
import android.os.IInterface
|
||||
import dev.mokkery.MockMode
|
||||
import dev.mokkery.every
|
||||
import dev.mokkery.matcher.any
|
||||
import dev.mokkery.matcher.capture.Capture
|
||||
import dev.mokkery.matcher.capture.capture
|
||||
import dev.mokkery.mock
|
||||
import dev.mokkery.verify
|
||||
import dev.mokkery.verify.exactly
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.concurrent.thread
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertNull
|
||||
import kotlin.test.fail
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class ServiceClientTest {
|
||||
|
||||
interface MyInterface : IInterface
|
||||
|
||||
private val stubFactory: (IBinder) -> MyInterface = { _ -> mock<MyInterface>() }
|
||||
private val client = ServiceClient(stubFactory)
|
||||
private val context = mock<Context>(MockMode.autofill)
|
||||
private val intent = mock<Intent>()
|
||||
private val binder = mock<IBinder>()
|
||||
|
||||
@Test
|
||||
fun `connect binds service successfully`() = runTest {
|
||||
val slot = Capture.slot<ServiceConnection>()
|
||||
every { context.bindService(any(), capture(slot), any()) } returns true
|
||||
|
||||
client.connect(context, intent, 0)
|
||||
|
||||
verify { context.bindService(intent, any(), 0) }
|
||||
|
||||
// Simulate connection
|
||||
try {
|
||||
slot.get().onServiceConnected(ComponentName("pkg", "cls"), binder)
|
||||
assertNotNull(client.serviceP)
|
||||
} catch (e: NoSuchElementException) {
|
||||
fail("ServiceConnection was not captured")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `connect retries on failure`() = runTest {
|
||||
val slot = Capture.slot<ServiceConnection>()
|
||||
// First attempt fails, second succeeds
|
||||
every { context.bindService(any(), capture(slot), any()) } sequentially
|
||||
{
|
||||
returns(false)
|
||||
returns(true)
|
||||
}
|
||||
|
||||
client.connect(context, intent, 0)
|
||||
|
||||
verify(exactly(2)) { context.bindService(intent, any(), 0) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `connect throws exception after two failures`() = runTest {
|
||||
every { context.bindService(any(), any(), any()) } returns false
|
||||
assertFailsWith<BindFailedException> { client.connect(context, intent, 0) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `waitConnect blocks until connected`() {
|
||||
val slot = Capture.slot<ServiceConnection>()
|
||||
every { context.bindService(any(), capture(slot), any()) } returns true
|
||||
|
||||
// Run connect in a coroutine scope (it's suspend)
|
||||
runTest { client.connect(context, intent, 0) }
|
||||
|
||||
val latch = CountDownLatch(1)
|
||||
thread {
|
||||
client.waitConnect()
|
||||
latch.countDown()
|
||||
}
|
||||
|
||||
// Verify it's blocked (wait a bit)
|
||||
if (latch.await(100, TimeUnit.MILLISECONDS)) {
|
||||
fail("waitConnect should block until connected")
|
||||
}
|
||||
|
||||
// Simulate connection
|
||||
try {
|
||||
slot.get().onServiceConnected(ComponentName("pkg", "cls"), binder)
|
||||
} catch (e: NoSuchElementException) {
|
||||
fail("ServiceConnection was not captured")
|
||||
}
|
||||
|
||||
// Verify it unblocks
|
||||
if (!latch.await(1, TimeUnit.SECONDS)) {
|
||||
fail("waitConnect should unblock after connection")
|
||||
}
|
||||
|
||||
assertNotNull(client.serviceP)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `close unbinds service`() = runTest {
|
||||
val slot = Capture.slot<ServiceConnection>()
|
||||
every { context.bindService(any(), capture(slot), any()) } returns true
|
||||
|
||||
client.connect(context, intent, 0)
|
||||
|
||||
try {
|
||||
client.close()
|
||||
verify { context.unbindService(slot.get()) }
|
||||
assertNull(client.serviceP)
|
||||
} catch (e: NoSuchElementException) {
|
||||
fail("ServiceConnection was not captured")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,55 +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.ui.timezone
|
||||
|
||||
import kotlinx.datetime.TimeZone
|
||||
import org.meshtastic.core.model.util.toPosixString
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class ZoneIdExtensionsTest {
|
||||
|
||||
@Test
|
||||
fun `test POSIX string generation`() {
|
||||
val zoneMap =
|
||||
mapOf(
|
||||
"US/Hawaii" to "HST10",
|
||||
"US/Alaska" to "AKST9AKDT,M3.2.0,M11.1.0",
|
||||
"US/Pacific" to "PST8PDT,M3.2.0,M11.1.0",
|
||||
"US/Arizona" to "MST7",
|
||||
"US/Mountain" to "MST7MDT,M3.2.0,M11.1.0",
|
||||
"US/Central" to "CST6CDT,M3.2.0,M11.1.0",
|
||||
"US/Eastern" to "EST5EDT,M3.2.0,M11.1.0",
|
||||
"America/Sao_Paulo" to "BRT3",
|
||||
"UTC" to "UTC0",
|
||||
"Europe/London" to "GMT0BST,M3.5.0/1,M10.5.0",
|
||||
"Europe/Lisbon" to "WET0WEST,M3.5.0/1,M10.5.0",
|
||||
"Europe/Budapest" to "CET-1CEST,M3.5.0,M10.5.0/3",
|
||||
"Europe/Kiev" to "EET-2EEST,M3.5.0/3,M10.5.0/4",
|
||||
"Africa/Cairo" to "EET-2EEST,M4.5.5/0,M10.5.5/0",
|
||||
"Asia/Kolkata" to "IST-5:30",
|
||||
"Asia/Hong_Kong" to "HKT-8",
|
||||
"Asia/Tokyo" to "JST-9",
|
||||
"Australia/Perth" to "AWST-8",
|
||||
"Australia/Adelaide" to "ACST-9:30ACDT,M10.1.0,M4.1.0/3",
|
||||
"Australia/Sydney" to "AEST-10AEDT,M10.1.0,M4.1.0/3",
|
||||
"Pacific/Auckland" to "NZST-12NZDT,M9.5.0,M4.1.0/3",
|
||||
)
|
||||
|
||||
zoneMap.forEach { (tz, expected) -> assertEquals(expected, TimeZone.of(tz).toPosixString()) }
|
||||
}
|
||||
}
|
||||
|
|
@ -49,12 +49,5 @@ kotlin {
|
|||
}
|
||||
|
||||
androidMain.dependencies { implementation(libs.usb.serial.android) }
|
||||
|
||||
val androidHostTest by getting {
|
||||
dependencies {
|
||||
implementation(libs.androidx.test.core)
|
||||
implementation(libs.robolectric)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,16 +58,9 @@ kotlin {
|
|||
|
||||
androidMain.dependencies { implementation(libs.markdown.renderer.android) }
|
||||
|
||||
commonTest.dependencies {
|
||||
implementation(projects.core.testing)
|
||||
implementation(libs.turbine)
|
||||
}
|
||||
|
||||
val androidHostTest by getting {
|
||||
dependencies {
|
||||
implementation(libs.junit)
|
||||
implementation(libs.robolectric)
|
||||
implementation(libs.turbine)
|
||||
implementation(libs.kotlinx.coroutines.test)
|
||||
implementation(libs.androidx.compose.ui.test.junit4)
|
||||
implementation(libs.androidx.test.ext.junit)
|
||||
|
|
|
|||
|
|
@ -42,9 +42,7 @@ kotlin {
|
|||
val androidHostTest by getting {
|
||||
dependencies {
|
||||
implementation(libs.junit)
|
||||
implementation(libs.robolectric)
|
||||
implementation(project.dependencies.platform(libs.androidx.compose.bom))
|
||||
implementation(libs.androidx.test.core)
|
||||
implementation(libs.kotlinx.coroutines.test)
|
||||
implementation(libs.androidx.compose.ui.test.junit4)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,10 +47,8 @@ kotlin {
|
|||
val androidHostTest by getting {
|
||||
dependencies {
|
||||
implementation(libs.junit)
|
||||
implementation(libs.robolectric)
|
||||
implementation(project.dependencies.platform(libs.androidx.compose.bom))
|
||||
implementation(libs.kotlinx.coroutines.test)
|
||||
implementation(libs.androidx.test.core)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,12 +56,6 @@ kotlin {
|
|||
|
||||
androidMain.dependencies { implementation(libs.androidx.work.runtime.ktx) }
|
||||
|
||||
val androidHostTest by getting {
|
||||
dependencies {
|
||||
implementation(libs.androidx.work.testing)
|
||||
implementation(libs.androidx.test.core)
|
||||
implementation(libs.robolectric)
|
||||
}
|
||||
}
|
||||
val androidHostTest by getting { dependencies { implementation(libs.androidx.work.testing) } }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ class HomoglyphCharacterTransformTest {
|
|||
fun `optimizeUtf8StringWithHomoglyphs shrinks binary size of cyrillic text containing some homoglyphs`() {
|
||||
val testString = "Мештастик - это проект с открытым исходным кодом"
|
||||
val transformedTestString = HomoglyphCharacterStringTransformer.optimizeUtf8StringWithHomoglyphs(testString)
|
||||
val testStringBytes = testString.toByteArray(charset = Charsets.UTF_8)
|
||||
val transformedTestStringBytes = transformedTestString.toByteArray(charset = Charsets.UTF_8)
|
||||
val testStringBytes = testString.encodeToByteArray()
|
||||
val transformedTestStringBytes = transformedTestString.encodeToByteArray()
|
||||
val transformedStringBinarySizeShrinked = transformedTestStringBytes.size < testStringBytes.size
|
||||
assertTrue(transformedStringBinarySizeShrinked)
|
||||
}
|
||||
|
|
@ -37,8 +37,8 @@ class HomoglyphCharacterTransformTest {
|
|||
fun `optimizeUtf8StringWithHomoglyphs shrinks binary size in half of cyrillic text containing only homoglyphs`() {
|
||||
val testString = "Косуха"
|
||||
val transformedTestString = HomoglyphCharacterStringTransformer.optimizeUtf8StringWithHomoglyphs(testString)
|
||||
val testStringBytes = testString.toByteArray(charset = Charsets.UTF_8)
|
||||
val transformedTestStringBytes = transformedTestString.toByteArray(charset = Charsets.UTF_8)
|
||||
val testStringBytes = testString.encodeToByteArray()
|
||||
val transformedTestStringBytes = transformedTestString.encodeToByteArray()
|
||||
assertEquals(transformedTestStringBytes.size, testStringBytes.size / 2)
|
||||
}
|
||||
|
||||
|
|
@ -66,8 +66,6 @@ kotlin {
|
|||
val androidHostTest by getting {
|
||||
dependencies {
|
||||
implementation(libs.junit)
|
||||
implementation(libs.robolectric)
|
||||
implementation(libs.turbine)
|
||||
implementation(libs.kotlinx.coroutines.test)
|
||||
implementation(libs.androidx.compose.ui.test.junit4)
|
||||
implementation(libs.androidx.test.ext.junit)
|
||||
|
|
|
|||
|
|
@ -1,95 +0,0 @@
|
|||
/*
|
||||
* 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.feature.node.metrics
|
||||
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithTag
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.meshtastic.core.model.TelemetryType
|
||||
import org.meshtastic.core.resources.Res
|
||||
import org.meshtastic.core.resources.device_metrics_log
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(sdk = [34])
|
||||
class BaseMetricScreenTest {
|
||||
|
||||
@get:Rule val composeTestRule = createComposeRule()
|
||||
|
||||
@Test
|
||||
fun baseMetricScreen_displaysTitleAndNodeName() {
|
||||
val nodeName = "Test Node 123"
|
||||
val testData = listOf("Item 1", "Item 2")
|
||||
|
||||
composeTestRule.setContent {
|
||||
AppTheme {
|
||||
BaseMetricScreen(
|
||||
onNavigateUp = {},
|
||||
telemetryType = TelemetryType.DEVICE,
|
||||
titleRes = Res.string.device_metrics_log,
|
||||
nodeName = nodeName,
|
||||
data = testData,
|
||||
timeProvider = { 0.0 },
|
||||
chartPart = { _, _, _, _ -> Text("Chart Placeholder") },
|
||||
listPart = { _, _, _, _ -> Text("List Placeholder") },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Verify Node Name is displayed (MainAppBar title)
|
||||
composeTestRule.onNodeWithText(nodeName).assertIsDisplayed()
|
||||
|
||||
// Verify Placeholders are displayed
|
||||
composeTestRule.onNodeWithText("Chart Placeholder").assertIsDisplayed()
|
||||
composeTestRule.onNodeWithText("List Placeholder").assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun baseMetricScreen_refreshButtonTriggersCallback() {
|
||||
var refreshClicked = false
|
||||
val testData = emptyList<String>()
|
||||
|
||||
composeTestRule.setContent {
|
||||
AppTheme {
|
||||
BaseMetricScreen(
|
||||
onNavigateUp = {},
|
||||
telemetryType = TelemetryType.DEVICE,
|
||||
titleRes = Res.string.device_metrics_log,
|
||||
nodeName = "Node",
|
||||
data = testData,
|
||||
timeProvider = { 0.0 },
|
||||
onRequestTelemetry = { refreshClicked = true },
|
||||
chartPart = { _, _, _, _ -> },
|
||||
listPart = { _, _, _, _ -> },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithTag("refresh_button").performClick()
|
||||
|
||||
assertTrue("Refresh callback should be triggered", refreshClicked)
|
||||
}
|
||||
}
|
||||
|
|
@ -57,17 +57,12 @@ kotlin {
|
|||
implementation(libs.androidx.appcompat)
|
||||
}
|
||||
|
||||
commonTest.dependencies {
|
||||
implementation(project(":core:testing"))
|
||||
implementation(project(":core:datastore"))
|
||||
}
|
||||
commonTest.dependencies { implementation(project(":core:datastore")) }
|
||||
|
||||
val androidHostTest by getting {
|
||||
dependencies {
|
||||
implementation(project(":core:datastore"))
|
||||
implementation(libs.junit)
|
||||
implementation(libs.robolectric)
|
||||
implementation(libs.turbine)
|
||||
implementation(libs.kotlinx.coroutines.test)
|
||||
implementation(libs.androidx.compose.ui.test.junit4)
|
||||
implementation(libs.androidx.compose.ui.test.manifest)
|
||||
|
|
|
|||
|
|
@ -1,134 +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.feature.settings.debugging
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.junit4.v2.createComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.compose.ui.test.performTextInput
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.meshtastic.core.resources.Res
|
||||
import org.meshtastic.core.resources.debug_active_filters
|
||||
import org.meshtastic.core.resources.debug_filters
|
||||
import org.meshtastic.core.resources.getString
|
||||
import org.meshtastic.feature.settings.debugging.DebugViewModel.UiMeshLog
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@Config(sdk = [34])
|
||||
class DebugFiltersTest {
|
||||
|
||||
@get:Rule val composeTestRule = createComposeRule()
|
||||
|
||||
@Test
|
||||
fun debugFilterBar_showsFilterButtonAndMenu() {
|
||||
val filterLabel = getString(Res.string.debug_filters)
|
||||
composeTestRule.setContent {
|
||||
var filterTexts by remember { mutableStateOf(listOf<String>()) }
|
||||
var customFilterText by remember { mutableStateOf("") }
|
||||
val presetFilters = listOf("Error", "Warning", "Info")
|
||||
val logs =
|
||||
listOf(
|
||||
UiMeshLog(
|
||||
uuid = "1",
|
||||
messageType = "Info",
|
||||
formattedReceivedDate = "2024-01-01 12:00:00",
|
||||
logMessage = "Sample log message",
|
||||
),
|
||||
)
|
||||
DebugFilterBar(
|
||||
filterTexts = filterTexts,
|
||||
onFilterTextsChange = { filterTexts = it },
|
||||
customFilterText = customFilterText,
|
||||
onCustomFilterTextChange = { customFilterText = it },
|
||||
presetFilters = presetFilters,
|
||||
logs = logs,
|
||||
)
|
||||
}
|
||||
// The filter button should be visible
|
||||
composeTestRule.onNodeWithText(filterLabel).assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun debugFilterBar_addCustomFilter_displaysActiveFilter() {
|
||||
val context = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
val activeFiltersLabel = getString(Res.string.debug_active_filters)
|
||||
composeTestRule.setContent {
|
||||
var filterTexts by remember { mutableStateOf(listOf<String>()) }
|
||||
var customFilterText by remember { mutableStateOf("") }
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
DebugActiveFilters(
|
||||
filterTexts = filterTexts,
|
||||
onFilterTextsChange = { filterTexts = it },
|
||||
filterMode = FilterMode.OR,
|
||||
onFilterModeChange = {},
|
||||
)
|
||||
DebugCustomFilterInput(
|
||||
customFilterText = customFilterText,
|
||||
onCustomFilterTextChange = { customFilterText = it },
|
||||
filterTexts = filterTexts,
|
||||
onFilterTextsChange = { filterTexts = it },
|
||||
)
|
||||
}
|
||||
}
|
||||
with(composeTestRule) {
|
||||
// Add a custom filter
|
||||
onNodeWithText("Add custom filter").performTextInput("MyFilter")
|
||||
onNodeWithContentDescription("Add filter").performClick()
|
||||
// The active filters label and the filter chip should be visible
|
||||
onNodeWithText(activeFiltersLabel).assertIsDisplayed()
|
||||
onNodeWithText("MyFilter").assertIsDisplayed()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun debugActiveFilters_clearAllFilters_removesFilters() {
|
||||
val activeFiltersLabel = getString(Res.string.debug_active_filters)
|
||||
composeTestRule.setContent {
|
||||
var filterTexts by remember { mutableStateOf(listOf("A", "B")) }
|
||||
DebugActiveFilters(
|
||||
filterTexts = filterTexts,
|
||||
onFilterTextsChange = { filterTexts = it },
|
||||
filterMode = FilterMode.OR,
|
||||
onFilterModeChange = {},
|
||||
)
|
||||
}
|
||||
// The active filters label and chips should be visible
|
||||
composeTestRule.onNodeWithText(activeFiltersLabel).assertIsDisplayed()
|
||||
composeTestRule.onNodeWithText("A").assertIsDisplayed()
|
||||
composeTestRule.onNodeWithText("B").assertIsDisplayed()
|
||||
// Click the clear all filters button
|
||||
composeTestRule.onNodeWithContentDescription("Clear all filters").performClick()
|
||||
// The filter chips should no longer be visible
|
||||
composeTestRule.onNodeWithText("A").assertDoesNotExist()
|
||||
composeTestRule.onNodeWithText("B").assertDoesNotExist()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
* 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.feature.settings
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.junit4.v2.createComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.meshtastic.core.resources.Res
|
||||
import org.meshtastic.core.resources.getString
|
||||
import org.meshtastic.core.resources.use_homoglyph_characters_encoding
|
||||
import org.meshtastic.feature.settings.component.HomoglyphSetting
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import java.util.Locale
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(sdk = [34])
|
||||
class HomoglyphSettingTest {
|
||||
|
||||
@get:Rule val composeTestRule = createComposeRule()
|
||||
|
||||
@Test
|
||||
fun homoglyphSetting_isVisible_forRussianLocale() {
|
||||
val russianConfig = Configuration().apply { setLocale(Locale.forLanguageTag("ru")) }
|
||||
|
||||
composeTestRule.setContent {
|
||||
CompositionLocalProvider(LocalConfiguration provides russianConfig) {
|
||||
HomoglyphSetting(homoglyphEncodingEnabled = false, onToggle = {})
|
||||
}
|
||||
}
|
||||
|
||||
val expectedText = getString(Res.string.use_homoglyph_characters_encoding)
|
||||
composeTestRule.onNodeWithText(expectedText).assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun homoglyphSetting_isNotVisible_forEnglishLocale() {
|
||||
val englishConfig = Configuration().apply { setLocale(Locale.forLanguageTag("en")) }
|
||||
|
||||
composeTestRule.setContent {
|
||||
CompositionLocalProvider(LocalConfiguration provides englishConfig) {
|
||||
HomoglyphSetting(homoglyphEncodingEnabled = false, onToggle = {})
|
||||
}
|
||||
}
|
||||
|
||||
val expectedText = getString(Res.string.use_homoglyph_characters_encoding)
|
||||
composeTestRule.onNodeWithText(expectedText).assertDoesNotExist()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue