blob: 2c3bea0d8b638b03e81be5d7d7751ab38e9ed447 [file] [log] [blame]
/*
* Copyright 2014 Google LLC
*
* 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.auto.value.processor;
import static com.google.common.truth.Truth.assertAbout;
import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.testing.compile.JavaFileObjects;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
import javax.tools.JavaFileObject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Tests to ensure annotations are kept on AutoValue generated classes
*
* @author jmcampanini
*/
@RunWith(JUnit4.class)
public class PropertyAnnotationsTest {
private static final String TEST_ANNOTATION =
"@PropertyAnnotationsTest.TestAnnotation";
private static final String TEST_ARRAY_ANNOTATION =
"@PropertyAnnotationsTest.TestArrayAnnotation";
public enum TestEnum {
A,
B;
@Override
public String toString() {
// used to prove that the method we determine the value does not use the `toString()` method
// of the enum
return "not the same value";
}
}
public @interface TestAnnotation {
byte testByte() default 1;
short testShort() default 2;
int testInt() default 3;
long testLong() default 4L;
float testFloat() default 5.6f;
double testDouble() default 7.8d;
char testChar() default 'a';
String testString() default "10";
boolean testBoolean() default false;
Class<?> testClass() default TestEnum.class;
TestEnum testEnum() default TestEnum.A;
OtherAnnotation testAnnotation() default @OtherAnnotation(foo = 23, bar = "baz");
}
public @interface OtherAnnotation {
int foo() default 123;
String bar() default "bar";
}
public @interface TestArrayAnnotation {
byte[] testBytes() default {1, 2};
short[] testShorts() default {3, 4};
int[] testInts() default {5, 6};
long[] testLongs() default {7L, 8L};
float[] testFloats() default {9.1f, 2.3f};
double[] testDoubles() default {4.5d, 6.7d};
char[] testChars() default {'a', 'b'};
String[] testStrings() default {"cde", "fgh"};
boolean[] testBooleans() default {true, false};
Class<?>[] testClasses() default {TestEnum.class, TestEnum.class};
TestEnum[] testEnums() default {TestEnum.A, TestEnum.B};
OtherAnnotation[] testAnnotations() default {
@OtherAnnotation(foo = 999), @OtherAnnotation(bar = "baz")
};
}
@Inherited
public @interface InheritedAnnotation {}
private static class InputFileBuilder {
Iterable<String> imports = ImmutableList.of();
List<String> annotations = new ArrayList<>();
List<String> innerTypes = new ArrayList<>();
InputFileBuilder setImports(Iterable<String> imports) {
this.imports = imports;
return this;
}
InputFileBuilder addAnnotations(String... annotations) {
this.annotations.addAll(ImmutableList.copyOf(annotations));
return this;
}
InputFileBuilder addAnnotations(Iterable<String> annotations) {
this.annotations.addAll(ImmutableList.copyOf(annotations));
return this;
}
InputFileBuilder addInnerTypes(String... innerTypes) {
this.innerTypes.addAll(ImmutableList.copyOf(innerTypes));
return this;
}
JavaFileObject build() {
ImmutableList<String> list =
ImmutableList.<String>builder()
.add("package foo.bar;", "", "import com.google.auto.value.AutoValue;")
.addAll(imports)
.add("", "@AutoValue", "public abstract class Baz {")
.addAll(annotations)
.add(
" public abstract int buh();",
"",
" public static Baz create(int buh) {",
" return new AutoValue_Baz(buh);",
" }")
.addAll(innerTypes)
.add("}")
.build();
String[] lines = list.toArray(new String[list.size()]);
return JavaFileObjects.forSourceLines("foo.bar.Baz", lines);
}
}
private static class OutputFileBuilder {
Iterable<String> imports = ImmutableList.of();
List<String> fieldAnnotations = new ArrayList<>();
List<String> methodAnnotations = new ArrayList<>();
OutputFileBuilder setImports(Iterable<String> imports) {
this.imports = imports;
return this;
}
OutputFileBuilder addFieldAnnotations(String... annotations) {
this.fieldAnnotations.addAll(ImmutableList.copyOf(annotations));
return this;
}
OutputFileBuilder addMethodAnnotations(String... innerTypes) {
this.methodAnnotations.addAll(ImmutableList.copyOf(innerTypes));
return this;
}
OutputFileBuilder addMethodAnnotations(Iterable<String> innerTypes) {
this.methodAnnotations.addAll(ImmutableList.copyOf(innerTypes));
return this;
}
JavaFileObject build() {
String nullable = methodAnnotations.contains("@Nullable") ? "@Nullable " : "";
ImmutableSortedSet<String> allImports =
ImmutableSortedSet.<String>naturalOrder()
.add(GeneratedImport.importGeneratedAnnotationType())
.addAll(imports)
.build();
ImmutableList<String> list =
ImmutableList.<String>builder()
.add("package foo.bar;", "")
.addAll(allImports)
.add(
"",
"@Generated(\"" + AutoValueProcessor.class.getName() + "\")",
"final class AutoValue_Baz extends Baz {")
.addAll(fieldAnnotations)
.add(
" private final int buh;",
" AutoValue_Baz(" + nullable + "int buh) {",
" this.buh = buh;",
" }",
"")
.addAll(methodAnnotations)
.add(
" @Override public int buh() {",
" return buh;",
" }",
"",
" @Override public String toString() {",
" return \"Baz{\"",
" + \"buh=\" + buh",
" + \"}\";",
" }",
"",
" @Override public boolean equals(Object o) {",
" if (o == this) {",
" return true;",
" }",
" if (o instanceof Baz) {",
" Baz that = (Baz) o;",
" return this.buh == that.buh();",
" }",
" return false;",
" }",
"",
" @Override public int hashCode() {",
" int h$ = 1;",
" h$ *= 1000003;",
" h$ ^= buh;",
" return h$;",
" }",
"}")
.build();
String[] lines = list.toArray(new String[list.size()]);
return JavaFileObjects.forSourceLines("foo.bar.AutoValue_Baz", lines);
}
}
private ImmutableSet<String> getImports(Class<?>... classes) {
ImmutableSet.Builder<String> stringBuilder = ImmutableSortedSet.naturalOrder();
for (Class<?> clazz : classes) {
stringBuilder.add("import " + clazz.getName() + ";");
}
return stringBuilder.build();
}
private void assertGeneratedMatches(
Iterable<String> imports,
Iterable<String> annotations,
Iterable<String> expectedMethodAnnotations) {
JavaFileObject javaFileObject =
new InputFileBuilder().setImports(imports).addAnnotations(annotations).build();
JavaFileObject expectedOutput =
new OutputFileBuilder()
.setImports(imports)
.addMethodAnnotations(expectedMethodAnnotations)
.build();
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor())
.compilesWithoutError()
.and()
.generatesSources(expectedOutput);
}
@Test
public void testSimpleAnnotation() {
assertGeneratedMatches(
ImmutableList.of(), ImmutableList.of("@Deprecated"), ImmutableList.of("@Deprecated"));
}
@Test
public void testSingleStringValueAnnotation() {
assertGeneratedMatches(
ImmutableList.of(),
ImmutableList.of("@SuppressWarnings(\"a\")"),
ImmutableList.of("@SuppressWarnings(\"a\")"));
}
@Test
public void testMultiStringValueAnnotation() {
assertGeneratedMatches(
ImmutableList.of(),
ImmutableList.of("@SuppressWarnings({\"a\", \"b\"})"),
ImmutableList.of("@SuppressWarnings({\"a\", \"b\"})"));
}
@Test
public void testNumberValueAnnotation() {
assertGeneratedMatches(
getImports(PropertyAnnotationsTest.class),
ImmutableList.of(TEST_ANNOTATION + "(testShort = 1, testInt = 2, testLong = 3L)"),
ImmutableList.of(TEST_ANNOTATION + "(testShort = 1, testInt = 2, testLong = 3L)"));
}
@Test
public void testByteValueAnnotation() {
assertGeneratedMatches(
getImports(PropertyAnnotationsTest.class),
ImmutableList.of(TEST_ANNOTATION + "(testByte = 0)"),
ImmutableList.of(TEST_ANNOTATION + "(testByte = 0)"));
}
@Test
public void testDecimalValueAnnotation() {
assertGeneratedMatches(
getImports(PropertyAnnotationsTest.class),
ImmutableList.of(TEST_ANNOTATION + "(testDouble = 1.2d, testFloat = 3.4f)"),
ImmutableList.of(TEST_ANNOTATION + "(testDouble = 1.2d, testFloat = 3.4f)"));
}
@Test
public void testOtherValuesAnnotation() {
assertGeneratedMatches(
getImports(PropertyAnnotationsTest.class),
ImmutableList.of(
TEST_ANNOTATION + "(testBoolean = true, testString = \"hallo\", testChar = 'a')"),
ImmutableList.of(
TEST_ANNOTATION + "(testBoolean = true, testString = \"hallo\", testChar = 'a')"));
}
@Test
public void testClassAnnotation() {
assertGeneratedMatches(
getImports(PropertyAnnotationsTest.class),
ImmutableList.of(TEST_ANNOTATION + "(testClass = String.class)"),
ImmutableList.of(TEST_ANNOTATION + "(testClass = String.class)"));
}
@Test
public void testEnumAnnotation() {
assertGeneratedMatches(
getImports(PropertyAnnotationsTest.class),
ImmutableList.of(
TEST_ANNOTATION
+ "(testEnum = "
+ PropertyAnnotationsTest.class.getName()
+ ".TestEnum.A)"),
ImmutableList.of(TEST_ANNOTATION + "(testEnum = PropertyAnnotationsTest.TestEnum.A)"));
}
@Test
public void testEmptyAnnotationAnnotation() {
assertGeneratedMatches(
getImports(PropertyAnnotationsTest.class),
ImmutableList.of(
TEST_ANNOTATION + "(testAnnotation = @PropertyAnnotationsTest.OtherAnnotation)"),
ImmutableList.of(
TEST_ANNOTATION + "(testAnnotation = @PropertyAnnotationsTest.OtherAnnotation)"));
}
@Test
public void testValuedAnnotationAnnotation() {
assertGeneratedMatches(
getImports(PropertyAnnotationsTest.class),
ImmutableList.of(
TEST_ANNOTATION
+ "(testAnnotation = @PropertyAnnotationsTest.OtherAnnotation(foo=999))"),
ImmutableList.of(
TEST_ANNOTATION
+ "(testAnnotation = @PropertyAnnotationsTest.OtherAnnotation(foo=999))"));
}
@Test
public void testNumberArrayAnnotation() {
assertGeneratedMatches(
getImports(PropertyAnnotationsTest.class),
ImmutableList.of(
TEST_ARRAY_ANNOTATION
+ "(testShorts = {2, 3}, testInts = {4, 5}, testLongs = {6L, 7L})"),
ImmutableList.of(
TEST_ARRAY_ANNOTATION
+ "(testShorts = {2, 3}, testInts = {4, 5}, testLongs = {6L, 7L})"));
}
@Test
public void testByteArrayAnnotation() {
assertGeneratedMatches(
getImports(PropertyAnnotationsTest.class),
ImmutableList.of(TEST_ARRAY_ANNOTATION + "(testBytes = {0, 1})"),
ImmutableList.of(TEST_ARRAY_ANNOTATION + "(testBytes = {0, 1})"));
}
@Test
public void testDecimalArrayAnnotation() {
assertGeneratedMatches(
getImports(PropertyAnnotationsTest.class),
ImmutableList.of(
TEST_ARRAY_ANNOTATION + "(testDoubles = {1.2d, 3.4d}, testFloats = {5.6f, 7.8f})"),
ImmutableList.of(
TEST_ARRAY_ANNOTATION + "(testDoubles = {1.2d, 3.4d}, testFloats = {5.6f, 7.8f})"));
}
@Test
public void testOtherArrayAnnotation() {
assertGeneratedMatches(
getImports(PropertyAnnotationsTest.class),
ImmutableList.of(
TEST_ARRAY_ANNOTATION
+ "(testBooleans = {false, false},"
+ " testStrings = {\"aaa\", \"bbb\"}, testChars={'x', 'y'})"),
ImmutableList.of(
TEST_ARRAY_ANNOTATION
+ "(testBooleans = {false, false},"
+ " testStrings = {\"aaa\", \"bbb\"}, testChars={'x', 'y'})"));
}
@Test
public void testClassArrayAnnotation() {
assertGeneratedMatches(
getImports(PropertyAnnotationsTest.class),
ImmutableList.of(TEST_ARRAY_ANNOTATION + "(testClasses = {String.class, Long.class})"),
ImmutableList.of(TEST_ARRAY_ANNOTATION + "(testClasses = {String.class, Long.class})"));
}
@Test
public void testImportedClassArrayAnnotation() {
assertGeneratedMatches(
getImports(PropertyAnnotationsTest.class, Nullable.class),
ImmutableList.of(
TEST_ARRAY_ANNOTATION
+ "(testClasses = {javax.annotation.Nullable.class, Long.class})"),
ImmutableList.of(TEST_ARRAY_ANNOTATION + "(testClasses = {Nullable.class, Long.class})"));
}
@Test
public void testEnumArrayAnnotation() {
assertGeneratedMatches(
getImports(PropertyAnnotationsTest.class),
ImmutableList.of(
TEST_ARRAY_ANNOTATION + "(testEnums = {PropertyAnnotationsTest.TestEnum.A,"
+ " PropertyAnnotationsTest.TestEnum.B})"),
ImmutableList.of(
TEST_ARRAY_ANNOTATION + "(testEnums = {PropertyAnnotationsTest.TestEnum.A,"
+ " PropertyAnnotationsTest.TestEnum.B})"));
}
@Test
public void testArrayOfEmptyAnnotationAnnotation() {
assertGeneratedMatches(
getImports(PropertyAnnotationsTest.class),
ImmutableList.of(
TEST_ARRAY_ANNOTATION
+ "(testAnnotations = {@PropertyAnnotationsTest.OtherAnnotation})"),
ImmutableList.of(
TEST_ARRAY_ANNOTATION
+ "(testAnnotations = @PropertyAnnotationsTest.OtherAnnotation)"));
assertGeneratedMatches(
getImports(PropertyAnnotationsTest.class),
ImmutableList.of(
TEST_ARRAY_ANNOTATION
+ "(testAnnotations = {@PropertyAnnotationsTest.OtherAnnotation,"
+ " @PropertyAnnotationsTest.OtherAnnotation})"),
ImmutableList.of(
TEST_ARRAY_ANNOTATION
+ "(testAnnotations = {@PropertyAnnotationsTest.OtherAnnotation,"
+ " @PropertyAnnotationsTest.OtherAnnotation})"));
}
@Test
public void testArrayOfValuedAnnotationAnnotation() {
assertGeneratedMatches(
getImports(PropertyAnnotationsTest.class),
ImmutableList.of(
TEST_ARRAY_ANNOTATION
+ "(testAnnotations = {@PropertyAnnotationsTest.OtherAnnotation(foo = 999)})"),
ImmutableList.of(
TEST_ARRAY_ANNOTATION
+ "(testAnnotations = @PropertyAnnotationsTest.OtherAnnotation(foo = 999))"));
}
/**
* Tests that when CopyAnnotations is present on a method, all non-inherited annotations (except
* those appearing in CopyAnnotations.exclude) are copied to the method implementation in the
* generated class.
*/
@Test
public void testCopyingMethodAnnotations() {
ImmutableSet<String> imports = getImports(PropertyAnnotationsTest.class);
JavaFileObject inputFile =
new InputFileBuilder()
.setImports(imports)
.addAnnotations(
"@AutoValue.CopyAnnotations(exclude="
+ "{PropertyAnnotationsTest.TestAnnotation.class})",
"@Deprecated",
"@PropertyAnnotationsTest.TestAnnotation",
"@PropertyAnnotationsTest.InheritedAnnotation")
.build();
JavaFileObject outputFile =
new OutputFileBuilder()
.setImports(imports)
.addMethodAnnotations("@Deprecated", "@PropertyAnnotationsTest.InheritedAnnotation")
.addFieldAnnotations("@Deprecated", "@PropertyAnnotationsTest.InheritedAnnotation")
.build();
assertAbout(javaSource())
.that(inputFile)
.processedWith(new AutoValueProcessor())
.compilesWithoutError()
.and()
.generatesSources(outputFile);
}
/**
* Tests that when CopyAnnotationsToGeneratedField is present on a method, all non-inherited
* annotations (except those appearing in CopyAnnotationsToGeneratedField.exclude) are copied to
* the method implementation in the generated class.
*/
@Test
public void testCopyingMethodAnnotationsToGeneratedFields() {
JavaFileObject inputFile =
new InputFileBuilder()
.setImports(getImports(PropertyAnnotationsTest.class, Target.class, ElementType.class))
.addAnnotations(
"@AutoValue.CopyAnnotations(exclude={PropertyAnnotationsTest."
+ "TestAnnotation.class})",
"@Deprecated",
"@PropertyAnnotationsTest.TestAnnotation",
"@PropertyAnnotationsTest.InheritedAnnotation",
"@MethodsOnly")
.addInnerTypes("@Target(ElementType.METHOD) @interface MethodsOnly {}")
.build();
JavaFileObject outputFile =
new OutputFileBuilder()
.setImports(getImports(PropertyAnnotationsTest.class))
.addFieldAnnotations("@Deprecated", "@PropertyAnnotationsTest.InheritedAnnotation")
.addMethodAnnotations(
"@Deprecated",
"@PropertyAnnotationsTest.InheritedAnnotation",
"@Baz.MethodsOnly")
.build();
assertAbout(javaSource())
.that(inputFile)
.processedWith(new AutoValueProcessor())
.compilesWithoutError()
.and()
.generatesSources(outputFile);
}
}