Merge "Adds listener support to FakeFeatureFlags" into tm-qpr-dev am: 34fdbdb544
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/19763934
Change-Id: Ie02e30fd5f2ebc251a46092db81d17691678ede9
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
index ff579a1..318f2bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
@@ -41,7 +41,7 @@
* specified. If not, an exception is thrown.
*/
@Test
- fun throwsIfUnspecifiedFlagIsAccessed() {
+ fun accessingUnspecifiedFlags_throwsException() {
val flags: FeatureFlags = FakeFeatureFlags()
try {
assertThat(flags.isEnabled(Flags.TEAMFOOD)).isFalse()
@@ -88,7 +88,7 @@
}
@Test
- fun specifiedFlagsReturnCorrectValues() {
+ fun specifiedFlags_returnCorrectValues() {
val flags = FakeFeatureFlags()
flags.set(unreleasedFlag, false)
flags.set(releasedFlag, false)
@@ -114,4 +114,125 @@
assertThat(flags.isEnabled(sysPropBooleanFlag)).isTrue()
assertThat(flags.getString(resourceStringFlag)).isEqualTo("Android")
}
+
+ @Test
+ fun listenerForBooleanFlag_calledOnlyWhenFlagChanged() {
+ val flags = FakeFeatureFlags()
+ val listener = VerifyingListener()
+ flags.addListener(unreleasedFlag, listener)
+
+ flags.set(unreleasedFlag, true)
+ flags.set(unreleasedFlag, true)
+ flags.set(unreleasedFlag, false)
+ flags.set(unreleasedFlag, false)
+
+ listener.verifyInOrder(unreleasedFlag.id, unreleasedFlag.id)
+ }
+
+ @Test
+ fun listenerForStringFlag_calledOnlyWhenFlagChanged() {
+ val flags = FakeFeatureFlags()
+ val listener = VerifyingListener()
+ flags.addListener(stringFlag, listener)
+
+ flags.set(stringFlag, "Test")
+ flags.set(stringFlag, "Test")
+
+ listener.verifyInOrder(stringFlag.id)
+ }
+
+ @Test
+ fun listenerForBooleanFlag_notCalledAfterRemoved() {
+ val flags = FakeFeatureFlags()
+ val listener = VerifyingListener()
+ flags.addListener(unreleasedFlag, listener)
+ flags.set(unreleasedFlag, true)
+ flags.removeListener(listener)
+ flags.set(unreleasedFlag, false)
+
+ listener.verifyInOrder(unreleasedFlag.id)
+ }
+
+ @Test
+ fun listenerForStringFlag_notCalledAfterRemoved() {
+ val flags = FakeFeatureFlags()
+ val listener = VerifyingListener()
+
+ flags.addListener(stringFlag, listener)
+ flags.set(stringFlag, "Test")
+ flags.removeListener(listener)
+ flags.set(stringFlag, "Other")
+
+ listener.verifyInOrder(stringFlag.id)
+ }
+
+ @Test
+ fun listenerForMultipleFlags_calledWhenFlagsChange() {
+ val flags = FakeFeatureFlags()
+ val listener = VerifyingListener()
+ flags.addListener(unreleasedFlag, listener)
+ flags.addListener(releasedFlag, listener)
+
+ flags.set(releasedFlag, true)
+ flags.set(unreleasedFlag, true)
+
+ listener.verifyInOrder(releasedFlag.id, unreleasedFlag.id)
+ }
+
+ @Test
+ fun listenerForMultipleFlags_notCalledAfterRemoved() {
+ val flags = FakeFeatureFlags()
+ val listener = VerifyingListener()
+
+ flags.addListener(unreleasedFlag, listener)
+ flags.addListener(releasedFlag, listener)
+ flags.set(releasedFlag, true)
+ flags.set(unreleasedFlag, true)
+ flags.removeListener(listener)
+ flags.set(releasedFlag, false)
+ flags.set(unreleasedFlag, false)
+
+ listener.verifyInOrder(releasedFlag.id, unreleasedFlag.id)
+ }
+
+ @Test
+ fun multipleListenersForSingleFlag_allAreCalledWhenChanged() {
+ val flags = FakeFeatureFlags()
+ val listener1 = VerifyingListener()
+ val listener2 = VerifyingListener()
+ flags.addListener(releasedFlag, listener1)
+ flags.addListener(releasedFlag, listener2)
+
+ flags.set(releasedFlag, true)
+
+ listener1.verifyInOrder(releasedFlag.id)
+ listener2.verifyInOrder(releasedFlag.id)
+ }
+
+ @Test
+ fun multipleListenersForSingleFlag_removedListenerNotCalledAfterRemoval() {
+ val flags = FakeFeatureFlags()
+ val listener1 = VerifyingListener()
+ val listener2 = VerifyingListener()
+ flags.addListener(releasedFlag, listener1)
+ flags.addListener(releasedFlag, listener2)
+
+ flags.set(releasedFlag, true)
+ flags.removeListener(listener2)
+ flags.set(releasedFlag, false)
+
+ listener1.verifyInOrder(releasedFlag.id, releasedFlag.id)
+ listener2.verifyInOrder(releasedFlag.id)
+ }
+
+ class VerifyingListener : FlagListenable.Listener {
+ var flagEventIds = mutableListOf<Int>()
+ override fun onFlagChanged(event: FlagListenable.FlagEvent) {
+ flagEventIds.add(event.flagId)
+ }
+
+ fun verifyInOrder(vararg eventIds: Int) {
+ assertThat(flagEventIds).containsExactlyElementsIn(eventIds.asList())
+ }
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
index b53ad0a..c56fdb1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
@@ -16,14 +16,12 @@
package com.android.systemui.flags
-import android.util.SparseArray
-import android.util.SparseBooleanArray
-import androidx.core.util.containsKey
-
class FakeFeatureFlags : FeatureFlags {
- private val booleanFlags = SparseBooleanArray()
- private val stringFlags = SparseArray<String>()
+ private val booleanFlags = mutableMapOf<Int, Boolean>()
+ private val stringFlags = mutableMapOf<Int, String>()
private val knownFlagNames = mutableMapOf<Int, String>()
+ private val flagListeners = mutableMapOf<Int, MutableSet<FlagListenable.Listener>>()
+ private val listenerFlagIds = mutableMapOf<FlagListenable.Listener, MutableSet<Int>>()
init {
Flags.getFlagFields().forEach { field ->
@@ -33,27 +31,52 @@
}
fun set(flag: BooleanFlag, value: Boolean) {
- booleanFlags.put(flag.id, value)
+ if (booleanFlags.put(flag.id, value)?.let { value != it } != false) {
+ notifyFlagChanged(flag)
+ }
}
fun set(flag: DeviceConfigBooleanFlag, value: Boolean) {
- booleanFlags.put(flag.id, value)
+ if (booleanFlags.put(flag.id, value)?.let { value != it } != false) {
+ notifyFlagChanged(flag)
+ }
}
fun set(flag: ResourceBooleanFlag, value: Boolean) {
- booleanFlags.put(flag.id, value)
+ if (booleanFlags.put(flag.id, value)?.let { value != it } != false) {
+ notifyFlagChanged(flag)
+ }
}
fun set(flag: SysPropBooleanFlag, value: Boolean) {
- booleanFlags.put(flag.id, value)
+ if (booleanFlags.put(flag.id, value)?.let { value != it } != false) {
+ notifyFlagChanged(flag)
+ }
}
fun set(flag: StringFlag, value: String) {
- stringFlags.put(flag.id, value)
+ if (stringFlags.put(flag.id, value)?.let { value != it } == null) {
+ notifyFlagChanged(flag)
+ }
}
fun set(flag: ResourceStringFlag, value: String) {
- stringFlags.put(flag.id, value)
+ if (stringFlags.put(flag.id, value)?.let { value != it } == null) {
+ notifyFlagChanged(flag)
+ }
+ }
+
+ private fun notifyFlagChanged(flag: Flag<*>) {
+ flagListeners[flag.id]?.let { listeners ->
+ listeners.forEach { listener ->
+ listener.onFlagChanged(
+ object : FlagListenable.FlagEvent {
+ override val flagId = flag.id
+ override fun requestNoRestart() {}
+ }
+ )
+ }
+ }
}
override fun isEnabled(flag: UnreleasedFlag): Boolean = requireBooleanValue(flag.id)
@@ -70,25 +93,30 @@
override fun getString(flag: ResourceStringFlag): String = requireStringValue(flag.id)
- override fun addListener(flag: Flag<*>, listener: FlagListenable.Listener) {}
+ override fun addListener(flag: Flag<*>, listener: FlagListenable.Listener) {
+ flagListeners.getOrPut(flag.id) { mutableSetOf() }.add(listener)
+ listenerFlagIds.getOrPut(listener) { mutableSetOf() }.add(flag.id)
+ }
- override fun removeListener(listener: FlagListenable.Listener) {}
+ override fun removeListener(listener: FlagListenable.Listener) {
+ listenerFlagIds.remove(listener)?.let {
+ flagIds -> flagIds.forEach {
+ id -> flagListeners[id]?.remove(listener)
+ }
+ }
+ }
private fun flagName(flagId: Int): String {
return knownFlagNames[flagId] ?: "UNKNOWN(id=$flagId)"
}
private fun requireBooleanValue(flagId: Int): Boolean {
- if (!booleanFlags.containsKey(flagId)) {
- throw IllegalStateException("Flag ${flagName(flagId)} was accessed but not specified.")
- }
return booleanFlags[flagId]
+ ?: error("Flag ${flagName(flagId)} was accessed but not specified.")
}
private fun requireStringValue(flagId: Int): String {
- if (!stringFlags.containsKey(flagId)) {
- throw IllegalStateException("Flag ${flagName(flagId)} was accessed but not specified.")
- }
return stringFlags[flagId]
+ ?: error("Flag ${flagName(flagId)} was accessed but not specified.")
}
}