Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update currentSyncJobStatus for oneTimeSync when syncJobStatus is null #2511

Merged
merged 10 commits into from
Jul 5, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package com.google.android.fhir.sync
import android.content.Context
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import androidx.work.BackoffPolicy
import androidx.work.Constraints
import androidx.work.WorkInfo
import androidx.work.WorkManager
Expand Down Expand Up @@ -104,12 +105,49 @@ class SyncInstrumentedTest {
assertThat(states.last()).isInstanceOf(CurrentSyncJobStatus.Succeeded::class.java)
}

@Test
fun oneTime_worker_nextExecutionAfterSucceeded() {
santosh-pingle marked this conversation as resolved.
Show resolved Hide resolved
WorkManagerTestInitHelper.initializeTestWorkManager(context)
val states = mutableListOf<CurrentSyncJobStatus>()
val nextExecutionStates = mutableListOf<CurrentSyncJobStatus>()
runBlocking {
Sync.oneTimeSync<TestSyncWorker>(context = context)
.transformWhile {
states.add(it)
emit(it is CurrentSyncJobStatus.Succeeded)
it !is CurrentSyncJobStatus.Succeeded
}
.shareIn(this, SharingStarted.Eagerly, 5)

Sync.oneTimeSync<TestSyncWorker>(context = context)
.transformWhile {
nextExecutionStates.add(it)
emit(it is CurrentSyncJobStatus.Succeeded)
it !is CurrentSyncJobStatus.Succeeded
}
.shareIn(this, SharingStarted.Eagerly, 5)
}
assertThat(states.first()).isInstanceOf(CurrentSyncJobStatus.Running::class.java)
assertThat(states.last()).isInstanceOf(CurrentSyncJobStatus.Succeeded::class.java)
assertThat(nextExecutionStates.first()).isInstanceOf(CurrentSyncJobStatus.Running::class.java)
}

@Test
fun oneTime_worker_failedSyncState() {
WorkManagerTestInitHelper.initializeTestWorkManager(context)
val states = mutableListOf<CurrentSyncJobStatus>()
runBlocking {
Sync.oneTimeSync<TestSyncWorkerForDownloadFailing>(context = context)
Sync.oneTimeSync<TestSyncWorkerForDownloadFailing>(
context = context,
RetryConfiguration(
BackoffCriteria(
BackoffPolicy.LINEAR,
30,
TimeUnit.SECONDS,
),
0,
),
)
.transformWhile {
states.add(it)
emit(it is CurrentSyncJobStatus.Failed)
Expand All @@ -121,6 +159,53 @@ class SyncInstrumentedTest {
assertThat(states.last()).isInstanceOf(CurrentSyncJobStatus.Failed::class.java)
}

@Test
fun oneTime_worker_nextExecutionAfterFailed() {
WorkManagerTestInitHelper.initializeTestWorkManager(context)
val states = mutableListOf<CurrentSyncJobStatus>()
val nextExecutionStates = mutableListOf<CurrentSyncJobStatus>()
runBlocking {
Sync.oneTimeSync<TestSyncWorkerForDownloadFailing>(
context = context,
RetryConfiguration(
BackoffCriteria(
BackoffPolicy.LINEAR,
30,
TimeUnit.SECONDS,
),
0,
),
)
.transformWhile {
states.add(it)
emit(it is CurrentSyncJobStatus.Failed)
it !is CurrentSyncJobStatus.Failed
}
.shareIn(this, SharingStarted.Eagerly, 5)

Sync.oneTimeSync<TestSyncWorkerForDownloadFailing>(
context = context,
RetryConfiguration(
BackoffCriteria(
BackoffPolicy.LINEAR,
30,
TimeUnit.SECONDS,
),
0,
),
)
.transformWhile {
nextExecutionStates.add(it)
emit(it is CurrentSyncJobStatus.Failed)
it !is CurrentSyncJobStatus.Failed
}
.shareIn(this, SharingStarted.Eagerly, 5)
}
assertThat(states.first()).isInstanceOf(CurrentSyncJobStatus.Running::class.java)
assertThat(states.last()).isInstanceOf(CurrentSyncJobStatus.Failed::class.java)
assertThat(nextExecutionStates.first()).isInstanceOf(CurrentSyncJobStatus.Running::class.java)
}

@Test
fun periodic_worker_periodicSyncState() {
WorkManagerTestInitHelper.initializeTestWorkManager(context)
Expand Down
43 changes: 27 additions & 16 deletions engine/src/main/java/com/google/android/fhir/sync/Sync.kt
santosh-pingle marked this conversation as resolved.
Show resolved Hide resolved
santosh-pingle marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ import androidx.work.PeriodicWorkRequest
import androidx.work.WorkInfo
import androidx.work.WorkInfo.State.CANCELLED
import androidx.work.WorkInfo.State.ENQUEUED
import androidx.work.WorkInfo.State.FAILED
import androidx.work.WorkInfo.State.RUNNING
import androidx.work.WorkInfo.State.SUCCEEDED
import androidx.work.WorkManager
import androidx.work.hasKeyWithValueOfType
import com.google.android.fhir.FhirEngineProvider
Expand Down Expand Up @@ -269,28 +271,37 @@ object Sync {
}

/**
* Only call this API when `syncJobStatusFromWorkManager` is null. Create a [CurrentSyncJobStatus]
* from `syncJobStatusFromDataStore` if it is not null; otherwise, create it from
* [WorkInfo.State].
* Creates terminal states of [CurrentSyncJobStatus] from [syncJobStatusFromDataStore]; and
* intermediate states of [CurrentSyncJobStatus] from [WorkInfo.State].
*
* Note : Only call this API when `syncJobStatusFromWorkManager` is null.
*/
private fun handleNullWorkManagerStatusForOneTimeSync(
workInfoState: WorkInfo.State,
syncJobStatusFromDataStore: SyncJobStatus?,
): CurrentSyncJobStatus =
syncJobStatusFromDataStore?.let {
when (it) {
is SyncJobStatus.Succeeded -> Succeeded(it.timestamp)
is SyncJobStatus.Failed -> Failed(it.timestamp)
else -> error("Inconsistent terminal syncJobStatus : $syncJobStatusFromDataStore")
}
when (workInfoState) {
santosh-pingle marked this conversation as resolved.
Show resolved Hide resolved
ENQUEUED -> Enqueued
RUNNING -> Running(SyncJobStatus.Started())
SUCCEEDED ->
syncJobStatusFromDataStore?.let {
when (it) {
is SyncJobStatus.Succeeded -> Succeeded(it.timestamp)
else -> error("Inconsistent terminal syncJobStatus : $syncJobStatusFromDataStore")
}
}
?: error("Inconsistent terminal syncJobStatus.")
FAILED ->
syncJobStatusFromDataStore?.let {
when (it) {
is SyncJobStatus.Failed -> Failed(it.timestamp)
else -> error("Inconsistent terminal syncJobStatus : $syncJobStatusFromDataStore")
}
}
?: error("Inconsistent terminal syncJobStatus.")
CANCELLED -> Cancelled
else -> error("Inconsistent WorkInfo.State: $workInfoState.")
}
?: when (workInfoState) {
RUNNING -> Running(SyncJobStatus.Started())
ENQUEUED -> Enqueued
CANCELLED -> Cancelled
// syncJobStatusFromDataStore should not be null for SUCCEEDED, FAILED.
else -> error("Inconsistent WorkInfo.State: $workInfoState.")
}

/**
* Only call this API when syncJobStatusFromWorkManager is null. Create a [CurrentSyncJobStatus]
Expand Down
Loading