blob: fc44f83eadc99a8003f36bcd0ad3c67f85d3ca43 [file] [log] [blame]
// 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.processor;
import static com.google.common.truth.Truth.assertAbout;
import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
import com.google.common.io.Resources;
import com.google.testing.compile.JavaFileObjects;
import javax.tools.JavaFileObject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for the compile-time checks in {@link OptionProcessor}. */
@RunWith(JUnit4.class)
public class OptionProcessorTest {
private static JavaFileObject getFile(String pathToFile) {
return JavaFileObjects.forResource(
Resources.getResource(
"com/google/devtools/common/options/processor/optiontestsources/" + pathToFile));
}
@Test
public void optionsInNonOptionBasesAreRejected() {
assertAbout(javaSource())
.that(getFile("OptionInNonOptionBase.java"))
.processedWith(new OptionProcessor())
.failsToCompile()
.withErrorContaining(
"@Option annotated fields can only be in classes that inherit from OptionsBase.");
}
@Test
public void privatelyDeclaredOptionsAreRejected() {
assertAbout(javaSource())
.that(getFile("PrivateOptionField.java"))
.processedWith(new OptionProcessor())
.failsToCompile()
.withErrorContaining("@Option annotated fields should be public.");
}
@Test
public void protectedOptionsAreRejected() {
assertAbout(javaSource())
.that(getFile("ProtectedOptionField.java"))
.processedWith(new OptionProcessor())
.failsToCompile()
.withErrorContaining("@Option annotated fields should be public.");
}
@Test
public void staticOptionsAreRejected() {
assertAbout(javaSource())
.that(getFile("StaticOptionField.java"))
.processedWith(new OptionProcessor())
.failsToCompile()
.withErrorContaining("@Option annotated fields should not be static.");
}
@Test
public void finalOptionsAreRejected() {
assertAbout(javaSource())
.that(getFile("FinalOptionField.java"))
.processedWith(new OptionProcessor())
.failsToCompile()
.withErrorContaining("@Option annotated fields should not be final.");
}
@Test
public void namelessOptionsAreRejected() {
assertAbout(javaSource())
.that(getFile("NamelessOption.java"))
.processedWith(new OptionProcessor())
.failsToCompile()
.withErrorContaining("Option must have an actual name.");
}
@Test
public void badNamesAreRejected() {
assertAbout(javaSource())
.that(getFile("BadNameForDocumentedOption.java"))
.processedWith(new OptionProcessor())
.failsToCompile()
.withErrorContaining(
"Options that are used on the command line as flags must have names made from word "
+ "characters only.");
assertAbout(javaSource())
.that(getFile("BadNameWithEqualsSign.java"))
.processedWith(new OptionProcessor())
.failsToCompile()
.withErrorContaining(
"Options that are used on the command line as flags must have names made from word "
+ "characters only.");
}
@Test
public void badNamesForHiddenOptionsPass() {
assertAbout(javaSource())
.that(getFile("BadNameForInternalOption.java"))
.processedWith(new OptionProcessor())
.compilesWithoutError();
}
@Test
public void deprecatedCategorySaysUndocumented() {
assertAbout(javaSource())
.that(getFile("DeprecatedUndocumentedCategory.java"))
.processedWith(new OptionProcessor())
.failsToCompile()
.withErrorContaining(
"Documentation level is no longer read from the option category. Category "
+ "\"undocumented\" is disallowed, see OptionMetadataTags for the relevant tags.");
}
@Test
public void deprecatedCategorySaysHidden() {
assertAbout(javaSource())
.that(getFile("DeprecatedHiddenCategory.java"))
.processedWith(new OptionProcessor())
.failsToCompile()
.withErrorContaining(
"Documentation level is no longer read from the option category. Category "
+ "\"hidden\" is disallowed, see OptionMetadataTags for the relevant tags.");
}
@Test
public void deprecatedCategorySaysInternal() {
assertAbout(javaSource())
.that(getFile("DeprecatedInternalCategory.java"))
.processedWith(new OptionProcessor())
.failsToCompile()
.withErrorContaining(
"Documentation level is no longer read from the option category. Category "
+ "\"internal\" is disallowed, see OptionMetadataTags for the relevant tags.");
}
@Test
public void optionMustHaveEffectExplicitlyStated() {
assertAbout(javaSource())
.that(getFile("EffectlessOption.java"))
.processedWith(new OptionProcessor())
.failsToCompile()
.withErrorContaining(
"Option does not list at least one OptionEffectTag. "
+ "If the option has no effect, please be explicit and add NO_OP. "
+ "Otherwise, add a tag representing its effect.");
}
@Test
public void contradictingEffectTagsAreRejected() {
assertAbout(javaSource())
.that(getFile("OptionWithContradictingNoopEffects.java"))
.processedWith(new OptionProcessor())
.failsToCompile()
.withErrorContaining(
"Option includes NO_OP with other effects. This doesn't make much sense. "
+ "Please remove NO_OP or the actual effects from the list, whichever is correct.");
assertAbout(javaSource())
.that(getFile("OptionWithContradictingUnknownEffects.java"))
.processedWith(new OptionProcessor())
.failsToCompile()
.withErrorContaining(
"Option includes UNKNOWN with other, known, effects. "
+ "Please remove UNKNOWN from the list.");
}
@Test
public void contradictoryDocumentationCategoryIsRejected() {
assertAbout(javaSource())
.that(getFile("HiddenOptionWithCategory.java"))
.processedWith(new OptionProcessor())
.failsToCompile()
.withErrorContaining(
"Option has metadata tag HIDDEN but does not have category UNDOCUMENTED. "
+ "Please fix.");
assertAbout(javaSource())
.that(getFile("InternalOptionWithCategory.java"))
.processedWith(new OptionProcessor())
.failsToCompile()
.withErrorContaining(
"Option has metadata tag INTERNAL but does not have category UNDOCUMENTED. "
+ "Please fix.");
}
@Test
public void defaultConvertersAreFound() {
assertAbout(javaSource())
.that(getFile("AllDefaultConverters.java"))
.processedWith(new OptionProcessor())
.compilesWithoutError();
}
@Test
public void defaultConvertersForAllowMultipleOptionsAreFound() {
assertAbout(javaSource())
.that(getFile("AllDefaultConvertersWithAllowMultiple.java"))
.processedWith(new OptionProcessor())
.compilesWithoutError();
}
@Test
public void converterReturnsListForAllowMultipleIsAllowed() {
assertAbout(javaSource())
.that(getFile("MultipleOptionWithListTypeConverter.java"))
.processedWith(new OptionProcessor())
.compilesWithoutError();
}
@Test
public void correctCustomConverterForPrimitiveTypePasses() {
assertAbout(javaSource())
.that(getFile("CorrectCustomConverterForPrimitiveType.java"))
.processedWith(new OptionProcessor())
.compilesWithoutError();
}
@Test
public void converterlessOptionIsRejected() {
assertAbout(javaSource())
.that(getFile("ConverterlessOption.java"))
.processedWith(new OptionProcessor())
.failsToCompile()
.withErrorContaining(
"Cannot find valid converter for option of type "
+ "java.util.Map<java.lang.String,java.lang.String>");
}
@Test
public void allowMultipleOptionWithCollectionTypeIsRejected() {
assertAbout(javaSource())
.that(getFile("CollectionTypeForAllowMultipleOption.java"))
.processedWith(new OptionProcessor())
.failsToCompile()
.withErrorContaining(
"Option that allows multiple occurrences must be of type java.util.List<E>, "
+ "but is of type java.util.Collection<java.lang.String>");
}
@Test
public void allowMultipleOptionWithNonListTypeIsRejected() {
assertAbout(javaSource())
.that(getFile("NonListTypeForAllowMultipleOption.java"))
.processedWith(new OptionProcessor())
.failsToCompile()
.withErrorContaining(
"Option that allows multiple occurrences must be of type java.util.List<E>, "
+ "but is of type java.lang.String");
}
@Test
public void optionWithIncorrectConverterIsRejected() {
assertAbout(javaSource())
.that(getFile("IncorrectConverterType.java"))
.processedWith(new OptionProcessor())
.failsToCompile()
.withErrorContaining(
"Type of field (java.lang.String) must be assignable from the converter's return type "
+ "(java.lang.Integer)");
}
@Test
public void allowMultipleOptionWithIncorrectConverterIsRejected() {
assertAbout(javaSource())
.that(getFile("IncorrectConverterTypeForAllowMultipleOption.java"))
.processedWith(new OptionProcessor())
.failsToCompile()
.withErrorContaining(
"Type of field (java.lang.String) must be assignable from the converter's return type "
+ "(java.lang.Integer)");
}
@Test
public void expansionOptionThatAllowsMultipleIsRejected() {
assertAbout(javaSource())
.that(getFile("ExpansionOptionWithAllowMultiple.java"))
.processedWith(new OptionProcessor())
.failsToCompile()
.withErrorContaining(
"Can't set an option to accumulate multiple values and let it expand to other flags.");
}
@Test
public void functionalExpansionOptionThatAllowsMultipleIsRejected() {
assertAbout(javaSource())
.that(getFile("FunctionalExpansionOptionWithAllowMultiple.java"))
.processedWith(new OptionProcessor())
.failsToCompile()
.withErrorContaining(
"Can't set an option to accumulate multiple values and let it expand to other flags.");
}
@Test
public void expansionOptionWithImplicitRequirementIsRejected() {
assertAbout(javaSource())
.that(getFile("ExpansionOptionWithImplicitRequirement.java"))
.processedWith(new OptionProcessor())
.failsToCompile()
.withErrorContaining(
"Can't set an option to be both an expansion option and have implicit requirements.");
}
@Test
public void expansionOptionThatExpandsInTwoWaysIsRejected() {
assertAbout(javaSource())
.that(getFile("DoubleExpansionOption.java"))
.processedWith(new OptionProcessor())
.failsToCompile()
.withErrorContaining(
"Options cannot expand using both a static expansion list and an expansion function.");
}
}