diff --git a/spring-core-test/src/main/java/org/springframework/aot/test/generate/compile/DynamicClassFileObject.java b/spring-core-test/src/main/java/org/springframework/aot/test/generate/compile/DynamicClassFileObject.java index e598a737778..92bfb365338 100644 --- a/spring-core-test/src/main/java/org/springframework/aot/test/generate/compile/DynamicClassFileObject.java +++ b/spring-core-test/src/main/java/org/springframework/aot/test/generate/compile/DynamicClassFileObject.java @@ -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()); } } diff --git a/spring-core-test/src/main/java/org/springframework/aot/test/generate/compile/DynamicClassLoader.java b/spring-core-test/src/main/java/org/springframework/aot/test/generate/compile/DynamicClassLoader.java index 844c60fd3ee..ff6bc1a8c65 100644 --- a/spring-core-test/src/main/java/org/springframework/aot/test/generate/compile/DynamicClassLoader.java +++ b/spring-core-test/src/main/java/org/springframework/aot/test/generate/compile/DynamicClassLoader.java @@ -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 compiledClasses; + private final ResourceFiles resourceFiles; + + private final Map dynamicClassFiles; + + private final Map dynamicResourceFiles; @Nullable private final Method defineClassMethod; - public DynamicClassLoader(ClassLoader parent, ResourceFiles resourceFiles, - ClassFiles classFiles, Map compiledClasses) { + public DynamicClassLoader(ClassLoader parent, ClassFiles classFiles, ResourceFiles resourceFiles, + Map dynamicClassFiles, + Map dynamicResourceFiles) { super(parent); - this.resourceFiles = resourceFiles; this.classFiles = classFiles; - this.compiledClasses = compiledClasses; + this.resourceFiles = resourceFiles; + this.dynamicClassFiles = dynamicClassFiles; + this.dynamicResourceFiles = dynamicResourceFiles; Class 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 bytesSupplier) { + try { + return new URL(null, "resource:///" + name, + new ResourceFileHandler(bytesSupplier)); + } + catch (MalformedURLException ex) { + throw new IllegalStateException(ex); + } + } + private static class SingletonEnumeration implements Enumeration { @@ -171,17 +175,17 @@ public class DynamicClassLoader extends ClassLoader { private static class ResourceFileHandler extends URLStreamHandler { - private final ResourceFile file; + private final Supplier bytesSupplier; - ResourceFileHandler(ResourceFile file) { - this.file = file; + ResourceFileHandler(Supplier 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 bytesSupplier; - protected ResourceFileConnection(URL url, ResourceFile file) { + protected ResourceFileConnection(URL url, Supplier 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()); } } diff --git a/spring-core-test/src/main/java/org/springframework/aot/test/generate/compile/DynamicJavaFileManager.java b/spring-core-test/src/main/java/org/springframework/aot/test/generate/compile/DynamicJavaFileManager.java index 0df5291af21..228ec53adc9 100644 --- a/spring-core-test/src/main/java/org/springframework/aot/test/generate/compile/DynamicJavaFileManager.java +++ b/spring-core-test/src/main/java/org/springframework/aot/test/generate/compile/DynamicJavaFileManager.java @@ -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 { @@ -48,28 +51,46 @@ class DynamicJavaFileManager extends ForwardingJavaFileManager private final ClassFiles classFiles; - private final Map compiledClasses = Collections.synchronizedMap( - new LinkedHashMap<>()); + private final ResourceFiles resourceFiles; + + private final Map dynamicClassFiles = Collections.synchronizedMap(new LinkedHashMap<>()); + + private final Map 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 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 return super.inferBinaryName(location, file); } - Map getCompiledClasses() { - return this.compiledClasses; + Map getDynamicClassFiles() { + return this.dynamicClassFiles; + } + + Map getDynamicResourceFiles() { + return Collections.unmodifiableMap(this.dynamicResourceFiles); } } diff --git a/spring-core-test/src/main/java/org/springframework/aot/test/generate/compile/DynamicJavaFileObject.java b/spring-core-test/src/main/java/org/springframework/aot/test/generate/compile/DynamicJavaFileObject.java index 8bcb0acee3f..86f1b970844 100644 --- a/spring-core-test/src/main/java/org/springframework/aot/test/generate/compile/DynamicJavaFileObject.java +++ b/spring-core-test/src/main/java/org/springframework/aot/test/generate/compile/DynamicJavaFileObject.java @@ -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; } diff --git a/spring-core-test/src/main/java/org/springframework/aot/test/generate/compile/DynamicResourceFileObject.java b/spring-core-test/src/main/java/org/springframework/aot/test/generate/compile/DynamicResourceFileObject.java new file mode 100644 index 00000000000..e8de4da4a2b --- /dev/null +++ b/spring-core-test/src/main/java/org/springframework/aot/test/generate/compile/DynamicResourceFileObject.java @@ -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()); + } + + } + +} diff --git a/spring-core-test/src/main/java/org/springframework/aot/test/generate/compile/TestCompiler.java b/spring-core-test/src/main/java/org/springframework/aot/test/generate/compile/TestCompiler.java index c1fd47caaec..44720df328b 100644 --- a/spring-core-test/src/main/java/org/springframework/aot/test/generate/compile/TestCompiler.java +++ b/spring-core-test/src/main/java/org/springframework/aot/test/generate/compile/TestCompiler.java @@ -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()); + } } } diff --git a/spring-core-test/src/main/java/org/springframework/aot/test/generate/file/DynamicFile.java b/spring-core-test/src/main/java/org/springframework/aot/test/generate/file/DynamicFile.java index d770697961b..14681acadd6 100644 --- a/spring-core-test/src/main/java/org/springframework/aot/test/generate/file/DynamicFile.java +++ b/spring-core-test/src/main/java/org/springframework/aot/test/generate/file/DynamicFile.java @@ -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 diff --git a/spring-core-test/src/main/java/org/springframework/aot/test/generate/file/ResourceFile.java b/spring-core-test/src/main/java/org/springframework/aot/test/generate/file/ResourceFile.java index 9c8c21f986b..4d4b50aad89 100644 --- a/spring-core-test/src/main/java/org/springframework/aot/test/generate/file/ResourceFile.java +++ b/spring-core-test/src/main/java/org/springframework/aot/test/generate/file/ResourceFile.java @@ -44,13 +44,25 @@ public final class ResourceFile extends DynamicFile implements AssertProvider * * @author Phillip Webb + * @author Scott Frederick * @since 6.0 */ public final class SourceFile extends DynamicFile implements AssertProvider { + 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 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 diff --git a/spring-core-test/src/test/java/org/springframework/aot/test/generate/compile/DynamicClassFileObjectTests.java b/spring-core-test/src/test/java/org/springframework/aot/test/generate/compile/DynamicClassFileObjectTests.java index e91f7c4c0d9..7c9bb6edb20 100644 --- a/spring-core-test/src/test/java/org/springframework/aot/test/generate/compile/DynamicClassFileObjectTests.java +++ b/spring-core-test/src/test/java/org/springframework/aot/test/generate/compile/DynamicClassFileObjectTests.java @@ -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); } diff --git a/spring-core-test/src/test/java/org/springframework/aot/test/generate/compile/DynamicJavaFileManagerTests.java b/spring-core-test/src/test/java/org/springframework/aot/test/generate/compile/DynamicJavaFileManagerTests.java index f3af3fd7f62..b0d1a013576 100644 --- a/spring-core-test/src/test/java/org/springframework/aot/test/generate/compile/DynamicJavaFileManagerTests.java +++ b/spring-core-test/src/test/java/org/springframework/aot/test/generate/compile/DynamicJavaFileManagerTests.java @@ -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); + } + } + } diff --git a/spring-core-test/src/test/java/org/springframework/aot/test/generate/compile/DynamicJavaFileObjectTests.java b/spring-core-test/src/test/java/org/springframework/aot/test/generate/compile/DynamicJavaFileObjectTests.java index 9c16a56fe0e..f918f4695da 100644 --- a/spring-core-test/src/test/java/org/springframework/aot/test/generate/compile/DynamicJavaFileObjectTests.java +++ b/spring-core-test/src/test/java/org/springframework/aot/test/generate/compile/DynamicJavaFileObjectTests.java @@ -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 diff --git a/spring-core-test/src/test/java/org/springframework/aot/test/generate/compile/DynamicResourceFileObjectTests.java b/spring-core-test/src/test/java/org/springframework/aot/test/generate/compile/DynamicResourceFileObjectTests.java new file mode 100644 index 00000000000..6ab6376b640 --- /dev/null +++ b/spring-core-test/src/test/java/org/springframework/aot/test/generate/compile/DynamicResourceFileObjectTests.java @@ -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); + } + +} diff --git a/spring-core-test/src/test/java/org/springframework/aot/test/generate/file/SourceFileTests.java b/spring-core-test/src/test/java/org/springframework/aot/test/generate/file/SourceFileTests.java index 6976322ed7e..d2d6d2b0121 100644 --- a/spring-core-test/src/test/java/org/springframework/aot/test/generate/file/SourceFileTests.java +++ b/spring-core-test/src/test/java/org/springframework/aot/test/generate/file/SourceFileTests.java @@ -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);