Allow TestCompiler to access and generate resources

Update `TestCompiler` so that it can access and generate resources.
This change will allow the `TestCompiler` to be used with annotation
processor tests that generate resources.

Closes gh-29174

Co-authored-by: Phillip Webb <pwebb@vmware.com>
This commit is contained in:
Scott Frederick 2022-09-19 21:00:53 -07:00 committed by Phillip Webb
parent cc7552ec61
commit c9009453c9
14 changed files with 397 additions and 86 deletions

View File

@ -34,24 +34,44 @@ import javax.tools.SimpleJavaFileObject;
*/
class DynamicClassFileObject extends SimpleJavaFileObject {
private static final byte[] NO_BYTES = new byte[0];
private final String className;
private volatile byte[] bytes;
DynamicClassFileObject(String className) {
this(className, NO_BYTES);
super(createUri(className), Kind.CLASS);
this.className = className;
}
DynamicClassFileObject(String className, byte[] bytes) {
super(URI.create("class:///" + className.replace('.', '/') + ".class"), Kind.CLASS);
super(createUri(className), Kind.CLASS);
this.className = className;
this.bytes = bytes;
}
private static URI createUri(String className) {
return URI.create("class:///" + className.replace('.', '/') + ".class");
}
@Override
public InputStream openInputStream() throws IOException {
if (this.bytes == null) {
throw new IOException("No data written");
}
return new ByteArrayInputStream(this.bytes);
}
@Override
public OutputStream openOutputStream() {
return new JavaClassOutputStream();
}
private void closeOutputStream(byte[] bytes) {
this.bytes = bytes;
}
String getClassName() {
return this.className;
}
@ -60,22 +80,12 @@ class DynamicClassFileObject extends SimpleJavaFileObject {
return this.bytes;
}
@Override
public InputStream openInputStream() throws IOException {
return new ByteArrayInputStream(this.bytes);
}
@Override
public OutputStream openOutputStream() {
return new JavaClassOutputStream();
}
class JavaClassOutputStream extends ByteArrayOutputStream {
@Override
public void close() {
DynamicClassFileObject.this.bytes = toByteArray();
closeOutputStream(toByteArray());
}
}

View File

@ -24,10 +24,10 @@ import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;
import org.springframework.aot.test.generate.file.ClassFile;
import org.springframework.aot.test.generate.file.ClassFiles;
@ -41,27 +41,32 @@ import org.springframework.util.ReflectionUtils;
*
* @author Phillip Webb
* @author Andy Wilkinson
* @author Scott Frederick
* @since 6.0
*/
public class DynamicClassLoader extends ClassLoader {
private final ResourceFiles resourceFiles;
private final ClassFiles classFiles;
private final Map<String, DynamicClassFileObject> compiledClasses;
private final ResourceFiles resourceFiles;
private final Map<String, DynamicClassFileObject> dynamicClassFiles;
private final Map<String, DynamicResourceFileObject> dynamicResourceFiles;
@Nullable
private final Method defineClassMethod;
public DynamicClassLoader(ClassLoader parent, ResourceFiles resourceFiles,
ClassFiles classFiles, Map<String, DynamicClassFileObject> compiledClasses) {
public DynamicClassLoader(ClassLoader parent, ClassFiles classFiles, ResourceFiles resourceFiles,
Map<String, DynamicClassFileObject> dynamicClassFiles,
Map<String, DynamicResourceFileObject> dynamicResourceFiles) {
super(parent);
this.resourceFiles = resourceFiles;
this.classFiles = classFiles;
this.compiledClasses = compiledClasses;
this.resourceFiles = resourceFiles;
this.dynamicClassFiles = dynamicClassFiles;
this.dynamicResourceFiles = dynamicResourceFiles;
Class<? extends ClassLoader> parentClass = parent.getClass();
if (parentClass.getName().equals(CompileWithForkedClassLoaderClassLoader.class.getName())) {
Method setClassResourceLookupMethod = ReflectionUtils.findMethod(parentClass,
@ -72,7 +77,7 @@ public class DynamicClassLoader extends ClassLoader {
this.defineClassMethod = ReflectionUtils.findMethod(parentClass,
"defineDynamicClass", String.class, byte[].class, int.class, int.class);
ReflectionUtils.makeAccessible(this.defineClassMethod);
this.compiledClasses.forEach((name, file) -> defineClass(name, file.getBytes()));
this.dynamicClassFiles.forEach((name, file) -> defineClass(name, file.getBytes()));
}
else {
this.defineClassMethod = null;
@ -83,28 +88,19 @@ public class DynamicClassLoader extends ClassLoader {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] bytes = findClassBytes(name);
if (bytes != null) {
if(bytes != null) {
return defineClass(name, bytes);
}
return super.findClass(name);
}
@Nullable
private byte[] findClassBytes(String name) {
DynamicClassFileObject compiledClass = this.compiledClasses.get(name);
if(compiledClass != null) {
return compiledClass.getBytes();
}
return findClassFileBytes(name);
}
@Nullable
private byte[] findClassFileBytes(String name) {
ClassFile classFile = this.classFiles.get(name);
if (classFile != null) {
return classFile.getContent();
}
return null;
DynamicClassFileObject dynamicClassFile = this.dynamicClassFiles.get(name);
return (dynamicClassFile != null) ? dynamicClassFile.getBytes() : null;
}
private Class<?> defineClass(String name, byte[] bytes) {
@ -128,19 +124,27 @@ public class DynamicClassLoader extends ClassLoader {
@Override
@Nullable
protected URL findResource(String name) {
ResourceFile file = this.resourceFiles.get(name);
if (file != null) {
try {
return new URL(null, "resource:///" + file.getPath(),
new ResourceFileHandler(file));
}
catch (MalformedURLException ex) {
throw new IllegalStateException(ex);
}
ResourceFile resourceFile = this.resourceFiles.get(name);
if (resourceFile != null) {
return createResourceUrl(resourceFile.getPath(), resourceFile::getBytes);
}
DynamicResourceFileObject dynamicResourceFile = this.dynamicResourceFiles.get(name);
if (dynamicResourceFile != null && dynamicResourceFile.getBytes() != null) {
return createResourceUrl(dynamicResourceFile.getName(), dynamicResourceFile::getBytes);
}
return super.findResource(name);
}
private URL createResourceUrl(String name, Supplier<byte[]> bytesSupplier) {
try {
return new URL(null, "resource:///" + name,
new ResourceFileHandler(bytesSupplier));
}
catch (MalformedURLException ex) {
throw new IllegalStateException(ex);
}
}
private static class SingletonEnumeration<E> implements Enumeration<E> {
@ -171,17 +175,17 @@ public class DynamicClassLoader extends ClassLoader {
private static class ResourceFileHandler extends URLStreamHandler {
private final ResourceFile file;
private final Supplier<byte[]> bytesSupplier;
ResourceFileHandler(ResourceFile file) {
this.file = file;
ResourceFileHandler(Supplier<byte[]> bytesSupplier) {
this.bytesSupplier = bytesSupplier;
}
@Override
protected URLConnection openConnection(URL url) {
return new ResourceFileConnection(url, this.file);
return new ResourceFileConnection(url, this.bytesSupplier);
}
}
@ -189,12 +193,12 @@ public class DynamicClassLoader extends ClassLoader {
private static class ResourceFileConnection extends URLConnection {
private final ResourceFile file;
private final Supplier<byte[]> bytesSupplier;
protected ResourceFileConnection(URL url, ResourceFile file) {
protected ResourceFileConnection(URL url, Supplier<byte[]> bytesSupplier) {
super(url);
this.file = file;
this.bytesSupplier = bytesSupplier;
}
@ -204,8 +208,7 @@ public class DynamicClassLoader extends ClassLoader {
@Override
public InputStream getInputStream() {
return new ByteArrayInputStream(
this.file.getContent().getBytes(StandardCharsets.UTF_8));
return new ByteArrayInputStream(this.bytesSupplier.get());
}
}

View File

@ -32,6 +32,8 @@ import javax.tools.JavaFileObject.Kind;
import org.springframework.aot.test.generate.file.ClassFile;
import org.springframework.aot.test.generate.file.ClassFiles;
import org.springframework.aot.test.generate.file.ResourceFile;
import org.springframework.aot.test.generate.file.ResourceFiles;
import org.springframework.util.ClassUtils;
/**
@ -40,6 +42,7 @@ import org.springframework.util.ClassUtils;
*
* @author Phillip Webb
* @author Andy Wilkinson
* @author Scott Frederick
* @since 6.0
*/
class DynamicJavaFileManager extends ForwardingJavaFileManager<JavaFileManager> {
@ -48,28 +51,46 @@ class DynamicJavaFileManager extends ForwardingJavaFileManager<JavaFileManager>
private final ClassFiles classFiles;
private final Map<String, DynamicClassFileObject> compiledClasses = Collections.synchronizedMap(
new LinkedHashMap<>());
private final ResourceFiles resourceFiles;
private final Map<String, DynamicClassFileObject> dynamicClassFiles = Collections.synchronizedMap(new LinkedHashMap<>());
private final Map<String, DynamicResourceFileObject> dynamicResourceFiles= Collections.synchronizedMap(new LinkedHashMap<>());
DynamicJavaFileManager(JavaFileManager fileManager, ClassLoader classLoader, ClassFiles classFiles) {
DynamicJavaFileManager(JavaFileManager fileManager, ClassLoader classLoader,
ClassFiles classFiles, ResourceFiles resourceFiles) {
super(fileManager);
this.classLoader = classLoader;
this.classFiles = classFiles;
this.resourceFiles = resourceFiles;
this.classLoader = classLoader;
}
@Override
public ClassLoader getClassLoader(Location location) {
return this.classLoader;
}
@Override
public FileObject getFileForOutput(Location location, String packageName,
String relativeName, FileObject sibling) {
ResourceFile resourceFile = this.resourceFiles.get(relativeName);
if (resourceFile != null) {
return new DynamicResourceFileObject(relativeName, resourceFile.getContent());
}
return this.dynamicResourceFiles.computeIfAbsent(relativeName, DynamicResourceFileObject::new);
}
@Override
public JavaFileObject getJavaFileForOutput(Location location, String className,
JavaFileObject.Kind kind, FileObject sibling) throws IOException {
if (kind == JavaFileObject.Kind.CLASS) {
return this.compiledClasses.computeIfAbsent(className,
DynamicClassFileObject::new);
ClassFile classFile = this.classFiles.get(className);
if (classFile != null) {
return new DynamicClassFileObject(className, classFile.getContent());
}
return this.dynamicClassFiles.computeIfAbsent(className, DynamicClassFileObject::new);
}
return super.getJavaFileForOutput(location, className, kind, sibling);
}
@ -85,6 +106,12 @@ class DynamicJavaFileManager extends ForwardingJavaFileManager<JavaFileManager>
result.add(new DynamicClassFileObject(candidate.getName(), candidate.getContent()));
}
}
for (DynamicClassFileObject candidate : this.dynamicClassFiles.values()) {
String existingPackageName = ClassUtils.getPackageName(candidate.getClassName());
if (existingPackageName.equals(packageName) || (recurse && existingPackageName.startsWith(packageName + "."))) {
result.add(candidate);
}
}
}
super.list(location, packageName, kinds, recurse).forEach(result::add);
return result;
@ -98,8 +125,12 @@ class DynamicJavaFileManager extends ForwardingJavaFileManager<JavaFileManager>
return super.inferBinaryName(location, file);
}
Map<String, DynamicClassFileObject> getCompiledClasses() {
return this.compiledClasses;
Map<String, DynamicClassFileObject> getDynamicClassFiles() {
return this.dynamicClassFiles;
}
Map<String, DynamicResourceFileObject> getDynamicResourceFiles() {
return Collections.unmodifiableMap(this.dynamicResourceFiles);
}
}

View File

@ -36,7 +36,7 @@ class DynamicJavaFileObject extends SimpleJavaFileObject {
DynamicJavaFileObject(SourceFile sourceFile) {
super(URI.create(sourceFile.getPath()), Kind.SOURCE);
super(URI.create("java:///" + sourceFile.getPath()), Kind.SOURCE);
this.sourceFile = sourceFile;
}

View File

@ -0,0 +1,86 @@
/*
* Copyright 2002-2022 the original author or 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
*
* https://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 org.springframework.aot.test.generate.compile;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
/**
* In-memory {@link JavaFileObject} used to hold generated resource file contents.
*
* @author Phillip Webb
* @author Scott Frederick
* @since 6.0
*/
class DynamicResourceFileObject extends SimpleJavaFileObject {
private volatile byte[] bytes;
DynamicResourceFileObject(String fileName) {
super(createUri(fileName), Kind.OTHER);
}
DynamicResourceFileObject(String fileName, String content) {
super(createUri(fileName), Kind.OTHER);
this.bytes = content.getBytes();
}
private static URI createUri(String fileName) {
return URI.create("resource:///" + fileName);
}
@Override
public InputStream openInputStream() throws IOException {
if (this.bytes == null) {
throw new IOException("No data written");
}
return new ByteArrayInputStream(this.bytes);
}
@Override
public OutputStream openOutputStream() {
return new JavaResourceOutputStream();
}
private void closeOutputStream(byte[] bytes) {
this.bytes = bytes;
}
byte[] getBytes() {
return this.bytes;
}
class JavaResourceOutputStream extends ByteArrayOutputStream {
@Override
public void close() {
closeOutputStream(toByteArray());
}
}
}

View File

@ -291,7 +291,7 @@ public final class TestCompiler {
StandardJavaFileManager standardFileManager = this.compiler.getStandardFileManager(
null, null, null);
DynamicJavaFileManager fileManager = new DynamicJavaFileManager(
standardFileManager, classLoaderToUse, this.classFiles);
standardFileManager, classLoaderToUse, this.classFiles, this.resourceFiles);
if (!this.sourceFiles.isEmpty()) {
Errors errors = new Errors();
CompilationTask task = this.compiler.getTask(null, fileManager, errors, null,
@ -304,7 +304,8 @@ public final class TestCompiler {
throw new CompilationException(errors.toString(), this.sourceFiles, this.resourceFiles);
}
}
return new DynamicClassLoader(classLoaderToUse, this.resourceFiles, this.classFiles, fileManager.getCompiledClasses());
return new DynamicClassLoader(classLoaderToUse, this.classFiles, this.resourceFiles,
fileManager.getDynamicClassFiles(), fileManager.getDynamicResourceFiles());
}
/**
@ -340,11 +341,13 @@ public final class TestCompiler {
if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
this.message.append('\n');
this.message.append(diagnostic.getMessage(Locale.getDefault()));
this.message.append(' ');
this.message.append(diagnostic.getSource().getName());
this.message.append(' ');
this.message.append(diagnostic.getLineNumber()).append(':')
.append(diagnostic.getColumnNumber());
if (diagnostic.getSource() != null) {
this.message.append(' ');
this.message.append(diagnostic.getSource().getName());
this.message.append(' ');
this.message.append(diagnostic.getLineNumber()).append(':')
.append(diagnostic.getColumnNumber());
}
}
}

View File

@ -17,6 +17,7 @@
package org.springframework.aot.test.generate.file;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import org.springframework.util.Assert;
@ -56,6 +57,14 @@ public abstract sealed class DynamicFile permits SourceFile, ResourceFile {
}
}
/**
* Return the contents of the file as a byte array.
* @return the file contents as a byte array
*/
public byte[] getBytes() {
return this.content.getBytes(StandardCharsets.UTF_8);
}
/**
* Return the contents of the file.
* @return the file contents

View File

@ -44,13 +44,25 @@ public final class ResourceFile extends DynamicFile implements AssertProvider<Re
* {@link CharSequence}.
* @param path the relative path of the file or {@code null} to have the
* path deduced
* @param charSequence a file containing the file contents
* @param charSequence a char sequence containing the file contents
* @return a {@link ResourceFile} instance
*/
public static ResourceFile of(String path, CharSequence charSequence) {
return new ResourceFile(path, charSequence.toString());
}
/**
* Factory method to create a new {@link ResourceFile} from the given
* {@code byte[]}.
* @param path the relative path of the file or {@code null} to have the
* path deduced
* @param bytes a byte array containing the file contents
* @return a {@link ResourceFile} instance
*/
public static ResourceFile of(String path, byte[] bytes) {
return new ResourceFile(path, new String(bytes, StandardCharsets.UTF_8));
}
/**
* Factory method to create a new {@link ResourceFile} from the given
* {@link InputStreamSource}.

View File

@ -16,6 +16,8 @@
package org.springframework.aot.test.generate.file;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
@ -42,10 +44,12 @@ import org.springframework.util.StringUtils;
* </pre>
*
* @author Phillip Webb
* @author Scott Frederick
* @since 6.0
*/
public final class SourceFile extends DynamicFile implements AssertProvider<SourceFileAssert> {
private static final File TEST_SOURCE_DIRECTORY = new File("src/test/java");
private final String className;
@ -55,6 +59,28 @@ public final class SourceFile extends DynamicFile implements AssertProvider<Sour
this.className = className;
}
/**
* Factory method to create a new {@link SourceFile} by looking up source
* for the given test {@code Class}.
* @param type the class file to get the source from
* @return a {@link SourceFile} instance
*/
public static SourceFile forTestClass(Class<?> type) {
return forClass(TEST_SOURCE_DIRECTORY, type);
}
/**
* Factory method to create a new {@link SourceFile} by looking up source
* for the given {@code Class}.
* @param sourceDirectory the source directory
* @param type the class file to get the source from
* @return a {@link SourceFile} instance
*/
public static SourceFile forClass(File sourceDirectory, Class<?> type) {
String sourceFileName = type.getName().replace('.', '/');
File sourceFile = new File(sourceDirectory, sourceFileName + ".java");
return SourceFile.of(() -> new FileInputStream(sourceFile));
}
/**
* Factory method to create a new {@link SourceFile} from the given

View File

@ -34,22 +34,19 @@ class DynamicClassFileObjectTests {
@Test
void getUriReturnsGeneratedUriBasedOnClassName() {
DynamicClassFileObject fileObject = new DynamicClassFileObject(
"com.example.MyClass");
DynamicClassFileObject fileObject = new DynamicClassFileObject("com.example.MyClass");
assertThat(fileObject.toUri()).hasToString("class:///com/example/MyClass.class");
}
@Test
void getKindReturnsClass() {
DynamicClassFileObject fileObject = new DynamicClassFileObject(
"com.example.MyClass");
DynamicClassFileObject fileObject = new DynamicClassFileObject("com.example.MyClass");
assertThat(fileObject.getKind()).isEqualTo(Kind.CLASS);
}
@Test
void openOutputStreamWritesToBytes() throws Exception {
DynamicClassFileObject fileObject = new DynamicClassFileObject(
"com.example.MyClass");
DynamicClassFileObject fileObject = new DynamicClassFileObject("com.example.MyClass");
try(OutputStream outputStream = fileObject.openOutputStream()) {
new ByteArrayInputStream("test".getBytes()).transferTo(outputStream);
}

View File

@ -17,8 +17,10 @@
package org.springframework.aot.test.generate.compile;
import java.io.IOException;
import java.io.OutputStream;
import java.util.EnumSet;
import javax.tools.FileObject;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileManager.Location;
import javax.tools.JavaFileObject;
@ -31,6 +33,9 @@ import org.mockito.MockitoAnnotations;
import org.springframework.aot.test.generate.file.ClassFile;
import org.springframework.aot.test.generate.file.ClassFiles;
import org.springframework.aot.test.generate.file.ResourceFile;
import org.springframework.aot.test.generate.file.ResourceFiles;
import org.springframework.util.StreamUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.then;
@ -39,11 +44,14 @@ import static org.mockito.BDDMockito.then;
* Tests for {@link DynamicJavaFileManager}.
*
* @author Phillip Webb
* @author Scott Frederick
*/
class DynamicJavaFileManagerTests {
private static final byte[] DUMMY_BYTECODE = new byte[] { 'a' };
private static final String DUMMY_RESOURCE = "a";
@Mock
private JavaFileManager parentFileManager;
@ -58,9 +66,14 @@ class DynamicJavaFileManagerTests {
void setup() {
MockitoAnnotations.openMocks(this);
this.classLoader = new ClassLoader() {};
ClassFiles classFiles = ClassFiles.of(
ClassFile.of("com.example.one.ClassOne", DUMMY_BYTECODE),
ClassFile.of("com.example.two.ClassTwo", DUMMY_BYTECODE));
ResourceFiles resourceFiles = ResourceFiles.of(
ResourceFile.of("com/example/one/resource.one", DUMMY_RESOURCE),
ResourceFile.of("com/example/two/resource.two", DUMMY_RESOURCE));
this.fileManager = new DynamicJavaFileManager(this.parentFileManager, this.classLoader,
ClassFiles.of(ClassFile.of("com.example.one.ClassOne", DUMMY_BYTECODE),
ClassFile.of("com.example.two.ClassTwo", DUMMY_BYTECODE)));
classFiles, resourceFiles);
}
@Test
@ -81,6 +94,7 @@ class DynamicJavaFileManagerTests {
throws Exception {
JavaFileObject fileObject1 = this.fileManager.getJavaFileForOutput(this.location,
"com.example.MyClass", Kind.CLASS, null);
writeDummyResource(fileObject1);
JavaFileObject fileObject2 = this.fileManager.getJavaFileForOutput(this.location,
"com.example.MyClass", Kind.CLASS, null);
assertThat(fileObject1).isSameAs(fileObject2);
@ -97,11 +111,11 @@ class DynamicJavaFileManagerTests {
@Test
void getClassFilesReturnsClassFiles() throws Exception {
this.fileManager.getJavaFileForOutput(this.location, "com.example.MyClass1",
Kind.CLASS, null);
this.fileManager.getJavaFileForOutput(this.location, "com.example.MyClass2",
Kind.CLASS, null);
assertThat(this.fileManager.getCompiledClasses()).containsKeys(
writeDummyBytecode(this.fileManager.getJavaFileForOutput(this.location, "com.example.MyClass1",
Kind.CLASS, null));
writeDummyBytecode(this.fileManager.getJavaFileForOutput(this.location, "com.example.MyClass2",
Kind.CLASS, null));
assertThat(this.fileManager.getDynamicClassFiles()).containsKeys(
"com.example.MyClass1", "com.example.MyClass2");
}
@ -129,4 +143,31 @@ class DynamicJavaFileManagerTests {
assertThat(listed).hasSize(0);
}
@Test
void getFileForOutputReturnsDynamicResourceFile() {
FileObject fileObject = this.fileManager.getFileForOutput(this.location,
"", "META-INF/generated.properties", null);
assertThat(fileObject).isInstanceOf(DynamicResourceFileObject.class);
}
@Test
void getFileForOutputReturnsFile() throws Exception {
writeDummyResource(this.fileManager.getFileForOutput(this.location, "", "META-INF/first.properties", null));
writeDummyResource(this.fileManager.getFileForOutput(this.location, "", "META-INF/second.properties", null));
assertThat(this.fileManager.getDynamicResourceFiles()).containsKeys("META-INF/first.properties",
"META-INF/second.properties");
}
private void writeDummyBytecode(JavaFileObject fileObject) throws IOException {
try (OutputStream outputStream = fileObject.openOutputStream()) {
StreamUtils.copy(DUMMY_BYTECODE, outputStream);
}
}
private void writeDummyResource(FileObject fileObject) throws IOException {
try (OutputStream outputStream = fileObject.openOutputStream()) {
StreamUtils.copy(DUMMY_RESOURCE.getBytes(), outputStream);
}
}
}

View File

@ -34,7 +34,7 @@ class DynamicJavaFileObjectTests {
@Test
void getUriReturnsPath() {
DynamicJavaFileObject fileObject = new DynamicJavaFileObject(SourceFile.of(CONTENT));
assertThat(fileObject.toUri()).hasToString("com/example/Hello.java");
assertThat(fileObject.toUri()).hasToString("java:///com/example/Hello.java");
}
@Test

View File

@ -0,0 +1,77 @@
/*
* Copyright 2002-2022 the original author or 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
*
* https://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 org.springframework.aot.test.generate.compile;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import javax.tools.JavaFileObject.Kind;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIOException;
/**
* Tests for {@link DynamicResourceFileObject}.
*
* @author Phillip Webb
* @author Scott Frederick
* @since 6.0
*/
class DynamicResourceFileObjectTests {
@Test
void getUriReturnsFileUri() {
DynamicResourceFileObject fileObject = new DynamicResourceFileObject("META-INF/test.properties");
assertThat(fileObject.toUri()).hasToString("resource:///META-INF/test.properties");
}
@Test
void getKindReturnsOther() {
DynamicResourceFileObject fileObject = new DynamicResourceFileObject("META-INF/test.properties");
assertThat(fileObject.getKind()).isEqualTo(Kind.OTHER);
}
@Test
void openOutputStreamWritesToBytes() throws Exception {
DynamicResourceFileObject fileObject = new DynamicResourceFileObject("META-INF/test.properties");
try(OutputStream outputStream = fileObject.openOutputStream()) {
new ByteArrayInputStream("test".getBytes()).transferTo(outputStream);
}
assertThat(fileObject.getBytes()).isEqualTo("test".getBytes());
}
@Test
void openInputStreamReadsFromBytes() throws Exception {
DynamicResourceFileObject fileObject = new DynamicResourceFileObject("META-INF/test.properties");
try(OutputStream outputStream = fileObject.openOutputStream()) {
new ByteArrayInputStream("test".getBytes()).transferTo(outputStream);
}
try(InputStream inputStream = fileObject.openInputStream()) {
assertThat(inputStream.readAllBytes()).isEqualTo("test".getBytes());
}
}
@Test
void openInputStreamWhenNothingWrittenThrowsException() {
DynamicResourceFileObject fileObject = new DynamicResourceFileObject("META-INF/test.properties");
assertThatIOException().isThrownBy(fileObject::openInputStream);
}
}

View File

@ -16,6 +16,7 @@
package org.springframework.aot.test.generate.file;
import java.io.File;
import java.io.IOException;
import org.junit.jupiter.api.Test;
@ -27,6 +28,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
* Tests for {@link SourceFile}.
*
* @author Phillip Webb
* @author Scott Frederick
*/
class SourceFileTests {
@ -83,6 +85,20 @@ class SourceFileTests {
assertThat(sourceFile.getPath()).isEqualTo("com/example/DifferentPath.java");
}
@Test
void forClassWithClassUsesClass() {
SourceFile sourceFile = SourceFile.forClass(new File("src/test/java"), SourceFileTests.class);
assertThat(sourceFile.getPath()).isEqualTo("org/springframework/aot/test/generate/file/SourceFileTests.java");
assertThat(sourceFile.getClassName()).isEqualTo("org.springframework.aot.test.generate.file.SourceFileTests");
}
@Test
void forTestClassWithClassUsesClass() {
SourceFile sourceFile = SourceFile.forTestClass(SourceFileTests.class);
assertThat(sourceFile.getPath()).isEqualTo("org/springframework/aot/test/generate/file/SourceFileTests.java");
assertThat(sourceFile.getClassName()).isEqualTo("org.springframework.aot.test.generate.file.SourceFileTests");
}
@Test
void getContentReturnsContent() {
SourceFile sourceFile = SourceFile.of(HELLO_WORLD);