summaryrefslogtreecommitdiff
path: root/base/test/android/javatests/src/org/chromium/base/test/util/CommandLineFlags.java
diff options
context:
space:
mode:
Diffstat (limited to 'base/test/android/javatests/src/org/chromium/base/test/util/CommandLineFlags.java')
-rw-r--r--base/test/android/javatests/src/org/chromium/base/test/util/CommandLineFlags.java188
1 files changed, 188 insertions, 0 deletions
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/CommandLineFlags.java b/base/test/android/javatests/src/org/chromium/base/test/util/CommandLineFlags.java
new file mode 100644
index 0000000000..71ef8e91ff
--- /dev/null
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/CommandLineFlags.java
@@ -0,0 +1,188 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.test.util;
+
+import android.text.TextUtils;
+
+import org.junit.Assert;
+import org.junit.Rule;
+
+import org.chromium.base.CommandLine;
+import org.chromium.base.CommandLineInitUtil;
+import org.chromium.base.test.BaseTestResult.PreTestHook;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Provides annotations related to command-line flag handling.
+ *
+ * Uses of these annotations on a derived class will take precedence over uses on its base classes,
+ * so a derived class can add a command-line flag that a base class has removed (or vice versa).
+ * Similarly, uses of these annotations on a test method will take precedence over uses on the
+ * containing class.
+ * <p>
+ * These annonations may also be used on Junit4 Rule classes and on their base classes. Note,
+ * however that the annotation processor only looks at the declared type of the Rule, not its actual
+ * type, so in, for example:
+ *
+ * <pre>
+ * &#64Rule
+ * TestRule mRule = new ChromeActivityTestRule();
+ * </pre>
+ *
+ * will only look for CommandLineFlags annotations on TestRule, not for CommandLineFlags annotations
+ * on ChromeActivityTestRule.
+ * <p>
+ * In addition a rule may not remove flags added by an independently invoked rule, although it may
+ * remove flags added by its base classes.
+ * <p>
+ * Uses of these annotations on the test class or methods take precedence over uses on Rule classes.
+ * <p>
+ * Note that this class should never be instantiated.
+ */
+public final class CommandLineFlags {
+ private static final String DISABLE_FEATURES = "disable-features";
+ private static final String ENABLE_FEATURES = "enable-features";
+
+ /**
+ * Adds command-line flags to the {@link org.chromium.base.CommandLine} for this test.
+ */
+ @Inherited
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.METHOD, ElementType.TYPE})
+ public @interface Add {
+ String[] value();
+ }
+
+ /**
+ * Removes command-line flags from the {@link org.chromium.base.CommandLine} from this test.
+ *
+ * Note that this can only remove flags added via {@link Add} above.
+ */
+ @Inherited
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.METHOD, ElementType.TYPE})
+ public @interface Remove {
+ String[] value();
+ }
+
+ /**
+ * Sets up the CommandLine with the appropriate flags.
+ *
+ * This will add the difference of the sets of flags specified by {@link CommandLineFlags.Add}
+ * and {@link CommandLineFlags.Remove} to the {@link org.chromium.base.CommandLine}. Note that
+ * trying to remove a flag set externally, i.e. by the command-line flags file, will not work.
+ */
+ public static void setUp(AnnotatedElement element) {
+ CommandLine.reset();
+ CommandLineInitUtil.initCommandLine(getTestCmdLineFile());
+ Set<String> enableFeatures = new HashSet<String>();
+ Set<String> disableFeatures = new HashSet<String>();
+ Set<String> flags = getFlags(element);
+ for (String flag : flags) {
+ String[] parsedFlags = flag.split("=", 2);
+ if (parsedFlags.length == 1) {
+ CommandLine.getInstance().appendSwitch(flag);
+ } else if (ENABLE_FEATURES.equals(parsedFlags[0])) {
+ // We collect enable/disable features flags separately and aggregate them because
+ // they may be specified multiple times, in which case the values will trample each
+ // other.
+ Collections.addAll(enableFeatures, parsedFlags[1].split(","));
+ } else if (DISABLE_FEATURES.equals(parsedFlags[0])) {
+ Collections.addAll(disableFeatures, parsedFlags[1].split(","));
+ } else {
+ CommandLine.getInstance().appendSwitchWithValue(parsedFlags[0], parsedFlags[1]);
+ }
+ }
+
+ if (enableFeatures.size() > 0) {
+ CommandLine.getInstance().appendSwitchWithValue(
+ ENABLE_FEATURES, TextUtils.join(",", enableFeatures));
+ }
+ if (disableFeatures.size() > 0) {
+ CommandLine.getInstance().appendSwitchWithValue(
+ DISABLE_FEATURES, TextUtils.join(",", disableFeatures));
+ }
+ }
+
+ private static Set<String> getFlags(AnnotatedElement type) {
+ Set<String> rule_flags = new HashSet<>();
+ updateFlagsForElement(type, rule_flags);
+ return rule_flags;
+ }
+
+ private static void updateFlagsForElement(AnnotatedElement element, Set<String> flags) {
+ if (element instanceof Class<?>) {
+ // Get flags from rules within the class.
+ for (Field field : ((Class<?>) element).getFields()) {
+ if (field.isAnnotationPresent(Rule.class)) {
+ // The order in which fields are returned is undefined, so, for consistency,
+ // a rule must not remove a flag added by a different rule. Ensure this by
+ // initially getting the flags into a new set.
+ Set<String> rule_flags = getFlags(field.getType());
+ flags.addAll(rule_flags);
+ }
+ }
+ for (Method method : ((Class<?>) element).getMethods()) {
+ if (method.isAnnotationPresent(Rule.class)) {
+ // The order in which methods are returned is undefined, so, for consistency,
+ // a rule must not remove a flag added by a different rule. Ensure this by
+ // initially getting the flags into a new set.
+ Set<String> rule_flags = getFlags(method.getReturnType());
+ flags.addAll(rule_flags);
+ }
+ }
+ }
+
+ // Add the flags from the parent. Override any flags defined by the rules.
+ AnnotatedElement parent = (element instanceof Method)
+ ? ((Method) element).getDeclaringClass()
+ : ((Class<?>) element).getSuperclass();
+ if (parent != null) updateFlagsForElement(parent, flags);
+
+ // Flags on the element itself override all other flag sources.
+ if (element.isAnnotationPresent(CommandLineFlags.Add.class)) {
+ flags.addAll(
+ Arrays.asList(element.getAnnotation(CommandLineFlags.Add.class).value()));
+ }
+
+ if (element.isAnnotationPresent(CommandLineFlags.Remove.class)) {
+ List<String> flagsToRemove =
+ Arrays.asList(element.getAnnotation(CommandLineFlags.Remove.class).value());
+ for (String flagToRemove : flagsToRemove) {
+ // If your test fails here, you have tried to remove a command-line flag via
+ // CommandLineFlags.Remove that was loaded into CommandLine via something other
+ // than CommandLineFlags.Add (probably the command-line flag file).
+ Assert.assertFalse("Unable to remove command-line flag \"" + flagToRemove + "\".",
+ CommandLine.getInstance().hasSwitch(flagToRemove));
+ }
+ flags.removeAll(flagsToRemove);
+ }
+ }
+
+ private CommandLineFlags() {
+ throw new AssertionError("CommandLineFlags is a non-instantiable class");
+ }
+
+ public static PreTestHook getRegistrationHook() {
+ return (targetContext, testMethod) -> CommandLineFlags.setUp(testMethod);
+ }
+
+ public static String getTestCmdLineFile() {
+ return "test-cmdline-file";
+ }
+}