blob: 1de82b72ad70c95747ef76421b0a7329bd837d6c [file] [log] [blame]
/*
* Copyright (C) 2014 The Guava Authors
*
* 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 static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.testing.compile.JavaFileObjects;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.ElementFilter;
import javax.tools.JavaFileObject;
/**
* @author emcmanus@google.com (Éamonn McManus)
*/
@RunWith(JUnit4.class)
public class CompilationTest {
@Test
public void simpleSuccess() {
// Positive test case that ensures we generate the expected code for at least one case.
// Most AutoValue code-generation tests are functional, meaning that we check that the generated
// code does the right thing rather than checking what it looks like, but this test is a sanity
// check that we are not generating correct but weird code.
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Baz {",
" public abstract int buh();",
"",
" public static Baz create(int buh) {",
" return new AutoValue_Baz(buh);",
" }",
"}");
JavaFileObject expectedOutput = JavaFileObjects.forSourceLines(
"foo.bar.AutoValue_Baz",
"package foo.bar;",
"",
"import javax.annotation.Generated;",
"",
"@Generated(\"" + AutoValueProcessor.class.getName() + "\")",
"final class AutoValue_Baz extends Baz {",
" private final int buh;",
"",
" AutoValue_Baz(int buh) {",
" this.buh = buh;",
" }",
"",
" @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 ^= this.buh;",
" return h;",
" }",
"}"
);
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor())
.compilesWithoutError()
.and().generatesSources(expectedOutput);
}
@Test
public void importTwoWays() {
// Test that referring to the same class in two different ways does not confuse the import logic
// into thinking it is two different classes and that therefore it can't import. The code here
// is nonsensical but successfully reproduces a real problem, which is that a TypeMirror that is
// extracted using Elements.getTypeElement(name).asType() does not compare equal to one that is
// extracted from ExecutableElement.getReturnType(), even though Types.isSameType considers them
// equal. So unless we are careful, the java.util.Arrays that we import explicitly to use its
// methods will appear different from the java.util.Arrays that is the return type of the
// arrays() method here.
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"import java.util.Arrays;",
"",
"@AutoValue",
"public abstract class Baz {",
" @SuppressWarnings(\"mutable\")",
" public abstract int[] ints();",
" public abstract Arrays arrays();",
"",
" public static Baz create(int[] ints, Arrays arrays) {",
" return new AutoValue_Baz(ints, arrays);",
" }",
"}");
JavaFileObject expectedOutput = JavaFileObjects.forSourceLines(
"foo.bar.AutoValue_Baz",
"package foo.bar;",
"",
"import java.util.Arrays;",
"import javax.annotation.Generated;",
"",
"@Generated(\"" + AutoValueProcessor.class.getName() + "\")",
"final class AutoValue_Baz extends Baz {",
" private final int[] ints;",
" private final Arrays arrays;",
"",
" AutoValue_Baz(int[] ints, Arrays arrays) {",
" if (ints == null) {",
" throw new NullPointerException(\"Null ints\");",
" }",
" this.ints = ints;",
" if (arrays == null) {",
" throw new NullPointerException(\"Null arrays\");",
" }",
" this.arrays = arrays;",
" }",
"",
" @SuppressWarnings(value = {\"mutable\"})",
" @Override public int[] ints() {",
" return ints;",
" }",
"",
" @Override public Arrays arrays() {",
" return arrays;",
" }",
"",
" @Override public String toString() {",
" return \"Baz{\"",
" + \"ints=\" + Arrays.toString(ints) + \", \"",
" + \"arrays=\" + arrays",
" + \"}\";",
" }",
"",
" @Override public boolean equals(Object o) {",
" if (o == this) {",
" return true;",
" }",
" if (o instanceof Baz) {",
" Baz that = (Baz) o;",
" return (Arrays.equals(this.ints, (that instanceof AutoValue_Baz) "
+ "? ((AutoValue_Baz) that).ints : that.ints()))",
" && (this.arrays.equals(that.arrays()));",
" }",
" return false;",
" }",
"",
" @Override public int hashCode() {",
" int h = 1;",
" h *= 1000003;",
" h ^= Arrays.hashCode(this.ints);",
" h *= 1000003;",
" h ^= this.arrays.hashCode();",
" return h;",
" }",
"}"
);
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor())
.compilesWithoutError()
.and().generatesSources(expectedOutput);
}
@Test
public void noMultidimensionalPrimitiveArrays() throws Exception {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Baz {",
" public abstract int[][] ints();",
"",
" public static Baz create(int[][] ints) {",
" return new AutoValue_Baz(ints);",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor())
.failsToCompile()
.withErrorContaining("AutoValue class cannot define an array-valued property "
+ "unless it is a primitive array")
.in(javaFileObject).onLine(7);
}
@Test
public void noObjectArrays() throws Exception {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Baz {",
" public abstract String[] strings();",
"",
" public static Baz create(String[] strings) {",
" return new AutoValue_Baz(strings);",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor())
.failsToCompile()
.withErrorContaining("AutoValue class cannot define an array-valued property "
+ "unless it is a primitive array")
.in(javaFileObject).onLine(7);
}
@Test
public void annotationOnInterface() throws Exception {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public interface Baz {}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor())
.failsToCompile()
.withErrorContaining("AutoValue only applies to classes")
.in(javaFileObject).onLine(6);
}
@Test
public void annotationOnEnum() throws Exception {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public enum Baz {}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor())
.failsToCompile()
.withErrorContaining("AutoValue only applies to classes")
.in(javaFileObject).onLine(6);
}
@Test
public void extendAutoValue() throws Exception {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Outer",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"public class Outer {",
" @AutoValue",
" static abstract class Parent {",
" static Parent create(int randomProperty) {",
" return new AutoValue_Outer_Parent(randomProperty);",
" }",
"",
" abstract int randomProperty();",
" }",
"",
" @AutoValue",
" static abstract class Child extends Parent {",
" static Child create(int randomProperty) {",
" return new AutoValue_Outer_Child(randomProperty);",
" }",
"",
" abstract int randomProperty();",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor())
.failsToCompile()
.withErrorContaining("may not extend")
.in(javaFileObject).onLine(16);
}
@Test
public void bogusSerialVersionUID() throws Exception {
String[] mistakes = {
"final long serialVersionUID = 1234L", // not static
"static long serialVersionUID = 1234L", // not final
"static final Long serialVersionUID = 1234L", // not long
"static final long serialVersionUID = (Long) 1234L", // not a compile-time constant
};
for (String mistake : mistakes) {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Baz implements java.io.Serializable {",
" " + mistake + ";",
"",
" public abstract int foo();",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor())
.failsToCompile()
.withErrorContaining(
"serialVersionUID must be a static final long compile-time constant")
.in(javaFileObject).onLine(7);
}
}
@Test
public void nonExistentSuperclass() throws Exception {
// The main purpose of this test is to check that AutoValueProcessor doesn't crash the
// compiler in this case.
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Existent extends NonExistent {",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor())
.failsToCompile()
.withErrorContaining("NonExistent")
.in(javaFileObject).onLine(6);
}
@Test
public void cannotImplementAnnotation() throws Exception {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.RetentionImpl",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"import java.lang.annotation.Retention;",
"import java.lang.annotation.RetentionPolicy;",
"",
"@AutoValue",
"public abstract class RetentionImpl implements Retention {",
" public static Retention create(RetentionPolicy policy) {",
" return new AutoValue_RetentionImpl(policy);",
" }",
"",
" @Override public Class<? extends Retention> annotationType() {",
" return Retention.class;",
" }",
"",
" @Override public boolean equals(Object o) {",
" return (o instanceof Retention && value().equals((Retention) o).value());",
" }",
"",
" @Override public int hashCode() {",
" return (\"value\".hashCode() * 127) ^ value().hashCode();",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor())
.failsToCompile()
.withErrorContaining("may not be used to implement an annotation interface")
.in(javaFileObject).onLine(8);
}
@Test
public void missingPropertyType() throws Exception {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Baz {",
" public abstract MissingType missingType();",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor())
.failsToCompile()
.withErrorContaining("MissingType")
.in(javaFileObject).onLine(7);
}
@Test
public void missingGenericPropertyType() throws Exception {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Baz {",
" public abstract MissingType<?> missingType();",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor())
.failsToCompile()
.withErrorContaining("MissingType")
.in(javaFileObject).onLine(7);
}
@Test
public void missingComplexGenericPropertyType() throws Exception {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"import java.util.Map;",
"import java.util.Set;",
"",
"@AutoValue",
"public abstract class Baz {",
" public abstract Map<Set<?>, MissingType<?>> missingType();",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor())
.failsToCompile()
.withErrorContaining("MissingType")
.in(javaFileObject).onLine(10);
}
@Test
public void missingSuperclassGenericParameter() throws Exception {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Baz<T extends MissingType<?>> {",
" public abstract int foo();",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor())
.failsToCompile()
.withErrorContaining("MissingType")
.in(javaFileObject).onLine(6);
}
@Test
public void correctBuilder() throws Exception {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"import com.google.common.collect.ImmutableList;",
"",
"import java.util.List;",
"import javax.annotation.Nullable;",
"",
"@AutoValue",
"public abstract class Baz<T extends Number> {",
" public abstract int anInt();",
" @SuppressWarnings(\"mutable\")",
" public abstract byte[] aByteArray();",
" @SuppressWarnings(\"mutable\")",
" @Nullable public abstract int[] aNullableIntArray();",
" public abstract List<T> aList();",
" public abstract ImmutableList<T> anImmutableList();",
"",
" public abstract Builder<T> toBuilder();",
"",
" @AutoValue.Builder",
" public interface Builder<T extends Number> {",
" Builder<T> anInt(int x);",
" Builder<T> aByteArray(byte[] x);",
" Builder<T> aNullableIntArray(@Nullable int[] x);",
" Builder<T> aList(List<T> x);",
" ImmutableList.Builder<T> anImmutableListBuilder();",
"",
" List<T> aList();",
" ImmutableList<T> anImmutableList();",
"",
" Baz<T> build();",
" }",
"",
" public static <T extends Number> Builder<T> builder() {",
" return AutoValue_Baz.builder();",
" }",
"}");
JavaFileObject expectedOutput = JavaFileObjects.forSourceLines(
"foo.bar.AutoValue_Baz",
"package foo.bar;",
"",
"import com.google.common.collect.ImmutableList",
"import java.util.Arrays;",
"import java.util.List;",
"import javax.annotation.Generated;",
"import javax.annotation.Nullable;",
"",
"@Generated(\"" + AutoValueProcessor.class.getName() + "\")",
"final class AutoValue_Baz<T extends Number> extends Baz<T> {",
" private final int anInt;",
" private final byte[] aByteArray;",
" private final int[] aNullableIntArray;",
" private final List<T> aList;",
" private final ImmutableList<T> anImmutableList;",
"",
" private AutoValue_Baz(",
" int anInt,",
" byte[] aByteArray,",
" @Nullable int[] aNullableIntArray,",
" List<T> aList,",
" ImmutableList<T> anImmutableList) {",
" this.anInt = anInt;",
" if (aByteArray == null) {",
" throw new NullPointerException(\"Null aByteArray\");",
" }",
" this.aByteArray = aByteArray;",
" this.aNullableIntArray = aNullableIntArray;",
" if (aList == null) {",
" throw new NullPointerException(\"Null aList\");",
" }",
" this.aList = aList;",
" this.anImmutableList = anImmutableList;",
" }",
"",
" @Override public int anInt() {",
" return anInt;",
" }",
"",
" @SuppressWarnings(value = {\"mutable\")",
" @Override public byte[] aByteArray() {",
" return aByteArray;",
" }",
"",
" @SuppressWarnings(value = {\"mutable\")",
" @Nullable",
" @Override public int[] aNullableIntArray() {",
" return aNullableIntArray;",
" }",
"",
" @Override public List<T> aList() {",
" return aList;",
" }",
"",
" @Override public ImmutableList<T> anImmutableList() {",
" return anImmutableList;",
" }",
"",
" @Override public String toString() {",
" return \"Baz{\"",
" + \"anInt=\" + anInt + \", \"",
" + \"aByteArray=\" + Arrays.toString(aByteArray) + \", \"",
" + \"aNullableIntArray=\" + Arrays.toString(aNullableIntArray) + \", \"",
" + \"aList=\" + aList + \", \"",
" + \"anImmutableList=\" + anImmutableList",
" + \"}\";",
" }",
"",
" @Override public boolean equals(Object o) {",
" if (o == this) {",
" return true;",
" }",
" if (o instanceof Baz) {",
" Baz<?> that = (Baz<?>) o;",
" return (this.anInt == that.anInt())",
" && (Arrays.equals(this.aByteArray, "
+ "(that instanceof AutoValue_Baz) "
+ "? ((AutoValue_Baz) that).aByteArray : that.aByteArray()))",
" && (Arrays.equals(this.aNullableIntArray, "
+ "(that instanceof AutoValue_Baz) "
+ "? ((AutoValue_Baz) that).aNullableIntArray : that.aNullableIntArray()))",
" && (this.aList.equals(that.aList()))",
" && (this.anImmutableList.equals(that.anImmutableList()));",
" }",
" return false;",
" }",
"",
" @Override public int hashCode() {",
" int h = 1;",
" h *= 1000003;",
" h ^= this.anInt;",
" h *= 1000003;",
" h ^= Arrays.hashCode(this.aByteArray);",
" h *= 1000003;",
" h ^= Arrays.hashCode(this.aNullableIntArray);",
" h *= 1000003;",
" h ^= this.aList.hashCode();",
" h *= 1000003;",
" h ^= this.anImmutableList.hashCode();",
" return h;",
" }",
"",
" @Override public Baz.Builder<T> toBuilder() {",
" return new Builder<T>(this);",
" }",
"",
" static final class Builder<T extends Number> implements Baz.Builder<T> {",
" private Integer anInt;",
" private byte[] aByteArray;",
" private int[] aNullableIntArray;",
" private List<T> aList;",
" private ImmutableList.Builder<T> anImmutableList = ImmutableList.builder();",
"",
" Builder() {",
" }",
"",
" Builder(Baz<T> source) {",
" this.anInt = source.anInt();",
" this.aByteArray = source.aByteArray();",
" this.aNullableIntArray = source.aNullableIntArray();",
" this.aList = source.aList();",
" this.anImmutableList.addAll(source.anImmutableList());",
" }",
"",
" @Override",
" public Baz.Builder<T> anInt(int anInt) {",
" this.anInt = anInt;",
" return this;",
" }",
"",
" @Override",
" public Baz.Builder<T> aByteArray(byte[] aByteArray) {",
" this.aByteArray = aByteArray;",
" return this;",
" }",
"",
" @Override",
" public Baz.Builder<T> aNullableIntArray(@Nullable int[] aNullableIntArray) {",
" this.aNullableIntArray = aNullableIntArray;",
" return this;",
" }",
"",
" @Override",
" public Baz.Builder<T> aList(List<T> aList) {",
" this.aList = aList;",
" return this;",
" }",
"",
" @Override",
" public List<T> aList() {",
" if (aList == null) {",
" throw new IllegalStateException(\"Property \\\"aList\\\" has not been set\");",
" }",
" return aList;",
" }",
"",
" @Override",
" public ImmutableList.Builder<T> anImmutableListBuilder() {",
" return anImmutableList;",
" }",
"",
" @Override",
" public ImmutableList<T> anImmutableList() {",
" return anImmutableList.build();",
" }",
"",
" @Override",
" public Baz<T> build() {",
" String missing = \"\";",
" if (anInt == null) {",
" missing += \" anInt\";",
" }",
" if (aByteArray == null) {",
" missing += \" aByteArray\";",
" }",
" if (aList == null) {",
" missing += \" aList\";",
" }",
" if (!missing.isEmpty()) {",
" throw new IllegalStateException(\"Missing required properties:\" + missing);",
" }",
" return new AutoValue_Baz<T>(",
" this.anInt,",
" this.aByteArray,",
" this.aNullableIntArray,",
" this.aList,",
" this.anImmutableList.build());",
" }",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor())
.compilesWithoutError()
.and()
.generatesSources(expectedOutput);
}
@Test
public void autoValueBuilderOnTopLevelClass() throws Exception {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Builder",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue.Builder",
"public interface Builder {",
" Builder foo(int x);",
" Object build();",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.failsToCompile()
.withErrorContaining("can only be applied to a class or interface inside")
.in(javaFileObject).onLine(6);
}
@Test
public void autoValueBuilderNotInsideAutoValue() throws Exception {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"public abstract class Baz {",
" abstract int foo();",
"",
" static Builder builder() {",
" return new AutoValue_Baz.Builder();",
" }",
"",
" @AutoValue.Builder",
" public interface Builder {",
" Builder foo(int x);",
" Baz build();",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.failsToCompile()
.withErrorContaining("can only be applied to a class or interface inside")
.in(javaFileObject).onLine(13);
}
@Test
public void autoValueBuilderOnEnum() throws Exception {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Baz {",
" abstract int foo();",
"",
" static Builder builder() {",
" return null;",
" }",
"",
" @AutoValue.Builder",
" public enum Builder {}",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.failsToCompile()
.withErrorContaining("can only apply to a class or an interface")
.in(javaFileObject).onLine(14);
}
@Test
public void autoValueBuilderDuplicate() {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Baz {",
" @AutoValue.Builder",
" public interface Builder1 {",
" Baz build();",
" }",
"",
" @AutoValue.Builder",
" public interface Builder2 {",
" Baz build();",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.failsToCompile()
.withErrorContaining("already has a Builder: foo.bar.Baz.Builder1")
.in(javaFileObject).onLine(13);
}
@Test
public void autoValueBuilderMissingSetter() {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Baz {",
" abstract int blim();",
" abstract String blam();",
"",
" @AutoValue.Builder",
" public interface Builder {",
" Builder blam(String x);",
" Baz build();",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.failsToCompile()
.withErrorContaining("with this signature: foo.bar.Baz.Builder blim(int)")
.in(javaFileObject).onLine(11);
}
@Test
public void autoValueBuilderMissingSetterUsingSetPrefix() {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Baz {",
" abstract int blim();",
" abstract String blam();",
"",
" @AutoValue.Builder",
" public interface Builder {",
" Builder setBlam(String x);",
" Baz build();",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.failsToCompile()
.withErrorContaining("with this signature: foo.bar.Baz.Builder setBlim(int)")
.in(javaFileObject).onLine(11);
}
@Test
public void autoValueBuilderWrongTypeSetter() {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Baz {",
" abstract int blim();",
" abstract String blam();",
"",
" @AutoValue.Builder",
" public interface Builder {",
" Builder blim(String x);",
" Builder blam(String x);",
" Baz build();",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.failsToCompile()
.withErrorContaining(
"Parameter type of setter method should be int to match getter foo.bar.Baz.blim")
.in(javaFileObject).onLine(12);
}
@Test
public void autoValueBuilderWrongTypeSetterWithCopyOf() {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"import com.google.common.collect.ImmutableList;",
"",
"@AutoValue",
"public abstract class Baz {",
" abstract String blim();",
" abstract ImmutableList<String> blam();",
"",
" @AutoValue.Builder",
" public interface Builder {",
" Builder blim(String x);",
" Builder blam(String x);",
" Baz build();",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.failsToCompile()
.withErrorContaining(
"Parameter type of setter method should be "
+ "com.google.common.collect.ImmutableList<java.lang.String> to match getter "
+ "foo.bar.Baz.blam, or it should be a type that can be passed to "
+ "ImmutableList.copyOf")
.in(javaFileObject).onLine(14);
}
@Test
public void autoValueBuilderWrongTypeSetterWithCopyOfGenericallyWrong() {
// This puts the finger on our insufficient error-detection logic for the case where the
// parameter would be compatible with copyOf were it not for generics. Currently, this leads to
// a compile error in the generated code. We don't want to suppose anything about the error
// message the compiler might come up with. It might be something like this for example:
// incompatible types: inference variable E has incompatible bounds
// equality constraints: java.lang.String
// lower bounds: java.lang.Integer
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"import com.google.common.collect.ImmutableList;",
"import java.util.Collection;",
"",
"@AutoValue",
"public abstract class Baz {",
" abstract String blim();",
" abstract ImmutableList<String> blam();",
"",
" @AutoValue.Builder",
" public interface Builder {",
" Builder blim(String x);",
" Builder blam(Collection<Integer> x);",
" Baz build();",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.failsToCompile();
}
@Test
public void autoValueBuilderWrongTypeSetterWithGetPrefix() {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Baz {",
" abstract int getBlim();",
" abstract String getBlam();",
"",
" @AutoValue.Builder",
" public interface Builder {",
" Builder blim(String x);",
" Builder blam(String x);",
" Baz build();",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.failsToCompile()
.withErrorContaining(
"Parameter type of setter method should be int to match getter foo.bar.Baz.getBlim")
.in(javaFileObject).onLine(12);
}
@Test
public void autoValueBuilderExtraSetter() {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Baz {",
" abstract String blam();",
"",
" @AutoValue.Builder",
" public interface Builder {",
" Builder blim(int x);",
" Builder blam(String x);",
" Baz build();",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.failsToCompile()
.withErrorContaining("Method does not correspond to a property of foo.bar.Baz")
.in(javaFileObject).onLine(11);
}
@Test
public void autoValueBuilderSetPrefixAndNoSetPrefix() {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Baz {",
" abstract int blim();",
" abstract String blam();",
"",
" @AutoValue.Builder",
" public interface Builder {",
" Builder blim(int x);",
" Builder setBlam(String x);",
" Baz build();",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.failsToCompile()
.withErrorContaining("If any setter methods use the setFoo convention then all must")
.in(javaFileObject).onLine(12);
}
@Test
public void autoValueBuilderWrongTypeGetter() {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Baz<T, U> {",
" abstract T blim();",
" abstract U blam();",
"",
" @AutoValue.Builder",
" public interface Builder<T, U> {",
" Builder<T, U> blim(T x);",
" Builder<T, U> blam(U x);",
" T blim();",
" T blam();",
" Baz<T, U> build();",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.failsToCompile()
.withErrorContaining(
"Method matches a property of foo.bar.Baz but has return type T instead of U")
.in(javaFileObject).onLine(15);
}
@Test
public void autoValueBuilderPropertyBuilderAndSetter() {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"import com.google.common.collect.ImmutableSet;",
"",
"@AutoValue",
"public abstract class Baz {",
" abstract ImmutableSet<String> blim();",
"",
" @AutoValue.Builder",
" public interface Builder {",
" abstract ImmutableSet.Builder<String> blimBuilder();",
" abstract Builder setBlim(ImmutableSet<String> blim);",
" Baz build();",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.failsToCompile()
.withErrorContaining("Property blim cannot have both a setter and a builder")
.in(javaFileObject).onLine(11);
}
@Test
public void autoValueBuilderPropertyBuilderInvalidType() {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Baz<T, U> {",
" abstract String blim();",
"",
" @AutoValue.Builder",
" public interface Builder<T, U> {",
" StringBuilder blimBuilder();",
" Baz<T, U> build();",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.failsToCompile()
.withErrorContaining(
"Method looks like a property builder, but its return type is not a builder for an "
+ "immutable type in com.google.common.collect")
.in(javaFileObject).onLine(11);
}
@Test
public void autoValueBuilderPropertyBuilderRawType() {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"import com.google.common.collect.ImmutableList;",
"",
"@AutoValue",
"public abstract class Baz<T, U> {",
" abstract ImmutableList blim();",
"",
" @AutoValue.Builder",
" public interface Builder<T, U> {",
" ImmutableList.Builder blimBuilder();",
" Baz<T, U> build();",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.failsToCompile()
.withErrorContaining(
"Property builder type cannot be raw (missing <...>)")
.in(javaFileObject).onLine(12);
}
@Test
public void autoValueBuilderPropertyBuilderWrongCollectionType() {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"import com.google.common.collect.ImmutableList;",
"import com.google.common.collect.ImmutableSet;",
"",
"@AutoValue",
"public abstract class Baz<T, U> {",
" abstract ImmutableList<T> blim();",
"",
" @AutoValue.Builder",
" public interface Builder<T, U> {",
" ImmutableSet.Builder<T> blimBuilder();",
" Baz<T, U> build();",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.failsToCompile()
.withErrorContaining(
"Return type of property-builder method implies a property of type "
+ "com.google.common.collect.ImmutableSet<T>, but property blim has type "
+ "com.google.common.collect.ImmutableList<T>")
.in(javaFileObject).onLine(13);
}
@Test
public void autoValueBuilderPropertyBuilderWrongElementType() {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"import com.google.common.collect.ImmutableSet;",
"",
"@AutoValue",
"public abstract class Baz<T, U> {",
" abstract ImmutableSet<T> blim();",
"",
" @AutoValue.Builder",
" public interface Builder<T, U> {",
" ImmutableSet.Builder<U> blimBuilder();",
" Baz<T, U> build();",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.failsToCompile()
.withErrorContaining(
"Return type of property-builder method implies a property of type "
+ "com.google.common.collect.ImmutableSet<U>, but property blim has type "
+ "com.google.common.collect.ImmutableSet<T>")
.in(javaFileObject).onLine(12);
}
@Test
public void autoValueBuilderAlienMethod0() {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Baz {",
" abstract String blam();",
"",
" @AutoValue.Builder",
" public interface Builder {",
" Builder blam(String x);",
" Builder whut();",
" Baz build();",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.failsToCompile()
.withErrorContaining(
"Method without arguments should be a build method returning foo.bar.Baz"
+ " or a getter method with the same name and type as a getter method of foo.bar.Baz")
.in(javaFileObject).onLine(12);
}
@Test
public void autoValueBuilderAlienMethod1() {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Baz {",
" abstract String blam();",
"",
" @AutoValue.Builder",
" public interface Builder {",
" void whut(String x);",
" Baz build();",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.failsToCompile()
.withErrorContaining("Method does not correspond to a property of foo.bar.Baz")
.in(javaFileObject).onLine(11);
}
@Test
public void autoValueBuilderAlienMethod2() {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Baz {",
" abstract String blam();",
"",
" @AutoValue.Builder",
" public interface Builder {",
" Builder blam(String x, String y);",
" Baz build();",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.failsToCompile()
.withErrorContaining("Builder methods must have 0 or 1 parameters")
.in(javaFileObject).onLine(11);
}
@Test
public void autoValueBuilderMissingBuildMethod() {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Baz<T> {",
" abstract T blam();",
"",
" @AutoValue.Builder",
" public interface Builder<T> {",
" Builder<T> blam(T x);",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.failsToCompile()
.withErrorContaining(
"Builder must have a single no-argument method returning foo.bar.Baz<T>")
.in(javaFileObject).onLine(10);
}
@Test
public void autoValueBuilderDuplicateBuildMethods() {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Baz {",
" abstract String blam();",
"",
" @AutoValue.Builder",
" public interface Builder {",
" Builder blam(String x);",
" Baz build();",
" Baz create();",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.failsToCompile()
.withErrorContaining("Builder must have a single no-argument method returning foo.bar.Baz")
.in(javaFileObject).onLine(12)
.and()
.withErrorContaining("Builder must have a single no-argument method returning foo.bar.Baz")
.in(javaFileObject).onLine(13);
}
@Test
public void autoValueBuilderWrongTypeBuildMethod() {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Baz {",
" abstract String blam();",
"",
" @AutoValue.Builder",
" public interface Builder {",
" Builder blam(String x);",
" String build();",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.failsToCompile()
.withErrorContaining(
"Method without arguments should be a build method returning foo.bar.Baz")
.in(javaFileObject).onLine(12);
}
@Test
public void autoValueBuilderTypeParametersDontMatch1() {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Baz<T> {",
" abstract String blam();",
"",
" @AutoValue.Builder",
" public interface Builder {",
" Builder blam(String x);",
" Baz build();",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.failsToCompile()
.withErrorContaining("Type parameters of foo.bar.Baz.Builder must have same names and "
+ "bounds as type parameters of foo.bar.Baz")
.in(javaFileObject).onLine(10);
}
@Test
public void autoValueBuilderTypeParametersDontMatch2() {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Baz<T> {",
" abstract T blam();",
"",
" @AutoValue.Builder",
" public interface Builder<E> {",
" Builder<E> blam(E x);",
" Baz build();",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.failsToCompile()
.withErrorContaining("Type parameters of foo.bar.Baz.Builder must have same names and "
+ "bounds as type parameters of foo.bar.Baz")
.in(javaFileObject).onLine(10);
}
@Test
public void autoValueBuilderTypeParametersDontMatch3() {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Baz<T extends Number & Comparable<T>> {",
" abstract T blam();",
"",
" @AutoValue.Builder",
" public interface Builder<T extends Number> {",
" Builder<T> blam(T x);",
" Baz build();",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.failsToCompile()
.withErrorContaining("Type parameters of foo.bar.Baz.Builder must have same names and "
+ "bounds as type parameters of foo.bar.Baz")
.in(javaFileObject).onLine(10);
}
@Test
public void autoValueBuilderToBuilderWrongTypeParameters() {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"abstract class Baz<K extends Comparable<K>, V> {",
" abstract K key();",
" abstract V value();",
" abstract Builder<V, K> toBuilder1();",
"",
" @AutoValue.Builder",
" interface Builder<K extends Comparable<K>, V> {",
" Builder<K, V> key(K key);",
" Builder<K, V> value(V value);",
" Baz<K, V> build();",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.failsToCompile()
.withErrorContaining("Builder converter method should return foo.bar.Baz.Builder<K, V>")
.in(javaFileObject).onLine(9);
}
@Test
public void autoValueBuilderToBuilderDuplicate() {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"abstract class Baz<K extends Comparable<K>, V> {",
" abstract K key();",
" abstract V value();",
" abstract Builder<K, V> toBuilder1();",
" abstract Builder<K, V> toBuilder2();",
"",
" @AutoValue.Builder",
" interface Builder<K extends Comparable<K>, V> {",
" Builder<K, V> key(K key);",
" Builder<K, V> value(V value);",
" Baz<K, V> build();",
" }",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.failsToCompile()
.withErrorContaining("There can be at most one builder converter method")
.in(javaFileObject).onLine(9);
}
@Test
public void getFooIsFoo() throws Exception {
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Baz {",
" abstract int getFoo();",
" abstract boolean isFoo();",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new AutoValueProcessor())
.failsToCompile()
.withErrorContaining("More than one @AutoValue property called foo")
.in(javaFileObject).onLine(8);
}
private static class PoisonedAutoValueProcessor extends AutoValueProcessor {
private final IllegalArgumentException filerException;
PoisonedAutoValueProcessor(IllegalArgumentException filerException) {
this.filerException = filerException;
}
private class ErrorInvocationHandler implements InvocationHandler {
private final ProcessingEnvironment originalProcessingEnv;
ErrorInvocationHandler(ProcessingEnvironment originalProcessingEnv) {
this.originalProcessingEnv = originalProcessingEnv;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("getFiler")) {
throw filerException;
} else {
return method.invoke(originalProcessingEnv, args);
}
}
};
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
ProcessingEnvironment poisonedProcessingEnv = (ProcessingEnvironment) Proxy.newProxyInstance(
getClass().getClassLoader(),
new Class<?>[] {ProcessingEnvironment.class},
new ErrorInvocationHandler(processingEnv));
processingEnv = poisonedProcessingEnv;
return super.process(annotations, roundEnv);
}
}
@Test
public void exceptionBecomesError() throws Exception {
// Ensure that if the annotation processor code gets an unexpected exception, it is converted
// into a compiler error rather than being propagated. Otherwise the output can be very
// confusing to the user who stumbles into a bug that causes an exception, whether in
// AutoValueProcessor or javac.
// We inject an exception by subclassing AutoValueProcessor in order to poison its processingEnv
// in a way that will cause an exception the first time it tries to get the Filer.
IllegalArgumentException exception =
new IllegalArgumentException("I don't understand the question, and I won't respond to it");
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Baz {",
" public abstract int foo();",
"}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(new PoisonedAutoValueProcessor(exception))
.failsToCompile()
.withErrorContaining(exception.toString())
.in(javaFileObject).onLine(6);
}
@Retention(RetentionPolicy.SOURCE)
public @interface Foo {}
/* Processor that generates an empty class BarFoo every time it sees a class Bar annotated with
* @Foo.
*/
public static class FooProcessor extends AbstractProcessor {
@Override
public Set<String> getSupportedAnnotationTypes() {
return ImmutableSet.of(Foo.class.getCanonicalName());
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Foo.class);
for (TypeElement type : ElementFilter.typesIn(elements)) {
try {
generateFoo(type);
} catch (IOException e) {
throw new AssertionError(e);
}
}
return false;
}
private void generateFoo(TypeElement type) throws IOException {
String pkg = TypeSimplifier.packageNameOf(type);
String className = type.getSimpleName().toString();
String generatedClassName = className + "Foo";
JavaFileObject source =
processingEnv.getFiler().createSourceFile(pkg + "." + generatedClassName, type);
PrintWriter writer = new PrintWriter(source.openWriter());
writer.println("package " + pkg + ";");
writer.println("public class " + generatedClassName + " {}");
writer.close();
}
}
@Test
public void referencingGeneratedClass() {
// Test that ensures that a type that does not exist can be the type of an @AutoValue property
// as long as it later does come into existence. The BarFoo type referenced here does not exist
// when the AutoValueProcessor runs on the first round, but the FooProcessor then generates it.
// That generation provokes a further round of annotation processing and AutoValueProcessor
// should succeed then.
JavaFileObject bazFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Baz",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@AutoValue",
"public abstract class Baz {",
" public abstract BarFoo barFoo();",
"",
" public static Baz create(BarFoo barFoo) {",
" return new AutoValue_Baz(barFoo);",
" }",
"}");
JavaFileObject barFileObject = JavaFileObjects.forSourceLines(
"foo.bar.Bar",
"package foo.bar;",
"",
"import com.google.auto.value.AutoValue;",
"",
"@" + Foo.class.getCanonicalName(),
"public abstract class Bar {",
" public abstract BarFoo barFoo();",
"}");
assertAbout(javaSources())
.that(ImmutableList.of(bazFileObject, barFileObject))
.processedWith(new AutoValueProcessor(), new FooProcessor())
.compilesWithoutError();
}
}