mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
feat(ci): shard test suite and enable JUnit 5 parallel execution (#4977)
This commit is contained in:
parent
7e041c00e1
commit
51251ab16a
80 changed files with 438 additions and 2730 deletions
|
|
@ -1,33 +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.data.repository
|
||||
|
||||
import org.junit.runner.RunWith
|
||||
import org.meshtastic.core.testing.setupTestContext
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import kotlin.test.BeforeTest
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(sdk = [34])
|
||||
class MeshLogRepositoryTest : CommonMeshLogRepositoryTest() {
|
||||
@BeforeTest
|
||||
fun setup() {
|
||||
setupTestContext()
|
||||
setupRepo()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +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.data.repository
|
||||
|
||||
import org.junit.runner.RunWith
|
||||
import org.meshtastic.core.testing.setupTestContext
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import kotlin.test.BeforeTest
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(sdk = [34])
|
||||
class NodeRepositoryTest : CommonNodeRepositoryTest() {
|
||||
@BeforeTest
|
||||
fun setup() {
|
||||
setupTestContext()
|
||||
setupRepo()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +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.data.repository
|
||||
|
||||
import org.junit.runner.RunWith
|
||||
import org.meshtastic.core.testing.setupTestContext
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import kotlin.test.BeforeTest
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(sdk = [34])
|
||||
class PacketRepositoryTest : CommonPacketRepositoryTest() {
|
||||
@BeforeTest
|
||||
fun setup() {
|
||||
setupTestContext()
|
||||
setupRepo()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,100 +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.data.manager
|
||||
|
||||
class CommandSenderHopLimitTest {
|
||||
/*
|
||||
|
||||
|
||||
|
||||
private val localConfigFlow = MutableStateFlow(LocalConfig())
|
||||
private val testDispatcher = UnconfinedTestDispatcher()
|
||||
private val testScope = CoroutineScope(testDispatcher)
|
||||
|
||||
private lateinit var commandSender: CommandSender
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
val myNum = 123
|
||||
val myNode = Node(num = myNum, user = User(id = "!id", long_name = "long", short_name = "shrt"))
|
||||
every { radioConfigRepository.localConfigFlow } returns localConfigFlow
|
||||
every { nodeManager.myNodeNum } returns myNum
|
||||
every { nodeManager.nodeDBbyNodeNum } returns mapOf(myNum to myNode)
|
||||
|
||||
commandSender = CommandSenderImpl(packetHandler, nodeManager, radioConfigRepository)
|
||||
commandSender.start(testScope)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `sendData uses default hop limit when config hop limit is zero`() = runTest(testDispatcher) {
|
||||
val packet =
|
||||
DataPacket(
|
||||
to = DataPacket.ID_BROADCAST,
|
||||
bytes = byteArrayOf(1, 2, 3).toByteString(),
|
||||
dataType = 1, // PortNum.TEXT_MESSAGE_APP
|
||||
)
|
||||
|
||||
val meshPacketSlot = Capture.slot<MeshPacket>()
|
||||
|
||||
// Ensure localConfig has lora.hop_limit = 0
|
||||
localConfigFlow.value = LocalConfig(lora = Config.LoRaConfig(hop_limit = 0))
|
||||
|
||||
commandSender.sendData(packet)
|
||||
|
||||
|
||||
val capturedHopLimit = meshPacketSlot.captured.hop_limit ?: 0
|
||||
assertTrue("Hop limit should be greater than 0, but was $capturedHopLimit", capturedHopLimit > 0)
|
||||
assertEquals(3, capturedHopLimit)
|
||||
assertEquals(3, meshPacketSlot.captured.hop_start)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `sendData respects non-zero hop limit from config`() = runTest(testDispatcher) {
|
||||
val packet =
|
||||
DataPacket(to = DataPacket.ID_BROADCAST, bytes = byteArrayOf(1, 2, 3).toByteString(), dataType = 1)
|
||||
|
||||
val meshPacketSlot = Capture.slot<MeshPacket>()
|
||||
|
||||
localConfigFlow.value = LocalConfig(lora = Config.LoRaConfig(hop_limit = 7))
|
||||
|
||||
commandSender.sendData(packet)
|
||||
|
||||
assertEquals(7, meshPacketSlot.captured.hop_limit)
|
||||
assertEquals(7, meshPacketSlot.captured.hop_start)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `requestUserInfo sets hopStart equal to hopLimit`() = runTest(testDispatcher) {
|
||||
val destNum = 12345
|
||||
val meshPacketSlot = Capture.slot<MeshPacket>()
|
||||
|
||||
localConfigFlow.value = LocalConfig(lora = Config.LoRaConfig(hop_limit = 6))
|
||||
|
||||
// Mock node manager interactions
|
||||
// Note: we need to keep myNode in the map for requestUserInfo to not return early
|
||||
val myNum = 123
|
||||
val myNode = Node(num = myNum, user = User(id = "!id", long_name = "long", short_name = "shrt"))
|
||||
every { nodeManager.nodeDBbyNodeNum } returns mapOf(myNum to myNode)
|
||||
|
||||
commandSender.requestUserInfo(destNum)
|
||||
|
||||
assertEquals("Hop Limit should be 6", 6, meshPacketSlot.captured.hop_limit)
|
||||
assertEquals("Hop Start should be 6", 6, meshPacketSlot.captured.hop_start)
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
|
@ -1,67 +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.data.manager
|
||||
|
||||
class CommandSenderImplTest {
|
||||
/*
|
||||
|
||||
|
||||
private lateinit var commandSender: CommandSenderImpl
|
||||
private lateinit var nodeManager: NodeManager
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `generatePacketId produces unique non-zero IDs`() {
|
||||
val ids = mutableSetOf<Int>()
|
||||
repeat(1000) {
|
||||
val id = commandSender.generatePacketId()
|
||||
assertNotEquals(0, id)
|
||||
ids.add(id)
|
||||
}
|
||||
assertEquals(1000, ids.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `resolveNodeNum handles broadcast ID`() {
|
||||
assertEquals(DataPacket.NODENUM_BROADCAST, commandSender.resolveNodeNum(DataPacket.ID_BROADCAST))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `resolveNodeNum handles hex ID with exclamation mark`() {
|
||||
assertEquals(123, commandSender.resolveNodeNum("!0000007b"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `resolveNodeNum handles custom node ID from database`() {
|
||||
val nodeNum = 456
|
||||
val userId = "custom_id"
|
||||
val node = Node(num = nodeNum, user = User(id = userId))
|
||||
every { nodeManager.nodeDBbyID } returns mapOf(userId to node)
|
||||
|
||||
assertEquals(nodeNum, commandSender.resolveNodeNum(userId))
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException::class)
|
||||
fun `resolveNodeNum throws for unknown ID`() {
|
||||
commandSender.resolveNodeNum("unknown")
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
|
@ -1,115 +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.data.repository
|
||||
|
||||
class DeviceHardwareRepositoryTest {
|
||||
/*
|
||||
|
||||
|
||||
private val remoteDataSource: DeviceHardwareRemoteDataSource = mock()
|
||||
private val localDataSource: DeviceHardwareLocalDataSource = mock()
|
||||
private val jsonDataSource: DeviceHardwareJsonDataSource = mock()
|
||||
private val bootloaderOtaQuirksJsonDataSource: BootloaderOtaQuirksJsonDataSource = mock()
|
||||
private val testDispatcher = StandardTestDispatcher()
|
||||
private val dispatchers = CoroutineDispatchers(main = testDispatcher, io = testDispatcher, default = testDispatcher)
|
||||
|
||||
private val repository =
|
||||
DeviceHardwareRepositoryImpl(
|
||||
remoteDataSource,
|
||||
localDataSource,
|
||||
jsonDataSource,
|
||||
bootloaderOtaQuirksJsonDataSource,
|
||||
dispatchers,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `getDeviceHardwareByModel uses target for disambiguation`() = runTest(testDispatcher) {
|
||||
val hwModel = 50 // T_DECK
|
||||
val target = "tdeck-pro"
|
||||
val entities =
|
||||
listOf(createEntity(hwModel, "t-deck", "T-Deck"), createEntity(hwModel, "tdeck-pro", "T-Deck Pro"))
|
||||
|
||||
everySuspend { localDataSource.getByHwModel(hwModel) } returns entities
|
||||
every { bootloaderOtaQuirksJsonDataSource.loadBootloaderOtaQuirksFromJsonAsset() } returns emptyList()
|
||||
|
||||
val result = repository.getDeviceHardwareByModel(hwModel, target).getOrNull()
|
||||
|
||||
assertEquals("T-Deck Pro", result?.displayName)
|
||||
assertEquals("tdeck-pro", result?.platformioTarget)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getDeviceHardwareByModel falls back to first entity when target not found`() = runTest(testDispatcher) {
|
||||
val hwModel = 50
|
||||
val target = "unknown-variant"
|
||||
val entities =
|
||||
listOf(createEntity(hwModel, "t-deck", "T-Deck"), createEntity(hwModel, "t-deck-tft", "T-Deck TFT"))
|
||||
|
||||
everySuspend { localDataSource.getByHwModel(hwModel) } returns entities
|
||||
every { bootloaderOtaQuirksJsonDataSource.loadBootloaderOtaQuirksFromJsonAsset() } returns emptyList()
|
||||
|
||||
val result = repository.getDeviceHardwareByModel(hwModel, target).getOrNull()
|
||||
|
||||
// Should fall back to first entity if no exact match
|
||||
assertEquals("T-Deck", result?.displayName)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getDeviceHardwareByModel falls back to target lookup when hwModel not found`() = runTest(testDispatcher) {
|
||||
val hwModel = 0 // Unknown
|
||||
val target = "tdeck-pro"
|
||||
val entity = createEntity(102, "tdeck-pro", "T-Deck Pro")
|
||||
|
||||
everySuspend { localDataSource.getByHwModel(hwModel) } returns emptyList()
|
||||
everySuspend { localDataSource.getByTarget(target) } returns entity
|
||||
every { bootloaderOtaQuirksJsonDataSource.loadBootloaderOtaQuirksFromJsonAsset() } returns emptyList()
|
||||
|
||||
val result = repository.getDeviceHardwareByModel(hwModel, target).getOrNull()
|
||||
|
||||
assertEquals("T-Deck Pro", result?.displayName)
|
||||
assertEquals("tdeck-pro", result?.platformioTarget)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getDeviceHardwareByModel correctly sets isEsp32Arc for ESP32 devices`() = runTest(testDispatcher) {
|
||||
val hwModel = 50
|
||||
val entities = listOf(createEntity(hwModel, "t-deck", "T-Deck").copy(architecture = "esp32-s3"))
|
||||
|
||||
everySuspend { localDataSource.getByHwModel(hwModel) } returns entities
|
||||
every { bootloaderOtaQuirksJsonDataSource.loadBootloaderOtaQuirksFromJsonAsset() } returns emptyList()
|
||||
|
||||
val result = repository.getDeviceHardwareByModel(hwModel).getOrNull()
|
||||
|
||||
assertEquals(true, result?.isEsp32Arc)
|
||||
}
|
||||
|
||||
private fun createEntity(hwModel: Int, target: String, displayName: String) = DeviceHardwareEntity(
|
||||
activelySupported = true,
|
||||
architecture = "esp32-s3",
|
||||
displayName = displayName,
|
||||
hwModel = hwModel,
|
||||
hwModelSlug = "T_DECK",
|
||||
images = listOf("image.svg"), // MUST be non-empty to avoid being considered incomplete/stale
|
||||
platformioTarget = target,
|
||||
requiresDfu = false,
|
||||
supportLevel = 0,
|
||||
tags = emptyList(),
|
||||
lastUpdated = nowMillis,
|
||||
)
|
||||
|
||||
*/
|
||||
}
|
||||
|
|
@ -1,26 +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.data.repository
|
||||
|
||||
import kotlin.test.BeforeTest
|
||||
|
||||
class MeshLogRepositoryTest : CommonMeshLogRepositoryTest() {
|
||||
@BeforeTest
|
||||
fun setup() {
|
||||
setupRepo()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +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.data.repository
|
||||
|
||||
import kotlin.test.BeforeTest
|
||||
|
||||
class NodeRepositoryTest : CommonNodeRepositoryTest() {
|
||||
@BeforeTest
|
||||
fun setup() {
|
||||
setupRepo()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +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.data.repository
|
||||
|
||||
import kotlin.test.BeforeTest
|
||||
|
||||
class PacketRepositoryTest : CommonPacketRepositoryTest() {
|
||||
@BeforeTest
|
||||
fun setup() {
|
||||
setupRepo()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue