diff options
| author | 2024-03-05 12:37:28 +0000 | |
|---|---|---|
| committer | 2024-03-05 12:37:28 +0000 | |
| commit | daaed65588353e08fed115e842d9a615e34a3e78 (patch) | |
| tree | 6a1d7d94a7f875f0b1e0c5e167a01f9c48788b14 | |
| parent | 4e62d6c3e69e9324a15c5b83f6bb3102b9b708dc (diff) | |
| parent | f5eeba5011410f07fe6efe5c2900f16ca13e9e30 (diff) | |
Merge "[DataStore] Add UT for Observer" into main
4 files changed, 140 insertions, 0 deletions
diff --git a/packages/SettingsLib/DataStore/tests/Android.bp b/packages/SettingsLib/DataStore/tests/Android.bp new file mode 100644 index 000000000000..8770dfa013d0 --- /dev/null +++ b/packages/SettingsLib/DataStore/tests/Android.bp @@ -0,0 +1,24 @@ +package { + default_applicable_licenses: ["frameworks_base_license"], +} + +android_app { + name: "SettingsLibDataStoreShell", + platform_apis: true, +} + +android_robolectric_test { + name: "SettingsLibDataStoreTest", + srcs: ["src/**/*"], + static_libs: [ + "SettingsLibDataStore", + "androidx.test.ext.junit", + "guava", + "mockito-robolectric-prebuilt", // mockito deps order matters! + "mockito-kotlin2", + ], + java_resource_dirs: ["config"], + instrumentation_for: "SettingsLibDataStoreShell", + coverage_libs: ["SettingsLibDataStore"], + upstream: true, +} diff --git a/packages/SettingsLib/DataStore/tests/AndroidManifest.xml b/packages/SettingsLib/DataStore/tests/AndroidManifest.xml new file mode 100644 index 000000000000..ffc24e4330d1 --- /dev/null +++ b/packages/SettingsLib/DataStore/tests/AndroidManifest.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.settingslib.datastore.test"> + + <application android:debuggable="true" /> +</manifest> diff --git a/packages/SettingsLib/DataStore/tests/config/robolectric.properties b/packages/SettingsLib/DataStore/tests/config/robolectric.properties new file mode 100644 index 000000000000..fab7251d020b --- /dev/null +++ b/packages/SettingsLib/DataStore/tests/config/robolectric.properties @@ -0,0 +1 @@ +sdk=NEWEST_SDK diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt new file mode 100644 index 000000000000..bb791dc9a23c --- /dev/null +++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2024 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 com.android.settingslib.datastore + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat +import com.google.common.util.concurrent.MoreExecutors +import java.util.concurrent.Executor +import java.util.concurrent.atomic.AtomicInteger +import org.junit.Assert.assertThrows +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule +import org.mockito.kotlin.any +import org.mockito.kotlin.never +import org.mockito.kotlin.reset +import org.mockito.kotlin.verify + +@RunWith(AndroidJUnit4::class) +class ObserverTest { + @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() + + @Mock private lateinit var observer1: Observer + + @Mock private lateinit var observer2: Observer + + @Mock private lateinit var executor: Executor + + private val observable = DataObservable() + + @Test + fun addObserver_sameExecutor() { + observable.addObserver(observer1, executor) + observable.addObserver(observer1, executor) + } + + @Test + fun addObserver_differentExecutor() { + observable.addObserver(observer1, executor) + assertThrows(IllegalStateException::class.java) { + observable.addObserver(observer1, MoreExecutors.directExecutor()) + } + } + + @Test + fun addObserver_weaklyReferenced() { + val counter = AtomicInteger() + var observer: Observer? = Observer { counter.incrementAndGet() } + observable.addObserver(observer!!, MoreExecutors.directExecutor()) + + observable.notifyChange(ChangeReason.UPDATE) + assertThat(counter.get()).isEqualTo(1) + + // trigger GC, the observer callback should not be invoked + @Suppress("unused") + observer = null + System.gc() + System.runFinalization() + + observable.notifyChange(ChangeReason.UPDATE) + assertThat(counter.get()).isEqualTo(1) + } + + @Test + fun addObserver_notifyObservers_removeObserver() { + observable.addObserver(observer1, MoreExecutors.directExecutor()) + observable.addObserver(observer2, executor) + + observable.notifyChange(ChangeReason.DELETE) + + verify(observer1).onChanged(ChangeReason.DELETE) + verify(observer2, never()).onChanged(any()) + verify(executor).execute(any()) + + reset(observer1, executor) + observable.removeObserver(observer2) + + observable.notifyChange(ChangeReason.UPDATE) + verify(observer1).onChanged(ChangeReason.UPDATE) + verify(executor, never()).execute(any()) + } + + @Test + fun notifyChange_addObserverWithinCallback() { + // ConcurrentModificationException is raised if it is not implemented correctly + observable.addObserver( + { observable.addObserver(observer1, executor) }, + MoreExecutors.directExecutor() + ) + observable.notifyChange(ChangeReason.UPDATE) + } +} |