// Copyright 2015 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.build.lib.analysis.config; import static com.google.common.truth.Truth.assertThat; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.devtools.common.options.Converter; import com.google.devtools.common.options.Option; import com.google.devtools.common.options.OptionsBase; import com.google.devtools.common.options.OptionsParser; import com.google.devtools.common.options.OptionsParser.OptionUsageRestrictions; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** Tests for {@link TransitiveOptionDetails}. */ @RunWith(JUnit4.class) public class TransitiveOptionDetailsTest { /** Instantiates the given options classes, parsing the given options as well. */ public Iterable<OptionsBase> parseOptions( Iterable<? extends Class<? extends OptionsBase>> optionsClasses, String... options) throws Exception { OptionsParser optionsParser = OptionsParser.newOptionsParser(optionsClasses); optionsParser.setAllowResidue(false); optionsParser.parse(options); ImmutableList.Builder<OptionsBase> output = new ImmutableList.Builder<OptionsBase>(); for (Class<? extends OptionsBase> optionsClass : optionsClasses) { output.add(optionsParser.getOptions(optionsClass)); } return output.build(); } /** Example converter for working with options with converters. */ public static final class Optionalizer implements Converter<Optional<String>> { @Override public Optional<String> convert(String input) { if ("".equals(input)) { return Optional.<String>absent(); } return Optional.of(input); } @Override public String getTypeDescription() { return "a string"; } } /** Example options class for testing options lookup. */ public static final class Options extends OptionsBase { @Option(name = "boolean_option", defaultValue = "true") public boolean booleanOption; @Option(name = "convertible_option", converter = Optionalizer.class, defaultValue = "") public Optional<String> convertibleOption; @Option(name = "null_default", defaultValue = "null") public String nullDefault; @Option(name = "late_bound_default", defaultValue = "null") public String lateBoundDefault; @Option(name = "multi_option", defaultValue = "n/a (allows multiple)", allowMultiple = true) public List<String> multiOption; @Option( name = "internal option", defaultValue = "secret", optionUsageRestrictions = OptionUsageRestrictions.INTERNAL ) public String internalOption; @Option( name = "internal multi option", defaultValue = "n/a", optionUsageRestrictions = OptionUsageRestrictions.INTERNAL, allowMultiple = true ) public List<String> internalMultiOption; } /** Additional options class for testing options lookup. */ public static final class MoreOptions extends OptionsBase { @Option(name = "other_option", defaultValue = "") public String otherOption; } @Test public void getOptionClass_ReturnsClassOfPresentOptions() throws Exception { TransitiveOptionDetails details = TransitiveOptionDetails.forOptionsWithDefaults( parseOptions(ImmutableList.of(Options.class)), ImmutableMap.<String, Object>of()); assertThat(details.getOptionClass("boolean_option")).isEqualTo(Options.class); } @Test public void getOptionClass_SelectsCorrectClassWhenMultipleArePresent() throws Exception { TransitiveOptionDetails details = TransitiveOptionDetails.forOptionsWithDefaults( parseOptions(ImmutableList.of(Options.class, MoreOptions.class)), ImmutableMap.<String, Object>of()); assertThat(details.getOptionClass("boolean_option")).isEqualTo(Options.class); assertThat(details.getOptionClass("other_option")).isEqualTo(MoreOptions.class); } @Test public void getOptionClass_ReturnsNullIfOptionsClassIsNotPartOfOptionDetails() throws Exception { TransitiveOptionDetails details = TransitiveOptionDetails.forOptionsWithDefaults( parseOptions(ImmutableList.of(Options.class)), ImmutableMap.<String, Object>of()); assertThat(details.getOptionClass("other_option")).isNull(); } @Test public void getOptionClass_SelectsCorrectClassEvenWhenOverridden() throws Exception { TransitiveOptionDetails details = TransitiveOptionDetails.forOptionsWithDefaults( parseOptions(ImmutableList.of(Options.class)), ImmutableMap.<String, Object>of("late_bound_default", "late bound replacement")); assertThat(details.getOptionClass("late_bound_default")).isEqualTo(Options.class); } @Test public void getOptionClass_SelectsCorrectClassEvenWhenValueIsNull() throws Exception { TransitiveOptionDetails details = TransitiveOptionDetails.forOptionsWithDefaults( parseOptions(ImmutableList.of(Options.class)), ImmutableMap.<String, Object>of()); assertThat(details.getOptionClass("null_default")).isEqualTo(Options.class); } @Test public void getOptionClass_ReturnsNullWhenOptionIsUndefined() throws Exception { TransitiveOptionDetails details = TransitiveOptionDetails.forOptionsWithDefaults( parseOptions(ImmutableList.of(Options.class)), ImmutableMap.<String, Object>of()); assertThat(details.getOptionClass("undefined_option")).isNull(); } @Test public void getOptionClass_ReturnsNullWhenOptionIsUndefinedEvenWhenOverridden() throws Exception { TransitiveOptionDetails details = TransitiveOptionDetails.forOptionsWithDefaults( parseOptions(ImmutableList.of(Options.class)), ImmutableMap.<String, Object>of("undefined_option", "override")); assertThat(details.getOptionClass("undefined_option")).isNull(); } @Test public void getOptionClass_ReturnsNullIfOptionIsInternal() throws Exception { TransitiveOptionDetails details = TransitiveOptionDetails.forOptionsWithDefaults( parseOptions(ImmutableList.of(Options.class)), ImmutableMap.<String, Object>of()); assertThat(details.getOptionClass("internal option")).isNull(); } @Test public void getOptionValue_ReturnsDefaultValueIfNotSet() throws Exception { TransitiveOptionDetails details = TransitiveOptionDetails.forOptionsWithDefaults( parseOptions(ImmutableList.of(Options.class)), ImmutableMap.<String, Object>of()); assertThat(details.getOptionValue("boolean_option")).isEqualTo(true); } @Test public void getOptionValue_ReturnsCommandLineValueIfSet() throws Exception { TransitiveOptionDetails details = TransitiveOptionDetails.forOptionsWithDefaults( parseOptions(ImmutableList.of(Options.class), "--noboolean_option"), ImmutableMap.<String, Object>of()); assertThat(details.getOptionValue("boolean_option")).isEqualTo(false); } @Test public void getOptionValue_ReturnsEmptyListForUnspecifiedMultiOptions() throws Exception { TransitiveOptionDetails details = TransitiveOptionDetails.forOptionsWithDefaults( parseOptions(ImmutableList.of(Options.class), "--noboolean_option"), ImmutableMap.<String, Object>of()); assertThat(details.getOptionValue("multi_option")).isEqualTo(ImmutableList.<String>of()); } @Test public void getOptionValue_ReturnsListOfValuesForSpecifiedMultiOptions() throws Exception { TransitiveOptionDetails details = TransitiveOptionDetails.forOptionsWithDefaults( parseOptions( ImmutableList.of(Options.class), "--multi_option=one", "--multi_option=2", "--multi_option=iii"), ImmutableMap.<String, Object>of()); assertThat(details.getOptionValue("multi_option")) .isEqualTo(ImmutableList.of("one", "2", "iii")); } @Test public void getOptionValue_DrawsValuesFromAllOptionsClasses() throws Exception { TransitiveOptionDetails details = TransitiveOptionDetails.forOptionsWithDefaults( parseOptions(ImmutableList.of(Options.class, MoreOptions.class), "--other_option=set"), ImmutableMap.<String, Object>of()); assertThat(details.getOptionValue("other_option")).isEqualTo("set"); } @Test public void getOptionValue_UsesConvertersIfSpecified() throws Exception { TransitiveOptionDetails details = TransitiveOptionDetails.forOptionsWithDefaults( parseOptions(ImmutableList.of(Options.class), "--convertible_option=Set"), ImmutableMap.<String, Object>of()); assertThat(details.getOptionValue("convertible_option")).isEqualTo(Optional.of("Set")); } @Test public void getOptionValue_UsesConvertersForDefaultsIfSpecified() throws Exception { TransitiveOptionDetails details = TransitiveOptionDetails.forOptionsWithDefaults( parseOptions(ImmutableList.of(Options.class)), ImmutableMap.<String, Object>of()); assertThat(details.getOptionValue("convertible_option")).isEqualTo(Optional.<String>absent()); } @Test public void getOptionValue_ReturnsNullIfOptionIsNotDefined() throws Exception { TransitiveOptionDetails details = TransitiveOptionDetails.forOptionsWithDefaults( parseOptions(ImmutableList.of(Options.class)), ImmutableMap.<String, Object>of()); assertThat(details.getOptionValue("undefined_option")).isNull(); } @Test public void getOptionValue_ReturnsNullIfOptionIsInternal() throws Exception { TransitiveOptionDetails details = TransitiveOptionDetails.forOptionsWithDefaults( parseOptions(ImmutableList.of(Options.class)), ImmutableMap.<String, Object>of()); assertThat(details.getOptionValue("internal option")).isNull(); } @Test public void getOptionValue_ReturnsNullIfOptionIsDefinedInNonIncludedOptionsClass() throws Exception { TransitiveOptionDetails details = TransitiveOptionDetails.forOptionsWithDefaults( parseOptions(ImmutableList.of(Options.class)), ImmutableMap.<String, Object>of()); assertThat(details.getOptionValue("other_option")).isNull(); } @Test public void getOptionValue_ReturnsNullIfOptionIsNotDefinedEvenIfOverridden() throws Exception { TransitiveOptionDetails details = TransitiveOptionDetails.forOptionsWithDefaults( parseOptions(ImmutableList.of(Options.class)), ImmutableMap.<String, Object>of("undefined_option_with_override", "default")); assertThat(details.getOptionValue("undefined_option_with_override")).isNull(); } @Test public void getOptionValue_ReturnsNullIfOptionDefaultValueIsNull() throws Exception { TransitiveOptionDetails details = TransitiveOptionDetails.forOptionsWithDefaults( parseOptions(ImmutableList.of(Options.class)), ImmutableMap.<String, Object>of()); assertThat(details.getOptionValue("null_option")).isNull(); } @Test public void getOptionValue_ReturnsOverrideValueIfOptionIsOverriddenAndDefaultsNull() throws Exception { TransitiveOptionDetails details = TransitiveOptionDetails.forOptionsWithDefaults( parseOptions(ImmutableList.of(Options.class)), ImmutableMap.<String, Object>of("late_bound_default", "late bound replacement")); assertThat(details.getOptionValue("late_bound_default")).isEqualTo("late bound replacement"); } @Test public void getOptionValue_ReturnsCommandLineValueIfOptionIsSetOnCommandLineEvenIfOverridden() throws Exception { TransitiveOptionDetails details = TransitiveOptionDetails.forOptionsWithDefaults( parseOptions(ImmutableList.of(Options.class), "--late_bound_default=commando"), ImmutableMap.<String, Object>of("late_bound_default", "late bound replacement")); assertThat(details.getOptionValue("late_bound_default")).isEqualTo("commando"); } @Test public void getOptionValue_ReturnsDefaultValueIfOptionDefaultsNonNullEvenIfOverridden() throws Exception { TransitiveOptionDetails details = TransitiveOptionDetails.forOptionsWithDefaults( parseOptions(ImmutableList.of(Options.class)), ImmutableMap.<String, Object>of("convertible_option", "late bound replacement")); assertThat(details.getOptionValue("convertible_option")).isEqualTo(Optional.<String>absent()); } @Test public void allowsMultipleValues_ReturnsFalseForUndefinedOption() throws Exception { TransitiveOptionDetails details = TransitiveOptionDetails.forOptionsWithDefaults( parseOptions(ImmutableList.of(Options.class)), ImmutableMap.<String, Object>of()); assertThat(details.allowsMultipleValues("undefined_option")).isFalse(); } @Test public void allowsMultipleValues_ReturnsFalseForNonMultiOption() throws Exception { TransitiveOptionDetails details = TransitiveOptionDetails.forOptionsWithDefaults( parseOptions(ImmutableList.of(Options.class)), ImmutableMap.<String, Object>of()); assertThat(details.allowsMultipleValues("boolean_option")).isFalse(); } @Test public void allowsMultipleValues_ReturnsFalseForInternalNonMultiOption() throws Exception { TransitiveOptionDetails details = TransitiveOptionDetails.forOptionsWithDefaults( parseOptions(ImmutableList.of(Options.class)), ImmutableMap.<String, Object>of()); assertThat(details.allowsMultipleValues("internal option")).isFalse(); } @Test public void allowsMultipleValues_ReturnsFalseForInternalMultiOption() throws Exception { TransitiveOptionDetails details = TransitiveOptionDetails.forOptionsWithDefaults( parseOptions(ImmutableList.of(Options.class)), ImmutableMap.<String, Object>of()); assertThat(details.allowsMultipleValues("internal multi option")).isFalse(); } @Test public void allowsMultipleValues_ReturnsTrueForMultiOption() throws Exception { TransitiveOptionDetails details = TransitiveOptionDetails.forOptionsWithDefaults( parseOptions(ImmutableList.of(Options.class)), ImmutableMap.<String, Object>of()); assertThat(details.allowsMultipleValues("multi_option")).isTrue(); } }