From 02f6fd67b8c02f9a05c27c01ece129b63ac18081 Mon Sep 17 00:00:00 2001 From: James Rich <2199651+jamesarich@users.noreply.github.com> Date: Fri, 10 Apr 2026 14:46:45 -0500 Subject: [PATCH] fix: clean up flaky, duplicated, and misplaced tests; remove redundant deps (#5048) --- .github/workflows/reusable-check.yml | 2 - core/barcode/build.gradle.kts | 3 - .../core/barcode/BarcodeScannerTest.kt | 29 ---- .../core/common/util/CommonUriTest.kt | 49 ------ core/database/build.gradle.kts | 2 - .../core/database/model/NodeTest.kt | 49 ------ .../database/DatabaseManagerEvictionTest.kt | 0 core/model/build.gradle.kts | 7 - core/service/build.gradle.kts | 2 - .../core/service/IMeshServiceContractTest.kt | 5 + .../core/service/ServiceClientTest.kt | 143 ------------------ .../core/ui/timezone/ZoneIdExtensionsTest.kt | 55 ------- feature/connections/build.gradle.kts | 7 - feature/firmware/build.gradle.kts | 7 - feature/intro/build.gradle.kts | 2 - feature/map/build.gradle.kts | 2 - feature/messaging/build.gradle.kts | 8 +- .../HomoglyphCharacterTransformTest.kt | 8 +- feature/node/build.gradle.kts | 2 - .../node/metrics/BaseMetricScreenTest.kt | 95 ------------ feature/settings/build.gradle.kts | 7 +- .../settings/debugging/DebugFiltersTest.kt | 134 ---------------- .../feature/settings/HomoglyphSettingTest.kt | 69 --------- 23 files changed, 11 insertions(+), 676 deletions(-) delete mode 100644 core/barcode/src/androidTest/kotlin/org/meshtastic/core/barcode/BarcodeScannerTest.kt delete mode 100644 core/common/src/androidHostTest/kotlin/org/meshtastic/core/common/util/CommonUriTest.kt delete mode 100644 core/database/src/androidHostTest/kotlin/org/meshtastic/core/database/model/NodeTest.kt rename core/database/src/{androidHostTest => commonTest}/kotlin/org/meshtastic/core/database/DatabaseManagerEvictionTest.kt (100%) rename core/service/src/{test => androidHostTest}/kotlin/org/meshtastic/core/service/IMeshServiceContractTest.kt (88%) delete mode 100644 core/service/src/test/kotlin/org/meshtastic/core/service/ServiceClientTest.kt delete mode 100644 core/ui/src/androidHostTest/kotlin/org/meshtastic/core/ui/timezone/ZoneIdExtensionsTest.kt rename feature/messaging/src/{androidHostTest => commonTest}/kotlin/org/meshtastic/feature/messaging/HomoglyphCharacterTransformTest.kt (89%) delete mode 100644 feature/node/src/test/kotlin/org/meshtastic/feature/node/metrics/BaseMetricScreenTest.kt delete mode 100644 feature/settings/src/androidHostTest/kotlin/org/meshtastic/feature/settings/debugging/DebugFiltersTest.kt delete mode 100644 feature/settings/src/test/kotlin/org/meshtastic/feature/settings/HomoglyphSettingTest.kt diff --git a/.github/workflows/reusable-check.yml b/.github/workflows/reusable-check.yml index ce24c1b66..75557fe00 100644 --- a/.github/workflows/reusable-check.yml +++ b/.github/workflows/reusable-check.yml @@ -294,8 +294,6 @@ jobs: tasks+=( "app:connectedFdroidDebugAndroidTest" "app:connectedGoogleDebugAndroidTest" - "core:barcode:connectedFdroidDebugAndroidTest" - "core:barcode:connectedGoogleDebugAndroidTest" ) fi diff --git a/core/barcode/build.gradle.kts b/core/barcode/build.gradle.kts index 0be6e2fa7..c2533dd3c 100644 --- a/core/barcode/build.gradle.kts +++ b/core/barcode/build.gradle.kts @@ -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) } diff --git a/core/barcode/src/androidTest/kotlin/org/meshtastic/core/barcode/BarcodeScannerTest.kt b/core/barcode/src/androidTest/kotlin/org/meshtastic/core/barcode/BarcodeScannerTest.kt deleted file mode 100644 index 6e36ca79a..000000000 --- a/core/barcode/src/androidTest/kotlin/org/meshtastic/core/barcode/BarcodeScannerTest.kt +++ /dev/null @@ -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 . - */ -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 - } -} diff --git a/core/common/src/androidHostTest/kotlin/org/meshtastic/core/common/util/CommonUriTest.kt b/core/common/src/androidHostTest/kotlin/org/meshtastic/core/common/util/CommonUriTest.kt deleted file mode 100644 index fc8c8d04e..000000000 --- a/core/common/src/androidHostTest/kotlin/org/meshtastic/core/common/util/CommonUriTest.kt +++ /dev/null @@ -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 . - */ -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)) - } -} diff --git a/core/database/build.gradle.kts b/core/database/build.gradle.kts index 6f5ae71ed..4ebdfbb92 100644 --- a/core/database/build.gradle.kts +++ b/core/database/build.gradle.kts @@ -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 { diff --git a/core/database/src/androidHostTest/kotlin/org/meshtastic/core/database/model/NodeTest.kt b/core/database/src/androidHostTest/kotlin/org/meshtastic/core/database/model/NodeTest.kt deleted file mode 100644 index 163e03b9e..000000000 --- a/core/database/src/androidHostTest/kotlin/org/meshtastic/core/database/model/NodeTest.kt +++ /dev/null @@ -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 . - */ -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) - } -} diff --git a/core/database/src/androidHostTest/kotlin/org/meshtastic/core/database/DatabaseManagerEvictionTest.kt b/core/database/src/commonTest/kotlin/org/meshtastic/core/database/DatabaseManagerEvictionTest.kt similarity index 100% rename from core/database/src/androidHostTest/kotlin/org/meshtastic/core/database/DatabaseManagerEvictionTest.kt rename to core/database/src/commonTest/kotlin/org/meshtastic/core/database/DatabaseManagerEvictionTest.kt diff --git a/core/model/build.gradle.kts b/core/model/build.gradle.kts index 4726457fd..4e01fc223 100644 --- a/core/model/build.gradle.kts +++ b/core/model/build.gradle.kts @@ -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) diff --git a/core/service/build.gradle.kts b/core/service/build.gradle.kts index 2e0b6965d..1c6b56346 100644 --- a/core/service/build.gradle.kts +++ b/core/service/build.gradle.kts @@ -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) } diff --git a/core/service/src/test/kotlin/org/meshtastic/core/service/IMeshServiceContractTest.kt b/core/service/src/androidHostTest/kotlin/org/meshtastic/core/service/IMeshServiceContractTest.kt similarity index 88% rename from core/service/src/test/kotlin/org/meshtastic/core/service/IMeshServiceContractTest.kt rename to core/service/src/androidHostTest/kotlin/org/meshtastic/core/service/IMeshServiceContractTest.kt index a2c02427e..c37f63fb4 100644 --- a/core/service/src/test/kotlin/org/meshtastic/core/service/IMeshServiceContractTest.kt +++ b/core/service/src/androidHostTest/kotlin/org/meshtastic/core/service/IMeshServiceContractTest.kt @@ -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 diff --git a/core/service/src/test/kotlin/org/meshtastic/core/service/ServiceClientTest.kt b/core/service/src/test/kotlin/org/meshtastic/core/service/ServiceClientTest.kt deleted file mode 100644 index 4548fe931..000000000 --- a/core/service/src/test/kotlin/org/meshtastic/core/service/ServiceClientTest.kt +++ /dev/null @@ -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 . - */ -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() } - private val client = ServiceClient(stubFactory) - private val context = mock(MockMode.autofill) - private val intent = mock() - private val binder = mock() - - @Test - fun `connect binds service successfully`() = runTest { - val slot = Capture.slot() - 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() - // 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 { client.connect(context, intent, 0) } - } - - @Test - fun `waitConnect blocks until connected`() { - val slot = Capture.slot() - 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() - 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") - } - } -} diff --git a/core/ui/src/androidHostTest/kotlin/org/meshtastic/core/ui/timezone/ZoneIdExtensionsTest.kt b/core/ui/src/androidHostTest/kotlin/org/meshtastic/core/ui/timezone/ZoneIdExtensionsTest.kt deleted file mode 100644 index 6d055886a..000000000 --- a/core/ui/src/androidHostTest/kotlin/org/meshtastic/core/ui/timezone/ZoneIdExtensionsTest.kt +++ /dev/null @@ -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 . - */ -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()) } - } -} diff --git a/feature/connections/build.gradle.kts b/feature/connections/build.gradle.kts index 9ac1a69ba..f6fb40ae8 100644 --- a/feature/connections/build.gradle.kts +++ b/feature/connections/build.gradle.kts @@ -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) - } - } } } diff --git a/feature/firmware/build.gradle.kts b/feature/firmware/build.gradle.kts index cf8d08e8b..8fee603bf 100644 --- a/feature/firmware/build.gradle.kts +++ b/feature/firmware/build.gradle.kts @@ -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) diff --git a/feature/intro/build.gradle.kts b/feature/intro/build.gradle.kts index e93ce2924..fe05a2b43 100644 --- a/feature/intro/build.gradle.kts +++ b/feature/intro/build.gradle.kts @@ -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) } diff --git a/feature/map/build.gradle.kts b/feature/map/build.gradle.kts index e417843e1..fff9fe21b 100644 --- a/feature/map/build.gradle.kts +++ b/feature/map/build.gradle.kts @@ -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) } } } diff --git a/feature/messaging/build.gradle.kts b/feature/messaging/build.gradle.kts index e6634e0a1..e06b417b7 100644 --- a/feature/messaging/build.gradle.kts +++ b/feature/messaging/build.gradle.kts @@ -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) } } } } diff --git a/feature/messaging/src/androidHostTest/kotlin/org/meshtastic/feature/messaging/HomoglyphCharacterTransformTest.kt b/feature/messaging/src/commonTest/kotlin/org/meshtastic/feature/messaging/HomoglyphCharacterTransformTest.kt similarity index 89% rename from feature/messaging/src/androidHostTest/kotlin/org/meshtastic/feature/messaging/HomoglyphCharacterTransformTest.kt rename to feature/messaging/src/commonTest/kotlin/org/meshtastic/feature/messaging/HomoglyphCharacterTransformTest.kt index f75031fa8..30ec27f16 100644 --- a/feature/messaging/src/androidHostTest/kotlin/org/meshtastic/feature/messaging/HomoglyphCharacterTransformTest.kt +++ b/feature/messaging/src/commonTest/kotlin/org/meshtastic/feature/messaging/HomoglyphCharacterTransformTest.kt @@ -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) } diff --git a/feature/node/build.gradle.kts b/feature/node/build.gradle.kts index 2e408d341..6195fb13b 100644 --- a/feature/node/build.gradle.kts +++ b/feature/node/build.gradle.kts @@ -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) diff --git a/feature/node/src/test/kotlin/org/meshtastic/feature/node/metrics/BaseMetricScreenTest.kt b/feature/node/src/test/kotlin/org/meshtastic/feature/node/metrics/BaseMetricScreenTest.kt deleted file mode 100644 index 99572b3a9..000000000 --- a/feature/node/src/test/kotlin/org/meshtastic/feature/node/metrics/BaseMetricScreenTest.kt +++ /dev/null @@ -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 . - */ -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() - - 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) - } -} diff --git a/feature/settings/build.gradle.kts b/feature/settings/build.gradle.kts index 5419e3276..4b868fbc4 100644 --- a/feature/settings/build.gradle.kts +++ b/feature/settings/build.gradle.kts @@ -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) diff --git a/feature/settings/src/androidHostTest/kotlin/org/meshtastic/feature/settings/debugging/DebugFiltersTest.kt b/feature/settings/src/androidHostTest/kotlin/org/meshtastic/feature/settings/debugging/DebugFiltersTest.kt deleted file mode 100644 index aeef9129d..000000000 --- a/feature/settings/src/androidHostTest/kotlin/org/meshtastic/feature/settings/debugging/DebugFiltersTest.kt +++ /dev/null @@ -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 . - */ -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()) } - 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()) } - 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() - } -} diff --git a/feature/settings/src/test/kotlin/org/meshtastic/feature/settings/HomoglyphSettingTest.kt b/feature/settings/src/test/kotlin/org/meshtastic/feature/settings/HomoglyphSettingTest.kt deleted file mode 100644 index 8ffb10fae..000000000 --- a/feature/settings/src/test/kotlin/org/meshtastic/feature/settings/HomoglyphSettingTest.kt +++ /dev/null @@ -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 . - */ -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() - } -}