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:
parent
cc7552ec61
commit
c9009453c9
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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}.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue