Skip to content

Commit

Permalink
Fixed background monitoring
Browse files Browse the repository at this point in the history
  • Loading branch information
VincentMasselis committed May 16, 2024
1 parent 128bf07 commit 00927ff
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 7 deletions.
2 changes: 2 additions & 0 deletions feature/background/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@ dependencies {
implementation(project(":feature:core"))

debugImplementation(project(":core:debug-ui"))

testImplementation(project(":core:test"))
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ internal class MonitorService : Service() {
super.onCreate()
scope = CoroutineScope(Dispatchers.Default)
vehiclesToMonitorUseCase
.realtimeIgnoredAndMonitored()
.appVisibilityIgnoredAndMonitored()
.onEach { (ignored, monitored) ->
mutex.withLock {
// Removes entries from `monitoring`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ internal class ManualBackgroundViewModel @AssistedInject constructor(

init {
vehiclesToMonitorUseCase
.expectedIgnoredAndMonitored()
.ignoredAndMonitored()
.map { (_, monitored) ->
monitored
.map { it.uuid }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ internal class ForegroundServiceUseCase @Inject constructor(
) {

init {
vehiclesToMonitorUseCase.realtimeIgnoredAndMonitored()
vehiclesToMonitorUseCase.appVisibilityIgnoredAndMonitored()
.map { (_, monitored) -> monitored.isNotEmpty() }
.flowOn(IO)
.distinctUntilChanged()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
Expand Down Expand Up @@ -52,7 +53,7 @@ public class VehiclesToMonitorUseCase @Inject internal constructor(
manuals.value = (manuals.value - vehicleUuid).toSortedSet()
}

public fun expectedIgnoredAndMonitored(): Flow<Pair<List<Vehicle>, List<Vehicle>>> = combine(
public fun ignoredAndMonitored(): Flow<Pair<List<Vehicle>, List<Vehicle>>> = combine(
vehicleDatabase
.selectAll()
.asFlow()
Expand All @@ -67,7 +68,12 @@ public class VehiclesToMonitorUseCase @Inject internal constructor(
}
},
manuals.flatMapLatest { manuals ->
combine(manuals.map { vehicleDatabase.selectByUuid(it).asFlow() }) { it }
// By default, if `combine()` is called with an empty array, combine acts like
// `emptyFlow()` but at least one value must be emit otherwise
// `expectedIgnoredAndMonitored()` will never return any value. As consequence, instead
// of a `emptyFlow()` like behavior, `flowOf(emptyArray())` is used.
if (manuals.isEmpty()) flowOf(emptyArray())
else combine(manuals.map { vehicleDatabase.selectByUuid(it).asFlow() }) { it }
},
) { (automaticIgnored, automaticMonitored), manuals ->
Pair(
Expand All @@ -79,10 +85,10 @@ public class VehiclesToMonitorUseCase @Inject internal constructor(
}.flowOn(Default)

@Suppress("NAME_SHADOWING")
public fun realtimeIgnoredAndMonitored(): Flow<Pair<List<Vehicle>, List<Vehicle>>> = combine(
public fun appVisibilityIgnoredAndMonitored(): Flow<Pair<List<Vehicle>, List<Vehicle>>> = combine(
currentVehicleUseCase.map { it.vehicle },
isAppVisibleFlow,
expectedIgnoredAndMonitored(),
ignoredAndMonitored(),
) { current, isAppVisible, (ignored, monitored) ->
val current = current.takeIf { isAppVisible }
Pair(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.masselis.tpmsadvanced.feature.background.usecase

import com.masselis.tpmsadvanced.core.database.QueryOne
import com.masselis.tpmsadvanced.data.vehicle.model.Pressure
import com.masselis.tpmsadvanced.data.vehicle.model.Pressure.CREATOR.bar
import com.masselis.tpmsadvanced.data.vehicle.model.Temperature
import com.masselis.tpmsadvanced.data.vehicle.model.Temperature.CREATOR.celsius
import com.masselis.tpmsadvanced.data.vehicle.model.Vehicle
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.flow.flowOf
import java.util.UUID

internal inline fun <reified T : Any> mockQueryOne(value: T) = mockk<QueryOne<T>> {
every { execute() } returns value
every { asFlow(any()) } returns flowOf(value)
every { asChillFlow(any()) } returns flowOf(value)
}

internal fun mockVehicle(
uuid: UUID = UUID.randomUUID(),
kind: Vehicle.Kind = Vehicle.Kind.CAR,
name: String = "MOCK",
lowPressure: Pressure = 1f.bar,
highPressure: Pressure = 5f.bar,
lowTemp: Temperature = 15f.celsius,
normalTemp: Temperature = 25f.celsius,
highTemp: Temperature = 45f.celsius,
isBackgroundMonitor: Boolean = false,
) = Vehicle(
uuid, kind, name, lowPressure, highPressure, lowTemp, normalTemp, highTemp, isBackgroundMonitor
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.masselis.tpmsadvanced.feature.background.usecase

import app.cash.turbine.test
import com.masselis.tpmsadvanced.core.feature.usecase.CurrentVehicleUseCase
import com.masselis.tpmsadvanced.data.vehicle.interfaces.VehicleDatabase
import com.masselis.tpmsadvanced.data.vehicle.model.Vehicle
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import kotlin.test.assertEquals


internal class VehiclesToMonitorUseCaseTest {

private lateinit var currentVehicleUseCase: CurrentVehicleUseCase
private lateinit var vehicleDatabase: VehicleDatabase

@Before
fun setup() {
currentVehicleUseCase = mockk()
vehicleDatabase = mockk {
every { selectUuidIsDeleting() } returns mockk {
every { asFlow() } returns flowOf(emptyList())
}
every { selectAll() } returns mockk {
every { asFlow() } returns flowOf(emptyList())
}
}
}

private fun test() = VehiclesToMonitorUseCase(currentVehicleUseCase, vehicleDatabase)

@Test
fun `no background monitor`() = runTest {
test().ignoredAndMonitored().test {
assertEquals(emptyList<Vehicle>() to emptyList(), awaitItem())
}
}

@Test
fun `add vehicle to monitor`() = runTest {
val vehicle = mockVehicle()
every { vehicleDatabase.selectByUuid(vehicle.uuid) } returns mockQueryOne(vehicle)
with(test()) {
ignoredAndMonitored().test {
assertEquals(emptyList<Vehicle>() to emptyList(), awaitItem())
enableManual(vehicle.uuid)
assertEquals(emptyList<Vehicle>() to listOf(vehicle), awaitItem())
}
}
}

@Test
fun `add vehicle to monitor and remove it`() = runTest {
val vehicle = mockVehicle()
every { vehicleDatabase.selectByUuid(vehicle.uuid) } returns mockQueryOne(vehicle)
with(test()) {
ignoredAndMonitored().test {
assertEquals(emptyList<Vehicle>() to emptyList(), awaitItem())
enableManual(vehicle.uuid)
assertEquals(emptyList<Vehicle>() to listOf(vehicle), awaitItem())
disableManual(vehicle.uuid)
assertEquals(emptyList<Vehicle>() to emptyList(), awaitItem())
}
}
}
}

0 comments on commit 00927ff

Please sign in to comment.