diff options
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 |