Merge "Add adb shell command to set flags." into tm-dev
This commit is contained in:
@@ -42,10 +42,14 @@ import com.android.systemui.Dumpable;
|
||||
import com.android.systemui.dagger.SysUISingleton;
|
||||
import com.android.systemui.dagger.qualifiers.Main;
|
||||
import com.android.systemui.dump.DumpManager;
|
||||
import com.android.systemui.statusbar.commandline.Command;
|
||||
import com.android.systemui.statusbar.commandline.CommandRegistry;
|
||||
import com.android.systemui.util.settings.SecureSettings;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.TreeMap;
|
||||
@@ -59,6 +63,10 @@ import javax.inject.Named;
|
||||
*
|
||||
* Flags can be set (or unset) via the following adb command:
|
||||
*
|
||||
* adb shell cmd statusbar flag <id> <on|off|toggle|erase>
|
||||
*
|
||||
* Alternatively, you can change flags via a broadcast intent:
|
||||
*
|
||||
* adb shell am broadcast -a com.android.systemui.action.SET_FLAG --ei id <id> [--ez value <0|1>]
|
||||
*
|
||||
* To restore a flag back to its default, leave the `--ez value <0|1>` off of the command.
|
||||
@@ -67,6 +75,7 @@ import javax.inject.Named;
|
||||
public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
|
||||
private static final String TAG = "SysUIFlags";
|
||||
static final String ALL_FLAGS = "all_flags";
|
||||
private static final String FLAG_COMMAND = "flag";
|
||||
|
||||
private final FlagManager mFlagManager;
|
||||
private final SecureSettings mSecureSettings;
|
||||
@@ -86,12 +95,15 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
|
||||
@Main Resources resources,
|
||||
DumpManager dumpManager,
|
||||
@Named(ALL_FLAGS) Map<Integer, Flag<?>> allFlags,
|
||||
CommandRegistry commandRegistry,
|
||||
IStatusBarService barService) {
|
||||
mFlagManager = flagManager;
|
||||
mSecureSettings = secureSettings;
|
||||
mResources = resources;
|
||||
mSystemProperties = systemProperties;
|
||||
mAllFlags = allFlags;
|
||||
mBarService = barService;
|
||||
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(ACTION_SET_FLAG);
|
||||
filter.addAction(ACTION_GET_FLAGS);
|
||||
@@ -100,7 +112,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
|
||||
context.registerReceiver(mReceiver, filter, null, null,
|
||||
Context.RECEIVER_EXPORTED_UNAUDITED);
|
||||
dumpManager.registerDumpable(TAG, this);
|
||||
mBarService = barService;
|
||||
commandRegistry.registerCommand(FLAG_COMMAND, FlagCommand::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -276,6 +288,31 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
|
||||
}
|
||||
}
|
||||
|
||||
private void setBooleanFlagInternal(Flag<?> flag, boolean value) {
|
||||
if (flag instanceof BooleanFlag) {
|
||||
setFlagValue(flag.getId(), value, BooleanFlagSerializer.INSTANCE);
|
||||
} else if (flag instanceof ResourceBooleanFlag) {
|
||||
setFlagValue(flag.getId(), value, BooleanFlagSerializer.INSTANCE);
|
||||
} else if (flag instanceof SysPropBooleanFlag) {
|
||||
// Store SysProp flags in SystemProperties where they can read by outside parties.
|
||||
mSystemProperties.setBoolean(((SysPropBooleanFlag) flag).getName(), value);
|
||||
dispatchListenersAndMaybeRestart(flag.getId(),
|
||||
FeatureFlagsDebug.this::restartAndroid);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown flag type");
|
||||
}
|
||||
}
|
||||
|
||||
private void setStringFlagInternal(Flag<?> flag, String value) {
|
||||
if (flag instanceof StringFlag) {
|
||||
setFlagValue(flag.getId(), value, StringFlagSerializer.INSTANCE);
|
||||
} else if (flag instanceof ResourceStringFlag) {
|
||||
setFlagValue(flag.getId(), value, StringFlagSerializer.INSTANCE);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown flag type");
|
||||
}
|
||||
}
|
||||
|
||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
@@ -327,24 +364,19 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
|
||||
}
|
||||
|
||||
Object value = extras.get(EXTRA_VALUE);
|
||||
if (flag instanceof BooleanFlag && value instanceof Boolean) {
|
||||
setFlagValue(id, (Boolean) value, BooleanFlagSerializer.INSTANCE);
|
||||
} else if (flag instanceof ResourceBooleanFlag && value instanceof Boolean) {
|
||||
setFlagValue(id, (Boolean) value, BooleanFlagSerializer.INSTANCE);
|
||||
} else if (flag instanceof SysPropBooleanFlag && value instanceof Boolean) {
|
||||
// Store SysProp flags in SystemProperties where they can read by outside parties.
|
||||
mSystemProperties.setBoolean(
|
||||
((SysPropBooleanFlag) flag).getName(), (Boolean) value);
|
||||
dispatchListenersAndMaybeRestart(flag.getId(),
|
||||
FeatureFlagsDebug.this::restartAndroid);
|
||||
} else if (flag instanceof StringFlag && value instanceof String) {
|
||||
setFlagValue(id, (String) value, StringFlagSerializer.INSTANCE);
|
||||
} else if (flag instanceof ResourceStringFlag && value instanceof String) {
|
||||
setFlagValue(id, (String) value, StringFlagSerializer.INSTANCE);
|
||||
} else {
|
||||
|
||||
try {
|
||||
if (value instanceof Boolean) {
|
||||
setBooleanFlagInternal(flag, (Boolean) value);
|
||||
} else if (value instanceof String) {
|
||||
setStringFlagInternal(flag, (String) value);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown value type");
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.w(TAG,
|
||||
"Unable to set " + id + " of type " + flag.getClass() + " to value of type "
|
||||
+ (value == null ? null : value.getClass()));
|
||||
"Unable to set " + flag.getId() + " of type " + flag.getClass()
|
||||
+ " to value of type " + (value == null ? null : value.getClass()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -388,4 +420,153 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
|
||||
mStringFlagCache.forEach((key, value) -> pw.println(" sysui_flag_" + key
|
||||
+ ": [length=" + value.length() + "] \"" + value + "\""));
|
||||
}
|
||||
|
||||
class FlagCommand implements Command {
|
||||
private final List<String> mOnCommands = List.of("true", "on", "1", "enabled");
|
||||
private final List<String> mOffCommands = List.of("false", "off", "0", "disable");
|
||||
|
||||
@Override
|
||||
public void execute(@NonNull PrintWriter pw, @NonNull List<String> args) {
|
||||
if (args.size() == 0) {
|
||||
pw.println("Error: no flag id supplied");
|
||||
help(pw);
|
||||
pw.println();
|
||||
printKnownFlags(pw);
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.size() > 2) {
|
||||
pw.println("Invalid number of arguments.");
|
||||
help(pw);
|
||||
return;
|
||||
}
|
||||
|
||||
int id = 0;
|
||||
try {
|
||||
id = Integer.parseInt(args.get(0));
|
||||
if (!mAllFlags.containsKey(id)) {
|
||||
pw.println("Unknown flag id: " + id);
|
||||
pw.println();
|
||||
printKnownFlags(pw);
|
||||
return;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
id = flagNameToId(args.get(0));
|
||||
if (id == 0) {
|
||||
pw.println("Invalid flag. Must an integer id or flag name: " + args.get(0));
|
||||
return;
|
||||
}
|
||||
}
|
||||
Flag<?> flag = mAllFlags.get(id);
|
||||
|
||||
String cmd = "";
|
||||
if (args.size() == 2) {
|
||||
cmd = args.get(1).toLowerCase();
|
||||
}
|
||||
|
||||
if ("erase".equals(cmd) || "reset".equals(cmd)) {
|
||||
eraseFlag(flag);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean newValue = true;
|
||||
if (args.size() == 1 || "toggle".equals(cmd)) {
|
||||
boolean enabled = isBooleanFlagEnabled(flag);
|
||||
|
||||
if (args.size() == 1) {
|
||||
pw.println("Flag " + id + " is " + enabled);
|
||||
return;
|
||||
}
|
||||
|
||||
newValue = !enabled;
|
||||
} else {
|
||||
newValue = mOnCommands.contains(cmd);
|
||||
if (!newValue && !mOffCommands.contains(cmd)) {
|
||||
pw.println("Invalid on/off argument supplied");
|
||||
help(pw);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pw.flush(); // Next command will restart sysui, so flush before we do so.
|
||||
setBooleanFlagInternal(flag, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void help(PrintWriter pw) {
|
||||
pw.println(
|
||||
"Usage: adb shell cmd statusbar flag <id> "
|
||||
+ "[true|false|1|0|on|off|enable|disable|toggle|erase|reset]");
|
||||
pw.println("The id can either be a numeric integer or the corresponding field name");
|
||||
pw.println(
|
||||
"If no argument is supplied after the id, the flags runtime value is output");
|
||||
}
|
||||
|
||||
private boolean isBooleanFlagEnabled(Flag<?> flag) {
|
||||
if (flag instanceof BooleanFlag) {
|
||||
return isEnabled((BooleanFlag) flag);
|
||||
} else if (flag instanceof ResourceBooleanFlag) {
|
||||
return isEnabled((ResourceBooleanFlag) flag);
|
||||
} else if (flag instanceof SysPropFlag) {
|
||||
return isEnabled((SysPropBooleanFlag) flag);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private int flagNameToId(String flagName) {
|
||||
List<Field> fields = Flags.getFlagFields();
|
||||
for (Field field : fields) {
|
||||
if (flagName.equals(field.getName())) {
|
||||
return fieldToId(field);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int fieldToId(Field field) {
|
||||
try {
|
||||
Flag<?> flag = (Flag<?>) field.get(null);
|
||||
return flag.getId();
|
||||
} catch (IllegalAccessException e) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void printKnownFlags(PrintWriter pw) {
|
||||
List<Field> fields = Flags.getFlagFields();
|
||||
|
||||
int longestFieldName = 0;
|
||||
for (Field field : fields) {
|
||||
longestFieldName = Math.max(longestFieldName, field.getName().length());
|
||||
}
|
||||
|
||||
pw.println("Known Flags:");
|
||||
pw.print("Flag Name");
|
||||
for (int i = 0; i < longestFieldName - "Flag Name".length() + 1; i++) {
|
||||
pw.print(" ");
|
||||
}
|
||||
pw.println("ID Enabled?");
|
||||
for (int i = 0; i < longestFieldName; i++) {
|
||||
pw.print("=");
|
||||
}
|
||||
pw.println(" ==== ========");
|
||||
for (Field field : fields) {
|
||||
int id = fieldToId(field);
|
||||
if (id == 0 || !mAllFlags.containsKey(id)) {
|
||||
continue;
|
||||
}
|
||||
pw.print(field.getName());
|
||||
int fieldWidth = field.getName().length();
|
||||
for (int i = 0; i < longestFieldName - fieldWidth + 1; i++) {
|
||||
pw.print(" ");
|
||||
}
|
||||
pw.printf("%-4d ", id);
|
||||
pw.println(isBooleanFlagEnabled(mAllFlags.get(id)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,9 @@ import com.android.internal.annotations.Keep;
|
||||
import com.android.systemui.R;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -182,19 +184,16 @@ public class Flags {
|
||||
if (sFlagMap != null) {
|
||||
return sFlagMap;
|
||||
}
|
||||
|
||||
Map<Integer, Flag<?>> flags = new HashMap<>();
|
||||
List<Field> flagFields = getFlagFields();
|
||||
|
||||
Field[] fields = Flags.class.getFields();
|
||||
|
||||
for (Field field : fields) {
|
||||
Class<?> t = field.getType();
|
||||
if (Flag.class.isAssignableFrom(t)) {
|
||||
try {
|
||||
Flag<?> flag = (Flag<?>) field.get(null);
|
||||
flags.put(flag.getId(), flag);
|
||||
} catch (IllegalAccessException e) {
|
||||
// no-op
|
||||
}
|
||||
for (Field field : flagFields) {
|
||||
try {
|
||||
Flag<?> flag = (Flag<?>) field.get(null);
|
||||
flags.put(flag.getId(), flag);
|
||||
} catch (IllegalAccessException e) {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,6 +201,20 @@ public class Flags {
|
||||
|
||||
return sFlagMap;
|
||||
}
|
||||
|
||||
static List<Field> getFlagFields() {
|
||||
Field[] fields = Flags.class.getFields();
|
||||
List<Field> result = new ArrayList<>();
|
||||
|
||||
for (Field field : fields) {
|
||||
Class<?> t = field.getType();
|
||||
if (Flag.class.isAssignableFrom(t)) {
|
||||
result.add(field);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
// | . . . . . . . . . . . . . . . . . . . |
|
||||
// | |
|
||||
// \_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/
|
||||
|
||||
@@ -24,7 +24,11 @@ import android.test.suitebuilder.annotation.SmallTest
|
||||
import com.android.internal.statusbar.IStatusBarService
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.dump.DumpManager
|
||||
import com.android.systemui.statusbar.commandline.Command
|
||||
import com.android.systemui.statusbar.commandline.CommandRegistry
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.argumentCaptor
|
||||
import com.android.systemui.util.mockito.capture
|
||||
import com.android.systemui.util.mockito.eq
|
||||
import com.android.systemui.util.mockito.nullable
|
||||
import com.android.systemui.util.mockito.withArgCaptor
|
||||
@@ -37,10 +41,12 @@ import org.mockito.ArgumentMatchers.anyInt
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.anyBoolean
|
||||
import org.mockito.Mockito.anyString
|
||||
import org.mockito.Mockito.atLeastOnce
|
||||
import org.mockito.Mockito.inOrder
|
||||
import org.mockito.Mockito.times
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.verifyNoMoreInteractions
|
||||
import org.mockito.Mockito.verifyZeroInteractions
|
||||
import org.mockito.MockitoAnnotations
|
||||
import java.io.PrintWriter
|
||||
import java.io.Serializable
|
||||
@@ -56,16 +62,18 @@ import org.mockito.Mockito.`when` as whenever
|
||||
class FeatureFlagsDebugTest : SysuiTestCase() {
|
||||
private lateinit var mFeatureFlagsDebug: FeatureFlagsDebug
|
||||
|
||||
@Mock private lateinit var mFlagManager: FlagManager
|
||||
@Mock private lateinit var mMockContext: Context
|
||||
@Mock private lateinit var mSecureSettings: SecureSettings
|
||||
@Mock private lateinit var mSystemProperties: SystemPropertiesHelper
|
||||
@Mock private lateinit var mResources: Resources
|
||||
@Mock private lateinit var mDumpManager: DumpManager
|
||||
@Mock private lateinit var mBarService: IStatusBarService
|
||||
private val mFlagMap = mutableMapOf<Int, Flag<*>>()
|
||||
private lateinit var mBroadcastReceiver: BroadcastReceiver
|
||||
private lateinit var mClearCacheAction: Consumer<Int>
|
||||
@Mock private lateinit var flagManager: FlagManager
|
||||
@Mock private lateinit var mockContext: Context
|
||||
@Mock private lateinit var secureSettings: SecureSettings
|
||||
@Mock private lateinit var systemProperties: SystemPropertiesHelper
|
||||
@Mock private lateinit var resources: Resources
|
||||
@Mock private lateinit var dumpManager: DumpManager
|
||||
@Mock private lateinit var commandRegistry: CommandRegistry
|
||||
@Mock private lateinit var barService: IStatusBarService
|
||||
@Mock private lateinit var pw: PrintWriter
|
||||
private val flagMap = mutableMapOf<Int, Flag<*>>()
|
||||
private lateinit var broadcastReceiver: BroadcastReceiver
|
||||
private lateinit var clearCacheAction: Consumer<Int>
|
||||
|
||||
private val teamfoodableFlagA = BooleanFlag(500, false, true)
|
||||
private val teamfoodableFlagB = BooleanFlag(501, true, true)
|
||||
@@ -73,34 +81,35 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
|
||||
@Before
|
||||
fun setup() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
mFlagMap.put(teamfoodableFlagA.id, teamfoodableFlagA)
|
||||
mFlagMap.put(teamfoodableFlagB.id, teamfoodableFlagB)
|
||||
flagMap.put(teamfoodableFlagA.id, teamfoodableFlagA)
|
||||
flagMap.put(teamfoodableFlagB.id, teamfoodableFlagB)
|
||||
mFeatureFlagsDebug = FeatureFlagsDebug(
|
||||
mFlagManager,
|
||||
mMockContext,
|
||||
mSecureSettings,
|
||||
mSystemProperties,
|
||||
mResources,
|
||||
mDumpManager,
|
||||
mFlagMap,
|
||||
mBarService
|
||||
flagManager,
|
||||
mockContext,
|
||||
secureSettings,
|
||||
systemProperties,
|
||||
resources,
|
||||
dumpManager,
|
||||
flagMap,
|
||||
commandRegistry,
|
||||
barService
|
||||
)
|
||||
verify(mFlagManager).onSettingsChangedAction = any()
|
||||
mBroadcastReceiver = withArgCaptor {
|
||||
verify(mMockContext).registerReceiver(capture(), any(), nullable(), nullable(),
|
||||
verify(flagManager).onSettingsChangedAction = any()
|
||||
broadcastReceiver = withArgCaptor {
|
||||
verify(mockContext).registerReceiver(capture(), any(), nullable(), nullable(),
|
||||
any())
|
||||
}
|
||||
mClearCacheAction = withArgCaptor {
|
||||
verify(mFlagManager).clearCacheAction = capture()
|
||||
clearCacheAction = withArgCaptor {
|
||||
verify(flagManager).clearCacheAction = capture()
|
||||
}
|
||||
whenever(mFlagManager.idToSettingsKey(any())).thenAnswer { "key-${it.arguments[0]}" }
|
||||
whenever(flagManager.idToSettingsKey(any())).thenAnswer { "key-${it.arguments[0]}" }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testReadBooleanFlag() {
|
||||
// Remember that the TEAMFOOD flag is id#1 and has special behavior.
|
||||
whenever(mFlagManager.readFlagValue<Boolean>(eq(3), any())).thenReturn(true)
|
||||
whenever(mFlagManager.readFlagValue<Boolean>(eq(4), any())).thenReturn(false)
|
||||
whenever(flagManager.readFlagValue<Boolean>(eq(3), any())).thenReturn(true)
|
||||
whenever(flagManager.readFlagValue<Boolean>(eq(4), any())).thenReturn(false)
|
||||
assertThat(mFeatureFlagsDebug.isEnabled(BooleanFlag(2, true))).isTrue()
|
||||
assertThat(mFeatureFlagsDebug.isEnabled(BooleanFlag(3, false))).isTrue()
|
||||
assertThat(mFeatureFlagsDebug.isEnabled(BooleanFlag(4, true))).isFalse()
|
||||
@@ -109,7 +118,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun testTeamFoodFlag_False() {
|
||||
whenever(mFlagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(false)
|
||||
whenever(flagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(false)
|
||||
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagA)).isFalse()
|
||||
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagB)).isTrue()
|
||||
|
||||
@@ -120,7 +129,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun testTeamFoodFlag_True() {
|
||||
whenever(mFlagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(true)
|
||||
whenever(flagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(true)
|
||||
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagA)).isTrue()
|
||||
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagB)).isTrue()
|
||||
|
||||
@@ -131,11 +140,11 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun testTeamFoodFlag_Overridden() {
|
||||
whenever(mFlagManager.readFlagValue<Boolean>(eq(teamfoodableFlagA.id), any()))
|
||||
whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagA.id), any()))
|
||||
.thenReturn(true)
|
||||
whenever(mFlagManager.readFlagValue<Boolean>(eq(teamfoodableFlagB.id), any()))
|
||||
whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagB.id), any()))
|
||||
.thenReturn(false)
|
||||
whenever(mFlagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(true)
|
||||
whenever(flagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(true)
|
||||
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagA)).isTrue()
|
||||
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagB)).isFalse()
|
||||
|
||||
@@ -146,14 +155,14 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun testReadResourceBooleanFlag() {
|
||||
whenever(mResources.getBoolean(1001)).thenReturn(false)
|
||||
whenever(mResources.getBoolean(1002)).thenReturn(true)
|
||||
whenever(mResources.getBoolean(1003)).thenReturn(false)
|
||||
whenever(mResources.getBoolean(1004)).thenAnswer { throw NameNotFoundException() }
|
||||
whenever(mResources.getBoolean(1005)).thenAnswer { throw NameNotFoundException() }
|
||||
whenever(resources.getBoolean(1001)).thenReturn(false)
|
||||
whenever(resources.getBoolean(1002)).thenReturn(true)
|
||||
whenever(resources.getBoolean(1003)).thenReturn(false)
|
||||
whenever(resources.getBoolean(1004)).thenAnswer { throw NameNotFoundException() }
|
||||
whenever(resources.getBoolean(1005)).thenAnswer { throw NameNotFoundException() }
|
||||
|
||||
whenever(mFlagManager.readFlagValue<Boolean>(eq(3), any())).thenReturn(true)
|
||||
whenever(mFlagManager.readFlagValue<Boolean>(eq(5), any())).thenReturn(false)
|
||||
whenever(flagManager.readFlagValue<Boolean>(eq(3), any())).thenReturn(true)
|
||||
whenever(flagManager.readFlagValue<Boolean>(eq(5), any())).thenReturn(false)
|
||||
|
||||
assertThat(mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(1, 1001))).isFalse()
|
||||
assertThat(mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(2, 1002))).isTrue()
|
||||
@@ -171,7 +180,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun testReadSysPropBooleanFlag() {
|
||||
whenever(mSystemProperties.getBoolean(anyString(), anyBoolean())).thenAnswer {
|
||||
whenever(systemProperties.getBoolean(anyString(), anyBoolean())).thenAnswer {
|
||||
if ("b".equals(it.getArgument<String?>(0))) {
|
||||
return@thenAnswer true
|
||||
}
|
||||
@@ -187,8 +196,8 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun testReadStringFlag() {
|
||||
whenever(mFlagManager.readFlagValue<String>(eq(3), any())).thenReturn("foo")
|
||||
whenever(mFlagManager.readFlagValue<String>(eq(4), any())).thenReturn("bar")
|
||||
whenever(flagManager.readFlagValue<String>(eq(3), any())).thenReturn("foo")
|
||||
whenever(flagManager.readFlagValue<String>(eq(4), any())).thenReturn("bar")
|
||||
assertThat(mFeatureFlagsDebug.getString(StringFlag(1, "biz"))).isEqualTo("biz")
|
||||
assertThat(mFeatureFlagsDebug.getString(StringFlag(2, "baz"))).isEqualTo("baz")
|
||||
assertThat(mFeatureFlagsDebug.getString(StringFlag(3, "buz"))).isEqualTo("foo")
|
||||
@@ -197,16 +206,16 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun testReadResourceStringFlag() {
|
||||
whenever(mResources.getString(1001)).thenReturn("")
|
||||
whenever(mResources.getString(1002)).thenReturn("resource2")
|
||||
whenever(mResources.getString(1003)).thenReturn("resource3")
|
||||
whenever(mResources.getString(1004)).thenReturn(null)
|
||||
whenever(mResources.getString(1005)).thenAnswer { throw NameNotFoundException() }
|
||||
whenever(mResources.getString(1006)).thenAnswer { throw NameNotFoundException() }
|
||||
whenever(resources.getString(1001)).thenReturn("")
|
||||
whenever(resources.getString(1002)).thenReturn("resource2")
|
||||
whenever(resources.getString(1003)).thenReturn("resource3")
|
||||
whenever(resources.getString(1004)).thenReturn(null)
|
||||
whenever(resources.getString(1005)).thenAnswer { throw NameNotFoundException() }
|
||||
whenever(resources.getString(1006)).thenAnswer { throw NameNotFoundException() }
|
||||
|
||||
whenever(mFlagManager.readFlagValue<String>(eq(3), any())).thenReturn("override3")
|
||||
whenever(mFlagManager.readFlagValue<String>(eq(4), any())).thenReturn("override4")
|
||||
whenever(mFlagManager.readFlagValue<String>(eq(6), any())).thenReturn("override6")
|
||||
whenever(flagManager.readFlagValue<String>(eq(3), any())).thenReturn("override3")
|
||||
whenever(flagManager.readFlagValue<String>(eq(4), any())).thenReturn("override4")
|
||||
whenever(flagManager.readFlagValue<String>(eq(6), any())).thenReturn("override6")
|
||||
|
||||
assertThat(mFeatureFlagsDebug.getString(ResourceStringFlag(1, 1001))).isEqualTo("")
|
||||
assertThat(mFeatureFlagsDebug.getString(ResourceStringFlag(2, 1002))).isEqualTo("resource2")
|
||||
@@ -232,16 +241,16 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
|
||||
addFlag(StringFlag(3, "flag3"))
|
||||
addFlag(ResourceStringFlag(4, 1004))
|
||||
|
||||
mBroadcastReceiver.onReceive(mMockContext, null)
|
||||
mBroadcastReceiver.onReceive(mMockContext, Intent())
|
||||
mBroadcastReceiver.onReceive(mMockContext, Intent("invalid action"))
|
||||
mBroadcastReceiver.onReceive(mMockContext, Intent(FlagManager.ACTION_SET_FLAG))
|
||||
broadcastReceiver.onReceive(mockContext, null)
|
||||
broadcastReceiver.onReceive(mockContext, Intent())
|
||||
broadcastReceiver.onReceive(mockContext, Intent("invalid action"))
|
||||
broadcastReceiver.onReceive(mockContext, Intent(FlagManager.ACTION_SET_FLAG))
|
||||
setByBroadcast(0, false) // unknown id does nothing
|
||||
setByBroadcast(1, "string") // wrong type does nothing
|
||||
setByBroadcast(2, 123) // wrong type does nothing
|
||||
setByBroadcast(3, false) // wrong type does nothing
|
||||
setByBroadcast(4, 123) // wrong type does nothing
|
||||
verifyNoMoreInteractions(mFlagManager, mSecureSettings)
|
||||
verifyNoMoreInteractions(flagManager, secureSettings)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -249,15 +258,15 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
|
||||
addFlag(BooleanFlag(1, false))
|
||||
|
||||
// trying to erase an id not in the map does noting
|
||||
mBroadcastReceiver.onReceive(
|
||||
mMockContext,
|
||||
broadcastReceiver.onReceive(
|
||||
mockContext,
|
||||
Intent(FlagManager.ACTION_SET_FLAG).putExtra(FlagManager.EXTRA_ID, 0)
|
||||
)
|
||||
verifyNoMoreInteractions(mFlagManager, mSecureSettings)
|
||||
verifyNoMoreInteractions(flagManager, secureSettings)
|
||||
|
||||
// valid id with no value puts empty string in the setting
|
||||
mBroadcastReceiver.onReceive(
|
||||
mMockContext,
|
||||
broadcastReceiver.onReceive(
|
||||
mockContext,
|
||||
Intent(FlagManager.ACTION_SET_FLAG).putExtra(FlagManager.EXTRA_ID, 1)
|
||||
)
|
||||
verifyPutData(1, "", numReads = 0)
|
||||
@@ -298,48 +307,102 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun testSetFlagClearsCache() {
|
||||
val flag1 = addFlag(StringFlag(1, "flag1"))
|
||||
whenever(mFlagManager.readFlagValue<String>(eq(1), any())).thenReturn("original")
|
||||
whenever(flagManager.readFlagValue<String>(eq(1), any())).thenReturn("original")
|
||||
|
||||
// gets the flag & cache it
|
||||
assertThat(mFeatureFlagsDebug.getString(flag1)).isEqualTo("original")
|
||||
verify(mFlagManager).readFlagValue(eq(1), eq(StringFlagSerializer))
|
||||
verify(flagManager).readFlagValue(eq(1), eq(StringFlagSerializer))
|
||||
|
||||
// hit the cache
|
||||
assertThat(mFeatureFlagsDebug.getString(flag1)).isEqualTo("original")
|
||||
verifyNoMoreInteractions(mFlagManager)
|
||||
verifyNoMoreInteractions(flagManager)
|
||||
|
||||
// set the flag
|
||||
setByBroadcast(1, "new")
|
||||
verifyPutData(1, "{\"type\":\"string\",\"value\":\"new\"}", numReads = 2)
|
||||
whenever(mFlagManager.readFlagValue<String>(eq(1), any())).thenReturn("new")
|
||||
whenever(flagManager.readFlagValue<String>(eq(1), any())).thenReturn("new")
|
||||
|
||||
assertThat(mFeatureFlagsDebug.getString(flag1)).isEqualTo("new")
|
||||
verify(mFlagManager, times(3)).readFlagValue(eq(1), eq(StringFlagSerializer))
|
||||
verify(flagManager, times(3)).readFlagValue(eq(1), eq(StringFlagSerializer))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRegisterCommand() {
|
||||
verify(commandRegistry).registerCommand(anyString(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNoOpCommand() {
|
||||
val cmd = captureCommand()
|
||||
|
||||
cmd.execute(pw, ArrayList())
|
||||
verify(pw, atLeastOnce()).println()
|
||||
verify(flagManager).readFlagValue<Boolean>(eq(1), any())
|
||||
verifyZeroInteractions(secureSettings)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testReadFlagCommand() {
|
||||
addFlag(BooleanFlag(1, false))
|
||||
val cmd = captureCommand()
|
||||
cmd.execute(pw, listOf("1"))
|
||||
verify(flagManager).readFlagValue<Boolean>(eq(1), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetFlagCommand() {
|
||||
addFlag(BooleanFlag(1, false))
|
||||
val cmd = captureCommand()
|
||||
cmd.execute(pw, listOf("1", "on"))
|
||||
verifyPutData(1, "{\"type\":\"boolean\",\"value\":true}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testToggleFlagCommand() {
|
||||
addFlag(BooleanFlag(1, true))
|
||||
val cmd = captureCommand()
|
||||
cmd.execute(pw, listOf("1", "toggle"))
|
||||
verifyPutData(1, "{\"type\":\"boolean\",\"value\":false}", 2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEraseFlagCommand() {
|
||||
addFlag(BooleanFlag(1, true))
|
||||
val cmd = captureCommand()
|
||||
cmd.execute(pw, listOf("1", "erase"))
|
||||
verify(secureSettings).putStringForUser(eq("key-1"), eq(""), anyInt())
|
||||
}
|
||||
|
||||
private fun verifyPutData(id: Int, data: String, numReads: Int = 1) {
|
||||
inOrder(mFlagManager, mSecureSettings).apply {
|
||||
verify(mFlagManager, times(numReads)).readFlagValue(eq(id), any<FlagSerializer<*>>())
|
||||
verify(mFlagManager).idToSettingsKey(eq(id))
|
||||
verify(mSecureSettings).putStringForUser(eq("key-$id"), eq(data), anyInt())
|
||||
verify(mFlagManager).dispatchListenersAndMaybeRestart(eq(id), any())
|
||||
inOrder(flagManager, secureSettings).apply {
|
||||
verify(flagManager, times(numReads)).readFlagValue(eq(id), any<FlagSerializer<*>>())
|
||||
verify(flagManager).idToSettingsKey(eq(id))
|
||||
verify(secureSettings).putStringForUser(eq("key-$id"), eq(data), anyInt())
|
||||
verify(flagManager).dispatchListenersAndMaybeRestart(eq(id), any())
|
||||
}.verifyNoMoreInteractions()
|
||||
verifyNoMoreInteractions(mFlagManager, mSecureSettings)
|
||||
verifyNoMoreInteractions(flagManager, secureSettings)
|
||||
}
|
||||
|
||||
private fun setByBroadcast(id: Int, value: Serializable?) {
|
||||
val intent = Intent(FlagManager.ACTION_SET_FLAG)
|
||||
intent.putExtra(FlagManager.EXTRA_ID, id)
|
||||
intent.putExtra(FlagManager.EXTRA_VALUE, value)
|
||||
mBroadcastReceiver.onReceive(mMockContext, intent)
|
||||
broadcastReceiver.onReceive(mockContext, intent)
|
||||
}
|
||||
|
||||
private fun <F : Flag<*>> addFlag(flag: F): F {
|
||||
val old = mFlagMap.put(flag.id, flag)
|
||||
val old = flagMap.put(flag.id, flag)
|
||||
check(old == null) { "Flag ${flag.id} already registered" }
|
||||
return flag
|
||||
}
|
||||
|
||||
private fun captureCommand(): Command {
|
||||
val captor = argumentCaptor<Function0<Command>>()
|
||||
verify(commandRegistry).registerCommand(anyString(), capture(captor))
|
||||
|
||||
return captor.value.invoke()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDump() {
|
||||
val flag1 = BooleanFlag(1, true)
|
||||
@@ -350,10 +413,10 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
|
||||
val flag6 = ResourceStringFlag(6, 1006)
|
||||
val flag7 = ResourceStringFlag(7, 1007)
|
||||
|
||||
whenever(mResources.getBoolean(1002)).thenReturn(true)
|
||||
whenever(mResources.getString(1006)).thenReturn("resource1006")
|
||||
whenever(mResources.getString(1007)).thenReturn("resource1007")
|
||||
whenever(mFlagManager.readFlagValue(eq(7), eq(StringFlagSerializer)))
|
||||
whenever(resources.getBoolean(1002)).thenReturn(true)
|
||||
whenever(resources.getString(1006)).thenReturn("resource1006")
|
||||
whenever(resources.getString(1007)).thenReturn("resource1007")
|
||||
whenever(flagManager.readFlagValue(eq(7), eq(StringFlagSerializer)))
|
||||
.thenReturn("override7")
|
||||
|
||||
// WHEN the flags have been accessed
|
||||
|
||||
Reference in New Issue
Block a user