mirror of
https://github.com/torlando-tech/columba.git
synced 2025-12-22 05:37:07 +00:00
Merge main into fix/event-driven-messages
This commit is contained in:
@@ -527,6 +527,7 @@ fun ColumbaNavigation(pendingNavigation: MutableState<PendingNavigation?>) {
|
||||
composable(Screen.Identity.route) {
|
||||
IdentityScreen(
|
||||
onBackClick = { navController.popBackStack() },
|
||||
settingsViewModel = settingsViewModel,
|
||||
onNavigateToBleStatus = {
|
||||
navController.navigate("ble_connection_status")
|
||||
},
|
||||
@@ -535,6 +536,7 @@ fun ColumbaNavigation(pendingNavigation: MutableState<PendingNavigation?>) {
|
||||
|
||||
composable(Screen.Settings.route) {
|
||||
SettingsScreen(
|
||||
viewModel = settingsViewModel,
|
||||
onNavigateToInterfaces = {
|
||||
navController.navigate("interface_management")
|
||||
},
|
||||
@@ -681,6 +683,7 @@ fun ColumbaNavigation(pendingNavigation: MutableState<PendingNavigation?>) {
|
||||
composable("my_identity") {
|
||||
MyIdentityScreen(
|
||||
onNavigateBack = { navController.popBackStack() },
|
||||
settingsViewModel = settingsViewModel,
|
||||
onNavigateToIdentityManager = {
|
||||
navController.navigate("identity_manager")
|
||||
},
|
||||
@@ -690,6 +693,7 @@ fun ColumbaNavigation(pendingNavigation: MutableState<PendingNavigation?>) {
|
||||
composable("network_status") {
|
||||
IdentityScreen(
|
||||
onBackClick = { navController.popBackStack() },
|
||||
settingsViewModel = settingsViewModel,
|
||||
onNavigateToBleStatus = {
|
||||
navController.navigate("ble_connection_status")
|
||||
},
|
||||
|
||||
@@ -91,9 +91,9 @@ import com.lxmf.messenger.viewmodel.TestAnnounceResult
|
||||
@Composable
|
||||
fun IdentityScreen(
|
||||
onBackClick: () -> Unit = {},
|
||||
settingsViewModel: com.lxmf.messenger.viewmodel.SettingsViewModel,
|
||||
viewModel: DebugViewModel = hiltViewModel(),
|
||||
bleConnectionsViewModel: com.lxmf.messenger.viewmodel.BleConnectionsViewModel = hiltViewModel(),
|
||||
settingsViewModel: com.lxmf.messenger.viewmodel.SettingsViewModel = hiltViewModel(),
|
||||
onNavigateToBleStatus: () -> Unit = {},
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
@@ -89,9 +89,9 @@ import com.lxmf.messenger.viewmodel.SettingsViewModel
|
||||
@Composable
|
||||
fun MyIdentityScreen(
|
||||
onNavigateBack: () -> Unit,
|
||||
settingsViewModel: SettingsViewModel,
|
||||
onNavigateToIdentityManager: () -> Unit = {},
|
||||
debugViewModel: DebugViewModel = hiltViewModel(),
|
||||
settingsViewModel: SettingsViewModel = hiltViewModel(),
|
||||
) {
|
||||
val settingsState by settingsViewModel.state.collectAsState()
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ import com.lxmf.messenger.viewmodel.SettingsViewModel
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun SettingsScreen(
|
||||
viewModel: SettingsViewModel = hiltViewModel(),
|
||||
viewModel: SettingsViewModel,
|
||||
debugViewModel: DebugViewModel = hiltViewModel(),
|
||||
onNavigateToInterfaces: () -> Unit = {},
|
||||
onNavigateToIdentity: () -> Unit = {},
|
||||
|
||||
@@ -1794,4 +1794,92 @@ class SettingsViewModelTest {
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Shared Instance Monitoring Tests
|
||||
|
||||
/**
|
||||
* Verifies that monitors don't run when enableMonitors is false.
|
||||
*
|
||||
* This is the key test for the duplicate SettingsViewModel fix:
|
||||
* - Before fix: Multiple screens created their own SettingsViewModel instances
|
||||
* - Each instance started its own monitor, causing 3-4x battery drain
|
||||
* - After fix: One shared SettingsViewModel means one monitor
|
||||
*
|
||||
* The enableMonitors flag allows disabling monitors in tests to prevent
|
||||
* the infinite while(true) loop from running. In production, monitors
|
||||
* are enabled and poll every 5 seconds.
|
||||
*/
|
||||
@Test
|
||||
fun `monitors are disabled when enableMonitors flag is false`() =
|
||||
runTest {
|
||||
// Verify that the enableMonitors flag prevents monitoring loops from starting
|
||||
// This is set to false in @Before, verify the monitor doesn't call isSharedInstanceAvailable
|
||||
SettingsViewModel.enableMonitors = false
|
||||
|
||||
var monitorCallCount = 0
|
||||
val serviceProtocol =
|
||||
mockk<com.lxmf.messenger.reticulum.protocol.ServiceReticulumProtocol>(relaxed = true) {
|
||||
every { networkStatus } returns networkStatusFlow
|
||||
coEvery { isSharedInstanceAvailable() } coAnswers {
|
||||
monitorCallCount++
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
viewModel =
|
||||
SettingsViewModel(
|
||||
settingsRepository = settingsRepository,
|
||||
identityRepository = identityRepository,
|
||||
reticulumProtocol = serviceProtocol,
|
||||
interfaceConfigManager = interfaceConfigManager,
|
||||
propagationNodeManager = propagationNodeManager,
|
||||
)
|
||||
|
||||
// Wait for any potential async operations to settle
|
||||
// With UnconfinedTestDispatcher, coroutines run eagerly
|
||||
// If monitors were enabled, the while(true) loop would run infinitely
|
||||
viewModel.state.test {
|
||||
awaitItem() // initial state
|
||||
cancelAndConsumeRemainingEvents()
|
||||
}
|
||||
|
||||
// Should NOT have been called since monitors are disabled
|
||||
assertEquals(
|
||||
"Monitor should not run when enableMonitors is false",
|
||||
0,
|
||||
monitorCallCount,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `viewmodel passes ServiceReticulumProtocol check for monitoring`() =
|
||||
runTest {
|
||||
// Verify that the ViewModel correctly identifies ServiceReticulumProtocol
|
||||
// for shared instance monitoring. This is important because the monitor
|
||||
// only calls isSharedInstanceAvailable() on ServiceReticulumProtocol.
|
||||
SettingsViewModel.enableMonitors = false
|
||||
|
||||
val serviceProtocol =
|
||||
mockk<com.lxmf.messenger.reticulum.protocol.ServiceReticulumProtocol>(relaxed = true) {
|
||||
every { networkStatus } returns networkStatusFlow
|
||||
}
|
||||
|
||||
viewModel =
|
||||
SettingsViewModel(
|
||||
settingsRepository = settingsRepository,
|
||||
identityRepository = identityRepository,
|
||||
reticulumProtocol = serviceProtocol,
|
||||
interfaceConfigManager = interfaceConfigManager,
|
||||
propagationNodeManager = propagationNodeManager,
|
||||
)
|
||||
|
||||
// The ViewModel should be created successfully with ServiceReticulumProtocol
|
||||
viewModel.state.test {
|
||||
val state = awaitItem()
|
||||
assertFalse(state.isRestarting)
|
||||
cancelAndConsumeRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user