Skip to content
Snippets Groups Projects
Unverified Commit c965b284 authored by Matthias Urhahn's avatar Matthias Urhahn Committed by GitHub
Browse files

Fix RiskLevelCalculationTask not being executed if no new match was found...

Fix RiskLevelCalculationTask not being executed if no new match was found (EXPOSUREAPP-3849) (#1657)

* Execute risk calculation even if no match.

* Pass an "originTag" with each started task to support debugging efforts.
Fix unit test regression.

* Re-add missing download task arguments.

* Fix misleading log statement.
parent 9af067fc
No related branches found
No related tags found
No related merge requests found
Showing
with 84 additions and 229 deletions
package de.rki.coronawarnapp.test
import android.content.Context
import android.text.format.Formatter
import de.rki.coronawarnapp.diagnosiskeys.download.DownloadDiagnosisKeysTask
import de.rki.coronawarnapp.diagnosiskeys.download.DownloadDiagnosisKeysTask.Progress.ApiSubmissionFinished
import de.rki.coronawarnapp.diagnosiskeys.download.DownloadDiagnosisKeysTask.Progress.ApiSubmissionStarted
import de.rki.coronawarnapp.diagnosiskeys.download.DownloadDiagnosisKeysTask.Progress.KeyFilesDownloadFinished
import de.rki.coronawarnapp.diagnosiskeys.download.DownloadDiagnosisKeysTask.Progress.KeyFilesDownloadStarted
import de.rki.coronawarnapp.risk.RiskLevelTask
import de.rki.coronawarnapp.task.Task
import de.rki.coronawarnapp.task.common.DefaultTaskRequest
import de.rki.coronawarnapp.task.submitAndListen
import de.rki.coronawarnapp.util.di.AppInjector
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.map
import timber.log.Timber
import java.util.UUID
class RiskLevelAndKeyRetrievalBenchmark(
private val context: Context,
private val countries: List<String>
) {
/**
* the key cache instance used to store queried dates and hours
*/
private val keyCache = AppInjector.component.keyCacheRepository
/**
* Calls the RetrieveDiagnosisKeysTransaction and RiskLevelTransaction and measures them.
* Results are displayed using a label
* @param callCount defines how often the transactions should be called (each call will be
* measured separately)
*/
suspend fun start(
callCount: Int,
onBenchmarkCompletedListener: OnBenchmarkCompletedListener
) {
var resultInfo = StringBuilder()
.append(
"MEASUREMENT Running for Countries:\n " +
"${countries.joinToString(", ")}\n\n"
)
.append("Result: \n\n")
.append("#\t Combined \t Download \t Sub \t Risk \t File # \t F. size\n")
onBenchmarkCompletedListener(resultInfo.toString())
repeat(callCount) { index ->
keyCache.clear()
var keyRetrievalError = ""
var keyFileCount: Int = -1
var keyFileDownloadDuration: Long = -1
var keyFilesSize: Long = -1
var apiSubmissionDuration: Long = -1
measureDiagnosticKeyRetrieval(
label = "#$index",
countries = countries,
downloadFinished = { duration, keyCount, totalFileSize ->
keyFileCount = keyCount
keyFileDownloadDuration = duration
keyFilesSize = totalFileSize
}, apiSubmissionFinished = { duration ->
apiSubmissionDuration = duration
})
var calculationDuration: Long = -1
var calculationError = ""
measureKeyCalculation("#$index") {
if (it != null) calculationDuration = it
// build result entry for current iteration with all gathered data
resultInfo.append(
"${index + 1}. \t ${calculationDuration + keyFileDownloadDuration + apiSubmissionDuration} ms \t " +
"$keyFileDownloadDuration ms " + "\t $apiSubmissionDuration ms" +
"\t $calculationDuration ms \t $keyFileCount \t " +
"${Formatter.formatFileSize(context, keyFilesSize)}\n"
)
if (keyRetrievalError.isNotEmpty()) {
resultInfo.append("Key Retrieval Error: $keyRetrievalError\n")
}
if (calculationError.isNotEmpty()) {
resultInfo.append("Calculation Error: $calculationError\n")
}
onBenchmarkCompletedListener(resultInfo.toString())
}
}
}
private suspend fun measureKeyCalculation(label: String, callback: (Long?) -> Unit) {
val uuid = UUID.randomUUID()
val t0 = System.currentTimeMillis()
AppInjector.component.taskController.tasks
.map {
it
.map { taskInfo -> taskInfo.taskState }
.filter { taskState -> taskState.request.id == uuid && taskState.isFinished }
}
.collect {
it.firstOrNull()?.also { state ->
Timber.v("MEASURE [Risk Level Calculation] $label finished")
callback.invoke(
if (state.error != null)
null
else
System.currentTimeMillis() - t0
)
}
}
Timber.v("MEASURE [Risk Level Calculation] $label started")
AppInjector.component.taskController.submit(
DefaultTaskRequest(
RiskLevelTask::class,
object : Task.Arguments {},
uuid
)
)
}
private suspend fun measureDiagnosticKeyRetrieval(
label: String,
countries: List<String>,
downloadFinished: (duration: Long, keyCount: Int, fileSize: Long) -> Unit,
apiSubmissionFinished: (duration: Long) -> Unit
) {
var keyFileDownloadStart: Long = -1
var apiSubmissionStarted: Long = -1
AppInjector.component.taskController.submitAndListen(
DefaultTaskRequest(DownloadDiagnosisKeysTask::class, DownloadDiagnosisKeysTask.Arguments(countries))
).collect { progress: Task.Progress ->
when (progress) {
is KeyFilesDownloadStarted -> {
Timber.v("MEASURE [Diagnostic Key Files] $label started")
keyFileDownloadStart = System.currentTimeMillis()
}
is KeyFilesDownloadFinished -> {
Timber.v("MEASURE [Diagnostic Key Files] $label finished")
val duration = System.currentTimeMillis() - keyFileDownloadStart
downloadFinished(duration, progress.keyCount, progress.fileSize)
}
is ApiSubmissionStarted -> {
apiSubmissionStarted = System.currentTimeMillis()
}
is ApiSubmissionFinished -> {
val duration = System.currentTimeMillis() - apiSubmissionStarted
apiSubmissionFinished(duration)
}
}
}
}
}
typealias OnBenchmarkCompletedListener = (resultInfo: String) -> Unit
...@@ -18,7 +18,7 @@ class TestForApiFragmentViewModel @AssistedInject constructor( ...@@ -18,7 +18,7 @@ class TestForApiFragmentViewModel @AssistedInject constructor(
) : CWAViewModel() { ) : CWAViewModel() {
fun calculateRiskLevelClicked() { fun calculateRiskLevelClicked() {
taskController.submit(DefaultTaskRequest(RiskLevelTask::class)) taskController.submit(DefaultTaskRequest(RiskLevelTask::class, originTag = "TestForApiFragmentViewModel"))
} }
val gmsState by smartLiveData { val gmsState by smartLiveData {
......
...@@ -83,14 +83,23 @@ class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor( ...@@ -83,14 +83,23 @@ class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor(
fun retrieveDiagnosisKeys() { fun retrieveDiagnosisKeys() {
launch { launch {
taskController.submitBlocking( taskController.submitBlocking(
DefaultTaskRequest(DownloadDiagnosisKeysTask::class, DownloadDiagnosisKeysTask.Arguments()) DefaultTaskRequest(
DownloadDiagnosisKeysTask::class,
DownloadDiagnosisKeysTask.Arguments(),
originTag = "TestRiskLevelCalculationFragmentCWAViewModel.retrieveDiagnosisKeys()"
)
) )
calculateRiskLevel() calculateRiskLevel()
} }
} }
fun calculateRiskLevel() { fun calculateRiskLevel() {
taskController.submit(DefaultTaskRequest(RiskLevelTask::class)) taskController.submit(
DefaultTaskRequest(
RiskLevelTask::class,
originTag = "TestRiskLevelCalculationFragmentCWAViewModel.calculateRiskLevel()"
)
)
} }
fun resetRiskLevel() { fun resetRiskLevel() {
......
...@@ -47,7 +47,7 @@ class ConfigChangeDetector @Inject constructor( ...@@ -47,7 +47,7 @@ class ConfigChangeDetector @Inject constructor(
if (newIdentifier != oldConfigId) { if (newIdentifier != oldConfigId) {
Timber.i("New config id ($newIdentifier) differs from last one ($oldConfigId), resetting.") Timber.i("New config id ($newIdentifier) differs from last one ($oldConfigId), resetting.")
RiskLevelRepositoryDeferrer.resetRiskLevel() RiskLevelRepositoryDeferrer.resetRiskLevel()
taskController.submit(DefaultTaskRequest(RiskLevelTask::class)) taskController.submit(DefaultTaskRequest(RiskLevelTask::class, originTag = "ConfigChangeDetector"))
} else { } else {
Timber.v("Config identifier ($oldConfigId) didn't change, NOOP.") Timber.v("Config identifier ($oldConfigId) didn't change, NOOP.")
} }
......
...@@ -223,8 +223,7 @@ class DownloadDiagnosisKeysTask @Inject constructor( ...@@ -223,8 +223,7 @@ class DownloadDiagnosisKeysTask @Inject constructor(
} }
class Arguments( class Arguments(
val requestedCountries: List<String>? = null, val requestedCountries: List<String>? = null
val withConstraints: Boolean = false
) : Task.Arguments ) : Task.Arguments
data class Config( data class Config(
......
...@@ -16,17 +16,19 @@ import de.rki.coronawarnapp.util.tryHumanReadableError ...@@ -16,17 +16,19 @@ import de.rki.coronawarnapp.util.tryHumanReadableError
import java.io.PrintWriter import java.io.PrintWriter
import java.io.StringWriter import java.io.StringWriter
fun Throwable.report(exceptionCategory: ExceptionCategory) = fun Throwable.report(exceptionCategory: ExceptionCategory) {
this.report(exceptionCategory, null, null) this.report(exceptionCategory, null, null)
}
fun Throwable.report( fun Throwable.report(
exceptionCategory: ExceptionCategory, exceptionCategory: ExceptionCategory,
prefix: String?, prefix: String?,
suffix: String? suffix: String?
) { ) {
reportProblem(tag = prefix, info = suffix)
if (CWADebug.isAUnitTest) return if (CWADebug.isAUnitTest) return
reportProblem(tag = prefix, info = suffix)
val context = CoronaWarnApplication.getAppContext() val context = CoronaWarnApplication.getAppContext()
val intent = Intent(ReportingConstants.ERROR_REPORT_LOCAL_BROADCAST_CHANNEL) val intent = Intent(ReportingConstants.ERROR_REPORT_LOCAL_BROADCAST_CHANNEL)
......
...@@ -36,7 +36,9 @@ class ExposureStateUpdateWorker @AssistedInject constructor( ...@@ -36,7 +36,9 @@ class ExposureStateUpdateWorker @AssistedInject constructor(
Timber.v("exposure summary state updated: $it") Timber.v("exposure summary state updated: $it")
} }
taskController.submit(DefaultTaskRequest(RiskLevelTask::class)) taskController.submit(
DefaultTaskRequest(RiskLevelTask::class, originTag = "ExposureStateUpdateWorker")
)
Timber.v("risk level calculation triggered") Timber.v("risk level calculation triggered")
} catch (e: ApiException) { } catch (e: ApiException) {
e.report(ExceptionCategory.EXPOSURENOTIFICATION) e.report(ExceptionCategory.EXPOSURENOTIFICATION)
......
...@@ -16,7 +16,7 @@ import de.rki.coronawarnapp.exception.UnknownBroadcastException ...@@ -16,7 +16,7 @@ import de.rki.coronawarnapp.exception.UnknownBroadcastException
import de.rki.coronawarnapp.exception.reporting.report import de.rki.coronawarnapp.exception.reporting.report
import de.rki.coronawarnapp.nearby.ExposureStateUpdateWorker import de.rki.coronawarnapp.nearby.ExposureStateUpdateWorker
import de.rki.coronawarnapp.nearby.modules.detectiontracker.ExposureDetectionTracker import de.rki.coronawarnapp.nearby.modules.detectiontracker.ExposureDetectionTracker
import de.rki.coronawarnapp.nearby.modules.detectiontracker.TrackedExposureDetection import de.rki.coronawarnapp.nearby.modules.detectiontracker.TrackedExposureDetection.Result
import de.rki.coronawarnapp.util.coroutine.AppScope import de.rki.coronawarnapp.util.coroutine.AppScope
import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
...@@ -42,25 +42,35 @@ class ExposureStateUpdateReceiver : BroadcastReceiver() { ...@@ -42,25 +42,35 @@ class ExposureStateUpdateReceiver : BroadcastReceiver() {
@Inject @AppScope lateinit var scope: CoroutineScope @Inject @AppScope lateinit var scope: CoroutineScope
@Inject lateinit var dispatcherProvider: DispatcherProvider @Inject lateinit var dispatcherProvider: DispatcherProvider
@Inject lateinit var exposureDetectionTracker: ExposureDetectionTracker @Inject lateinit var exposureDetectionTracker: ExposureDetectionTracker
lateinit var context: Context @Inject lateinit var workManager: WorkManager
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
Timber.tag(TAG).d("onReceive(context=%s, intent=%s)", context, intent) Timber.tag(TAG).d("onReceive(context=%s, intent=%s)", context, intent)
AndroidInjection.inject(this, context) AndroidInjection.inject(this, context)
this.context = context
val action = intent.action val action = intent.action
Timber.tag(TAG).v("Looking up action: %s", action) Timber.tag(TAG).v("Looking up action: %s", action)
val async = goAsync() val async = goAsync()
scope.launch(context = dispatcherProvider.Default) {
scope.launch(context = scope.coroutineContext) {
try { try {
when (action) { val token = intent.requireToken()
ACTION_EXPOSURE_STATE_UPDATED -> processStateUpdates(intent)
ACTION_EXPOSURE_NOT_FOUND -> processNotFound(intent) trackDetection(token, action)
else -> throw UnknownBroadcastException(action)
} val data = Data
.Builder()
.putString(EXTRA_TOKEN, token)
.build()
OneTimeWorkRequest
.Builder(ExposureStateUpdateWorker::class.java)
.setInputData(data)
.build()
.let { workManager.enqueue(it) }
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e, "Failed to process intent.")
e.report(INTERNAL) e.report(INTERNAL)
} finally { } finally {
Timber.tag(TAG).i("Finished processing broadcast.") Timber.tag(TAG).i("Finished processing broadcast.")
...@@ -69,39 +79,16 @@ class ExposureStateUpdateReceiver : BroadcastReceiver() { ...@@ -69,39 +79,16 @@ class ExposureStateUpdateReceiver : BroadcastReceiver() {
} }
} }
private fun processStateUpdates(intent: Intent) { private fun trackDetection(token: String, action: String?) {
Timber.tag(TAG).i("Processing ACTION_EXPOSURE_STATE_UPDATED") when (action) {
ACTION_EXPOSURE_STATE_UPDATED -> {
val workManager = WorkManager.getInstance(context) exposureDetectionTracker.finishExposureDetection(token, Result.UPDATED_STATE)
}
val token = intent.requireToken() ACTION_EXPOSURE_NOT_FOUND -> {
exposureDetectionTracker.finishExposureDetection(token, Result.NO_MATCHES)
val data = Data }
.Builder() else -> throw UnknownBroadcastException(action)
.putString(EXTRA_TOKEN, token) }
.build()
OneTimeWorkRequest
.Builder(ExposureStateUpdateWorker::class.java)
.setInputData(data)
.build()
.let { workManager.enqueue(it) }
exposureDetectionTracker.finishExposureDetection(
token,
TrackedExposureDetection.Result.UPDATED_STATE
)
}
private fun processNotFound(intent: Intent) {
Timber.tag(TAG).i("Processing ACTION_EXPOSURE_NOT_FOUND")
val token = intent.requireToken()
exposureDetectionTracker.finishExposureDetection(
token,
TrackedExposureDetection.Result.NO_MATCHES
)
} }
private fun Intent.requireToken(): String = getStringExtra(EXTRA_TOKEN).also { private fun Intent.requireToken(): String = getStringExtra(EXTRA_TOKEN).also {
......
...@@ -71,6 +71,7 @@ class TracingRepository @Inject constructor( ...@@ -71,6 +71,7 @@ class TracingRepository @Inject constructor(
private fun List<TaskInfo>.isRiskLevelTaskRunning() = any { private fun List<TaskInfo>.isRiskLevelTaskRunning() = any {
it.taskState.isActive && it.taskState.request.type == RiskLevelTask::class it.taskState.isActive && it.taskState.request.type == RiskLevelTask::class
} }
private fun List<TaskInfo>.isDownloadDiagnosisKeysTaskRunning() = any { private fun List<TaskInfo>.isDownloadDiagnosisKeysTaskRunning() = any {
it.taskState.isActive && it.taskState.request.type == DownloadDiagnosisKeysTask::class it.taskState.isActive && it.taskState.request.type == DownloadDiagnosisKeysTask::class
} }
...@@ -89,10 +90,15 @@ class TracingRepository @Inject constructor( ...@@ -89,10 +90,15 @@ class TracingRepository @Inject constructor(
taskController.submitBlocking( taskController.submitBlocking(
DefaultTaskRequest( DefaultTaskRequest(
DownloadDiagnosisKeysTask::class, DownloadDiagnosisKeysTask::class,
DownloadDiagnosisKeysTask.Arguments() DownloadDiagnosisKeysTask.Arguments(),
originTag = "TracingRepository.refreshDiagnosisKeys()"
)
)
taskController.submit(
DefaultTaskRequest(
RiskLevelTask::class, originTag = "TracingRepository.refreshDiagnosisKeys()"
) )
) )
taskController.submit(DefaultTaskRequest(RiskLevelTask::class))
TimerHelper.startManualKeyRetrievalTimer() TimerHelper.startManualKeyRetrievalTimer()
} }
} }
...@@ -137,12 +143,15 @@ class TracingRepository @Inject constructor( ...@@ -137,12 +143,15 @@ class TracingRepository @Inject constructor(
taskController.submitBlocking( taskController.submitBlocking(
DefaultTaskRequest( DefaultTaskRequest(
DownloadDiagnosisKeysTask::class, DownloadDiagnosisKeysTask::class,
DownloadDiagnosisKeysTask.Arguments() DownloadDiagnosisKeysTask.Arguments(),
originTag = "TracingRepository.refreshRisklevel()"
) )
) )
TimerHelper.checkManualKeyRetrievalTimer() TimerHelper.checkManualKeyRetrievalTimer()
taskController.submit(DefaultTaskRequest(RiskLevelTask::class)) taskController.submit(
DefaultTaskRequest(RiskLevelTask::class, originTag = "TracingRepository.refreshRiskLevel()")
)
} }
} }
} }
......
...@@ -8,7 +8,8 @@ import kotlin.reflect.KClass ...@@ -8,7 +8,8 @@ import kotlin.reflect.KClass
data class DefaultTaskRequest( data class DefaultTaskRequest(
override val type: KClass<out Task<Task.Progress, Task.Result>>, override val type: KClass<out Task<Task.Progress, Task.Result>>,
override val arguments: Task.Arguments = object : Task.Arguments {}, override val arguments: Task.Arguments = object : Task.Arguments {},
override val id: UUID = UUID.randomUUID() override val id: UUID = UUID.randomUUID(),
val originTag: String? = null
) : TaskRequest { ) : TaskRequest {
fun toNewTask(): DefaultTaskRequest = copy(id = UUID.randomUUID()) fun toNewTask(): DefaultTaskRequest = copy(id = UUID.randomUUID())
......
...@@ -51,7 +51,8 @@ class WatchdogService @Inject constructor( ...@@ -51,7 +51,8 @@ class WatchdogService @Inject constructor(
val state = taskController.submitBlocking( val state = taskController.submitBlocking(
DefaultTaskRequest( DefaultTaskRequest(
DownloadDiagnosisKeysTask::class, DownloadDiagnosisKeysTask::class,
DownloadDiagnosisKeysTask.Arguments(null, true) DownloadDiagnosisKeysTask.Arguments(),
originTag = "WatchdogService"
) )
) )
if (state.isFailed) { if (state.isFailed) {
......
...@@ -40,12 +40,11 @@ class DiagnosisKeyRetrievalOneTimeWorker @AssistedInject constructor( ...@@ -40,12 +40,11 @@ class DiagnosisKeyRetrievalOneTimeWorker @AssistedInject constructor(
taskController.submitBlocking( taskController.submitBlocking(
DefaultTaskRequest( DefaultTaskRequest(
DownloadDiagnosisKeysTask::class, DownloadDiagnosisKeysTask::class,
DownloadDiagnosisKeysTask.Arguments(null, true) DownloadDiagnosisKeysTask.Arguments(),
originTag = "DiagnosisKeyRetrievalOneTimeWorker"
) )
).error?.also { error: Throwable -> ).error?.also { error: Throwable ->
Timber.w( Timber.w(error, "$id: Error when submitting DownloadDiagnosisKeysTask.")
error, "$id: Error during startWithConstraints()."
)
if (runAttemptCount > BackgroundConstants.WORKER_RETRY_COUNT_THRESHOLD) { if (runAttemptCount > BackgroundConstants.WORKER_RETRY_COUNT_THRESHOLD) {
Timber.w(error, "$id: Retry attempts exceeded.") Timber.w(error, "$id: Retry attempts exceeded.")
......
...@@ -12,12 +12,13 @@ import de.rki.coronawarnapp.nearby.modules.detectiontracker.ExposureDetectionTra ...@@ -12,12 +12,13 @@ import de.rki.coronawarnapp.nearby.modules.detectiontracker.ExposureDetectionTra
import de.rki.coronawarnapp.nearby.modules.detectiontracker.TrackedExposureDetection import de.rki.coronawarnapp.nearby.modules.detectiontracker.TrackedExposureDetection
import de.rki.coronawarnapp.util.di.AppInjector import de.rki.coronawarnapp.util.di.AppInjector
import io.mockk.MockKAnnotations import io.mockk.MockKAnnotations
import io.mockk.Runs
import io.mockk.clearAllMocks import io.mockk.clearAllMocks
import io.mockk.every import io.mockk.every
import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.MockK
import io.mockk.just
import io.mockk.mockk import io.mockk.mockk
import io.mockk.mockkObject import io.mockk.mockkObject
import io.mockk.mockkStatic
import io.mockk.verifySequence import io.mockk.verifySequence
import kotlinx.coroutines.test.TestCoroutineScope import kotlinx.coroutines.test.TestCoroutineScope
import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.AfterEach
...@@ -36,6 +37,7 @@ class ExposureStateUpdateReceiverTest : BaseTest() { ...@@ -36,6 +37,7 @@ class ExposureStateUpdateReceiverTest : BaseTest() {
@MockK private lateinit var intent: Intent @MockK private lateinit var intent: Intent
@MockK private lateinit var workManager: WorkManager @MockK private lateinit var workManager: WorkManager
@MockK private lateinit var exposureDetectionTracker: ExposureDetectionTracker @MockK private lateinit var exposureDetectionTracker: ExposureDetectionTracker
private val scope = TestCoroutineScope() private val scope = TestCoroutineScope()
class TestApp : Application(), HasAndroidInjector { class TestApp : Application(), HasAndroidInjector {
...@@ -48,23 +50,25 @@ class ExposureStateUpdateReceiverTest : BaseTest() { ...@@ -48,23 +50,25 @@ class ExposureStateUpdateReceiverTest : BaseTest() {
@BeforeEach @BeforeEach
fun setUp() { fun setUp() {
MockKAnnotations.init(this) MockKAnnotations.init(this)
mockkStatic(WorkManager::class)
every { intent.getStringExtra(ExposureNotificationClient.EXTRA_TOKEN) } returns "token" every { intent.getStringExtra(ExposureNotificationClient.EXTRA_TOKEN) } returns "token"
mockkObject(AppInjector) mockkObject(AppInjector)
every { workManager.enqueue(any<WorkRequest>()) } answers { mockk() }
val application = mockk<TestApp>() val application = mockk<TestApp>()
every { context.applicationContext } returns application every { context.applicationContext } returns application
val broadcastReceiverInjector = AndroidInjector<Any> { val broadcastReceiverInjector = AndroidInjector<Any> {
it as ExposureStateUpdateReceiver it as ExposureStateUpdateReceiver
it.exposureDetectionTracker = exposureDetectionTracker it.exposureDetectionTracker = exposureDetectionTracker
it.dispatcherProvider = TestDispatcherProvider it.dispatcherProvider = TestDispatcherProvider
it.scope = scope it.scope = scope
it.workManager = workManager
} }
every { application.androidInjector() } returns broadcastReceiverInjector every { application.androidInjector() } returns broadcastReceiverInjector
every { WorkManager.getInstance(context) } returns workManager every { exposureDetectionTracker.finishExposureDetection(any(), any()) } just Runs
every { workManager.enqueue(any<WorkRequest>()) } answers { mockk() }
} }
@AfterEach @AfterEach
...@@ -77,9 +81,11 @@ class ExposureStateUpdateReceiverTest : BaseTest() { ...@@ -77,9 +81,11 @@ class ExposureStateUpdateReceiverTest : BaseTest() {
every { intent.action } returns ExposureNotificationClient.ACTION_EXPOSURE_STATE_UPDATED every { intent.action } returns ExposureNotificationClient.ACTION_EXPOSURE_STATE_UPDATED
ExposureStateUpdateReceiver().onReceive(context, intent) ExposureStateUpdateReceiver().onReceive(context, intent)
scope.advanceUntilIdle()
verifySequence { verifySequence {
workManager.enqueue(any<WorkRequest>())
exposureDetectionTracker.finishExposureDetection("token", TrackedExposureDetection.Result.UPDATED_STATE) exposureDetectionTracker.finishExposureDetection("token", TrackedExposureDetection.Result.UPDATED_STATE)
workManager.enqueue(any<WorkRequest>())
} }
} }
...@@ -88,8 +94,11 @@ class ExposureStateUpdateReceiverTest : BaseTest() { ...@@ -88,8 +94,11 @@ class ExposureStateUpdateReceiverTest : BaseTest() {
every { intent.action } returns ExposureNotificationClient.ACTION_EXPOSURE_NOT_FOUND every { intent.action } returns ExposureNotificationClient.ACTION_EXPOSURE_NOT_FOUND
ExposureStateUpdateReceiver().onReceive(context, intent) ExposureStateUpdateReceiver().onReceive(context, intent)
scope.advanceUntilIdle()
verifySequence { verifySequence {
exposureDetectionTracker.finishExposureDetection("token", TrackedExposureDetection.Result.NO_MATCHES) exposureDetectionTracker.finishExposureDetection("token", TrackedExposureDetection.Result.NO_MATCHES)
workManager.enqueue(any<WorkRequest>())
} }
} }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment