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.
|
* specified. If not, an exception is thrown.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun throwsIfUnspecifiedFlagIsAccessed() {
|
fun accessingUnspecifiedFlags_throwsException() {
|
||||||
val flags: FeatureFlags = FakeFeatureFlags()
|
val flags: FeatureFlags = FakeFeatureFlags()
|
||||||
try {
|
try {
|
||||||
assertThat(flags.isEnabled(Flags.TEAMFOOD)).isFalse()
|
assertThat(flags.isEnabled(Flags.TEAMFOOD)).isFalse()
|
||||||
@@ -88,7 +88,7 @@ class FakeFeatureFlagsTest : SysuiTestCase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun specifiedFlagsReturnCorrectValues() {
|
fun specifiedFlags_returnCorrectValues() {
|
||||||
val flags = FakeFeatureFlags()
|
val flags = FakeFeatureFlags()
|
||||||
flags.set(unreleasedFlag, false)
|
flags.set(unreleasedFlag, false)
|
||||||
flags.set(releasedFlag, false)
|
flags.set(releasedFlag, false)
|
||||||
@@ -114,4 +114,125 @@ class FakeFeatureFlagsTest : SysuiTestCase() {
|
|||||||
assertThat(flags.isEnabled(sysPropBooleanFlag)).isTrue()
|
assertThat(flags.isEnabled(sysPropBooleanFlag)).isTrue()
|
||||||
assertThat(flags.getString(resourceStringFlag)).isEqualTo("Android")
|
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
|
package com.android.systemui.flags
|
||||||
|
|
||||||
import android.util.SparseArray
|
|
||||||
import android.util.SparseBooleanArray
|
|
||||||
import androidx.core.util.containsKey
|
|
||||||
|
|
||||||
class FakeFeatureFlags : FeatureFlags {
|
class FakeFeatureFlags : FeatureFlags {
|
||||||
private val booleanFlags = SparseBooleanArray()
|
private val booleanFlags = mutableMapOf<Int, Boolean>()
|
||||||
private val stringFlags = SparseArray<String>()
|
private val stringFlags = mutableMapOf<Int, String>()
|
||||||
private val knownFlagNames = 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 {
|
init {
|
||||||
Flags.getFlagFields().forEach { field ->
|
Flags.getFlagFields().forEach { field ->
|
||||||
@@ -33,27 +31,52 @@ class FakeFeatureFlags : FeatureFlags {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun set(flag: BooleanFlag, value: Boolean) {
|
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) {
|
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) {
|
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) {
|
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) {
|
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) {
|
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)
|
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 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 {
|
private fun flagName(flagId: Int): String {
|
||||||
return knownFlagNames[flagId] ?: "UNKNOWN(id=$flagId)"
|
return knownFlagNames[flagId] ?: "UNKNOWN(id=$flagId)"
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun requireBooleanValue(flagId: Int): Boolean {
|
private fun requireBooleanValue(flagId: Int): Boolean {
|
||||||
if (!booleanFlags.containsKey(flagId)) {
|
|
||||||
throw IllegalStateException("Flag ${flagName(flagId)} was accessed but not specified.")
|
|
||||||
}
|
|
||||||
return booleanFlags[flagId]
|
return booleanFlags[flagId]
|
||||||
|
?: error("Flag ${flagName(flagId)} was accessed but not specified.")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun requireStringValue(flagId: Int): String {
|
private fun requireStringValue(flagId: Int): String {
|
||||||
if (!stringFlags.containsKey(flagId)) {
|
|
||||||
throw IllegalStateException("Flag ${flagName(flagId)} was accessed but not specified.")
|
|
||||||
}
|
|
||||||
return stringFlags[flagId]
|
return stringFlags[flagId]
|
||||||
|
?: error("Flag ${flagName(flagId)} was accessed but not specified.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user