| /* |
| * 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.testing.compile.CompilationSubject.assertThat; |
| import static com.google.testing.compile.Compiler.javac; |
| |
| import com.google.testing.compile.Compilation; |
| import com.google.testing.compile.JavaFileObjects; |
| import javax.tools.JavaFileObject; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| |
| /** |
| * Tests for compilation errors with the AutoAnnotation processor. |
| * |
| * @author emcmanus@google.com (Éamonn McManus) |
| */ |
| @RunWith(JUnit4.class) |
| public class AutoAnnotationErrorsTest { |
| private static final JavaFileObject TEST_ANNOTATION = |
| JavaFileObjects.forSourceLines( |
| "com.example.TestAnnotation", |
| "package com.example;", |
| "", |
| "public @interface TestAnnotation {", |
| " int value();", |
| "}"); |
| |
| @Test |
| public void testCorrect() { |
| JavaFileObject testSource = |
| JavaFileObjects.forSourceLines( |
| "com.foo.Test", |
| "package com.foo;", |
| "", |
| "import com.example.TestAnnotation;", |
| "import com.google.auto.value.AutoAnnotation;", |
| "", |
| "class Test {", |
| " @AutoAnnotation static TestAnnotation newTestAnnotation(int value) {", |
| " return new AutoAnnotation_Test_newTestAnnotation(value);", |
| " }", |
| "}"); |
| Compilation compilation = |
| javac() |
| .withProcessors(new AutoAnnotationProcessor()) |
| .compile(TEST_ANNOTATION, testSource); |
| assertThat(compilation).succeededWithoutWarnings(); |
| } |
| |
| @Test |
| public void testNotStatic() { |
| JavaFileObject testSource = |
| JavaFileObjects.forSourceLines( |
| "com.foo.Test", |
| "package com.foo;", |
| "", |
| "import com.example.TestAnnotation;", |
| "import com.google.auto.value.AutoAnnotation;", |
| "", |
| "class Test {", |
| " @AutoAnnotation TestAnnotation newTestAnnotation(int value) {", |
| " return new AutoAnnotation_Test_newTestAnnotation(value);", |
| " }", |
| "}"); |
| Compilation compilation = |
| javac() |
| .withProcessors(new AutoAnnotationProcessor()) |
| .compile(TEST_ANNOTATION, testSource); |
| assertThat(compilation) |
| .hadErrorContaining("must be static") |
| .inFile(testSource) |
| .onLineContaining("TestAnnotation newTestAnnotation(int value)"); |
| } |
| |
| @Test |
| public void testDoesNotReturnAnnotation() { |
| JavaFileObject testSource = |
| JavaFileObjects.forSourceLines( |
| "com.foo.Test", |
| "package com.foo;", |
| "", |
| "import com.google.auto.value.AutoAnnotation;", |
| "", |
| "class Test {", |
| " @AutoAnnotation static String newString(int value) {", |
| " return new AutoAnnotation_Test_newString(value);", |
| " }", |
| "}"); |
| Compilation compilation = |
| javac() |
| .withProcessors(new AutoAnnotationProcessor()) |
| .compile(TEST_ANNOTATION, testSource); |
| assertThat(compilation) |
| .hadErrorContaining("must be an annotation type, not java.lang.String") |
| .inFile(testSource) |
| .onLineContaining("static String newString(int value)"); |
| } |
| |
| @Test |
| public void testOverload() { |
| JavaFileObject testSource = |
| JavaFileObjects.forSourceLines( |
| "com.foo.Test", |
| "package com.foo;", |
| "", |
| "import com.example.TestAnnotation;", |
| "import com.google.auto.value.AutoAnnotation;", |
| "", |
| "class Test {", |
| " @AutoAnnotation static TestAnnotation newTestAnnotation(int value) {", |
| " return new AutoAnnotation_Test_newTestAnnotation(value);", |
| " }", |
| "", |
| " @AutoAnnotation static TestAnnotation newTestAnnotation(Integer value) {", |
| " return new AutoAnnotation_Test_newTestAnnotation(value);", |
| " }", |
| "}"); |
| Compilation compilation = |
| javac() |
| .withProcessors(new AutoAnnotationProcessor()) |
| .compile(TEST_ANNOTATION, testSource); |
| assertThat(compilation) |
| .hadErrorContaining("@AutoAnnotation methods cannot be overloaded") |
| .inFile(testSource) |
| .onLineContaining("newTestAnnotation(Integer value)"); |
| } |
| |
| // Overload detection used to detect all @AutoAnnotation methods that resulted in |
| // annotation class of the same SimpleName as being an overload. |
| // This verifies that implementations in different packages work correctly. |
| @Test |
| public void testSameNameDifferentPackagesDoesNotTriggerOverload() { |
| |
| JavaFileObject fooTestSource = |
| JavaFileObjects.forSourceLines( |
| "com.foo.Test", |
| "package com.foo;", |
| "", |
| "import com.example.TestAnnotation;", |
| "import com.google.auto.value.AutoAnnotation;", |
| "", |
| "class Test {", |
| " @AutoAnnotation static TestAnnotation newTestAnnotation(int value) {", |
| " return new AutoAnnotation_Test_newTestAnnotation(value);", |
| " }", |
| "}"); |
| JavaFileObject barTestSource = |
| JavaFileObjects.forSourceLines( |
| "com.bar.Test", |
| "package com.bar;", |
| "", |
| "import com.example.TestAnnotation;", |
| "import com.google.auto.value.AutoAnnotation;", |
| "", |
| "class Test {", |
| " @AutoAnnotation static TestAnnotation newTestAnnotation(int value) {", |
| " return new AutoAnnotation_Test_newTestAnnotation(value);", |
| " }", |
| "}"); |
| Compilation compilation = |
| javac() |
| .withProcessors(new AutoAnnotationProcessor()) |
| .compile(fooTestSource, barTestSource, TEST_ANNOTATION); |
| assertThat(compilation).succeededWithoutWarnings(); |
| } |
| |
| @Test |
| public void testWrongName() { |
| JavaFileObject testSource = |
| JavaFileObjects.forSourceLines( |
| "com.foo.Test", |
| "package com.foo;", |
| "", |
| "import com.example.TestAnnotation;", |
| "import com.google.auto.value.AutoAnnotation;", |
| "", |
| "class Test {", |
| " @AutoAnnotation static TestAnnotation newTestAnnotation(int fred) {", |
| " return new AutoAnnotation_Test_newTestAnnotation(fred);", |
| " }", |
| "}"); |
| Compilation compilation = |
| javac() |
| .withProcessors(new AutoAnnotationProcessor()) |
| .compile(TEST_ANNOTATION, testSource); |
| assertThat(compilation) |
| .hadErrorContaining("method parameter 'fred' must have the same name") |
| .inFile(testSource) |
| .onLineContaining("newTestAnnotation(int fred)"); |
| } |
| |
| @Test |
| public void testWrongType() { |
| JavaFileObject testSource = |
| JavaFileObjects.forSourceLines( |
| "com.foo.Test", |
| "package com.foo;", |
| "", |
| "import com.example.TestAnnotation;", |
| "import com.google.auto.value.AutoAnnotation;", |
| "", |
| "class Test {", |
| " @AutoAnnotation static TestAnnotation newTestAnnotation(String value) {", |
| " return new AutoAnnotation_Test_newTestAnnotation(value);", |
| " }", |
| "}"); |
| Compilation compilation = |
| javac() |
| .withProcessors(new AutoAnnotationProcessor()) |
| .compile(TEST_ANNOTATION, testSource); |
| assertThat(compilation) |
| .hadErrorContaining( |
| "method parameter 'value' has type java.lang.String " |
| + "but com.example.TestAnnotation.value has type int") |
| .inFile(testSource) |
| .onLineContaining("newTestAnnotation(String value)"); |
| } |
| |
| @Test |
| public void testWrongTypeCollection() { |
| JavaFileObject testAnnotation = |
| JavaFileObjects.forSourceLines( |
| "com.example.TestAnnotation", |
| "package com.example;", |
| "", |
| "public @interface TestAnnotation {", |
| " int[] value();", |
| "}"); |
| String[] wrongTypes = { |
| "java.util.List<java.lang.Long>", |
| "java.util.List<java.lang.String>", |
| "java.util.Set<java.lang.Long>", |
| "java.util.Map<java.lang.Integer,java.lang.Integer>", |
| "java.util.concurrent.Callable<java.lang.Integer>", |
| "java.util.List<int[]>", |
| }; |
| for (String wrongType : wrongTypes) { |
| JavaFileObject testSource = |
| JavaFileObjects.forSourceLines( |
| "com.foo.Test", |
| "package com.foo;", |
| "", |
| "import com.example.TestAnnotation;", |
| "import com.google.auto.value.AutoAnnotation;", |
| "", |
| "class Test {", |
| " @AutoAnnotation static TestAnnotation newTestAnnotation(" |
| + wrongType |
| + " value) {", |
| " return new AutoAnnotation_Test_newTestAnnotation(value);", |
| " }", |
| "}"); |
| Compilation compilation = |
| javac() |
| .withProcessors(new AutoAnnotationProcessor()) |
| .compile(testSource, testAnnotation); |
| assertThat(compilation) |
| .hadErrorContaining( |
| "method parameter 'value' has type " |
| + wrongType |
| + " but com.example.TestAnnotation.value has type int[]") |
| .inFile(testSource) |
| .onLineContaining("TestAnnotation newTestAnnotation("); |
| } |
| } |
| |
| @Test |
| public void testExtraParameters() { |
| JavaFileObject testSource = |
| JavaFileObjects.forSourceLines( |
| "com.foo.Test", |
| "package com.foo;", |
| "", |
| "import com.example.TestAnnotation;", |
| "import com.google.auto.value.AutoAnnotation;", |
| "", |
| "class Test {", |
| " @AutoAnnotation static TestAnnotation newTestAnnotation(int value, int other) {", |
| " return new AutoAnnotation_Test_newTestAnnotation(value);", |
| " }", |
| "}"); |
| Compilation compilation = |
| javac() |
| .withProcessors(new AutoAnnotationProcessor()) |
| .compile(TEST_ANNOTATION, testSource); |
| assertThat(compilation) |
| .hadErrorContaining( |
| "method parameter 'other' must have the same name as a member of " |
| + "com.example.TestAnnotation") |
| .inFile(testSource) |
| .onLineContaining("newTestAnnotation(int value, int other)"); |
| } |
| |
| @Test |
| public void testMissingParameters() { |
| JavaFileObject testSource = |
| JavaFileObjects.forSourceLines( |
| "com.foo.Test", |
| "package com.foo;", |
| "", |
| "import com.example.TestAnnotation;", |
| "import com.google.auto.value.AutoAnnotation;", |
| "", |
| "class Test {", |
| " @AutoAnnotation static TestAnnotation newTestAnnotation() {", |
| " return new AutoAnnotation_Test_newTestAnnotation();", |
| " }", |
| "}"); |
| Compilation compilation = |
| javac() |
| .withProcessors(new AutoAnnotationProcessor()) |
| .compile(TEST_ANNOTATION, testSource); |
| assertThat(compilation) |
| .hadErrorContaining("method needs a parameter with name 'value' and type int") |
| .inFile(testSource) |
| .onLineContaining("TestAnnotation newTestAnnotation()"); |
| } |
| |
| @Test |
| public void testAnnotationValuedDefaultsNotSupportedYet() { |
| JavaFileObject annotationSource = |
| JavaFileObjects.forSourceLines( |
| "com.example.TestAnnotation", |
| "package com.example;", |
| "", |
| "public @interface TestAnnotation {", |
| " String value();", |
| " Override optionalAnnotation() default @Override;", |
| "}"); |
| JavaFileObject testSource = |
| JavaFileObjects.forSourceLines( |
| "com.foo.Test", |
| "package com.foo;", |
| "", |
| "import com.example.TestAnnotation;", |
| "import com.google.auto.value.AutoAnnotation;", |
| "", |
| "class Test {", |
| " @AutoAnnotation static TestAnnotation newTestAnnotation(String value) {", |
| " return new AutoAnnotation_Test_newTestAnnotation(value);", |
| " }", |
| "}"); |
| Compilation compilation = |
| javac() |
| .withProcessors(new AutoAnnotationProcessor()) |
| .compile(annotationSource, testSource); |
| assertThat(compilation) |
| .hadErrorContaining( |
| "@AutoAnnotation cannot yet supply a default value for annotation-valued member " |
| + "'optionalAnnotation'") |
| .inFile(testSource) |
| .onLineContaining("TestAnnotation newTestAnnotation(String value)"); |
| } |
| |
| @Test |
| public void testAnnotationMemberNameConflictWithGeneratedLocal() { |
| JavaFileObject annotationSource = |
| JavaFileObjects.forSourceLines( |
| "com.example.TestAnnotation", |
| "package com.example;", |
| "", |
| "import java.lang.annotation.Annotation;", |
| "", |
| "public @interface TestAnnotation {", |
| " Class<? extends Annotation>[] value();", |
| " int value$();", |
| "}"); |
| JavaFileObject testSource = |
| JavaFileObjects.forSourceLines( |
| "com.foo.Test", |
| "package com.foo;", |
| "", |
| "import java.lang.annotation.Annotation;", |
| "import java.util.Collection;", |
| "", |
| "import com.example.TestAnnotation;", |
| "import com.google.auto.value.AutoAnnotation;", |
| "", |
| "class Test {", |
| " @AutoAnnotation static TestAnnotation newTestAnnotation(", |
| " Collection<Class<? extends Annotation>> value, int value$) {", |
| " return new AutoAnnotation_Test_newTestAnnotation(value, value$);", |
| " }", |
| "}"); |
| Compilation compilation = |
| javac() |
| .withProcessors(new AutoAnnotationProcessor()) |
| .compile(annotationSource, testSource); |
| assertThat(compilation) |
| .hadErrorContaining("variable value$ is already defined in constructor"); |
| } |
| } |