Skip to content

Commit

Permalink
Allow sources being compiled to be read from annotation processors.
Browse files Browse the repository at this point in the history
Fixes #189.

RELNOTES=Allow sources being compiled to be read from annotation processors.
PiperOrigin-RevId: 364564477
  • Loading branch information
netdpb authored and Compile-Testing Team committed Mar 23, 2021
1 parent 1142aec commit 95e749d
Show file tree
Hide file tree
Showing 10 changed files with 164 additions and 80 deletions.
1 change: 1 addition & 0 deletions src/main/java/com/google/testing/compile/Compiler.java
Expand Up @@ -182,6 +182,7 @@ public final Compilation compile(Iterable<? extends JavaFileObject> files) {
InMemoryJavaFileManager fileManager =
new InMemoryJavaFileManager(
javaCompiler().getStandardFileManager(diagnosticCollector, Locale.getDefault(), UTF_8));
fileManager.addSourceFiles(files);
classPath().ifPresent(path -> setLocation(fileManager, StandardLocation.CLASS_PATH, path));
annotationProcessorPath()
.ifPresent(
Expand Down
Expand Up @@ -15,6 +15,8 @@
*/
package com.google.testing.compile;

import static com.google.common.collect.MoreCollectors.toOptional;

import com.google.common.base.MoreObjects;
import com.google.common.base.Optional;
import com.google.common.cache.CacheBuilder;
Expand All @@ -32,7 +34,8 @@
import java.io.Writer;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.Map.Entry;
import java.util.HashMap;
import java.util.Map;
import javax.tools.FileObject;
import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject.Kind;
Expand All @@ -47,13 +50,17 @@
*/
// TODO(gak): under java 1.7 this could all be done with a PathFileManager
final class InMemoryJavaFileManager extends ForwardingStandardJavaFileManager {
private final LoadingCache<URI, JavaFileObject> inMemoryFileObjects =
CacheBuilder.newBuilder().build(new CacheLoader<URI, JavaFileObject>() {
@Override
public JavaFileObject load(URI key) {
return new InMemoryJavaFileObject(key);
}
});
private final LoadingCache<URI, JavaFileObject> inMemoryOutputs =
CacheBuilder.newBuilder()
.build(
new CacheLoader<URI, JavaFileObject>() {
@Override
public JavaFileObject load(URI key) {
return new InMemoryJavaFileObject(key);
}
});

private final Map<URI, JavaFileObject> inMemoryInputs = new HashMap<>();

InMemoryJavaFileManager(StandardJavaFileManager fileManager) {
super(fileManager);
Expand Down Expand Up @@ -86,40 +93,64 @@ public boolean isSameFile(FileObject a, FileObject b) {
public FileObject getFileForInput(Location location, String packageName,
String relativeName) throws IOException {
if (location.isOutputLocation()) {
return inMemoryFileObjects.getIfPresent(
uriForFileObject(location, packageName, relativeName));
} else {
return super.getFileForInput(location, packageName, relativeName);
return inMemoryOutputs.getIfPresent(uriForFileObject(location, packageName, relativeName));
}
Optional<JavaFileObject> inMemoryInput = findInMemoryInput(packageName, relativeName);
if (inMemoryInput.isPresent()) {
return inMemoryInput.get();
}
return super.getFileForInput(location, packageName, relativeName);
}

@Override
public JavaFileObject getJavaFileForInput(Location location, String className, Kind kind)
throws IOException {
if (location.isOutputLocation()) {
return inMemoryFileObjects.getIfPresent(uriForJavaFileObject(location, className, kind));
} else {
return super.getJavaFileForInput(location, className, kind);
return inMemoryOutputs.getIfPresent(uriForJavaFileObject(location, className, kind));
}
Optional<JavaFileObject> inMemoryInput = findInMemoryInput(className);
if (inMemoryInput.isPresent()) {
return inMemoryInput.get();
}
return super.getJavaFileForInput(location, className, kind);
}

private Optional<JavaFileObject> findInMemoryInput(String className) {
int lastDot = className.lastIndexOf('.');
return findInMemoryInput(
lastDot == -1 ? "" : className.substring(0, lastDot - 1),
className.substring(lastDot + 1) + ".java");
}

private Optional<JavaFileObject> findInMemoryInput(String packageName, String relativeName) {
// Assume each input file's URI ends with the package/relative name. It might have other parts
// to the left.
String suffix =
packageName.isEmpty() ? relativeName : packageName.replace('.', '/') + "/" + relativeName;
return Optional.fromJavaUtil(
inMemoryInputs.entrySet().stream()
.filter(entry -> entry.getKey().getPath().endsWith(suffix))
.map(Map.Entry::getValue)
.collect(toOptional())); // Might have problems if more than one input file matches.
}

@Override
public FileObject getFileForOutput(Location location, String packageName,
String relativeName, FileObject sibling) throws IOException {
URI uri = uriForFileObject(location, packageName, relativeName);
return inMemoryFileObjects.getUnchecked(uri);
return inMemoryOutputs.getUnchecked(uri);
}

@Override
public JavaFileObject getJavaFileForOutput(Location location, String className, final Kind kind,
FileObject sibling) throws IOException {
URI uri = uriForJavaFileObject(location, className, kind);
return inMemoryFileObjects.getUnchecked(uri);
return inMemoryOutputs.getUnchecked(uri);
}

ImmutableList<JavaFileObject> getGeneratedSources() {
ImmutableList.Builder<JavaFileObject> result = ImmutableList.builder();
for (Entry<URI, JavaFileObject> entry : inMemoryFileObjects.asMap().entrySet()) {
for (Map.Entry<URI, JavaFileObject> entry : inMemoryOutputs.asMap().entrySet()) {
if (entry.getKey().getPath().startsWith("/" + StandardLocation.SOURCE_OUTPUT.name())
&& (entry.getValue().getKind() == Kind.SOURCE)) {
result.add(entry.getValue());
Expand All @@ -129,7 +160,14 @@ ImmutableList<JavaFileObject> getGeneratedSources() {
}

ImmutableList<JavaFileObject> getOutputFiles() {
return ImmutableList.copyOf(inMemoryFileObjects.asMap().values());
return ImmutableList.copyOf(inMemoryOutputs.asMap().values());
}

/** Adds files that should be available in the source path. */
void addSourceFiles(Iterable<? extends JavaFileObject> files) {
for (JavaFileObject file : files) {
inMemoryInputs.put(file.toUri(), file);
}
}

private static final class InMemoryJavaFileObject extends SimpleJavaFileObject
Expand Down
Expand Up @@ -77,13 +77,13 @@ public class CompilationSubjectTest {
"}");

private static final JavaFileObject HELLO_WORLD_RESOURCE =
JavaFileObjects.forResource("HelloWorld.java");
JavaFileObjects.forResource("test/HelloWorld.java");

private static final JavaFileObject HELLO_WORLD_BROKEN_RESOURCE =
JavaFileObjects.forResource("HelloWorld-broken.java");
JavaFileObjects.forResource("test/HelloWorld-broken.java");

private static final JavaFileObject HELLO_WORLD_DIFFERENT_RESOURCE =
JavaFileObjects.forResource("HelloWorld-different.java");
JavaFileObjects.forResource("test/HelloWorld-different.java");

@RunWith(JUnit4.class)
public static class StatusTest {
Expand Down
54 changes: 53 additions & 1 deletion src/test/java/com/google/testing/compile/CompilerTest.java
Expand Up @@ -23,17 +23,27 @@
import static org.junit.Assume.assumeTrue;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import javax.tools.FileObject;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
Expand All @@ -52,7 +62,8 @@ public final class CompilerTest {

@Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder();

private static final JavaFileObject HELLO_WORLD = JavaFileObjects.forResource("HelloWorld.java");
private static final JavaFileObject HELLO_WORLD =
JavaFileObjects.forResource("test/HelloWorld.java");

@Test
public void options() {
Expand Down Expand Up @@ -230,6 +241,47 @@ public void annotationProcessorPath_customFiles() throws Exception {
assertThat(compilation).succeeded();
}

@Test // See https://github.com/google/compile-testing/issues/189
public void readInputFile() throws IOException {
AtomicReference<String> content = new AtomicReference<>();
Compilation compilation =
javac()
.withProcessors(
new AbstractProcessor() {
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
Filer filer = processingEnv.getFiler();
try {
FileObject helloWorld =
filer.getResource(
StandardLocation.SOURCE_PATH, "test", "HelloWorld.java");
content.set(helloWorld.getCharContent(true).toString());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

@Override
public boolean process(
Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
return false;
}

@Override
public ImmutableSet<String> getSupportedAnnotationTypes() {
return ImmutableSet.of("*");
}

@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
})
.compile(HELLO_WORLD);
assertThat(compilation).succeeded();
assertThat(content.get()).isEqualTo(HELLO_WORLD.getCharContent(true).toString());
}

/**
* Sets up a jar containing a single file 'tmp.txt', for use in annotation processor path tests.
*/
Expand Down
Expand Up @@ -19,20 +19,18 @@
import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;

import com.google.common.io.Resources;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

/**
* An integration test to ensure that testing works when resources are in jar files.
Expand All @@ -52,7 +50,7 @@ public void createJarFile() throws IOException {
JarOutputStream out = new JarOutputStream(new FileOutputStream(jarFile));
JarEntry helloWorldEntry = new JarEntry("test/HelloWorld.java");
out.putNextEntry(helloWorldEntry);
out.write(Resources.toByteArray(Resources.getResource("HelloWorld.java")));
out.write(Resources.toByteArray(Resources.getResource("test/HelloWorld.java")));
out.close();
}

Expand Down

0 comments on commit 95e749d

Please sign in to comment.