summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Giulio Fiscella <fiscella@google.com> 2022-03-04 11:00:30 +0000
committer Giulio Fiscella <fiscella@google.com> 2022-03-04 11:28:50 +0000
commit0d386dcf211f772785a241477920f167639e2270 (patch)
treefbb322a22af036b8b69c350e83b4f1b1053bb653
parent56aaa3e896a01ce30d6449d520f1665947e35fc0 (diff)
Migrate Safety Center CTS tests inside Permission module
Nest APEX tests in their own folder Test: atest PermissionApexTests Test: atest CtsSafetyCenterTestCases Bug: 222475656 Change-Id: I686d211a499dbd4fe2fbe088fe8ed0ef53696a97
-rw-r--r--service/Android.bp2
-rw-r--r--tests/apex/Android.bp (renamed from tests/Android.bp)0
-rw-r--r--tests/apex/AndroidManifest.xml (renamed from tests/AndroidManifest.xml)0
-rw-r--r--tests/apex/AndroidTest.xml (renamed from tests/AndroidTest.xml)0
-rw-r--r--tests/apex/java/com/android/permission/persistence/RuntimePermissionsPersistenceTest.kt (renamed from tests/java/com/android/permission/persistence/RuntimePermissionsPersistenceTest.kt)0
-rw-r--r--tests/apex/java/com/android/role/persistence/RolesPersistenceTest.kt (renamed from tests/java/com/android/role/persistence/RolesPersistenceTest.kt)0
-rw-r--r--tests/cts/safetycenter/Android.bp49
-rw-r--r--tests/cts/safetycenter/AndroidManifest.xml39
-rw-r--r--tests/cts/safetycenter/AndroidTest.xml50
-rw-r--r--tests/cts/safetycenter/OWNERS3
-rw-r--r--tests/cts/safetycenter/res/values/strings.xml20
-rw-r--r--tests/cts/safetycenter/res/xml/config_dynamic_safety_source_all_disabled_no_work.xml17
-rw-r--r--tests/cts/safetycenter/res/xml/config_dynamic_safety_source_all_no_work.xml16
-rw-r--r--tests/cts/safetycenter/res/xml/config_dynamic_safety_source_disabled_no_summary.xml16
-rw-r--r--tests/cts/safetycenter/res/xml/config_dynamic_safety_source_disabled_no_title.xml16
-rw-r--r--tests/cts/safetycenter/res/xml/config_dynamic_safety_source_duplicate_key.xml28
-rw-r--r--tests/cts/safetycenter/res/xml/config_dynamic_safety_source_hidden_with_intent.xml15
-rw-r--r--tests/cts/safetycenter/res/xml/config_dynamic_safety_source_hidden_with_summary.xml15
-rw-r--r--tests/cts/safetycenter/res/xml/config_dynamic_safety_source_hidden_with_title.xml15
-rw-r--r--tests/cts/safetycenter/res/xml/config_dynamic_safety_source_invalid_display.xml17
-rw-r--r--tests/cts/safetycenter/res/xml/config_dynamic_safety_source_invalid_profile.xml16
-rw-r--r--tests/cts/safetycenter/res/xml/config_dynamic_safety_source_no_id.xml15
-rw-r--r--tests/cts/safetycenter/res/xml/config_dynamic_safety_source_no_intent.xml15
-rw-r--r--tests/cts/safetycenter/res/xml/config_dynamic_safety_source_no_package.xml15
-rw-r--r--tests/cts/safetycenter/res/xml/config_dynamic_safety_source_no_profile.xml15
-rw-r--r--tests/cts/safetycenter/res/xml/config_dynamic_safety_source_no_summary.xml15
-rw-r--r--tests/cts/safetycenter/res/xml/config_dynamic_safety_source_no_title.xml15
-rw-r--r--tests/cts/safetycenter/res/xml/config_dynamic_safety_source_primary_hidden_with_work.xml15
-rw-r--r--tests/cts/safetycenter/res/xml/config_dynamic_safety_source_primary_with_work.xml17
-rw-r--r--tests/cts/safetycenter/res/xml/config_issue_only_safety_source_duplicate_key.xml15
-rw-r--r--tests/cts/safetycenter/res/xml/config_issue_only_safety_source_invalid_profile.xml11
-rw-r--r--tests/cts/safetycenter/res/xml/config_issue_only_safety_source_no_id.xml10
-rw-r--r--tests/cts/safetycenter/res/xml/config_issue_only_safety_source_no_package.xml10
-rw-r--r--tests/cts/safetycenter/res/xml/config_issue_only_safety_source_no_profile.xml10
-rw-r--r--tests/cts/safetycenter/res/xml/config_issue_only_safety_source_with_display.xml12
-rw-r--r--tests/cts/safetycenter/res/xml/config_issue_only_safety_source_with_intent.xml12
-rw-r--r--tests/cts/safetycenter/res/xml/config_issue_only_safety_source_with_search.xml12
-rw-r--r--tests/cts/safetycenter/res/xml/config_issue_only_safety_source_with_summary.xml12
-rw-r--r--tests/cts/safetycenter/res/xml/config_issue_only_safety_source_with_title.xml12
-rw-r--r--tests/cts/safetycenter/res/xml/config_issue_only_safety_source_with_work.xml12
-rw-r--r--tests/cts/safetycenter/res/xml/config_mixed_safety_source_duplicate_key.xml26
-rw-r--r--tests/cts/safetycenter/res/xml/config_reference_invalid.xml15
-rw-r--r--tests/cts/safetycenter/res/xml/config_safety_center_config_missing.xml2
-rw-r--r--tests/cts/safetycenter/res/xml/config_safety_sources_config_empty.xml4
-rw-r--r--tests/cts/safetycenter/res/xml/config_safety_sources_config_missing.xml4
-rw-r--r--tests/cts/safetycenter/res/xml/config_safety_sources_group_duplicate_id.xml26
-rw-r--r--tests/cts/safetycenter/res/xml/config_safety_sources_group_empty.xml9
-rw-r--r--tests/cts/safetycenter/res/xml/config_safety_sources_group_invalid_icon.xml16
-rw-r--r--tests/cts/safetycenter/res/xml/config_safety_sources_group_no_id.xml14
-rw-r--r--tests/cts/safetycenter/res/xml/config_safety_sources_group_no_title.xml15
-rw-r--r--tests/cts/safetycenter/res/xml/config_static_safety_source_duplicate_key.xml26
-rw-r--r--tests/cts/safetycenter/res/xml/config_static_safety_source_invalid_profile.xml15
-rw-r--r--tests/cts/safetycenter/res/xml/config_static_safety_source_no_id.xml14
-rw-r--r--tests/cts/safetycenter/res/xml/config_static_safety_source_no_intent.xml14
-rw-r--r--tests/cts/safetycenter/res/xml/config_static_safety_source_no_profile.xml14
-rw-r--r--tests/cts/safetycenter/res/xml/config_static_safety_source_no_summary.xml14
-rw-r--r--tests/cts/safetycenter/res/xml/config_static_safety_source_no_title.xml14
-rw-r--r--tests/cts/safetycenter/res/xml/config_static_safety_source_with_broadcast.xml16
-rw-r--r--tests/cts/safetycenter/res/xml/config_static_safety_source_with_display.xml16
-rw-r--r--tests/cts/safetycenter/res/xml/config_static_safety_source_with_logging.xml16
-rw-r--r--tests/cts/safetycenter/res/xml/config_static_safety_source_with_package.xml16
-rw-r--r--tests/cts/safetycenter/res/xml/config_static_safety_source_with_primary_and_work.xml16
-rw-r--r--tests/cts/safetycenter/res/xml/config_static_safety_source_with_refresh.xml16
-rw-r--r--tests/cts/safetycenter/res/xml/config_static_safety_source_with_severity.xml16
-rw-r--r--tests/cts/safetycenter/res/xml/config_valid.xml97
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/config/cts/ParserConfigInvalidTest.kt382
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/config/cts/ParserConfigValidTest.kt145
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/config/cts/SafetyCenterConfigTest.kt82
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/config/cts/SafetySourceTest.kt699
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/config/cts/SafetySourcesGroupTest.kt285
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterActivityTest.kt45
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterApisWithShellPermissions.kt109
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterDataTest.kt251
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryGroupTest.kt258
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryOrGroupTest.kt172
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryTest.kt375
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterErrorTest.kt81
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterIssueTest.kt503
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt498
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryGroupTest.kt125
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryTest.kt138
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStatusTest.kt207
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetyEventTest.kt189
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceBroadcastReceiver.kt93
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceDataTest.kt296
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceErrorTest.kt78
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceIssueTest.kt785
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceStatusTest.kt458
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/XmlConfigTest.kt39
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/testers/AnyTester.kt42
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/testers/ParcelableTester.kt25
91 files changed, 7394 insertions, 1 deletions
diff --git a/service/Android.bp b/service/Android.bp
index 3cdcf0599..3377541e9 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -87,7 +87,7 @@ java_sdk_library {
"//frameworks/base/apex/permission/tests",
"//frameworks/base/services/tests/mockingservicestests",
"//frameworks/base/services/tests/servicestests",
- "//packages/modules/Permission/tests",
+ "//packages/modules/Permission/tests/apex",
],
srcs: [
":service-permission-sources",
diff --git a/tests/Android.bp b/tests/apex/Android.bp
index 126fcb4b2..126fcb4b2 100644
--- a/tests/Android.bp
+++ b/tests/apex/Android.bp
diff --git a/tests/AndroidManifest.xml b/tests/apex/AndroidManifest.xml
index 57ee6417a..57ee6417a 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/apex/AndroidManifest.xml
diff --git a/tests/AndroidTest.xml b/tests/apex/AndroidTest.xml
index 283f008a5..283f008a5 100644
--- a/tests/AndroidTest.xml
+++ b/tests/apex/AndroidTest.xml
diff --git a/tests/java/com/android/permission/persistence/RuntimePermissionsPersistenceTest.kt b/tests/apex/java/com/android/permission/persistence/RuntimePermissionsPersistenceTest.kt
index 2987da087..2987da087 100644
--- a/tests/java/com/android/permission/persistence/RuntimePermissionsPersistenceTest.kt
+++ b/tests/apex/java/com/android/permission/persistence/RuntimePermissionsPersistenceTest.kt
diff --git a/tests/java/com/android/role/persistence/RolesPersistenceTest.kt b/tests/apex/java/com/android/role/persistence/RolesPersistenceTest.kt
index f9d9d5afb..f9d9d5afb 100644
--- a/tests/java/com/android/role/persistence/RolesPersistenceTest.kt
+++ b/tests/apex/java/com/android/role/persistence/RolesPersistenceTest.kt
diff --git a/tests/cts/safetycenter/Android.bp b/tests/cts/safetycenter/Android.bp
new file mode 100644
index 000000000..f3b971635
--- /dev/null
+++ b/tests/cts/safetycenter/Android.bp
@@ -0,0 +1,49 @@
+//
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+ name: "CtsSafetyCenterTestCases",
+ defaults: ["mts-target-sdk-version-current"],
+ sdk_version: "test_current",
+ min_sdk_version: "30",
+ srcs: [
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+ "androidx.test.runner",
+ "androidx.test.uiautomator_uiautomator",
+ "compatibility-device-util-axt",
+ "ctstestrunner-axt",
+ "kotlin-stdlib",
+ "kotlinx-coroutines-android",
+ "kotlin-test",
+ "modules-utils-build_system",
+ "safety-center-resources-lib",
+ "truth-prebuilt",
+ ],
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ ],
+}
diff --git a/tests/cts/safetycenter/AndroidManifest.xml b/tests/cts/safetycenter/AndroidManifest.xml
new file mode 100644
index 000000000..cfa22f72b
--- /dev/null
+++ b/tests/cts/safetycenter/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.safetycenter.cts">
+
+ <application>
+ <uses-library android:name="android.test.runner"/>
+
+ <receiver android:name=".SafetySourceBroadcastReceiver" android:exported="false">
+ <intent-filter>
+ <action android:name="android.safetycenter.action.REFRESH_SAFETY_SOURCES"/>
+ </intent-filter>
+ </receiver>
+ </application>
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:label="CTS tests for SafetyCenter"
+ android:targetPackage="android.safetycenter.cts">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener"/>
+ </instrumentation>
+
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
+
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+</manifest>
diff --git a/tests/cts/safetycenter/AndroidTest.xml b/tests/cts/safetycenter/AndroidTest.xml
new file mode 100644
index 000000000..fc74c4a71
--- /dev/null
+++ b/tests/cts/safetycenter/AndroidTest.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<configuration description="Config for CTS SafetyCenter test cases">
+
+ <!-- TODO(b/207111503): Use Sdk33ModuleController once available, and remove @SdkSuppress
+ annotations -->
+ <object
+ class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController"
+ type="module_controller"/>
+
+ <option name="config-descriptor:metadata" key="component" value="framework"/>
+ <option name="config-descriptor:metadata" key="parameter"
+ value="not_instant_app"/>
+ <option name="config-descriptor:metadata" key="parameter"
+ value="not_multi_abi"/>
+ <option name="config-descriptor:metadata" key="parameter"
+ value="secondary_user"/>
+
+ <option name="test-suite-tag" value="cts"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="force-skip-system-props"
+ value="true"/> <!-- avoid restarting device -->
+ </target_preparer>
+
+ <target_preparer
+ class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true"/>
+ <option name="test-file-name" value="CtsSafetyCenterTestCases.apk"/>
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="android.safetycenter.cts"/>
+ <option name="runtime-hint" value="5m"/>
+ </test>
+</configuration>
diff --git a/tests/cts/safetycenter/OWNERS b/tests/cts/safetycenter/OWNERS
new file mode 100644
index 000000000..026d342a8
--- /dev/null
+++ b/tests/cts/safetycenter/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 1026964
+
+include platform/frameworks/base:/core/java/android/permission/OWNERS
diff --git a/tests/cts/safetycenter/res/values/strings.xml b/tests/cts/safetycenter/res/values/strings.xml
new file mode 100644
index 000000000..8ba7f04a8
--- /dev/null
+++ b/tests/cts/safetycenter/res/values/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Test reference -->
+ <string name="reference" translatable="false">Reference</string>
+</resources>
diff --git a/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_all_disabled_no_work.xml b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_all_disabled_no_work.xml
new file mode 100644
index 000000000..b2594c7ab
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_all_disabled_no_work.xml
@@ -0,0 +1,17 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <dynamic-safety-source
+ id="id"
+ packageName="package"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="all_profiles"
+ initialDisplayState="disabled"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_all_no_work.xml b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_all_no_work.xml
new file mode 100644
index 000000000..e582e0470
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_all_no_work.xml
@@ -0,0 +1,16 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <dynamic-safety-source
+ id="id"
+ packageName="package"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="all_profiles"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_disabled_no_summary.xml b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_disabled_no_summary.xml
new file mode 100644
index 000000000..6d065992c
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_disabled_no_summary.xml
@@ -0,0 +1,16 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <dynamic-safety-source
+ id="id"
+ packageName="package"
+ title="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"
+ initialDisplayState="disabled"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_disabled_no_title.xml b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_disabled_no_title.xml
new file mode 100644
index 000000000..6d535766a
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_disabled_no_title.xml
@@ -0,0 +1,16 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <dynamic-safety-source
+ id="id"
+ packageName="package"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"
+ initialDisplayState="disabled"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_duplicate_key.xml b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_duplicate_key.xml
new file mode 100644
index 000000000..be7114d61
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_duplicate_key.xml
@@ -0,0 +1,28 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id1"
+ title="@string/reference"
+ summary="@string/reference">
+ <dynamic-safety-source
+ id="id"
+ packageName="package"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ <safety-sources-group
+ id="id2"
+ title="@string/reference"
+ summary="@string/reference">
+ <dynamic-safety-source
+ id="id"
+ packageName="package"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_hidden_with_intent.xml b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_hidden_with_intent.xml
new file mode 100644
index 000000000..b30a6d921
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_hidden_with_intent.xml
@@ -0,0 +1,15 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <dynamic-safety-source
+ id="id"
+ packageName="package"
+ intentAction="intent"
+ profile="primary_profile_only"
+ initialDisplayState="hidden"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_hidden_with_summary.xml b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_hidden_with_summary.xml
new file mode 100644
index 000000000..c0fd18120
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_hidden_with_summary.xml
@@ -0,0 +1,15 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <dynamic-safety-source
+ id="id"
+ packageName="package"
+ summary="@string/reference"
+ profile="primary_profile_only"
+ initialDisplayState="hidden"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_hidden_with_title.xml b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_hidden_with_title.xml
new file mode 100644
index 000000000..190fcdc87
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_hidden_with_title.xml
@@ -0,0 +1,15 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <dynamic-safety-source
+ id="id"
+ packageName="package"
+ title="@string/reference"
+ profile="primary_profile_only"
+ initialDisplayState="hidden"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_invalid_display.xml b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_invalid_display.xml
new file mode 100644
index 000000000..4c1d00a4c
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_invalid_display.xml
@@ -0,0 +1,17 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <dynamic-safety-source
+ id="id"
+ packageName="package"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"
+ initialDisplayState="invalid"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_invalid_profile.xml b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_invalid_profile.xml
new file mode 100644
index 000000000..303415b75
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_invalid_profile.xml
@@ -0,0 +1,16 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <dynamic-safety-source
+ id="id"
+ packageName="package"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="invalid"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_no_id.xml b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_no_id.xml
new file mode 100644
index 000000000..8ace4ea4c
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_no_id.xml
@@ -0,0 +1,15 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <dynamic-safety-source
+ packageName="package"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_no_intent.xml b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_no_intent.xml
new file mode 100644
index 000000000..6119e4aca
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_no_intent.xml
@@ -0,0 +1,15 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <dynamic-safety-source
+ id="id"
+ packageName="package"
+ title="@string/reference"
+ summary="@string/reference"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_no_package.xml b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_no_package.xml
new file mode 100644
index 000000000..2b40602b5
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_no_package.xml
@@ -0,0 +1,15 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <dynamic-safety-source
+ id="id"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_no_profile.xml b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_no_profile.xml
new file mode 100644
index 000000000..f54d9400a
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_no_profile.xml
@@ -0,0 +1,15 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <dynamic-safety-source
+ id="id"
+ packageName="package"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_no_summary.xml b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_no_summary.xml
new file mode 100644
index 000000000..0683f163a
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_no_summary.xml
@@ -0,0 +1,15 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <dynamic-safety-source
+ id="id"
+ packageName="package"
+ title="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_no_title.xml b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_no_title.xml
new file mode 100644
index 000000000..84f90e5d9
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_no_title.xml
@@ -0,0 +1,15 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <dynamic-safety-source
+ id="id"
+ packageName="package"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_primary_hidden_with_work.xml b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_primary_hidden_with_work.xml
new file mode 100644
index 000000000..ad7add545
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_primary_hidden_with_work.xml
@@ -0,0 +1,15 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <dynamic-safety-source
+ id="id"
+ packageName="package"
+ titleForWork="@string/reference"
+ profile="primary_profile_only"
+ initialDisplayState="hidden"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_primary_with_work.xml b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_primary_with_work.xml
new file mode 100644
index 000000000..3ecf80264
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_dynamic_safety_source_primary_with_work.xml
@@ -0,0 +1,17 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <dynamic-safety-source
+ id="id"
+ packageName="package"
+ title="@string/reference"
+ titleForWork="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_duplicate_key.xml b/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_duplicate_key.xml
new file mode 100644
index 000000000..9c00d5727
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_duplicate_key.xml
@@ -0,0 +1,15 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id">
+ <issue-only-safety-source
+ id="id"
+ packageName="package"
+ profile="primary_profile_only"/>
+ <issue-only-safety-source
+ id="id"
+ packageName="package"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_invalid_profile.xml b/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_invalid_profile.xml
new file mode 100644
index 000000000..dd85d55a8
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_invalid_profile.xml
@@ -0,0 +1,11 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id">
+ <issue-only-safety-source
+ id="id"
+ packageName="package"
+ profile="invalid"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_no_id.xml b/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_no_id.xml
new file mode 100644
index 000000000..d57db8e42
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_no_id.xml
@@ -0,0 +1,10 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id">
+ <issue-only-safety-source
+ packageName="package"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_no_package.xml b/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_no_package.xml
new file mode 100644
index 000000000..d68b55740
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_no_package.xml
@@ -0,0 +1,10 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id">
+ <issue-only-safety-source
+ id="id"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_no_profile.xml b/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_no_profile.xml
new file mode 100644
index 000000000..7e7b6ef5f
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_no_profile.xml
@@ -0,0 +1,10 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id">
+ <issue-only-safety-source
+ id="id"
+ packageName="package"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_with_display.xml b/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_with_display.xml
new file mode 100644
index 000000000..1fab83913
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_with_display.xml
@@ -0,0 +1,12 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id">
+ <issue-only-safety-source
+ id="id"
+ packageName="package"
+ profile="primary_profile_only"
+ initialDisplayState="disabled"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_with_intent.xml b/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_with_intent.xml
new file mode 100644
index 000000000..9a7fa6bb1
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_with_intent.xml
@@ -0,0 +1,12 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id">
+ <issue-only-safety-source
+ id="id"
+ packageName="package"
+ profile="primary_profile_only"
+ intentAction="intent"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_with_search.xml b/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_with_search.xml
new file mode 100644
index 000000000..b065a3898
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_with_search.xml
@@ -0,0 +1,12 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id">
+ <issue-only-safety-source
+ id="id"
+ packageName="package"
+ profile="primary_profile_only"
+ searchTerms="@string/reference"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_with_summary.xml b/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_with_summary.xml
new file mode 100644
index 000000000..7d010018b
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_with_summary.xml
@@ -0,0 +1,12 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id">
+ <issue-only-safety-source
+ id="id"
+ packageName="package"
+ profile="primary_profile_only"
+ summary="@string/reference"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_with_title.xml b/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_with_title.xml
new file mode 100644
index 000000000..3b46eeab7
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_with_title.xml
@@ -0,0 +1,12 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id">
+ <issue-only-safety-source
+ id="id"
+ packageName="package"
+ profile="primary_profile_only"
+ title="@string/reference"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_with_work.xml b/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_with_work.xml
new file mode 100644
index 000000000..e50149008
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_issue_only_safety_source_with_work.xml
@@ -0,0 +1,12 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id">
+ <issue-only-safety-source
+ id="id"
+ packageName="package"
+ profile="all_profiles"
+ titleForWork="@string/reference"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_mixed_safety_source_duplicate_key.xml b/tests/cts/safetycenter/res/xml/config_mixed_safety_source_duplicate_key.xml
new file mode 100644
index 000000000..e939a9c22
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_mixed_safety_source_duplicate_key.xml
@@ -0,0 +1,26 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id1"
+ title="@string/reference"
+ summary="@string/reference">
+ <static-safety-source
+ id="id"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ <safety-sources-group
+ id="id2"
+ title="@string/reference">
+ <dynamic-safety-source
+ id="id"
+ packageName="package"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_reference_invalid.xml b/tests/cts/safetycenter/res/xml/config_reference_invalid.xml
new file mode 100644
index 000000000..743172994
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_reference_invalid.xml
@@ -0,0 +1,15 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="title"
+ summary="@string/reference">
+ <static-safety-source
+ id="id"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="1"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_safety_center_config_missing.xml b/tests/cts/safetycenter/res/xml/config_safety_center_config_missing.xml
new file mode 100644
index 000000000..475791691
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_safety_center_config_missing.xml
@@ -0,0 +1,2 @@
+<other-root>
+</other-root>
diff --git a/tests/cts/safetycenter/res/xml/config_safety_sources_config_empty.xml b/tests/cts/safetycenter/res/xml/config_safety_sources_config_empty.xml
new file mode 100644
index 000000000..b26ffc008
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_safety_sources_config_empty.xml
@@ -0,0 +1,4 @@
+<safety-center-config>
+ <safety-sources-config>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_safety_sources_config_missing.xml b/tests/cts/safetycenter/res/xml/config_safety_sources_config_missing.xml
new file mode 100644
index 000000000..12545759a
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_safety_sources_config_missing.xml
@@ -0,0 +1,4 @@
+<safety-center-config>
+ <other-internal-config>
+ </other-internal-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_safety_sources_group_duplicate_id.xml b/tests/cts/safetycenter/res/xml/config_safety_sources_group_duplicate_id.xml
new file mode 100644
index 000000000..0ce73371e
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_safety_sources_group_duplicate_id.xml
@@ -0,0 +1,26 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <static-safety-source
+ id="id1"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <static-safety-source
+ id="id2"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_safety_sources_group_empty.xml b/tests/cts/safetycenter/res/xml/config_safety_sources_group_empty.xml
new file mode 100644
index 000000000..a395152f8
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_safety_sources_group_empty.xml
@@ -0,0 +1,9 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_safety_sources_group_invalid_icon.xml b/tests/cts/safetycenter/res/xml/config_safety_sources_group_invalid_icon.xml
new file mode 100644
index 000000000..42b4c47d6
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_safety_sources_group_invalid_icon.xml
@@ -0,0 +1,16 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference"
+ statelessIconType="invalid">
+ <static-safety-source
+ id="id"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_safety_sources_group_no_id.xml b/tests/cts/safetycenter/res/xml/config_safety_sources_group_no_id.xml
new file mode 100644
index 000000000..eaee6731a
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_safety_sources_group_no_id.xml
@@ -0,0 +1,14 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ title="@string/reference"
+ summary="@string/reference">
+ <static-safety-source
+ id="id"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_safety_sources_group_no_title.xml b/tests/cts/safetycenter/res/xml/config_safety_sources_group_no_title.xml
new file mode 100644
index 000000000..de3ce82b9
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_safety_sources_group_no_title.xml
@@ -0,0 +1,15 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ summary="@string/reference">
+ <dynamic-safety-source
+ id="id"
+ packageName="package"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_static_safety_source_duplicate_key.xml b/tests/cts/safetycenter/res/xml/config_static_safety_source_duplicate_key.xml
new file mode 100644
index 000000000..4fe5e1e65
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_static_safety_source_duplicate_key.xml
@@ -0,0 +1,26 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id1"
+ title="@string/reference"
+ summary="@string/reference">
+ <static-safety-source
+ id="id"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ <safety-sources-group
+ id="id2"
+ title="@string/reference"
+ summary="@string/reference">
+ <static-safety-source
+ id="id"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_static_safety_source_invalid_profile.xml b/tests/cts/safetycenter/res/xml/config_static_safety_source_invalid_profile.xml
new file mode 100644
index 000000000..7c135e782
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_static_safety_source_invalid_profile.xml
@@ -0,0 +1,15 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <static-safety-source
+ id="id"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="invalid"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_static_safety_source_no_id.xml b/tests/cts/safetycenter/res/xml/config_static_safety_source_no_id.xml
new file mode 100644
index 000000000..7b919343b
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_static_safety_source_no_id.xml
@@ -0,0 +1,14 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <static-safety-source
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_static_safety_source_no_intent.xml b/tests/cts/safetycenter/res/xml/config_static_safety_source_no_intent.xml
new file mode 100644
index 000000000..9e97efbc9
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_static_safety_source_no_intent.xml
@@ -0,0 +1,14 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <static-safety-source
+ id="id"
+ title="@string/reference"
+ summary="@string/reference"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_static_safety_source_no_profile.xml b/tests/cts/safetycenter/res/xml/config_static_safety_source_no_profile.xml
new file mode 100644
index 000000000..32d23c789
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_static_safety_source_no_profile.xml
@@ -0,0 +1,14 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <static-safety-source
+ id="id"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_static_safety_source_no_summary.xml b/tests/cts/safetycenter/res/xml/config_static_safety_source_no_summary.xml
new file mode 100644
index 000000000..fa92f0bd8
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_static_safety_source_no_summary.xml
@@ -0,0 +1,14 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <static-safety-source
+ id="id"
+ title="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_static_safety_source_no_title.xml b/tests/cts/safetycenter/res/xml/config_static_safety_source_no_title.xml
new file mode 100644
index 000000000..654a486a5
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_static_safety_source_no_title.xml
@@ -0,0 +1,14 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <static-safety-source
+ id="id"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_static_safety_source_with_broadcast.xml b/tests/cts/safetycenter/res/xml/config_static_safety_source_with_broadcast.xml
new file mode 100644
index 000000000..090bd5795
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_static_safety_source_with_broadcast.xml
@@ -0,0 +1,16 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <static-safety-source
+ id="id"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"
+ broadcastReceiverClassName="broadcast"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_static_safety_source_with_display.xml b/tests/cts/safetycenter/res/xml/config_static_safety_source_with_display.xml
new file mode 100644
index 000000000..69c9ca215
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_static_safety_source_with_display.xml
@@ -0,0 +1,16 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <static-safety-source
+ id="id"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"
+ initialDisplayState="disabled"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_static_safety_source_with_logging.xml b/tests/cts/safetycenter/res/xml/config_static_safety_source_with_logging.xml
new file mode 100644
index 000000000..090554b1e
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_static_safety_source_with_logging.xml
@@ -0,0 +1,16 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <static-safety-source
+ id="id"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"
+ loggingAllowed="false"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_static_safety_source_with_package.xml b/tests/cts/safetycenter/res/xml/config_static_safety_source_with_package.xml
new file mode 100644
index 000000000..2a982b4cc
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_static_safety_source_with_package.xml
@@ -0,0 +1,16 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <static-safety-source
+ id="id"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"
+ packageName="package"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_static_safety_source_with_primary_and_work.xml b/tests/cts/safetycenter/res/xml/config_static_safety_source_with_primary_and_work.xml
new file mode 100644
index 000000000..296e2bc4a
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_static_safety_source_with_primary_and_work.xml
@@ -0,0 +1,16 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <static-safety-source
+ id="id"
+ title="@string/reference"
+ titleForWork="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_static_safety_source_with_refresh.xml b/tests/cts/safetycenter/res/xml/config_static_safety_source_with_refresh.xml
new file mode 100644
index 000000000..caaeee6fb
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_static_safety_source_with_refresh.xml
@@ -0,0 +1,16 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <static-safety-source
+ id="id"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"
+ refreshOnPageOpenAllowed="true"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_static_safety_source_with_severity.xml b/tests/cts/safetycenter/res/xml/config_static_safety_source_with_severity.xml
new file mode 100644
index 000000000..a9f5d9b89
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_static_safety_source_with_severity.xml
@@ -0,0 +1,16 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@string/reference"
+ summary="@string/reference">
+ <static-safety-source
+ id="id"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"
+ maxSeverityLevel="300"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/res/xml/config_valid.xml b/tests/cts/safetycenter/res/xml/config_valid.xml
new file mode 100644
index 000000000..e2f9e7703
--- /dev/null
+++ b/tests/cts/safetycenter/res/xml/config_valid.xml
@@ -0,0 +1,97 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="dynamic"
+ title="@string/reference"
+ summary="@string/reference"
+ statelessIconType="privacy">
+ <dynamic-safety-source
+ id="dynamic_barebone"
+ packageName="package"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ <dynamic-safety-source
+ id="dynamic_all_optional"
+ packageName="package"
+ title="@string/reference"
+ titleForWork="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="all_profiles"
+ initialDisplayState="disabled"
+ maxSeverityLevel="300"
+ searchTerms="@string/reference"
+ broadcastReceiverClassName="broadcast"
+ loggingAllowed="false"
+ refreshOnPageOpenAllowed="true"/>
+ <dynamic-safety-source
+ id="dynamic_disabled"
+ packageName="package"
+ title="@string/reference"
+ summary="@string/reference"
+ profile="primary_profile_only"
+ initialDisplayState="disabled"/>
+ <dynamic-safety-source
+ id="dynamic_hidden"
+ packageName="package"
+ profile="all_profiles"
+ initialDisplayState="hidden"/>
+ </safety-sources-group>
+ <safety-sources-group
+ id="static"
+ title="@string/reference">
+ <static-safety-source
+ id="static_barebone"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ <static-safety-source
+ id="static_all_optional"
+ title="@string/reference"
+ titleForWork="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="all_profiles"
+ searchTerms="@string/reference"/>
+ </safety-sources-group>
+ <safety-sources-group
+ id="issue_only">
+ <issue-only-safety-source
+ id="issue_only_barebone"
+ packageName="package"
+ profile="primary_profile_only"/>
+ <issue-only-safety-source
+ id="issue_only_all_optional"
+ packageName="package"
+ profile="all_profiles"
+ maxSeverityLevel="300"
+ broadcastReceiverClassName="broadcast"
+ loggingAllowed="false"
+ refreshOnPageOpenAllowed="true"/>
+ </safety-sources-group>
+ <safety-sources-group
+ id="mixed"
+ title="@string/reference">
+ <dynamic-safety-source
+ id="mixed_dynamic_barebone"
+ packageName="package"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ <issue-only-safety-source
+ id="mixed_issue_only_barebone"
+ packageName="package"
+ profile="primary_profile_only"/>
+ <static-safety-source
+ id="mixed_static_barebone"
+ title="@string/reference"
+ summary="@string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/tests/cts/safetycenter/src/android/safetycenter/config/cts/ParserConfigInvalidTest.kt b/tests/cts/safetycenter/src/android/safetycenter/config/cts/ParserConfigInvalidTest.kt
new file mode 100644
index 000000000..d276865e4
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/config/cts/ParserConfigInvalidTest.kt
@@ -0,0 +1,382 @@
+/*
+ * 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.config.cts
+
+import android.content.Context
+import android.safetycenter.config.ParseException
+import android.safetycenter.config.SafetyCenterConfig
+import android.safetycenter.cts.R
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertThrows
+import org.junit.Test
+import org.junit.runners.Parameterized
+import org.junit.runner.RunWith
+
+@RunWith(Parameterized::class)
+class ParserConfigInvalidTest {
+ private val context: Context = getApplicationContext()
+
+ data class Params(
+ private val testName: String,
+ val configResourceId: Int,
+ val errorMessage: String,
+ val causeErrorMessage: String?
+ ) {
+ override fun toString() = testName
+ }
+
+ @Parameterized.Parameter
+ lateinit var params: Params
+
+ @Test
+ fun invalidConfig_throws() {
+ val parser = context.resources.getXml(params.configResourceId)
+ val thrown = assertThrows(ParseException::class.java) {
+ SafetyCenterConfig.fromXml(parser)
+ }
+ assertThat(thrown).hasMessageThat().isEqualTo(params.errorMessage)
+ if (params.causeErrorMessage != null) {
+ assertThat(thrown.cause).hasMessageThat().isEqualTo(params.causeErrorMessage)
+ }
+ }
+
+ companion object {
+ @JvmStatic
+ @Parameterized.Parameters(name = "{0}")
+ fun parameters() = arrayOf(
+ Params(
+ "ConfigDynamicSafetySourceAllDisabledNoWork",
+ R.xml.config_dynamic_safety_source_all_disabled_no_work,
+ "Element dynamic-safety-source invalid",
+ "Required attribute titleForWork missing"
+ ),
+ Params(
+ "ConfigDynamicSafetySourceAllNoWork",
+ R.xml.config_dynamic_safety_source_all_no_work,
+ "Element dynamic-safety-source invalid",
+ "Required attribute titleForWork missing"
+ ),
+ Params(
+ "ConfigDynamicSafetySourceDisabledNoSummary",
+ R.xml.config_dynamic_safety_source_disabled_no_summary,
+ "Element dynamic-safety-source invalid",
+ "Required attribute summary missing"
+ ),
+ Params(
+ "ConfigDynamicSafetySourceDisabledNoTitle",
+ R.xml.config_dynamic_safety_source_disabled_no_title,
+ "Element dynamic-safety-source invalid",
+ "Required attribute title missing"
+ ),
+ Params(
+ "ConfigDynamicSafetySourceDuplicateKey",
+ R.xml.config_dynamic_safety_source_duplicate_key,
+ "Element safety-sources-config invalid",
+ "Duplicate id id among safety sources"
+ ),
+ Params(
+ "ConfigDynamicSafetySourceHiddenWithIntent",
+ R.xml.config_dynamic_safety_source_hidden_with_intent,
+ "Element dynamic-safety-source invalid",
+ "Prohibited attribute intentAction present"
+ ),
+ Params(
+ "ConfigDynamicSafetySourceHiddenWithSummary",
+ R.xml.config_dynamic_safety_source_hidden_with_summary,
+ "Element dynamic-safety-source invalid",
+ "Prohibited attribute summary present"
+ ),
+ Params(
+ "ConfigDynamicSafetySourceHiddenWithTitle",
+ R.xml.config_dynamic_safety_source_hidden_with_title,
+ "Element dynamic-safety-source invalid",
+ "Prohibited attribute title present"
+ ),
+ Params(
+ "ConfigDynamicSafetySourceInvalidDisplay",
+ R.xml.config_dynamic_safety_source_invalid_display,
+ "Attribute dynamic-safety-source.initialDisplayState invalid",
+ null
+ ),
+ Params(
+ "ConfigDynamicSafetySourceInvalidProfile",
+ R.xml.config_dynamic_safety_source_invalid_profile,
+ "Attribute dynamic-safety-source.profile invalid",
+ null
+ ),
+ Params(
+ "ConfigDynamicSafetySourceNoId",
+ R.xml.config_dynamic_safety_source_no_id,
+ "Element dynamic-safety-source invalid",
+ "Required attribute id missing"
+ ),
+ Params(
+ "ConfigDynamicSafetySourceNoIntent",
+ R.xml.config_dynamic_safety_source_no_intent,
+ "Element dynamic-safety-source invalid",
+ "Required attribute intentAction missing"
+ ),
+ Params(
+ "ConfigDynamicSafetySourceNoPackage",
+ R.xml.config_dynamic_safety_source_no_package,
+ "Element dynamic-safety-source invalid",
+ "Required attribute packageName missing"
+ ),
+ Params(
+ "ConfigDynamicSafetySourceNoProfile",
+ R.xml.config_dynamic_safety_source_no_profile,
+ "Element dynamic-safety-source invalid",
+ "Required attribute profile missing"
+ ),
+ Params(
+ "ConfigDynamicSafetySourceNoSummary",
+ R.xml.config_dynamic_safety_source_no_summary,
+ "Element dynamic-safety-source invalid",
+ "Required attribute summary missing"
+ ),
+ Params(
+ "ConfigDynamicSafetySourceNoTitle",
+ R.xml.config_dynamic_safety_source_no_title,
+ "Element dynamic-safety-source invalid",
+ "Required attribute title missing"
+ ),
+ Params(
+ "ConfigDynamicSafetySourcePrimaryHiddenWithWork",
+ R.xml.config_dynamic_safety_source_primary_hidden_with_work,
+ "Element dynamic-safety-source invalid",
+ "Prohibited attribute titleForWork present"
+ ),
+ Params(
+ "ConfigDynamicSafetySourcePrimaryWithWork",
+ R.xml.config_dynamic_safety_source_primary_with_work,
+ "Element dynamic-safety-source invalid",
+ "Prohibited attribute titleForWork present"
+ ),
+ Params(
+ "ConfigIssueOnlySafetySourceDuplicateKey",
+ R.xml.config_issue_only_safety_source_duplicate_key,
+ "Element safety-sources-config invalid",
+ "Duplicate id id among safety sources"
+ ),
+ Params(
+ "ConfigIssueOnlySafetySourceInvalidProfile",
+ R.xml.config_issue_only_safety_source_invalid_profile,
+ "Attribute issue-only-safety-source.profile invalid",
+ null
+ ),
+ Params(
+ "ConfigIssueOnlySafetySourceNoId",
+ R.xml.config_issue_only_safety_source_no_id,
+ "Element issue-only-safety-source invalid",
+ "Required attribute id missing"
+ ),
+ Params(
+ "ConfigIssueOnlySafetySourceNoPackage",
+ R.xml.config_issue_only_safety_source_no_package,
+ "Element issue-only-safety-source invalid",
+ "Required attribute packageName missing"
+ ),
+ Params(
+ "ConfigIssueOnlySafetySourceNoProfile",
+ R.xml.config_issue_only_safety_source_no_profile,
+ "Element issue-only-safety-source invalid",
+ "Required attribute profile missing"
+ ),
+ Params(
+ "ConfigIssueOnlySafetySourceWithDisplay",
+ R.xml.config_issue_only_safety_source_with_display,
+ "Element issue-only-safety-source invalid",
+ "Prohibited attribute initialDisplayState present"
+ ),
+ Params(
+ "ConfigIssueOnlySafetySourceWithIntent",
+ R.xml.config_issue_only_safety_source_with_intent,
+ "Element issue-only-safety-source invalid",
+ "Prohibited attribute intentAction present"
+ ),
+ Params(
+ "ConfigIssueOnlySafetySourceWithSearch",
+ R.xml.config_issue_only_safety_source_with_search,
+ "Element issue-only-safety-source invalid",
+ "Prohibited attribute searchTerms present"
+ ),
+ Params(
+ "ConfigIssueOnlySafetySourceWithSummary",
+ R.xml.config_issue_only_safety_source_with_summary,
+ "Element issue-only-safety-source invalid",
+ "Prohibited attribute summary present"
+ ),
+ Params(
+ "ConfigIssueOnlySafetySourceWithTitle",
+ R.xml.config_issue_only_safety_source_with_title,
+ "Element issue-only-safety-source invalid",
+ "Prohibited attribute title present"
+ ),
+ Params(
+ "ConfigIssueOnlySafetySourceWithWork",
+ R.xml.config_issue_only_safety_source_with_work,
+ "Element issue-only-safety-source invalid",
+ "Prohibited attribute titleForWork present"
+ ),
+ Params(
+ "ConfigMixedSafetySourceDuplicateKey",
+ R.xml.config_mixed_safety_source_duplicate_key,
+ "Element safety-sources-config invalid",
+ "Duplicate id id among safety sources"
+ ),
+ Params(
+ "ConfigReferenceInvalid",
+ R.xml.config_reference_invalid,
+ "Reference title in safety-sources-group.title missing or invalid",
+ null
+ ),
+ Params(
+ "ConfigSafetyCenterConfigMissing",
+ R.xml.config_safety_center_config_missing,
+ "Element safety-center-config missing",
+ null
+ ),
+ Params(
+ "ConfigSafetySourcesConfigEmpty",
+ R.xml.config_safety_sources_config_empty,
+ "Element safety-sources-config invalid",
+ "No safety sources groups present"
+ ),
+ Params(
+ "ConfigSafetySourcesConfigMissing",
+ R.xml.config_safety_sources_config_missing,
+ "Element safety-sources-config missing",
+ null
+ ),
+ Params(
+ "ConfigSafetySourcesGroupDuplicateId",
+ R.xml.config_safety_sources_group_duplicate_id,
+ "Element safety-sources-config invalid",
+ "Duplicate id id among safety sources groups"
+ ),
+ Params(
+ "ConfigSafetySourcesGroupEmpty",
+ R.xml.config_safety_sources_group_empty,
+ "Element safety-sources-group invalid",
+ "Safety sources group empty"
+ ),
+ Params(
+ "ConfigSafetySourcesGroupInvalidIcon",
+ R.xml.config_safety_sources_group_invalid_icon,
+ "Attribute safety-sources-group.statelessIconType invalid",
+ null
+ ),
+ Params(
+ "ConfigSafetySourcesGroupNoId",
+ R.xml.config_safety_sources_group_no_id,
+ "Element safety-sources-group invalid",
+ "Required attribute id missing"
+ ),
+ Params(
+ "ConfigSafetySourcesGroupNoTitle",
+ R.xml.config_safety_sources_group_no_title,
+ "Element safety-sources-group invalid",
+ "Required attribute title missing"
+ ),
+ Params(
+ "ConfigStaticSafetySourceDuplicateKey",
+ R.xml.config_static_safety_source_duplicate_key,
+ "Element safety-sources-config invalid",
+ "Duplicate id id among safety sources"
+ ),
+ Params(
+ "ConfigStaticSafetySourceInvalidProfile",
+ R.xml.config_static_safety_source_invalid_profile,
+ "Attribute static-safety-source.profile invalid",
+ null
+ ),
+ Params(
+ "ConfigStaticSafetySourceNoId",
+ R.xml.config_static_safety_source_no_id,
+ "Element static-safety-source invalid",
+ "Required attribute id missing"
+ ),
+ Params(
+ "ConfigStaticSafetySourceNoIntent",
+ R.xml.config_static_safety_source_no_intent,
+ "Element static-safety-source invalid",
+ "Required attribute intentAction missing"
+ ),
+ Params(
+ "ConfigStaticSafetySourceNoProfile",
+ R.xml.config_static_safety_source_no_profile,
+ "Element static-safety-source invalid",
+ "Required attribute profile missing"
+ ),
+ Params(
+ "ConfigStaticSafetySourceNoSummary",
+ R.xml.config_static_safety_source_no_summary,
+ "Element static-safety-source invalid",
+ "Required attribute summary missing"
+ ),
+ Params(
+ "ConfigStaticSafetySourceNoTitle",
+ R.xml.config_static_safety_source_no_title,
+ "Element static-safety-source invalid",
+ "Required attribute title missing"
+ ),
+ Params(
+ "ConfigStaticSafetySourceWithBroadcast",
+ R.xml.config_static_safety_source_with_broadcast,
+ "Element static-safety-source invalid",
+ "Prohibited attribute broadcastReceiverClassName present"
+ ),
+ Params(
+ "ConfigStaticSafetySourceWithDisplay",
+ R.xml.config_static_safety_source_with_display,
+ "Element static-safety-source invalid",
+ "Prohibited attribute initialDisplayState present"
+ ),
+ Params(
+ "ConfigStaticSafetySourceWithLogging",
+ R.xml.config_static_safety_source_with_logging,
+ "Element static-safety-source invalid",
+ "Prohibited attribute loggingAllowed present"
+ ),
+ Params(
+ "ConfigStaticSafetySourceWithPackage",
+ R.xml.config_static_safety_source_with_package,
+ "Element static-safety-source invalid",
+ "Prohibited attribute packageName present"
+ ),
+ Params(
+ "ConfigStaticSafetySourceWithPrimaryAndWork",
+ R.xml.config_static_safety_source_with_primary_and_work,
+ "Element static-safety-source invalid",
+ "Prohibited attribute titleForWork present"
+ ),
+ Params(
+ "ConfigStaticSafetySourceWithRefresh",
+ R.xml.config_static_safety_source_with_refresh,
+ "Element static-safety-source invalid",
+ "Prohibited attribute refreshOnPageOpenAllowed present"
+ ),
+ Params(
+ "ConfigStaticSafetySourceWithSeverity",
+ R.xml.config_static_safety_source_with_severity,
+ "Element static-safety-source invalid",
+ "Prohibited attribute maxSeverityLevel present"
+ )
+ )
+ }
+}
diff --git a/tests/cts/safetycenter/src/android/safetycenter/config/cts/ParserConfigValidTest.kt b/tests/cts/safetycenter/src/android/safetycenter/config/cts/ParserConfigValidTest.kt
new file mode 100644
index 000000000..9190b6d0a
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/config/cts/ParserConfigValidTest.kt
@@ -0,0 +1,145 @@
+/*
+ * 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.config.cts
+
+import android.content.Context
+import android.safetycenter.config.SafetyCenterConfig
+import android.safetycenter.config.SafetySource
+import android.safetycenter.config.SafetySourcesGroup
+import android.safetycenter.cts.R
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class ParserConfigValidTest {
+ private val context: Context = getApplicationContext()
+
+ @Test
+ fun validConfig_matchesExpected() {
+ val parser = context.resources.getXml(R.xml.config_valid)
+ val expected = SafetyCenterConfig.Builder()
+ .addSafetySourcesGroup(SafetySourcesGroup.Builder()
+ .setId("dynamic")
+ .setTitleResId(R.string.reference)
+ .setSummaryResId(R.string.reference)
+ .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
+ .addSafetySource(SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+ .setId("dynamic_barebone")
+ .setPackageName("package")
+ .setTitleResId(R.string.reference)
+ .setSummaryResId(R.string.reference)
+ .setIntentAction("intent")
+ .setProfile(SafetySource.PROFILE_PRIMARY)
+ .build())
+ .addSafetySource(SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+ .setId("dynamic_all_optional")
+ .setPackageName("package")
+ .setTitleResId(R.string.reference)
+ .setTitleForWorkResId(R.string.reference)
+ .setSummaryResId(R.string.reference)
+ .setIntentAction("intent")
+ .setProfile(SafetySource.PROFILE_ALL)
+ .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED)
+ .setMaxSeverityLevel(300)
+ .setSearchTermsResId(R.string.reference)
+ .setBroadcastReceiverClassName("broadcast")
+ .setLoggingAllowed(false)
+ .setRefreshOnPageOpenAllowed(true)
+ .build())
+ .addSafetySource(SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+ .setId("dynamic_disabled")
+ .setPackageName("package")
+ .setTitleResId(R.string.reference)
+ .setSummaryResId(R.string.reference)
+ .setProfile(SafetySource.PROFILE_PRIMARY)
+ .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED)
+ .build())
+ .addSafetySource(SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+ .setId("dynamic_hidden")
+ .setPackageName("package")
+ .setProfile(SafetySource.PROFILE_ALL)
+ .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_HIDDEN)
+ .build())
+ .build())
+ .addSafetySourcesGroup(SafetySourcesGroup.Builder()
+ .setId("static")
+ .setTitleResId(R.string.reference)
+ .addSafetySource(SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_STATIC)
+ .setId("static_barebone")
+ .setTitleResId(R.string.reference)
+ .setSummaryResId(R.string.reference)
+ .setIntentAction("intent")
+ .setProfile(SafetySource.PROFILE_PRIMARY)
+ .build())
+ .addSafetySource(SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_STATIC)
+ .setId("static_all_optional")
+ .setTitleResId(R.string.reference)
+ .setTitleForWorkResId(R.string.reference)
+ .setSummaryResId(R.string.reference)
+ .setIntentAction("intent")
+ .setProfile(SafetySource.PROFILE_ALL)
+ .setSearchTermsResId(R.string.reference)
+ .build())
+ .build())
+ .addSafetySourcesGroup(SafetySourcesGroup.Builder()
+ .setId("issue_only")
+ .addSafetySource(SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY)
+ .setId("issue_only_barebone")
+ .setPackageName("package")
+ .setProfile(SafetySource.PROFILE_PRIMARY)
+ .build())
+ .addSafetySource(SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY)
+ .setId("issue_only_all_optional")
+ .setPackageName("package")
+ .setProfile(SafetySource.PROFILE_ALL)
+ .setMaxSeverityLevel(300)
+ .setBroadcastReceiverClassName("broadcast")
+ .setLoggingAllowed(false)
+ .setRefreshOnPageOpenAllowed(true)
+ .build())
+ .build())
+ .addSafetySourcesGroup(SafetySourcesGroup.Builder()
+ .setId("mixed")
+ .setTitleResId(R.string.reference)
+ .addSafetySource(SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+ .setId("mixed_dynamic_barebone")
+ .setPackageName("package")
+ .setTitleResId(R.string.reference)
+ .setSummaryResId(R.string.reference)
+ .setIntentAction("intent")
+ .setProfile(SafetySource.PROFILE_PRIMARY)
+ .build())
+ .addSafetySource(SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY)
+ .setId("mixed_issue_only_barebone")
+ .setPackageName("package")
+ .setProfile(SafetySource.PROFILE_PRIMARY)
+ .build())
+ .addSafetySource(SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_STATIC)
+ .setId("mixed_static_barebone")
+ .setTitleResId(R.string.reference)
+ .setSummaryResId(R.string.reference)
+ .setIntentAction("intent")
+ .setProfile(SafetySource.PROFILE_PRIMARY)
+ .build())
+ .build())
+ .build()
+ assertEquals(expected, SafetyCenterConfig.fromXml(parser))
+ }
+}
diff --git a/tests/cts/safetycenter/src/android/safetycenter/config/cts/SafetyCenterConfigTest.kt b/tests/cts/safetycenter/src/android/safetycenter/config/cts/SafetyCenterConfigTest.kt
new file mode 100644
index 000000000..4c5723343
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/config/cts/SafetyCenterConfigTest.kt
@@ -0,0 +1,82 @@
+/*
+ * 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.config.cts
+
+import android.os.Build.VERSION_CODES.TIRAMISU
+import android.safetycenter.config.SafetyCenterConfig
+import android.safetycenter.testers.AnyTester
+import android.safetycenter.testers.ParcelableTester
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** CTS tests for [SafetyCenterConfig]. */
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = TIRAMISU, codeName = "Tiramisu")
+class SafetyCenterConfigTest {
+ @Test
+ fun getSafetySources_returnsSafetySources() {
+ assertThat(BASE.safetySourcesGroups)
+ .containsExactly(
+ SafetySourcesGroupTest.RIGID,
+ SafetySourcesGroupTest.HIDDEN
+ ).inOrder()
+ }
+
+ @Test
+ fun describeContents_returns0() {
+ assertThat(BASE.describeContents()).isEqualTo(0)
+ }
+
+ @Test
+ fun createFromParcel_withWriteToParcel_returnsOriginalSafetyCenterConfig() {
+ ParcelableTester.assertThatRoundTripReturnsOriginal(BASE, SafetyCenterConfig.CREATOR)
+ }
+
+ // TODO(b/208473675): Use `EqualsTester` for testing `hashcode` and `equals`.
+ @Test
+ fun hashCode_equals_toString_withEqualByReference_areEqual() {
+ AnyTester.assertThatRepresentationsAreEqual(BASE, BASE)
+ }
+
+ @Test
+ fun hashCode_equals_toString_withAllFieldsEqual_areEqual() {
+ val baseAlt = SafetyCenterConfig.Builder()
+ .addSafetySourcesGroup(SafetySourcesGroupTest.RIGID)
+ .addSafetySourcesGroup(SafetySourcesGroupTest.HIDDEN)
+ .build()
+ AnyTester.assertThatRepresentationsAreEqual(BASE, baseAlt)
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentSafetySources_areNotEqual() {
+ val baseAlt = SafetyCenterConfig.Builder()
+ .addSafetySourcesGroup(SafetySourcesGroupTest.HIDDEN)
+ .addSafetySourcesGroup(SafetySourcesGroupTest.RIGID)
+ .build()
+ AnyTester.assertThatRepresentationsAreNotEqual(BASE, baseAlt)
+ }
+
+ companion object {
+ private val BASE = SafetyCenterConfig.Builder()
+ .addSafetySourcesGroup(SafetySourcesGroupTest.RIGID)
+ .addSafetySourcesGroup(SafetySourcesGroupTest.HIDDEN)
+ .build()
+ }
+}
diff --git a/tests/cts/safetycenter/src/android/safetycenter/config/cts/SafetySourceTest.kt b/tests/cts/safetycenter/src/android/safetycenter/config/cts/SafetySourceTest.kt
new file mode 100644
index 000000000..f2a96df7d
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/config/cts/SafetySourceTest.kt
@@ -0,0 +1,699 @@
+/*
+ * 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.config.cts
+
+import android.content.res.Resources
+import android.os.Build.VERSION_CODES.TIRAMISU
+import android.safetycenter.config.SafetySource
+import android.safetycenter.testers.AnyTester
+import android.safetycenter.testers.ParcelableTester
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertThrows
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** CTS tests for [SafetySource]. */
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = TIRAMISU, codeName = "Tiramisu")
+class SafetySourceTest {
+ @Test
+ fun getType_returnsType() {
+ assertThat(DYNAMIC_BAREBONE.type).isEqualTo(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+ assertThat(DYNAMIC_ALL_OPTIONAL.type).isEqualTo(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+ assertThat(DYNAMIC_HIDDEN.type).isEqualTo(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+ assertThat(DYNAMIC_DISABLED.type).isEqualTo(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+ assertThat(STATIC_BAREBONE.type).isEqualTo(SafetySource.SAFETY_SOURCE_TYPE_STATIC)
+ assertThat(STATIC_ALL_OPTIONAL.type).isEqualTo(SafetySource.SAFETY_SOURCE_TYPE_STATIC)
+ assertThat(ISSUE_ONLY_BAREBONE.type).isEqualTo(SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY)
+ assertThat(ISSUE_ONLY_ALL_OPTIONAL.type)
+ .isEqualTo(SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY)
+ }
+
+ @Test
+ fun getId_returnsId() {
+ assertThat(DYNAMIC_BAREBONE.id).isEqualTo(DYNAMIC_BAREBONE_ID)
+ assertThat(DYNAMIC_ALL_OPTIONAL.id).isEqualTo(DYNAMIC_ALL_OPTIONAL_ID)
+ assertThat(DYNAMIC_HIDDEN.id).isEqualTo(DYNAMIC_HIDDEN_ID)
+ assertThat(DYNAMIC_DISABLED.id).isEqualTo(DYNAMIC_DISABLED_ID)
+ assertThat(STATIC_BAREBONE.id).isEqualTo(STATIC_BAREBONE_ID)
+ assertThat(STATIC_ALL_OPTIONAL.id).isEqualTo(STATIC_ALL_OPTIONAL_ID)
+ assertThat(ISSUE_ONLY_BAREBONE.id).isEqualTo(ISSUE_ONLY_BAREBONE_ID)
+ assertThat(ISSUE_ONLY_ALL_OPTIONAL.id).isEqualTo(ISSUE_ONLY_ALL_OPTIONAL_ID)
+ }
+
+ @Test
+ fun getPackageName_returnsPackageNameOrThrows() {
+ assertThat(DYNAMIC_BAREBONE.packageName).isEqualTo(PACKAGE_NAME)
+ assertThat(DYNAMIC_ALL_OPTIONAL.packageName).isEqualTo(PACKAGE_NAME)
+ assertThat(DYNAMIC_HIDDEN.packageName).isEqualTo(PACKAGE_NAME)
+ assertThat(DYNAMIC_DISABLED.packageName).isEqualTo(PACKAGE_NAME)
+ assertThrows(UnsupportedOperationException::class.java) {
+ STATIC_BAREBONE.packageName
+ }
+ assertThrows(UnsupportedOperationException::class.java) {
+ STATIC_ALL_OPTIONAL.packageName
+ }
+ assertThat(ISSUE_ONLY_BAREBONE.packageName).isEqualTo(PACKAGE_NAME)
+ assertThat(ISSUE_ONLY_ALL_OPTIONAL.packageName).isEqualTo(PACKAGE_NAME)
+ }
+
+ @Test
+ fun getTitleResId_returnsTitleResIdOrThrows() {
+ assertThat(DYNAMIC_BAREBONE.titleResId).isEqualTo(REFERENCE_RES_ID)
+ assertThat(DYNAMIC_ALL_OPTIONAL.titleResId).isEqualTo(REFERENCE_RES_ID)
+ assertThat(DYNAMIC_DISABLED.titleResId).isEqualTo(REFERENCE_RES_ID)
+ assertThat(DYNAMIC_HIDDEN.titleResId).isEqualTo(Resources.ID_NULL)
+ assertThat(STATIC_BAREBONE.titleResId).isEqualTo(REFERENCE_RES_ID)
+ assertThat(STATIC_ALL_OPTIONAL.titleResId).isEqualTo(REFERENCE_RES_ID)
+ assertThrows(UnsupportedOperationException::class.java) {
+ ISSUE_ONLY_BAREBONE.titleResId
+ }
+ assertThrows(UnsupportedOperationException::class.java) {
+ ISSUE_ONLY_ALL_OPTIONAL.titleResId
+ }
+ }
+
+ @Test
+ fun getTitleForWorkResId_returnsTitleForWorkResIdOrThrows() {
+ assertThrows(UnsupportedOperationException::class.java) {
+ DYNAMIC_BAREBONE.titleForWorkResId
+ }
+ assertThat(DYNAMIC_ALL_OPTIONAL.titleForWorkResId).isEqualTo(REFERENCE_RES_ID)
+ assertThrows(UnsupportedOperationException::class.java) {
+ DYNAMIC_DISABLED.titleForWorkResId
+ }
+ assertThat(DYNAMIC_HIDDEN.titleForWorkResId).isEqualTo(Resources.ID_NULL)
+ assertThrows(UnsupportedOperationException::class.java) {
+ STATIC_BAREBONE.titleForWorkResId
+ }
+ assertThat(STATIC_ALL_OPTIONAL.titleForWorkResId).isEqualTo(REFERENCE_RES_ID)
+ assertThrows(UnsupportedOperationException::class.java) {
+ ISSUE_ONLY_BAREBONE.titleForWorkResId
+ }
+ assertThrows(UnsupportedOperationException::class.java) {
+ ISSUE_ONLY_ALL_OPTIONAL.titleForWorkResId
+ }
+ }
+
+ @Test
+ fun getSummaryResId_returnsSummaryResIdOrThrows() {
+ assertThat(DYNAMIC_BAREBONE.summaryResId).isEqualTo(REFERENCE_RES_ID)
+ assertThat(DYNAMIC_ALL_OPTIONAL.summaryResId).isEqualTo(REFERENCE_RES_ID)
+ assertThat(DYNAMIC_DISABLED.summaryResId).isEqualTo(REFERENCE_RES_ID)
+ assertThat(DYNAMIC_HIDDEN.summaryResId).isEqualTo(Resources.ID_NULL)
+ assertThat(STATIC_BAREBONE.summaryResId).isEqualTo(REFERENCE_RES_ID)
+ assertThat(STATIC_ALL_OPTIONAL.summaryResId).isEqualTo(REFERENCE_RES_ID)
+ assertThrows(UnsupportedOperationException::class.java) {
+ ISSUE_ONLY_BAREBONE.summaryResId
+ }
+ assertThrows(UnsupportedOperationException::class.java) {
+ ISSUE_ONLY_ALL_OPTIONAL.summaryResId
+ }
+ }
+
+ @Test
+ fun getIntentAction_returnsIntentActionOrThrows() {
+ assertThat(DYNAMIC_BAREBONE.intentAction).isEqualTo(INTENT_ACTION)
+ assertThat(DYNAMIC_ALL_OPTIONAL.intentAction).isEqualTo(INTENT_ACTION)
+ assertThat(DYNAMIC_DISABLED.intentAction).isNull()
+ assertThat(DYNAMIC_HIDDEN.intentAction).isNull()
+ assertThat(STATIC_BAREBONE.intentAction).isEqualTo(INTENT_ACTION)
+ assertThat(STATIC_ALL_OPTIONAL.intentAction).isEqualTo(INTENT_ACTION)
+ assertThrows(UnsupportedOperationException::class.java) {
+ ISSUE_ONLY_BAREBONE.intentAction
+ }
+ assertThrows(UnsupportedOperationException::class.java) {
+ ISSUE_ONLY_ALL_OPTIONAL.intentAction
+ }
+ }
+
+ @Test
+ fun getProfile_returnsProfile() {
+ assertThat(DYNAMIC_BAREBONE.profile).isEqualTo(SafetySource.PROFILE_PRIMARY)
+ assertThat(DYNAMIC_ALL_OPTIONAL.profile).isEqualTo(SafetySource.PROFILE_ALL)
+ assertThat(DYNAMIC_DISABLED.profile).isEqualTo(SafetySource.PROFILE_PRIMARY)
+ assertThat(DYNAMIC_HIDDEN.profile).isEqualTo(SafetySource.PROFILE_ALL)
+ assertThat(STATIC_BAREBONE.profile).isEqualTo(SafetySource.PROFILE_PRIMARY)
+ assertThat(STATIC_ALL_OPTIONAL.profile).isEqualTo(SafetySource.PROFILE_ALL)
+ assertThat(ISSUE_ONLY_BAREBONE.profile).isEqualTo(SafetySource.PROFILE_PRIMARY)
+ assertThat(ISSUE_ONLY_ALL_OPTIONAL.profile).isEqualTo(SafetySource.PROFILE_ALL)
+ }
+
+ @Test
+ fun getInitialDisplayState_returnsInitialDisplayStateOrThrows() {
+ assertThat(DYNAMIC_BAREBONE.initialDisplayState)
+ .isEqualTo(SafetySource.INITIAL_DISPLAY_STATE_ENABLED)
+ assertThat(DYNAMIC_ALL_OPTIONAL.initialDisplayState)
+ .isEqualTo(SafetySource.INITIAL_DISPLAY_STATE_DISABLED)
+ assertThat(DYNAMIC_DISABLED.initialDisplayState)
+ .isEqualTo(SafetySource.INITIAL_DISPLAY_STATE_DISABLED)
+ assertThat(DYNAMIC_HIDDEN.initialDisplayState)
+ .isEqualTo(SafetySource.INITIAL_DISPLAY_STATE_HIDDEN)
+ assertThrows(UnsupportedOperationException::class.java) {
+ STATIC_BAREBONE.initialDisplayState
+ }
+ assertThrows(UnsupportedOperationException::class.java) {
+ STATIC_ALL_OPTIONAL.initialDisplayState
+ }
+ assertThrows(UnsupportedOperationException::class.java) {
+ ISSUE_ONLY_BAREBONE.initialDisplayState
+ }
+ assertThrows(UnsupportedOperationException::class.java) {
+ ISSUE_ONLY_ALL_OPTIONAL.initialDisplayState
+ }
+ }
+
+ @Test
+ fun getMaxSeverityLevel_returnsMaxSeverityLevelOrThrows() {
+ assertThat(DYNAMIC_BAREBONE.maxSeverityLevel).isEqualTo(Integer.MAX_VALUE)
+ assertThat(DYNAMIC_ALL_OPTIONAL.maxSeverityLevel).isEqualTo(MAX_SEVERITY_LEVEL)
+ assertThat(DYNAMIC_DISABLED.maxSeverityLevel).isEqualTo(Integer.MAX_VALUE)
+ assertThat(DYNAMIC_HIDDEN.maxSeverityLevel).isEqualTo(Integer.MAX_VALUE)
+ assertThrows(UnsupportedOperationException::class.java) {
+ STATIC_BAREBONE.maxSeverityLevel
+ }
+ assertThrows(UnsupportedOperationException::class.java) {
+ STATIC_ALL_OPTIONAL.maxSeverityLevel
+ }
+ assertThat(ISSUE_ONLY_BAREBONE.maxSeverityLevel).isEqualTo(Integer.MAX_VALUE)
+ assertThat(ISSUE_ONLY_ALL_OPTIONAL.maxSeverityLevel).isEqualTo(MAX_SEVERITY_LEVEL)
+ }
+
+ @Test
+ fun getSearchTermsResId_returnsSearchTermsResIdOrThrows() {
+ assertThat(DYNAMIC_BAREBONE.searchTermsResId).isEqualTo(Resources.ID_NULL)
+ assertThat(DYNAMIC_ALL_OPTIONAL.searchTermsResId).isEqualTo(REFERENCE_RES_ID)
+ assertThat(DYNAMIC_DISABLED.searchTermsResId).isEqualTo(Resources.ID_NULL)
+ assertThat(DYNAMIC_HIDDEN.searchTermsResId).isEqualTo(Resources.ID_NULL)
+ assertThat(STATIC_BAREBONE.searchTermsResId).isEqualTo(Resources.ID_NULL)
+ assertThat(STATIC_ALL_OPTIONAL.searchTermsResId).isEqualTo(REFERENCE_RES_ID)
+ assertThrows(UnsupportedOperationException::class.java) {
+ ISSUE_ONLY_BAREBONE.searchTermsResId
+ }
+ assertThrows(UnsupportedOperationException::class.java) {
+ ISSUE_ONLY_ALL_OPTIONAL.searchTermsResId
+ }
+ }
+
+ @Test
+ fun getBroadcastReceiverClassName_returnsBroadcastReceiverClassNameOrThrows() {
+ assertThat(DYNAMIC_BAREBONE.broadcastReceiverClassName).isNull()
+ assertThat(DYNAMIC_ALL_OPTIONAL.broadcastReceiverClassName)
+ .isEqualTo(BROADCAST_RECEIVER_CLASS_NAME)
+ assertThat(DYNAMIC_DISABLED.broadcastReceiverClassName).isNull()
+ assertThat(DYNAMIC_HIDDEN.broadcastReceiverClassName).isNull()
+ assertThrows(UnsupportedOperationException::class.java) {
+ STATIC_BAREBONE.broadcastReceiverClassName
+ }
+ assertThrows(UnsupportedOperationException::class.java) {
+ STATIC_ALL_OPTIONAL.broadcastReceiverClassName
+ }
+ assertThat(ISSUE_ONLY_BAREBONE.broadcastReceiverClassName).isNull()
+ assertThat(ISSUE_ONLY_ALL_OPTIONAL.broadcastReceiverClassName)
+ .isEqualTo(BROADCAST_RECEIVER_CLASS_NAME)
+ }
+
+ @Test
+ fun isLoggingAllowed_returnsLoggingAllowedOrThrows() {
+ assertThat(DYNAMIC_BAREBONE.isLoggingAllowed).isEqualTo(true)
+ assertThat(DYNAMIC_ALL_OPTIONAL.isLoggingAllowed).isEqualTo(false)
+ assertThat(DYNAMIC_DISABLED.isLoggingAllowed).isEqualTo(true)
+ assertThat(DYNAMIC_HIDDEN.isLoggingAllowed).isEqualTo(true)
+ assertThrows(UnsupportedOperationException::class.java) {
+ STATIC_BAREBONE.isLoggingAllowed
+ }
+ assertThrows(UnsupportedOperationException::class.java) {
+ STATIC_ALL_OPTIONAL.isLoggingAllowed
+ }
+ assertThat(ISSUE_ONLY_BAREBONE.isLoggingAllowed).isEqualTo(true)
+ assertThat(ISSUE_ONLY_ALL_OPTIONAL.isLoggingAllowed).isEqualTo(false)
+ }
+
+ @Test
+ fun isRefreshOnPageOpenAllowed_returnsRefreshOnPageOpenAllowedOrThrows() {
+ assertThat(DYNAMIC_BAREBONE.isRefreshOnPageOpenAllowed).isEqualTo(false)
+ assertThat(DYNAMIC_ALL_OPTIONAL.isRefreshOnPageOpenAllowed).isEqualTo(true)
+ assertThat(DYNAMIC_DISABLED.isRefreshOnPageOpenAllowed).isEqualTo(false)
+ assertThat(DYNAMIC_HIDDEN.isRefreshOnPageOpenAllowed).isEqualTo(false)
+ assertThrows(UnsupportedOperationException::class.java) {
+ STATIC_BAREBONE.isRefreshOnPageOpenAllowed
+ }
+ assertThrows(UnsupportedOperationException::class.java) {
+ STATIC_ALL_OPTIONAL.isRefreshOnPageOpenAllowed
+ }
+ assertThat(ISSUE_ONLY_BAREBONE.isRefreshOnPageOpenAllowed).isEqualTo(false)
+ assertThat(ISSUE_ONLY_ALL_OPTIONAL.isRefreshOnPageOpenAllowed).isEqualTo(true)
+ }
+
+ @Test
+ fun describeContents_returns0() {
+ assertThat(DYNAMIC_BAREBONE.describeContents()).isEqualTo(0)
+ assertThat(DYNAMIC_ALL_OPTIONAL.describeContents()).isEqualTo(0)
+ assertThat(DYNAMIC_HIDDEN.describeContents()).isEqualTo(0)
+ assertThat(DYNAMIC_DISABLED.describeContents()).isEqualTo(0)
+ assertThat(STATIC_BAREBONE.describeContents()).isEqualTo(0)
+ assertThat(STATIC_ALL_OPTIONAL.describeContents()).isEqualTo(0)
+ assertThat(ISSUE_ONLY_BAREBONE.describeContents()).isEqualTo(0)
+ assertThat(ISSUE_ONLY_ALL_OPTIONAL.describeContents()).isEqualTo(0)
+ }
+
+ @Test
+ fun createFromParcel_withWriteToParcel_returnsOriginalSafetySource() {
+ ParcelableTester.assertThatRoundTripReturnsOriginal(DYNAMIC_BAREBONE, SafetySource.CREATOR)
+ ParcelableTester.assertThatRoundTripReturnsOriginal(
+ DYNAMIC_ALL_OPTIONAL,
+ SafetySource.CREATOR
+ )
+ ParcelableTester.assertThatRoundTripReturnsOriginal(DYNAMIC_HIDDEN, SafetySource.CREATOR)
+ ParcelableTester.assertThatRoundTripReturnsOriginal(DYNAMIC_DISABLED, SafetySource.CREATOR)
+ ParcelableTester.assertThatRoundTripReturnsOriginal(STATIC_BAREBONE, SafetySource.CREATOR)
+ ParcelableTester.assertThatRoundTripReturnsOriginal(
+ STATIC_ALL_OPTIONAL,
+ SafetySource.CREATOR
+ )
+ ParcelableTester.assertThatRoundTripReturnsOriginal(
+ ISSUE_ONLY_BAREBONE,
+ SafetySource.CREATOR
+ )
+ ParcelableTester.assertThatRoundTripReturnsOriginal(
+ ISSUE_ONLY_ALL_OPTIONAL,
+ SafetySource.CREATOR
+ )
+ }
+
+ // TODO(b/208473675): Use `EqualsTester` for testing `hashcode` and `equals`.
+ @Test
+ fun hashCode_equals_toString_withEqualByReference_areEqual() {
+ AnyTester.assertThatRepresentationsAreEqual(DYNAMIC_BAREBONE, DYNAMIC_BAREBONE)
+ AnyTester.assertThatRepresentationsAreEqual(DYNAMIC_ALL_OPTIONAL, DYNAMIC_ALL_OPTIONAL)
+ AnyTester.assertThatRepresentationsAreEqual(DYNAMIC_HIDDEN, DYNAMIC_HIDDEN)
+ AnyTester.assertThatRepresentationsAreEqual(DYNAMIC_DISABLED, DYNAMIC_DISABLED)
+ AnyTester.assertThatRepresentationsAreEqual(STATIC_BAREBONE, STATIC_BAREBONE)
+ AnyTester.assertThatRepresentationsAreEqual(STATIC_ALL_OPTIONAL, STATIC_ALL_OPTIONAL)
+ AnyTester.assertThatRepresentationsAreEqual(ISSUE_ONLY_BAREBONE, ISSUE_ONLY_BAREBONE)
+ AnyTester.assertThatRepresentationsAreEqual(
+ ISSUE_ONLY_ALL_OPTIONAL,
+ ISSUE_ONLY_ALL_OPTIONAL
+ )
+ }
+
+ @Test
+ fun hashCode_equals_toString_withAllFieldsEqual_areEqual() {
+ val dynamicAllOptionalCopy = SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+ .setId(DYNAMIC_ALL_OPTIONAL_ID)
+ .setPackageName(PACKAGE_NAME)
+ .setTitleResId(REFERENCE_RES_ID)
+ .setTitleForWorkResId(REFERENCE_RES_ID)
+ .setSummaryResId(REFERENCE_RES_ID)
+ .setIntentAction(INTENT_ACTION)
+ .setProfile(SafetySource.PROFILE_ALL)
+ .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED)
+ .setMaxSeverityLevel(MAX_SEVERITY_LEVEL)
+ .setSearchTermsResId(REFERENCE_RES_ID)
+ .setBroadcastReceiverClassName(BROADCAST_RECEIVER_CLASS_NAME)
+ .setLoggingAllowed(false)
+ .setRefreshOnPageOpenAllowed(true)
+ .build()
+ AnyTester.assertThatRepresentationsAreEqual(DYNAMIC_ALL_OPTIONAL, dynamicAllOptionalCopy)
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentTypes_areNotEqual() {
+ AnyTester.assertThatRepresentationsAreNotEqual(DYNAMIC_BAREBONE, STATIC_BAREBONE)
+ AnyTester.assertThatRepresentationsAreNotEqual(STATIC_BAREBONE, ISSUE_ONLY_BAREBONE)
+ AnyTester.assertThatRepresentationsAreNotEqual(ISSUE_ONLY_BAREBONE, DYNAMIC_BAREBONE)
+ AnyTester.assertThatRepresentationsAreNotEqual(DYNAMIC_ALL_OPTIONAL, STATIC_ALL_OPTIONAL)
+ AnyTester.assertThatRepresentationsAreNotEqual(STATIC_ALL_OPTIONAL, ISSUE_ONLY_ALL_OPTIONAL)
+ AnyTester.assertThatRepresentationsAreNotEqual(
+ ISSUE_ONLY_ALL_OPTIONAL,
+ DYNAMIC_ALL_OPTIONAL
+ )
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentIds_areNotEqual() {
+ val dynamicAllOptionalAlt = SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+ .setId("other")
+ .setPackageName(PACKAGE_NAME)
+ .setTitleResId(REFERENCE_RES_ID)
+ .setTitleForWorkResId(REFERENCE_RES_ID)
+ .setSummaryResId(REFERENCE_RES_ID)
+ .setIntentAction(INTENT_ACTION)
+ .setProfile(SafetySource.PROFILE_ALL)
+ .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED)
+ .setMaxSeverityLevel(MAX_SEVERITY_LEVEL)
+ .setSearchTermsResId(REFERENCE_RES_ID)
+ .setBroadcastReceiverClassName(BROADCAST_RECEIVER_CLASS_NAME)
+ .setLoggingAllowed(false)
+ .setRefreshOnPageOpenAllowed(true)
+ .build()
+ AnyTester.assertThatRepresentationsAreNotEqual(DYNAMIC_ALL_OPTIONAL, dynamicAllOptionalAlt)
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentPackageNames_areNotEqual() {
+ val dynamicAllOptionalAlt = SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+ .setId(DYNAMIC_ALL_OPTIONAL_ID)
+ .setPackageName("other")
+ .setTitleResId(REFERENCE_RES_ID)
+ .setTitleForWorkResId(REFERENCE_RES_ID)
+ .setSummaryResId(REFERENCE_RES_ID)
+ .setIntentAction(INTENT_ACTION)
+ .setProfile(SafetySource.PROFILE_ALL)
+ .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED)
+ .setMaxSeverityLevel(MAX_SEVERITY_LEVEL)
+ .setSearchTermsResId(REFERENCE_RES_ID)
+ .setBroadcastReceiverClassName(BROADCAST_RECEIVER_CLASS_NAME)
+ .setLoggingAllowed(false)
+ .setRefreshOnPageOpenAllowed(true)
+ .build()
+ AnyTester.assertThatRepresentationsAreNotEqual(DYNAMIC_ALL_OPTIONAL, dynamicAllOptionalAlt)
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentTitleResIds_areNotEqual() {
+ val dynamicAllOptionalAlt = SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+ .setId(DYNAMIC_ALL_OPTIONAL_ID)
+ .setPackageName(PACKAGE_NAME)
+ .setTitleResId(-1)
+ .setTitleForWorkResId(REFERENCE_RES_ID)
+ .setSummaryResId(REFERENCE_RES_ID)
+ .setIntentAction(INTENT_ACTION)
+ .setProfile(SafetySource.PROFILE_ALL)
+ .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED)
+ .setMaxSeverityLevel(MAX_SEVERITY_LEVEL)
+ .setSearchTermsResId(REFERENCE_RES_ID)
+ .setBroadcastReceiverClassName(BROADCAST_RECEIVER_CLASS_NAME)
+ .setLoggingAllowed(false)
+ .setRefreshOnPageOpenAllowed(true)
+ .build()
+ AnyTester.assertThatRepresentationsAreNotEqual(DYNAMIC_ALL_OPTIONAL, dynamicAllOptionalAlt)
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentTitleForWorkResIds_areNotEqual() {
+ val dynamicAllOptionalAlt = SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+ .setId(DYNAMIC_ALL_OPTIONAL_ID)
+ .setPackageName(PACKAGE_NAME)
+ .setTitleResId(REFERENCE_RES_ID)
+ .setTitleForWorkResId(-1)
+ .setSummaryResId(REFERENCE_RES_ID)
+ .setIntentAction(INTENT_ACTION)
+ .setProfile(SafetySource.PROFILE_ALL)
+ .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED)
+ .setMaxSeverityLevel(MAX_SEVERITY_LEVEL)
+ .setSearchTermsResId(REFERENCE_RES_ID)
+ .setBroadcastReceiverClassName(BROADCAST_RECEIVER_CLASS_NAME)
+ .setLoggingAllowed(false)
+ .setRefreshOnPageOpenAllowed(true)
+ .build()
+ AnyTester.assertThatRepresentationsAreNotEqual(DYNAMIC_ALL_OPTIONAL, dynamicAllOptionalAlt)
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentSummaryResIds_areNotEqual() {
+ val dynamicAllOptionalAlt = SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+ .setId(DYNAMIC_ALL_OPTIONAL_ID)
+ .setPackageName(PACKAGE_NAME)
+ .setTitleResId(REFERENCE_RES_ID)
+ .setTitleForWorkResId(REFERENCE_RES_ID)
+ .setSummaryResId(-1)
+ .setIntentAction(INTENT_ACTION)
+ .setProfile(SafetySource.PROFILE_ALL)
+ .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED)
+ .setMaxSeverityLevel(MAX_SEVERITY_LEVEL)
+ .setSearchTermsResId(REFERENCE_RES_ID)
+ .setBroadcastReceiverClassName(BROADCAST_RECEIVER_CLASS_NAME)
+ .setLoggingAllowed(false)
+ .setRefreshOnPageOpenAllowed(true)
+ .build()
+ AnyTester.assertThatRepresentationsAreNotEqual(DYNAMIC_ALL_OPTIONAL, dynamicAllOptionalAlt)
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentIntentActions_areNotEqual() {
+ val dynamicAllOptionalAlt = SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+ .setId(DYNAMIC_ALL_OPTIONAL_ID)
+ .setPackageName(PACKAGE_NAME)
+ .setTitleResId(REFERENCE_RES_ID)
+ .setTitleForWorkResId(REFERENCE_RES_ID)
+ .setSummaryResId(REFERENCE_RES_ID)
+ .setIntentAction("other")
+ .setProfile(SafetySource.PROFILE_ALL)
+ .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED)
+ .setMaxSeverityLevel(MAX_SEVERITY_LEVEL)
+ .setSearchTermsResId(REFERENCE_RES_ID)
+ .setBroadcastReceiverClassName(BROADCAST_RECEIVER_CLASS_NAME)
+ .setLoggingAllowed(false)
+ .setRefreshOnPageOpenAllowed(true)
+ .build()
+ AnyTester.assertThatRepresentationsAreNotEqual(DYNAMIC_ALL_OPTIONAL, dynamicAllOptionalAlt)
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentProfiles_areNotEqual() {
+ val dynamicHiddenAlt = SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+ .setId(DYNAMIC_HIDDEN_ID)
+ .setPackageName(PACKAGE_NAME)
+ .setProfile(SafetySource.PROFILE_PRIMARY)
+ .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_HIDDEN)
+ .build()
+ AnyTester.assertThatRepresentationsAreNotEqual(DYNAMIC_HIDDEN, dynamicHiddenAlt)
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentInitialDisplayStates_areNotEqual() {
+ val dynamicAllOptionalAlt = SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+ .setId(DYNAMIC_ALL_OPTIONAL_ID)
+ .setPackageName(PACKAGE_NAME)
+ .setTitleResId(REFERENCE_RES_ID)
+ .setTitleForWorkResId(REFERENCE_RES_ID)
+ .setSummaryResId(REFERENCE_RES_ID)
+ .setIntentAction(INTENT_ACTION)
+ .setProfile(SafetySource.PROFILE_ALL)
+ .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_ENABLED)
+ .setMaxSeverityLevel(MAX_SEVERITY_LEVEL)
+ .setSearchTermsResId(REFERENCE_RES_ID)
+ .setBroadcastReceiverClassName(BROADCAST_RECEIVER_CLASS_NAME)
+ .setLoggingAllowed(false)
+ .setRefreshOnPageOpenAllowed(true)
+ .build()
+ AnyTester.assertThatRepresentationsAreNotEqual(DYNAMIC_ALL_OPTIONAL, dynamicAllOptionalAlt)
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentMaxSeverityLevel_areNotEqual() {
+ val dynamicAllOptionalAlt = SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+ .setId(DYNAMIC_ALL_OPTIONAL_ID)
+ .setPackageName(PACKAGE_NAME)
+ .setTitleResId(REFERENCE_RES_ID)
+ .setTitleForWorkResId(REFERENCE_RES_ID)
+ .setSummaryResId(REFERENCE_RES_ID)
+ .setIntentAction(INTENT_ACTION)
+ .setProfile(SafetySource.PROFILE_ALL)
+ .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED)
+ .setMaxSeverityLevel(-1)
+ .setSearchTermsResId(REFERENCE_RES_ID)
+ .setBroadcastReceiverClassName(BROADCAST_RECEIVER_CLASS_NAME)
+ .setLoggingAllowed(false)
+ .setRefreshOnPageOpenAllowed(true)
+ .build()
+ AnyTester.assertThatRepresentationsAreNotEqual(DYNAMIC_ALL_OPTIONAL, dynamicAllOptionalAlt)
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentSearchTermsResIds_areNotEqual() {
+ val dynamicAllOptionalAlt = SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+ .setId(DYNAMIC_ALL_OPTIONAL_ID)
+ .setPackageName(PACKAGE_NAME)
+ .setTitleResId(REFERENCE_RES_ID)
+ .setTitleForWorkResId(REFERENCE_RES_ID)
+ .setSummaryResId(REFERENCE_RES_ID)
+ .setIntentAction(INTENT_ACTION)
+ .setProfile(SafetySource.PROFILE_ALL)
+ .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED)
+ .setMaxSeverityLevel(MAX_SEVERITY_LEVEL)
+ .setSearchTermsResId(-1)
+ .setBroadcastReceiverClassName(BROADCAST_RECEIVER_CLASS_NAME)
+ .setLoggingAllowed(false)
+ .setRefreshOnPageOpenAllowed(true)
+ .build()
+ AnyTester.assertThatRepresentationsAreNotEqual(DYNAMIC_ALL_OPTIONAL, dynamicAllOptionalAlt)
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentBroadcastReceiverClassNames_areNotEqual() {
+ val dynamicAllOptionalAlt = SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+ .setId(DYNAMIC_ALL_OPTIONAL_ID)
+ .setPackageName(PACKAGE_NAME)
+ .setTitleResId(REFERENCE_RES_ID)
+ .setTitleForWorkResId(REFERENCE_RES_ID)
+ .setSummaryResId(REFERENCE_RES_ID)
+ .setIntentAction(INTENT_ACTION)
+ .setProfile(SafetySource.PROFILE_ALL)
+ .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED)
+ .setMaxSeverityLevel(MAX_SEVERITY_LEVEL)
+ .setSearchTermsResId(REFERENCE_RES_ID)
+ .setBroadcastReceiverClassName("other")
+ .setLoggingAllowed(false)
+ .setRefreshOnPageOpenAllowed(true)
+ .build()
+ AnyTester.assertThatRepresentationsAreNotEqual(DYNAMIC_ALL_OPTIONAL, dynamicAllOptionalAlt)
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentLoggingAlloweds_areNotEqual() {
+ val dynamicAllOptionalAlt = SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+ .setId(DYNAMIC_ALL_OPTIONAL_ID)
+ .setPackageName(PACKAGE_NAME)
+ .setTitleResId(REFERENCE_RES_ID)
+ .setTitleForWorkResId(REFERENCE_RES_ID)
+ .setSummaryResId(REFERENCE_RES_ID)
+ .setIntentAction(INTENT_ACTION)
+ .setProfile(SafetySource.PROFILE_ALL)
+ .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED)
+ .setMaxSeverityLevel(MAX_SEVERITY_LEVEL)
+ .setSearchTermsResId(REFERENCE_RES_ID)
+ .setBroadcastReceiverClassName(BROADCAST_RECEIVER_CLASS_NAME)
+ .setLoggingAllowed(true)
+ .setRefreshOnPageOpenAllowed(true)
+ .build()
+ AnyTester.assertThatRepresentationsAreNotEqual(DYNAMIC_ALL_OPTIONAL, dynamicAllOptionalAlt)
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentRefreshOnPageOpenAlloweds_areNotEqual() {
+ val dynamicAllOptionalAlt = SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+ .setId(DYNAMIC_ALL_OPTIONAL_ID)
+ .setPackageName(PACKAGE_NAME)
+ .setTitleResId(REFERENCE_RES_ID)
+ .setTitleForWorkResId(REFERENCE_RES_ID)
+ .setSummaryResId(REFERENCE_RES_ID)
+ .setIntentAction(INTENT_ACTION)
+ .setProfile(SafetySource.PROFILE_ALL)
+ .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED)
+ .setMaxSeverityLevel(MAX_SEVERITY_LEVEL)
+ .setSearchTermsResId(REFERENCE_RES_ID)
+ .setBroadcastReceiverClassName(BROADCAST_RECEIVER_CLASS_NAME)
+ .setLoggingAllowed(false)
+ .setRefreshOnPageOpenAllowed(false)
+ .build()
+ AnyTester.assertThatRepresentationsAreNotEqual(DYNAMIC_ALL_OPTIONAL, dynamicAllOptionalAlt)
+ }
+
+ companion object {
+ private const val PACKAGE_NAME = "package"
+ private const val REFERENCE_RES_ID = 9999
+ private const val INTENT_ACTION = "intent"
+ private const val BROADCAST_RECEIVER_CLASS_NAME = "broadcast"
+ private const val MAX_SEVERITY_LEVEL = 300
+
+ private const val DYNAMIC_BAREBONE_ID = "dynamic_barebone"
+ private const val DYNAMIC_ALL_OPTIONAL_ID = "dynamic_all_optional"
+ private const val DYNAMIC_DISABLED_ID = "dynamic_disabled"
+ private const val DYNAMIC_HIDDEN_ID = "dynamic_hidden"
+ private const val STATIC_BAREBONE_ID = "static_barebone"
+ private const val STATIC_ALL_OPTIONAL_ID = "static_all_optional"
+ private const val ISSUE_ONLY_BAREBONE_ID = "issue_only_barebone"
+ private const val ISSUE_ONLY_ALL_OPTIONAL_ID = "issue_only_all_optional"
+
+ internal val DYNAMIC_BAREBONE =
+ SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+ .setId(DYNAMIC_BAREBONE_ID)
+ .setPackageName(PACKAGE_NAME)
+ .setTitleResId(REFERENCE_RES_ID)
+ .setSummaryResId(REFERENCE_RES_ID)
+ .setIntentAction(INTENT_ACTION)
+ .setProfile(SafetySource.PROFILE_PRIMARY)
+ .build()
+
+ private val DYNAMIC_ALL_OPTIONAL =
+ SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+ .setId(DYNAMIC_ALL_OPTIONAL_ID)
+ .setPackageName(PACKAGE_NAME)
+ .setTitleResId(REFERENCE_RES_ID)
+ .setTitleForWorkResId(REFERENCE_RES_ID)
+ .setSummaryResId(REFERENCE_RES_ID)
+ .setIntentAction(INTENT_ACTION)
+ .setProfile(SafetySource.PROFILE_ALL)
+ .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED)
+ .setMaxSeverityLevel(MAX_SEVERITY_LEVEL)
+ .setSearchTermsResId(REFERENCE_RES_ID)
+ .setBroadcastReceiverClassName(BROADCAST_RECEIVER_CLASS_NAME)
+ .setLoggingAllowed(false)
+ .setRefreshOnPageOpenAllowed(true)
+ .build()
+
+ private val DYNAMIC_DISABLED =
+ SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+ .setId(DYNAMIC_DISABLED_ID)
+ .setPackageName(PACKAGE_NAME)
+ .setTitleResId(REFERENCE_RES_ID)
+ .setSummaryResId(REFERENCE_RES_ID)
+ .setProfile(SafetySource.PROFILE_PRIMARY)
+ .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED)
+ .build()
+
+ private val DYNAMIC_HIDDEN =
+ SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+ .setId(DYNAMIC_HIDDEN_ID)
+ .setPackageName(PACKAGE_NAME)
+ .setProfile(SafetySource.PROFILE_ALL)
+ .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_HIDDEN)
+ .build()
+
+ internal val STATIC_BAREBONE =
+ SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_STATIC)
+ .setId(STATIC_BAREBONE_ID)
+ .setTitleResId(REFERENCE_RES_ID)
+ .setSummaryResId(REFERENCE_RES_ID)
+ .setIntentAction(INTENT_ACTION)
+ .setProfile(SafetySource.PROFILE_PRIMARY)
+ .build()
+
+ private val STATIC_ALL_OPTIONAL =
+ SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_STATIC)
+ .setId(STATIC_ALL_OPTIONAL_ID)
+ .setTitleResId(REFERENCE_RES_ID)
+ .setTitleForWorkResId(REFERENCE_RES_ID)
+ .setSummaryResId(REFERENCE_RES_ID)
+ .setIntentAction(INTENT_ACTION)
+ .setProfile(SafetySource.PROFILE_ALL)
+ .setSearchTermsResId(REFERENCE_RES_ID)
+ .build()
+
+ internal val ISSUE_ONLY_BAREBONE =
+ SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY)
+ .setId(ISSUE_ONLY_BAREBONE_ID)
+ .setPackageName(PACKAGE_NAME)
+ .setProfile(SafetySource.PROFILE_PRIMARY)
+ .build()
+
+ private val ISSUE_ONLY_ALL_OPTIONAL =
+ SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY)
+ .setId(ISSUE_ONLY_ALL_OPTIONAL_ID)
+ .setPackageName(PACKAGE_NAME)
+ .setProfile(SafetySource.PROFILE_ALL)
+ .setMaxSeverityLevel(MAX_SEVERITY_LEVEL)
+ .setBroadcastReceiverClassName(BROADCAST_RECEIVER_CLASS_NAME)
+ .setLoggingAllowed(false)
+ .setRefreshOnPageOpenAllowed(true)
+ .build()
+ }
+}
diff --git a/tests/cts/safetycenter/src/android/safetycenter/config/cts/SafetySourcesGroupTest.kt b/tests/cts/safetycenter/src/android/safetycenter/config/cts/SafetySourcesGroupTest.kt
new file mode 100644
index 000000000..2e61411bc
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/config/cts/SafetySourcesGroupTest.kt
@@ -0,0 +1,285 @@
+/*
+ * 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.config.cts
+
+import android.content.res.Resources
+import android.os.Build.VERSION_CODES.TIRAMISU
+import android.safetycenter.config.SafetySourcesGroup
+import android.safetycenter.testers.AnyTester
+import android.safetycenter.testers.ParcelableTester
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** CTS tests for [SafetySourcesGroup]. */
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = TIRAMISU, codeName = "Tiramisu")
+class SafetySourcesGroupTest {
+ @Test
+ fun getType_returnsType() {
+ assertThat(COLLAPSIBLE_WITH_SUMMARY.type)
+ .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE)
+ assertThat(COLLAPSIBLE_WITH_ICON.type)
+ .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE)
+ assertThat(COLLAPSIBLE_WITH_BOTH.type)
+ .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE)
+ assertThat(RIGID.type).isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_RIGID)
+ assertThat(HIDDEN.type).isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN)
+ }
+
+ @Test
+ fun getId_returnsId() {
+ assertThat(COLLAPSIBLE_WITH_SUMMARY.id).isEqualTo(COLLAPSIBLE_WITH_SUMMARY_ID)
+ assertThat(COLLAPSIBLE_WITH_ICON.id).isEqualTo(COLLAPSIBLE_WITH_ICON_ID)
+ assertThat(COLLAPSIBLE_WITH_BOTH.id).isEqualTo(COLLAPSIBLE_WITH_BOTH_ID)
+ assertThat(RIGID.id).isEqualTo(RIGID_ID)
+ assertThat(HIDDEN.id).isEqualTo(HIDDEN_ID)
+ }
+
+ @Test
+ fun getTitleResId_returnsTitleResId() {
+ assertThat(COLLAPSIBLE_WITH_SUMMARY.titleResId).isEqualTo(REFERENCE_RES_ID)
+ assertThat(COLLAPSIBLE_WITH_ICON.titleResId).isEqualTo(REFERENCE_RES_ID)
+ assertThat(COLLAPSIBLE_WITH_BOTH.titleResId).isEqualTo(REFERENCE_RES_ID)
+ assertThat(RIGID.titleResId).isEqualTo(REFERENCE_RES_ID)
+ // This is not an enforced invariant, titleResId should just be ignored for hidden groups
+ assertThat(HIDDEN.titleResId).isEqualTo(Resources.ID_NULL)
+ }
+
+ @Test
+ fun getSummaryResId_returnsSummaryResId() {
+ assertThat(COLLAPSIBLE_WITH_SUMMARY.summaryResId).isEqualTo(REFERENCE_RES_ID)
+ assertThat(COLLAPSIBLE_WITH_ICON.summaryResId).isEqualTo(Resources.ID_NULL)
+ assertThat(COLLAPSIBLE_WITH_BOTH.summaryResId).isEqualTo(REFERENCE_RES_ID)
+ assertThat(RIGID.summaryResId).isEqualTo(Resources.ID_NULL)
+ // This is not an enforced invariant, summaryResId should just be ignored for hidden groups
+ assertThat(HIDDEN.summaryResId).isEqualTo(Resources.ID_NULL)
+ }
+
+ @Test
+ fun getStatelessIconType_returnsStatelessIconType() {
+ assertThat(COLLAPSIBLE_WITH_SUMMARY.statelessIconType)
+ .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE)
+ assertThat(COLLAPSIBLE_WITH_ICON.statelessIconType)
+ .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
+ assertThat(COLLAPSIBLE_WITH_BOTH.statelessIconType)
+ .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
+ assertThat(RIGID.statelessIconType).isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE)
+ // This is not an enforced invariant
+ // statelessIconType should just be ignored for hidden groups
+ assertThat(HIDDEN.statelessIconType).isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE)
+ }
+
+ @Test
+ fun getSafetySources_returnsSafetySources() {
+ assertThat(COLLAPSIBLE_WITH_SUMMARY.safetySources)
+ .containsExactly(SafetySourceTest.DYNAMIC_BAREBONE)
+ assertThat(COLLAPSIBLE_WITH_ICON.safetySources)
+ .containsExactly(SafetySourceTest.STATIC_BAREBONE)
+ assertThat(COLLAPSIBLE_WITH_BOTH.safetySources)
+ .containsExactly(
+ SafetySourceTest.DYNAMIC_BAREBONE,
+ SafetySourceTest.STATIC_BAREBONE,
+ SafetySourceTest.ISSUE_ONLY_BAREBONE
+ ).inOrder()
+ assertThat(RIGID.safetySources).containsExactly(SafetySourceTest.STATIC_BAREBONE)
+ assertThat(HIDDEN.safetySources).containsExactly(SafetySourceTest.ISSUE_ONLY_BAREBONE)
+ }
+
+ @Test
+ fun describeContents_returns0() {
+ assertThat(COLLAPSIBLE_WITH_SUMMARY.describeContents()).isEqualTo(0)
+ assertThat(COLLAPSIBLE_WITH_ICON.describeContents()).isEqualTo(0)
+ assertThat(COLLAPSIBLE_WITH_BOTH.describeContents()).isEqualTo(0)
+ assertThat(RIGID.describeContents()).isEqualTo(0)
+ assertThat(HIDDEN.describeContents()).isEqualTo(0)
+ }
+
+ @Test
+ fun createFromParcel_withWriteToParcel_returnsOriginalSafetySourcesGroup() {
+ ParcelableTester.assertThatRoundTripReturnsOriginal(
+ COLLAPSIBLE_WITH_SUMMARY,
+ SafetySourcesGroup.CREATOR
+ )
+ ParcelableTester.assertThatRoundTripReturnsOriginal(
+ COLLAPSIBLE_WITH_ICON,
+ SafetySourcesGroup.CREATOR
+ )
+ ParcelableTester.assertThatRoundTripReturnsOriginal(
+ COLLAPSIBLE_WITH_BOTH,
+ SafetySourcesGroup.CREATOR
+ )
+ ParcelableTester.assertThatRoundTripReturnsOriginal(RIGID, SafetySourcesGroup.CREATOR)
+ ParcelableTester.assertThatRoundTripReturnsOriginal(HIDDEN, SafetySourcesGroup.CREATOR)
+ }
+
+ // TODO(b/208473675): Use `EqualsTester` for testing `hashcode` and `equals`.
+ @Test
+ fun hashCode_equals_toString_withEqualByReference_areEqual() {
+ AnyTester.assertThatRepresentationsAreEqual(
+ COLLAPSIBLE_WITH_SUMMARY,
+ COLLAPSIBLE_WITH_SUMMARY
+ )
+ AnyTester.assertThatRepresentationsAreEqual(COLLAPSIBLE_WITH_ICON, COLLAPSIBLE_WITH_ICON)
+ AnyTester.assertThatRepresentationsAreEqual(COLLAPSIBLE_WITH_BOTH, COLLAPSIBLE_WITH_BOTH)
+ AnyTester.assertThatRepresentationsAreEqual(RIGID, RIGID)
+ AnyTester.assertThatRepresentationsAreEqual(HIDDEN, HIDDEN)
+ }
+
+ @Test
+ fun hashCode_equals_toString_withAllFieldsEqual_areEqual() {
+ val collapsibleWithBothCopy = SafetySourcesGroup.Builder()
+ .setId(COLLAPSIBLE_WITH_BOTH_ID)
+ .setTitleResId(REFERENCE_RES_ID)
+ .setSummaryResId(REFERENCE_RES_ID)
+ .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
+ .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE)
+ .addSafetySource(SafetySourceTest.STATIC_BAREBONE)
+ .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE)
+ .build()
+ AnyTester.assertThatRepresentationsAreEqual(COLLAPSIBLE_WITH_BOTH, collapsibleWithBothCopy)
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentTypes_areNotEqual() {
+ AnyTester.assertThatRepresentationsAreNotEqual(COLLAPSIBLE_WITH_BOTH, RIGID)
+ AnyTester.assertThatRepresentationsAreNotEqual(RIGID, HIDDEN)
+ AnyTester.assertThatRepresentationsAreNotEqual(HIDDEN, COLLAPSIBLE_WITH_BOTH)
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentIds_areNotEqual() {
+ val collapsibleWithBothAlt = SafetySourcesGroup.Builder()
+ .setId("other")
+ .setTitleResId(REFERENCE_RES_ID)
+ .setSummaryResId(REFERENCE_RES_ID)
+ .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
+ .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE)
+ .build()
+ AnyTester.assertThatRepresentationsAreNotEqual(
+ COLLAPSIBLE_WITH_BOTH,
+ collapsibleWithBothAlt
+ )
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentTitleResIds_areNotEqual() {
+ val collapsibleWithBothAlt = SafetySourcesGroup.Builder()
+ .setId(COLLAPSIBLE_WITH_BOTH_ID)
+ .setTitleResId(-1)
+ .setSummaryResId(REFERENCE_RES_ID)
+ .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
+ .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE)
+ .build()
+ AnyTester.assertThatRepresentationsAreNotEqual(
+ COLLAPSIBLE_WITH_BOTH,
+ collapsibleWithBothAlt
+ )
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentSummaryResIds_areNotEqual() {
+ val collapsibleWithBothAlt = SafetySourcesGroup.Builder()
+ .setId(COLLAPSIBLE_WITH_BOTH_ID)
+ .setTitleResId(REFERENCE_RES_ID)
+ .setSummaryResId(-1)
+ .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
+ .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE)
+ .build()
+ AnyTester.assertThatRepresentationsAreNotEqual(
+ COLLAPSIBLE_WITH_BOTH,
+ collapsibleWithBothAlt
+ )
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentInitialDisplayStates_areNotEqual() {
+ val collapsibleWithBothAlt = SafetySourcesGroup.Builder()
+ .setId(COLLAPSIBLE_WITH_BOTH_ID)
+ .setTitleResId(REFERENCE_RES_ID)
+ .setSummaryResId(REFERENCE_RES_ID)
+ .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE)
+ .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE)
+ .build()
+ AnyTester.assertThatRepresentationsAreNotEqual(
+ COLLAPSIBLE_WITH_BOTH,
+ collapsibleWithBothAlt
+ )
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentSafetySources_areNotEqual() {
+ val collapsibleWithBothAlt = SafetySourcesGroup.Builder()
+ .setId(COLLAPSIBLE_WITH_BOTH_ID)
+ .setTitleResId(REFERENCE_RES_ID)
+ .setSummaryResId(REFERENCE_RES_ID)
+ .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
+ .addSafetySource(SafetySourceTest.STATIC_BAREBONE)
+ .build()
+ AnyTester.assertThatRepresentationsAreNotEqual(
+ COLLAPSIBLE_WITH_BOTH,
+ collapsibleWithBothAlt
+ )
+ }
+
+ companion object {
+ private const val REFERENCE_RES_ID = 9999
+
+ private const val COLLAPSIBLE_WITH_SUMMARY_ID = "collapsible_with_summary"
+ private const val COLLAPSIBLE_WITH_ICON_ID = "collapsible_with_icon"
+ private const val COLLAPSIBLE_WITH_BOTH_ID = "collapsible_with_both"
+ private const val RIGID_ID = "rigid"
+ private const val HIDDEN_ID = "hidden"
+
+ private val COLLAPSIBLE_WITH_SUMMARY = SafetySourcesGroup.Builder()
+ .setId(COLLAPSIBLE_WITH_SUMMARY_ID)
+ .setTitleResId(REFERENCE_RES_ID)
+ .setSummaryResId(REFERENCE_RES_ID)
+ .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE)
+ .build()
+
+ private val COLLAPSIBLE_WITH_ICON = SafetySourcesGroup.Builder()
+ .setId(COLLAPSIBLE_WITH_ICON_ID)
+ .setTitleResId(REFERENCE_RES_ID)
+ .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
+ .addSafetySource(SafetySourceTest.STATIC_BAREBONE)
+ .build()
+
+ private val COLLAPSIBLE_WITH_BOTH = SafetySourcesGroup.Builder()
+ .setId(COLLAPSIBLE_WITH_BOTH_ID)
+ .setTitleResId(REFERENCE_RES_ID)
+ .setSummaryResId(REFERENCE_RES_ID)
+ .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
+ .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE)
+ .addSafetySource(SafetySourceTest.STATIC_BAREBONE)
+ .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE)
+ .build()
+
+ internal val RIGID = SafetySourcesGroup.Builder()
+ .setId(RIGID_ID)
+ .setTitleResId(REFERENCE_RES_ID)
+ .addSafetySource(SafetySourceTest.STATIC_BAREBONE)
+ .build()
+
+ internal val HIDDEN = SafetySourcesGroup.Builder()
+ .setId(HIDDEN_ID)
+ .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE)
+ .build()
+ }
+}
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterActivityTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterActivityTest.kt
new file mode 100644
index 000000000..0adf45852
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterActivityTest.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.content.Intent
+import android.content.Intent.ACTION_SAFETY_CENTER
+import android.os.Build.VERSION_CODES.TIRAMISU
+import android.support.test.uiautomator.By
+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.waitFindObject
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = TIRAMISU, codeName = "Tiramisu")
+class SafetyCenterActivityTest {
+ private val context: Context = getApplicationContext()
+
+ @Test
+ fun launchActivity_showsSecurityAndPrivacyTitle() {
+ context.startActivity(
+ Intent(ACTION_SAFETY_CENTER).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ )
+
+ // CollapsingToolbar title can't be found by text, so using description instead.
+ waitFindObject(By.desc("Security & Privacy"))
+ }
+}
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterApisWithShellPermissions.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterApisWithShellPermissions.kt
new file mode 100644
index 000000000..c3c7e7266
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterApisWithShellPermissions.kt
@@ -0,0 +1,109 @@
+/*
+ * 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.cts
+
+import android.Manifest.permission.MANAGE_SAFETY_CENTER
+import android.Manifest.permission.SEND_SAFETY_CENTER_UPDATE
+import android.safetycenter.SafetyCenterData
+import android.safetycenter.SafetyCenterManager
+import android.safetycenter.SafetyCenterManager.OnSafetyCenterDataChangedListener
+import android.safetycenter.SafetySourceData
+import android.safetycenter.SafetyEvent
+import android.safetycenter.config.SafetyCenterConfig
+import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity
+import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import java.util.concurrent.Executor
+
+/**
+ * Call {@link SafetyCenterManager#setSafetySourceData} adopting Shell's
+ * {@link SEND_SAFETY_CENTER_UPDATE} permission.
+ */
+fun SafetyCenterManager.setSafetySourceDataWithPermission(
+ safetySourceId: String,
+ safetySourceData: SafetySourceData,
+ safetyEvent: SafetyEvent
+) =
+ runWithShellPermissionIdentity({
+ setSafetySourceData(safetySourceId, safetySourceData, safetyEvent)
+ }, SEND_SAFETY_CENTER_UPDATE)
+
+/**
+ * Call {@link SafetyCenterManager#getSafetySourceData} adopting Shell's
+ * {@link SEND_SAFETY_CENTER_UPDATE} permission.
+ */
+fun SafetyCenterManager.getSafetySourceDataWithPermission(id: String): SafetySourceData? =
+ callWithShellPermissionIdentity({
+ getSafetySourceData(id)
+ }, SEND_SAFETY_CENTER_UPDATE)
+
+/**
+ * Call {@link SafetyCenterManager#isSafetyCenterEnabled} adopting Shell's
+ * {@link SEND_SAFETY_CENTER_UPDATE} permission.
+ */
+fun SafetyCenterManager.isSafetyCenterEnabledWithPermission(): Boolean =
+ callWithShellPermissionIdentity({
+ isSafetyCenterEnabled
+ }, SEND_SAFETY_CENTER_UPDATE)
+
+/**
+ * Call {@link SafetyCenterManager#refreshSafetySources} adopting Shell's
+ * {@link MANAGE_SAFETY_CENTER} permission (required for
+ * {@link SafetyCenterManager#refreshSafetySources}).
+ */
+fun SafetyCenterManager.refreshSafetySourcesWithPermission(refreshReason: Int) =
+ runWithShellPermissionIdentity({
+ refreshSafetySources(refreshReason)
+ }, MANAGE_SAFETY_CENTER)
+
+fun SafetyCenterManager.getSafetyCenterDataWithPermission(): SafetyCenterData =
+ runWithShellPermissionIdentity(::getSafetyCenterData, MANAGE_SAFETY_CENTER)
+
+fun SafetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission(
+ executor: Executor,
+ listener: OnSafetyCenterDataChangedListener
+) =
+ runWithShellPermissionIdentity({
+ addOnSafetyCenterDataChangedListener(executor, listener)
+ }, MANAGE_SAFETY_CENTER)
+
+fun SafetyCenterManager.removeOnSafetyCenterDataChangedListenerWithPermission(
+ listener: OnSafetyCenterDataChangedListener
+) =
+ runWithShellPermissionIdentity({
+ removeOnSafetyCenterDataChangedListener(listener)
+ }, MANAGE_SAFETY_CENTER)
+
+/**
+ * Call {@link SafetyCenterManager#clearAllSafetySourceData} adopting Shell's
+ * {@link MANAGE_SAFETY_CENTER} permission.
+ */
+fun SafetyCenterManager.clearAllSafetySourceDataWithPermission() =
+ runWithShellPermissionIdentity({
+ clearAllSafetySourceData()
+ }, MANAGE_SAFETY_CENTER)
+
+fun SafetyCenterManager.setSafetyCenterConfigOverrideWithPermission(
+ safetyCenterConfig: SafetyCenterConfig
+) =
+ runWithShellPermissionIdentity({
+ setSafetyCenterConfigOverride(safetyCenterConfig)
+ }, MANAGE_SAFETY_CENTER)
+
+fun SafetyCenterManager.clearSafetyCenterConfigOverrideWithPermission() =
+ runWithShellPermissionIdentity({
+ clearSafetyCenterConfigOverride()
+ }, MANAGE_SAFETY_CENTER)
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterDataTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterDataTest.kt
new file mode 100644
index 000000000..5d3df8995
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterDataTest.kt
@@ -0,0 +1,251 @@
+/*
+ * 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.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.os.Build.VERSION_CODES.TIRAMISU
+import android.os.Parcel
+import android.safetycenter.SafetyCenterData
+import android.safetycenter.SafetyCenterEntry
+import android.safetycenter.SafetyCenterEntryGroup
+import android.safetycenter.SafetyCenterEntryOrGroup
+import android.safetycenter.SafetyCenterIssue
+import android.safetycenter.SafetyCenterStaticEntry
+import android.safetycenter.SafetyCenterStaticEntryGroup
+import android.safetycenter.SafetyCenterStatus
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = TIRAMISU, codeName = "Tiramisu")
+class SafetyCenterDataTest {
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ private val pendingIntent = PendingIntent.getActivity(
+ context,
+ /* requestCode= */ 0,
+ Intent("Fake Data"),
+ PendingIntent.FLAG_IMMUTABLE)
+
+ val status1 = SafetyCenterStatus.Builder()
+ .setTitle("This is my title")
+ .setSummary("This is my summary")
+ .setSeverityLevel(SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_RECOMMENDATION)
+ .build()
+ val status2 = SafetyCenterStatus.Builder()
+ .setTitle("This is also my title")
+ .setSummary("This is also my summary")
+ .setSeverityLevel(SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK)
+ .build()
+
+ val issue1 = SafetyCenterIssue.Builder("iSsUe_iD_oNe")
+ .setTitle("An issue title")
+ .setSummary("An issue summary")
+ .setSeverityLevel(SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_OK)
+ .build()
+ val issue2 = SafetyCenterIssue.Builder("iSsUe_iD_tWo")
+ .setTitle("Another issue title")
+ .setSummary("Another issue summary")
+ .setSeverityLevel(SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_RECOMMENDATION)
+ .build()
+
+ val entry1 = SafetyCenterEntry.Builder("eNtRy_iD_OnE")
+ .setTitle("An entry title")
+ .setPendingIntent(pendingIntent)
+ .setSeverityLevel(SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_OK)
+ .build()
+ val entry2 = SafetyCenterEntry.Builder("eNtRy_iD_TwO")
+ .setTitle("Another entry title")
+ .setPendingIntent(pendingIntent)
+ .setSeverityLevel(SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_RECOMMENDATION)
+ .build()
+
+ val entryGroup1 = SafetyCenterEntryGroup.Builder("eNtRy_gRoUp_iD")
+ .setTitle("An entry group title")
+ .setSeverityLevel(SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_RECOMMENDATION)
+ .setEntries(listOf(entry2))
+ .build()
+
+ val entryOrGroup1 = SafetyCenterEntryOrGroup(entry1)
+ val entryOrGroup2 = SafetyCenterEntryOrGroup(entryGroup1)
+
+ val staticEntry1 = SafetyCenterStaticEntry(
+ "A static entry title",
+ "A static entry summary",
+ pendingIntent)
+ val staticEntry2 = SafetyCenterStaticEntry(
+ "Another static entry title",
+ "Another static entry summary",
+ pendingIntent)
+
+ val staticEntryGroup1 = SafetyCenterStaticEntryGroup(
+ "A static entry group title", listOf(staticEntry1))
+ val staticEntryGroup2 = SafetyCenterStaticEntryGroup(
+ "Another static entry group title", listOf(staticEntry2))
+
+ val data1 = SafetyCenterData(
+ status1, listOf(issue1), listOf(entryOrGroup1), listOf(staticEntryGroup1))
+ val data2 = SafetyCenterData(
+ status2, listOf(issue2), listOf(entryOrGroup2), listOf(staticEntryGroup2))
+
+ @Test
+ fun getStatus_returnsStatus() {
+ assertThat(data1.status).isEqualTo(status1)
+ assertThat(data2.status).isEqualTo(status2)
+ }
+
+ @Test
+ fun getIssues_returnsIssues() {
+ assertThat(data1.issues).containsExactly(issue1)
+ assertThat(data2.issues).containsExactly(issue2)
+ }
+
+ @Test
+ fun getIssues_mutationsAreNotReflected() {
+ val mutatedIssues = data1.issues
+ mutatedIssues.add(issue2)
+
+ assertThat(mutatedIssues).containsExactly(issue1, issue2)
+ assertThat(data1.issues).doesNotContain(issue2)
+ }
+
+ @Test
+ fun getEntriesOrGroups_returnsEntriesOrGroups() {
+ assertThat(data1.entriesOrGroups).containsExactly(entryOrGroup1)
+ assertThat(data2.entriesOrGroups).containsExactly(entryOrGroup2)
+ }
+
+ @Test
+ fun getEntriesOrGroups_mutationsAreNotReflected() {
+ val mutatedEntriesOrGroups = data1.entriesOrGroups
+ mutatedEntriesOrGroups.add(entryOrGroup2)
+
+ assertThat(mutatedEntriesOrGroups).containsExactly(entryOrGroup1, entryOrGroup2)
+ assertThat(data1.entriesOrGroups).doesNotContain(entryOrGroup2)
+ }
+
+ @Test
+ fun getStaticEntryGroups_returnsStaticEntryGroups() {
+ assertThat(data1.staticEntryGroups).containsExactly(staticEntryGroup1)
+ assertThat(data2.staticEntryGroups).containsExactly(staticEntryGroup2)
+ }
+
+ @Test
+ fun getStaticEntryGroups_mutationsAreNotReflected() {
+ val mutatedStaticEntryGroups = data1.staticEntryGroups
+ mutatedStaticEntryGroups.add(staticEntryGroup2)
+
+ assertThat(mutatedStaticEntryGroups).containsExactly(staticEntryGroup1, staticEntryGroup2)
+ assertThat(data1.staticEntryGroups).doesNotContain(staticEntryGroup2)
+ }
+
+ @Test
+ fun describeContents_returns0() {
+ assertThat(data1.describeContents()).isEqualTo(0)
+ }
+
+ @Test
+ fun createFromParcel_withWriteToParcel_returnsEquivalentObject() {
+ val parcel: Parcel = Parcel.obtain()
+
+ data1.writeToParcel(parcel, 0 /* flags */)
+ parcel.setDataPosition(0)
+ val fromParcel = SafetyCenterData.CREATOR.createFromParcel(parcel)
+ parcel.recycle()
+
+ assertThat(fromParcel).isEqualTo(data1)
+ }
+
+ @Test
+ fun equals_hashCode_toString_equalByReference_areEqual() {
+ assertThat(data1).isEqualTo(data1)
+ assertThat(data1.hashCode()).isEqualTo(data1.hashCode())
+ assertThat(data1.toString()).isEqualTo(data1.toString())
+ }
+
+ @Test
+ fun equals_hashCode_toString_equalByValue_areEqual() {
+ val data = SafetyCenterData(
+ status1, listOf(issue1), listOf(entryOrGroup1), listOf(staticEntryGroup1))
+ val equivalentData = SafetyCenterData(
+ status1, listOf(issue1), listOf(entryOrGroup1), listOf(staticEntryGroup1))
+
+ assertThat(data).isEqualTo(equivalentData)
+ assertThat(data.hashCode()).isEqualTo(equivalentData.hashCode())
+ assertThat(data.toString()).isEqualTo(equivalentData.toString())
+ }
+
+ @Test
+ fun equals_hashCode_toString_withEmptyLists_equalByValue_areEqual() {
+ val data = SafetyCenterData(status1, listOf(), listOf(), listOf())
+ val equivalentData = SafetyCenterData(status1, listOf(), listOf(), listOf())
+
+ assertThat(data).isEqualTo(equivalentData)
+ assertThat(data.hashCode()).isEqualTo(equivalentData.hashCode())
+ assertThat(data.toString()).isEqualTo(equivalentData.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentStatuses_areNotEqual() {
+ val data = SafetyCenterData(
+ status1, listOf(issue1), listOf(entryOrGroup1), listOf(staticEntryGroup1))
+ val differentData = SafetyCenterData(
+ status2, listOf(issue1), listOf(entryOrGroup1), listOf(staticEntryGroup1))
+
+ assertThat(data).isNotEqualTo(differentData)
+ assertThat(data.toString()).isNotEqualTo(differentData.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentIssues_areNotEqual() {
+ val data = SafetyCenterData(
+ status1, listOf(issue1), listOf(entryOrGroup1), listOf(staticEntryGroup1))
+ val differentData = SafetyCenterData(
+ status1, listOf(issue2), listOf(entryOrGroup1), listOf(staticEntryGroup1))
+
+ assertThat(data).isNotEqualTo(differentData)
+ assertThat(data.toString()).isNotEqualTo(differentData.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentEntriesOrGroups_areNotEqual() {
+ val data = SafetyCenterData(
+ status1, listOf(issue1), listOf(entryOrGroup1), listOf(staticEntryGroup1))
+ val differentData = SafetyCenterData(
+ status1, listOf(issue1), listOf(entryOrGroup2), listOf(staticEntryGroup1))
+
+ assertThat(data).isNotEqualTo(differentData)
+ assertThat(data.toString()).isNotEqualTo(differentData.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentStaticEntryGroups_areNotEqual() {
+ val data = SafetyCenterData(
+ status1, listOf(issue1), listOf(entryOrGroup1), listOf(staticEntryGroup1))
+ val differentData = SafetyCenterData(
+ status1, listOf(issue1), listOf(entryOrGroup1), listOf(staticEntryGroup2))
+
+ assertThat(data).isNotEqualTo(differentData)
+ assertThat(data.toString()).isNotEqualTo(differentData.toString())
+ }
+} \ No newline at end of file
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryGroupTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryGroupTest.kt
new file mode 100644
index 000000000..4140e40d8
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryGroupTest.kt
@@ -0,0 +1,258 @@
+/*
+ * 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.cts
+
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.os.Build.VERSION_CODES.TIRAMISU
+import android.os.Parcel
+import android.safetycenter.SafetyCenterEntry
+import android.safetycenter.SafetyCenterEntryGroup
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = TIRAMISU, codeName = "Tiramisu")
+class SafetyCenterEntryGroupTest {
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ private val pendingIntent = PendingIntent.getActivity(
+ context,
+ /* requestCode= */ 0,
+ Intent("Fake Data"),
+ PendingIntent.FLAG_IMMUTABLE)
+
+ val entry1 = SafetyCenterEntry.Builder("eNtRy_iD_OnE")
+ .setTitle("An entry title")
+ .setPendingIntent(pendingIntent)
+ .setSeverityLevel(SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_OK)
+ .build()
+ val entry2 = SafetyCenterEntry.Builder("eNtRy_iD_TwO")
+ .setTitle("Another entry title")
+ .setPendingIntent(pendingIntent)
+ .setSeverityLevel(SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_RECOMMENDATION)
+ .build()
+
+ val groupId1 = "gRoUp_iD_oNe"
+ val groupId2 = "gRoUp_iD_tWo"
+
+ val entryGroup1 = SafetyCenterEntryGroup.Builder(groupId1)
+ .setTitle("A group title")
+ .setSummary("A group summary")
+ .setSeverityLevel(SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_OK)
+ .setEntries(listOf(entry1))
+ .build()
+ val entryGroup2 = SafetyCenterEntryGroup.Builder(groupId2)
+ .setTitle("Another group title")
+ .setSummary("Another group summary")
+ .setSeverityLevel(SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_RECOMMENDATION)
+ .setEntries(listOf(entry2))
+ .build()
+
+ @Test
+ fun getId_returnsId() {
+ assertThat(entryGroup1.id).isEqualTo(groupId1)
+ assertThat(entryGroup2.id).isEqualTo(groupId2)
+ }
+
+ @Test
+ fun getTitle_returnsTitle() {
+ assertThat(SafetyCenterEntryGroup.Builder(entryGroup1).setTitle("title one").build().title)
+ .isEqualTo("title one")
+ assertThat(SafetyCenterEntryGroup.Builder(entryGroup1).setTitle("title two").build().title)
+ .isEqualTo("title two")
+ }
+
+ @Test
+ fun getSummary_returnsSummary() {
+ assertThat(SafetyCenterEntryGroup.Builder(entryGroup1).setSummary("one").build().summary)
+ .isEqualTo("one")
+ assertThat(SafetyCenterEntryGroup.Builder(entryGroup1).setSummary("two").build().summary)
+ .isEqualTo("two")
+ assertThat(SafetyCenterEntryGroup.Builder(entryGroup1).setSummary(null).build().summary)
+ .isNull()
+ }
+
+ @Test
+ fun getSeverityLevel_returnsSeverityLevel() {
+ assertThat(SafetyCenterEntryGroup.Builder(entryGroup1)
+ .setSeverityLevel(SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_RECOMMENDATION)
+ .build()
+ .severityLevel)
+ .isEqualTo(SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_RECOMMENDATION)
+ assertThat(SafetyCenterEntryGroup.Builder(entryGroup1)
+ .setSeverityLevel(SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_NONE)
+ .build()
+ .severityLevel)
+ .isEqualTo(SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_NONE)
+ }
+
+ @Test
+ fun getSeverityNoneIconType_returnsSeverityNoneIconType() {
+ assertThat(entryGroup1.severityNoneIconType)
+ .isEqualTo(SafetyCenterEntry.SEVERITY_NONE_ICON_TYPE_NO_ICON)
+ assertThat(SafetyCenterEntryGroup.Builder(entryGroup1)
+ .setSeverityNoneIconType(SafetyCenterEntry.SEVERITY_NONE_ICON_TYPE_PRIVACY)
+ .build()
+ .severityNoneIconType)
+ .isEqualTo(SafetyCenterEntry.SEVERITY_NONE_ICON_TYPE_PRIVACY)
+ }
+
+ @Test
+ fun getEntries_returnsEntries() {
+ assertThat(entryGroup1.entries).containsExactly(entry1)
+ assertThat(entryGroup2.entries).containsExactly(entry2)
+ }
+
+ @Test
+ fun getEntries_mutationsAreNotReflected() {
+ val mutatedEntries = entryGroup1.entries
+ mutatedEntries.add(entry2)
+
+ assertThat(mutatedEntries).containsExactly(entry1, entry2)
+ assertThat(entryGroup1.entries).doesNotContain(entry2)
+ }
+
+ @Test
+ fun describeContents_returns0() {
+ assertThat(entryGroup1.describeContents()).isEqualTo(0)
+ }
+
+ @Test
+ fun createFromParcel_withWriteToParcel_returnsEquivalentObject() {
+ val parcel = Parcel.obtain()
+
+ entryGroup1.writeToParcel(parcel, /* flags= */ 0)
+ parcel.setDataPosition(0)
+
+ val fromParcel = SafetyCenterEntryGroup.CREATOR.createFromParcel(parcel)
+ parcel.recycle()
+
+ assertThat(fromParcel).isEqualTo(entryGroup1)
+ }
+
+ @Test
+ fun equals_hashCode_toString_equalByReference_areEqual() {
+ assertThat(entryGroup1).isEqualTo(entryGroup1)
+ assertThat(entryGroup1.hashCode()).isEqualTo(entryGroup1.hashCode())
+ assertThat(entryGroup1.toString()).isEqualTo(entryGroup1.toString())
+ }
+
+ @Test
+ fun equals_hashCode_toString_equalByValue_areEqual() {
+ val entry = SafetyCenterEntryGroup.Builder(groupId1)
+ .setTitle("A group title")
+ .setSummary("A group summary")
+ .setSeverityLevel(SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_OK)
+ .setEntries(listOf(entry1))
+ .build()
+ val equivalentEntry = SafetyCenterEntryGroup.Builder(groupId1)
+ .setTitle("A group title")
+ .setSummary("A group summary")
+ .setSeverityLevel(SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_OK)
+ .setEntries(listOf(entry1))
+ .build()
+
+ assertThat(entry).isEqualTo(equivalentEntry)
+ assertThat(entry.hashCode()).isEqualTo(equivalentEntry.hashCode())
+ assertThat(entry.toString()).isEqualTo(equivalentEntry.toString())
+ }
+
+ @Test
+ fun equals_hashCode_toString_fromCopyBuilder_areEqual() {
+ val equivalentToEntryGroup1 = SafetyCenterEntryGroup.Builder(entryGroup1).build()
+
+ assertThat(equivalentToEntryGroup1).isEqualTo(entryGroup1)
+ assertThat(equivalentToEntryGroup1.hashCode()).isEqualTo(entryGroup1.hashCode())
+ assertThat(equivalentToEntryGroup1.toString()).isEqualTo(entryGroup1.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentIds_areNotEqual() {
+ val differentFromEntryGroup1 = SafetyCenterEntryGroup.Builder(entryGroup1)
+ .setId("different!")
+ .build()
+
+ assertThat(differentFromEntryGroup1).isNotEqualTo(entryGroup1)
+ assertThat(differentFromEntryGroup1.toString()).isNotEqualTo(entryGroup1.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentTitles_areNotEqual() {
+ val differentFromEntryGroup1 = SafetyCenterEntryGroup.Builder(entryGroup1)
+ .setTitle("different!")
+ .build()
+
+ assertThat(differentFromEntryGroup1).isNotEqualTo(entryGroup1)
+ assertThat(differentFromEntryGroup1.toString()).isNotEqualTo(entryGroup1.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentSummaries_areNotEqual() {
+ val differentFromEntryGroup1 = SafetyCenterEntryGroup.Builder(entryGroup1)
+ .setSummary("different!")
+ .build()
+
+ assertThat(differentFromEntryGroup1).isNotEqualTo(entryGroup1)
+ assertThat(differentFromEntryGroup1.toString()).isNotEqualTo(entryGroup1.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentSeverityLevels_areNotEqual() {
+ val differentFromEntryGroup1 = SafetyCenterEntryGroup.Builder(entryGroup1)
+ .setSeverityLevel(SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_UNKNOWN)
+ .build()
+
+ assertThat(differentFromEntryGroup1).isNotEqualTo(entryGroup1)
+ assertThat(differentFromEntryGroup1.toString()).isNotEqualTo(entryGroup1.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentSeverityNoneIconTypes_areNotEqual() {
+ val differentFromEntryGroup1 = SafetyCenterEntryGroup.Builder(entryGroup1)
+ .setSeverityNoneIconType(SafetyCenterEntry.SEVERITY_NONE_ICON_TYPE_PRIVACY)
+ .build()
+
+ assertThat(differentFromEntryGroup1).isNotEqualTo(entryGroup1)
+ assertThat(differentFromEntryGroup1.toString()).isNotEqualTo(entryGroup1.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentEntries_areNotEqual() {
+ val differentFromEntryGroup1 = SafetyCenterEntryGroup.Builder(entryGroup1)
+ .setEntries(listOf(entry2))
+ .build()
+
+ assertThat(differentFromEntryGroup1).isNotEqualTo(entryGroup1)
+ assertThat(differentFromEntryGroup1.toString()).isNotEqualTo(entryGroup1.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentEntries_emptyList_areNotEqual() {
+ val differentFromEntryGroup1 = SafetyCenterEntryGroup.Builder(entryGroup1)
+ .setEntries(listOf())
+ .build()
+
+ assertThat(differentFromEntryGroup1).isNotEqualTo(entryGroup1)
+ assertThat(differentFromEntryGroup1.toString()).isNotEqualTo(entryGroup1.toString())
+ }
+} \ No newline at end of file
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryOrGroupTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryOrGroupTest.kt
new file mode 100644
index 000000000..193643bcf
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryOrGroupTest.kt
@@ -0,0 +1,172 @@
+/*
+ * 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.cts
+
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.os.Build.VERSION_CODES.TIRAMISU
+import android.os.Parcel
+import android.safetycenter.SafetyCenterEntry
+import android.safetycenter.SafetyCenterEntryGroup
+import android.safetycenter.SafetyCenterEntryOrGroup
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = TIRAMISU, codeName = "Tiramisu")
+class SafetyCenterEntryOrGroupTest {
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ private val pendingIntent = PendingIntent.getActivity(
+ context,
+ /* requestCode= */ 0,
+ Intent("Fake Data"),
+ PendingIntent.FLAG_IMMUTABLE)
+
+ val entry1 = SafetyCenterEntry.Builder("eNtRy_iD_OnE")
+ .setTitle("An entry title")
+ .setPendingIntent(pendingIntent)
+ .setSeverityLevel(SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_OK)
+ .build()
+ val entry2 = SafetyCenterEntry.Builder("eNtRy_iD_TwO")
+ .setTitle("Another entry title")
+ .setPendingIntent(pendingIntent)
+ .setSeverityLevel(SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_RECOMMENDATION)
+ .build()
+
+ val entryGroup1 = SafetyCenterEntryGroup.Builder("gRoUp_iD_oNe")
+ .setTitle("A group title")
+ .setSummary("A group summary")
+ .setSeverityLevel(SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_OK)
+ .setEntries(listOf(entry1))
+ .build()
+ val entryGroup2 = SafetyCenterEntryGroup.Builder("gRoUp_iD_tWo")
+ .setTitle("Another group title")
+ .setSummary("Another group summary")
+ .setSeverityLevel(SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_RECOMMENDATION)
+ .setEntries(listOf(entry2))
+ .build()
+
+ val entryOrGroupWithEntry = SafetyCenterEntryOrGroup(entry1)
+ val entryOrGroupWithGroup = SafetyCenterEntryOrGroup(entryGroup1)
+
+ @Test
+ fun getEntry_returnsEntry() {
+ assertThat(entryOrGroupWithEntry.entry).isEqualTo(entry1)
+ }
+
+ @Test
+ fun getEntry_returnsEntry_whenNull() {
+ assertThat(entryOrGroupWithGroup.entry).isNull()
+ }
+
+ @Test
+ fun getEntryGroup_returnsEntryGroup() {
+ assertThat(entryOrGroupWithGroup.entryGroup).isEqualTo(entryGroup1)
+ }
+
+ @Test
+ fun getEntryGroup_returnsEntryGroup_whenNull() {
+ assertThat(entryOrGroupWithEntry.entryGroup).isNull()
+ }
+
+ @Test
+ fun describeContents_returns0() {
+ assertThat(entryOrGroupWithEntry.describeContents()).isEqualTo(0)
+ }
+
+ @Test
+ fun createFromParcel_withWriteToParcel_withEntry_returnsEquivalentObject() {
+ runCreateFromParcel_withWriteToParcel_withGroup_returnsEquivalentObjectTest(
+ entryOrGroupWithEntry)
+ }
+
+ @Test
+ fun createFromParcel_withWriteToParcel_withGroup_returnsEquivalentObject() {
+ runCreateFromParcel_withWriteToParcel_withGroup_returnsEquivalentObjectTest(
+ entryOrGroupWithGroup)
+ }
+
+ fun runCreateFromParcel_withWriteToParcel_withGroup_returnsEquivalentObjectTest(
+ entryOrGroup: SafetyCenterEntryOrGroup
+ ) {
+ val parcel: Parcel = Parcel.obtain()
+
+ entryOrGroup.writeToParcel(parcel, 0 /* flags */)
+ parcel.setDataPosition(0)
+ val fromParcel = SafetyCenterEntryOrGroup.CREATOR.createFromParcel(parcel)
+ parcel.recycle()
+
+ assertThat(fromParcel).isEqualTo(entryOrGroup)
+ }
+
+ @Test
+ fun equals_hashCode_toString_whenEqualByReference_areEqual() {
+ assertThat(entryOrGroupWithEntry).isEqualTo(entryOrGroupWithEntry)
+ assertThat(entryOrGroupWithEntry.hashCode()).isEqualTo(entryOrGroupWithEntry.hashCode())
+ assertThat(entryOrGroupWithEntry.toString()).isEqualTo(entryOrGroupWithEntry.toString())
+ }
+
+ @Test
+ fun equals_hashCode_toString_whenEqualByValue_withEntry_areEqual() {
+ val entryOrGroup = SafetyCenterEntryOrGroup(entry1)
+ val equivalentEntryOrGroup = SafetyCenterEntryOrGroup(entry1)
+
+ assertThat(entryOrGroup).isEqualTo(equivalentEntryOrGroup)
+ assertThat(entryOrGroup.hashCode()).isEqualTo(equivalentEntryOrGroup.hashCode())
+ assertThat(entryOrGroup.toString()).isEqualTo(equivalentEntryOrGroup.toString())
+ }
+
+ @Test
+ fun equals_hashCode_toString_whenEqualByValue_withGroup_areEqual() {
+ val entryOrGroup = SafetyCenterEntryOrGroup(entryGroup1)
+ val equivalentEntryOrGroup = SafetyCenterEntryOrGroup(entryGroup1)
+
+ assertThat(entryOrGroup).isEqualTo(equivalentEntryOrGroup)
+ assertThat(entryOrGroup.hashCode()).isEqualTo(equivalentEntryOrGroup.hashCode())
+ assertThat(entryOrGroup.toString()).isEqualTo(equivalentEntryOrGroup.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentEntryAndGroup_areNotEqual() {
+ assertThat(entryOrGroupWithEntry).isNotEqualTo(entryOrGroupWithGroup)
+ assertThat(entryOrGroupWithEntry.toString()).isNotEqualTo(entryOrGroupWithGroup.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentEntries_areNotEqual() {
+ val entryOrGroup = SafetyCenterEntryOrGroup(entry1)
+ val differentEntryOrGroup = SafetyCenterEntryOrGroup(entry2)
+
+ assertThat(entryOrGroup).isNotEqualTo(differentEntryOrGroup)
+ assertThat(entryOrGroup.toString()).isNotEqualTo(differentEntryOrGroup.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentGroups_areNotEqual() {
+ val entryOrGroup = SafetyCenterEntryOrGroup(entryGroup1)
+ val differentEntryOrGroup = SafetyCenterEntryOrGroup(entryGroup2)
+
+ assertThat(entryOrGroup).isNotEqualTo(differentEntryOrGroup)
+ assertThat(entryOrGroup.toString()).isNotEqualTo(differentEntryOrGroup.toString())
+ }
+} \ No newline at end of file
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryTest.kt
new file mode 100644
index 000000000..94fea0017
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryTest.kt
@@ -0,0 +1,375 @@
+/*
+ * 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.cts
+
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.os.Build.VERSION_CODES.TIRAMISU
+import android.os.Parcel
+import android.safetycenter.SafetyCenterEntry
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = TIRAMISU, codeName = "Tiramisu")
+class SafetyCenterEntryTest {
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ private val pendingIntent1 = PendingIntent.getActivity(
+ context,
+ /* requestCode= */ 0,
+ Intent("Fake Data"),
+ PendingIntent.FLAG_IMMUTABLE)
+ private val pendingIntent2 = PendingIntent.getActivity(
+ context,
+ /* requestCode= */ 0,
+ Intent("Fake Different Data"),
+ PendingIntent.FLAG_IMMUTABLE)
+
+ private val iconAction1 = SafetyCenterEntry.IconAction(
+ SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_GEAR, pendingIntent1)
+ private val iconAction2 = SafetyCenterEntry.IconAction(
+ SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_INFO, pendingIntent2)
+
+ private val entry1 = SafetyCenterEntry.Builder("eNtRy_iD")
+ .setTitle("a title")
+ .setSummary("a summary")
+ .setPendingIntent(pendingIntent1)
+ .setSeverityLevel(SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_UNKNOWN)
+ .setIconAction(iconAction1)
+ .build()
+
+ @Test
+ fun getId_returnsId() {
+ assertThat(SafetyCenterEntry.Builder(entry1).setId("id_one").build().id)
+ .isEqualTo("id_one")
+ assertThat(SafetyCenterEntry.Builder(entry1).setId("id_two").build().id)
+ .isEqualTo("id_two")
+ }
+
+ @Test
+ fun getTitle_returnsTitle() {
+ assertThat(SafetyCenterEntry.Builder(entry1).setTitle("a title").build().title)
+ .isEqualTo("a title")
+ assertThat(SafetyCenterEntry.Builder(entry1).setTitle("another title").build().title)
+ .isEqualTo("another title")
+ }
+
+ @Test
+ fun getSummary_returnsSummary() {
+ assertThat(SafetyCenterEntry.Builder(entry1).setSummary("a summary").build().summary)
+ .isEqualTo("a summary")
+ assertThat(SafetyCenterEntry.Builder(entry1).setSummary("another summary").build().summary)
+ .isEqualTo("another summary")
+ assertThat(SafetyCenterEntry.Builder(entry1).setSummary(null).build().summary)
+ .isNull()
+ }
+
+ @Test
+ fun getSeverityLevel_returnsSeverityLevel() {
+ assertThat(SafetyCenterEntry.Builder(entry1)
+ .setSeverityLevel(SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_RECOMMENDATION)
+ .build()
+ .severityLevel)
+ .isEqualTo(SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_RECOMMENDATION)
+ assertThat(SafetyCenterEntry.Builder(entry1)
+ .setSeverityLevel(SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_OK)
+ .build()
+ .severityLevel)
+ .isEqualTo(SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_OK)
+ }
+
+ @Test
+ fun getSeverityNoneIconType_returnsSeverityNoneIconType() {
+ assertThat(entry1.severityNoneIconType).isEqualTo(
+ SafetyCenterEntry.SEVERITY_NONE_ICON_TYPE_NO_ICON)
+ assertThat(SafetyCenterEntry.Builder(entry1)
+ .setSeverityNoneIconType(SafetyCenterEntry.SEVERITY_NONE_ICON_TYPE_PRIVACY)
+ .build()
+ .severityNoneIconType)
+ .isEqualTo(SafetyCenterEntry.SEVERITY_NONE_ICON_TYPE_PRIVACY)
+ }
+
+ @Test
+ fun isEnabled_returnsIsEnabled() {
+ assertThat(SafetyCenterEntry.Builder(entry1).setEnabled(true).build().isEnabled)
+ .isTrue()
+ assertThat(SafetyCenterEntry.Builder(entry1).setEnabled(false).build().isEnabled)
+ .isFalse()
+ }
+
+ @Test
+ fun isEnabled_defaultTrue() {
+ assertThat(SafetyCenterEntry.Builder("eNtRy_iD")
+ .setTitle("a title")
+ .setPendingIntent(pendingIntent1)
+ .setSeverityLevel(SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_UNKNOWN)
+ .build()
+ .isEnabled)
+ .isTrue()
+ }
+
+ @Test
+ fun getPendingIntent_returnsPendingIntent() {
+ assertThat(SafetyCenterEntry.Builder(entry1)
+ .setPendingIntent(pendingIntent1)
+ .build()
+ .pendingIntent)
+ .isEqualTo(pendingIntent1)
+ assertThat(SafetyCenterEntry.Builder(entry1)
+ .setPendingIntent(pendingIntent2)
+ .build()
+ .pendingIntent)
+ .isEqualTo(pendingIntent2)
+ }
+
+ @Test
+ fun getIconAction_returnsIconAction() {
+ assertThat(SafetyCenterEntry.Builder(entry1).setIconAction(iconAction1).build().iconAction)
+ .isEqualTo(iconAction1)
+ assertThat(SafetyCenterEntry.Builder(entry1).setIconAction(iconAction2).build().iconAction)
+ .isEqualTo(iconAction2)
+ assertThat(SafetyCenterEntry.Builder(entry1).setIconAction(null).build().iconAction)
+ .isNull()
+ }
+
+ @Test
+ fun describeContents_returns0() {
+ assertThat(entry1.describeContents()).isEqualTo(0)
+ }
+
+ @Test
+ fun createFromParcel_withWriteToParcel_returnsEquivalentObject() {
+ val parcel = Parcel.obtain()
+
+ entry1.writeToParcel(parcel, /* flags= */ 0)
+ parcel.setDataPosition(0)
+
+ val fromParcel = SafetyCenterEntry.CREATOR.createFromParcel(parcel)
+ parcel.recycle()
+
+ assertThat(fromParcel).isEqualTo(entry1)
+ }
+
+ @Test
+ fun equals_hashCode_toString_equalByReference_areEqual() {
+ assertThat(entry1).isEqualTo(entry1)
+ assertThat(entry1.hashCode()).isEqualTo(entry1.hashCode())
+ assertThat(entry1.toString()).isEqualTo(entry1.toString())
+ }
+
+ @Test
+ fun equals_hashCode_toString_equalByValue_areEqual() {
+ val entry = SafetyCenterEntry.Builder("id")
+ .setTitle("a title")
+ .setSummary("a summary")
+ .setSeverityLevel(SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_OK)
+ .setSeverityNoneIconType(SafetyCenterEntry.SEVERITY_NONE_ICON_TYPE_PRIVACY)
+ .setPendingIntent(pendingIntent1)
+ .setIconAction(SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_INFO, pendingIntent2)
+ .build()
+ val equivalentEntry = SafetyCenterEntry.Builder("id")
+ .setTitle("a title")
+ .setSummary("a summary")
+ .setSeverityLevel(SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_OK)
+ .setSeverityNoneIconType(SafetyCenterEntry.SEVERITY_NONE_ICON_TYPE_PRIVACY)
+ .setPendingIntent(pendingIntent1)
+ .setIconAction(SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_INFO, pendingIntent2)
+ .build()
+
+ assertThat(entry).isEqualTo(equivalentEntry)
+ assertThat(entry.hashCode()).isEqualTo(equivalentEntry.hashCode())
+ assertThat(entry.toString()).isEqualTo(equivalentEntry.toString())
+ }
+
+ @Test
+ fun equals_hashCode_toString_fromCopyBuilder_areEqual() {
+ val copyOfEntry1 = SafetyCenterEntry.Builder(entry1).build()
+
+ assertThat(copyOfEntry1).isEqualTo(entry1)
+ assertThat(copyOfEntry1.hashCode()).isEqualTo(entry1.hashCode())
+ assertThat(copyOfEntry1.toString()).isEqualTo(entry1.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentIds_areNotEqual() {
+ val differentFromEntry1 = SafetyCenterEntry.Builder(entry1)
+ .setId("a different id")
+ .build()
+
+ assertThat(differentFromEntry1).isNotEqualTo(entry1)
+ assertThat(differentFromEntry1.toString()).isNotEqualTo(entry1.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentTitles_areNotEqual() {
+ val differentFromEntry1 = SafetyCenterEntry.Builder(entry1)
+ .setTitle("a different title")
+ .build()
+
+ assertThat(differentFromEntry1).isNotEqualTo(entry1)
+ assertThat(differentFromEntry1.toString()).isNotEqualTo(entry1.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentSummaries_areNotEqual() {
+ val differentFromEntry1 = SafetyCenterEntry.Builder(entry1)
+ .setSummary("a different summary")
+ .build()
+
+ assertThat(differentFromEntry1).isNotEqualTo(entry1)
+ assertThat(differentFromEntry1.toString()).isNotEqualTo(entry1.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentSeverityLevels_areNotEqual() {
+ val differentFromEntry1 = SafetyCenterEntry.Builder(entry1)
+ .setSeverityLevel(SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_CRITICAL_WARNING)
+ .build()
+
+ assertThat(differentFromEntry1).isNotEqualTo(entry1)
+ assertThat(differentFromEntry1.toString()).isNotEqualTo(entry1.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentSeverityNoneIconTypes_areNotEqual() {
+ val differentFromEntry1 = SafetyCenterEntry.Builder(entry1)
+ .setSeverityNoneIconType(SafetyCenterEntry.SEVERITY_NONE_ICON_TYPE_PRIVACY)
+ .build()
+
+ assertThat(differentFromEntry1).isNotEqualTo(entry1)
+ assertThat(differentFromEntry1.toString()).isNotEqualTo(entry1.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentEnabledValues_areNotEqual() {
+ val differentFromEntry1 = SafetyCenterEntry.Builder(entry1)
+ .setEnabled(false)
+ .build()
+
+ assertThat(differentFromEntry1).isNotEqualTo(entry1)
+ assertThat(differentFromEntry1.toString()).isNotEqualTo(entry1.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentPendingIntents_areNotEqual() {
+ val differentFromEntry1 = SafetyCenterEntry.Builder(entry1)
+ .setPendingIntent(pendingIntent2)
+ .build()
+
+ assertThat(differentFromEntry1).isNotEqualTo(entry1)
+ assertThat(differentFromEntry1.toString()).isNotEqualTo(entry1.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentIconActions_areNotEqual() {
+ val differentFromEntry1 = SafetyCenterEntry.Builder(entry1)
+ .setIconAction(iconAction2)
+ .build()
+
+ assertThat(differentFromEntry1).isNotEqualTo(entry1)
+ assertThat(differentFromEntry1.toString()).isNotEqualTo(entry1.toString())
+ }
+
+ @Test
+ fun iconAction_getType_returnsType() {
+ assertThat(SafetyCenterEntry.IconAction(
+ SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_GEAR, pendingIntent1)
+ .type)
+ .isEqualTo(SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_GEAR)
+ assertThat(SafetyCenterEntry.IconAction(
+ SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_INFO, pendingIntent1)
+ .type)
+ .isEqualTo(SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_INFO)
+ }
+
+ @Test
+ fun iconAction_getPendingIntent_returnsPendingIntent() {
+ assertThat(SafetyCenterEntry.IconAction(
+ SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_GEAR, pendingIntent1)
+ .pendingIntent)
+ .isEqualTo(pendingIntent1)
+ assertThat(SafetyCenterEntry.IconAction(
+ SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_GEAR, pendingIntent2)
+ .pendingIntent)
+ .isEqualTo(pendingIntent2)
+ }
+
+ @Test
+ fun iconAction_describeContents_returns0() {
+ assertThat(iconAction1.describeContents()).isEqualTo(0)
+ }
+
+ @Test
+ fun iconAction_createFromParcel_withWriteToParcel_returnsEquivalentObject() {
+ val parcel = Parcel.obtain()
+
+ iconAction1.writeToParcel(parcel, /* flags= */ 0)
+ parcel.setDataPosition(0)
+
+ val fromParcel = SafetyCenterEntry.IconAction.CREATOR.createFromParcel(parcel)
+ parcel.recycle()
+
+ assertThat(fromParcel).isEqualTo(iconAction1)
+ }
+
+ @Test
+ fun iconAction_equals_hashCode_toString_equalByReference_areEqual() {
+ assertThat(iconAction1).isEqualTo(iconAction1)
+ assertThat(iconAction1.hashCode()).isEqualTo(iconAction1.hashCode())
+ assertThat(iconAction1.toString()).isEqualTo(iconAction1.toString())
+ }
+
+ @Test
+ fun iconAction_equals_hashCode_toString_equalByValue_areEqual() {
+ val iconAction = SafetyCenterEntry.IconAction(
+ SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_GEAR, pendingIntent1)
+ val equivalentIconAction = SafetyCenterEntry.IconAction(
+ SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_GEAR, pendingIntent1)
+
+ assertThat(iconAction).isEqualTo(equivalentIconAction)
+ assertThat(iconAction.hashCode()).isEqualTo(equivalentIconAction.hashCode())
+ assertThat(iconAction.toString()).isEqualTo(equivalentIconAction.toString())
+ }
+
+ @Test
+ fun iconAction_equals_toString_differentTypes_areNotEqual() {
+ val iconAction = SafetyCenterEntry.IconAction(
+ SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_GEAR, pendingIntent1)
+ val differentIconAction = SafetyCenterEntry.IconAction(
+ SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_INFO, pendingIntent1)
+
+ assertThat(iconAction).isNotEqualTo(differentIconAction)
+ assertThat(iconAction.toString()).isNotEqualTo(differentIconAction.toString())
+ }
+
+ @Test
+ fun intentAction_equals_toString_differentPendingIntents_areNotEqual() {
+ val iconAction = SafetyCenterEntry.IconAction(
+ SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_GEAR, pendingIntent1)
+ val differentIconAction = SafetyCenterEntry.IconAction(
+ SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_GEAR, pendingIntent2)
+
+ assertThat(iconAction).isNotEqualTo(differentIconAction)
+ assertThat(iconAction.toString()).isNotEqualTo(differentIconAction.toString())
+ }
+} \ No newline at end of file
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterErrorTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterErrorTest.kt
new file mode 100644
index 000000000..06fd6d5cd
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterErrorTest.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.cts
+
+import android.os.Parcel
+import android.safetycenter.SafetyCenterError
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SafetyCenterErrorTest {
+
+ val error1 = SafetyCenterError("an error message")
+ val error2 = SafetyCenterError("another error message")
+
+ @Test
+ fun getErrorMessage_returnsErrorMessage() {
+ assertThat(error1.errorMessage).isEqualTo("an error message")
+ assertThat(error2.errorMessage).isEqualTo("another error message")
+ }
+
+ @Test
+ fun describeContents_returns0() {
+ assertThat(error1.describeContents()).isEqualTo(0)
+ }
+
+ @Test
+ fun createFromParcel_withWriteToParcel_returnsEquivalentObject() {
+ val parcel = Parcel.obtain()
+
+ error1.writeToParcel(parcel, /* flags= */ 0)
+ parcel.setDataPosition(0)
+
+ val fromParcel = SafetyCenterError.CREATOR.createFromParcel(parcel)
+ parcel.recycle()
+
+ assertThat(fromParcel).isEqualTo(error1)
+ }
+
+ @Test
+ fun equals_hashCode_toString_equalByReference_areEqual() {
+ assertThat(error1).isEqualTo(error1)
+ assertThat(error1.hashCode()).isEqualTo(error1.hashCode())
+ assertThat(error1.toString()).isEqualTo(error1.toString())
+ }
+
+ @Test
+ fun equals_hashCode_toString_equalByValue_areEqual() {
+ val error = SafetyCenterError("an error message")
+ val equivalentError = SafetyCenterError("an error message")
+
+ assertThat(error).isEqualTo(equivalentError)
+ assertThat(error.hashCode()).isEqualTo(equivalentError.hashCode())
+ assertThat(error.toString()).isEqualTo(equivalentError.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentErrorMessages_areNotEqual() {
+ val error = SafetyCenterError("an error message")
+ val differentError = SafetyCenterError("a different error message")
+
+ assertThat(error).isNotEqualTo(differentError)
+ assertThat(error.toString()).isNotEqualTo(differentError.toString())
+ }
+} \ No newline at end of file
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterIssueTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterIssueTest.kt
new file mode 100644
index 000000000..a8428049c
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterIssueTest.kt
@@ -0,0 +1,503 @@
+/*
+ * 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.cts
+
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.os.Build.VERSION_CODES.TIRAMISU
+import android.os.Parcel
+import android.safetycenter.SafetyCenterIssue
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = TIRAMISU, codeName = "Tiramisu")
+class SafetyCenterIssueTest {
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ private val pendingIntent1 = PendingIntent.getActivity(
+ context,
+ /* requestCode= */ 0,
+ Intent("Fake Data"),
+ PendingIntent.FLAG_IMMUTABLE)
+ private val pendingIntent2 = PendingIntent.getActivity(
+ context,
+ /* requestCode= */ 0,
+ Intent("Fake Different Data"),
+ PendingIntent.FLAG_IMMUTABLE)
+
+ val action1 = SafetyCenterIssue.Action.Builder("action_id_1")
+ .setLabel("an action")
+ .setPendingIntent(pendingIntent1)
+ .setResolving(true)
+ .setInFlight(true)
+ .setSuccessMessage("a success message")
+ .build()
+ val action2 = SafetyCenterIssue.Action.Builder("action_id_2")
+ .setLabel("another action")
+ .setPendingIntent(pendingIntent2)
+ .setResolving(false)
+ .setInFlight(false)
+ .build()
+
+ val issue1 = SafetyCenterIssue.Builder("issue_id")
+ .setTitle("Everything's good")
+ .setSubtitle("In the neighborhood")
+ .setSummary("Please acknowledge this")
+ .setSeverityLevel(SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_OK)
+ .setDismissible(true)
+ .setShouldConfirmDismissal(true)
+ .setActions(listOf(action1))
+ .build()
+
+ val issueWithRequiredFieldsOnly = SafetyCenterIssue.Builder("issue_id")
+ .setTitle("Everything's good")
+ .setSummary("Please acknowledge this")
+ .setSeverityLevel(SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_OK)
+ .build()
+
+ @Test
+ fun getId_returnsId() {
+ assertThat(SafetyCenterIssue.Builder(issue1).setId("an id").build().id)
+ .isEqualTo("an id")
+ assertThat(SafetyCenterIssue.Builder(issue1).setId("another id").build().id)
+ .isEqualTo("another id")
+ }
+
+ @Test
+ fun getTitle_returnsTitle() {
+ assertThat(SafetyCenterIssue.Builder(issue1).setTitle("a title").build().title)
+ .isEqualTo("a title")
+ assertThat(SafetyCenterIssue.Builder(issue1).setTitle("another title").build().title)
+ .isEqualTo("another title")
+ }
+
+ @Test
+ fun getSubtitle_returnsSubtitle() {
+ assertThat(SafetyCenterIssue.Builder(issue1).setSubtitle("a subtitle").build().subtitle)
+ .isEqualTo("a subtitle")
+ assertThat(
+ SafetyCenterIssue.Builder(issue1).setSubtitle("another subtitle").build().subtitle)
+ .isEqualTo("another subtitle")
+ }
+
+ @Test
+ fun getSummary_returnsSummary() {
+ assertThat(SafetyCenterIssue.Builder(issue1).setSummary("a summary").build().summary)
+ .isEqualTo("a summary")
+ assertThat(SafetyCenterIssue.Builder(issue1).setSummary("another summary").build().summary)
+ .isEqualTo("another summary")
+ }
+
+ @Test
+ fun getSeverityLevel_returnsSeverityLevel() {
+ assertThat(SafetyCenterIssue.Builder(issue1)
+ .setSeverityLevel(SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_RECOMMENDATION)
+ .build()
+ .severityLevel)
+ .isEqualTo(SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_RECOMMENDATION)
+ assertThat(SafetyCenterIssue.Builder(issue1)
+ .setSeverityLevel(SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_CRITICAL_WARNING)
+ .build()
+ .severityLevel)
+ .isEqualTo(SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_CRITICAL_WARNING)
+ }
+
+ @Test
+ fun isDismissible_returnsIsDismissible() {
+ assertThat(SafetyCenterIssue.Builder(issue1).setDismissible(true).build().isDismissible)
+ .isTrue()
+ assertThat(SafetyCenterIssue.Builder(issue1).setDismissible(false).build().isDismissible)
+ .isFalse()
+ }
+
+ @Test
+ fun isDismissible_defaultsToTrue() {
+ assertThat(issueWithRequiredFieldsOnly.isDismissible).isTrue()
+ }
+
+ @Test
+ fun shouldConfirmDismissal_returnsShouldConfirmDismissal() {
+ assertThat(SafetyCenterIssue.Builder(issue1)
+ .setShouldConfirmDismissal(true)
+ .build()
+ .shouldConfirmDismissal())
+ .isTrue()
+ assertThat(SafetyCenterIssue.Builder(issue1)
+ .setShouldConfirmDismissal(false)
+ .build()
+ .shouldConfirmDismissal())
+ .isFalse()
+ }
+
+ @Test
+ fun shouldConfirmDismissal_defaultsToTrue() {
+ assertThat(issueWithRequiredFieldsOnly.shouldConfirmDismissal()).isTrue()
+ }
+
+ @Test
+ fun getActions_returnsActions() {
+ assertThat(SafetyCenterIssue.Builder(issue1)
+ .setActions(listOf(action1, action2))
+ .build().actions)
+ .containsExactly(action1, action2)
+ assertThat(SafetyCenterIssue.Builder(issue1).setActions(listOf(action2)).build().actions)
+ .containsExactly(action2)
+ assertThat(SafetyCenterIssue.Builder(issue1).setActions(listOf()).build().actions)
+ .isEmpty()
+ }
+
+ @Test
+ fun getActions_mutationsAreNotReflected() {
+ val mutatedActions = issue1.actions
+ mutatedActions.add(action2)
+
+ assertThat(mutatedActions).containsExactly(action1, action2)
+ assertThat(issue1.actions).doesNotContain(action2)
+ }
+
+ @Test
+ fun describeContents_returns0() {
+ assertThat(issue1.describeContents()).isEqualTo(0)
+ }
+
+ @Test
+ fun createFromParcel_withWriteToParcel_returnsEquivalentObject() {
+ val parcel = Parcel.obtain()
+
+ issue1.writeToParcel(parcel, /* flags= */ 0)
+ parcel.setDataPosition(0)
+
+ val fromParcel = SafetyCenterIssue.CREATOR.createFromParcel(parcel)
+ parcel.recycle()
+
+ assertThat(fromParcel).isEqualTo(issue1)
+ }
+
+ @Test
+ fun equals_hashCode_toString_equalByReference_areEqual() {
+ assertThat(issue1).isEqualTo(issue1)
+ assertThat(issue1.hashCode()).isEqualTo(issue1.hashCode())
+ assertThat(issue1.toString()).isEqualTo(issue1.toString())
+ }
+
+ @Test
+ fun equals_hashCode_toString_equalByValue_areEqual() {
+ val issue = SafetyCenterIssue.Builder("an id")
+ .setTitle("a title")
+ .setSubtitle("In the neighborhood")
+ .setSummary("Please acknowledge this")
+ .setSeverityLevel(SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_OK)
+ .setActions(listOf(action1))
+ .build()
+ val equivalentIssue = SafetyCenterIssue.Builder("an id")
+ .setTitle("a title")
+ .setSubtitle("In the neighborhood")
+ .setSummary("Please acknowledge this")
+ .setSeverityLevel(SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_OK)
+ .setActions(listOf(action1))
+ .build()
+
+ assertThat(issue).isEqualTo(equivalentIssue)
+ assertThat(issue.hashCode()).isEqualTo(equivalentIssue.hashCode())
+ assertThat(issue.toString()).isEqualTo(equivalentIssue.toString())
+ }
+
+ @Test
+ fun equals_hashCode_toString_fromCopyBuilder() {
+ val copyOfIssue1 = SafetyCenterIssue.Builder(issue1).build()
+
+ assertThat(copyOfIssue1).isEqualTo(issue1)
+ assertThat(copyOfIssue1.hashCode()).isEqualTo(issue1.hashCode())
+ assertThat(copyOfIssue1.toString()).isEqualTo(issue1.toString())
+ }
+
+ @Test
+ fun equals_toString_differentIds_areNotEqual() {
+ val differentFromIssue1 = SafetyCenterIssue.Builder(issue1)
+ .setId("a different id")
+ .build()
+
+ assertThat(differentFromIssue1).isNotEqualTo(issue1)
+ assertThat(differentFromIssue1.toString()).isNotEqualTo(issue1.toString())
+ }
+
+ @Test
+ fun equals_toString_differentTitles_areNotEqual() {
+ val differentFromIssue1 = SafetyCenterIssue.Builder(issue1)
+ .setTitle("a different title")
+ .build()
+
+ assertThat(differentFromIssue1).isNotEqualTo(issue1)
+ assertThat(differentFromIssue1.toString()).isNotEqualTo(issue1.toString())
+ }
+
+ @Test
+ fun equals_toString_differentSubtitles_areNotEqual() {
+ val differentFromIssue1 = SafetyCenterIssue.Builder(issue1)
+ .setSubtitle("a different subtitle")
+ .build()
+
+ assertThat(differentFromIssue1).isNotEqualTo(issue1)
+ assertThat(differentFromIssue1.toString()).isNotEqualTo(issue1.toString())
+ }
+
+ @Test
+ fun equals_toString_differentSummaries_areNotEqual() {
+ val differentFromIssue1 = SafetyCenterIssue.Builder(issue1)
+ .setSummary("a different summary")
+ .build()
+
+ assertThat(differentFromIssue1).isNotEqualTo(issue1)
+ assertThat(differentFromIssue1.toString()).isNotEqualTo(issue1.toString())
+ }
+
+ @Test
+ fun equals_toString_differentSeverityLevels_areNotEqual() {
+ val differentFromIssue1 = SafetyCenterIssue.Builder(issue1)
+ .setSeverityLevel(SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_CRITICAL_WARNING)
+ .build()
+
+ assertThat(differentFromIssue1).isNotEqualTo(issue1)
+ assertThat(differentFromIssue1.toString()).isNotEqualTo(issue1.toString())
+ }
+
+ @Test
+ fun equals_toString_differentIsDismissibleValues_areNotEqual() {
+ val differentFromIssue1 = SafetyCenterIssue.Builder(issue1)
+ .setDismissible(false)
+ .build()
+
+ assertThat(differentFromIssue1).isNotEqualTo(issue1)
+ assertThat(differentFromIssue1.toString()).isNotEqualTo(issue1.toString())
+ }
+
+ @Test
+ fun equals_toString_differentShouldConfirmDismissalValues_areNotEqual() {
+ val differentFromIssue1 = SafetyCenterIssue.Builder(issue1)
+ .setShouldConfirmDismissal(false)
+ .build()
+
+ assertThat(differentFromIssue1).isNotEqualTo(issue1)
+ assertThat(differentFromIssue1.toString()).isNotEqualTo(issue1.toString())
+ }
+
+ @Test
+ fun equals_toString_differentActions_areNotEqual() {
+ val differentFromIssue1 = SafetyCenterIssue.Builder(issue1)
+ .setActions(listOf(action2))
+ .build()
+
+ assertThat(differentFromIssue1).isNotEqualTo(issue1)
+ assertThat(differentFromIssue1.toString()).isNotEqualTo(issue1.toString())
+ }
+
+ @Test
+ fun action_getId_returnsId() {
+ assertThat(action1.id).isEqualTo("action_id_1")
+ assertThat(action2.id).isEqualTo("action_id_2")
+ }
+
+ @Test
+ fun action_getLabel_returnsLabel() {
+ assertThat(action1.label).isEqualTo("an action")
+ assertThat(action2.label).isEqualTo("another action")
+ }
+
+ @Test
+ fun action_getPendingIntent_returnsPendingIntent() {
+ assertThat(action1.pendingIntent).isEqualTo(pendingIntent1)
+ assertThat(action2.pendingIntent).isEqualTo(pendingIntent2)
+ }
+
+ @Test
+ fun action_isResolving_returnsIsResolving() {
+ assertThat(action1.isResolving).isTrue()
+ assertThat(action2.isResolving).isFalse()
+ }
+
+ @Test
+ fun action_isInFlight_returnsIsInFlight() {
+ assertThat(action1.isInFlight).isTrue()
+ assertThat(action2.isInFlight).isFalse()
+ }
+
+ @Test
+ fun action_getSuccessMessage_returnsSuccessMessage() {
+ assertThat(action1.successMessage).isEqualTo("a success message")
+ assertThat(action2.successMessage).isNull()
+ }
+
+ @Test
+ fun action_describeContents_returns0() {
+ assertThat(action1.describeContents()).isEqualTo(0)
+ }
+
+ @Test
+ fun action_createFromParcel_withWriteToParcel_returnsEquivalentObject() {
+ val parcel = Parcel.obtain()
+
+ action1.writeToParcel(parcel, /* flags= */ 0)
+
+ parcel.setDataPosition(0)
+ val fromParcel = SafetyCenterIssue.Action.CREATOR.createFromParcel(parcel)
+ parcel.recycle()
+
+ assertThat(fromParcel).isEqualTo(action1)
+ }
+
+ @Test
+ fun action_equals_hashCode_toString_equalByReference_areEqual() {
+ assertThat(action1).isEqualTo(action1)
+ assertThat(action1.hashCode()).isEqualTo(action1.hashCode())
+ assertThat(action1.toString()).isEqualTo(action1.toString())
+ }
+
+ @Test
+ fun action_equals_hashCode_toString_equalByValue_areEqual() {
+ val action = SafetyCenterIssue.Action.Builder("an_id")
+ .setLabel("a label")
+ .setPendingIntent(pendingIntent1)
+ .setResolving(true)
+ .setInFlight(true)
+ .setSuccessMessage("a success message")
+ .build()
+ val equivalentAction = SafetyCenterIssue.Action.Builder("an_id")
+ .setLabel("a label")
+ .setPendingIntent(pendingIntent1)
+ .setResolving(true)
+ .setInFlight(true)
+ .setSuccessMessage("a success message")
+ .build()
+
+ assertThat(action).isEqualTo(equivalentAction)
+ assertThat(action.toString()).isEqualTo(equivalentAction.toString())
+ }
+
+ @Test
+ fun action_equals_toString_differentIds_areNotEqual() {
+ val action = SafetyCenterIssue.Action.Builder("an_id")
+ .setLabel("a label")
+ .setPendingIntent(pendingIntent1)
+ .setSuccessMessage("a success message")
+ .build()
+ val differentAction = SafetyCenterIssue.Action.Builder("a_different_id")
+ .setLabel("a label")
+ .setPendingIntent(pendingIntent1)
+ .setSuccessMessage("a success message")
+ .build()
+
+ assertThat(action).isNotEqualTo(differentAction)
+ assertThat(action.toString()).isNotEqualTo(differentAction.toString())
+ }
+
+ @Test
+ fun action_equals_toString_differentLabels_areNotEqual() {
+ val action = SafetyCenterIssue.Action.Builder("an_id")
+ .setLabel("a label")
+ .setPendingIntent(pendingIntent1)
+ .setSuccessMessage("a success message")
+ .build()
+ val differentAction = SafetyCenterIssue.Action.Builder("an_id")
+ .setLabel("a different label")
+ .setPendingIntent(pendingIntent1)
+ .setSuccessMessage("a success message")
+ .build()
+
+ assertThat(action).isNotEqualTo(differentAction)
+ assertThat(action.toString()).isNotEqualTo(differentAction.toString())
+ }
+
+ @Test
+ fun action_equals_toString_differentPendingIntents_areNotEqual() {
+ val action = SafetyCenterIssue.Action.Builder("an_id")
+ .setLabel("a label")
+ .setPendingIntent(pendingIntent1)
+ .setSuccessMessage("a success message")
+ .build()
+ val differentAction = SafetyCenterIssue.Action.Builder("an_id")
+ .setLabel("a label")
+ .setPendingIntent(pendingIntent2)
+ .setSuccessMessage("a success message")
+ .build()
+
+ assertThat(action).isNotEqualTo(differentAction)
+ assertThat(action.toString()).isNotEqualTo(differentAction.toString())
+ }
+
+ @Test
+ fun action_equals_toString_differentResovlingValues_areNotEqual() {
+ val action = SafetyCenterIssue.Action.Builder("an_id")
+ .setLabel("a label")
+ .setPendingIntent(pendingIntent1)
+ .setResolving(true)
+ .setSuccessMessage("a success message")
+ .build()
+ val differentAction = SafetyCenterIssue.Action.Builder("an_id")
+ .setLabel("a label")
+ .setPendingIntent(pendingIntent1)
+ .setResolving(false)
+ .setSuccessMessage("a success message")
+ .build()
+
+ assertThat(action).isNotEqualTo(differentAction)
+ assertThat(action.toString()).isNotEqualTo(differentAction.toString())
+ }
+
+ @Test
+ fun action_equals_toString_differentInFlightValues_areNotEqual() {
+ val action = SafetyCenterIssue.Action.Builder("an_id")
+ .setLabel("a label")
+ .setPendingIntent(pendingIntent1)
+ .setResolving(true)
+ .setInFlight(true)
+ .setSuccessMessage("a success message")
+ .build()
+ val differentAction = SafetyCenterIssue.Action.Builder("an_id")
+ .setLabel("a label")
+ .setPendingIntent(pendingIntent1)
+ .setResolving(true)
+ .setInFlight(false)
+ .setSuccessMessage("a success message")
+ .build()
+
+ assertThat(action).isNotEqualTo(differentAction)
+ assertThat(action.toString()).isNotEqualTo(differentAction.toString())
+ }
+
+ @Test
+ fun action_equals_toString_differentSuccessMessages_areNotEqual() {
+ val action = SafetyCenterIssue.Action.Builder("an_id")
+ .setLabel("a label")
+ .setPendingIntent(pendingIntent1)
+ .setSuccessMessage("a success message")
+ .build()
+ val differentAction = SafetyCenterIssue.Action.Builder("an_id")
+ .setLabel("a label")
+ .setPendingIntent(pendingIntent1)
+ .setSuccessMessage("a different success message")
+ .build()
+
+ assertThat(action).isNotEqualTo(differentAction)
+ assertThat(action.toString()).isNotEqualTo(differentAction.toString())
+ }
+} \ No newline at end of file
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt
new file mode 100644
index 000000000..bd58849cc
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt
@@ -0,0 +1,498 @@
+/*
+ * 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.Manifest.permission.MANAGE_SAFETY_CENTER
+import android.Manifest.permission.SEND_SAFETY_CENTER_UPDATE
+import android.app.PendingIntent
+import android.app.PendingIntent.FLAG_IMMUTABLE
+import android.content.Context
+import android.content.Intent
+import android.content.Intent.ACTION_SAFETY_CENTER
+import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
+import android.content.res.Resources
+import android.os.Build.VERSION_CODES.TIRAMISU
+import android.provider.DeviceConfig
+import android.safetycenter.SafetyCenterData
+import android.safetycenter.SafetyCenterManager
+import android.safetycenter.SafetyCenterManager.REFRESH_REASON_PAGE_OPEN
+import android.safetycenter.SafetyCenterManager.REFRESH_REASON_RESCAN_BUTTON_CLICK
+import android.safetycenter.SafetyCenterManager.OnSafetyCenterDataChangedListener
+import android.safetycenter.SafetySourceData
+import android.safetycenter.SafetySourceIssue
+import android.safetycenter.SafetyEvent
+import android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED
+import android.safetycenter.SafetySourceIssue.SEVERITY_LEVEL_CRITICAL_WARNING
+import android.safetycenter.SafetySourceStatus
+import android.safetycenter.SafetySourceStatus.STATUS_LEVEL_CRITICAL_WARNING
+import android.safetycenter.config.SafetyCenterConfig
+import android.safetycenter.config.SafetySource
+import android.safetycenter.config.SafetySourcesGroup
+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.SystemUtil.runWithShellPermissionIdentity
+import com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.MoreExecutors.directExecutor
+import kotlinx.coroutines.TimeoutCancellationException
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withTimeout
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.time.Duration
+import kotlin.test.assertFailsWith
+
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = TIRAMISU, codeName = "Tiramisu")
+class SafetyCenterManagerTest {
+ private val context: Context = getApplicationContext()
+ private val safetyCenterManager = context.getSystemService(SafetyCenterManager::class.java)!!
+ private val somePendingIntent = PendingIntent.getActivity(
+ context, 0 /* requestCode */,
+ Intent(ACTION_SAFETY_CENTER).addFlags(FLAG_ACTIVITY_NEW_TASK),
+ FLAG_IMMUTABLE
+ )
+ private val safetySourceDataOnPageOpen = SafetySourceData.Builder()
+ .setStatus(
+ SafetySourceStatus.Builder(
+ "safetySourceDataOnPageOpen status title",
+ "safetySourceDataOnPageOpen status summary",
+ SafetySourceStatus.STATUS_LEVEL_NONE,
+ somePendingIntent
+ )
+ .build()
+ )
+ .build()
+ private val safetySourceDataOnRescanClick = SafetySourceData.Builder()
+ .setStatus(
+ SafetySourceStatus.Builder(
+ "safetySourceDataOnRescanClick status title",
+ "safetySourceDataOnRescanClick status summary",
+ SafetySourceStatus.STATUS_LEVEL_RECOMMENDATION,
+ somePendingIntent
+ )
+ .build()
+ ).build()
+ private val listenerChannel = Channel<SafetyCenterData>()
+ // The lambda has to be wrapped to the right type because kotlin wraps lambdas in a new Java
+ // functional interface implementation each time they are referenced/cast to a Java interface:
+ // b/215569072.
+ private val listener = OnSafetyCenterDataChangedListener {
+ runBlockingWithTimeout {
+ listenerChannel.send(it)
+ }
+ }
+
+ @Before
+ @After
+ fun clearDataBetweenTest() {
+ safetyCenterManager.removeOnSafetyCenterDataChangedListenerWithPermission(listener)
+ safetyCenterManager.clearAllSafetySourceDataWithPermission()
+ SafetySourceBroadcastReceiver.reset()
+ }
+
+ @Before
+ fun setSafetyCenterConfigOverrideBeforeTest() {
+ safetyCenterManager.clearSafetyCenterConfigOverrideWithPermission()
+ // TODO(b/217944317): When the test API impl is finalized to override XML config, only
+ // override config in select test cases that require it. This is to ensure that we do have
+ // some test cases running with the XML config.
+ safetyCenterManager.setSafetyCenterConfigOverrideWithPermission(CTS_ONLY_CONFIG)
+ }
+
+ @After
+ fun clearSafetyCenterConfigOverrideAfterTest() {
+ safetyCenterManager.clearSafetyCenterConfigOverrideWithPermission()
+ }
+
+ @Test
+ fun getSafetySourceData_notSet_returnsNull() {
+ val safetySourceData =
+ safetyCenterManager.getSafetySourceDataWithPermission("some_unknown_id")
+
+ assertThat(safetySourceData).isNull()
+ }
+
+ @Test
+ fun setSafetySourceData_getSafetySourceDataReturnsNewValue() {
+ val safetySourceData = SafetySourceData.Builder().build()
+ safetyCenterManager.setSafetySourceDataWithPermission(
+ CTS_SOURCE_ID,
+ safetySourceData,
+ EVENT_SOURCE_STATE_CHANGED
+ )
+
+ val safetySourceDataResult =
+ safetyCenterManager.getSafetySourceDataWithPermission(CTS_SOURCE_ID)
+
+ assertThat(safetySourceDataResult).isEqualTo(safetySourceData)
+ }
+
+ @Test
+ fun setSafetySourceData_withSameId_replacesValue() {
+ val firstSafetySourceData = SafetySourceData.Builder().build()
+ safetyCenterManager.setSafetySourceDataWithPermission(
+ CTS_SOURCE_ID,
+ firstSafetySourceData,
+ EVENT_SOURCE_STATE_CHANGED
+ )
+
+ val secondSafetySourceData = SafetySourceData.Builder().setStatus(
+ SafetySourceStatus.Builder(
+ "Status title", "Summary of the status", STATUS_LEVEL_CRITICAL_WARNING,
+ somePendingIntent
+ ).build()
+ ).addIssue(
+ SafetySourceIssue.Builder(
+ "Issue id", "Issue title", "Summary of the issue",
+ SEVERITY_LEVEL_CRITICAL_WARNING,
+ "issue_type_id"
+ )
+ .addAction(
+ SafetySourceIssue.Action.Builder(
+ "action_id",
+ "Solve issue",
+ somePendingIntent
+ ).build()
+ ).build()
+ ).build()
+ safetyCenterManager.setSafetySourceDataWithPermission(
+ CTS_SOURCE_ID,
+ secondSafetySourceData,
+ EVENT_SOURCE_STATE_CHANGED
+ )
+
+ val safetySourceData = safetyCenterManager.getSafetySourceDataWithPermission(CTS_SOURCE_ID)
+
+ assertThat(safetySourceData).isEqualTo(secondSafetySourceData)
+ }
+
+ @Test
+ fun isSafetyCenterEnabled_whenConfigEnabled_andFlagEnabled_returnsTrue() {
+ if (!deviceSupportsSafetyCenter()) {
+ return
+ }
+
+ runWithShellPermissionIdentity {
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_SAFETY_CENTER_ENABLED,
+ /* value = */ true.toString(),
+ /* makeDefault = */ false
+ )
+ }
+
+ val isSafetyCenterEnabled = safetyCenterManager.isSafetyCenterEnabledWithPermission()
+
+ assertThat(isSafetyCenterEnabled).isTrue()
+ }
+
+ @Test
+ fun isSafetyCenterEnabled_whenConfigEnabled_andFlagDisabled_returnsFalse() {
+ if (!deviceSupportsSafetyCenter()) {
+ return
+ }
+
+ runWithShellPermissionIdentity {
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_SAFETY_CENTER_ENABLED,
+ /* value = */ false.toString(),
+ /* makeDefault = */ false
+ )
+ }
+
+ val isSafetyCenterEnabled = safetyCenterManager.isSafetyCenterEnabledWithPermission()
+
+ assertThat(isSafetyCenterEnabled).isFalse()
+ }
+
+ @Test
+ fun isSafetyCenterEnabled_whenConfigDisabled_andFlagEnabled_returnsFalse() {
+ if (deviceSupportsSafetyCenter()) {
+ return
+ }
+
+ runWithShellPermissionIdentity {
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_SAFETY_CENTER_ENABLED,
+ /* value = */ true.toString(),
+ /* makeDefault = */ false
+ )
+ }
+
+ val isSafetyCenterEnabled = safetyCenterManager.isSafetyCenterEnabledWithPermission()
+
+ assertThat(isSafetyCenterEnabled).isFalse()
+ }
+
+ @Test
+ fun isSafetyCenterEnabled_whenConfigDisabled_andFlagDisabled_returnsFalse() {
+ if (deviceSupportsSafetyCenter()) {
+ return
+ }
+
+ runWithShellPermissionIdentity {
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_SAFETY_CENTER_ENABLED,
+ /* value = */ false.toString(),
+ /* makeDefault = */ false
+ )
+ }
+
+ val isSafetyCenterEnabled = safetyCenterManager.isSafetyCenterEnabledWithPermission()
+
+ assertThat(isSafetyCenterEnabled).isFalse()
+ }
+
+ @Test
+ fun isSafetyCenterEnabled_whenAppDoesNotHoldPermission_methodThrows() {
+ assertFailsWith(SecurityException::class) {
+ safetyCenterManager.isSafetyCenterEnabled
+ }
+ }
+
+ @Test
+ fun refreshSafetySources_withoutManageSafetyCenterPermission_throwsSecurityException() {
+ assertFailsWith(SecurityException::class) {
+ safetyCenterManager.refreshSafetySources(REFRESH_REASON_RESCAN_BUTTON_CLICK)
+ }
+ }
+
+ @Test
+ fun refreshSafetySources_whenReceiverDoesNotHaveSendingPermission_sourceDoesNotSendData() {
+ SafetySourceBroadcastReceiver.safetySourceId = CTS_SOURCE_ID
+ SafetySourceBroadcastReceiver.safetySourceDataOnRescanClick = safetySourceDataOnRescanClick
+
+ safetyCenterManager.refreshSafetySourcesWithPermission(
+ REFRESH_REASON_RESCAN_BUTTON_CLICK
+ )
+
+ assertFailsWith(TimeoutCancellationException::class) {
+ SafetySourceBroadcastReceiver.waitTillOnReceiveComplete(TIMEOUT_SHORT)
+ }
+ val safetySourceData =
+ safetyCenterManager.getSafetySourceDataWithPermission(CTS_SOURCE_ID)
+ assertThat(safetySourceData).isNull()
+ }
+
+ @Test
+ fun refreshSafetySources_withRefreshReasonRescanButtonClick_sourceSendsRescanData() {
+ SafetySourceBroadcastReceiver.safetySourceId = CTS_SOURCE_ID
+ SafetySourceBroadcastReceiver.safetySourceDataOnRescanClick = safetySourceDataOnRescanClick
+
+ safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
+ REFRESH_REASON_RESCAN_BUTTON_CLICK
+ )
+
+ val safetySourceData = safetyCenterManager.getSafetySourceDataWithPermission(CTS_SOURCE_ID)
+ assertThat(safetySourceData).isEqualTo(safetySourceDataOnRescanClick)
+ }
+
+ @Test
+ fun refreshSafetySources_withRefreshReasonPageOpen_sourceSendsPageOpenData() {
+ SafetySourceBroadcastReceiver.safetySourceId = CTS_SOURCE_ID
+ SafetySourceBroadcastReceiver.safetySourceDataOnPageOpen = safetySourceDataOnPageOpen
+
+ safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
+ REFRESH_REASON_PAGE_OPEN
+ )
+
+ val safetySourceData = safetyCenterManager.getSafetySourceDataWithPermission(CTS_SOURCE_ID)
+ assertThat(safetySourceData).isEqualTo(safetySourceDataOnPageOpen)
+ }
+
+ @Test
+ fun refreshSafetySources_withInvalidRefreshSeason_throwsIllegalArgumentException() {
+ SafetySourceBroadcastReceiver.safetySourceId = CTS_SOURCE_ID
+ SafetySourceBroadcastReceiver.safetySourceDataOnPageOpen = safetySourceDataOnPageOpen
+ SafetySourceBroadcastReceiver.safetySourceDataOnRescanClick = safetySourceDataOnRescanClick
+
+ assertFailsWith(IllegalArgumentException::class) {
+ safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(500)
+ }
+ }
+
+ @Test
+ fun getSafetyCenterData_returnsSafetyCenterData() {
+ val safetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
+
+ // TODO(b/218830137): Assert on content.
+ assertThat(safetyCenterData).isNotNull()
+ }
+
+ @Test
+ fun getSafetyCenterData_whenAppDoesNotHoldPermission_methodThrows() {
+ assertFailsWith(SecurityException::class) {
+ safetyCenterManager.safetyCenterData
+ }
+ }
+
+ @Test
+ fun addOnSafetyCenterDataChangedListener_listenerCalledWithSafetyCenterData() {
+ safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission(
+ directExecutor(), listener
+ )
+ val safetyCenterDataFromListener = receiveListenerUpdate()
+
+ // TODO(b/218830137): Assert on content.
+ assertThat(safetyCenterDataFromListener).isNotNull()
+ }
+
+ @Test
+ fun addOnSafetyCenterDataChangedListener_listenerCalledOnSafetySourceData() {
+ safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission(
+ directExecutor(), listener
+ )
+ // Receive initial data.
+ receiveListenerUpdate()
+
+ val safetySourceData = SafetySourceData.Builder().build()
+ safetyCenterManager.setSafetySourceDataWithPermission(
+ CTS_SOURCE_ID,
+ safetySourceData,
+ EVENT_SOURCE_STATE_CHANGED
+ )
+ val safetyCenterDataFromListener = receiveListenerUpdate()
+
+ // TODO(b/218830137): Assert on content.
+ assertThat(safetyCenterDataFromListener).isNotNull()
+ }
+
+ @Test
+ fun removeOnSafetyCenterDataChangedListener_listenerNotCalledOnSafetySourceData() {
+ safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission(
+ directExecutor(), listener
+ )
+ // Receive initial data.
+ receiveListenerUpdate()
+
+ safetyCenterManager.removeOnSafetyCenterDataChangedListenerWithPermission(listener)
+ val safetySourceData = SafetySourceData.Builder().build()
+ safetyCenterManager.setSafetySourceDataWithPermission(
+ CTS_SOURCE_ID,
+ safetySourceData,
+ EVENT_SOURCE_STATE_CHANGED
+ )
+
+ assertFailsWith(TimeoutCancellationException::class) {
+ receiveListenerUpdate(TIMEOUT_SHORT)
+ }
+ }
+
+ @Test
+ fun addOnSafetyCenterDataChangedListener_whenAppDoesNotHoldPermission_methodThrows() {
+ assertFailsWith(SecurityException::class) {
+ safetyCenterManager.addOnSafetyCenterDataChangedListener(
+ directExecutor(), listener
+ )
+ }
+ }
+
+ @Test
+ fun removeOnSafetyCenterDataChangedListener_whenAppDoesNotHoldPermission_methodThrows() {
+ safetyCenterManager.addOnSafetyCenterDataChangedListenerWithPermission(
+ directExecutor(), listener
+ )
+
+ assertFailsWith(SecurityException::class) {
+ safetyCenterManager.removeOnSafetyCenterDataChangedListener(listener)
+ }
+ }
+
+ @Test
+ fun dismissSafetyIssue_whenAppDoesNotHoldPermission_methodThrows() {
+ assertFailsWith(SecurityException::class) {
+ safetyCenterManager.dismissSafetyIssue("bleh")
+ }
+ }
+
+ private fun deviceSupportsSafetyCenter() =
+ context.resources.getBoolean(
+ Resources.getSystem().getIdentifier(
+ "config_enableSafetyCenter",
+ "bool",
+ "android"
+ )
+ )
+
+ private fun SafetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
+ refreshReason: Int
+ ) {
+ try {
+ runWithShellPermissionIdentity({
+ refreshSafetySources(refreshReason)
+ SafetySourceBroadcastReceiver.waitTillOnReceiveComplete(TIMEOUT_LONG)
+ }, SEND_SAFETY_CENTER_UPDATE, MANAGE_SAFETY_CENTER)
+ } catch (e: RuntimeException) {
+ throw e.cause ?: e
+ }
+ }
+
+ private fun receiveListenerUpdate(timeout: Duration = TIMEOUT_LONG): SafetyCenterData =
+ runBlockingWithTimeout(timeout) {
+ listenerChannel.receive()
+ }
+
+ private fun <T> runBlockingWithTimeout(
+ timeout: Duration = TIMEOUT_LONG,
+ block: suspend () -> T
+ ) =
+ runBlocking {
+ withTimeout(timeout.toMillis()) {
+ block()
+ }
+ }
+
+ companion object {
+ /** Name of the flag that determines whether SafetyCenter is enabled. */
+ private const val PROPERTY_SAFETY_CENTER_ENABLED = "safety_center_is_enabled"
+ private const val CTS_PACKAGE_NAME = "android.safetycenter.cts"
+ private const val CTS_BROADCAST_RECEIVER_NAME =
+ "android.safetycenter.cts.SafetySourceBroadcastReceiver"
+ private val TIMEOUT_LONG: Duration = Duration.ofMillis(5000)
+ private val TIMEOUT_SHORT: Duration = Duration.ofMillis(1000)
+ private val EVENT_SOURCE_STATE_CHANGED =
+ SafetyEvent.Builder(SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build()
+ private const val CTS_SOURCE_ID = "cts_source_id"
+ // TODO(b/217944317): Consider moving the following to a file where they can be used by
+ // other tests.
+ private val CTS_SOURCE = SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+ .setId(CTS_SOURCE_ID)
+ .setPackageName(CTS_PACKAGE_NAME)
+ .setTitleResId(R.string.reference)
+ .setSummaryResId(R.string.reference)
+ .setIntentAction("SafetyCenterManagerTest.INTENT_ACTION")
+ .setBroadcastReceiverClassName(CTS_BROADCAST_RECEIVER_NAME)
+ .setProfile(SafetySource.PROFILE_PRIMARY)
+ .build()
+ private val CTS_SOURCE_GROUP = SafetySourcesGroup.Builder()
+ .setId("cts_source_group")
+ .setTitleResId(R.string.reference)
+ .setSummaryResId(R.string.reference)
+ .addSafetySource(CTS_SOURCE)
+ .build()
+ private val CTS_ONLY_CONFIG = SafetyCenterConfig.Builder()
+ .addSafetySourcesGroup(CTS_SOURCE_GROUP)
+ .build()
+ }
+}
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryGroupTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryGroupTest.kt
new file mode 100644
index 000000000..9557833a2
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryGroupTest.kt
@@ -0,0 +1,125 @@
+/*
+ * 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.cts
+
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.os.Build.VERSION_CODES.TIRAMISU
+import android.os.Parcel
+import android.safetycenter.SafetyCenterStaticEntry
+import android.safetycenter.SafetyCenterStaticEntryGroup
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = TIRAMISU, codeName = "Tiramisu")
+class SafetyCenterStaticEntryGroupTest {
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ private val pendingIntent1 = PendingIntent.getActivity(
+ context,
+ /* requestCode= */ 0,
+ Intent("Fake Data"),
+ PendingIntent.FLAG_IMMUTABLE)
+ private val pendingIntent2 = PendingIntent.getActivity(
+ context,
+ /* requestCode= */ 0,
+ Intent("Fake Different Data"),
+ PendingIntent.FLAG_IMMUTABLE)
+
+ private val staticEntry1 =
+ SafetyCenterStaticEntry("an entry title", "an entry summary", pendingIntent1)
+ private val staticEntry2 =
+ SafetyCenterStaticEntry("another entry title", "another entry summary", pendingIntent2)
+
+ private val staticEntryGroup =
+ SafetyCenterStaticEntryGroup("a title", listOf(staticEntry1, staticEntry2))
+
+ @Test
+ fun getTitle_returnsTitle() {
+ assertThat(SafetyCenterStaticEntryGroup("a title", listOf()).title).isEqualTo("a title")
+ assertThat(SafetyCenterStaticEntryGroup("another title", listOf()).title)
+ .isEqualTo("another title")
+ }
+
+ @Test
+ fun getStaticEntries_returnsStaticEntries() {
+ assertThat(SafetyCenterStaticEntryGroup("", listOf(staticEntry1)).staticEntries)
+ .containsExactly(staticEntry1)
+ assertThat(
+ SafetyCenterStaticEntryGroup("", listOf(staticEntry1, staticEntry2)).staticEntries)
+ .containsExactly(staticEntry1, staticEntry2)
+ assertThat(SafetyCenterStaticEntryGroup("", listOf()).staticEntries).isEmpty()
+ }
+
+ @Test
+ fun describeContents_returns0() {
+ assertThat(staticEntryGroup.describeContents()).isEqualTo(0)
+ }
+
+ @Test
+ fun createFromParcel_withWriteToParcel_returnsEquivalentObject() {
+ val parcel: Parcel = Parcel.obtain()
+
+ staticEntryGroup.writeToParcel(parcel, 0 /* flags */)
+ parcel.setDataPosition(0)
+ val fromParcel = SafetyCenterStaticEntryGroup.CREATOR.createFromParcel(parcel)
+ parcel.recycle()
+
+ assertThat(fromParcel).isEqualTo(staticEntryGroup)
+ }
+
+ @Test
+ fun equals_hashCode_toString_equalByReference_areEqual() {
+ assertThat(staticEntryGroup).isEqualTo(staticEntryGroup)
+ assertThat(staticEntryGroup.hashCode()).isEqualTo(staticEntryGroup.hashCode())
+ assertThat(staticEntryGroup.toString()).isEqualTo(staticEntryGroup.toString())
+ }
+
+ @Test
+ fun equals_hashCode_toString_equalByValue_areEqual() {
+ val group = SafetyCenterStaticEntryGroup("a title", listOf(staticEntry1))
+ val equivalentGroup = SafetyCenterStaticEntryGroup("a title", listOf(staticEntry1))
+
+ assertThat(group).isEqualTo(equivalentGroup)
+ assertThat(group.hashCode()).isEqualTo(equivalentGroup.hashCode())
+ assertThat(group.toString()).isEqualTo(equivalentGroup.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentTitles_areNotEqual() {
+ val group = SafetyCenterStaticEntryGroup("a title", listOf(staticEntry1))
+ val differentGroup = SafetyCenterStaticEntryGroup("a different title", listOf(staticEntry1))
+
+ assertThat(group).isNotEqualTo(differentGroup)
+ assertThat(group.toString()).isNotEqualTo(differentGroup.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentStaticEntries_areNotEqual() {
+ val group = SafetyCenterStaticEntryGroup("a title", listOf(staticEntry1))
+ val differentGroup = SafetyCenterStaticEntryGroup("a different title", listOf(staticEntry2))
+
+ assertThat(group).isNotEqualTo(differentGroup)
+ assertThat(group.toString()).isNotEqualTo(differentGroup.toString())
+ }
+} \ No newline at end of file
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryTest.kt
new file mode 100644
index 000000000..8902aebfd
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryTest.kt
@@ -0,0 +1,138 @@
+/*
+ * 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.cts
+
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.os.Build.VERSION_CODES.TIRAMISU
+import android.os.Parcel
+import android.safetycenter.SafetyCenterStaticEntry
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = TIRAMISU, codeName = "Tiramisu")
+class SafetyCenterStaticEntryTest {
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ private val pendingIntent1 = PendingIntent.getActivity(
+ context,
+ /* requestCode= */ 0,
+ Intent("Fake Data"),
+ PendingIntent.FLAG_IMMUTABLE)
+ private val pendingIntent2 = PendingIntent.getActivity(
+ context,
+ /* requestCode= */ 0,
+ Intent("Fake Different Data"),
+ PendingIntent.FLAG_IMMUTABLE)
+
+ private val title1 = "a title"
+ private val title2 = "another title"
+
+ private val summary1 = "a summary"
+ private val summary2 = "another summary"
+
+ private val staticEntry1 = SafetyCenterStaticEntry(title1, summary1, pendingIntent1)
+ private val staticEntry2 = SafetyCenterStaticEntry(title2, summary2, pendingIntent2)
+
+ @Test
+ fun getTitle_returnsTitle() {
+ assertThat(staticEntry1.title).isEqualTo(title1)
+ assertThat(staticEntry2.title).isEqualTo(title2)
+ }
+
+ @Test
+ fun getSummary_returnsSummary() {
+ assertThat(staticEntry1.summary).isEqualTo(summary1)
+ assertThat(staticEntry2.summary).isEqualTo(summary2)
+ assertThat(SafetyCenterStaticEntry("", null, pendingIntent1).summary).isNull()
+ }
+
+ @Test
+ fun getPendingIntent_returnsPendingIntent() {
+ assertThat(staticEntry1.pendingIntent).isEqualTo(pendingIntent1)
+ assertThat(staticEntry2.pendingIntent).isEqualTo(pendingIntent2)
+ }
+
+ @Test
+ fun describeContents_returns0() {
+ assertThat(staticEntry1.describeContents()).isEqualTo(0)
+ }
+
+ @Test
+ fun createFromParcel_withWriteToParcel_returnsEquivalentObject() {
+ val parcel: Parcel = Parcel.obtain()
+
+ staticEntry1.writeToParcel(parcel, 0 /* flags */)
+ parcel.setDataPosition(0)
+ val fromParcel = SafetyCenterStaticEntry.CREATOR.createFromParcel(parcel)
+ parcel.recycle()
+
+ assertThat(fromParcel).isEqualTo(staticEntry1)
+ }
+
+ @Test
+ fun equals_hashCode_toString_equalByReference_areEqual() {
+ assertThat(staticEntry1).isEqualTo(staticEntry1)
+ assertThat(staticEntry1.hashCode()).isEqualTo(staticEntry1.hashCode())
+ assertThat(staticEntry1.toString()).isEqualTo(staticEntry1.toString())
+ }
+
+ @Test
+ fun equals_hashCode_toString_equalByValue_areEqual() {
+ val staticEntry = SafetyCenterStaticEntry("titlee", "sumaree", pendingIntent1)
+ val equivalentStaticEntry = SafetyCenterStaticEntry("titlee", "sumaree", pendingIntent1)
+
+ assertThat(staticEntry).isEqualTo(equivalentStaticEntry)
+ assertThat(staticEntry.hashCode()).isEqualTo(equivalentStaticEntry.hashCode())
+ assertThat(staticEntry.toString()).isEqualTo(equivalentStaticEntry.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentTitles_areNotEqual() {
+ val staticEntry = SafetyCenterStaticEntry("a title", "a summary", pendingIntent1)
+ val differentStaticEntry =
+ SafetyCenterStaticEntry("a different title", "a summary", pendingIntent1)
+
+ assertThat(staticEntry).isNotEqualTo(differentStaticEntry)
+ assertThat(staticEntry.toString()).isNotEqualTo(differentStaticEntry.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentSummaries_areNotEqual() {
+ val staticEntry = SafetyCenterStaticEntry("a title", "a summary", pendingIntent1)
+ val differentStaticEntry =
+ SafetyCenterStaticEntry("a title", "a different summary", pendingIntent1)
+
+ assertThat(staticEntry).isNotEqualTo(differentStaticEntry)
+ assertThat(staticEntry.toString()).isNotEqualTo(differentStaticEntry.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentPendingIntents_areNotEqual() {
+ val staticEntry = SafetyCenterStaticEntry("a title", "a summary", pendingIntent1)
+ val differentStaticEntry = SafetyCenterStaticEntry("a title", "a summary", pendingIntent2)
+
+ assertThat(staticEntry).isNotEqualTo(differentStaticEntry)
+ assertThat(staticEntry.toString()).isNotEqualTo(differentStaticEntry.toString())
+ }
+} \ No newline at end of file
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStatusTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStatusTest.kt
new file mode 100644
index 000000000..68edbf39e
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStatusTest.kt
@@ -0,0 +1,207 @@
+/*
+ * 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.cts
+
+import android.os.Build.VERSION_CODES.TIRAMISU
+import android.os.Parcel
+import android.safetycenter.SafetyCenterStatus
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = TIRAMISU, codeName = "Tiramisu")
+class SafetyCenterStatusTest {
+
+ val baseStatus = SafetyCenterStatus.Builder()
+ .setTitle("This is my title")
+ .setSummary("This is my summary")
+ .setSeverityLevel(SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_RECOMMENDATION)
+ .setRefreshStatus(SafetyCenterStatus.REFRESH_STATUS_DATA_FETCH_IN_PROGRESS)
+ .build()
+
+ @Test
+ fun getTitle_returnsTitle() {
+ assertThat(SafetyCenterStatus.Builder(baseStatus).setTitle("title").build().title)
+ .isEqualTo("title")
+
+ assertThat(SafetyCenterStatus.Builder(baseStatus).setTitle("different title").build().title)
+ .isEqualTo("different title")
+ }
+
+ @Test
+ fun getSummary_returnsSummary() {
+ assertThat(SafetyCenterStatus.Builder(baseStatus).setSummary("summary").build().summary)
+ .isEqualTo("summary")
+
+ assertThat(
+ SafetyCenterStatus.Builder(baseStatus)
+ .setSummary("different summary")
+ .build()
+ .summary)
+ .isEqualTo("different summary")
+ }
+
+ @Test
+ fun getSeverityLevel_returnsSeverityLevel() {
+ assertThat(
+ SafetyCenterStatus.Builder(baseStatus)
+ .setSeverityLevel(SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK)
+ .build()
+ .severityLevel)
+ .isEqualTo(SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK)
+
+ assertThat(
+ SafetyCenterStatus.Builder(baseStatus)
+ .setSeverityLevel(
+ SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)
+ .build()
+ .severityLevel)
+ .isEqualTo(SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING)
+ }
+
+ @Test
+ fun getSeverityLevel_defaultUnknown() {
+ assertThat(
+ SafetyCenterStatus.Builder()
+ .setTitle("This is my title")
+ .setSummary("This is my summary")
+ .build()
+ .severityLevel)
+ .isEqualTo(SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN)
+ }
+
+ @Test
+ fun getRefreshStatus_returnsRefreshStatus() {
+ assertThat(
+ SafetyCenterStatus.Builder(baseStatus)
+ .setRefreshStatus(SafetyCenterStatus.REFRESH_STATUS_NONE)
+ .build()
+ .refreshStatus)
+ .isEqualTo(SafetyCenterStatus.REFRESH_STATUS_NONE)
+
+ assertThat(
+ SafetyCenterStatus.Builder(baseStatus)
+ .setRefreshStatus(SafetyCenterStatus.REFRESH_STATUS_FULL_RESCAN_IN_PROGRESS)
+ .build()
+ .refreshStatus)
+ .isEqualTo(SafetyCenterStatus.REFRESH_STATUS_FULL_RESCAN_IN_PROGRESS)
+ }
+
+ @Test
+ fun getRefreshStatus_defaultNone() {
+ assertThat(
+ SafetyCenterStatus.Builder()
+ .setTitle("This is my title")
+ .setSummary("This is my summary")
+ .build()
+ .refreshStatus)
+ .isEqualTo(SafetyCenterStatus.REFRESH_STATUS_NONE)
+ }
+
+ @Test
+ fun describeContents_returns0() {
+ assertThat(baseStatus.describeContents()).isEqualTo(0)
+ }
+
+ @Test
+ fun createFromParcel_withWriteToParcel_returnsEquivalentObject() {
+ val parcel: Parcel = Parcel.obtain()
+ baseStatus.writeToParcel(parcel, 0 /* flags */)
+ parcel.setDataPosition(0)
+ val fromParcel = SafetyCenterStatus.CREATOR.createFromParcel(parcel)
+ parcel.recycle()
+
+ assertThat(fromParcel).isEqualTo(baseStatus)
+ }
+
+ @Test
+ fun equals_hashCode_toString_equalByReference_areEqual() {
+ assertThat(baseStatus).isEqualTo(baseStatus)
+ assertThat(baseStatus.hashCode()).isEqualTo(baseStatus.hashCode())
+ assertThat(baseStatus.toString()).isEqualTo(baseStatus.toString())
+ }
+
+ @Test
+ fun equals_hashCode_toString_equalByValue_areEqual() {
+ val status = SafetyCenterStatus.Builder()
+ .setTitle("same title")
+ .setSummary("same summary")
+ .setSeverityLevel(SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK)
+ .build()
+ val equivalentStatus = SafetyCenterStatus.Builder()
+ .setTitle("same title")
+ .setSummary("same summary")
+ .setSeverityLevel(SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK)
+ .build()
+
+ assertThat(status).isEqualTo(equivalentStatus)
+ assertThat(status.hashCode()).isEqualTo(equivalentStatus.hashCode())
+ assertThat(status.toString()).isEqualTo(equivalentStatus.toString())
+ }
+
+ @Test
+ fun equals_hashCode_toString_fromCopyBuilder_areEqual() {
+ val copyOfBaseStatus = SafetyCenterStatus.Builder(baseStatus).build()
+
+ assertThat(copyOfBaseStatus).isEqualTo(baseStatus)
+ assertThat(copyOfBaseStatus.hashCode()).isEqualTo(baseStatus.hashCode())
+ assertThat(copyOfBaseStatus.toString()).isEqualTo(baseStatus.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentTitles_areNotEqual() {
+ val unequalStatus = SafetyCenterStatus.Builder(baseStatus)
+ .setTitle("that's discarsting")
+ .build()
+
+ assertThat(unequalStatus).isNotEqualTo(baseStatus)
+ assertThat(unequalStatus.toString()).isNotEqualTo(baseStatus.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentSummaries_areNotEqual() {
+ val unequalStatus = SafetyCenterStatus.Builder(baseStatus)
+ .setSummary("discarsting sheet")
+ .build()
+
+ assertThat(unequalStatus).isNotEqualTo(baseStatus)
+ assertThat(unequalStatus.toString()).isNotEqualTo(baseStatus.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentSeverityLevels_arNotEqual() {
+ val unequalStatus = SafetyCenterStatus.Builder(baseStatus)
+ .setSeverityLevel(SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK)
+ .build()
+
+ assertThat(unequalStatus).isNotEqualTo(baseStatus)
+ assertThat(unequalStatus.toString()).isNotEqualTo(baseStatus.toString())
+ }
+
+ @Test
+ fun equals_toString_withDifferentRefreshStatuses_areNotEqual() {
+ val unequalStatus = SafetyCenterStatus.Builder(baseStatus)
+ .setRefreshStatus(SafetyCenterStatus.REFRESH_STATUS_NONE)
+ .build()
+
+ assertThat(unequalStatus).isNotEqualTo(baseStatus)
+ assertThat(unequalStatus.toString()).isNotEqualTo(baseStatus.toString())
+ }
+} \ No newline at end of file
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyEventTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyEventTest.kt
new file mode 100644
index 000000000..bc9d5384a
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyEventTest.kt
@@ -0,0 +1,189 @@
+/*
+ * 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.cts
+
+import android.os.Build
+import android.safetycenter.SafetyEvent
+import android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_REFRESH_REQUESTED
+import android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_DEVICE_REBOOTED
+import android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED
+import android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED
+import android.safetycenter.testers.AnyTester.assertThatRepresentationsAreEqual
+import android.safetycenter.testers.AnyTester.assertThatRepresentationsAreNotEqual
+import android.safetycenter.testers.ParcelableTester.assertThatRoundTripReturnsOriginal
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** CTS tests for [SafetyEvent]. */
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+class SafetyEventTest {
+ @Test
+ fun getSafetyEventType_returnsSafetyEventType() {
+ val safetyEvent = SafetyEvent.Builder(SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build()
+
+ assertThat(safetyEvent.safetyEventType).isEqualTo(SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED)
+ }
+
+ @Test
+ fun getRefreshBroadcastId_returnsRefreshBroadcastId() {
+ val safetyEvent =
+ SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED)
+ .setRefreshBroadcastId(REFRESH_BROADCAST_ID)
+ .build()
+
+ assertThat(safetyEvent.refreshBroadcastId).isEqualTo(REFRESH_BROADCAST_ID)
+ }
+
+ @Test
+ fun getSafetySourceIssueId_returnsSafetySourceIssueId() {
+ val safetyEvent =
+ SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED)
+ .setSafetySourceIssueId(SAFETY_SOURCE_ISSUE_ID)
+ .build()
+
+ assertThat(safetyEvent.safetySourceIssueId).isEqualTo(SAFETY_SOURCE_ISSUE_ID)
+ }
+
+ @Test
+ fun getSafetySourceIssueActionId_returnsSafetySourceIssueActionId() {
+ val safetyEvent =
+ SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED)
+ .setSafetySourceIssueActionId(SAFETY_SOURCE_ISSUE_ACTION_ID)
+ .build()
+
+ assertThat(safetyEvent.safetySourceIssueActionId).isEqualTo(SAFETY_SOURCE_ISSUE_ACTION_ID)
+ }
+
+ @Test
+ fun describeContents_returns0() {
+ val safetyEvent =
+ SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED)
+ .setSafetySourceIssueId(SAFETY_SOURCE_ISSUE_ID)
+ .setSafetySourceIssueActionId(SAFETY_SOURCE_ISSUE_ACTION_ID)
+ .build()
+
+ assertThat(safetyEvent.describeContents()).isEqualTo(0)
+ }
+
+ @Test
+ fun createFromParcel_withWriteToParcel_returnsOriginalSafetySourceData() {
+ val safetyEvent =
+ SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED)
+ .setSafetySourceIssueId(SAFETY_SOURCE_ISSUE_ID)
+ .setSafetySourceIssueActionId(SAFETY_SOURCE_ISSUE_ACTION_ID)
+ .build()
+
+ assertThatRoundTripReturnsOriginal(safetyEvent, SafetyEvent.CREATOR)
+ }
+
+ // TODO(b/208473675): Use `EqualsTester` for testing `hashcode` and `equals`.
+ @Test
+ fun hashCode_equals_toString_withEqualByReference_areEqual() {
+ val safetyEvent =
+ SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED)
+ .setRefreshBroadcastId(REFRESH_BROADCAST_ID)
+ .build()
+ val otherSafetyEvent = safetyEvent
+
+ assertThatRepresentationsAreEqual(safetyEvent, otherSafetyEvent)
+ }
+
+ @Test
+ fun hashCode_equals_toString_withAllFieldsEqual_areEqual() {
+ val safetyEvent =
+ SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED)
+ .setSafetySourceIssueId(SAFETY_SOURCE_ISSUE_ID)
+ .setSafetySourceIssueActionId(SAFETY_SOURCE_ISSUE_ACTION_ID)
+ .build()
+ val otherSafetyEvent =
+ SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED)
+ .setSafetySourceIssueId(SAFETY_SOURCE_ISSUE_ID)
+ .setSafetySourceIssueActionId(SAFETY_SOURCE_ISSUE_ACTION_ID)
+ .build()
+
+ assertThatRepresentationsAreEqual(safetyEvent, otherSafetyEvent)
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentSafetyEventTypes_areNotEqual() {
+ val safetyEvent =
+ SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED)
+ .build()
+ val otherSafetyEvent =
+ SafetyEvent.Builder(SAFETY_EVENT_TYPE_DEVICE_REBOOTED)
+ .build()
+
+ assertThatRepresentationsAreNotEqual(safetyEvent, otherSafetyEvent)
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentRefreshBroadcastIds_areNotEqual() {
+ val safetyEvent =
+ SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED)
+ .setRefreshBroadcastId(REFRESH_BROADCAST_ID)
+ .build()
+ val otherSafetyEvent =
+ SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED)
+ .setRefreshBroadcastId(OTHER_REFRESH_BROADCAST_ID)
+ .build()
+
+ assertThatRepresentationsAreNotEqual(safetyEvent, otherSafetyEvent)
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentIssueIds_areNotEqual() {
+ val safetyEvent =
+ SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED)
+ .setSafetySourceIssueId(SAFETY_SOURCE_ISSUE_ID)
+ .build()
+ val otherSafetyEvent =
+ SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED)
+ .setSafetySourceIssueId(OTHER_SAFETY_SOURCE_ISSUE_ID)
+ .build()
+
+ assertThatRepresentationsAreNotEqual(safetyEvent, otherSafetyEvent)
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentActionIds_areNotEqual() {
+ val safetyEvent =
+ SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED)
+ .setSafetySourceIssueId(SAFETY_SOURCE_ISSUE_ID)
+ .setSafetySourceIssueActionId(SAFETY_SOURCE_ISSUE_ACTION_ID)
+ .build()
+ val otherSafetyEvent =
+ SafetyEvent.Builder(SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED)
+ .setSafetySourceIssueId(SAFETY_SOURCE_ISSUE_ID)
+ .setSafetySourceIssueActionId(OTHER_SAFETY_SOURCE_ISSUE_ACTION_ID)
+ .build()
+
+ assertThatRepresentationsAreNotEqual(safetyEvent, otherSafetyEvent)
+ }
+
+ companion object {
+ const val REFRESH_BROADCAST_ID = "refresh_broadcast_id"
+ const val OTHER_REFRESH_BROADCAST_ID = "other_refresh_broadcast_id"
+ const val SAFETY_SOURCE_ISSUE_ID = "safety_source_issue_id"
+ const val OTHER_SAFETY_SOURCE_ISSUE_ID = "other_safety_source_issue_id"
+ const val SAFETY_SOURCE_ISSUE_ACTION_ID = "safety_source_issue_action_id"
+ const val OTHER_SAFETY_SOURCE_ISSUE_ACTION_ID = "other_safety_source_issue_action_id"
+ }
+} \ No newline at end of file
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceBroadcastReceiver.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceBroadcastReceiver.kt
new file mode 100644
index 000000000..5f8e8b890
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceBroadcastReceiver.kt
@@ -0,0 +1,93 @@
+/*
+ * 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.cts
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.safetycenter.SafetyCenterManager.ACTION_REFRESH_SAFETY_SOURCES
+import android.safetycenter.SafetyCenterManager.EXTRA_REFRESH_REQUEST_TYPE_GET_DATA
+import android.safetycenter.SafetyCenterManager.EXTRA_REFRESH_REQUEST_TYPE_FETCH_FRESH_DATA
+import android.safetycenter.SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_REQUEST_TYPE
+import android.safetycenter.SafetyCenterManager
+import android.safetycenter.SafetySourceData
+import android.safetycenter.SafetyEvent
+import android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_REFRESH_REQUESTED
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withTimeout
+import java.time.Duration
+
+/** Broadcast receiver to be used for testing broadcasts sent to safety source apps. */
+class SafetySourceBroadcastReceiver : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent?) {
+ if (intent == null) {
+ throw IllegalArgumentException("Received null intent")
+ }
+
+ if (intent.action != ACTION_REFRESH_SAFETY_SOURCES) {
+ throw IllegalArgumentException("Received intent with action: ${intent.action}")
+ }
+
+ val safetyCenterManager = context.getSystemService(SafetyCenterManager::class.java)!!
+
+ when (intent.getIntExtra(EXTRA_REFRESH_SAFETY_SOURCES_REQUEST_TYPE, -1)) {
+ EXTRA_REFRESH_REQUEST_TYPE_GET_DATA ->
+ safetyCenterManager.setSafetySourceDataWithPermission(
+ safetySourceId!!,
+ safetySourceDataOnPageOpen!!,
+ EVENT_REFRESH_REQUESTED
+ )
+ EXTRA_REFRESH_REQUEST_TYPE_FETCH_FRESH_DATA ->
+ safetyCenterManager.setSafetySourceDataWithPermission(
+ safetySourceId!!,
+ safetySourceDataOnRescanClick!!,
+ EVENT_REFRESH_REQUESTED
+ )
+ }
+
+ runBlocking {
+ updateChannel.send(Unit)
+ }
+ }
+
+ companion object {
+ private var updateChannel = Channel<Unit>()
+ private val EVENT_REFRESH_REQUESTED =
+ SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED)
+ .setRefreshBroadcastId("refresh_id")
+ .build()
+ var safetySourceId: String? = null
+ var safetySourceDataOnPageOpen: SafetySourceData? = null
+ var safetySourceDataOnRescanClick: SafetySourceData? = null
+
+ fun reset() {
+ safetySourceId = null
+ safetySourceDataOnRescanClick = null
+ safetySourceDataOnPageOpen = null
+ updateChannel = Channel()
+ }
+
+ fun waitTillOnReceiveComplete(duration: Duration) {
+ runBlocking {
+ withTimeout(duration.toMillis()) {
+ updateChannel.receive()
+ }
+ }
+ }
+ }
+}
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceDataTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceDataTest.kt
new file mode 100644
index 000000000..76090bf3c
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceDataTest.kt
@@ -0,0 +1,296 @@
+/*
+ * 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.app.PendingIntent
+import android.app.PendingIntent.FLAG_IMMUTABLE
+import android.content.Context
+import android.content.Intent
+import android.os.Build.VERSION_CODES.TIRAMISU
+import android.os.Parcel
+import android.safetycenter.SafetySourceData
+import android.safetycenter.SafetySourceIssue
+import android.safetycenter.SafetySourceIssue.ISSUE_CATEGORY_ACCOUNT
+import android.safetycenter.SafetySourceStatus
+import android.safetycenter.SafetySourceStatus.IconAction.ICON_TYPE_GEAR
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** CTS tests for [SafetySourceData]. */
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = TIRAMISU, codeName = "Tiramisu")
+class SafetySourceDataTest {
+ private val context: Context = getApplicationContext()
+
+ private val status1 = SafetySourceStatus.Builder(
+ "Status title 1",
+ "Status summary 1",
+ SafetySourceStatus.STATUS_LEVEL_NONE,
+ PendingIntent.getActivity(context, 0 /* requestCode= */,
+ Intent("Status PendingIntent 1"), FLAG_IMMUTABLE))
+ .setEnabled(false)
+ .build()
+ private val status2 = SafetySourceStatus.Builder(
+ "Status title 2",
+ "Status summary 2",
+ SafetySourceStatus.STATUS_LEVEL_RECOMMENDATION,
+ PendingIntent.getActivity(context, 0 /* requestCode= */,
+ Intent("Status PendingIntent 2"), FLAG_IMMUTABLE))
+ .setIconAction(SafetySourceStatus.IconAction(ICON_TYPE_GEAR,
+ PendingIntent.getActivity(context, 0 /* requestCode= */,
+ Intent("IconAction PendingIntent 2"), FLAG_IMMUTABLE)))
+ .build()
+ private val issue1 = SafetySourceIssue.Builder(
+ "Issue id 1",
+ "Issue summary 1",
+ "Issue summary 1",
+ SafetySourceIssue.SEVERITY_LEVEL_INFORMATION, "issue_type_id"
+ )
+ .setSubtitle("Issue subtitle 1")
+ .setIssueCategory(ISSUE_CATEGORY_ACCOUNT)
+ .addAction(
+ SafetySourceIssue.Action.Builder(
+ "action_id_1",
+ "Action label 1",
+ PendingIntent.getActivity(
+ context, 0 /* requestCode= */,
+ Intent("Issue PendingIntent 1"), FLAG_IMMUTABLE
+ )
+ )
+ .build()
+ )
+ .build()
+ private val issue2 = SafetySourceIssue.Builder(
+ "Issue id 2",
+ "Issue title 2",
+ "Issue summary 2",
+ SafetySourceIssue.SEVERITY_LEVEL_RECOMMENDATION, "issue_type_id"
+ )
+ .addAction(
+ SafetySourceIssue.Action.Builder(
+ "action_id_2",
+ "Action label 2",
+ PendingIntent.getService(
+ context, 0 /* requestCode= */,
+ Intent("Issue PendingIntent 2"), FLAG_IMMUTABLE
+ )
+ ).build()
+ )
+ .setOnDismissPendingIntent(
+ PendingIntent.getService(
+ context,
+ 0 /* requestCode= */,
+ Intent("Issue OnDismissPendingIntent 2"), FLAG_IMMUTABLE
+ )
+ )
+ .build()
+
+ @Test
+ fun getStatus_withDefaultBuilder_returnsNull() {
+ val safetySourceData = SafetySourceData.Builder().build()
+
+ assertThat(safetySourceData.status).isNull()
+ }
+
+ @Test
+ fun getStatus_whenSetExplicitly_returnsStatus() {
+ val safetySourceData = SafetySourceData.Builder()
+ .setStatus(status1)
+ .build()
+
+ assertThat(safetySourceData.status).isEqualTo(status1)
+ }
+
+ @Test
+ fun getIssues_withDefaultBuilder_returnsEmptyList() {
+ val safetySourceData = SafetySourceData.Builder().build()
+
+ assertThat(safetySourceData.issues).isEmpty()
+ }
+
+ @Test
+ fun getIssues_whenSetExplicitly_returnsIssues() {
+ val safetySourceData = SafetySourceData.Builder()
+ .addIssue(issue1)
+ .addIssue(issue2)
+ .build()
+
+ assertThat(safetySourceData.issues).containsExactly(issue1, issue2).inOrder()
+ }
+
+ @Test
+ fun clearIssues_removesAllIssues() {
+ val safetySourceData = SafetySourceData.Builder()
+ .addIssue(issue1)
+ .addIssue(issue2)
+ .clearIssues()
+ .build()
+
+ assertThat(safetySourceData.issues).isEmpty()
+ }
+
+ @Test
+ fun describeContents_returns0() {
+ val safetySourceData = SafetySourceData.Builder()
+ .setStatus(status1)
+ .addIssue(issue1)
+ .addIssue(issue2)
+ .build()
+
+ assertThat(safetySourceData.describeContents()).isEqualTo(0)
+ }
+
+ @Test
+ fun createFromParcel_withWriteToParcel_returnsOriginalSafetySourceData() {
+ val safetySourceData = SafetySourceData.Builder()
+ .setStatus(status1)
+ .addIssue(issue1)
+ .addIssue(issue2)
+ .build()
+
+ val parcel: Parcel = Parcel.obtain()
+ safetySourceData.writeToParcel(parcel, 0 /* flags */)
+ parcel.setDataPosition(0)
+ val safetySourceDataFromParcel: SafetySourceData =
+ SafetySourceData.CREATOR.createFromParcel(parcel)
+ parcel.recycle()
+
+ assertThat(safetySourceDataFromParcel).isEqualTo(safetySourceData)
+ }
+
+ // TODO(b/208473675): Use `EqualsTester` for testing `hashcode` and `equals`.
+ @Test
+ fun hashCode_equals_toString_withEqualByReference_withoutStatusAndIssues_areEqual() {
+ val safetySourceData = SafetySourceData.Builder().build()
+ val otherSafetySourceData = safetySourceData
+
+ assertThat(safetySourceData.hashCode()).isEqualTo(otherSafetySourceData.hashCode())
+ assertThat(safetySourceData).isEqualTo(otherSafetySourceData)
+ assertThat(safetySourceData.toString()).isEqualTo(otherSafetySourceData.toString())
+ }
+
+ @Test
+ fun hashCode_equals_toString_withEqualByReference_withoutIssues_areEqual() {
+ val safetySourceData = SafetySourceData.Builder()
+ .setStatus(status1)
+ .build()
+ val otherSafetySourceData = safetySourceData
+
+ assertThat(safetySourceData.hashCode()).isEqualTo(otherSafetySourceData.hashCode())
+ assertThat(safetySourceData).isEqualTo(otherSafetySourceData)
+ assertThat(safetySourceData.toString()).isEqualTo(otherSafetySourceData.toString())
+ }
+
+ @Test
+ fun hashCode_equals_toString_withEqualByReference_areEqual() {
+ val safetySourceData = SafetySourceData.Builder()
+ .setStatus(status1)
+ .addIssue(issue1)
+ .addIssue(issue2)
+ .build()
+ val otherSafetySourceData = safetySourceData
+
+ assertThat(safetySourceData.hashCode()).isEqualTo(otherSafetySourceData.hashCode())
+ assertThat(safetySourceData).isEqualTo(otherSafetySourceData)
+ assertThat(safetySourceData.toString()).isEqualTo(otherSafetySourceData.toString())
+ }
+
+ @Test
+ fun hashCode_equals_toString_withAllFieldsEqual_areEqual() {
+ val safetySourceData = SafetySourceData.Builder()
+ .setStatus(status1)
+ .addIssue(issue1)
+ .addIssue(issue2)
+ .build()
+ val otherSafetySourceData = SafetySourceData.Builder()
+ .setStatus(status1)
+ .addIssue(issue1)
+ .addIssue(issue2)
+ .build()
+
+ assertThat(safetySourceData.hashCode()).isEqualTo(otherSafetySourceData.hashCode())
+ assertThat(safetySourceData).isEqualTo(otherSafetySourceData)
+ assertThat(safetySourceData.toString()).isEqualTo(otherSafetySourceData.toString())
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentIssues_areNotEqual() {
+ val safetySourceData = SafetySourceData.Builder()
+ .setStatus(status1)
+ .addIssue(issue1)
+ .addIssue(issue2)
+ .build()
+ val otherSafetySourceData = SafetySourceData.Builder()
+ .setStatus(status2)
+ .addIssue(issue1)
+ .addIssue(issue2)
+ .build()
+
+ assertThat(safetySourceData.hashCode()).isNotEqualTo(otherSafetySourceData.hashCode())
+ assertThat(safetySourceData).isNotEqualTo(otherSafetySourceData)
+ assertThat(safetySourceData.toString()).isNotEqualTo(otherSafetySourceData.toString())
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentStatuses_areNotEqual() {
+ val safetySourceData = SafetySourceData.Builder()
+ .setStatus(status1)
+ .addIssue(issue1)
+ .addIssue(issue2)
+ .build()
+ val otherSafetySourceData = SafetySourceData.Builder()
+ .setStatus(status1)
+ .addIssue(issue1)
+ .build()
+
+ assertThat(safetySourceData.hashCode()).isNotEqualTo(otherSafetySourceData.hashCode())
+ assertThat(safetySourceData).isNotEqualTo(otherSafetySourceData)
+ assertThat(safetySourceData.toString()).isNotEqualTo(otherSafetySourceData.toString())
+ }
+
+ @Test
+ fun hashCode_equals_toString_withStatusSetInOneAndNotOther_areNotEqual() {
+ val safetySourceData = SafetySourceData.Builder()
+ .setStatus(status1)
+ .build()
+ val otherSafetySourceData = SafetySourceData.Builder().build()
+
+ assertThat(safetySourceData.hashCode()).isNotEqualTo(otherSafetySourceData.hashCode())
+ assertThat(safetySourceData).isNotEqualTo(otherSafetySourceData)
+ assertThat(safetySourceData.toString()).isNotEqualTo(otherSafetySourceData.toString())
+ }
+
+ @Test
+ fun hashCode_equals_toString_withIssuesSetInOneAndNotOther_areNotEqual() {
+ val safetySourceData = SafetySourceData.Builder()
+ .setStatus(status1)
+ .addIssue(issue1)
+ .addIssue(issue2)
+ .build()
+ val otherSafetySourceData = SafetySourceData.Builder()
+ .setStatus(status1)
+ .build()
+
+ assertThat(safetySourceData.hashCode()).isNotEqualTo(otherSafetySourceData.hashCode())
+ assertThat(safetySourceData).isNotEqualTo(otherSafetySourceData)
+ assertThat(safetySourceData.toString()).isNotEqualTo(otherSafetySourceData.toString())
+ }
+} \ No newline at end of file
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceErrorTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceErrorTest.kt
new file mode 100644
index 000000000..0d69f2c93
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceErrorTest.kt
@@ -0,0 +1,78 @@
+/*
+ * 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.cts
+
+import android.os.Build
+import android.safetycenter.SafetyEvent
+import android.safetycenter.SafetySourceError
+import android.safetycenter.testers.AnyTester.assertThatRepresentationsAreEqual
+import android.safetycenter.testers.AnyTester.assertThatRepresentationsAreNotEqual
+import android.safetycenter.testers.ParcelableTester.assertThatRoundTripReturnsOriginal
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** CTS tests for [SafetySourceError]. */
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+class SafetySourceErrorTest {
+ @Test
+ fun getSafetyEvent_returnsSafetyEvent() {
+ val safetySourceError = SafetySourceError(SAFETY_EVENT)
+
+ assertThat(safetySourceError.safetyEvent).isEqualTo(SAFETY_EVENT)
+ }
+
+ @Test
+ fun createFromParcel_withWriteToParcel_returnsEquivalentObject() {
+ val safetySourceError = SafetySourceError(SAFETY_EVENT)
+
+ assertThatRoundTripReturnsOriginal(safetySourceError, SafetySourceError.CREATOR)
+ }
+
+ @Test
+ fun equals_hashCode_toString_equalByReference_areEqual() {
+ val safetySourceError = SafetySourceError(SAFETY_EVENT)
+
+ assertThatRepresentationsAreEqual(safetySourceError, safetySourceError)
+ }
+
+ @Test
+ fun equals_hashCode_toString_equalByValue_areEqual() {
+ val safetySourceError = SafetySourceError(SAFETY_EVENT)
+ val equivalentSafetySourceError = SafetySourceError(SAFETY_EVENT)
+
+ assertThatRepresentationsAreEqual(safetySourceError, equivalentSafetySourceError)
+ }
+
+ @Test
+ fun equals_toString_withDifferentSafetyEvents_areNotEqual() {
+ val safetySourceError = SafetySourceError(
+ SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build())
+ val otherSafetySourceError = SafetySourceError(
+ SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_DEVICE_REBOOTED).build())
+
+ assertThatRepresentationsAreNotEqual(safetySourceError, otherSafetySourceError)
+ }
+
+ companion object {
+ private val SAFETY_EVENT =
+ SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build()
+ }
+} \ No newline at end of file
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceIssueTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceIssueTest.kt
new file mode 100644
index 000000000..ad3e44155
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceIssueTest.kt
@@ -0,0 +1,785 @@
+/*
+ * 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.app.PendingIntent
+import android.app.PendingIntent.FLAG_IMMUTABLE
+import android.content.Context
+import android.content.Intent
+import android.os.Build.VERSION_CODES.TIRAMISU
+import android.os.Parcel
+import android.safetycenter.SafetySourceIssue
+import android.safetycenter.SafetySourceIssue.ISSUE_CATEGORY_ACCOUNT
+import android.safetycenter.SafetySourceIssue.ISSUE_CATEGORY_DEVICE
+import android.safetycenter.SafetySourceIssue.ISSUE_CATEGORY_GENERAL
+import android.safetycenter.SafetySourceIssue.Action
+import android.safetycenter.SafetySourceIssue.SEVERITY_LEVEL_CRITICAL_WARNING
+import android.safetycenter.SafetySourceIssue.SEVERITY_LEVEL_INFORMATION
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertThrows
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** CTS tests for [SafetySourceIssue]. */
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = TIRAMISU, codeName = "Tiramisu")
+class SafetySourceIssueTest {
+ private val context: Context = getApplicationContext()
+
+ private val pendingIntent1: PendingIntent = PendingIntent.getActivity(
+ context,
+ 0 /* requestCode= */, Intent("PendingIntent 1"), FLAG_IMMUTABLE
+ )
+ private val action1 = Action.Builder("action_id_1", "Action label 1", pendingIntent1).build()
+ private val pendingIntent2: PendingIntent = PendingIntent.getActivity(
+ context,
+ 0 /* requestCode= */, Intent("PendingIntent 2"), FLAG_IMMUTABLE
+ )
+ private val action2 = Action.Builder("action_id_2", "Action label 2", pendingIntent2).build()
+ private val action3 = Action.Builder("action_id_3", "Action label 3", pendingIntent1).build()
+
+ @Test
+ fun action_getId_returnsId() {
+ val action = Action.Builder("action_id", "Action label", pendingIntent1).build()
+
+ assertThat(action.id).isEqualTo("action_id")
+ }
+
+ @Test
+ fun action_getLabel_returnsLabel() {
+ val action = Action.Builder("action_id", "Action label", pendingIntent1).build()
+
+ assertThat(action.label).isEqualTo("Action label")
+ }
+
+ @Test
+ fun action_isResolving_withDefaultBuilder_returnsFalse() {
+ val action = Action.Builder("action_id", "Action label", pendingIntent1).build()
+
+ assertThat(action.isResolving).isFalse()
+ }
+
+ @Test
+ fun action_isResolving_whenSetExplicitly_returnsResolving() {
+ val action = Action.Builder("action_id", "Action label", pendingIntent1)
+ .setResolving(true)
+ .build()
+
+ assertThat(action.isResolving).isTrue()
+ }
+
+ @Test
+ fun action_getPendingIntent_returnsPendingIntent() {
+ val action = Action.Builder("action_id", "Action label", pendingIntent1).build()
+
+ assertThat(action.pendingIntent).isEqualTo(pendingIntent1)
+ }
+
+ @Test
+ fun action_getSuccessMessage_withDefaultBuilder_returnsNull() {
+ val action = Action.Builder("action_id", "Action label", pendingIntent1).build()
+
+ assertThat(action.successMessage).isNull()
+ }
+
+ @Test
+ fun action_getSuccessMessage_whenSetExplicitly_returnsSuccessMessage() {
+ val action = Action.Builder("action_id", "Action label", pendingIntent1)
+ .setSuccessMessage("Action successfully completed")
+ .build()
+
+ assertThat(action.successMessage).isEqualTo("Action successfully completed")
+ }
+
+ @Test
+ fun action_describeContents_returns0() {
+ val action = Action.Builder("action_id", "Action label", pendingIntent1).build()
+
+ assertThat(action.describeContents()).isEqualTo(0)
+ }
+
+ @Test
+ fun action_createFromParcel_withWriteToParcel_returnsOriginalAction() {
+ val action = Action.Builder("action_id", "Action label", pendingIntent1)
+ .setSuccessMessage("Action successfully completed")
+ .build()
+
+ val parcel: Parcel = Parcel.obtain()
+ action.writeToParcel(parcel, 0 /* flags */)
+ parcel.setDataPosition(0)
+ val actionFromParcel: Action = Action.CREATOR.createFromParcel(parcel)
+ parcel.recycle()
+
+ assertThat(actionFromParcel).isEqualTo(action)
+ }
+
+ // TODO(b/208473675): Use `EqualsTester` for testing `hashcode` and `equals`.
+ @Test
+ fun action_hashCode_equals_toString_withEqualByReferenceActions_areEqual() {
+ val action = Action.Builder("action_id", "Action label", pendingIntent1).build()
+ val otherAction = action
+
+ assertThat(action.hashCode()).isEqualTo(otherAction.hashCode())
+ assertThat(action).isEqualTo(otherAction)
+ assertThat(action.toString()).isEqualTo(otherAction.toString())
+ }
+
+ @Test
+ fun action_hashCode_equals_toString_withAllFieldsEqual_areEqual() {
+ val action = Action.Builder("action_id", "Action label", pendingIntent1).build()
+ val otherAction = Action.Builder("action_id", "Action label", pendingIntent1).build()
+
+ assertThat(action.hashCode()).isEqualTo(otherAction.hashCode())
+ assertThat(action).isEqualTo(otherAction)
+ assertThat(action.toString()).isEqualTo(otherAction.toString())
+ }
+
+ @Test
+ fun action_hashCode_equals_toString_withDifferentIds_areNotEqual() {
+ val action = Action.Builder("action_id", "Action label", pendingIntent1).build()
+ val otherAction = Action.Builder("other_action_id", "Action label", pendingIntent1).build()
+
+ assertThat(action.hashCode()).isNotEqualTo(otherAction.hashCode())
+ assertThat(action).isNotEqualTo(otherAction)
+ assertThat(action.toString()).isNotEqualTo(otherAction.toString())
+ }
+
+ @Test
+ fun action_hashCode_equals_toString_withDifferentLabels_areNotEqual() {
+ val action = Action.Builder("action_id", "Action label", pendingIntent1).build()
+ val otherAction = Action.Builder("action_id", "Other action label", pendingIntent1).build()
+
+ assertThat(action.hashCode()).isNotEqualTo(otherAction.hashCode())
+ assertThat(action).isNotEqualTo(otherAction)
+ assertThat(action.toString()).isNotEqualTo(otherAction.toString())
+ }
+
+ @Test
+ fun action_hashCode_equals_toString_withDifferentResolving_areNotEqual() {
+ val action =
+ Action.Builder("action_id", "Action label", pendingIntent1).setResolving(false)
+ .build()
+ val otherAction =
+ Action.Builder("action_id", "Action label", pendingIntent1).setResolving(true).build()
+
+ assertThat(action.hashCode()).isNotEqualTo(otherAction.hashCode())
+ assertThat(action).isNotEqualTo(otherAction)
+ assertThat(action.toString()).isNotEqualTo(otherAction.toString())
+ }
+
+ @Test
+ fun action_hashCode_equals_toString_withDifferentPendingIntents_areNotEqual() {
+ val action = Action.Builder(
+ "action_id",
+ "Action label",
+ PendingIntent.getActivity(
+ context, 0 /* requestCode= */,
+ Intent("Action PendingIntent"), FLAG_IMMUTABLE
+ )
+ )
+ .build()
+ val otherAction = Action.Builder(
+ "action_id",
+ "Action label",
+ PendingIntent.getActivity(
+ context, 0 /* requestCode= */,
+ Intent("Other action PendingIntent"), FLAG_IMMUTABLE
+ )
+ )
+ .build()
+
+ assertThat(action.hashCode()).isNotEqualTo(otherAction.hashCode())
+ assertThat(action).isNotEqualTo(otherAction)
+ assertThat(action.toString()).isNotEqualTo(otherAction.toString())
+ }
+
+ @Test
+ fun action_hashCode_equals_toString_withDifferentSuccessMessages_areNotEqual() {
+ val action =
+ Action.Builder("action_id", "Action label", pendingIntent1)
+ .setSuccessMessage("Action successfully completed")
+ .build()
+ val otherAction =
+ Action.Builder("action_id", "Action label", pendingIntent1)
+ .setSuccessMessage("Other action successfully completed")
+ .build()
+
+ assertThat(action.hashCode()).isNotEqualTo(otherAction.hashCode())
+ assertThat(action).isNotEqualTo(otherAction)
+ assertThat(action.toString()).isNotEqualTo(otherAction.toString())
+ }
+
+ @Test
+ fun getId_returnsId() {
+ val safetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ )
+ .addAction(action1)
+ .build()
+
+ assertThat(safetySourceIssue.id).isEqualTo("Issue id")
+ }
+
+ @Test
+ fun getTitle_returnsTitle() {
+ val safetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ )
+ .addAction(action1)
+ .build()
+
+ assertThat(safetySourceIssue.title).isEqualTo("Issue title")
+ }
+
+ @Test
+ fun getSubtitle_withDefaultBuilder_returnsNull() {
+ val safetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ )
+ .addAction(action1)
+ .build()
+
+ assertThat(safetySourceIssue.subtitle).isNull()
+ }
+
+ @Test
+ fun getSubtitle_whenSetExplicitly_returnsSubtitle() {
+ val safetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ )
+ .setSubtitle("Issue subtitle")
+ .addAction(action1)
+ .build()
+
+ assertThat(safetySourceIssue.subtitle).isEqualTo("Issue subtitle")
+ }
+
+ @Test
+ fun getSummary_returnsSummary() {
+ val safetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ )
+ .addAction(action1)
+ .build()
+
+ assertThat(safetySourceIssue.summary).isEqualTo("Issue summary")
+ }
+
+ @Test
+ fun getSeverityLevel_returnsSeverityLevel() {
+ val safetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ ).addAction(action1)
+ .build()
+
+ assertThat(safetySourceIssue.severityLevel).isEqualTo(SEVERITY_LEVEL_INFORMATION)
+ }
+
+ @Test
+ fun getIssueCategory_withDefaultBuilder_returnsGeneralCategory() {
+ val safetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ ).addAction(action1)
+ .build()
+
+ assertThat(safetySourceIssue.issueCategory).isEqualTo(ISSUE_CATEGORY_GENERAL)
+ }
+
+ @Test
+ fun getIssueCategory_whenSetExplicitly_returnsIssueCategory() {
+ val safetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ ).addAction(action1)
+ .setIssueCategory(ISSUE_CATEGORY_DEVICE)
+ .build()
+
+ assertThat(safetySourceIssue.issueCategory).isEqualTo(ISSUE_CATEGORY_DEVICE)
+ }
+
+ @Test
+ fun getActions_returnsActions() {
+ val safetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ ).addAction(action1)
+ .addAction(action2)
+ .build()
+
+ assertThat(safetySourceIssue.actions).containsExactly(action1, action2).inOrder()
+ }
+
+ @Test
+ fun clearActions_removesAllActions() {
+ val safetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ ).addAction(action1)
+ .addAction(action2)
+ .clearActions()
+ .addAction(action3)
+ .build()
+
+ assertThat(safetySourceIssue.actions).containsExactly(action3)
+ }
+
+ @Test
+ fun getOnDismissPendingIntent_withDefaultBuilder_returnsNull() {
+ val safetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ ).addAction(action1)
+ .build()
+
+ assertThat(safetySourceIssue.onDismissPendingIntent).isNull()
+ }
+
+ @Test
+ fun getOnDismissPendingIntent_whenSetExplicitly_returnsOnDismissPendingIntent() {
+ val safetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ ).addAction(action1)
+ .setOnDismissPendingIntent(pendingIntent1)
+ .build()
+
+ assertThat(safetySourceIssue.onDismissPendingIntent).isEqualTo(pendingIntent1)
+ }
+
+ @Test
+ fun getIssueTypeId_returnsIssueTypeId() {
+ val safetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ ).addAction(action1)
+ .build()
+
+ assertThat(safetySourceIssue.issueTypeId).isEqualTo("issue_type_id")
+ }
+
+ @Test
+ fun build_withNoActions_throwsIllegalArgumentException() {
+ val safetySourceIssueBuilder = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ )
+ assertThrows(
+ "Safety source issue must contain at least 1 action",
+ IllegalArgumentException::class.java
+ ) { safetySourceIssueBuilder.build() }
+ }
+
+ @Test
+ fun build_withMoreThanTwoActions_throwsIllegalArgumentException() {
+ val safetySourceIssueBuilder = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ ).addAction(action1)
+ .addAction(action2)
+ .addAction(action1)
+
+ assertThrows(
+ "Safety source issue must not contain more than 2 actions",
+ IllegalArgumentException::class.java
+ ) { safetySourceIssueBuilder.build() }
+ }
+
+ @Test
+ fun describeContents_returns0() {
+ val safetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ ).setSubtitle("Issue subtitle")
+ .setIssueCategory(ISSUE_CATEGORY_ACCOUNT)
+ .addAction(action1)
+ .addAction(action2)
+ .setOnDismissPendingIntent(pendingIntent1)
+ .build()
+
+ assertThat(safetySourceIssue.describeContents()).isEqualTo(0)
+ }
+
+ @Test
+ fun createFromParcel_withWriteToParcel_returnsOriginalSafetySourceIssue() {
+ val safetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ ).setSubtitle("Issue subtitle")
+ .setIssueCategory(ISSUE_CATEGORY_ACCOUNT)
+ .addAction(action1)
+ .addAction(action2)
+ .setOnDismissPendingIntent(pendingIntent1)
+ .build()
+
+ val parcel: Parcel = Parcel.obtain()
+ safetySourceIssue.writeToParcel(parcel, 0 /* flags */)
+ parcel.setDataPosition(0)
+ val safetySourceIssueFromParcel: SafetySourceIssue =
+ SafetySourceIssue.CREATOR.createFromParcel(parcel)
+ parcel.recycle()
+
+ assertThat(safetySourceIssueFromParcel).isEqualTo(safetySourceIssue)
+ }
+
+ // TODO(b/208473675): Use `EqualsTester` for testing `hashcode` and `equals`.
+ @Test
+ fun hashCode_equals_toString_withEqualByReferenceSafetySourceIssues_areEqual() {
+ val safetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ ).setSubtitle("Issue subtitle")
+ .setIssueCategory(ISSUE_CATEGORY_ACCOUNT)
+ .addAction(action1)
+ .addAction(action2)
+ .setOnDismissPendingIntent(pendingIntent1)
+ .build()
+ val otherSafetySourceIssue = safetySourceIssue
+
+ assertThat(safetySourceIssue.hashCode()).isEqualTo(otherSafetySourceIssue.hashCode())
+ assertThat(safetySourceIssue).isEqualTo(otherSafetySourceIssue)
+ assertThat(safetySourceIssue.toString()).isEqualTo(otherSafetySourceIssue.toString())
+ }
+
+ @Test
+ fun hashCode_equals_toString_withAllFieldsEqual_areEqual() {
+ val safetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ ).setSubtitle("Issue subtitle")
+ .setIssueCategory(ISSUE_CATEGORY_ACCOUNT)
+ .addAction(
+ Action.Builder("action_id", "Action label 1", pendingIntent1)
+ .setResolving(false)
+ .build()
+ )
+ .setOnDismissPendingIntent(pendingIntent1)
+ .build()
+ val otherSafetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ ).setSubtitle("Issue subtitle")
+ .setIssueCategory(ISSUE_CATEGORY_ACCOUNT)
+ .addAction(
+ Action.Builder("action_id", "Action label 1", pendingIntent1)
+ .setResolving(false)
+ .build()
+ )
+ .setOnDismissPendingIntent(pendingIntent1)
+ .build()
+
+ assertThat(safetySourceIssue.hashCode()).isEqualTo(otherSafetySourceIssue.hashCode())
+ assertThat(safetySourceIssue).isEqualTo(otherSafetySourceIssue)
+ assertThat(safetySourceIssue.toString()).isEqualTo(otherSafetySourceIssue.toString())
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentIds_areNotEqual() {
+ val safetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ ).addAction(action1)
+ .build()
+ val otherSafetySourceIssue = SafetySourceIssue.Builder(
+ "Other issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ ).addAction(action1)
+ .build()
+
+ assertThat(safetySourceIssue.hashCode()).isNotEqualTo(otherSafetySourceIssue.hashCode())
+ assertThat(safetySourceIssue).isNotEqualTo(otherSafetySourceIssue)
+ assertThat(safetySourceIssue.toString()).isNotEqualTo(otherSafetySourceIssue.toString())
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentTitles_areNotEqual() {
+ val safetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ ).addAction(action1)
+ .build()
+ val otherSafetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Other issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ ).addAction(action1)
+ .build()
+
+ assertThat(safetySourceIssue.hashCode()).isNotEqualTo(otherSafetySourceIssue.hashCode())
+ assertThat(safetySourceIssue).isNotEqualTo(otherSafetySourceIssue)
+ assertThat(safetySourceIssue.toString()).isNotEqualTo(otherSafetySourceIssue.toString())
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentSubtitles_areNotEqual() {
+ val safetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ ).setSubtitle("Issue subtitle")
+ .addAction(action1)
+ .build()
+ val otherSafetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ ).setSubtitle("Other issue subtitle")
+ .addAction(action1)
+ .build()
+
+ assertThat(safetySourceIssue.hashCode()).isNotEqualTo(otherSafetySourceIssue.hashCode())
+ assertThat(safetySourceIssue).isNotEqualTo(otherSafetySourceIssue)
+ assertThat(safetySourceIssue.toString()).isNotEqualTo(otherSafetySourceIssue.toString())
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentSummaries_areNotEqual() {
+ val safetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ ).addAction(action1)
+ .build()
+ val otherSafetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Other issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ ).addAction(action1)
+ .build()
+
+ assertThat(safetySourceIssue.hashCode()).isNotEqualTo(otherSafetySourceIssue.hashCode())
+ assertThat(safetySourceIssue).isNotEqualTo(otherSafetySourceIssue)
+ assertThat(safetySourceIssue.toString()).isNotEqualTo(otherSafetySourceIssue.toString())
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentSeverityLevels_areNotEqual() {
+ val safetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ ).addAction(action1)
+ .build()
+ val otherSafetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_CRITICAL_WARNING,
+ "issue_type_id"
+ )
+ .addAction(action1)
+ .build()
+
+ assertThat(safetySourceIssue.hashCode()).isNotEqualTo(otherSafetySourceIssue.hashCode())
+ assertThat(safetySourceIssue).isNotEqualTo(otherSafetySourceIssue)
+ assertThat(safetySourceIssue.toString()).isNotEqualTo(otherSafetySourceIssue.toString())
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentIssueCategories_areNotEqual() {
+ val safetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ )
+ .addAction(action1)
+ .setIssueCategory(ISSUE_CATEGORY_DEVICE)
+ .build()
+ val otherSafetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ )
+ .addAction(action1)
+ .setIssueCategory(ISSUE_CATEGORY_GENERAL)
+ .build()
+
+ assertThat(safetySourceIssue.hashCode()).isNotEqualTo(otherSafetySourceIssue.hashCode())
+ assertThat(safetySourceIssue).isNotEqualTo(otherSafetySourceIssue)
+ assertThat(safetySourceIssue.toString()).isNotEqualTo(otherSafetySourceIssue.toString())
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentActions_areNotEqual() {
+ val safetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ ).addAction(action1)
+ .addAction(action2)
+ .build()
+ val otherSafetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ ).addAction(action2)
+ .addAction(action1)
+ .build()
+
+ assertThat(safetySourceIssue.hashCode()).isNotEqualTo(otherSafetySourceIssue.hashCode())
+ assertThat(safetySourceIssue).isNotEqualTo(otherSafetySourceIssue)
+ assertThat(safetySourceIssue.toString()).isNotEqualTo(otherSafetySourceIssue.toString())
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentOnDismissPendingIntents_areNotEqual() {
+ val safetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ ).addAction(action1)
+ .setOnDismissPendingIntent(pendingIntent1)
+ .build()
+ val otherSafetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ ).addAction(action1)
+ .setOnDismissPendingIntent(pendingIntent2)
+ .build()
+
+ assertThat(safetySourceIssue.hashCode()).isNotEqualTo(otherSafetySourceIssue.hashCode())
+ assertThat(safetySourceIssue).isNotEqualTo(otherSafetySourceIssue)
+ assertThat(safetySourceIssue.toString()).isNotEqualTo(otherSafetySourceIssue.toString())
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentIssueTypeIds_areNotEqual() {
+ val safetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "issue_type_id"
+ ).addAction(action1)
+ .build()
+ val otherSafetySourceIssue = SafetySourceIssue.Builder(
+ "Issue id",
+ "Issue title",
+ "Issue summary",
+ SEVERITY_LEVEL_INFORMATION,
+ "other_issue_type_id"
+ ).addAction(action1)
+ .build()
+
+ assertThat(safetySourceIssue.hashCode()).isNotEqualTo(otherSafetySourceIssue.hashCode())
+ assertThat(safetySourceIssue).isNotEqualTo(otherSafetySourceIssue)
+ assertThat(safetySourceIssue.toString()).isNotEqualTo(otherSafetySourceIssue.toString())
+ }
+} \ No newline at end of file
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceStatusTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceStatusTest.kt
new file mode 100644
index 000000000..b44f6c5d3
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceStatusTest.kt
@@ -0,0 +1,458 @@
+/*
+ * 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.app.PendingIntent
+import android.app.PendingIntent.FLAG_IMMUTABLE
+import android.content.Context
+import android.content.Intent
+import android.os.Build.VERSION_CODES.TIRAMISU
+import android.os.Parcel
+import android.safetycenter.SafetySourceStatus
+import android.safetycenter.SafetySourceStatus.IconAction
+import android.safetycenter.SafetySourceStatus.IconAction.ICON_TYPE_GEAR
+import android.safetycenter.SafetySourceStatus.IconAction.ICON_TYPE_INFO
+import android.safetycenter.SafetySourceStatus.STATUS_LEVEL_CRITICAL_WARNING
+import android.safetycenter.SafetySourceStatus.STATUS_LEVEL_OK
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** CTS tests for [SafetySourceStatus]. */
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = TIRAMISU, codeName = "Tiramisu")
+class SafetySourceStatusTest {
+ private val context: Context = getApplicationContext()
+
+ private val pendingIntent1: PendingIntent = PendingIntent.getActivity(
+ context,
+ 0 /* requestCode= */, Intent("PendingIntent 1"), FLAG_IMMUTABLE
+ )
+ private val iconAction1 = IconAction(ICON_TYPE_INFO, pendingIntent1)
+ private val pendingIntent2: PendingIntent = PendingIntent.getActivity(
+ context,
+ 0 /* requestCode= */, Intent("PendingIntent 2"), FLAG_IMMUTABLE
+ )
+ private val iconAction2 = IconAction(ICON_TYPE_GEAR, pendingIntent2)
+
+ @Test
+ fun iconAction_getIconType_returnsIconType() {
+ val iconAction = IconAction(ICON_TYPE_INFO, pendingIntent1)
+
+ assertThat(iconAction.iconType).isEqualTo(ICON_TYPE_INFO)
+ }
+
+ @Test
+ fun iconAction_getPendingIntent_returnsPendingIntent() {
+ val iconAction = IconAction(ICON_TYPE_GEAR, pendingIntent1)
+
+ assertThat(iconAction.pendingIntent).isEqualTo(pendingIntent1)
+ }
+
+ @Test
+ fun iconAction_describeContents_returns0() {
+ val iconAction = IconAction(ICON_TYPE_GEAR, pendingIntent1)
+
+ assertThat(iconAction.describeContents()).isEqualTo(0)
+ }
+
+ @Test
+ fun iconAction_createFromParcel_withWriteToParcel_returnsOriginalAction() {
+ val iconAction = IconAction(ICON_TYPE_GEAR, pendingIntent1)
+
+ val parcel: Parcel = Parcel.obtain()
+ iconAction.writeToParcel(parcel, 0 /* flags */)
+ parcel.setDataPosition(0)
+ val iconActionFromParcel: IconAction = IconAction.CREATOR.createFromParcel(parcel)
+ parcel.recycle()
+
+ assertThat(iconActionFromParcel).isEqualTo(iconAction)
+ }
+
+ // TODO(b/208473675): Use `EqualsTester` for testing `hashcode` and `equals`.
+ @Test
+ fun iconAction_hashCode_equals_toString_withEqualByReferenceIconActions_areEqual() {
+ val iconAction = IconAction(ICON_TYPE_GEAR, pendingIntent1)
+ val otherIconAction = iconAction
+
+ assertThat(iconAction.hashCode()).isEqualTo(otherIconAction.hashCode())
+ assertThat(iconAction).isEqualTo(otherIconAction)
+ assertThat(iconAction.toString()).isEqualTo(otherIconAction.toString())
+ }
+
+ @Test
+ fun iconAction_hashCode_equals_toString_withAllFieldsEqual_areEqual() {
+ val iconAction = IconAction(ICON_TYPE_GEAR, pendingIntent1)
+ val otherIconAction = IconAction(ICON_TYPE_GEAR, pendingIntent1)
+
+ assertThat(iconAction.hashCode()).isEqualTo(otherIconAction.hashCode())
+ assertThat(iconAction).isEqualTo(otherIconAction)
+ assertThat(iconAction.toString()).isEqualTo(otherIconAction.toString())
+ }
+
+ @Test
+ fun iconAction_hashCode_equals_toString_withDifferentIconTypes_areNotEqual() {
+ val iconAction = IconAction(ICON_TYPE_GEAR, pendingIntent1)
+ val otherIconAction = IconAction(ICON_TYPE_INFO, pendingIntent1)
+
+ assertThat(iconAction.hashCode()).isNotEqualTo(otherIconAction.hashCode())
+ assertThat(iconAction).isNotEqualTo(otherIconAction)
+ assertThat(iconAction.toString()).isNotEqualTo(otherIconAction.toString())
+ }
+
+ @Test
+ fun iconAction_hashCode_equals_toString_withDifferentPendingIntents_areNotEqual() {
+ val iconAction = IconAction(ICON_TYPE_GEAR, pendingIntent1)
+ val otherIconAction = IconAction(ICON_TYPE_GEAR, pendingIntent2)
+
+ assertThat(iconAction.hashCode()).isNotEqualTo(otherIconAction.hashCode())
+ assertThat(iconAction).isNotEqualTo(otherIconAction)
+ assertThat(iconAction.toString()).isNotEqualTo(otherIconAction.toString())
+ }
+
+ @Test
+ fun getTitle_returnsTitle() {
+ val safetySourceStatus = SafetySourceStatus.Builder(
+ "Status title",
+ "Status summary",
+ STATUS_LEVEL_OK,
+ pendingIntent1
+ )
+ .build()
+
+ assertThat(safetySourceStatus.title).isEqualTo("Status title")
+ }
+
+ @Test
+ fun getSummary_returnsSummary() {
+ val safetySourceStatus = SafetySourceStatus.Builder(
+ "Status title",
+ "Status summary",
+ STATUS_LEVEL_OK,
+ pendingIntent1
+ )
+ .build()
+
+ assertThat(safetySourceStatus.summary).isEqualTo("Status summary")
+ }
+
+ @Test
+ fun getStatusLevel_returnsStatusLevel() {
+ val safetySourceStatus = SafetySourceStatus.Builder(
+ "Status title",
+ "Status summary",
+ STATUS_LEVEL_OK,
+ pendingIntent1
+ )
+ .build()
+
+ assertThat(safetySourceStatus.statusLevel).isEqualTo(STATUS_LEVEL_OK)
+ }
+
+ @Test
+ fun getPendingIntent_returnsPendingIntent() {
+ val safetySourceStatus = SafetySourceStatus.Builder(
+ "Status title",
+ "Status summary",
+ STATUS_LEVEL_OK,
+ pendingIntent1
+ )
+ .build()
+
+ assertThat(safetySourceStatus.pendingIntent).isEqualTo(pendingIntent1)
+ }
+
+ @Test
+ fun getIconAction_withDefaultBuilder_returnsNull() {
+ val safetySourceStatus = SafetySourceStatus.Builder(
+ "Status title",
+ "Status summary",
+ STATUS_LEVEL_OK,
+ pendingIntent1
+ )
+ .build()
+
+ assertThat(safetySourceStatus.iconAction).isNull()
+ }
+
+ @Test
+ fun getIconAction_whenSetExplicitly_returnsIconAction() {
+ val safetySourceStatus = SafetySourceStatus.Builder(
+ "Status title",
+ "Status summary",
+ STATUS_LEVEL_OK,
+ pendingIntent1
+ )
+ .setIconAction(iconAction1)
+ .build()
+
+ assertThat(safetySourceStatus.iconAction).isEqualTo(iconAction1)
+ }
+
+ @Test
+ fun isEnabled_withDefaultBuilder_returnsTrue() {
+ val safetySourceStatus = SafetySourceStatus.Builder(
+ "Status title",
+ "Status summary",
+ STATUS_LEVEL_OK,
+ pendingIntent1
+ )
+ .build()
+
+ assertThat(safetySourceStatus.isEnabled).isTrue()
+ }
+
+ @Test
+ fun isEnabled_whenSetExplicitly_returnsEnabled() {
+ val safetySourceStatus = SafetySourceStatus.Builder(
+ "Status title",
+ "Status summary",
+ STATUS_LEVEL_OK,
+ pendingIntent1
+ )
+ .setEnabled(false)
+ .build()
+
+ assertThat(safetySourceStatus.isEnabled).isFalse()
+ }
+
+ @Test
+ fun describeContents_returns0() {
+ val safetySourceStatus = SafetySourceStatus.Builder(
+ "Status title",
+ "Status summary",
+ STATUS_LEVEL_OK,
+ pendingIntent1
+ )
+ .setIconAction(iconAction1)
+ .build()
+
+ assertThat(safetySourceStatus.describeContents()).isEqualTo(0)
+ }
+
+ @Test
+ fun createFromParcel_withWriteToParcel_returnsOriginalSafetySourceStatus() {
+ val safetySourceStatus = SafetySourceStatus.Builder(
+ "Status title",
+ "Status summary",
+ STATUS_LEVEL_OK,
+ pendingIntent1
+ )
+ .setIconAction(iconAction1)
+ .setEnabled(true)
+ .build()
+
+ val parcel: Parcel = Parcel.obtain()
+ safetySourceStatus.writeToParcel(parcel, 0 /* flags */)
+ parcel.setDataPosition(0)
+ val safetySourceStatusFromParcel: SafetySourceStatus =
+ SafetySourceStatus.CREATOR.createFromParcel(parcel)
+ parcel.recycle()
+
+ assertThat(safetySourceStatusFromParcel).isEqualTo(safetySourceStatus)
+ }
+
+ // TODO(b/208473675): Use `EqualsTester` for testing `hashcode` and `equals`.
+ @Test
+ fun hashCode_equals_toString_withEqualByReferenceSafetySourceStatuses_areEqual() {
+ val safetySourceStatus = SafetySourceStatus.Builder(
+ "Status title",
+ "Status summary",
+ STATUS_LEVEL_OK,
+ pendingIntent1
+ )
+ .setIconAction(iconAction1)
+ .setEnabled(true)
+ .build()
+ val otherSafetySourceStatus = safetySourceStatus
+
+ assertThat(safetySourceStatus.hashCode()).isEqualTo(otherSafetySourceStatus.hashCode())
+ assertThat(safetySourceStatus).isEqualTo(otherSafetySourceStatus)
+ assertThat(safetySourceStatus.toString()).isEqualTo(otherSafetySourceStatus.toString())
+ }
+
+ @Test
+ fun hashCode_equals_toString_withAllFieldsEqual_areEqual() {
+ val safetySourceStatus = SafetySourceStatus.Builder(
+ "Status title",
+ "Status summary",
+ STATUS_LEVEL_OK,
+ pendingIntent1
+ )
+ .setIconAction(iconAction1)
+ .setEnabled(true)
+ .build()
+ val otherSafetySourceStatus = SafetySourceStatus.Builder(
+ "Status title",
+ "Status summary",
+ STATUS_LEVEL_OK,
+ pendingIntent1
+ )
+ .setIconAction(iconAction1)
+ .setEnabled(true)
+ .build()
+
+ assertThat(safetySourceStatus.hashCode()).isEqualTo(otherSafetySourceStatus.hashCode())
+ assertThat(safetySourceStatus).isEqualTo(otherSafetySourceStatus)
+ assertThat(safetySourceStatus.toString()).isEqualTo(otherSafetySourceStatus.toString())
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentTitles_areNotEqual() {
+ val safetySourceStatus = SafetySourceStatus.Builder(
+ "Status title",
+ "Status summary",
+ STATUS_LEVEL_OK,
+ pendingIntent1
+ )
+ .build()
+ val otherSafetySourceStatus = SafetySourceStatus.Builder(
+ "Other status title",
+ "Status summary",
+ STATUS_LEVEL_OK,
+ pendingIntent1
+ )
+ .build()
+
+ assertThat(safetySourceStatus.hashCode()).isNotEqualTo(otherSafetySourceStatus.hashCode())
+ assertThat(safetySourceStatus).isNotEqualTo(otherSafetySourceStatus)
+ assertThat(safetySourceStatus.toString()).isNotEqualTo(otherSafetySourceStatus.toString())
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentSummaries_areNotEqual() {
+ val safetySourceStatus = SafetySourceStatus.Builder(
+ "Status title",
+ "Status summary",
+ STATUS_LEVEL_OK,
+ pendingIntent1
+ )
+ .build()
+ val otherSafetySourceStatus = SafetySourceStatus.Builder(
+ "Status title",
+ "Other status summary",
+ STATUS_LEVEL_OK,
+ pendingIntent1
+ )
+ .build()
+
+ assertThat(safetySourceStatus.hashCode()).isNotEqualTo(otherSafetySourceStatus.hashCode())
+ assertThat(safetySourceStatus).isNotEqualTo(otherSafetySourceStatus)
+ assertThat(safetySourceStatus.toString()).isNotEqualTo(otherSafetySourceStatus.toString())
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentStatusLevels_areNotEqual() {
+ val safetySourceStatus = SafetySourceStatus.Builder(
+ "Status title",
+ "Status summary",
+ STATUS_LEVEL_OK,
+ pendingIntent1
+ )
+ .build()
+ val otherSafetySourceStatus = SafetySourceStatus.Builder(
+ "Status title",
+ "Status summary",
+ STATUS_LEVEL_CRITICAL_WARNING,
+ pendingIntent1
+ )
+ .build()
+
+ assertThat(safetySourceStatus.hashCode()).isNotEqualTo(otherSafetySourceStatus.hashCode())
+ assertThat(safetySourceStatus).isNotEqualTo(otherSafetySourceStatus)
+ assertThat(safetySourceStatus.toString()).isNotEqualTo(otherSafetySourceStatus.toString())
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentPendingIntents_areNotEqual() {
+ val safetySourceStatus = SafetySourceStatus.Builder(
+ "Status title",
+ "Status summary",
+ STATUS_LEVEL_OK,
+ PendingIntent.getActivity(
+ context, 0 /* requestCode= */,
+ Intent("Status PendingIntent"), FLAG_IMMUTABLE
+ )
+ )
+ .build()
+ val otherSafetySourceStatus = SafetySourceStatus.Builder(
+ "Status title",
+ "Status summary",
+ STATUS_LEVEL_CRITICAL_WARNING,
+ PendingIntent.getActivity(
+ context, 0 /* requestCode= */,
+ Intent("Other status PendingIntent"), FLAG_IMMUTABLE
+ )
+ )
+ .build()
+
+ assertThat(safetySourceStatus.hashCode()).isNotEqualTo(otherSafetySourceStatus.hashCode())
+ assertThat(safetySourceStatus).isNotEqualTo(otherSafetySourceStatus)
+ assertThat(safetySourceStatus.toString()).isNotEqualTo(otherSafetySourceStatus.toString())
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentIconActions_areNotEqual() {
+ val safetySourceStatus = SafetySourceStatus.Builder(
+ "Status title",
+ "Status summary",
+ STATUS_LEVEL_CRITICAL_WARNING,
+ pendingIntent1
+ )
+ .setIconAction(iconAction1)
+ .build()
+ val otherSafetySourceStatus = SafetySourceStatus.Builder(
+ "Status title",
+ "Status summary",
+ STATUS_LEVEL_CRITICAL_WARNING,
+ pendingIntent1
+ )
+ .setIconAction(iconAction2)
+ .build()
+
+ assertThat(safetySourceStatus.hashCode()).isNotEqualTo(otherSafetySourceStatus.hashCode())
+ assertThat(safetySourceStatus).isNotEqualTo(otherSafetySourceStatus)
+ assertThat(safetySourceStatus.toString()).isNotEqualTo(otherSafetySourceStatus.toString())
+ }
+
+ @Test
+ fun hashCode_equals_toString_withDifferentEnabled_areNotEqual() {
+ val safetySourceStatus = SafetySourceStatus.Builder(
+ "Status title",
+ "Status summary",
+ STATUS_LEVEL_CRITICAL_WARNING,
+ pendingIntent1
+ )
+ .setEnabled(true)
+ .build()
+ val otherSafetySourceStatus = SafetySourceStatus.Builder(
+ "Status title",
+ "Status summary",
+ STATUS_LEVEL_CRITICAL_WARNING,
+ pendingIntent1
+ )
+ .setEnabled(false)
+ .build()
+
+ assertThat(safetySourceStatus.hashCode()).isNotEqualTo(otherSafetySourceStatus.hashCode())
+ assertThat(safetySourceStatus).isNotEqualTo(otherSafetySourceStatus)
+ assertThat(safetySourceStatus.toString()).isNotEqualTo(otherSafetySourceStatus.toString())
+ }
+} \ No newline at end of file
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/XmlConfigTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/XmlConfigTest.kt
new file mode 100644
index 000000000..9b77e994e
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/XmlConfigTest.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.cts
+
+import android.os.Build.VERSION_CODES.TIRAMISU
+import android.safetycenter.config.SafetyCenterConfig
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import com.android.safetycenter.resources.SafetyCenterResourcesContext
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = TIRAMISU, codeName = "Tiramisu")
+class XmlConfigTest {
+ private val safetyCenterContext = SafetyCenterResourcesContext(getApplicationContext())
+
+ @Test
+ fun safetyCenterConfigResource_validConfig() {
+ // Assert that the parser validates the Safety Center config without throwing any exception
+ assertThat(SafetyCenterConfig.fromXml(safetyCenterContext.safetyCenterConfig!!)).isNotNull()
+ }
+}
diff --git a/tests/cts/safetycenter/src/android/safetycenter/testers/AnyTester.kt b/tests/cts/safetycenter/src/android/safetycenter/testers/AnyTester.kt
new file mode 100644
index 000000000..f3fbf77a8
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/testers/AnyTester.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.testers
+
+import com.google.common.truth.Truth.assertThat
+
+/** Collection of functions to test generic objects */
+object AnyTester {
+ /**
+ * Asserts that two generic objects are equal and that the values returned by the [hashCode] and
+ * [toString] methods for the two generic objects are also equal.
+ */
+ fun assertThatRepresentationsAreEqual(a: Any, b: Any) {
+ assertThat(a.hashCode()).isEqualTo(b.hashCode())
+ assertThat(a).isEqualTo(b)
+ assertThat(a.toString()).isEqualTo(b.toString())
+ }
+
+ /**
+ * Asserts that two generic objects are not equal and that the values returned by the [hashCode]
+ * and [toString] methods for the two generic objects are also not equal.
+ */
+ fun assertThatRepresentationsAreNotEqual(a: Any, b: Any) {
+ assertThat(a.hashCode()).isNotEqualTo(b.hashCode())
+ assertThat(a).isNotEqualTo(b)
+ assertThat(a.toString()).isNotEqualTo(b.toString())
+ }
+} \ No newline at end of file
diff --git a/tests/cts/safetycenter/src/android/safetycenter/testers/ParcelableTester.kt b/tests/cts/safetycenter/src/android/safetycenter/testers/ParcelableTester.kt
new file mode 100644
index 000000000..f2c2482a1
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/testers/ParcelableTester.kt
@@ -0,0 +1,25 @@
+package android.safetycenter.testers
+
+import android.os.Parcel
+import android.os.Parcelable
+import com.google.common.truth.Truth.assertThat
+
+/** Collection of functions to test [Parcelable] objects */
+object ParcelableTester {
+ /**
+ * Asserts that writing a [Parcelable] object to a [Parcel] and creating an object from that
+ * [Parcel] returns an object that is equal to the original [Parcelable] object.
+ */
+ fun <T : Parcelable> assertThatRoundTripReturnsOriginal(
+ parcelable: T,
+ creator: Parcelable.Creator<T>
+ ) {
+ val parcel: Parcel = Parcel.obtain()
+ parcelable.writeToParcel(parcel, 0)
+ parcel.setDataPosition(0)
+ val parcelableFromParcel: T = creator.createFromParcel(parcel)
+ parcel.recycle()
+
+ assertThat(parcelableFromParcel).isEqualTo(parcelable)
+ }
+} \ No newline at end of file