mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
chore(conductor): Mark track 'Migrate tests to KMP best practices and expand coverage' as complete
This commit is contained in:
parent
0e1461b5e4
commit
2395cb91e1
33 changed files with 358 additions and 307 deletions
|
|
@ -53,7 +53,8 @@ open class ScannerViewModel(
|
|||
private val getDiscoveredDevicesUseCase: GetDiscoveredDevicesUseCase,
|
||||
private val bleScanner: org.meshtastic.core.ble.BleScanner? = null,
|
||||
) : ViewModel() {
|
||||
val showMockInterface: StateFlow<Boolean> = MutableStateFlow(radioInterfaceService.isMockInterface()).asStateFlow()
|
||||
private val _showMockInterface = MutableStateFlow(false)
|
||||
val showMockInterface: StateFlow<Boolean> = _showMockInterface.asStateFlow()
|
||||
|
||||
private val _errorText = MutableStateFlow<String?>(null)
|
||||
val errorText: StateFlow<String?> = _errorText.asStateFlow()
|
||||
|
|
@ -65,6 +66,10 @@ open class ScannerViewModel(
|
|||
|
||||
private var scanJob: kotlinx.coroutines.Job? = null
|
||||
|
||||
init {
|
||||
_showMockInterface.value = radioInterfaceService.isMockInterface()
|
||||
}
|
||||
|
||||
fun startBleScan() {
|
||||
if (isBleScanningState.value || bleScanner == null) return
|
||||
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@ import android.app.Application
|
|||
import android.net.Uri
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import com.google.android.gms.maps.model.UrlTileProvider
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import dev.mokkery.MockMode
|
||||
import dev.mokkery.every
|
||||
import dev.mokkery.mock
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
|
@ -54,15 +54,15 @@ import org.robolectric.RobolectricTestRunner
|
|||
@RunWith(RobolectricTestRunner::class)
|
||||
class MapViewModelTest {
|
||||
|
||||
private val application = mockk<Application>(relaxed = true)
|
||||
private val mapPrefs = mockk<MapPrefs>(relaxed = true)
|
||||
private val googleMapsPrefs = mockk<GoogleMapsPrefs>(relaxed = true)
|
||||
private val nodeRepository = mockk<NodeRepository>(relaxed = true)
|
||||
private val packetRepository = mockk<PacketRepository>(relaxed = true)
|
||||
private val radioConfigRepository = mockk<RadioConfigRepository>(relaxed = true)
|
||||
private val radioController = mockk<RadioController>(relaxed = true)
|
||||
private val customTileProviderRepository = mockk<CustomTileProviderRepository>(relaxed = true)
|
||||
private val uiPreferencesDataSource = mockk<UiPreferencesDataSource>(relaxed = true)
|
||||
private val application = mock<Application>(MockMode.autofill)
|
||||
private val mapPrefs = mock<MapPrefs>(MockMode.autofill)
|
||||
private val googleMapsPrefs = mock<GoogleMapsPrefs>(MockMode.autofill)
|
||||
private val nodeRepository = mock<NodeRepository>(MockMode.autofill)
|
||||
private val packetRepository = mock<PacketRepository>(MockMode.autofill)
|
||||
private val radioConfigRepository = mock<RadioConfigRepository>(MockMode.autofill)
|
||||
private val radioController = mock<RadioController>(MockMode.autofill)
|
||||
private val customTileProviderRepository = mock<CustomTileProviderRepository>(MockMode.autofill)
|
||||
private val uiPreferencesDataSource = mock<UiPreferencesDataSource>(MockMode.autofill)
|
||||
private val savedStateHandle = SavedStateHandle(mapOf("waypointId" to null))
|
||||
|
||||
private val testDispatcher = StandardTestDispatcher()
|
||||
|
|
@ -89,7 +89,7 @@ class MapViewModelTest {
|
|||
every { googleMapsPrefs.hiddenLayerUrls } returns MutableStateFlow(emptySet())
|
||||
|
||||
every { customTileProviderRepository.getCustomTileProviders() } returns flowOf(emptyList())
|
||||
every { radioConfigRepository.deviceProfileFlow } returns flowOf(mockk(relaxed = true))
|
||||
every { radioConfigRepository.deviceProfileFlow } returns flowOf(mock(MockMode.autofill))
|
||||
every { uiPreferencesDataSource.theme } returns MutableStateFlow(1)
|
||||
every { nodeRepository.myNodeInfo } returns MutableStateFlow(null)
|
||||
every { nodeRepository.ourNodeInfo } returns MutableStateFlow(null)
|
||||
|
|
@ -133,13 +133,6 @@ class MapViewModelTest {
|
|||
|
||||
@Test
|
||||
fun `addNetworkMapLayer detects GeoJSON based on extension`() = runTest(testDispatcher) {
|
||||
mockkStatic(Uri::class)
|
||||
val mockUri = mockk<Uri>()
|
||||
every { Uri.parse("https://example.com/data.geojson") } returns mockUri
|
||||
every { mockUri.scheme } returns "https"
|
||||
every { mockUri.path } returns "/data.geojson"
|
||||
every { mockUri.toString() } returns "https://example.com/data.geojson"
|
||||
|
||||
viewModel.addNetworkMapLayer("Test Layer", "https://example.com/data.geojson")
|
||||
advanceUntilIdle()
|
||||
|
||||
|
|
@ -149,13 +142,6 @@ class MapViewModelTest {
|
|||
|
||||
@Test
|
||||
fun `addNetworkMapLayer defaults to KML for other extensions`() = runTest(testDispatcher) {
|
||||
mockkStatic(Uri::class)
|
||||
val mockUri = mockk<Uri>()
|
||||
every { Uri.parse("https://example.com/map.kml") } returns mockUri
|
||||
every { mockUri.scheme } returns "https"
|
||||
every { mockUri.path } returns "/map.kml"
|
||||
every { mockUri.toString() } returns "https://example.com/map.kml"
|
||||
|
||||
viewModel.addNetworkMapLayer("Test KML", "https://example.com/map.kml")
|
||||
advanceUntilIdle()
|
||||
|
||||
|
|
|
|||
|
|
@ -43,14 +43,14 @@ import org.meshtastic.core.resources.unmute
|
|||
import org.meshtastic.core.ui.util.AlertManager
|
||||
|
||||
@Single
|
||||
class NodeManagementActions
|
||||
open class NodeManagementActions
|
||||
constructor(
|
||||
private val nodeRepository: NodeRepository,
|
||||
private val serviceRepository: ServiceRepository,
|
||||
private val radioController: RadioController,
|
||||
private val alertManager: AlertManager,
|
||||
) {
|
||||
fun requestRemoveNode(scope: CoroutineScope, node: Node) {
|
||||
open fun requestRemoveNode(scope: CoroutineScope, node: Node) {
|
||||
alertManager.showAlert(
|
||||
titleRes = Res.string.remove,
|
||||
messageRes = Res.string.remove_node_text,
|
||||
|
|
@ -58,7 +58,7 @@ constructor(
|
|||
)
|
||||
}
|
||||
|
||||
fun removeNode(scope: CoroutineScope, nodeNum: Int) {
|
||||
open fun removeNode(scope: CoroutineScope, nodeNum: Int) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
Logger.i { "Removing node '$nodeNum'" }
|
||||
val packetId = radioController.getPacketId()
|
||||
|
|
@ -67,7 +67,7 @@ constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun requestIgnoreNode(scope: CoroutineScope, node: Node) {
|
||||
open fun requestIgnoreNode(scope: CoroutineScope, node: Node) {
|
||||
scope.launch {
|
||||
val message =
|
||||
getString(if (node.isIgnored) Res.string.ignore_remove else Res.string.ignore_add, node.user.long_name)
|
||||
|
|
@ -79,11 +79,11 @@ constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun ignoreNode(scope: CoroutineScope, node: Node) {
|
||||
open fun ignoreNode(scope: CoroutineScope, node: Node) {
|
||||
scope.launch(Dispatchers.IO) { serviceRepository.onServiceAction(ServiceAction.Ignore(node)) }
|
||||
}
|
||||
|
||||
fun requestMuteNode(scope: CoroutineScope, node: Node) {
|
||||
open fun requestMuteNode(scope: CoroutineScope, node: Node) {
|
||||
scope.launch {
|
||||
val message =
|
||||
getString(if (node.isMuted) Res.string.mute_remove else Res.string.mute_add, node.user.long_name)
|
||||
|
|
@ -95,11 +95,11 @@ constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun muteNode(scope: CoroutineScope, node: Node) {
|
||||
open fun muteNode(scope: CoroutineScope, node: Node) {
|
||||
scope.launch(Dispatchers.IO) { serviceRepository.onServiceAction(ServiceAction.Mute(node)) }
|
||||
}
|
||||
|
||||
fun requestFavoriteNode(scope: CoroutineScope, node: Node) {
|
||||
open fun requestFavoriteNode(scope: CoroutineScope, node: Node) {
|
||||
scope.launch {
|
||||
val message =
|
||||
getString(
|
||||
|
|
@ -114,11 +114,11 @@ constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun favoriteNode(scope: CoroutineScope, node: Node) {
|
||||
open fun favoriteNode(scope: CoroutineScope, node: Node) {
|
||||
scope.launch(Dispatchers.IO) { serviceRepository.onServiceAction(ServiceAction.Favorite(node)) }
|
||||
}
|
||||
|
||||
fun setNodeNotes(scope: CoroutineScope, nodeNum: Int, notes: String) {
|
||||
open fun setNodeNotes(scope: CoroutineScope, nodeNum: Int, notes: String) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
nodeRepository.setNodeNotes(nodeNum, notes)
|
||||
|
|
|
|||
|
|
@ -27,9 +27,9 @@ import org.meshtastic.feature.node.model.isEffectivelyUnmessageable
|
|||
import org.meshtastic.proto.Config
|
||||
|
||||
@Single
|
||||
class GetFilteredNodesUseCase constructor(private val nodeRepository: NodeRepository) {
|
||||
open class GetFilteredNodesUseCase constructor(private val nodeRepository: NodeRepository) {
|
||||
@Suppress("CyclomaticComplexMethod", "LongMethod")
|
||||
operator fun invoke(filter: NodeFilterState, sort: NodeSortOption): Flow<List<Node>> = nodeRepository
|
||||
open operator fun invoke(filter: NodeFilterState, sort: NodeSortOption): Flow<List<Node>> = nodeRepository
|
||||
.getNodes(
|
||||
sort = sort,
|
||||
filter = filter.filterText,
|
||||
|
|
|
|||
|
|
@ -18,46 +18,46 @@ package org.meshtastic.feature.node.list
|
|||
|
||||
import kotlinx.coroutines.flow.map
|
||||
import org.koin.core.annotation.Single
|
||||
import org.meshtastic.core.datastore.UiPreferencesDataSource
|
||||
import org.meshtastic.core.common.UiPreferences
|
||||
import org.meshtastic.core.model.NodeSortOption
|
||||
|
||||
@Single
|
||||
class NodeFilterPreferences constructor(private val uiPreferencesDataSource: UiPreferencesDataSource) {
|
||||
val includeUnknown = uiPreferencesDataSource.includeUnknown
|
||||
val excludeInfrastructure = uiPreferencesDataSource.excludeInfrastructure
|
||||
val onlyOnline = uiPreferencesDataSource.onlyOnline
|
||||
val onlyDirect = uiPreferencesDataSource.onlyDirect
|
||||
val showIgnored = uiPreferencesDataSource.showIgnored
|
||||
val excludeMqtt = uiPreferencesDataSource.excludeMqtt
|
||||
open class NodeFilterPreferences constructor(private val uiPreferences: UiPreferences) {
|
||||
open val includeUnknown = uiPreferences.includeUnknown
|
||||
open val excludeInfrastructure = uiPreferences.excludeInfrastructure
|
||||
open val onlyOnline = uiPreferences.onlyOnline
|
||||
open val onlyDirect = uiPreferences.onlyDirect
|
||||
open val showIgnored = uiPreferences.showIgnored
|
||||
open val excludeMqtt = uiPreferences.excludeMqtt
|
||||
|
||||
val nodeSortOption =
|
||||
uiPreferencesDataSource.nodeSort.map { NodeSortOption.entries.getOrElse(it) { NodeSortOption.VIA_FAVORITE } }
|
||||
open val nodeSortOption =
|
||||
uiPreferences.nodeSort.map { NodeSortOption.entries.getOrElse(it) { NodeSortOption.VIA_FAVORITE } }
|
||||
|
||||
fun setNodeSort(option: NodeSortOption) {
|
||||
uiPreferencesDataSource.setNodeSort(option.ordinal)
|
||||
open fun setNodeSort(option: NodeSortOption) {
|
||||
uiPreferences.setNodeSort(option.ordinal)
|
||||
}
|
||||
|
||||
fun toggleIncludeUnknown() {
|
||||
uiPreferencesDataSource.setIncludeUnknown(!includeUnknown.value)
|
||||
open fun toggleIncludeUnknown() {
|
||||
uiPreferences.setIncludeUnknown(!includeUnknown.value)
|
||||
}
|
||||
|
||||
fun toggleExcludeInfrastructure() {
|
||||
uiPreferencesDataSource.setExcludeInfrastructure(!excludeInfrastructure.value)
|
||||
open fun toggleExcludeInfrastructure() {
|
||||
uiPreferences.setExcludeInfrastructure(!excludeInfrastructure.value)
|
||||
}
|
||||
|
||||
fun toggleOnlyOnline() {
|
||||
uiPreferencesDataSource.setOnlyOnline(!onlyOnline.value)
|
||||
open fun toggleOnlyOnline() {
|
||||
uiPreferences.setOnlyOnline(!onlyOnline.value)
|
||||
}
|
||||
|
||||
fun toggleOnlyDirect() {
|
||||
uiPreferencesDataSource.setOnlyDirect(!onlyDirect.value)
|
||||
open fun toggleOnlyDirect() {
|
||||
uiPreferences.setOnlyDirect(!onlyDirect.value)
|
||||
}
|
||||
|
||||
fun toggleShowIgnored() {
|
||||
uiPreferencesDataSource.setShowIgnored(!showIgnored.value)
|
||||
open fun toggleShowIgnored() {
|
||||
uiPreferences.setShowIgnored(!showIgnored.value)
|
||||
}
|
||||
|
||||
fun toggleExcludeMqtt() {
|
||||
uiPreferencesDataSource.setExcludeMqtt(!excludeMqtt.value)
|
||||
open fun toggleExcludeMqtt() {
|
||||
uiPreferences.setExcludeMqtt(!excludeMqtt.value)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,14 +16,18 @@
|
|||
*/
|
||||
package org.meshtastic.feature.node.list
|
||||
|
||||
import io.kotest.matchers.shouldBe
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import app.cash.turbine.test
|
||||
import dev.mokkery.MockMode
|
||||
import dev.mokkery.answering.returns
|
||||
import dev.mokkery.every
|
||||
import dev.mokkery.mock
|
||||
import dev.mokkery.matcher.any
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.test.resetMain
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlinx.coroutines.test.setMain
|
||||
import org.meshtastic.core.model.ConnectionState
|
||||
import org.meshtastic.core.model.Node
|
||||
import org.meshtastic.core.model.NodeSortOption
|
||||
import org.meshtastic.core.repository.RadioConfigRepository
|
||||
import org.meshtastic.core.repository.ServiceRepository
|
||||
import org.meshtastic.core.testing.FakeNodeRepository
|
||||
|
|
@ -34,96 +38,85 @@ import org.meshtastic.feature.node.domain.usecase.GetFilteredNodesUseCase
|
|||
import kotlin.test.BeforeTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
/**
|
||||
* Bootstrap tests for NodeListViewModel.
|
||||
*
|
||||
* Demonstrates using FakeNodeRepository with a node list feature.
|
||||
*/
|
||||
class NodeListViewModelTest {
|
||||
/*
|
||||
|
||||
|
||||
private lateinit var viewModel: NodeListViewModel
|
||||
private lateinit var nodeRepository: FakeNodeRepository
|
||||
private lateinit var radioController: FakeRadioController
|
||||
private lateinit var radioConfigRepository: RadioConfigRepository
|
||||
private lateinit var serviceRepository: ServiceRepository
|
||||
private lateinit var nodeFilterPreferences: NodeFilterPreferences
|
||||
private lateinit var nodeManagementActions: NodeManagementActions
|
||||
private lateinit var getFilteredNodesUseCase: GetFilteredNodesUseCase
|
||||
private val radioConfigRepository: RadioConfigRepository = mock(MockMode.autofill)
|
||||
private val serviceRepository: ServiceRepository = mock(MockMode.autofill)
|
||||
private val nodeFilterPreferences: NodeFilterPreferences = mock(MockMode.autofill)
|
||||
private val nodeManagementActions: NodeManagementActions = mock(MockMode.autofill)
|
||||
private val getFilteredNodesUseCase: GetFilteredNodesUseCase = mock(MockMode.autofill)
|
||||
|
||||
@BeforeTest
|
||||
fun setUp() {
|
||||
kotlinx.coroutines.Dispatchers.setMain(kotlinx.coroutines.Dispatchers.Unconfined)
|
||||
// Use real fakes
|
||||
nodeRepository = FakeNodeRepository()
|
||||
radioController = FakeRadioController()
|
||||
|
||||
// Mock remaining dependencies with explicit types
|
||||
nodeFilterPreferences =
|
||||
every { nodeSortOption } returns MutableStateFlow(org.meshtastic.core.model.NodeSortOption.LAST_HEARD)
|
||||
every { includeUnknown } returns MutableStateFlow(true)
|
||||
every { excludeInfrastructure } returns MutableStateFlow(false)
|
||||
every { onlyOnline } returns MutableStateFlow(false)
|
||||
}
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
every { radioConfigRepository.localConfigFlow } returns MutableStateFlow(org.meshtastic.proto.LocalConfig())
|
||||
every { radioConfigRepository.deviceProfileFlow } returns MutableStateFlow(org.meshtastic.proto.DeviceProfile())
|
||||
every { serviceRepository.connectionState } returns MutableStateFlow(ConnectionState.Disconnected)
|
||||
|
||||
every { nodeFilterPreferences.nodeSortOption } returns MutableStateFlow(NodeSortOption.LAST_HEARD)
|
||||
every { nodeFilterPreferences.includeUnknown } returns MutableStateFlow(true)
|
||||
every { nodeFilterPreferences.excludeInfrastructure } returns MutableStateFlow(false)
|
||||
every { nodeFilterPreferences.onlyOnline } returns MutableStateFlow(false)
|
||||
every { nodeFilterPreferences.onlyDirect } returns MutableStateFlow(false)
|
||||
every { nodeFilterPreferences.showIgnored } returns MutableStateFlow(false)
|
||||
every { nodeFilterPreferences.excludeMqtt } returns MutableStateFlow(false)
|
||||
|
||||
viewModel =
|
||||
NodeListViewModel(
|
||||
savedStateHandle = SavedStateHandle(),
|
||||
nodeRepository = nodeRepository,
|
||||
radioConfigRepository = radioConfigRepository,
|
||||
serviceRepository = serviceRepository,
|
||||
radioController = radioController,
|
||||
nodeManagementActions = nodeManagementActions,
|
||||
getFilteredNodesUseCase = getFilteredNodesUseCase,
|
||||
nodeFilterPreferences = nodeFilterPreferences,
|
||||
)
|
||||
every { getFilteredNodesUseCase(any(), any()) } returns MutableStateFlow(emptyList())
|
||||
|
||||
viewModel = createViewModel()
|
||||
}
|
||||
|
||||
@kotlin.test.AfterTest
|
||||
fun tearDown() {
|
||||
kotlinx.coroutines.Dispatchers.resetMain()
|
||||
private fun createViewModel() = NodeListViewModel(
|
||||
savedStateHandle = SavedStateHandle(),
|
||||
nodeRepository = nodeRepository,
|
||||
radioConfigRepository = radioConfigRepository,
|
||||
serviceRepository = serviceRepository,
|
||||
radioController = radioController,
|
||||
nodeManagementActions = nodeManagementActions,
|
||||
getFilteredNodesUseCase = getFilteredNodesUseCase,
|
||||
nodeFilterPreferences = nodeFilterPreferences,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun testInitialization() {
|
||||
assertNotNull(viewModel)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInitialization() = runTest {
|
||||
setUp()
|
||||
// ViewModel should initialize without errors
|
||||
assertTrue(true, "NodeListViewModel initialized successfully")
|
||||
fun `nodeList emits updates when repository changes`() = runTest {
|
||||
val nodesFlow = MutableStateFlow<List<Node>>(emptyList())
|
||||
every { getFilteredNodesUseCase(any(), any()) } returns nodesFlow
|
||||
|
||||
val vm = createViewModel()
|
||||
vm.nodeList.test {
|
||||
// Initial value from stateIn
|
||||
assertEquals(emptyList(), awaitItem())
|
||||
|
||||
// Trigger update
|
||||
val testNodes = TestDataFactory.createTestNodes(3)
|
||||
nodesFlow.value = testNodes
|
||||
|
||||
assertEquals(3, awaitItem().size)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOurNodeInfoFlow() = runTest {
|
||||
setUp()
|
||||
// Verify ourNodeInfo StateFlow is accessible
|
||||
val ourNode = viewModel.ourNodeInfo.value
|
||||
assertTrue(ourNode == null, "ourNodeInfo starts as null before connection")
|
||||
fun `connectionState reflects serviceRepository state`() = runTest {
|
||||
val stateFlow = MutableStateFlow<ConnectionState>(ConnectionState.Disconnected)
|
||||
every { serviceRepository.connectionState } returns stateFlow
|
||||
|
||||
val vm = createViewModel()
|
||||
vm.connectionState.test {
|
||||
assertEquals(ConnectionState.Disconnected, awaitItem())
|
||||
stateFlow.value = ConnectionState.Connected
|
||||
assertEquals(ConnectionState.Connected, awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNodeCounts() = runTest {
|
||||
setUp()
|
||||
// Add test nodes to repository
|
||||
val testNodes = TestDataFactory.createTestNodes(3)
|
||||
nodeRepository.setNodes(testNodes)
|
||||
|
||||
// Verify nodes are in repository
|
||||
"Test nodes added to repository" shouldBe 3, nodeRepository.nodeDBbyNum.value.size
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testTotalAndOnlineNodeCounts() = runTest {
|
||||
setUp()
|
||||
// Verify count flows are accessible
|
||||
val totalCount = viewModel.totalNodeCount.value
|
||||
val onlineCount = viewModel.onlineNodeCount.value
|
||||
|
||||
// Both should be accessible without error
|
||||
assertTrue(true, "Node count flows are accessible")
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,10 @@
|
|||
*/
|
||||
package org.meshtastic.feature.node.detail
|
||||
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import dev.mokkery.MockMode
|
||||
import dev.mokkery.matcher.any
|
||||
import dev.mokkery.mock
|
||||
import dev.mokkery.verify
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
|
|
@ -33,10 +35,10 @@ import org.meshtastic.proto.User
|
|||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class NodeManagementActionsTest {
|
||||
|
||||
private val nodeRepository = mockk<NodeRepository>(relaxed = true)
|
||||
private val serviceRepository = mockk<ServiceRepository>(relaxed = true)
|
||||
private val radioController = mockk<RadioController>(relaxed = true)
|
||||
private val alertManager = mockk<AlertManager>(relaxed = true)
|
||||
private val nodeRepository = mock<NodeRepository>(MockMode.autofill)
|
||||
private val serviceRepository = mock<ServiceRepository>(MockMode.autofill)
|
||||
private val radioController = mock<RadioController>(MockMode.autofill)
|
||||
private val alertManager = mock<AlertManager>(MockMode.autofill)
|
||||
private val testDispatcher = StandardTestDispatcher()
|
||||
private val testScope = TestScope(testDispatcher)
|
||||
|
||||
|
|
|
|||
|
|
@ -16,8 +16,9 @@
|
|||
*/
|
||||
package org.meshtastic.feature.node.domain.usecase
|
||||
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import dev.mokkery.every
|
||||
import dev.mokkery.matcher.any
|
||||
import dev.mokkery.mock
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.runTest
|
||||
|
|
@ -38,7 +39,7 @@ class GetFilteredNodesUseCaseTest {
|
|||
|
||||
@Before
|
||||
fun setUp() {
|
||||
nodeRepository = mockk()
|
||||
nodeRepository = mock()
|
||||
useCase = GetFilteredNodesUseCase(nodeRepository)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,9 +16,10 @@
|
|||
*/
|
||||
package org.meshtastic.feature.settings
|
||||
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import dev.mokkery.MockMode
|
||||
import dev.mokkery.every
|
||||
import dev.mokkery.mock
|
||||
import dev.mokkery.verify
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
|
@ -52,13 +53,13 @@ class LegacySettingsViewModelTest {
|
|||
|
||||
private val testDispatcher = StandardTestDispatcher()
|
||||
|
||||
private val radioConfigRepository: RadioConfigRepository = mockk(relaxed = true)
|
||||
private val radioController: RadioController = mockk(relaxed = true)
|
||||
private val nodeRepository: NodeRepository = mockk(relaxed = true)
|
||||
private val uiPrefs: UiPrefs = mockk(relaxed = true)
|
||||
private val buildConfigProvider: BuildConfigProvider = mockk(relaxed = true)
|
||||
private val databaseManager: DatabaseManager = mockk(relaxed = true)
|
||||
private val meshLogPrefs: MeshLogPrefs = mockk(relaxed = true)
|
||||
private val radioConfigRepository: RadioConfigRepository = mock(MockMode.autofill)
|
||||
private val radioController: RadioController = mock(MockMode.autofill)
|
||||
private val nodeRepository: NodeRepository = mock(MockMode.autofill)
|
||||
private val uiPrefs: UiPrefs = mock(MockMode.autofill)
|
||||
private val buildConfigProvider: BuildConfigProvider = mock(MockMode.autofill)
|
||||
private val databaseManager: DatabaseManager = mock(MockMode.autofill)
|
||||
private val meshLogPrefs: MeshLogPrefs = mock(MockMode.autofill)
|
||||
|
||||
private lateinit var setThemeUseCase: SetThemeUseCase
|
||||
private lateinit var setAppIntroCompletedUseCase: SetAppIntroCompletedUseCase
|
||||
|
|
@ -75,14 +76,14 @@ class LegacySettingsViewModelTest {
|
|||
fun setUp() {
|
||||
Dispatchers.setMain(testDispatcher)
|
||||
|
||||
setThemeUseCase = mockk(relaxed = true)
|
||||
setAppIntroCompletedUseCase = mockk(relaxed = true)
|
||||
setProvideLocationUseCase = mockk(relaxed = true)
|
||||
setDatabaseCacheLimitUseCase = mockk(relaxed = true)
|
||||
setMeshLogSettingsUseCase = mockk(relaxed = true)
|
||||
meshLocationUseCase = mockk(relaxed = true)
|
||||
exportDataUseCase = mockk(relaxed = true)
|
||||
isOtaCapableUseCase = mockk(relaxed = true)
|
||||
setThemeUseCase = mock(MockMode.autofill)
|
||||
setAppIntroCompletedUseCase = mock(MockMode.autofill)
|
||||
setProvideLocationUseCase = mock(MockMode.autofill)
|
||||
setDatabaseCacheLimitUseCase = mock(MockMode.autofill)
|
||||
setMeshLogSettingsUseCase = mock(MockMode.autofill)
|
||||
meshLocationUseCase = mock(MockMode.autofill)
|
||||
exportDataUseCase = mock(MockMode.autofill)
|
||||
isOtaCapableUseCase = mock(MockMode.autofill)
|
||||
|
||||
// Return real StateFlows to avoid ClassCastException
|
||||
every { databaseManager.cacheLimit } returns MutableStateFlow(100)
|
||||
|
|
@ -95,7 +96,7 @@ class LegacySettingsViewModelTest {
|
|||
|
||||
viewModel =
|
||||
SettingsViewModel(
|
||||
app = mockk(),
|
||||
app = mock(),
|
||||
radioConfigRepository = radioConfigRepository,
|
||||
radioController = radioController,
|
||||
nodeRepository = nodeRepository,
|
||||
|
|
|
|||
|
|
@ -16,9 +16,11 @@
|
|||
*/
|
||||
package org.meshtastic.feature.settings.filter
|
||||
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import dev.mokkery.MockMode
|
||||
import dev.mokkery.every
|
||||
import dev.mokkery.matcher.any
|
||||
import dev.mokkery.mock
|
||||
import dev.mokkery.verify
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
|
@ -27,8 +29,8 @@ import org.meshtastic.core.repository.MessageFilter
|
|||
|
||||
class FilterSettingsViewModelTest {
|
||||
|
||||
private val filterPrefs: FilterPrefs = mockk(relaxed = true)
|
||||
private val messageFilter: MessageFilter = mockk(relaxed = true)
|
||||
private val filterPrefs: FilterPrefs = mock(MockMode.autofill)
|
||||
private val messageFilter: MessageFilter = mock(MockMode.autofill)
|
||||
|
||||
private lateinit var viewModel: FilterSettingsViewModel
|
||||
|
||||
|
|
|
|||
|
|
@ -16,9 +16,11 @@
|
|||
*/
|
||||
package org.meshtastic.feature.settings.radio
|
||||
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.mockk
|
||||
import dev.mokkery.MockMode
|
||||
import dev.mokkery.everySuspend
|
||||
import dev.mokkery.matcher.any
|
||||
import dev.mokkery.mock
|
||||
import dev.mokkery.verifySuspend
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||
|
|
@ -45,8 +47,8 @@ class CleanNodeDatabaseViewModelTest {
|
|||
@Before
|
||||
fun setUp() {
|
||||
Dispatchers.setMain(testDispatcher)
|
||||
cleanNodeDatabaseUseCase = mockk(relaxed = true)
|
||||
alertManager = mockk(relaxed = true)
|
||||
cleanNodeDatabaseUseCase = mock(MockMode.autofill)
|
||||
alertManager = mock(MockMode.autofill)
|
||||
viewModel = CleanNodeDatabaseViewModel(cleanNodeDatabaseUseCase, alertManager)
|
||||
}
|
||||
|
||||
|
|
@ -58,7 +60,7 @@ class CleanNodeDatabaseViewModelTest {
|
|||
@Test
|
||||
fun `getNodesToDelete updates state`() = runTest {
|
||||
val nodes = listOf(Node(num = 1), Node(num = 2))
|
||||
coEvery { cleanNodeDatabaseUseCase.getNodesToClean(any(), any(), any()) } returns nodes
|
||||
everySuspend { cleanNodeDatabaseUseCase.getNodesToClean(any(), any(), any()) } returns nodes
|
||||
|
||||
viewModel.getNodesToDelete()
|
||||
advanceUntilIdle()
|
||||
|
|
@ -69,14 +71,14 @@ class CleanNodeDatabaseViewModelTest {
|
|||
@Test
|
||||
fun `cleanNodes calls useCase and clears state`() = runTest {
|
||||
val nodes = listOf(Node(num = 1))
|
||||
coEvery { cleanNodeDatabaseUseCase.getNodesToClean(any(), any(), any()) } returns nodes
|
||||
everySuspend { cleanNodeDatabaseUseCase.getNodesToClean(any(), any(), any()) } returns nodes
|
||||
viewModel.getNodesToDelete()
|
||||
advanceUntilIdle()
|
||||
|
||||
viewModel.cleanNodes()
|
||||
advanceUntilIdle()
|
||||
|
||||
coVerify { cleanNodeDatabaseUseCase.cleanNodes(listOf(1)) }
|
||||
verifySuspend { cleanNodeDatabaseUseCase.cleanNodes(listOf(1)) }
|
||||
assertEquals(0, viewModel.nodesToDelete.value.size)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue