diff options
5 files changed, 90 insertions, 41 deletions
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt index b6bb475d8..9f986c706 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt @@ -1206,11 +1206,11 @@ class SafetyCenterManagerTest { ) // Because wrong ID, refresh hasn't finished. Wait for timeout. - listener.receiveSafetyCenterErrorDetails() + listener.waitForSafetyCenterRefresh(withErrorEntry = true) SafetyCenterFlags.setAllRefreshTimeoutsTo(TIMEOUT_LONG) SafetySourceReceiver.setResponse( - Request.Rescan(SINGLE_SOURCE_ID), + Request.Refresh(SINGLE_SOURCE_ID), Response.SetData(safetySourceTestData.information) ) safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( @@ -1280,7 +1280,7 @@ class SafetyCenterManagerTest { safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID) assertThat(apiSafetySourceData1).isNull() // Wait for the ongoing refresh to timeout. - listener.receiveSafetyCenterErrorDetails() + listener.waitForSafetyCenterRefresh(withErrorEntry = true) SafetyCenterFlags.setAllRefreshTimeoutsTo(TIMEOUT_LONG) SafetySourceReceiver.setResponse( Request.Refresh(SINGLE_SOURCE_ID), @@ -1332,18 +1332,11 @@ class SafetyCenterManagerTest { REFRESH_REASON_RESCAN_BUTTON_CLICK ) - val safetyCenterErrorDetailsFromListener = listener.receiveSafetyCenterErrorDetails() - assertThat(safetyCenterErrorDetailsFromListener) - .isEqualTo( - SafetyCenterErrorDetails( - safetyCenterResourcesContext.getStringByName("refresh_timeout") - ) - ) + listener.waitForSafetyCenterRefresh(withErrorEntry = true) } @Test fun refreshSafetySources_withUntrackedSourceThatTimesOut_doesNotTimeOut() { - SafetyCenterFlags.setAllRefreshTimeoutsTo(TIMEOUT_SHORT) SafetyCenterFlags.untrackedSources = setOf(SOURCE_ID_1) safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig) // SOURCE_ID_1 will timeout @@ -1359,14 +1352,11 @@ class SafetyCenterManagerTest { REFRESH_REASON_RESCAN_BUTTON_CLICK ) - assertFailsWith(TimeoutCancellationException::class) { - listener.receiveSafetyCenterErrorDetails(TIMEOUT_SHORT) - } + listener.waitForSafetyCenterRefresh(withErrorEntry = false) } @Test fun refreshSafetySources_withMultipleUntrackedSourcesThatTimeOut_doesNotTimeOut() { - SafetyCenterFlags.setAllRefreshTimeoutsTo(TIMEOUT_SHORT) SafetyCenterFlags.untrackedSources = setOf(SOURCE_ID_1, SOURCE_ID_2) safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig) // SOURCE_ID_1 and SOURCE_ID_2 will timeout @@ -1380,9 +1370,7 @@ class SafetyCenterManagerTest { REFRESH_REASON_RESCAN_BUTTON_CLICK ) - assertFailsWith(TimeoutCancellationException::class) { - listener.receiveSafetyCenterErrorDetails(TIMEOUT_SHORT) - } + listener.waitForSafetyCenterRefresh(withErrorEntry = false) } @Test @@ -1396,25 +1384,20 @@ class SafetyCenterManagerTest { REFRESH_REASON_RESCAN_BUTTON_CLICK ) - val safetyCenterErrorDetailsFromListener = listener.receiveSafetyCenterErrorDetails() - assertThat(safetyCenterErrorDetailsFromListener) - .isEqualTo( - SafetyCenterErrorDetails( - safetyCenterResourcesContext.getStringByName("refresh_timeout") - ) - ) + listener.waitForSafetyCenterRefresh(withErrorEntry = true) } @Test fun refreshSafetySources_withTrackedSourceThatHasNoReceiver_doesNotTimeOut() { - SafetyCenterFlags.setAllRefreshTimeoutsTo(TIMEOUT_SHORT) safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceOtherPackageConfig) val listener = safetyCenterTestHelper.addListener() safetyCenterManager.refreshSafetySourcesWithPermission(REFRESH_REASON_RESCAN_BUTTON_CLICK) assertFailsWith(TimeoutCancellationException::class) { - listener.receiveSafetyCenterErrorDetails(TIMEOUT_SHORT) + // In this case a refresh isn't even started because there is only a single source + // without a receiver. + listener.receiveSafetyCenterData(TIMEOUT_SHORT) } } diff --git a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterManagerTest.kt b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterManagerTest.kt index 75d2fd57d..b8fd17b37 100644 --- a/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterManagerTest.kt +++ b/tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterManagerTest.kt @@ -731,9 +731,8 @@ class SafetyCenterManagerTest { } @Test - fun refreshSafetySources_withShowEntriesOnTimeout_marksSafetySourceAsError() { + fun refreshSafetySources_timeout_marksSafetySourceAsError() { SafetyCenterFlags.setAllRefreshTimeoutsTo(TIMEOUT_SHORT) - SafetyCenterFlags.showErrorEntriesOnTimeout = true safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) val listener = safetyCenterTestHelper.addListener() @@ -751,9 +750,8 @@ class SafetyCenterManagerTest { } @Test - fun refreshSafetySources_withShowEntriesOnTimeout_keepsShowingErrorUntilClearedBySource() { + fun refreshSafetySources_timeout_keepsShowingErrorUntilClearedBySource() { SafetyCenterFlags.setAllRefreshTimeoutsTo(TIMEOUT_SHORT) - SafetyCenterFlags.showErrorEntriesOnTimeout = true safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) val listener = safetyCenterTestHelper.addListener() safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait( @@ -781,18 +779,15 @@ class SafetyCenterManagerTest { } @Test - fun refreshSafetySources_withShowEntriesOnTimeout_doesntSetErrorForBackgroundRefreshes() { + fun refreshSafetySources_timeout_doesntSetErrorForBackgroundRefreshes() { SafetyCenterFlags.setAllRefreshTimeoutsTo(TIMEOUT_SHORT) - SafetyCenterFlags.showErrorEntriesOnTimeout = true safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceConfig) val listener = safetyCenterTestHelper.addListener() safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(REFRESH_REASON_OTHER) - val safetyCenterBeforeTimeout = listener.receiveSafetyCenterData() - assertThat(safetyCenterBeforeTimeout.status.refreshStatus) - .isEqualTo(REFRESH_STATUS_DATA_FETCH_IN_PROGRESS) - val safetyCenterDataAfterTimeout = listener.receiveSafetyCenterData() + val safetyCenterDataAfterTimeout = + listener.waitForSafetyCenterRefresh(withErrorEntry = false) assertThat(safetyCenterDataAfterTimeout).isEqualTo(safetyCenterDataFromConfig) } diff --git a/tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetySourceStateCollectedLoggingHelperTests.kt b/tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetySourceStateCollectedLoggingHelperTests.kt index b7bd60d10..d0e6dd430 100644 --- a/tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetySourceStateCollectedLoggingHelperTests.kt +++ b/tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetySourceStateCollectedLoggingHelperTests.kt @@ -154,8 +154,9 @@ class SafetySourceStateCollectedLoggingHelperTests { if (source3Response != null) { SafetySourceReceiver.setResponse(Request.Refresh(SOURCE_ID_3), source3Response) } + + val listener = safetyCenterTestHelper.addListener() safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(refreshReason) - // Give time for responses to all sources - Thread.sleep(Coroutines.TIMEOUT_SHORT.toMillis()) + listener.waitForSafetyCenterRefresh() } } diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterFlags.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterFlags.kt index aad979229..46b7f5630 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterFlags.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterFlags.kt @@ -106,7 +106,7 @@ object SafetyCenterFlags { * refreshing them. */ private val showErrorEntriesOnTimeoutFlag = - Flag("safety_center_show_error_entries_on_timeout", defaultValue = false, BooleanParser()) + Flag("safety_center_show_error_entries_on_timeout", defaultValue = true, BooleanParser()) /** Flag that determines whether we should replace the IconAction of the lock screen source. */ private val replaceLockScreenIconActionFlag = @@ -116,6 +116,11 @@ object SafetyCenterFlags { * Flag that determines the time for which a Safety Center refresh is allowed to wait for a * source to respond to a refresh request before timing out and marking the refresh as finished, * depending on the refresh reason. + * + * Unlike the production code, this flag is set to [TIMEOUT_LONG] for all refresh reasons by + * default for convenience. UI tests typically will set some data manually rather than going + * through a full refresh, and we don't want to timeout the refresh and potentially end up with + * error entries in this case (as it could lead to flakyness). */ private val refreshSourceTimeoutsFlag = Flag( @@ -355,7 +360,7 @@ object SafetyCenterFlags { var replaceLockScreenIconAction: Boolean by replaceLockScreenIconActionFlag /** A property that allows getting and setting the [refreshSourceTimeoutsFlag]. */ - var refreshTimeouts: Map<Int, Duration> by refreshSourceTimeoutsFlag + private var refreshTimeouts: Map<Int, Duration> by refreshSourceTimeoutsFlag /** A property that allows getting and setting the [resolveActionTimeoutFlag]. */ var resolveActionTimeout: Duration by resolveActionTimeoutFlag diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestListener.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestListener.kt index 624c32d5f..8ce5c25d4 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestListener.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestListener.kt @@ -18,9 +18,14 @@ package com.android.safetycenter.testing import android.os.Build.VERSION_CODES.TIRAMISU import android.safetycenter.SafetyCenterData +import android.safetycenter.SafetyCenterEntry import android.safetycenter.SafetyCenterErrorDetails import android.safetycenter.SafetyCenterManager.OnSafetyCenterDataChangedListener +import android.safetycenter.SafetyCenterStaticEntry +import android.safetycenter.SafetyCenterStatus +import android.text.TextUtils import androidx.annotation.RequiresApi +import androidx.test.core.app.ApplicationProvider.getApplicationContext import com.android.safetycenter.testing.Coroutines.TIMEOUT_LONG import com.android.safetycenter.testing.Coroutines.runBlockingWithTimeout import java.time.Duration @@ -64,6 +69,47 @@ class SafetyCenterTestListener : OnSafetyCenterDataChangedListener { } /** + * Waits for a full Safety Center refresh to complete, where each change to the underlying + * [SafetyCenterData] must happen within the given [timeout]. + * + * @param withErrorEntry optionally check whether we should expect the [SafetyCenterData] to + * have or not have at least one an error entry after the refresh completes + * @return the [SafetyCenterData] after the refresh completes + */ + fun waitForSafetyCenterRefresh( + timeout: Duration = TIMEOUT_LONG, + withErrorEntry: Boolean? = null + ): SafetyCenterData { + receiveSafetyCenterData(timeout) { + it.status.refreshStatus == SafetyCenterStatus.REFRESH_STATUS_DATA_FETCH_IN_PROGRESS || + it.status.refreshStatus == SafetyCenterStatus.REFRESH_STATUS_FULL_RESCAN_IN_PROGRESS + } + val afterRefresh = + receiveSafetyCenterData(timeout) { + it.status.refreshStatus == SafetyCenterStatus.REFRESH_STATUS_NONE + } + if (withErrorEntry == null) { + return afterRefresh + } + val errorMessage = + SafetyCenterTestData(getApplicationContext()) + .getRefreshErrorString(numberOfErrorEntries = 1) + val containsErrorEntry = afterRefresh.containsAnyEntryWithSummary(errorMessage) + if (withErrorEntry && !containsErrorEntry) { + throw AssertionError( + "No error entry with message: \"$errorMessage\" found in SafetyCenterData" + + " after refresh: $afterRefresh" + ) + } else if (!withErrorEntry && containsErrorEntry) { + throw AssertionError( + "Found an error entry with message: \"$errorMessage\" in SafetyCenterData" + + " after refresh: $afterRefresh" + ) + } + return afterRefresh + } + + /** * Waits for a [SafetyCenterErrorDetails] update from SafetyCenter within the given [timeout]. */ fun receiveSafetyCenterErrorDetails(timeout: Duration = TIMEOUT_LONG) = @@ -74,4 +120,23 @@ class SafetyCenterTestListener : OnSafetyCenterDataChangedListener { dataChannel.cancel() errorChannel.cancel() } + + private companion object { + fun SafetyCenterData.containsAnyEntryWithSummary(summary: CharSequence): Boolean = + entries().any { TextUtils.equals(it.summary, summary) } || + staticEntries().any { TextUtils.equals(it.summary, summary) } + + fun SafetyCenterData.entries(): List<SafetyCenterEntry> = + entriesOrGroups.flatMap { + val entry = it.entry + if (entry != null) { + listOf(entry) + } else { + it.entryGroup!!.entries + } + } + + fun SafetyCenterData.staticEntries(): List<SafetyCenterStaticEntry> = + staticEntryGroups.flatMap { it.staticEntries } + } } |