Merge "Adds listener support to FakeFeatureFlags" into tm-qpr-dev
This commit is contained in:
@@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user