diff options
author | 2023-04-11 14:40:30 +0000 | |
---|---|---|
committer | 2023-04-18 14:20:26 +0000 | |
commit | 0bcd6616b16b54c6f5f27313b1cfae2e925f2f21 (patch) | |
tree | 5a8a455c49173be26cce2b3aa4405caad7e6fb11 /tests/hostside | |
parent | 64914b8eef408dc8d122ee42df22e05fe7f89602 (diff) |
Host/helper logging tests for state
Bug: 268309211
Test: atest SafetySourceStateCollectedLoggingHostTest
Change-Id: Ibe3342f9c299757a904a5b708b7ec8dd6d891258
Diffstat (limited to 'tests/hostside')
5 files changed, 313 insertions, 92 deletions
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 new file mode 100644 index 000000000..7c85ea02a --- /dev/null +++ b/tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetySourceStateCollectedLoggingHelperTests.kt @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2022 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.hostside.device + +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.compatibility.common.util.SystemUtil +import com.android.safetycenter.testing.SafetyCenterFlags +import com.android.safetycenter.testing.SafetyCenterTestConfigs +import com.android.safetycenter.testing.SafetyCenterTestConfigs.Companion.SOURCE_ID_1 +import com.android.safetycenter.testing.SafetyCenterTestHelper +import com.android.safetycenter.testing.SafetySourceTestData +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class SafetySourceStateCollectedLoggingHelperTests { + private val context: Context = ApplicationProvider.getApplicationContext() + private val safetyCenterTestHelper = SafetyCenterTestHelper(context) + private val safetySourceTestData = SafetySourceTestData(context) + private val safetyCenterTestConfigs = SafetyCenterTestConfigs(context) + + @Before + fun setUp() { + safetyCenterTestHelper.setup() + SafetyCenterFlags.allowStatsdLogging = true + safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.multipleSourcesConfig) + } + + @After + fun tearDown() { + safetyCenterTestHelper.reset() + } + + @Test + fun triggerStatsPull() { + val label = 1 // Arbitrary label in [0, 16) + val state = 3 // START + val command = "cmd stats log-app-breadcrumb $label $state" + SystemUtil.runShellCommandOrThrow(command) + } + + @Test + fun setSafetySourceData_source1() { + safetyCenterTestHelper.setData(SOURCE_ID_1, safetySourceTestData.information) + } +} diff --git a/tests/hostside/safetycenter/src/android/safetycenter/hostside/HelperApp.kt b/tests/hostside/safetycenter/src/android/safetycenter/hostside/HelperApp.kt new file mode 100644 index 000000000..810cce5c9 --- /dev/null +++ b/tests/hostside/safetycenter/src/android/safetycenter/hostside/HelperApp.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 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.hostside + +/** Constants related to the hostside test helper app */ +internal object HelperApp { + const val APK_NAME = "SafetyCenterHostSideTestsHelper.apk" + const val PACKAGE_NAME = "android.safetycenter.hostside.device" +} diff --git a/tests/hostside/safetycenter/src/android/safetycenter/hostside/ITestDeviceExtensions.kt b/tests/hostside/safetycenter/src/android/safetycenter/hostside/ITestDeviceExtensions.kt new file mode 100644 index 000000000..f8d4d64c0 --- /dev/null +++ b/tests/hostside/safetycenter/src/android/safetycenter/hostside/ITestDeviceExtensions.kt @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2023 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.hostside + +import android.cts.statsdatom.lib.DeviceUtils +import com.android.tradefed.device.ITestDevice +import com.android.tradefed.util.CommandStatus +import java.io.IOException + +/** Runs a test in the Safety Center hostside test helper app */ +internal fun ITestDevice.runTest(testClassName: String, testMethodName: String) { + DeviceUtils.runDeviceTests(this, HelperApp.PACKAGE_NAME, testClassName, testMethodName) +} + +/** Checks whether this [ITestDevice] supports Safety Center. */ +internal fun ITestDevice.supportsSafetyCenter(): Boolean = + executeShellCommandOrThrow("cmd safety_center supported").toBoolean() + +/** Checks whether Safety Center is currently enabled on this [ITestDevice]. */ +internal fun ITestDevice.isSafetyCenterEnabled(): Boolean = + executeShellCommandOrThrow("cmd safety_center enabled").toBoolean() + +/** Returns the package name of Safety Center on this [ITestDevice]. */ +internal fun ITestDevice.getSafetyCenterPackageName(): String = + executeShellCommandOrThrow("cmd safety_center package-name") + +private fun ITestDevice.executeShellCommandOrThrow(command: String): String { + val result = executeShellV2Command(command) + if (result.status != CommandStatus.SUCCESS) { + throw IOException("$command exited with status ${result.exitCode}") + } + return result.stdout.trim() +} diff --git a/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetyCenterInteractionLoggingHostTest.kt b/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetyCenterInteractionLoggingHostTest.kt index 4fa3b5830..c49e20c15 100644 --- a/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetyCenterInteractionLoggingHostTest.kt +++ b/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetyCenterInteractionLoggingHostTest.kt @@ -16,16 +16,15 @@ package android.safetycenter.hostside -import android.cts.statsdatom.lib.AtomTestUtils import android.cts.statsdatom.lib.ConfigUtils import android.cts.statsdatom.lib.DeviceUtils import android.cts.statsdatom.lib.ReportUtils import com.android.os.AtomsProto.Atom import com.android.os.AtomsProto.SafetyCenterInteractionReported -import com.android.tradefed.device.ITestDevice +import com.android.os.AtomsProto.SafetyCenterInteractionReported.Action +import com.android.os.AtomsProto.SafetyCenterInteractionReported.ViewType import com.android.tradefed.testtype.DeviceJUnit4ClassRunner import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test -import com.android.tradefed.util.CommandStatus import com.google.common.truth.Truth.assertThat import org.junit.After import org.junit.Assume.assumeTrue @@ -58,12 +57,10 @@ class SafetyCenterInteractionLoggingHostTest : BaseHostJUnit4Test() { ReportUtils.clearReports(device) ConfigUtils.uploadConfigForPushedAtom( device, - getSafetyCenterPackageName(), + device.getSafetyCenterPackageName(), Atom.SAFETY_CENTER_INTERACTION_REPORTED_FIELD_NUMBER ) - Thread.sleep(AtomTestUtils.WAIT_TIME_LONG.toLong()) - - DeviceUtils.installTestApp(device, HELPER_APK_NAME, HELPER_PACKAGE, build) + DeviceUtils.installTestApp(device, HelperApp.APK_NAME, HelperApp.PACKAGE_NAME, build) // TODO(b/239682646): Consider adding a target preparer that unlocks the device (like CTS) } @@ -74,132 +71,88 @@ class SafetyCenterInteractionLoggingHostTest : BaseHostJUnit4Test() { ConfigUtils.removeConfig(device) ReportUtils.clearReports(device) - DeviceUtils.uninstallTestApp(device, HELPER_PACKAGE) + DeviceUtils.uninstallTestApp(device, HelperApp.PACKAGE_NAME) } @Test fun openSafetyCenter_recordsSafetyCenterViewedEvent() { - executeDeviceTest(testMethodName = "openSafetyCenter") + device.runTest(TEST_CLASS_NAME, testMethodName = "openSafetyCenter") - val safetyCenterViewedEvents = - filterEventsByAction(SafetyCenterInteractionReported.Action.SAFETY_CENTER_VIEWED) + val safetyCenterViewedAtoms = getInteractionReportedAtoms(Action.SAFETY_CENTER_VIEWED) - assertThat(safetyCenterViewedEvents).isNotEmpty() + assertThat(safetyCenterViewedAtoms).isNotEmpty() } @Test fun sendNotification_recordsNotificationPostedEvent() { - executeDeviceTest( + device.runTest( testClassName = ".SafetyCenterNotificationLoggingHelperTests", testMethodName = "sendNotification" ) - val notificationPostedEvents = - filterEventsByAction(SafetyCenterInteractionReported.Action.NOTIFICATION_POSTED) + val notificationPostedAtoms = getInteractionReportedAtoms(Action.NOTIFICATION_POSTED) - assertThat(notificationPostedEvents).hasSize(1) - val atom = notificationPostedEvents.first().atom.safetyCenterInteractionReported - assertThat(atom.viewType) - .isEqualTo(SafetyCenterInteractionReported.ViewType.VIEW_TYPE_NOTIFICATION) + assertThat(notificationPostedAtoms).hasSize(1) + assertThat(notificationPostedAtoms.first().viewType) + .isEqualTo(ViewType.VIEW_TYPE_NOTIFICATION) } @Test fun openSubpageFromIntentExtra_recordsEventWithUnknownNavigationSource() { - executeDeviceTest(testMethodName = "openSubpageFromIntentExtra") + device.runTest(TEST_CLASS_NAME, testMethodName = "openSubpageFromIntentExtra") - val safetyCenterViewedEvents = - filterEventsByAction(SafetyCenterInteractionReported.Action.SAFETY_CENTER_VIEWED) + val safetyCenterViewedAtoms = getInteractionReportedAtoms(Action.SAFETY_CENTER_VIEWED) - assertThat(safetyCenterViewedEvents).hasSize(1) - val atom = safetyCenterViewedEvents.first().atom.safetyCenterInteractionReported - assertThat(atom.viewType).isEqualTo(SafetyCenterInteractionReported.ViewType.SUBPAGE) - assertThat(atom.navigationSource) - .isEqualTo(SafetyCenterInteractionReported.NavigationSource.SOURCE_UNKNOWN) - assertThat(atom.sessionId).isNotNull() + assertThat(safetyCenterViewedAtoms).hasSize(1) + with(safetyCenterViewedAtoms.first()) { + assertThat(viewType).isEqualTo(ViewType.SUBPAGE) + assertThat(navigationSource) + .isEqualTo(SafetyCenterInteractionReported.NavigationSource.SOURCE_UNKNOWN) + assertThat(sessionId).isNotNull() + } } @Test + @Ignore + // TODO(b/278202773): Fix/de-flake this test fun openSubpageFromHomepage_recordsEventWithSafetyCenterNavigationSource() { - executeDeviceTest(testMethodName = "openSubpageFromHomepage") - - val safetyCenterViewedEvents = - filterEventsByAction(SafetyCenterInteractionReported.Action.SAFETY_CENTER_VIEWED) + device.runTest(TEST_CLASS_NAME, testMethodName = "openSubpageFromHomepage") - assertThat(safetyCenterViewedEvents).hasSize(3) - val firstAtom = safetyCenterViewedEvents[0].atom.safetyCenterInteractionReported - assertThat(firstAtom.viewType).isEqualTo(SafetyCenterInteractionReported.ViewType.FULL) + val safetyCenterViewedAtoms = getInteractionReportedAtoms(Action.SAFETY_CENTER_VIEWED) - val secondAtom = safetyCenterViewedEvents[1].atom.safetyCenterInteractionReported - assertThat(secondAtom.viewType).isEqualTo(SafetyCenterInteractionReported.ViewType.SUBPAGE) - assertThat(secondAtom.navigationSource) + assertThat(safetyCenterViewedAtoms.map { it.viewType }) + .containsExactly(ViewType.FULL, ViewType.SUBPAGE, ViewType.FULL) + .inOrder() + assertThat(safetyCenterViewedAtoms[1].navigationSource) .isEqualTo(SafetyCenterInteractionReported.NavigationSource.SAFETY_CENTER) - - val thirdAtom = safetyCenterViewedEvents[2].atom.safetyCenterInteractionReported - assertThat(thirdAtom.viewType).isEqualTo(SafetyCenterInteractionReported.ViewType.FULL) - - assertThat(firstAtom.sessionId).isEqualTo(secondAtom.sessionId) - assertThat(secondAtom.sessionId).isEqualTo(thirdAtom.sessionId) + assertThat(safetyCenterViewedAtoms.map { it.sessionId }.distinct()).hasSize(1) } @Test @Ignore // TODO(b/278202773): Fix/de-flake this test fun openSubpageFromSettingsSearch_recordsEventWithSettingsNavigationSource() { - executeDeviceTest(testMethodName = "openSubpageFromSettingsSearch") - - val safetyCenterViewedEvents = - filterEventsByAction(SafetyCenterInteractionReported.Action.SAFETY_CENTER_VIEWED) - - assertThat(safetyCenterViewedEvents).hasSize(1) - val atom = safetyCenterViewedEvents.first().atom.safetyCenterInteractionReported - assertThat(atom.viewType).isEqualTo(SafetyCenterInteractionReported.ViewType.SUBPAGE) - assertThat(atom.navigationSource) - .isEqualTo(SafetyCenterInteractionReported.NavigationSource.SETTINGS) - assertThat(atom.sessionId).isNotNull() - } - - // TODO(b/239682646): Add more tests + device.runTest(TEST_CLASS_NAME, testMethodName = "openSubpageFromSettingsSearch") - private fun ITestDevice.supportsSafetyCenter(): Boolean { - val commandResult = executeShellV2Command("cmd safety_center supported") + val safetyCenterViewedAtoms = getInteractionReportedAtoms(Action.SAFETY_CENTER_VIEWED) - if (commandResult.status != CommandStatus.SUCCESS) { - throw AssertionError("Unable to check if Safety Center is supported: $commandResult") + assertThat(safetyCenterViewedAtoms).hasSize(1) + with(safetyCenterViewedAtoms.first()) { + assertThat(viewType).isEqualTo(ViewType.SUBPAGE) + assertThat(navigationSource) + .isEqualTo(SafetyCenterInteractionReported.NavigationSource.SETTINGS) + assertThat(sessionId).isNotNull() } - - return commandResult.stdout.trim().toBoolean() } - private fun ITestDevice.isSafetyCenterEnabled(): Boolean { - val commandResult = executeShellV2Command("cmd safety_center enabled") - - if (commandResult.status != CommandStatus.SUCCESS) { - throw AssertionError("Unable to check if Safety Center is enabled: $commandResult") - } - - return commandResult.stdout.trim().toBoolean() - } - - private fun getSafetyCenterPackageName(): String = - device.executeShellCommand("cmd safety_center package-name").trim() - - private fun executeDeviceTest( - testPkgName: String = HELPER_PACKAGE, - testClassName: String = HELPER_TEST_CLASS_NAME, - testMethodName: String = "openSafetyCenter" - ) { - DeviceUtils.runDeviceTests(device, testPkgName, testClassName, testMethodName) - Thread.sleep(AtomTestUtils.WAIT_TIME_LONG.toLong()) // Wait for report to be updated - } + // TODO(b/239682646): Add more tests - private fun filterEventsByAction(action: SafetyCenterInteractionReported.Action) = - ReportUtils.getEventMetricDataList(device).filter { - it.atom.safetyCenterInteractionReported.action == action - } + private fun getInteractionReportedAtoms(action: SafetyCenterInteractionReported.Action) = + ReportUtils.getEventMetricDataList(device) + .mapNotNull { it.atom.safetyCenterInteractionReported } + .filter { it.action == action } private companion object { - const val HELPER_APK_NAME = "SafetyCenterHostSideTestsHelper.apk" - const val HELPER_PACKAGE = "android.safetycenter.hostside.device" - const val HELPER_TEST_CLASS_NAME = ".SafetyCenterInteractionLoggingHelperTests" + const val TEST_CLASS_NAME = ".SafetyCenterInteractionLoggingHelperTests" } } diff --git a/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetySourceStateCollectedLoggingHostTest.kt b/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetySourceStateCollectedLoggingHostTest.kt new file mode 100644 index 000000000..610f1111f --- /dev/null +++ b/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetySourceStateCollectedLoggingHostTest.kt @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2022 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.hostside + +import android.cts.statsdatom.lib.ConfigUtils +import android.cts.statsdatom.lib.DeviceUtils +import android.cts.statsdatom.lib.ReportUtils +import com.android.os.AtomsProto.Atom +import com.android.os.AtomsProto.SafetySourceStateCollected +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner +import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test +import com.google.common.truth.Truth.assertThat +import org.junit.After +import org.junit.Assume.assumeTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +/** Host-side tests for Safety Center statsd logging. */ +@RunWith(DeviceJUnit4ClassRunner::class) +class SafetySourceStateCollectedLoggingHostTest : BaseHostJUnit4Test() { + + // Use lazy here because device is not available when the test is first constructed + private val shouldRunTests: Boolean by lazy { + // TODO(b/239682646): These tests should enable Safety Center + device.supportsSafetyCenter() && device.isSafetyCenterEnabled() + } + + @Before + fun assumeDeviceSupportsSafetyCenterToRunTests() { + assumeTrue(shouldRunTests) + } + + @Before + fun setUp() { + if (!shouldRunTests) return + + ConfigUtils.removeConfig(device) + ReportUtils.clearReports(device) + + val config = ConfigUtils.createConfigBuilder(device.getSafetyCenterPackageName()) + ConfigUtils.addGaugeMetric(config, Atom.SAFETY_STATE_FIELD_NUMBER) + ConfigUtils.addEventMetric(config, Atom.SAFETY_SOURCE_STATE_COLLECTED_FIELD_NUMBER) + ConfigUtils.uploadConfig(device, config) + DeviceUtils.installTestApp(device, HelperApp.APK_NAME, HelperApp.PACKAGE_NAME, build) + + // TODO(b/239682646): Consider adding a target preparer that unlocks the device (like CTS) + } + + @After + fun tearDown() { + if (!shouldRunTests) return + + ReportUtils.clearReports(device) + ConfigUtils.removeConfig(device) + DeviceUtils.uninstallTestApp(device, HelperApp.PACKAGE_NAME) + } + + @Test + fun triggerStatsPull_atomsPushedForAllSources() { + device.runTest(TEST_CLASS_NAME, "triggerStatsPull") + + val sourceStateAtoms = getSafetySourceStateCollectedAtoms() + + assertThat(sourceStateAtoms.map { it.encodedSafetySourceId }) + .containsExactly( + SOURCE_1_ENCODED_SOURCE_ID, + SOURCE_2_ENCODED_SOURCE_ID, + SOURCE_3_ENCODED_SOURCE_ID + ) + } + + @Test + fun triggerStatsPull_atomsHaveCollectionTypeAutomatic() { + device.runTest(TEST_CLASS_NAME, "triggerStatsPull") + + val sourceStateAtoms = getSafetySourceStateCollectedAtoms() + + assertThat(sourceStateAtoms.map { it.collectionType }.distinct()) + .containsExactly(SafetySourceStateCollected.CollectionType.AUTOMATIC) + } + + @Test + fun setSafetySourceData_atomPushedForThatSource() { + device.runTest(TEST_CLASS_NAME, "setSafetySourceData_source1") + + val sourceStateAtoms = getSafetySourceStateCollectedAtoms() + + assertThat(sourceStateAtoms.map { it.encodedSafetySourceId }) + .containsExactly(SOURCE_1_ENCODED_SOURCE_ID) + } + + @Test + fun setSafetySourceData_atomHasCollectionTypeSourceUpdated() { + device.runTest(TEST_CLASS_NAME, "setSafetySourceData_source1") + + val sourceStateAtoms = getSafetySourceStateCollectedAtoms() + + assertThat(sourceStateAtoms.map { it.collectionType }) + .containsExactly(SafetySourceStateCollected.CollectionType.SOURCE_UPDATED) + } + + private fun getSafetySourceStateCollectedAtoms() = + ReportUtils.getEventMetricDataList(device) + .mapNotNull { it.atom.safetySourceStateCollected } + .filterNot { + // Installing/uninstalling the helper app can cause Play Protect to run a scan and + // push new data to Safety Center which interferes with the test results so we + // specifically filter the resultant atoms out using the real encoded source ID + it.encodedSafetySourceId == PLAY_PROTECT_ENCODED_SOURCE_ID + } + + private companion object { + const val TEST_CLASS_NAME = ".SafetySourceStateCollectedLoggingHelperTests" + const val PLAY_PROTECT_ENCODED_SOURCE_ID = 7711894340233229936L + const val SOURCE_1_ENCODED_SOURCE_ID = 6446219357586936066L + const val SOURCE_2_ENCODED_SOURCE_ID = -5887429047684886602L + const val SOURCE_3_ENCODED_SOURCE_ID = -619470868366498469L + } +} |