blob: 94abe35a7582d527e4bf2ae8cbb25c0a307ce8a4 [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.safetycenter.cts
import android.content.Context
import android.os.Build.VERSION_CODES.TIRAMISU
import android.os.SystemClock
import android.safetycenter.SafetySourceData
import android.safetycenter.SafetySourceIssue
import android.safetycenter.cts.testing.SafetyCenterActivityLauncher.launchSafetyCenterActivity
import android.safetycenter.cts.testing.SafetyCenterCtsConfigs
import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.MULTIPLE_SOURCES_CONFIG
import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SINGLE_SOURCE_CONFIG
import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SINGLE_SOURCE_ID
import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SOURCE_ID_1
import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SOURCE_ID_2
import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SOURCE_ID_3
import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.STATIC_SOURCES_CONFIG
import android.safetycenter.cts.testing.SafetyCenterCtsHelper
import android.safetycenter.cts.testing.SafetyCenterFlags.deviceSupportsSafetyCenter
import android.safetycenter.cts.testing.SafetySourceCtsData
import android.support.test.uiautomator.By
import android.support.test.uiautomator.BySelector
import android.support.test.uiautomator.StaleObjectException
import android.support.test.uiautomator.UiDevice
import android.support.test.uiautomator.UiObject2
import android.util.Log
import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SdkSuppress
import com.android.compatibility.common.util.UiAutomatorUtils.getUiDevice
import com.android.compatibility.common.util.UiAutomatorUtils.waitFindObject
import com.android.compatibility.common.util.UiAutomatorUtils.waitFindObjectOrNull
import java.time.Duration
import java.util.regex.Pattern
import org.junit.After
import org.junit.Assume.assumeTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@SdkSuppress(minSdkVersion = TIRAMISU, codeName = "Tiramisu")
class SafetyCenterActivityTest {
private val context: Context = getApplicationContext()
private val safetyCenterCtsHelper = SafetyCenterCtsHelper(context)
private val safetySourceCtsData = SafetySourceCtsData(context)
// JUnit's Assume is not supported in @BeforeClass by the CTS tests runner, so this is used to
// manually skip the setup and teardown methods.
private val shouldRunTests = context.deviceSupportsSafetyCenter()
@Before
fun assumeDeviceSupportsSafetyCenterToRunTests() {
assumeTrue(shouldRunTests)
}
@Before
fun enableSafetyCenterBeforeTest() {
if (!shouldRunTests) {
return
}
safetyCenterCtsHelper.setEnabled(true)
}
@After
fun clearDataAfterTest() {
if (!shouldRunTests) {
return
}
safetyCenterCtsHelper.reset()
getUiDevice().resetRotation()
}
@Test
fun launchActivity_withFlagEnabled_showsSecurityAndPrivacyTitle() {
context.launchSafetyCenterActivity {
// CollapsingToolbar title can't be found by text, so using description instead.
waitFindObject(By.desc("Security & Privacy"))
}
}
@Test
fun launchActivity_withFlagDisabled_showsSettingsTitle() {
safetyCenterCtsHelper.setEnabled(false)
context.launchSafetyCenterActivity { waitFindObject(By.text("Settings")) }
}
@Test
fun launchActivity_displaysStaticSources() {
safetyCenterCtsHelper.setConfig(STATIC_SOURCES_CONFIG)
context.launchSafetyCenterActivity {
findAllText(
context.getString(SafetyCenterCtsConfigs.STATIC_SOURCE_GROUP_1.titleResId),
context.getString(SafetyCenterCtsConfigs.STATIC_SOURCE_1.titleResId),
context.getString(SafetyCenterCtsConfigs.STATIC_SOURCE_1.summaryResId),
context.getString(SafetyCenterCtsConfigs.STATIC_SOURCE_GROUP_2.titleResId),
context.getString(SafetyCenterCtsConfigs.STATIC_SOURCE_2.titleResId),
context.getString(SafetyCenterCtsConfigs.STATIC_SOURCE_2.summaryResId))
}
}
@Test
fun launchActivity_displaysSafetyData() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
val dataToDisplay = safetySourceCtsData.criticalWithIssue
safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, dataToDisplay)
context.launchSafetyCenterActivity { assertSourceDataDisplayed(dataToDisplay) }
}
@Test
fun updatingSafetySourceData_updatesDisplayedSafetyData() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, safetySourceCtsData.information)
context.launchSafetyCenterActivity {
val dataToDisplay = safetySourceCtsData.recommendationWithIssue
safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, dataToDisplay)
assertSourceDataDisplayed(dataToDisplay)
}
}
@Test
fun issueCard_confirmsDismissal_dismisses() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithIssue)
context.launchSafetyCenterActivity {
waitFindObject(By.desc("Dismiss")).click()
waitFindObject(By.text("Dismiss this alert?"))
findButton("Dismiss").click()
assertIssueNotDisplayed(safetySourceCtsData.criticalIssue)
findButton("Scan")
}
}
@Test
fun issueCard_confirmsDismissal_cancels() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithIssue)
context.launchSafetyCenterActivity {
waitFindObject(By.desc("Dismiss")).click()
waitFindObject(By.text("Dismiss this alert?"))
findButton("Cancel").click()
assertIssueDisplayed(safetySourceCtsData.criticalIssue)
}
}
@Test
fun launchActivity_withNoIssues_hasRescanButton() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, safetySourceCtsData.information)
context.launchSafetyCenterActivity { findButton("Scan") }
}
@Test
fun launchActivity_withIssue_doesNotHaveRescanButton() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, safetySourceCtsData.informationWithIssue)
context.launchSafetyCenterActivity { waitButtonNotDisplayed("Scan") }
}
@Test
fun moreIssuesCard_underMaxShownIssues_noMoreIssuesCard() {
safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithIssue)
context.launchSafetyCenterActivity {
assertIssueDisplayed(safetySourceCtsData.criticalIssue)
waitTextNotDisplayed("See all alerts")
}
}
@Test
fun moreIssuesCard_moreIssuesCardShown_additionalIssueCardsCollapsed() {
safetyCenterCtsHelper.setConfig(MULTIPLE_SOURCES_CONFIG)
safetyCenterCtsHelper.setData(SOURCE_ID_1, safetySourceCtsData.criticalWithIssue)
safetyCenterCtsHelper.setData(SOURCE_ID_2, safetySourceCtsData.recommendationWithIssue)
safetyCenterCtsHelper.setData(SOURCE_ID_3, safetySourceCtsData.informationWithIssue)
context.launchSafetyCenterActivity {
assertIssueDisplayed(safetySourceCtsData.criticalIssue)
waitFindObject(By.text("See all alerts"))
assertIssueNotDisplayed(safetySourceCtsData.recommendationIssue)
assertIssueNotDisplayed(safetySourceCtsData.informationIssue)
}
}
@Test
fun moreIssuesCard_expandAdditionalIssueCards() {
safetyCenterCtsHelper.setConfig(MULTIPLE_SOURCES_CONFIG)
safetyCenterCtsHelper.setData(SOURCE_ID_1, safetySourceCtsData.criticalWithIssue)
safetyCenterCtsHelper.setData(SOURCE_ID_2, safetySourceCtsData.recommendationWithIssue)
safetyCenterCtsHelper.setData(SOURCE_ID_3, safetySourceCtsData.informationWithIssue)
context.launchSafetyCenterActivity {
assertIssueDisplayed(safetySourceCtsData.criticalIssue)
expandMoreIssuesCard()
// Verify cards expanded
waitTextNotDisplayed("See all alerts")
assertIssueDisplayed(safetySourceCtsData.criticalIssue)
assertIssueDisplayed(safetySourceCtsData.recommendationIssue)
assertIssueDisplayed(safetySourceCtsData.informationIssue)
}
}
@Test
fun moreIssuesCard_rotation_cardsStillExpanded() {
safetyCenterCtsHelper.setConfig(MULTIPLE_SOURCES_CONFIG)
safetyCenterCtsHelper.setData(SOURCE_ID_1, safetySourceCtsData.criticalWithIssue)
safetyCenterCtsHelper.setData(SOURCE_ID_2, safetySourceCtsData.recommendationWithIssue)
safetyCenterCtsHelper.setData(SOURCE_ID_3, safetySourceCtsData.informationWithIssue)
context.launchSafetyCenterActivity {
expandMoreIssuesCard()
val uiDevice = getUiDevice()
uiDevice.waitForIdle()
// Verify cards initially expanded
waitTextNotDisplayed("See all alerts")
assertIssueDisplayed(safetySourceCtsData.criticalIssue)
assertIssueDisplayed(safetySourceCtsData.recommendationIssue)
assertIssueDisplayed(safetySourceCtsData.informationIssue)
// Device rotation to trigger usage of savedinstancestate via config update
uiDevice.rotate()
// Verify cards remain expanded
waitTextNotDisplayed("See all alerts")
assertIssueDisplayed(safetySourceCtsData.criticalIssue)
assertIssueDisplayed(safetySourceCtsData.recommendationIssue)
assertIssueDisplayed(safetySourceCtsData.informationIssue)
}
}
private fun assertSourceDataDisplayed(sourceData: SafetySourceData) {
findAllText(sourceData.status?.title, sourceData.status?.summary)
for (sourceIssue in sourceData.issues) {
assertIssueDisplayed(sourceIssue)
}
}
private fun assertIssueDisplayed(sourceIssue: SafetySourceIssue) {
findAllText(sourceIssue.title, sourceIssue.subtitle, sourceIssue.summary)
for (action in sourceIssue.actions) {
findButton(action.label)
}
}
private fun assertIssueNotDisplayed(sourceIssue: SafetySourceIssue) {
waitTextNotDisplayed(sourceIssue.title.toString())
}
private fun findButton(label: CharSequence): UiObject2 {
return waitFindObject(buttonSelector(label))
}
private fun waitButtonNotDisplayed(label: CharSequence) {
waitNotDisplayed(buttonSelector(label))
}
private fun buttonSelector(label: CharSequence): BySelector {
return By.clickable(true).text(Pattern.compile("$label|${label.toString().uppercase()}"))
}
private fun findAllText(vararg textToFind: CharSequence?) {
for (text in textToFind) if (text != null)
waitFindObject(By.text(text.toString()), FIND_TEXT_TIMEOUT.toMillis())
}
private fun waitTextNotDisplayed(text: String) {
waitNotDisplayed(By.text(text))
}
private fun waitNotDisplayed(selector: BySelector) {
val startMillis = SystemClock.elapsedRealtime()
while (true) {
try {
if (waitFindObjectOrNull(selector, NOT_DISPLAYED_CHECK_INTERVAL.toMillis()) ==
null) {
return
}
} catch (e: StaleObjectException) {
Log.d(
TAG,
"StaleObjectException while calling waitTextNotDisplayed, will retry " +
"if within timeout.")
}
if (Duration.ofMillis(SystemClock.elapsedRealtime() - startMillis) >=
NOT_DISPLAYED_TIMEOUT) {
break
}
Thread.sleep(NOT_DISPLAYED_CHECK_INTERVAL.toMillis())
}
throw AssertionError(
"View matching selector $selector is still displayed after waiting for at least" +
"$NOT_DISPLAYED_TIMEOUT")
}
private fun expandMoreIssuesCard() {
waitFindObject(By.text("See all alerts")).click()
}
companion object {
private val TAG = SafetyCenterActivityTest::class.java.simpleName
private val NOT_DISPLAYED_TIMEOUT = Duration.ofSeconds(20)
private val NOT_DISPLAYED_CHECK_INTERVAL = Duration.ofMillis(100)
private val FIND_TEXT_TIMEOUT = Duration.ofSeconds(25)
private fun UiDevice.rotate() {
if (isNaturalOrientation) {
setOrientationLeft()
} else {
setOrientationNatural()
}
waitForIdle()
}
private fun UiDevice.resetRotation() {
if (isNaturalOrientation) {
return
}
setOrientationNatural()
waitForIdle()
}
}
}