aboutsummaryrefslogtreecommitdiff
path: root/service/processor
diff options
context:
space:
mode:
authorronshapiro <ronshapiro@google.com>2018-11-01 14:11:51 -0700
committerRon Shapiro <shapiro.rd@gmail.com>2018-11-06 16:53:54 -0600
commitaf1e5daff849b4762062018b9e8bce1716bfc8b0 (patch)
treeb56d93fcccb28de6955480fce2b3c9fe8a7c86ed /service/processor
parent34a6a037617102c25b8de8dbac6d2adb5fcb2c43 (diff)
downloadauto-af1e5daff849b4762062018b9e8bce1716bfc8b0.tar.gz
Separate AutoService annotations and processor
Fixes https://github.com/google/auto/issues/632 RELNOTES=`@AutoService` now has a separate artifact (`auto-service-annotations`) separate from the annotation processor (still `auto-service`) ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=219696198
Diffstat (limited to 'service/processor')
-rw-r--r--service/processor/pom.xml93
-rw-r--r--service/processor/src/main/java/com/google/auto/service/processor/AutoServiceProcessor.java294
-rw-r--r--service/processor/src/main/java/com/google/auto/service/processor/ServicesFiles.java99
-rw-r--r--service/processor/src/main/java/com/google/auto/service/processor/package-info.java18
-rw-r--r--service/processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor1
-rw-r--r--service/processor/src/test/java/com/google/auto/service/processor/AutoServiceProcessorTest.java67
-rw-r--r--service/processor/src/test/resources/META-INF/services/test.AnotherService1
-rw-r--r--service/processor/src/test/resources/META-INF/services/test.AnotherServiceMulti1
-rw-r--r--service/processor/src/test/resources/META-INF/services/test.SomeService3
-rw-r--r--service/processor/src/test/resources/META-INF/services/test.SomeServiceMulti1
-rw-r--r--service/processor/src/test/resources/test/AnotherService.java18
-rw-r--r--service/processor/src/test/resources/test/AnotherServiceProvider.java21
-rw-r--r--service/processor/src/test/resources/test/Enclosing.java23
-rw-r--r--service/processor/src/test/resources/test/MultiServiceProvider.java21
-rw-r--r--service/processor/src/test/resources/test/NoServices.java21
-rw-r--r--service/processor/src/test/resources/test/SomeService.java18
-rw-r--r--service/processor/src/test/resources/test/SomeServiceProvider1.java21
-rw-r--r--service/processor/src/test/resources/test/SomeServiceProvider2.java21
18 files changed, 742 insertions, 0 deletions
diff --git a/service/processor/pom.xml b/service/processor/pom.xml
new file mode 100644
index 00000000..341b2087
--- /dev/null
+++ b/service/processor/pom.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2013 Google, Inc.
+
+ 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>com.google.auto</groupId>
+ <artifactId>auto-parent</artifactId>
+ <version>6</version>
+ </parent>
+
+ <groupId>com.google.auto.service</groupId>
+ <artifactId>auto-service</artifactId>
+ <version>HEAD-SNAPSHOT</version>
+ <name>AutoService Processor</name>
+ <description>
+ Provider-configuration files for ServiceLoader.
+ </description>
+
+ <scm>
+ <url>http://github.com/google/auto</url>
+ <connection>scm:git:git://github.com/google/auto.git</connection>
+ <developerConnection>scm:git:ssh://git@github.com/google/auto.git</developerConnection>
+ <tag>HEAD</tag>
+ </scm>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.auto.service</groupId>
+ <artifactId>auto-service-annotations</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.auto</groupId>
+ <artifactId>auto-common</artifactId>
+ <version>0.10</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <!-- test dependencies -->
+ <dependency>
+ <groupId>com.google.testing.compile</groupId>
+ <artifactId>compile-testing</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.truth</groupId>
+ <artifactId>truth</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <!-- disable processing because the definition in META-INF/services breaks javac -->
+ <compilerArgument>-proc:none</compilerArgument>
+ <source>1.8</source>
+ <target>1.8</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/service/processor/src/main/java/com/google/auto/service/processor/AutoServiceProcessor.java b/service/processor/src/main/java/com/google/auto/service/processor/AutoServiceProcessor.java
new file mode 100644
index 00000000..4bd7ceca
--- /dev/null
+++ b/service/processor/src/main/java/com/google/auto/service/processor/AutoServiceProcessor.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * 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.service.processor;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
+import static com.google.auto.common.MoreElements.getAnnotationMirror;
+import static com.google.common.collect.ImmutableSet.toImmutableSet;
+
+import com.google.auto.common.MoreTypes;
+import com.google.auto.service.AutoService;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.Filer;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedOptions;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.SimpleAnnotationValueVisitor8;
+import javax.lang.model.util.Types;
+import javax.tools.Diagnostic.Kind;
+import javax.tools.FileObject;
+import javax.tools.StandardLocation;
+
+/**
+ * Processes {@link AutoService} annotations and generates the service provider
+ * configuration files described in {@link java.util.ServiceLoader}.
+ * <p>
+ * Processor Options:<ul>
+ * <li>debug - turns on debug statements</li>
+ * </ul>
+ */
+@SupportedOptions({ "debug", "verify" })
+public class AutoServiceProcessor extends AbstractProcessor {
+
+ @VisibleForTesting
+ static final String MISSING_SERVICES_ERROR = "No service interfaces provided for element!";
+
+ /**
+ * Maps the class names of service provider interfaces to the
+ * class names of the concrete classes which implement them.
+ * <p>
+ * For example,
+ * {@code "com.google.apphosting.LocalRpcService" ->
+ * "com.google.apphosting.datastore.LocalDatastoreService"}
+ */
+ private Multimap<String, String> providers = HashMultimap.create();
+
+ @Override
+ public ImmutableSet<String> getSupportedAnnotationTypes() {
+ return ImmutableSet.of(AutoService.class.getName());
+ }
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ /**
+ * <ol>
+ * <li> For each class annotated with {@link AutoService}<ul>
+ * <li> Verify the {@link AutoService} interface value is correct
+ * <li> Categorize the class by its service interface
+ * </ul>
+ *
+ * <li> For each {@link AutoService} interface <ul>
+ * <li> Create a file named {@code META-INF/services/<interface>}
+ * <li> For each {@link AutoService} annotated class for this interface <ul>
+ * <li> Create an entry in the file
+ * </ul>
+ * </ul>
+ * </ol>
+ */
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ try {
+ return processImpl(annotations, roundEnv);
+ } catch (Exception e) {
+ // We don't allow exceptions of any kind to propagate to the compiler
+ StringWriter writer = new StringWriter();
+ e.printStackTrace(new PrintWriter(writer));
+ fatalError(writer.toString());
+ return true;
+ }
+ }
+
+ private boolean processImpl(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ if (roundEnv.processingOver()) {
+ generateConfigFiles();
+ } else {
+ processAnnotations(annotations, roundEnv);
+ }
+
+ return true;
+ }
+
+ private void processAnnotations(Set<? extends TypeElement> annotations,
+ RoundEnvironment roundEnv) {
+
+ Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(AutoService.class);
+
+ log(annotations.toString());
+ log(elements.toString());
+
+ for (Element e : elements) {
+ // TODO(gak): check for error trees?
+ TypeElement providerImplementer = (TypeElement) e;
+ AnnotationMirror annotationMirror = getAnnotationMirror(e, AutoService.class).get();
+ Set<DeclaredType> providerInterfaces = getValueFieldOfClasses(annotationMirror);
+ if (providerInterfaces.isEmpty()) {
+ error(MISSING_SERVICES_ERROR, e, annotationMirror);
+ continue;
+ }
+ for (DeclaredType providerInterface : providerInterfaces) {
+ TypeElement providerType = MoreTypes.asTypeElement(providerInterface);
+
+ log("provider interface: " + providerType.getQualifiedName());
+ log("provider implementer: " + providerImplementer.getQualifiedName());
+
+ if (checkImplementer(providerImplementer, providerType)) {
+ providers.put(getBinaryName(providerType), getBinaryName(providerImplementer));
+ } else {
+ String message = "ServiceProviders must implement their service provider interface. "
+ + providerImplementer.getQualifiedName() + " does not implement "
+ + providerType.getQualifiedName();
+ error(message, e, annotationMirror);
+ }
+ }
+ }
+ }
+
+ private void generateConfigFiles() {
+ Filer filer = processingEnv.getFiler();
+
+ for (String providerInterface : providers.keySet()) {
+ String resourceFile = "META-INF/services/" + providerInterface;
+ log("Working on resource file: " + resourceFile);
+ try {
+ SortedSet<String> allServices = Sets.newTreeSet();
+ try {
+ // would like to be able to print the full path
+ // before we attempt to get the resource in case the behavior
+ // of filer.getResource does change to match the spec, but there's
+ // no good way to resolve CLASS_OUTPUT without first getting a resource.
+ FileObject existingFile = filer.getResource(StandardLocation.CLASS_OUTPUT, "",
+ resourceFile);
+ log("Looking for existing resource file at " + existingFile.toUri());
+ Set<String> oldServices = ServicesFiles.readServiceFile(existingFile.openInputStream());
+ log("Existing service entries: " + oldServices);
+ allServices.addAll(oldServices);
+ } catch (IOException e) {
+ // According to the javadoc, Filer.getResource throws an exception
+ // if the file doesn't already exist. In practice this doesn't
+ // appear to be the case. Filer.getResource will happily return a
+ // FileObject that refers to a non-existent file but will throw
+ // IOException if you try to open an input stream for it.
+ log("Resource file did not already exist.");
+ }
+
+ Set<String> newServices = new HashSet<String>(providers.get(providerInterface));
+ if (allServices.containsAll(newServices)) {
+ log("No new service entries being added.");
+ return;
+ }
+
+ allServices.addAll(newServices);
+ log("New service file contents: " + allServices);
+ FileObject fileObject = filer.createResource(StandardLocation.CLASS_OUTPUT, "",
+ resourceFile);
+ OutputStream out = fileObject.openOutputStream();
+ ServicesFiles.writeServiceFile(allServices, out);
+ out.close();
+ log("Wrote to: " + fileObject.toUri());
+ } catch (IOException e) {
+ fatalError("Unable to create " + resourceFile + ", " + e);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Verifies {@link ServiceProvider} constraints on the concrete provider class.
+ * Note that these constraints are enforced at runtime via the ServiceLoader,
+ * we're just checking them at compile time to be extra nice to our users.
+ */
+ private boolean checkImplementer(TypeElement providerImplementer, TypeElement providerType) {
+
+ String verify = processingEnv.getOptions().get("verify");
+ if (verify == null || !Boolean.valueOf(verify)) {
+ return true;
+ }
+
+ // TODO: We're currently only enforcing the subtype relationship
+ // constraint. It would be nice to enforce them all.
+
+ Types types = processingEnv.getTypeUtils();
+
+ return types.isSubtype(providerImplementer.asType(), providerType.asType());
+ }
+
+ /**
+ * Returns the binary name of a reference type. For example,
+ * {@code com.google.Foo$Bar}, instead of {@code com.google.Foo.Bar}.
+ *
+ */
+ private String getBinaryName(TypeElement element) {
+ return getBinaryNameImpl(element, element.getSimpleName().toString());
+ }
+
+ private String getBinaryNameImpl(TypeElement element, String className) {
+ Element enclosingElement = element.getEnclosingElement();
+
+ if (enclosingElement instanceof PackageElement) {
+ PackageElement pkg = (PackageElement) enclosingElement;
+ if (pkg.isUnnamed()) {
+ return className;
+ }
+ return pkg.getQualifiedName() + "." + className;
+ }
+
+ TypeElement typeElement = (TypeElement) enclosingElement;
+ return getBinaryNameImpl(typeElement, typeElement.getSimpleName() + "$" + className);
+ }
+
+ /**
+ * Returns the contents of a {@code Class[]}-typed "value" field in a given {@code annotationMirror}.
+ */
+ private ImmutableSet<DeclaredType> getValueFieldOfClasses(AnnotationMirror annotationMirror) {
+ return getAnnotationValue(annotationMirror, "value")
+ .accept(
+ new SimpleAnnotationValueVisitor8<ImmutableSet<DeclaredType>, Void>() {
+ @Override
+ public ImmutableSet<DeclaredType> visitType(TypeMirror typeMirror, Void v) {
+ // TODO(ronshapiro): class literals may not always be declared types, i.e. int.class,
+ // int[].class
+ return ImmutableSet.of(MoreTypes.asDeclared(typeMirror));
+ }
+
+ @Override
+ public ImmutableSet<DeclaredType> visitArray(
+ List<? extends AnnotationValue> values, Void v) {
+ return values
+ .stream()
+ .flatMap(value -> value.accept(this, null).stream())
+ .collect(toImmutableSet());
+ }
+ },
+ null);
+ }
+
+ private void log(String msg) {
+ if (processingEnv.getOptions().containsKey("debug")) {
+ processingEnv.getMessager().printMessage(Kind.NOTE, msg);
+ }
+ }
+
+ private void error(String msg, Element element, AnnotationMirror annotation) {
+ processingEnv.getMessager().printMessage(Kind.ERROR, msg, element, annotation);
+ }
+
+ private void fatalError(String msg) {
+ processingEnv.getMessager().printMessage(Kind.ERROR, "FATAL ERROR: " + msg);
+ }
+}
diff --git a/service/processor/src/main/java/com/google/auto/service/processor/ServicesFiles.java b/service/processor/src/main/java/com/google/auto/service/processor/ServicesFiles.java
new file mode 100644
index 00000000..c43200ab
--- /dev/null
+++ b/service/processor/src/main/java/com/google/auto/service/processor/ServicesFiles.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * 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.service.processor;
+
+import static com.google.common.base.Charsets.UTF_8;
+
+import com.google.common.io.Closer;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A helper class for reading and writing Services files.
+ */
+final class ServicesFiles {
+ public static final String SERVICES_PATH = "META-INF/services";
+
+ private ServicesFiles() { }
+
+ /**
+ * Returns an absolute path to a service file given the class
+ * name of the service.
+ *
+ * @param serviceName not {@code null}
+ * @return SERVICES_PATH + serviceName
+ */
+ static String getPath(String serviceName) {
+ return SERVICES_PATH + "/" + serviceName;
+ }
+
+ /**
+ * Reads the set of service classes from a service file.
+ *
+ * @param input not {@code null}. Closed after use.
+ * @return a not {@code null Set} of service class names.
+ * @throws IOException
+ */
+ static Set<String> readServiceFile(InputStream input) throws IOException {
+ HashSet<String> serviceClasses = new HashSet<String>();
+ Closer closer = Closer.create();
+ try {
+ // TODO(gak): use CharStreams
+ BufferedReader r = closer.register(new BufferedReader(new InputStreamReader(input, UTF_8)));
+ String line;
+ while ((line = r.readLine()) != null) {
+ int commentStart = line.indexOf('#');
+ if (commentStart >= 0) {
+ line = line.substring(0, commentStart);
+ }
+ line = line.trim();
+ if (!line.isEmpty()) {
+ serviceClasses.add(line);
+ }
+ }
+ return serviceClasses;
+ } catch (Throwable t) {
+ throw closer.rethrow(t);
+ } finally {
+ closer.close();
+ }
+ }
+
+ /**
+ * Writes the set of service class names to a service file.
+ *
+ * @param output not {@code null}. Not closed after use.
+ * @param services a not {@code null Collection} of service class names.
+ * @throws IOException
+ */
+ static void writeServiceFile(Collection<String> services, OutputStream output)
+ throws IOException {
+ BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output, UTF_8));
+ for (String service : services) {
+ writer.write(service);
+ writer.newLine();
+ }
+ writer.flush();
+ }
+} \ No newline at end of file
diff --git a/service/processor/src/main/java/com/google/auto/service/processor/package-info.java b/service/processor/src/main/java/com/google/auto/service/processor/package-info.java
new file mode 100644
index 00000000..233f37cf
--- /dev/null
+++ b/service/processor/src/main/java/com/google/auto/service/processor/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * 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.
+ */
+/**
+ * This package contains the annotation processor that implements the
+ * {@link com.google.auto.service.AutoService} API.
+ */
+package com.google.auto.service.processor; \ No newline at end of file
diff --git a/service/processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/service/processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor
new file mode 100644
index 00000000..a4406e1d
--- /dev/null
+++ b/service/processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor
@@ -0,0 +1 @@
+com.google.auto.service.processor.AutoServiceProcessor
diff --git a/service/processor/src/test/java/com/google/auto/service/processor/AutoServiceProcessorTest.java b/service/processor/src/test/java/com/google/auto/service/processor/AutoServiceProcessorTest.java
new file mode 100644
index 00000000..6b5e1cfa
--- /dev/null
+++ b/service/processor/src/test/java/com/google/auto/service/processor/AutoServiceProcessorTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * 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.service.processor;
+
+import static com.google.auto.service.processor.AutoServiceProcessor.MISSING_SERVICES_ERROR;
+import static com.google.testing.compile.JavaSourcesSubject.assertThat;
+
+import com.google.testing.compile.JavaFileObjects;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests the {@link AutoServiceProcessor}.
+ */
+@RunWith(JUnit4.class)
+public class AutoServiceProcessorTest {
+ @Test
+ public void autoService() {
+ assertThat(
+ JavaFileObjects.forResource("test/SomeService.java"),
+ JavaFileObjects.forResource("test/SomeServiceProvider1.java"),
+ JavaFileObjects.forResource("test/SomeServiceProvider2.java"),
+ JavaFileObjects.forResource("test/Enclosing.java"),
+ JavaFileObjects.forResource("test/AnotherService.java"),
+ JavaFileObjects.forResource("test/AnotherServiceProvider.java"))
+ .processedWith(new AutoServiceProcessor())
+ .compilesWithoutError()
+ .and().generatesFiles(
+ JavaFileObjects.forResource("META-INF/services/test.SomeService"),
+ JavaFileObjects.forResource("META-INF/services/test.AnotherService"));
+ }
+
+ @Test
+ public void multiService() {
+ assertThat(
+ JavaFileObjects.forResource("test/SomeService.java"),
+ JavaFileObjects.forResource("test/AnotherService.java"),
+ JavaFileObjects.forResource("test/MultiServiceProvider.java"))
+ .processedWith(new AutoServiceProcessor())
+ .compilesWithoutError()
+ .and().generatesFiles(
+ JavaFileObjects.forResource("META-INF/services/test.SomeServiceMulti"),
+ JavaFileObjects.forResource("META-INF/services/test.AnotherServiceMulti"));
+ }
+
+ @Test
+ public void badMultiService() {
+ assertThat(JavaFileObjects.forResource("test/NoServices.java"))
+ .processedWith(new AutoServiceProcessor())
+ .failsToCompile()
+ .withErrorContaining(MISSING_SERVICES_ERROR);
+ }
+}
diff --git a/service/processor/src/test/resources/META-INF/services/test.AnotherService b/service/processor/src/test/resources/META-INF/services/test.AnotherService
new file mode 100644
index 00000000..7453e4da
--- /dev/null
+++ b/service/processor/src/test/resources/META-INF/services/test.AnotherService
@@ -0,0 +1 @@
+test.AnotherServiceProvider
diff --git a/service/processor/src/test/resources/META-INF/services/test.AnotherServiceMulti b/service/processor/src/test/resources/META-INF/services/test.AnotherServiceMulti
new file mode 100644
index 00000000..f6ef36ac
--- /dev/null
+++ b/service/processor/src/test/resources/META-INF/services/test.AnotherServiceMulti
@@ -0,0 +1 @@
+test.MultiServiceProvider
diff --git a/service/processor/src/test/resources/META-INF/services/test.SomeService b/service/processor/src/test/resources/META-INF/services/test.SomeService
new file mode 100644
index 00000000..1b71d9db
--- /dev/null
+++ b/service/processor/src/test/resources/META-INF/services/test.SomeService
@@ -0,0 +1,3 @@
+test.Enclosing$NestedSomeServiceProvider
+test.SomeServiceProvider1
+test.SomeServiceProvider2
diff --git a/service/processor/src/test/resources/META-INF/services/test.SomeServiceMulti b/service/processor/src/test/resources/META-INF/services/test.SomeServiceMulti
new file mode 100644
index 00000000..f6ef36ac
--- /dev/null
+++ b/service/processor/src/test/resources/META-INF/services/test.SomeServiceMulti
@@ -0,0 +1 @@
+test.MultiServiceProvider
diff --git a/service/processor/src/test/resources/test/AnotherService.java b/service/processor/src/test/resources/test/AnotherService.java
new file mode 100644
index 00000000..2cbf7a6b
--- /dev/null
+++ b/service/processor/src/test/resources/test/AnotherService.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * 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 test;
+
+interface AnotherService { } \ No newline at end of file
diff --git a/service/processor/src/test/resources/test/AnotherServiceProvider.java b/service/processor/src/test/resources/test/AnotherServiceProvider.java
new file mode 100644
index 00000000..b425c0ed
--- /dev/null
+++ b/service/processor/src/test/resources/test/AnotherServiceProvider.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * 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 test;
+
+import com.google.auto.service.AutoService;
+
+@AutoService(AnotherService.class)
+public class AnotherServiceProvider implements AnotherService { } \ No newline at end of file
diff --git a/service/processor/src/test/resources/test/Enclosing.java b/service/processor/src/test/resources/test/Enclosing.java
new file mode 100644
index 00000000..5176a7ee
--- /dev/null
+++ b/service/processor/src/test/resources/test/Enclosing.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * 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 test;
+
+import com.google.auto.service.AutoService;
+
+public class Enclosing {
+ @AutoService(SomeService.class)
+ public static class NestedSomeServiceProvider implements SomeService { }
+} \ No newline at end of file
diff --git a/service/processor/src/test/resources/test/MultiServiceProvider.java b/service/processor/src/test/resources/test/MultiServiceProvider.java
new file mode 100644
index 00000000..804904bf
--- /dev/null
+++ b/service/processor/src/test/resources/test/MultiServiceProvider.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 Google, Inc.
+ *
+ * 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 test;
+
+import com.google.auto.service.AutoService;
+
+@AutoService({SomeService.class, AnotherService.class})
+public class MultiServiceProvider implements SomeService, AnotherService {}
diff --git a/service/processor/src/test/resources/test/NoServices.java b/service/processor/src/test/resources/test/NoServices.java
new file mode 100644
index 00000000..3687eb55
--- /dev/null
+++ b/service/processor/src/test/resources/test/NoServices.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * 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 test;
+
+import com.google.auto.service.AutoService;
+
+@AutoService({})
+public class NoServices {}
diff --git a/service/processor/src/test/resources/test/SomeService.java b/service/processor/src/test/resources/test/SomeService.java
new file mode 100644
index 00000000..5a4a3888
--- /dev/null
+++ b/service/processor/src/test/resources/test/SomeService.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * 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 test;
+
+interface SomeService { } \ No newline at end of file
diff --git a/service/processor/src/test/resources/test/SomeServiceProvider1.java b/service/processor/src/test/resources/test/SomeServiceProvider1.java
new file mode 100644
index 00000000..1a1a5e8f
--- /dev/null
+++ b/service/processor/src/test/resources/test/SomeServiceProvider1.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * 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 test;
+
+import com.google.auto.service.AutoService;
+
+@AutoService(SomeService.class)
+public class SomeServiceProvider1 implements SomeService { } \ No newline at end of file
diff --git a/service/processor/src/test/resources/test/SomeServiceProvider2.java b/service/processor/src/test/resources/test/SomeServiceProvider2.java
new file mode 100644
index 00000000..fb6a1bd3
--- /dev/null
+++ b/service/processor/src/test/resources/test/SomeServiceProvider2.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * 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 test;
+
+import com.google.auto.service.AutoService;
+
+@AutoService(SomeService.class)
+public class SomeServiceProvider2 implements SomeService { } \ No newline at end of file