diff options
| -rw-r--r-- | packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt | 125 | ||||
| -rw-r--r-- | packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt | 68 |
2 files changed, 171 insertions, 22 deletions
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 ff579a1cc4aa..318f2bc1c227 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 @@ class FakeFeatureFlagsTest : SysuiTestCase() { * 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 @@ class FakeFeatureFlagsTest : SysuiTestCase() { } @Test - fun specifiedFlagsReturnCorrectValues() { + fun specifiedFlags_returnCorrectValues() { val flags = FakeFeatureFlags() flags.set(unreleasedFlag, false) flags.set(releasedFlag, false) @@ -114,4 +114,125 @@ class FakeFeatureFlagsTest : SysuiTestCase() { 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 b53ad0a3726f..c56fdb17b5f1 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 @@ class FakeFeatureFlags : FeatureFlags { } 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 @@ class FakeFeatureFlags : FeatureFlags { 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.") } } |