| // 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.build.lib.runtime; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static org.junit.Assert.fail; |
| |
| import com.google.devtools.common.options.Converters; |
| import com.google.devtools.common.options.ExpansionFunction; |
| import com.google.devtools.common.options.IsolatedOptionsData; |
| 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.OptionsParsingException; |
| import java.util.List; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| |
| /** |
| * Tests for the Incompatible Changes system (--incompatible_* flags). These go in their own suite |
| * because the options parser doesn't know the business logic for incompatible changes. |
| */ |
| @RunWith(JUnit4.class) |
| public class AllIncompatibleChangesExpansionTest { |
| |
| /** Dummy comment (linter suppression) */ |
| public static class ExampleOptions extends OptionsBase { |
| @Option( |
| name = "all", |
| defaultValue = "null", |
| expansionFunction = AllIncompatibleChangesExpansion.class |
| ) |
| public Void all; |
| |
| @Option(name = "X", defaultValue = "false") |
| public boolean x; |
| |
| @Option(name = "Y", defaultValue = "true") |
| public boolean y; |
| |
| @Option( |
| name = "incompatible_A", |
| category = "incompatible changes", |
| defaultValue = "false", |
| help = "Migrate to A" |
| ) |
| public boolean incompatibleA; |
| |
| @Option( |
| name = "incompatible_B", |
| category = "incompatible changes", |
| defaultValue = "false", |
| help = "Migrate to B" |
| ) |
| public boolean incompatibleB; |
| } |
| |
| /** Dummy comment (linter suppression) */ |
| public static class ExampleExpansionOptions extends OptionsBase { |
| @Option( |
| name = "incompatible_expX", |
| category = "incompatible changes", |
| defaultValue = "null", |
| expansion = {"--X"}, |
| help = "Start using X" |
| ) |
| public Void incompatibleExpX; |
| |
| /** Dummy comment (linter suppression) */ |
| public static class YExpansion implements ExpansionFunction { |
| @Override |
| public String[] getExpansion(IsolatedOptionsData optionsData) { |
| return new String[] {"--noY"}; |
| } |
| } |
| |
| @Option( |
| name = "incompatible_expY", |
| category = "incompatible changes", |
| defaultValue = "null", |
| expansionFunction = YExpansion.class, |
| help = "Stop using Y" |
| ) |
| public Void incompatibleExpY; |
| } |
| |
| @Test |
| public void noChangesSelected() throws OptionsParsingException { |
| OptionsParser parser = OptionsParser.newOptionsParser(ExampleOptions.class); |
| parser.parse(""); |
| ExampleOptions opts = parser.getOptions(ExampleOptions.class); |
| assertThat(opts.x).isFalse(); |
| assertThat(opts.y).isTrue(); |
| assertThat(opts.incompatibleA).isFalse(); |
| assertThat(opts.incompatibleB).isFalse(); |
| } |
| |
| @Test |
| public void allChangesSelected() throws OptionsParsingException { |
| OptionsParser parser = OptionsParser.newOptionsParser(ExampleOptions.class); |
| parser.parse("--all"); |
| ExampleOptions opts = parser.getOptions(ExampleOptions.class); |
| assertThat(opts.x).isFalse(); |
| assertThat(opts.y).isTrue(); |
| assertThat(opts.incompatibleA).isTrue(); |
| assertThat(opts.incompatibleB).isTrue(); |
| } |
| |
| @Test |
| public void rightmostOverrides() throws OptionsParsingException { |
| // Check that all-expansion behaves just like any other expansion flag: |
| // the rightmost setting of any individual option wins. |
| OptionsParser parser = OptionsParser.newOptionsParser(ExampleOptions.class); |
| parser.parse("--noincompatible_A", "--all", "--noincompatible_B"); |
| ExampleOptions opts = parser.getOptions(ExampleOptions.class); |
| assertThat(opts.incompatibleA).isTrue(); |
| assertThat(opts.incompatibleB).isFalse(); |
| } |
| |
| @Test |
| public void expansionOptions() throws OptionsParsingException { |
| // Check that all-expansion behaves just like any other expansion flag: |
| // the rightmost setting of any individual option wins. |
| OptionsParser parser = |
| OptionsParser.newOptionsParser(ExampleOptions.class, ExampleExpansionOptions.class); |
| parser.parse("--all"); |
| ExampleOptions opts = parser.getOptions(ExampleOptions.class); |
| assertThat(opts.x).isTrue(); |
| assertThat(opts.y).isFalse(); |
| assertThat(opts.incompatibleA).isTrue(); |
| assertThat(opts.incompatibleB).isTrue(); |
| } |
| |
| // There's no unit test to check that the expansion of --all is sorted. IsolatedOptionsData is not |
| // exposed from OptionsParser, making it difficult to check, and it's not clear that exposing it |
| // would be worth it. |
| |
| /** |
| * Ensure that we get an {@link OptionsParser.ConstructionException} containing {@code message} |
| * when the incompatible changes in the given {@link OptionsBase} subclass are validated. |
| */ |
| // Because javadoc can't resolve inner classes. |
| @SuppressWarnings("javadoc") |
| private static void assertBadness(Class<? extends OptionsBase> optionsBaseClass, String message) { |
| try { |
| OptionsParser.newOptionsParser(ExampleOptions.class, optionsBaseClass); |
| fail("Should have failed with message \"" + message + "\""); |
| } catch (OptionsParser.ConstructionException e) { |
| assertThat(e.getMessage()).contains(message); |
| } |
| } |
| |
| /** Dummy comment (linter suppression) */ |
| public static class BadNameOptions extends OptionsBase { |
| @Option( |
| name = "badname", |
| category = "incompatible changes", |
| defaultValue = "false", |
| help = "nohelp" |
| ) |
| public boolean bad; |
| } |
| |
| @Test |
| public void badName() { |
| assertBadness( |
| BadNameOptions.class, |
| "Incompatible change option '--badname' must have name " |
| + "starting with \"incompatible_\""); |
| } |
| |
| /** Dummy comment (linter suppression) */ |
| public static class BadCategoryOptions extends OptionsBase { |
| @Option(name = "incompatible_bad", category = "badcat", defaultValue = "false", help = "nohelp") |
| public boolean bad; |
| } |
| |
| @Test |
| public void badCategory() { |
| assertBadness(BadCategoryOptions.class, "must have category \"incompatible changes\""); |
| } |
| |
| /** Dummy comment (linter suppression) */ |
| public static class BadTypeOptions extends OptionsBase { |
| @Option( |
| name = "incompatible_bad", |
| category = "incompatible changes", |
| defaultValue = "0", |
| help = "nohelp" |
| ) |
| public int bad; |
| } |
| |
| @Test |
| public void badType() { |
| assertBadness(BadTypeOptions.class, "must have boolean type"); |
| } |
| |
| /** Dummy comment (linter suppression) */ |
| public static class BadHelpOptions extends OptionsBase { |
| @Option(name = "incompatible_bad", category = "incompatible changes", defaultValue = "false") |
| public boolean bad; |
| } |
| |
| @Test |
| public void badHelp() { |
| assertBadness(BadHelpOptions.class, "must have a \"help\" string"); |
| } |
| |
| /** Dummy comment (linter suppression) */ |
| public static class BadAbbrevOptions extends OptionsBase { |
| @Option( |
| name = "incompatible_bad", |
| category = "incompatible changes", |
| defaultValue = "false", |
| help = "nohelp", |
| abbrev = 'x' |
| ) |
| public boolean bad; |
| } |
| |
| @Test |
| public void badAbbrev() { |
| assertBadness(BadAbbrevOptions.class, "must not use the abbrev field"); |
| } |
| |
| /** Dummy comment (linter suppression) */ |
| public static class BadValueHelpOptions extends OptionsBase { |
| @Option( |
| name = "incompatible_bad", |
| category = "incompatible changes", |
| defaultValue = "false", |
| help = "nohelp", |
| valueHelp = "x" |
| ) |
| public boolean bad; |
| } |
| |
| @Test |
| public void badValueHelp() { |
| assertBadness(BadValueHelpOptions.class, "must not use the valueHelp field"); |
| } |
| |
| /** Dummy comment (linter suppression) */ |
| public static class BadConverterOptions extends OptionsBase { |
| @Option( |
| name = "incompatible_bad", |
| category = "incompatible changes", |
| defaultValue = "false", |
| help = "nohelp", |
| converter = Converters.BooleanConverter.class |
| ) |
| public boolean bad; |
| } |
| |
| @Test |
| public void badConverter() { |
| assertBadness(BadConverterOptions.class, "must not use the converter field"); |
| } |
| |
| /** Dummy comment (linter suppression) */ |
| public static class BadAllowMultipleOptions extends OptionsBase { |
| @Option( |
| name = "incompatible_bad", |
| category = "incompatible changes", |
| defaultValue = "null", |
| help = "nohelp", |
| allowMultiple = true |
| ) |
| public List<String> bad; |
| } |
| |
| @Test |
| public void badAllowMutliple() { |
| assertBadness(BadAllowMultipleOptions.class, "must not use the allowMultiple field"); |
| } |
| |
| /** Dummy comment (linter suppression) */ |
| public static class BadImplicitRequirementsOptions extends OptionsBase { |
| @Option( |
| name = "incompatible_bad", |
| category = "incompatible changes", |
| defaultValue = "false", |
| help = "nohelp", |
| implicitRequirements = "--x" |
| ) |
| public boolean bad; |
| } |
| |
| @Test |
| public void badImplicitRequirements() { |
| assertBadness( |
| BadImplicitRequirementsOptions.class, "must not use the implicitRequirements field"); |
| } |
| |
| /** Dummy comment (linter suppression) */ |
| public static class BadOldNameOptions extends OptionsBase { |
| @Option( |
| name = "incompatible_bad", |
| category = "incompatible changes", |
| defaultValue = "false", |
| help = "nohelp", |
| oldName = "x" |
| ) |
| public boolean bad; |
| } |
| |
| @Test |
| public void badOldName() { |
| assertBadness(BadOldNameOptions.class, "must not use the oldName field"); |
| } |
| |
| /** Dummy comment (linter suppression) */ |
| public static class BadWrapperOptionOptions extends OptionsBase { |
| @Option( |
| name = "incompatible_bad", |
| category = "incompatible changes", |
| defaultValue = "false", |
| help = "nohelp", |
| wrapperOption = true |
| ) |
| public boolean bad; |
| } |
| |
| @Test |
| public void badWrapperOption() { |
| assertBadness(BadWrapperOptionOptions.class, "must not use the wrapperOption field"); |
| } |
| } |