Fix terminal output expansion flag spacing & add implicit requirements.

Add unit tests for individual flag usage output. Also move around some of the sample expansion function test options, which could be used more widely, to minimize recreating the boilerplate samples.

RELNOTES: None.
PiperOrigin-RevId: 165478776
diff --git a/src/test/java/com/google/devtools/common/options/TestOptions.java b/src/test/java/com/google/devtools/common/options/TestOptions.java
index 7dc0228..4fa0309 100644
--- a/src/test/java/com/google/devtools/common/options/TestOptions.java
+++ b/src/test/java/com/google/devtools/common/options/TestOptions.java
@@ -16,6 +16,8 @@
 import com.google.common.collect.ImmutableList;
 import com.google.devtools.common.options.InvocationPolicyEnforcerTestBase.ToListConverter;
 import java.util.List;
+import java.util.Map;
+import java.util.TreeSet;
 
 /** Options for testing. */
 public class TestOptions extends OptionsBase {
@@ -30,7 +32,8 @@
     name = "test_string",
     documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
     effectTags = {OptionEffectTag.NO_OP},
-    defaultValue = TEST_STRING_DEFAULT
+    defaultValue = TEST_STRING_DEFAULT,
+    help = "a string-valued option to test simple option operations"
   )
   public String testString;
 
@@ -43,7 +46,8 @@
     documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
     effectTags = {OptionEffectTag.NO_OP},
     defaultValue = "", // default value is ignored when allowMultiple=true.
-    allowMultiple = true
+    allowMultiple = true,
+    help = "a repeatable string-valued flag with its own unhelpful help text"
   )
   public List<String> testMultipleString;
 
@@ -57,7 +61,10 @@
     effectTags = {OptionEffectTag.NO_OP},
     defaultValue = "",
     allowMultiple = true,
-    converter = ToListConverter.class
+    converter = ToListConverter.class,
+    help =
+        "a repeatable flag that accepts lists, but doesn't want to have lists of lists "
+            + "as a final type"
   )
   public List<String> testListConverters;
 
@@ -82,7 +89,8 @@
       "42",
       "--expanded_d",
       "bar"
-    }
+    },
+    help = "this expands to an alphabet soup."
   )
   public Void testExpansion;
 
@@ -99,7 +107,8 @@
     expansion = {
       "--test_recursive_expansion_middle1",
       "--test_recursive_expansion_middle2",
-    }
+    },
+    help = "Lets the children do all the work."
   )
   public Void testRecursiveExpansionTopLevel;
 
@@ -153,7 +162,8 @@
     name = "expanded_c",
     documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
     effectTags = {OptionEffectTag.NO_OP},
-    defaultValue = "12"
+    defaultValue = "12",
+    help = "an int-value'd flag used to test expansion logic"
   )
   public int expandedC;
 
@@ -182,7 +192,8 @@
     expansion = {
       "--test_multiple_string=expandedFirstValue",
       "--test_multiple_string=expandedSecondValue"
-    }
+    },
+    help = "Go forth and multiply, they said."
   )
   public Void testExpansionToRepeatable;
 
@@ -198,7 +209,8 @@
     documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
     effectTags = {OptionEffectTag.NO_OP},
     defaultValue = TEST_IMPLICIT_REQUIREMENT_DEFAULT,
-    implicitRequirements = {"--implicit_requirement_a=" + IMPLICIT_REQUIREMENT_A_REQUIRED}
+    implicitRequirements = {"--implicit_requirement_a=" + IMPLICIT_REQUIREMENT_A_REQUIRED},
+    help = "this option really needs that other one, isolation of purpose has failed."
   )
   public String testImplicitRequirement;
 
@@ -227,6 +239,10 @@
   public static final String TEST_EXPANSION_FUNCTION_ACCEPTED_VALUE = "valueA";
   public static final String EXPANDED_D_EXPANSION_FUNCTION_VALUE = "expanded valueA";
 
+  /*
+   * Expansion function flags
+   */
+
   /** Used for testing an expansion flag that requires a value. */
   public static class TestExpansionFunction implements ExpansionFunction {
     @Override
@@ -248,7 +264,8 @@
     defaultValue = "null",
     documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
     effectTags = {OptionEffectTag.NO_OP},
-    expansionFunction = TestExpansionFunction.class
+    expansionFunction = TestExpansionFunction.class,
+    help = "this is for testing expansion-by-function functionality."
   )
   public Void testExpansionFunction;
 
@@ -270,4 +287,79 @@
     expansionFunction = TestVoidExpansionFunction.class
   )
   public Void testVoidExpansionFunction;
+
+  // Interestingly, the class needs to be public, or else the default constructor ends up not
+  // being public and the expander can't be instantiated.
+  /**
+   * Defines an expansion function that looks at other options defined with it and expands to
+   * options that match a pattern.
+   */
+  public static class ExpansionDependsOnOtherOptionDefinitions implements ExpansionFunction {
+    @Override
+    public ImmutableList<String> getExpansion(ExpansionContext context) {
+      TreeSet<String> flags = new TreeSet<>();
+      for (Map.Entry<String, ?> entry : context.getOptionsData().getAllNamedFields()) {
+        if (entry.getKey().startsWith("specialexp_")) {
+          flags.add("--" + entry.getKey());
+        }
+      }
+      return ImmutableList.copyOf(flags);
+    }
+  }
+
+  @Option(
+    name = "prefix_expansion",
+    documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+    effectTags = {OptionEffectTag.NO_OP},
+    defaultValue = "null",
+    expansionFunction = ExpansionDependsOnOtherOptionDefinitions.class,
+    help = "Expands to all options with a specific prefix."
+  )
+  public Void specialExp;
+
+  /**
+   * Defines an expansion function that adapts its expansion to the value assigned to the original
+   * expansion option.
+   */
+  public static class ExpansionDependsOnFlagValue implements ExpansionFunction {
+    @Override
+    public ImmutableList<String> getExpansion(ExpansionContext context)
+        throws OptionsParsingException {
+      String value = context.getUnparsedValue();
+      if (value == null) {
+        throw new ExpansionNeedsValueException("Expansion value not set.");
+      }
+      if (value.equals("foo_bar")) {
+        return ImmutableList.<String>of("--specialexp_foo", "--specialexp_bar");
+      }
+
+      throw new OptionsParsingException("Unexpected expansion argument: " + value);
+    }
+  }
+
+  @Option(
+    name = "dynamicexp",
+    documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+    effectTags = {OptionEffectTag.NO_OP},
+    defaultValue = "null",
+    expansionFunction = ExpansionDependsOnFlagValue.class,
+    help = "Expands depending on the value provided."
+  )
+  public Void variableExpansion;
+
+  @Option(
+    name = "specialexp_foo",
+    documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+    effectTags = {OptionEffectTag.NO_OP},
+    defaultValue = "false"
+  )
+  public boolean specialExpFoo;
+
+  @Option(
+    name = "specialexp_bar",
+    documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+    effectTags = {OptionEffectTag.NO_OP},
+    defaultValue = "false"
+  )
+  public boolean specialExpBar;
 }