diff --git a/factory/pom.xml b/factory/pom.xml
index f2f74dece2..a7bd250571 100644
--- a/factory/pom.xml
+++ b/factory/pom.xml
@@ -118,6 +118,12 @@
0.21.0
test
+
+ com.google.testparameterinjector
+ test-parameter-injector
+ 1.12
+ test
+
junit
junit
diff --git a/factory/src/test/java/com/google/auto/factory/processor/AutoFactoryProcessorNegativeTest.java b/factory/src/test/java/com/google/auto/factory/processor/AutoFactoryProcessorNegativeTest.java
new file mode 100644
index 0000000000..c8394a6325
--- /dev/null
+++ b/factory/src/test/java/com/google/auto/factory/processor/AutoFactoryProcessorNegativeTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2013 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.factory.processor;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.Compiler;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Testing compilation errors from {@link AutoFactoryProcessor}. */
+@RunWith(JUnit4.class)
+public class AutoFactoryProcessorNegativeTest {
+ private final Compiler javac = Compiler.javac().withProcessors(new AutoFactoryProcessor());
+
+ @Test
+ public void failsWithMixedFinals() {
+ JavaFileObject file = JavaFileObjects.forResource("bad/MixedFinals.java");
+ Compilation compilation = javac.compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "Cannot mix allowSubclasses=true and allowSubclasses=false in one factory.")
+ .inFile(file)
+ .onLine(24);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "Cannot mix allowSubclasses=true and allowSubclasses=false in one factory.")
+ .inFile(file)
+ .onLine(27);
+ }
+
+ @Test
+ public void providedButNoAutoFactory() {
+ JavaFileObject file = JavaFileObjects.forResource("bad/ProvidedButNoAutoFactory.java");
+ Compilation compilation = javac.compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@Provided may only be applied to constructors requesting an auto-factory")
+ .inFile(file)
+ .onLineContaining("@Provided");
+ }
+
+ @Test
+ public void providedOnMethodParameter() {
+ JavaFileObject file = JavaFileObjects.forResource("bad/ProvidedOnMethodParameter.java");
+ Compilation compilation = javac.compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("@Provided may only be applied to constructor parameters")
+ .inFile(file)
+ .onLineContaining("@Provided");
+ }
+
+ @Test
+ public void invalidCustomName() {
+ JavaFileObject file = JavaFileObjects.forResource("bad/InvalidCustomName.java");
+ Compilation compilation = javac.compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("\"SillyFactory!\" is not a valid Java identifier")
+ .inFile(file)
+ .onLineContaining("SillyFactory!");
+ }
+
+ @Test
+ public void factoryExtendingAbstractClass_withConstructorParams() {
+ JavaFileObject file =
+ JavaFileObjects.forResource("bad/FactoryExtendingAbstractClassWithConstructorParams.java");
+ Compilation compilation = javac.compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "tests.FactoryExtendingAbstractClassWithConstructorParams.AbstractFactory is not a"
+ + " valid supertype for a factory. Factory supertypes must have a no-arg"
+ + " constructor.")
+ .inFile(file)
+ .onLineContaining("@AutoFactory");
+ }
+
+ @Test
+ public void factoryExtendingInterface() {
+ JavaFileObject file = JavaFileObjects.forResource("bad/InterfaceSupertype.java");
+ Compilation compilation = javac.compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "java.lang.Runnable is not a valid supertype for a factory. Supertypes must be"
+ + " non-final classes.")
+ .inFile(file)
+ .onLineContaining("@AutoFactory");
+ }
+
+ @Test
+ public void factoryExtendingEnum() {
+ JavaFileObject file = JavaFileObjects.forResource("bad/EnumSupertype.java");
+ Compilation compilation = javac.compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "java.util.concurrent.TimeUnit is not a valid supertype for a factory. Supertypes must"
+ + " be non-final classes.")
+ .inFile(file)
+ .onLineContaining("@AutoFactory");
+ }
+
+ @Test
+ public void factoryExtendingFinalClass() {
+ JavaFileObject file = JavaFileObjects.forResource("bad/FinalSupertype.java");
+ Compilation compilation = javac.compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "java.lang.Boolean is not a valid supertype for a factory. Supertypes must be"
+ + " non-final classes.")
+ .inFile(file)
+ .onLineContaining("@AutoFactory");
+ }
+}
diff --git a/factory/src/test/java/com/google/auto/factory/processor/AutoFactoryProcessorTest.java b/factory/src/test/java/com/google/auto/factory/processor/AutoFactoryProcessorTest.java
index 515ac43550..52018a390c 100644
--- a/factory/src/test/java/com/google/auto/factory/processor/AutoFactoryProcessorTest.java
+++ b/factory/src/test/java/com/google/auto/factory/processor/AutoFactoryProcessorTest.java
@@ -19,6 +19,7 @@
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.StandardSystemProperty.JAVA_SPECIFICATION_VERSION;
import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.TruthJUnit.assume;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static java.lang.Math.max;
@@ -32,8 +33,12 @@
import com.google.testing.compile.Compilation;
import com.google.testing.compile.Compiler;
import com.google.testing.compile.JavaFileObjects;
+import com.google.testing.junit.testparameterinjector.TestParameter;
+import com.google.testing.junit.testparameterinjector.TestParameterInjector;
+import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
+import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -46,12 +51,78 @@
import org.junit.AfterClass;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
/** Functional tests for the {@link AutoFactoryProcessor}. */
-@RunWith(JUnit4.class)
+@RunWith(TestParameterInjector.class)
public class AutoFactoryProcessorTest {
- private final Compiler javac = Compiler.javac().withProcessors(new AutoFactoryProcessor());
+ private final Config config;
+
+ public AutoFactoryProcessorTest(@TestParameter Config config) {
+ this.config = config;
+ }
+
+ private enum InjectPackage {
+ JAVAX,
+ }
+
+ private enum Config {
+ JAVAX_ONLY_ON_CLASSPATH(ImmutableList.of(InjectPackage.JAVAX), InjectPackage.JAVAX);
+
+ /** Config that is used for negative tests, and to update the golden files. */
+ static final Config DEFAULT = JAVAX_ONLY_ON_CLASSPATH;
+
+ final ImmutableList packagesOnClasspath;
+ final InjectPackage unusedExpectedPackage;
+ final ImmutableList options;
+
+ Config(ImmutableList packagesOnClasspath, InjectPackage expectedPackage) {
+ this(packagesOnClasspath, expectedPackage, ImmutableList.of());
+ }
+
+ Config(
+ ImmutableList packagesOnClasspath,
+ InjectPackage expectedPackage,
+ ImmutableList options) {
+ this.packagesOnClasspath = packagesOnClasspath;
+ this.unusedExpectedPackage = expectedPackage;
+ this.options = options;
+ }
+
+ static final ImmutableList COMMON_CLASSPATH =
+ ImmutableList.of(
+ fileForClass("com.google.auto.factory.AutoFactory"),
+ fileForClass("javax.annotation.Nullable"),
+ fileForClass("org.checkerframework.checker.nullness.compatqual.NullableType"));
+ static final File JAVAX_CLASSPATH = fileForClass("javax.inject.Provider");
+
+ static File fileForClass(String className) {
+ Class> c;
+ try {
+ c = Class.forName(className);
+ } catch (ClassNotFoundException e) {
+ throw new IllegalArgumentException(e);
+ }
+ URL url = c.getProtectionDomain().getCodeSource().getLocation();
+ assertThat(url.getProtocol()).isEqualTo("file");
+ return new File(url.getPath());
+ }
+
+ ImmutableList classpath() {
+ ImmutableList.Builder classpathBuilder =
+ ImmutableList.builder().addAll(COMMON_CLASSPATH);
+ if (packagesOnClasspath.contains(InjectPackage.JAVAX)) {
+ classpathBuilder.add(JAVAX_CLASSPATH);
+ }
+ return classpathBuilder.build();
+ }
+
+ Compiler javac() {
+ return Compiler.javac()
+ .withClasspath(classpath())
+ .withProcessors(new AutoFactoryProcessor())
+ .withOptions(options);
+ }
+ }
private static volatile boolean goldenFileFailures;
@@ -83,8 +154,8 @@ public static void explainGoldenFileFailures() {
private void goldenTest(
ImmutableList inputResources, ImmutableMap expectedOutput) {
ImmutableList javaFileObjects =
- inputResources.stream().map(JavaFileObjects::forResource).collect(toImmutableList());
- Compilation compilation = javac.compile(javaFileObjects);
+ inputResources.stream().map(this::goldenFile).collect(toImmutableList());
+ Compilation compilation = config.javac().compile(javaFileObjects);
assertThat(compilation).succeededWithoutWarnings();
expectedOutput.forEach(
(className, expectedSourceResource) -> {
@@ -97,15 +168,28 @@ private void goldenTest(
goldenFileFailures = true;
throw e;
}
- try {
- updateGoldenFile(compilation, className, expectedSourceResource);
- } catch (IOException e2) {
- throw new UncheckedIOException(e2);
+ if (config.equals(Config.DEFAULT)) {
+ try {
+ updateGoldenFile(compilation, className, expectedSourceResource);
+ } catch (IOException e2) {
+ throw new UncheckedIOException(e2);
+ }
}
}
});
}
+ private JavaFileObject goldenFile(String resourceName) {
+ try {
+ URL resourceUrl = Resources.getResource(resourceName);
+ String source = Resources.toString(resourceUrl, UTF_8);
+ String className = resourceName.replaceFirst("\\.java$", "").replace('/', '.');
+ return JavaFileObjects.forSourceString(className, source);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
private void updateGoldenFile(Compilation compilation, String className, String relativePath)
throws IOException {
Path goldenFileRootPath = Paths.get(GOLDEN_FILE_ROOT);
@@ -287,7 +371,7 @@ public void mixedDepsImplementingInterfaces() {
@Test
public void failsWithMixedFinals() {
JavaFileObject file = JavaFileObjects.forResource("bad/MixedFinals.java");
- Compilation compilation = javac.compile(file);
+ Compilation compilation = config.javac().compile(file);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
@@ -304,7 +388,7 @@ public void failsWithMixedFinals() {
@Test
public void providedButNoAutoFactory() {
JavaFileObject file = JavaFileObjects.forResource("bad/ProvidedButNoAutoFactory.java");
- Compilation compilation = javac.compile(file);
+ Compilation compilation = config.javac().compile(file);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
@@ -316,7 +400,7 @@ public void providedButNoAutoFactory() {
@Test
public void providedOnMethodParameter() {
JavaFileObject file = JavaFileObjects.forResource("bad/ProvidedOnMethodParameter.java");
- Compilation compilation = javac.compile(file);
+ Compilation compilation = config.javac().compile(file);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining("@Provided may only be applied to constructor parameters")
@@ -327,7 +411,7 @@ public void providedOnMethodParameter() {
@Test
public void invalidCustomName() {
JavaFileObject file = JavaFileObjects.forResource("bad/InvalidCustomName.java");
- Compilation compilation = javac.compile(file);
+ Compilation compilation = config.javac().compile(file);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining("\"SillyFactory!\" is not a valid Java identifier")
@@ -357,7 +441,7 @@ public void factoryWithConstructorThrowsClauseExtendingAbstractClass() {
public void factoryExtendingAbstractClass_withConstructorParams() {
JavaFileObject file =
JavaFileObjects.forResource("bad/FactoryExtendingAbstractClassWithConstructorParams.java");
- Compilation compilation = javac.compile(file);
+ Compilation compilation = config.javac().compile(file);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
@@ -378,7 +462,7 @@ public void factoryExtendingAbstractClass_multipleConstructors() {
@Test
public void factoryExtendingInterface() {
JavaFileObject file = JavaFileObjects.forResource("bad/InterfaceSupertype.java");
- Compilation compilation = javac.compile(file);
+ Compilation compilation = config.javac().compile(file);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
@@ -391,7 +475,7 @@ public void factoryExtendingInterface() {
@Test
public void factoryExtendingEnum() {
JavaFileObject file = JavaFileObjects.forResource("bad/EnumSupertype.java");
- Compilation compilation = javac.compile(file);
+ Compilation compilation = config.javac().compile(file);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
@@ -404,7 +488,7 @@ public void factoryExtendingEnum() {
@Test
public void factoryExtendingFinalClass() {
JavaFileObject file = JavaFileObjects.forResource("bad/FinalSupertype.java");
- Compilation compilation = javac.compile(file);
+ Compilation compilation = config.javac().compile(file);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
@@ -557,12 +641,9 @@ public void parameterAnnotations() {
}
private JavaFileObject loadExpectedFile(String resourceName) {
- if (isJavaxAnnotationProcessingGeneratedAvailable()) {
- return JavaFileObjects.forResource(resourceName);
- }
try {
List sourceLines = Resources.readLines(Resources.getResource(resourceName), UTF_8);
- replaceGeneratedImport(sourceLines);
+ rewriteImports(sourceLines);
return JavaFileObjects.forSourceLines(
resourceName.replace('/', '.').replace(".java", ""), sourceLines);
} catch (IOException e) {
@@ -570,11 +651,11 @@ private JavaFileObject loadExpectedFile(String resourceName) {
}
}
- private boolean isJavaxAnnotationProcessingGeneratedAvailable() {
+ private static boolean isJavaxAnnotationProcessingGeneratedAvailable() {
return SourceVersion.latestSupported().compareTo(SourceVersion.RELEASE_8) > 0;
}
- private static void replaceGeneratedImport(List sourceLines) {
+ private void rewriteImports(List sourceLines) {
int i = 0;
int firstImport = Integer.MAX_VALUE;
int lastImport = -1;
@@ -587,11 +668,13 @@ private static void replaceGeneratedImport(List sourceLines) {
}
if (lastImport >= 0) {
List importLines = sourceLines.subList(firstImport, lastImport + 1);
- importLines.replaceAll(
- line ->
- line.startsWith("import javax.annotation.processing.Generated;")
- ? "import javax.annotation.Generated;"
- : line);
+ if (!isJavaxAnnotationProcessingGeneratedAvailable()) {
+ importLines.replaceAll(
+ line ->
+ line.startsWith("import javax.annotation.processing.Generated;")
+ ? "import javax.annotation.Generated;"
+ : line);
+ }
Collections.sort(importLines);
}
}