// Copyright 2017 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.devtools.common.options;

import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import static com.google.common.truth.TruthJUnit.assume;
import static org.junit.Assert.assertThrows;

import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.InvocationPolicy;
import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.SetValue;
import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.SetValue.Behavior;
import com.google.testing.junit.testparameterinjector.TestParameter;
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
import org.junit.Test;
import org.junit.runner.RunWith;

/** Test InvocationPolicies with the SetValues operation. */
@RunWith(TestParameterInjector.class)
public class InvocationPolicySetValueTest extends InvocationPolicyEnforcerTestBase {

  public static final String BUILD_COMMAND = "build";
  public static final String TEST_STRING_USER_VALUE = "user value";
  public static final String TEST_STRING_USER_VALUE_2 = "user value 2";
  public static final String TEST_STRING_POLICY_VALUE = "policy value";
  public static final String TEST_STRING_POLICY_VALUE_2 = "policy value 2";

  /** Tests that policy overwrites a value when that value is from the user. */
  @Test
  public void finalValueIgnoreOverrides_overwritesUserSetting() throws Exception {
    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
    invocationPolicy
        .addFlagPoliciesBuilder()
        .setFlagName("test_string")
        .getSetValueBuilder()
        .setBehavior(Behavior.FINAL_VALUE_IGNORE_OVERRIDES)
        .addFlagValue(TEST_STRING_POLICY_VALUE);

    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
    parser.parse("--test_string=" + TEST_STRING_USER_VALUE);

    TestOptions testOptions = getTestOptions();
    assertThat(testOptions.testString).isEqualTo(TEST_STRING_USER_VALUE);

    enforcer.enforce(parser, BUILD_COMMAND);

    // Get the options again after policy enforcement.
    testOptions = getTestOptions();
    assertThat(testOptions.testString).isEqualTo(TEST_STRING_POLICY_VALUE);
    assertThat(
            parser.asCompleteListOfParsedOptions().stream()
                .map(ParsedOptionDescription::getCommandLineForm))
        .containsExactly(
            "--test_string=" + TEST_STRING_USER_VALUE, "--test_string=" + TEST_STRING_POLICY_VALUE)
        .inOrder();
  }

  /**
   * Tests that policy overwrites a value when the user doesn't specify the value (i.e., the value
   * is from the flag's default from its definition).
   */
  @Test
  public void finalValueIgnoreOverrides_overwritesDefault() throws Exception {
    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
    invocationPolicy
        .addFlagPoliciesBuilder()
        .setFlagName("test_string")
        .getSetValueBuilder()
        .setBehavior(Behavior.FINAL_VALUE_IGNORE_OVERRIDES)
        .addFlagValue(TEST_STRING_POLICY_VALUE);

    // No user value.
    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);

    // All the flags should be their default value.
    TestOptions testOptions = getTestOptions();
    assertThat(testOptions.testString).isEqualTo(TestOptions.TEST_STRING_DEFAULT);

    enforcer.enforce(parser, BUILD_COMMAND);

    // Get the options again after policy enforcement.
    testOptions = getTestOptions();
    assertThat(testOptions.testString).isEqualTo(TEST_STRING_POLICY_VALUE);
  }

  /** Tests that SetValue overwrites the user's value when the flag allows multiple values. */
  @Test
  public void finalValueIgnoreOverrides_flagWithMultipleValues_overwritesUserSetting()
      throws Exception {
    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
    invocationPolicy
        .addFlagPoliciesBuilder()
        .setFlagName("test_multiple_string")
        .getSetValueBuilder()
        .setBehavior(Behavior.FINAL_VALUE_IGNORE_OVERRIDES)
        .addFlagValue(TEST_STRING_POLICY_VALUE)
        .addFlagValue(TEST_STRING_POLICY_VALUE_2);

    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
    parser.parse(
        "--test_multiple_string=" + TEST_STRING_USER_VALUE,
        "--test_multiple_string=" + TEST_STRING_USER_VALUE_2);

    // Options should not be modified by running the parser through OptionsPolicyEnforcer.create().
    TestOptions testOptions = getTestOptions();
    assertThat(testOptions.testMultipleString)
        .containsExactly(TEST_STRING_USER_VALUE, TEST_STRING_USER_VALUE_2)
        .inOrder();

    enforcer.enforce(parser, BUILD_COMMAND);

    // Get the options again after policy enforcement.
    testOptions = getTestOptions();
    assertThat(testOptions.testMultipleString)
        .containsExactly(TEST_STRING_POLICY_VALUE, TEST_STRING_POLICY_VALUE_2)
        .inOrder();
  }

  /**
   * Tests that policy overwrites the default value when the flag allows multiple values and the
   * user doesn't provide a value.
   */
  @Test
  public void finalValueIgnoreOverrides_flagWithMultipleValues_overwritesDefault()
      throws Exception {
    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
    invocationPolicy
        .addFlagPoliciesBuilder()
        .setFlagName("test_multiple_string")
        .getSetValueBuilder()
        .setBehavior(Behavior.FINAL_VALUE_IGNORE_OVERRIDES)
        .addFlagValue(TEST_STRING_POLICY_VALUE)
        .addFlagValue(TEST_STRING_POLICY_VALUE_2);

    // No user value.
    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);

    // Repeatable flags always default to the empty list.
    TestOptions testOptions = getTestOptions();
    assertThat(testOptions.testMultipleString).isEmpty();

    enforcer.enforce(parser, BUILD_COMMAND);

    // Options should now be the values from the policy.
    testOptions = getTestOptions();
    assertThat(testOptions.testMultipleString)
        .containsExactly(TEST_STRING_POLICY_VALUE, TEST_STRING_POLICY_VALUE_2)
        .inOrder();
  }

  @Test
  public void setMultipleValuesForSingleValuedFlag_fails(@TestParameter Behavior behavior)
      throws Exception {
    assume().that(behavior).isNotEqualTo(Behavior.UNDEFINED);
    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
    invocationPolicy
        .addFlagPoliciesBuilder()
        .setFlagName("test_string") // Not repeatable flag.
        .getSetValueBuilder()
        .setBehavior(behavior)
        .addFlagValue(TEST_STRING_POLICY_VALUE) // Has multiple values.
        .addFlagValue(TEST_STRING_POLICY_VALUE_2);

    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);

    assertThrows(OptionsParsingException.class, () -> enforcer.enforce(parser, BUILD_COMMAND));
  }

  @Test
  public void append_appendsToMultipleValuedFlag() throws Exception {
    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
    invocationPolicy
        .addFlagPoliciesBuilder()
        .setFlagName("test_multiple_string")
        .getSetValueBuilder()
        .setBehavior(Behavior.APPEND)
        .addFlagValue(TEST_STRING_POLICY_VALUE)
        .addFlagValue(TEST_STRING_POLICY_VALUE_2);

    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
    parser.parse(
        "--test_multiple_string=" + TEST_STRING_USER_VALUE,
        "--test_multiple_string=" + TEST_STRING_USER_VALUE_2);

    // Options should not be modified by running the parser through OptionsPolicyEnforcer.create().
    TestOptions testOptions = getTestOptions();
    assertThat(testOptions.testMultipleString)
        .containsExactly(TEST_STRING_USER_VALUE, TEST_STRING_USER_VALUE_2)
        .inOrder();

    enforcer.enforce(parser, BUILD_COMMAND);

    // Get the options again after policy enforcement.
    testOptions = getTestOptions();
    assertThat(testOptions.testMultipleString)
        .containsExactly(
            TEST_STRING_USER_VALUE,
            TEST_STRING_USER_VALUE_2,
            TEST_STRING_POLICY_VALUE,
            TEST_STRING_POLICY_VALUE_2)
        .inOrder();
  }

  @Test
  public void setFlagWithExpansion_finalValueIgnoreOverrides_setsExpandedValuesAsFinal(
      @TestParameter({"null", "", "some value"}) String value) throws Exception {
    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
    SetValue.Builder setValue =
        invocationPolicy
            .addFlagPoliciesBuilder()
            .setFlagName("test_expansion")
            // SetValue must have no values for a Void flag.
            .getSetValueBuilder()
            .setBehavior(Behavior.FINAL_VALUE_IGNORE_OVERRIDES);
    if (value != null) {
      setValue.addFlagValue(value);
    }

    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
    // Unrelated flag, but --test_expansion is not set
    parser.parse("--test_string=throwaway value");

    // The flags that --test_expansion expands into should still be their default values
    TestOptions testOptions = getTestOptions();
    assertThat(testOptions.expandedA).isEqualTo(TestOptions.EXPANDED_A_DEFAULT);
    assertThat(testOptions.expandedB).isEqualTo(TestOptions.EXPANDED_B_DEFAULT);
    assertThat(testOptions.expandedC).isEqualTo(TestOptions.EXPANDED_C_DEFAULT);
    assertThat(testOptions.expandedD).isEqualTo(TestOptions.EXPANDED_D_DEFAULT);

    enforcer.enforce(parser, BUILD_COMMAND);

    // After policy enforcement, the flags should be the values from --test_expansion
    testOptions = getTestOptions();
    assertThat(testOptions.expandedA).isEqualTo(TestOptions.EXPANDED_A_TEST_EXPANSION);
    assertThat(testOptions.expandedB).isEqualTo(TestOptions.EXPANDED_B_TEST_EXPANSION);
    assertThat(testOptions.expandedC).isEqualTo(TestOptions.EXPANDED_C_TEST_EXPANSION);
    assertThat(testOptions.expandedD).isEqualTo(TestOptions.EXPANDED_D_TEST_EXPANSION);
  }

  @Test
  public void finalValueIgnoreOverrides_flagExpandingToExpansion_setsRecursiveValues()
      throws Exception {
    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
    invocationPolicy
        .addFlagPoliciesBuilder()
        .setFlagName("test_recursive_expansion_top_level")
        .getSetValueBuilder()
        .setBehavior(Behavior.FINAL_VALUE_IGNORE_OVERRIDES);

    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
    // Unrelated flag, but --test_expansion is not set
    parser.parse("--test_string=throwaway value");

    // The flags that --test_expansion expands into should still be their default values
    TestOptions testOptions = getTestOptions();
    assertThat(testOptions.expandedA).isEqualTo(TestOptions.EXPANDED_A_DEFAULT);
    assertThat(testOptions.expandedB).isEqualTo(TestOptions.EXPANDED_B_DEFAULT);
    assertThat(testOptions.expandedC).isEqualTo(TestOptions.EXPANDED_C_DEFAULT);
    assertThat(testOptions.expandedD).isEqualTo(TestOptions.EXPANDED_D_DEFAULT);

    enforcer.enforce(parser, BUILD_COMMAND);

    // After policy enforcement, the flags should be the values from the expansion flag
    testOptions = getTestOptions();
    assertThat(testOptions.expandedA).isEqualTo(TestOptions.EXPANDED_A_TEST_RECURSIVE_EXPANSION);
    assertThat(testOptions.expandedB).isEqualTo(TestOptions.EXPANDED_B_TEST_RECURSIVE_EXPANSION);
    assertThat(testOptions.expandedC).isEqualTo(TestOptions.EXPANDED_C_TEST_RECURSIVE_EXPANSION);
    assertThat(testOptions.expandedD).isEqualTo(TestOptions.EXPANDED_D_TEST_RECURSIVE_EXPANSION);
  }

  @Test
  public void allowOverrides_setFlagWithExpansion_keepsUserSpecifiedFlag() throws Exception {
    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
    invocationPolicy
        .addFlagPoliciesBuilder()
        .setFlagName("test_expansion")
        .getSetValueBuilder()
        .setBehavior(Behavior.ALLOW_OVERRIDES)
        .addFlagValue(""); // this value is arbitrary, the value for a Void flag is ignored

    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
    // Unrelated flag, but --test_expansion is not set
    parser.parse("--expanded_c=23");

    // The flags that --test_expansion expands into should still be their default values
    // except for the explicitly marked flag.
    TestOptions testOptions = getTestOptions();
    assertThat(testOptions.expandedA).isEqualTo(TestOptions.EXPANDED_B_DEFAULT);
    assertThat(testOptions.expandedB).isEqualTo(TestOptions.EXPANDED_B_DEFAULT);
    assertThat(testOptions.expandedC).isEqualTo(23);
    assertThat(testOptions.expandedD).isEqualTo(TestOptions.EXPANDED_D_DEFAULT);

    enforcer.enforce(parser, "build");

    // After policy enforcement, the flags should be the values from --test_expansion,
    // except for the user-set value, since the expansion flag was set to overridable.
    testOptions = getTestOptions();
    assertThat(testOptions.expandedA).isEqualTo(TestOptions.EXPANDED_A_TEST_EXPANSION);
    assertThat(testOptions.expandedB).isEqualTo(TestOptions.EXPANDED_B_TEST_EXPANSION);
    assertThat(testOptions.expandedC).isEqualTo(23);
    assertThat(testOptions.expandedD).isEqualTo(TestOptions.EXPANDED_D_TEST_EXPANSION);
  }

  @Test
  public void allowOverrides_flagExpandingToRepeatingFlag_appendsRepeatedValues() throws Exception {
    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
    invocationPolicy
        .addFlagPoliciesBuilder()
        .setFlagName("test_expansion_to_repeatable")
        .getSetValueBuilder()
        // SetValue must have no values for a Void flag.
        .setBehavior(Behavior.ALLOW_OVERRIDES);

    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
    // Unrelated flag, but --test_expansion is not set
    parser.parse("--test_multiple_string=foo");

    // The flags that --test_expansion expands into should still be their default values
    // except for the explicitly marked flag.
    TestOptions testOptions = getTestOptions();
    assertThat(testOptions.testMultipleString).containsExactly("foo");

    enforcer.enforce(parser, "build");

    // After policy enforcement, the flags should be the values from --test_expansion,
    // except for the user-set value, since the expansion flag was set to overridable.
    testOptions = getTestOptions();
    assertThat(testOptions.testMultipleString).containsExactly(
        "foo", TestOptions.EXPANDED_MULTIPLE_1, TestOptions.EXPANDED_MULTIPLE_2);
  }

  @Test
  public void finalValueIgnoreOverrides_setFlagWithExpansion_overwritesUserSettingForExpandedFlag()
      throws Exception {
    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
    invocationPolicy
        .addFlagPoliciesBuilder()
        .setFlagName("test_expansion")
        // SetValue must have no values for a Void flag.
        .getSetValueBuilder()
        .setBehavior(Behavior.FINAL_VALUE_IGNORE_OVERRIDES);

    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
    // Unrelated flag, but --test_expansion is not set
    parser.parse("--expanded_c=23");

    // The flags that --test_expansion expands into should still be their default values
    // except for the explicitly marked flag.
    TestOptions testOptions = getTestOptions();
    assertThat(testOptions.expandedA).isEqualTo(TestOptions.EXPANDED_B_DEFAULT);
    assertThat(testOptions.expandedB).isEqualTo(TestOptions.EXPANDED_B_DEFAULT);
    assertThat(testOptions.expandedC).isEqualTo(23);
    assertThat(testOptions.expandedD).isEqualTo(TestOptions.EXPANDED_D_DEFAULT);

    enforcer.enforce(parser, "build");

    // After policy enforcement, the flags should be the values from --test_expansion,
    // including the value that the user tried to set, since the expansion flag was set
    // non-overridably.
    testOptions = getTestOptions();
    assertThat(testOptions.expandedA).isEqualTo(TestOptions.EXPANDED_A_TEST_EXPANSION);
    assertThat(testOptions.expandedB).isEqualTo(TestOptions.EXPANDED_B_TEST_EXPANSION);
    assertThat(testOptions.expandedC).isEqualTo(TestOptions.EXPANDED_C_TEST_EXPANSION);
    assertThat(testOptions.expandedD).isEqualTo(TestOptions.EXPANDED_D_TEST_EXPANSION);
  }

  @Test
  public void finalValueIgnoreOverrides_flagWithExpansionToRepeatingFlag_overwritesUserSetting()
      throws Exception {
    InvocationPolicy.Builder invocationPolicyBuilder = InvocationPolicy.newBuilder();
    invocationPolicyBuilder
        .addFlagPoliciesBuilder()
        .setFlagName("test_expansion_to_repeatable")
        // SetValue must have no values for a Void flag.
        .getSetValueBuilder()
        .setBehavior(Behavior.FINAL_VALUE_IGNORE_OVERRIDES);

    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicyBuilder);
    // Unrelated flag, but --test_expansion is not set
    parser.parse("--test_multiple_string=foo");

    // The flags that --test_expansion expands into should still be their default values
    // except for the explicitly marked flag.
    assertThat(getTestOptions().testMultipleString).contains("foo");

    enforcer.enforce(parser, "build");

    // After policy enforcement, the flag should no longer have the user's value.
    assertThat(getTestOptions().testMultipleString)
        .containsExactly(TestOptions.EXPANDED_MULTIPLE_1, TestOptions.EXPANDED_MULTIPLE_2);
  }

  @Test
  public void finalValueIgnoreOverrides_overwritesUserSettingFromExpandedFlag() throws Exception {
    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
    invocationPolicy
        .addFlagPoliciesBuilder()
        .setFlagName("expanded_c")
        .getSetValueBuilder()
        .setBehavior(Behavior.FINAL_VALUE_IGNORE_OVERRIDES)
        .addFlagValue("64");

    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
    parser.parse("--test_expansion");

    // --test_expansion should set the values from its expansion
    TestOptions testOptions = getTestOptions();
    assertThat(testOptions.expandedA).isEqualTo(TestOptions.EXPANDED_A_TEST_EXPANSION);
    assertThat(testOptions.expandedB).isEqualTo(TestOptions.EXPANDED_B_TEST_EXPANSION);
    assertThat(testOptions.expandedC).isEqualTo(TestOptions.EXPANDED_C_TEST_EXPANSION);
    assertThat(testOptions.expandedD).isEqualTo(TestOptions.EXPANDED_D_TEST_EXPANSION);

    enforcer.enforce(parser, BUILD_COMMAND);

    // After policy enforcement, expanded_c should be set to 64 from the policy, but the
    // flags should remain the same from the expansion of --test_expansion.
    testOptions = getTestOptions();
    assertThat(testOptions.expandedA).isEqualTo(TestOptions.EXPANDED_A_TEST_EXPANSION);
    assertThat(testOptions.expandedB).isEqualTo(TestOptions.EXPANDED_B_TEST_EXPANSION);
    assertThat(testOptions.expandedC).isEqualTo(64);
    assertThat(testOptions.expandedD).isEqualTo(TestOptions.EXPANDED_D_TEST_EXPANSION);
  }

  @Test
  public void finalValueIgnoreOverrides_overwritesImplicitRequirementFromUserSetting()
      throws Exception {
    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
    invocationPolicy
        .addFlagPoliciesBuilder()
        .setFlagName("implicit_requirement_a")
        .getSetValueBuilder()
        .setBehavior(Behavior.FINAL_VALUE_IGNORE_OVERRIDES)
        .addFlagValue(TEST_STRING_POLICY_VALUE);

    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
    parser.parse("--test_implicit_requirement=" + TEST_STRING_USER_VALUE);

    // test_implicit_requirement sets implicit_requirement_a to "foo"
    TestOptions testOptions = getTestOptions();
    assertThat(testOptions.testImplicitRequirement).isEqualTo(TEST_STRING_USER_VALUE);
    assertThat(testOptions.implicitRequirementA)
        .isEqualTo(TestOptions.IMPLICIT_REQUIREMENT_A_REQUIRED);

    enforcer.enforce(parser, BUILD_COMMAND);

    testOptions = getTestOptions();
    assertThat(testOptions.testImplicitRequirement).isEqualTo(TEST_STRING_USER_VALUE);
    assertThat(testOptions.implicitRequirementA).isEqualTo(TEST_STRING_POLICY_VALUE);
  }

  @Test
  public void testSetValueWithImplicitlyRequiredFlags() throws Exception {
    InvocationPolicy.Builder invocationPolicyBuilder = InvocationPolicy.newBuilder();
    invocationPolicyBuilder
        .addFlagPoliciesBuilder()
        .setFlagName("test_implicit_requirement")
        .getSetValueBuilder()
        .setBehavior(Behavior.FINAL_VALUE_IGNORE_OVERRIDES)
        .addFlagValue(TEST_STRING_POLICY_VALUE);
    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicyBuilder);
    parser.parse("--implicit_requirement_a=" + TEST_STRING_USER_VALUE);
    TestOptions testOptions = getTestOptions();
    assertThat(testOptions.implicitRequirementA).isEqualTo(TEST_STRING_USER_VALUE);

    enforcer.enforce(parser, BUILD_COMMAND);

    testOptions = getTestOptions();
    assertThat(testOptions.testImplicitRequirement).isEqualTo(TEST_STRING_POLICY_VALUE);
    assertThat(testOptions.implicitRequirementA)
        .isEqualTo(TestOptions.IMPLICIT_REQUIREMENT_A_REQUIRED);
  }

  @Test
  public void allowOverrides_leavesUserSetting() throws Exception {
    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
    invocationPolicy
        .addFlagPoliciesBuilder()
        .setFlagName("test_string")
        .getSetValueBuilder()
        .setBehavior(Behavior.ALLOW_OVERRIDES)
        .addFlagValue(TEST_STRING_POLICY_VALUE);

    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
    parser.parse("--test_string=" + TEST_STRING_USER_VALUE);

    TestOptions testOptions = getTestOptions();
    assertThat(testOptions.testString).isEqualTo(TEST_STRING_USER_VALUE);

    enforcer.enforce(parser, BUILD_COMMAND);

    // Even though the policy sets the value for test_string, the policy is overridable and the
    // user set the value, so it should be the user's value.
    testOptions = getTestOptions();
    assertThat(testOptions.testString).isEqualTo(TEST_STRING_USER_VALUE);
  }

  @Test
  public void setFlagValueWithNoValue_fails(@TestParameter Behavior behavior) throws Exception {
    assume().that(behavior).isNotEqualTo(Behavior.UNDEFINED);
    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
    invocationPolicy
        .addFlagPoliciesBuilder()
        .setFlagName("test_string")
        .getSetValueBuilder()
        .setBehavior(behavior); // No value.

    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
    parser.parse("--test_string=" + TEST_STRING_USER_VALUE);

    TestOptions testOptions = getTestOptions();
    assertThat(testOptions.testString).isEqualTo(TEST_STRING_USER_VALUE);

    assertThrows(OptionsParsingException.class, () -> enforcer.enforce(parser, BUILD_COMMAND));
  }

  @Test
  public void enforce_setValueWithUndefinedBehavior_fails(
      @TestParameter boolean hasBehavior,
      @TestParameter({"test_string", "test_expansion", "test_implicit_requirement"})
          String flagName)
      throws Exception {
    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
    SetValue.Builder setValue =
        invocationPolicy
            .addFlagPoliciesBuilder()
            .setFlagName(flagName)
            .getSetValueBuilder()
            .addFlagValue("any value");
    if (hasBehavior) {
      setValue.setBehavior(Behavior.UNDEFINED);
    }
    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);

    OptionsParsingException e =
        assertThrows(OptionsParsingException.class, () -> enforcer.enforce(parser, BUILD_COMMAND));
    assertThat(e)
        .hasMessageThat()
        .startsWith(
            String.format(
                "SetValue operation from invocation policy for has an undefined behavior:"
                    + " flag_name: \"%s\"\n"
                    + "set_value {\n",
                flagName));
  }

  @Test
  public void enforce_policySettingConfig_fails(@TestParameter Behavior behavior) throws Exception {
    assume().that(behavior).isNotEqualTo(Behavior.UNDEFINED);
    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
    invocationPolicy
        .addFlagPoliciesBuilder()
        .setFlagName("config")
        .getSetValueBuilder()
        .setBehavior(behavior)
        .addFlagValue("foo");

    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
    parser.parse();
    OptionsParsingException expected =
        assertThrows(OptionsParsingException.class, () -> enforcer.enforce(parser, BUILD_COMMAND));
    assertThat(expected)
        .hasMessageThat()
        .startsWith(
            "Invocation policy is applied after --config expansion, changing config values now "
                + "would have no effect and is disallowed to prevent confusion. Please remove "
                + "the following policy : flag_name: \"config\"\n"
                + "set_value {\n"
                + "  flag_value: \"foo\"\n"
                + "  behavior: "
                + behavior
                + "\n");
  }

  @Test
  public void enforce_setValueForNonexistentFlag_doesNothing(@TestParameter Behavior behavior)
      throws Exception {
    assume().that(behavior).isNotEqualTo(Behavior.UNDEFINED);
    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
    invocationPolicy
        .addFlagPoliciesBuilder()
        .setFlagName("nonexistent")
        .getSetValueBuilder()
        .setBehavior(behavior)
        .addFlagValue("hello");
    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
    parser.parse();

    enforcer.enforce(parser, "build");

    assertThat(getTestOptions()).isEqualTo(Options.getDefaults(TestOptions.class));
  }
}
