feat(ci): shard test suite and enable JUnit 5 parallel execution (#4977)

This commit is contained in:
James Rich 2026-04-03 08:08:49 -05:00 committed by GitHub
parent 7e041c00e1
commit 51251ab16a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
80 changed files with 438 additions and 2730 deletions

View file

@ -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()
}
}

View file

@ -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()
}
}

View file

@ -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()
}
}

View file

@ -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)
}
*/
}

View file

@ -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")
}
*/
}

View file

@ -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,
)
*/
}

View file

@ -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()
}
}

View file

@ -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()
}
}

View file

@ -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()
}
}