xref: /aosp_15_r20/frameworks/base/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1 /*
<lambda>null2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.flags
18 
19 import dagger.Binds
20 import dagger.Module
21 import dagger.Provides
22 import java.io.PrintWriter
23 
24 class FakeFeatureFlagsClassic : FakeFeatureFlags()
25 
26 val FeatureFlagsClassic.fake
27     get() = this as FakeFeatureFlagsClassic
28 
29 @Deprecated(
30     message = "Use FakeFeatureFlagsClassic instead.",
31     replaceWith =
32         ReplaceWith("FakeFeatureFlagsClassic", "com.android.systemui.flags.FakeFeatureFlagsClassic"),
33 )
34 open class FakeFeatureFlags : FeatureFlagsClassic {
35     private val booleanFlags = mutableMapOf<String, Boolean>()
36     private val stringFlags = mutableMapOf<String, String>()
37     private val intFlags = mutableMapOf<String, Int>()
38     private val knownFlagNames = mutableMapOf<String, String>()
39     private val flagListeners = mutableMapOf<String, MutableSet<FlagListenable.Listener>>()
40     private val listenerflagNames = mutableMapOf<FlagListenable.Listener, MutableSet<String>>()
41 
42     init {
43         FlagsFactory.knownFlags.forEach { entry: Map.Entry<String, Flag<*>> ->
44             knownFlagNames[entry.value.name] = entry.key
45         }
46     }
47 
48     fun set(flag: BooleanFlag, value: Boolean) {
49         if (booleanFlags.put(flag.name, value)?.let { value != it } != false) {
50             notifyFlagChanged(flag)
51         }
52     }
53 
54     fun set(flag: ResourceBooleanFlag, value: Boolean) {
55         if (booleanFlags.put(flag.name, value)?.let { value != it } != false) {
56             notifyFlagChanged(flag)
57         }
58     }
59 
60     fun set(flag: SysPropBooleanFlag, value: Boolean) {
61         if (booleanFlags.put(flag.name, value)?.let { value != it } != false) {
62             notifyFlagChanged(flag)
63         }
64     }
65 
66     fun set(flag: StringFlag, value: String) {
67         if (stringFlags.put(flag.name, value)?.let { value != it } == null) {
68             notifyFlagChanged(flag)
69         }
70     }
71 
72     fun set(flag: ResourceStringFlag, value: String) {
73         if (stringFlags.put(flag.name, value)?.let { value != it } == null) {
74             notifyFlagChanged(flag)
75         }
76     }
77 
78     /**
79      * Set the given flag's default value if no other value has been set.
80      *
81      * REMINDER: You should always test your code with your flag in both configurations, so
82      * generally you should be setting a particular value. This method should be reserved for
83      * situations where the flag needs to be read (e.g. in the class constructor), but its value
84      * shouldn't affect the actual test cases. In those cases, it's mildly safer to use this method
85      * than to hard-code `false` or `true` because then at least if you're wrong, and the flag value
86      * *does* matter, you'll notice when the flag is flipped and tests start failing.
87      */
88     fun setDefault(flag: BooleanFlag) = booleanFlags.putIfAbsent(flag.name, flag.default)
89 
90     /**
91      * Set the given flag's default value if no other value has been set.
92      *
93      * REMINDER: You should always test your code with your flag in both configurations, so
94      * generally you should be setting a particular value. This method should be reserved for
95      * situations where the flag needs to be read (e.g. in the class constructor), but its value
96      * shouldn't affect the actual test cases. In those cases, it's mildly safer to use this method
97      * than to hard-code `false` or `true` because then at least if you're wrong, and the flag value
98      * *does* matter, you'll notice when the flag is flipped and tests start failing.
99      */
100     fun setDefault(flag: SysPropBooleanFlag) = booleanFlags.putIfAbsent(flag.name, flag.default)
101 
102     private fun notifyFlagChanged(flag: Flag<*>) {
103         flagListeners[flag.name]?.let { listeners ->
104             listeners.forEach { listener ->
105                 listener.onFlagChanged(
106                     object : FlagListenable.FlagEvent {
107                         override val flagName = flag.name
108 
109                         override fun requestNoRestart() {}
110                     }
111                 )
112             }
113         }
114     }
115 
116     override fun isEnabled(flag: UnreleasedFlag): Boolean = requireBooleanValue(flag.name)
117 
118     override fun isEnabled(flag: ReleasedFlag): Boolean = requireBooleanValue(flag.name)
119 
120     override fun isEnabled(flag: ResourceBooleanFlag): Boolean = requireBooleanValue(flag.name)
121 
122     override fun isEnabled(flag: SysPropBooleanFlag): Boolean = requireBooleanValue(flag.name)
123 
124     override fun getString(flag: StringFlag): String = requireStringValue(flag.name)
125 
126     override fun getString(flag: ResourceStringFlag): String = requireStringValue(flag.name)
127 
128     override fun getInt(flag: IntFlag): Int = requireIntValue(flag.name)
129 
130     override fun getInt(flag: ResourceIntFlag): Int = requireIntValue(flag.name)
131 
132     override fun addListener(flag: Flag<*>, listener: FlagListenable.Listener) {
133         flagListeners.getOrPut(flag.name) { mutableSetOf() }.add(listener)
134         listenerflagNames.getOrPut(listener) { mutableSetOf() }.add(flag.name)
135     }
136 
137     override fun removeListener(listener: FlagListenable.Listener) {
138         listenerflagNames.remove(listener)?.let { flagNames ->
139             flagNames.forEach { id -> flagListeners[id]?.remove(listener) }
140         }
141     }
142 
143     override fun dump(writer: PrintWriter, args: Array<out String>?) {
144         // no-op
145     }
146 
147     private fun flagName(flagName: String): String {
148         return knownFlagNames[flagName] ?: "UNKNOWN($flagName)"
149     }
150 
151     private fun requireBooleanValue(flagName: String): Boolean {
152         return booleanFlags[flagName]
153             ?: error("Flag ${flagName(flagName)} was accessed as boolean but not specified.")
154     }
155 
156     private fun requireStringValue(flagName: String): String {
157         return stringFlags[flagName]
158             ?: error("Flag ${flagName(flagName)} was accessed as string but not specified.")
159     }
160 
161     private fun requireIntValue(flagName: String): Int {
162         return intFlags[flagName]
163             ?: error("Flag ${flagName(flagName)} was accessed as int but not specified.")
164     }
165 }
166 
167 @Module(includes = [FakeFeatureFlagsClassicModule.Bindings::class])
168 class FakeFeatureFlagsClassicModule(
169     @get:Provides val fakeFeatureFlagsClassic: FakeFeatureFlagsClassic = FakeFeatureFlagsClassic()
170 ) {
171 
172     constructor(
173         block: FakeFeatureFlagsClassic.() -> Unit
174     ) : this(FakeFeatureFlagsClassic().apply(block))
175 
176     @Module
177     interface Bindings {
bindFakenull178         @Binds fun bindFake(fake: FakeFeatureFlagsClassic): FeatureFlagsClassic
179 
180         @Binds fun bindClassic(classic: FeatureFlagsClassic): FeatureFlags
181 
182         @Binds fun bindFakeClassic(fake: FakeFeatureFlagsClassic): FakeFeatureFlags
183     }
184 }
185