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,169 +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.node.list
/**
* Error handling tests for node feature.
*
* Tests edge cases, failure recovery, and boundary conditions.
*/
class NodeErrorHandlingTest {
/*
private lateinit var nodeRepository: FakeNodeRepository
private lateinit var radioController: FakeRadioController
@BeforeTest
fun setUp() {
kotlinx.coroutines.Dispatchers.setMain(kotlinx.coroutines.Dispatchers.Unconfined)
nodeRepository = FakeNodeRepository()
radioController = FakeRadioController()
}
@kotlin.test.AfterTest
fun tearDown() {
kotlinx.coroutines.Dispatchers.resetMain()
}
@Test
fun testGetNonexistentNode() = runTest {
val node = nodeRepository.getNode("!nonexistent")
// FakeNodeRepository returns a fallback node (never null)
node.user.id shouldBe "!nonexistent"
}
@Test
fun testDeleteNonexistentNode() = runTest {
val beforeCount = nodeRepository.nodeDBbyNum.value.size
nodeRepository.deleteNode(999)
val afterCount = nodeRepository.nodeDBbyNum.value.size
afterCount shouldBe beforeCount
}
@Test
fun testNodeDatabaseEmptyOnStart() = runTest {
val nodes = nodeRepository.nodeDBbyNum.value
nodes.size shouldBe 0
}
@Test
fun testRepeatedClear() = runTest {
nodeRepository.setNodes(TestDataFactory.createTestNodes(5))
nodeRepository.nodeDBbyNum.value.size shouldBe 5
// Clear multiple times
nodeRepository.clearNodeDB(preserveFavorites = false)
nodeRepository.clearNodeDB(preserveFavorites = false)
nodeRepository.clearNodeDB(preserveFavorites = false)
// Should still be empty
nodeRepository.nodeDBbyNum.value.size shouldBe 0
}
@Test
fun testSetEmptyNodeList() = runTest {
nodeRepository.setNodes(TestDataFactory.createTestNodes(3))
nodeRepository.nodeDBbyNum.value.size shouldBe 3
// Set to empty
nodeRepository.setNodes(emptyList())
nodeRepository.nodeDBbyNum.value.size shouldBe 0
}
@Test
fun testDeleteAllNodes() = runTest {
val nodes = TestDataFactory.createTestNodes(5)
nodeRepository.setNodes(nodes)
// Delete each node
nodes.forEach { node -> nodeRepository.deleteNode(node.num) }
nodeRepository.nodeDBbyNum.value.size shouldBe 0
}
@Test
fun testNodeMetadataOnDeletedNode() = runTest {
val node = TestDataFactory.createTestNode(num = 1, longName = "Test")
nodeRepository.setNodes(listOf(node))
// Delete node
nodeRepository.deleteNode(1)
// Try to get notes on deleted node
// Should not crash
assertTrue(true)
}
@Test
fun testNotesOnNonexistentNode() = runTest {
// Set notes on node that never existed
nodeRepository.setNodeNotes(999, "Notes")
// Should be no-op
nodeRepository.nodeDBbyNum.value.size shouldBe 0
}
@Test
fun testConnectionStateChangesDuringNodeManagement() = runTest {
radioController.setConnectionState(org.meshtastic.core.model.ConnectionState.Disconnected)
// Add nodes while disconnected (local operation)
nodeRepository.setNodes(TestDataFactory.createTestNodes(3))
nodeRepository.nodeDBbyNum.value.size shouldBe 3
// Switch to connected
radioController.setConnectionState(org.meshtastic.core.model.ConnectionState.Connected)
// Nodes should still be there
nodeRepository.nodeDBbyNum.value.size shouldBe 3
// Switch back to disconnected
radioController.setConnectionState(org.meshtastic.core.model.ConnectionState.Disconnected)
// Nodes still there
nodeRepository.nodeDBbyNum.value.size shouldBe 3
}
@Test
fun testLargeNodeDatabaseHandling() = runTest {
// Create large dataset
val largeNodeSet = TestDataFactory.createTestNodes(500)
nodeRepository.setNodes(largeNodeSet)
nodeRepository.nodeDBbyNum.value.size shouldBe 500
}
@Test
fun testRapidAddDelete() = runTest {
// Rapidly add and delete nodes
repeat(10) { iteration ->
nodeRepository.setNodes(TestDataFactory.createTestNodes(5))
nodeRepository.nodeDBbyNum.value.size shouldBe 5
nodeRepository.clearNodeDB(preserveFavorites = false)
nodeRepository.nodeDBbyNum.value.size shouldBe 0
}
// Final state should be clean
nodeRepository.nodeDBbyNum.value.size shouldBe 0
}
*/
}

View file

@ -1,180 +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.node.list
/**
* Integration tests for node feature.
*
* Tests node filtering, sorting, and state management with multiple nodes.
*/
class NodeIntegrationTest {
/*
private lateinit var nodeRepository: FakeNodeRepository
private lateinit var radioController: FakeRadioController
@BeforeTest
fun setUp() {
kotlinx.coroutines.Dispatchers.setMain(kotlinx.coroutines.Dispatchers.Unconfined)
nodeRepository = FakeNodeRepository()
radioController = FakeRadioController()
}
@kotlin.test.AfterTest
fun tearDown() {
kotlinx.coroutines.Dispatchers.resetMain()
}
@Test
fun testPopulatingMeshWithMultipleNodes() = runTest {
// Create diverse node set
val nodes =
listOf(
TestDataFactory.createTestNode(num = 1, longName = "Alice", shortName = "A"),
TestDataFactory.createTestNode(num = 2, longName = "Bob", shortName = "B"),
TestDataFactory.createTestNode(num = 3, longName = "Charlie", shortName = "C"),
TestDataFactory.createTestNode(num = 4, longName = "Diana", shortName = "D"),
TestDataFactory.createTestNode(num = 5, longName = "Eve", shortName = "E"),
)
// Add to repository
nodeRepository.setNodes(nodes)
// Verify all nodes present
nodeRepository.nodeDBbyNum.value.size shouldBe 5
assertTrue(nodeRepository.nodeDBbyNum.value.containsKey(1))
assertTrue(nodeRepository.nodeDBbyNum.value.containsKey(5))
}
@Test
fun testRetrievingNodeByUserId() = runTest {
val node = TestDataFactory.createTestNode(num = 42, userId = "!alice123", longName = "Alice")
nodeRepository.setNodes(listOf(node))
// Retrieve by userId
val retrieved = nodeRepository.getNode("!alice123")
retrieved.user.long_name shouldBe "Alice"
retrieved.num shouldBe 42
}
@Test
fun testNodeDeletionAndRemoval() = runTest {
val nodes = TestDataFactory.createTestNodes(5)
nodeRepository.setNodes(nodes)
nodeRepository.nodeDBbyNum.value.size shouldBe 5
// Delete one node
nodeRepository.deleteNode(2)
// Verify deletion
nodeRepository.nodeDBbyNum.value.size shouldBe 4
assertTrue(!nodeRepository.nodeDBbyNum.value.containsKey(2))
}
@Test
fun testBulkNodeDeletion() = runTest {
val nodes = TestDataFactory.createTestNodes(10)
nodeRepository.setNodes(nodes)
nodeRepository.nodeDBbyNum.value.size shouldBe 10
// Delete multiple nodes
nodeRepository.deleteNodes(listOf(1, 3, 5, 7, 9))
// Verify deletions
nodeRepository.nodeDBbyNum.value.size shouldBe 5
assertTrue(!nodeRepository.nodeDBbyNum.value.containsKey(1))
assertTrue(!nodeRepository.nodeDBbyNum.value.containsKey(3))
}
@Test
fun testUpdatingNodeMetadata() = runTest {
val originalNode = TestDataFactory.createTestNode(num = 1, longName = "Original Name")
nodeRepository.setNodes(listOf(originalNode))
// Update node notes
nodeRepository.setNodeNotes(1, "Test notes")
// Retrieve and verify
val updated = nodeRepository.getUser(1)
assertTrue(true, "Node updated successfully")
}
@Test
fun testNodeConnectionStateTracking() = runTest {
// Create nodes with different last heard times
val onlineNode =
TestDataFactory.createTestNode(num = 1, lastHeard = (System.currentTimeMillis() / 1000).toInt())
val offlineNode =
TestDataFactory.createTestNode(
num = 2,
lastHeard = ((System.currentTimeMillis() / 1000) - 86400).toInt(), // 24 hours ago
)
nodeRepository.setNodes(listOf(onlineNode, offlineNode))
// Verify both nodes exist
nodeRepository.nodeDBbyNum.value.size shouldBe 2
}
@Test
fun testFilteringNodesBySearchTerm() = runTest {
val nodes =
listOf(
TestDataFactory.createTestNode(num = 1, longName = "Alice Wonderland", shortName = "AW"),
TestDataFactory.createTestNode(num = 2, longName = "Bob Builder", shortName = "BB"),
TestDataFactory.createTestNode(num = 3, longName = "Charlie Chaplin", shortName = "CC"),
)
nodeRepository.setNodes(nodes)
// Manual filtering for test
val allNodes = nodeRepository.nodeDBbyNum.value.values.toList()
val filtered = allNodes.filter { it.user.long_name.contains("Alice", ignoreCase = true) }
filtered.size shouldBe 1
filtered.first().user.long_name shouldBe "Alice Wonderland"
}
@Test
fun testMaintainingFavoriteNodesList() = runTest {
val node1 = TestDataFactory.createTestNode(num = 1, longName = "Favorite Node")
val node2 = TestDataFactory.createTestNode(num = 2, longName = "Regular Node")
// Add nodes
nodeRepository.setNodes(listOf(node1, node2))
// In real implementation, would have separate favorite tracking
// For now, verify nodes are accessible
nodeRepository.nodeDBbyNum.value.size shouldBe 2
}
@Test
fun testClearingAllNodesFromMesh() = runTest {
nodeRepository.setNodes(TestDataFactory.createTestNodes(10))
nodeRepository.nodeDBbyNum.value.size shouldBe 10
// Clear database
nodeRepository.clearNodeDB(preserveFavorites = false)
// Verify cleared
nodeRepository.nodeDBbyNum.value.size shouldBe 0
}
*/
}

View file

@ -22,7 +22,6 @@ 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.Assert.assertTrue
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@ -32,6 +31,7 @@ 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])