Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eb40375d authored by Mark Renouf's avatar Mark Renouf Committed by Automerger Merge Worker
Browse files

Merge "Adds listener support to FakeFeatureFlags" into tm-qpr-dev am: 34fdbdb5

parents 4405dd01 34fdbdb5
Loading
Loading
Loading
Loading
+123 −2
Original line number Diff line number Diff line
@@ -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())
        }
    }
}
+48 −20
Original line number Diff line number Diff line
@@ -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.")
    }
}