Add module to support testing of generated code
Add a new unpublished `spring-core-test` module to support testing of generated code. The module include a `TestCompiler` class which can be used to dynamically compile generated Java code. It also include an AssertJ friendly `SourceFile` class which uses qdox to provide targeted assertions on specific parts of a generated source file. See gh-28120
This commit is contained in:
parent
b5695b9248
commit
653dc5951d
|
|
@ -74,6 +74,7 @@ configure(allprojects) { project ->
|
|||
dependency "com.google.code.gson:gson:2.8.9"
|
||||
dependency "com.google.protobuf:protobuf-java-util:3.19.3"
|
||||
dependency "com.googlecode.protobuf-java-format:protobuf-java-format:1.4"
|
||||
dependency "com.thoughtworks.qdox:qdox:2.0.1"
|
||||
dependency("com.thoughtworks.xstream:xstream:1.4.18") {
|
||||
exclude group: "xpp3", name: "xpp3_min"
|
||||
exclude group: "xmlpull", name: "xmlpull"
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ group = "org.springframework"
|
|||
|
||||
dependencies {
|
||||
constraints {
|
||||
parent.moduleProjects.sort { "$it.name" }.each {
|
||||
parent.moduleProjects.findAll{ it.name != 'spring-core-test' }.sort{ "$it.name" }.each {
|
||||
api it
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ include "spring-context"
|
|||
include "spring-context-indexer"
|
||||
include "spring-context-support"
|
||||
include "spring-core"
|
||||
include "spring-core-test"
|
||||
include "spring-expression"
|
||||
include "spring-instrument"
|
||||
include "spring-jcl"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
|
||||
description = "Spring Core Test"
|
||||
|
||||
dependencies {
|
||||
api(project(":spring-core"))
|
||||
api("org.assertj:assertj-core")
|
||||
api("com.thoughtworks.qdox:qdox")
|
||||
}
|
||||
|
||||
tasks.withType(PublishToMavenRepository).configureEach {
|
||||
it.enabled = false
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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.generator.compile;
|
||||
|
||||
/**
|
||||
* Exception thrown when code cannot compile.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class CompilationException extends RuntimeException {
|
||||
|
||||
CompilationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* 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.generator.compile;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.aot.test.generator.file.ResourceFile;
|
||||
import org.springframework.aot.test.generator.file.ResourceFiles;
|
||||
import org.springframework.aot.test.generator.file.SourceFile;
|
||||
import org.springframework.aot.test.generator.file.SourceFiles;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Fully compiled results provided from a {@link TestCompiler}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
*/
|
||||
public class Compiled {
|
||||
|
||||
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
private final SourceFiles sourceFiles;
|
||||
|
||||
private final ResourceFiles resourceFiles;
|
||||
|
||||
private List<Class<?>> compiledClasses;
|
||||
|
||||
|
||||
Compiled(ClassLoader classLoader, SourceFiles sourceFiles,
|
||||
ResourceFiles resourceFiles) {
|
||||
this.classLoader = classLoader;
|
||||
this.sourceFiles = sourceFiles;
|
||||
this.resourceFiles = resourceFiles;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the classloader containing the compiled content and access to the
|
||||
* resources.
|
||||
* @return the classLoader
|
||||
*/
|
||||
public ClassLoader getClassLoader() {
|
||||
return this.classLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the single source file that was compiled.
|
||||
* @return the single source file
|
||||
* @throws IllegalStateException if the compiler wasn't passed exactly one
|
||||
* file
|
||||
*/
|
||||
public SourceFile getSourceFile() {
|
||||
return this.sourceFiles.getSingle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all source files that were compiled.
|
||||
* @return the source files used by the compiler
|
||||
*/
|
||||
public SourceFiles getSourceFiles() {
|
||||
return this.sourceFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the single resource file that was used when compiled.
|
||||
* @return the single resource file
|
||||
* @throws IllegalStateException if the compiler wasn't passed exactly one
|
||||
* file
|
||||
*/
|
||||
public ResourceFile getResourceFile() {
|
||||
return this.resourceFiles.getSingle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all resource files that were compiled.
|
||||
* @return the resource files used by the compiler
|
||||
*/
|
||||
public ResourceFiles getResourceFiles() {
|
||||
return this.resourceFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new instance of a compiled class of the given type. There must
|
||||
* be only a single instance and it must have a default constructor.
|
||||
* @param <T> the required type
|
||||
* @param type the required type
|
||||
* @return an instance of type created from the compiled classes
|
||||
* @throws IllegalStateException if no instance can be found or instantiated
|
||||
*/
|
||||
public <T> T getInstance(Class<T> type) {
|
||||
List<Class<?>> matching = getAllCompiledClasses().stream().filter(
|
||||
candidate -> type.isAssignableFrom(candidate)).toList();
|
||||
Assert.state(!matching.isEmpty(), () -> "No instance found of type " + type.getName());
|
||||
Assert.state(matching.size() == 1, () -> "Multiple instances found of type " + type.getName());
|
||||
return newInstance(matching.get(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an instance of a compiled class identified by its class name. The
|
||||
* class must have a default constructor.
|
||||
* @param <T> the type to return
|
||||
* @param type the type to return
|
||||
* @param className the class name to load
|
||||
* @return an instance of the class
|
||||
* @throws IllegalStateException if no instance can be found or instantiated
|
||||
*/
|
||||
public <T> T getInstance(Class<T> type, String className) {
|
||||
Class<?> loaded = loadClass(className);
|
||||
return newInstance(loaded);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all compiled classes.
|
||||
* @return a list of all compiled classes
|
||||
*/
|
||||
public List<Class<?>> getAllCompiledClasses() {
|
||||
List<Class<?>> compiledClasses = this.compiledClasses;
|
||||
if (compiledClasses == null) {
|
||||
compiledClasses = new ArrayList<>();
|
||||
this.sourceFiles.stream().map(this::loadClass).forEach(compiledClasses::add);
|
||||
this.compiledClasses = Collections.unmodifiableList(compiledClasses);
|
||||
}
|
||||
return compiledClasses;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T newInstance(Class<?> loaded) {
|
||||
try {
|
||||
Constructor<?> constructor = loaded.getDeclaredConstructor();
|
||||
return (T) constructor.newInstance();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?> loadClass(SourceFile sourceFile) {
|
||||
return loadClass(sourceFile.getClassName());
|
||||
}
|
||||
|
||||
private Class<?> loadClass(String className) {
|
||||
try {
|
||||
return this.classLoader.loadClass(className);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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.generator.compile;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.SimpleJavaFileObject;
|
||||
|
||||
/**
|
||||
* In-memory {@link JavaFileObject} used to hold class bytecode.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
*/
|
||||
class DynamicClassFileObject extends SimpleJavaFileObject {
|
||||
|
||||
private volatile byte[] bytes;
|
||||
|
||||
|
||||
DynamicClassFileObject(String className) {
|
||||
super(URI.create("class:///" + className.replace('.', '/') + ".class"),
|
||||
Kind.CLASS);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public OutputStream openOutputStream() throws IOException {
|
||||
return new JavaClassOutputStream();
|
||||
}
|
||||
|
||||
byte[] getBytes() {
|
||||
return this.bytes;
|
||||
}
|
||||
|
||||
|
||||
class JavaClassOutputStream extends ByteArrayOutputStream {
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
DynamicClassFileObject.this.bytes = toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* 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.generator.compile;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.System.Logger;
|
||||
import java.lang.System.Logger.Level;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
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 org.springframework.aot.test.generator.file.ResourceFile;
|
||||
import org.springframework.aot.test.generator.file.ResourceFiles;
|
||||
import org.springframework.aot.test.generator.file.SourceFile;
|
||||
import org.springframework.aot.test.generator.file.SourceFiles;
|
||||
|
||||
/**
|
||||
* {@link ClassLoader} used to expose dynamically generated content.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
*/
|
||||
public class DynamicClassLoader extends ClassLoader {
|
||||
|
||||
private static final Logger logger = System.getLogger(
|
||||
DynamicClassLoader.class.getName());
|
||||
|
||||
|
||||
private final SourceFiles sourceFiles;
|
||||
|
||||
private final ResourceFiles resourceFiles;
|
||||
|
||||
private final Map<String, DynamicClassFileObject> classFiles;
|
||||
|
||||
|
||||
public DynamicClassLoader(ClassLoader parent, SourceFiles sourceFiles,
|
||||
ResourceFiles resourceFiles, Map<String, DynamicClassFileObject> classFiles) {
|
||||
super(parent);
|
||||
this.sourceFiles = sourceFiles;
|
||||
this.resourceFiles = resourceFiles;
|
||||
this.classFiles = classFiles;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
DynamicClassFileObject classFile = this.classFiles.get(name);
|
||||
if (classFile != null) {
|
||||
return defineClass(name, classFile);
|
||||
}
|
||||
return super.findClass(name);
|
||||
}
|
||||
|
||||
private Class<?> defineClass(String name, DynamicClassFileObject classFile) {
|
||||
byte[] bytes = classFile.getBytes();
|
||||
SourceFile sourceFile = this.sourceFiles.get(name);
|
||||
if (sourceFile != null && sourceFile.getTarget() != null) {
|
||||
try {
|
||||
Lookup lookup = MethodHandles.privateLookupIn(sourceFile.getTarget(),
|
||||
MethodHandles.lookup());
|
||||
return lookup.defineClass(bytes);
|
||||
}
|
||||
catch (IllegalAccessException ex) {
|
||||
logger.log(Level.WARNING,
|
||||
"Unable to define class using MethodHandles Lookup, "
|
||||
+ "only public methods and classes will be accessible");
|
||||
}
|
||||
}
|
||||
return defineClass(name, bytes, 0, bytes.length, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Enumeration<URL> findResources(String name) throws IOException {
|
||||
URL resource = findResource(name);
|
||||
if (resource != null) {
|
||||
return new SingletonEnumeration<>(resource);
|
||||
}
|
||||
return super.findResources(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
}
|
||||
return super.findResource(name);
|
||||
}
|
||||
|
||||
|
||||
private static class SingletonEnumeration<E> implements Enumeration<E> {
|
||||
|
||||
private E element;
|
||||
|
||||
|
||||
SingletonEnumeration(E element) {
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean hasMoreElements() {
|
||||
return this.element != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E nextElement() {
|
||||
E next = this.element;
|
||||
this.element = null;
|
||||
return next;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static class ResourceFileHandler extends URLStreamHandler {
|
||||
|
||||
private final ResourceFile file;
|
||||
|
||||
|
||||
ResourceFileHandler(ResourceFile file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected URLConnection openConnection(URL url) throws IOException {
|
||||
return new ResourceFileConnection(url, this.file);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static class ResourceFileConnection extends URLConnection {
|
||||
|
||||
private final ResourceFile file;
|
||||
|
||||
|
||||
protected ResourceFileConnection(URL url, ResourceFile file) {
|
||||
super(url);
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void connect() throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return new ByteArrayInputStream(
|
||||
this.file.getContent().getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* 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.generator.compile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.tools.FileObject;
|
||||
import javax.tools.ForwardingJavaFileManager;
|
||||
import javax.tools.JavaFileManager;
|
||||
import javax.tools.JavaFileObject;
|
||||
|
||||
/**
|
||||
* {@link JavaFileManager} to create in-memory {@link DynamicClassFileObject
|
||||
* ClassFileObjects} when compiling.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
*/
|
||||
class DynamicJavaFileManager extends ForwardingJavaFileManager<JavaFileManager> {
|
||||
|
||||
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
private final Map<String, DynamicClassFileObject> classFiles = Collections.synchronizedMap(
|
||||
new LinkedHashMap<>());
|
||||
|
||||
|
||||
|
||||
DynamicJavaFileManager(JavaFileManager fileManager, ClassLoader classLoader) {
|
||||
super(fileManager);
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ClassLoader getClassLoader(Location location) {
|
||||
return this.classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaFileObject getJavaFileForOutput(Location location, String className,
|
||||
JavaFileObject.Kind kind, FileObject sibling) throws IOException {
|
||||
if (kind == JavaFileObject.Kind.CLASS) {
|
||||
return this.classFiles.computeIfAbsent(className,
|
||||
DynamicClassFileObject::new);
|
||||
}
|
||||
return super.getJavaFileForOutput(location, className, kind, sibling);
|
||||
}
|
||||
|
||||
Map<String, DynamicClassFileObject> getClassFiles() {
|
||||
return Collections.unmodifiableMap(this.classFiles);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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.generator.compile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.SimpleJavaFileObject;
|
||||
|
||||
import org.springframework.aot.test.generator.file.SourceFile;
|
||||
|
||||
/**
|
||||
* Adapts a {@link SourceFile} instance to a {@link JavaFileObject}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
*/
|
||||
class DynamicJavaFileObject extends SimpleJavaFileObject {
|
||||
|
||||
|
||||
private final SourceFile sourceFile;
|
||||
|
||||
|
||||
DynamicJavaFileObject(SourceFile sourceFile) {
|
||||
super(URI.create(sourceFile.getPath()), Kind.SOURCE);
|
||||
this.sourceFile = sourceFile;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
|
||||
return this.sourceFile.getContent();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,242 @@
|
|||
/*
|
||||
* 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.generator.compile;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.tools.Diagnostic;
|
||||
import javax.tools.DiagnosticListener;
|
||||
import javax.tools.JavaCompiler;
|
||||
import javax.tools.JavaCompiler.CompilationTask;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.StandardJavaFileManager;
|
||||
import javax.tools.ToolProvider;
|
||||
|
||||
import org.springframework.aot.test.generator.file.ResourceFile;
|
||||
import org.springframework.aot.test.generator.file.ResourceFiles;
|
||||
import org.springframework.aot.test.generator.file.SourceFile;
|
||||
import org.springframework.aot.test.generator.file.SourceFiles;
|
||||
import org.springframework.aot.test.generator.file.WritableContent;
|
||||
|
||||
/**
|
||||
* Utility that can be used to dynamically compile and test Java source code.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
* @see #forSystem()
|
||||
*/
|
||||
public final class TestCompiler {
|
||||
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
private final JavaCompiler compiler;
|
||||
|
||||
private final SourceFiles sourceFiles;
|
||||
|
||||
private final ResourceFiles resourceFiles;
|
||||
|
||||
|
||||
private TestCompiler(ClassLoader classLoader, JavaCompiler compiler,
|
||||
SourceFiles sourceFiles, ResourceFiles resourceFiles) {
|
||||
this.classLoader = classLoader;
|
||||
this.compiler = compiler;
|
||||
this.sourceFiles = sourceFiles;
|
||||
this.resourceFiles = resourceFiles;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a new {@link TestCompiler} backed by the system java compiler.
|
||||
* @return a new {@link TestCompiler} instance
|
||||
*/
|
||||
public static TestCompiler forSystem() {
|
||||
return forCompiler(ToolProvider.getSystemJavaCompiler());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link TestCompiler} backed by the given
|
||||
* {@link JavaCompiler}.
|
||||
* @param javaCompiler the java compiler to use
|
||||
* @return a new {@link TestCompiler} instance
|
||||
*/
|
||||
public static TestCompiler forCompiler(JavaCompiler javaCompiler) {
|
||||
return new TestCompiler(null, javaCompiler, SourceFiles.none(),
|
||||
ResourceFiles.none());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link TestCompiler} instance with addition source files.
|
||||
* @param sourceFiles the additional source files
|
||||
* @return a new {@link TestCompiler} instance
|
||||
*/
|
||||
public TestCompiler withSources(SourceFile... sourceFiles) {
|
||||
return new TestCompiler(this.classLoader, this.compiler,
|
||||
this.sourceFiles.and(sourceFiles), this.resourceFiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link TestCompiler} instance with addition source files.
|
||||
* @param sourceFiles the additional source files
|
||||
* @return a new {@link TestCompiler} instance
|
||||
*/
|
||||
public TestCompiler withSources(SourceFiles sourceFiles) {
|
||||
return new TestCompiler(this.classLoader, this.compiler,
|
||||
this.sourceFiles.and(sourceFiles), this.resourceFiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link TestCompiler} instance with addition resource files.
|
||||
* @param resourceFiles the additional resource files
|
||||
* @return a new {@link TestCompiler} instance
|
||||
*/
|
||||
public TestCompiler withResources(ResourceFile... resourceFiles) {
|
||||
return new TestCompiler(this.classLoader, this.compiler, this.sourceFiles,
|
||||
this.resourceFiles.and(resourceFiles));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link TestCompiler} instance with addition resource files.
|
||||
* @param resourceFiles the additional resource files
|
||||
* @return a new {@link TestCompiler} instance
|
||||
*/
|
||||
public TestCompiler withResources(ResourceFiles resourceFiles) {
|
||||
return new TestCompiler(this.classLoader, this.compiler, this.sourceFiles,
|
||||
this.resourceFiles.and(resourceFiles));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compile content from this instance along with the additional provided
|
||||
* content.
|
||||
* @param content the additional content to compile
|
||||
* @param compiled a consumed used to further assert the compiled code
|
||||
* @throws CompilationException if source cannot be compiled
|
||||
*/
|
||||
public void compile(WritableContent content, Consumer<Compiled> compiled) {
|
||||
compile(SourceFile.of(content), compiled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile content from this instance along with the additional provided
|
||||
* source file.
|
||||
* @param sourceFile the additional source file to compile
|
||||
* @param compiled a consumed used to further assert the compiled code
|
||||
* @throws CompilationException if source cannot be compiled
|
||||
*/
|
||||
public void compile(SourceFile sourceFile, Consumer<Compiled> compiled) {
|
||||
withSources(sourceFile).compile(compiled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile content from this instance along with the additional provided
|
||||
* source files.
|
||||
* @param sourceFiles the additional source files to compile
|
||||
* @param compiled a consumed used to further assert the compiled code
|
||||
* @throws CompilationException if source cannot be compiled
|
||||
*/
|
||||
public void compile(SourceFiles sourceFiles, Consumer<Compiled> compiled) {
|
||||
withSources(sourceFiles).compile(compiled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile content from this instance along with the additional provided
|
||||
* source and resource files.
|
||||
* @param sourceFiles the additional source files to compile
|
||||
* @param resourceFiles the additional resource files to include
|
||||
* @param compiled a consumed used to further assert the compiled code
|
||||
* @throws CompilationException if source cannot be compiled
|
||||
*/
|
||||
public void compile(SourceFiles sourceFiles, ResourceFiles resourceFiles,
|
||||
Consumer<Compiled> compiled) {
|
||||
withSources(sourceFiles).withResources(resourceFiles).compile(compiled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile content from this instance.
|
||||
* @param compiled a consumed used to further assert the compiled code
|
||||
* @throws CompilationException if source cannot be compiled
|
||||
*/
|
||||
public void compile(Consumer<Compiled> compiled) throws CompilationException {
|
||||
DynamicClassLoader dynamicClassLoader = compile();
|
||||
ClassLoader previousClassLoader = Thread.currentThread().getContextClassLoader();
|
||||
try {
|
||||
Thread.currentThread().setContextClassLoader(dynamicClassLoader);
|
||||
compiled.accept(new Compiled(dynamicClassLoader, this.sourceFiles,
|
||||
this.resourceFiles));
|
||||
}
|
||||
finally {
|
||||
Thread.currentThread().setContextClassLoader(previousClassLoader);
|
||||
}
|
||||
}
|
||||
|
||||
private DynamicClassLoader compile() {
|
||||
ClassLoader classLoader = (this.classLoader != null) ? this.classLoader
|
||||
: Thread.currentThread().getContextClassLoader();
|
||||
List<DynamicJavaFileObject> compilationUnits = this.sourceFiles.stream().map(
|
||||
DynamicJavaFileObject::new).toList();
|
||||
StandardJavaFileManager standardFileManager = this.compiler.getStandardFileManager(
|
||||
null, null, null);
|
||||
DynamicJavaFileManager fileManager = new DynamicJavaFileManager(
|
||||
standardFileManager, classLoader);
|
||||
if (!this.sourceFiles.isEmpty()) {
|
||||
Errors errors = new Errors();
|
||||
CompilationTask task = this.compiler.getTask(null, fileManager, errors, null,
|
||||
null, compilationUnits);
|
||||
boolean result = task.call();
|
||||
if (!result || errors.hasReportedErrors()) {
|
||||
throw new CompilationException("Unable to compile source" + errors);
|
||||
}
|
||||
}
|
||||
return new DynamicClassLoader(this.classLoader, this.sourceFiles,
|
||||
this.resourceFiles, fileManager.getClassFiles());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@link DiagnosticListener} used to collect errors.
|
||||
*/
|
||||
static class Errors implements DiagnosticListener<JavaFileObject> {
|
||||
|
||||
private final StringBuilder message = new StringBuilder();
|
||||
|
||||
@Override
|
||||
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
|
||||
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() + ":" + diagnostic.getColumnNumber());
|
||||
}
|
||||
}
|
||||
|
||||
boolean hasReportedErrors() {
|
||||
return this.message.length() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.message.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* 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.generator.file;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.assertj.core.util.Strings;
|
||||
|
||||
/**
|
||||
* Abstract base class for dynamically generated files.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
* @see SourceFile
|
||||
* @see ResourceFile
|
||||
*/
|
||||
public abstract sealed class DynamicFile permits SourceFile,ResourceFile {
|
||||
|
||||
|
||||
private final String path;
|
||||
|
||||
private final String content;
|
||||
|
||||
|
||||
protected DynamicFile(String path, String content) {
|
||||
if (Strings.isNullOrEmpty(content)) {
|
||||
throw new IllegalArgumentException("'path' must not to be empty");
|
||||
}
|
||||
if (Strings.isNullOrEmpty(content)) {
|
||||
throw new IllegalArgumentException("'content' must not to be empty");
|
||||
}
|
||||
this.path = path;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
|
||||
protected static String toString(WritableContent writableContent) {
|
||||
if (writableContent == null) {
|
||||
throw new IllegalArgumentException("'writableContent' must not to be empty");
|
||||
}
|
||||
try {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
writableContent.writeTo(stringBuilder);
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException("Unable to read content", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the contents of the file.
|
||||
* @return the file contents
|
||||
*/
|
||||
public String getContent() {
|
||||
return this.content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the relative path of the file.
|
||||
* @return the file path
|
||||
*/
|
||||
public String getPath() {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
DynamicFile other = (DynamicFile) obj;
|
||||
return Objects.equals(this.path, other.path)
|
||||
&& Objects.equals(this.content, other.content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.path, this.content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* 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.generator.file;
|
||||
|
||||
import org.assertj.core.api.AbstractAssert;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Assertion methods for {@code DynamicFile} instances.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
* @param <A> the assertion type
|
||||
* @param <F> the file type
|
||||
*/
|
||||
public class DynamicFileAssert<A extends DynamicFileAssert<A, F>, F extends DynamicFile>
|
||||
extends AbstractAssert<A, F> {
|
||||
|
||||
|
||||
DynamicFileAssert(F actual, Class<?> selfType) {
|
||||
super(actual, selfType);
|
||||
}
|
||||
|
||||
|
||||
public A contains(CharSequence... values) {
|
||||
assertThat(this.actual.getContent()).contains(values);
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
public A isEqualTo(Object expected) {
|
||||
if (expected instanceof DynamicFile) {
|
||||
return super.isEqualTo(expected);
|
||||
}
|
||||
assertThat(this.actual.getContent()).isEqualTo(
|
||||
expected != null ? expected.toString() : null);
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* 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.generator.file;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Internal class used by {@link SourceFiles} and {@link ResourceFiles} to
|
||||
* manage {@link DynamicFile} instances.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
* @param <F> the {@link DynamicFile} type
|
||||
*/
|
||||
final class DynamicFiles<F extends DynamicFile> implements Iterable<F> {
|
||||
|
||||
|
||||
private static final DynamicFiles<?> NONE = new DynamicFiles<>(
|
||||
Collections.emptyMap());
|
||||
|
||||
|
||||
private final Map<String, F> files;
|
||||
|
||||
|
||||
private DynamicFiles(Map<String, F> files) {
|
||||
this.files = files;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <F extends DynamicFile> DynamicFiles<F> none() {
|
||||
return (DynamicFiles<F>) NONE;
|
||||
}
|
||||
|
||||
DynamicFiles<F> and(F[] files) {
|
||||
Map<String, F> merged = new LinkedHashMap<>(this.files);
|
||||
Arrays.stream(files).forEach(file -> merged.put(file.getPath(), file));
|
||||
return new DynamicFiles<>(Collections.unmodifiableMap(merged));
|
||||
}
|
||||
|
||||
DynamicFiles<F> and(DynamicFiles<F> files) {
|
||||
Map<String, F> merged = new LinkedHashMap<>(this.files);
|
||||
merged.putAll(files.files);
|
||||
return new DynamicFiles<>(Collections.unmodifiableMap(merged));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<F> iterator() {
|
||||
return this.files.values().iterator();
|
||||
}
|
||||
|
||||
Stream<F> stream() {
|
||||
return this.files.values().stream();
|
||||
}
|
||||
|
||||
boolean isEmpty() {
|
||||
return this.files.isEmpty();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
F get(String path) {
|
||||
return this.files.get(path);
|
||||
}
|
||||
|
||||
F getSingle() {
|
||||
if (this.files.size() != 1) {
|
||||
throw new IllegalStateException("No single file available");
|
||||
}
|
||||
return this.files.values().iterator().next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
return this.files.equals(((DynamicFiles<?>) obj).files);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.files.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.files.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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.generator.file;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.thoughtworks.qdox.model.JavaMethod;
|
||||
import com.thoughtworks.qdox.model.JavaParameter;
|
||||
import org.assertj.core.api.AbstractAssert;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Assertion methods for {@code SourceFile} methods.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
*/
|
||||
public class MethodAssert extends AbstractAssert<MethodAssert, JavaMethod> {
|
||||
|
||||
|
||||
MethodAssert(JavaMethod actual) {
|
||||
super(actual, MethodAssert.class);
|
||||
as(describe(actual));
|
||||
}
|
||||
|
||||
|
||||
private String describe(JavaMethod actual) {
|
||||
return actual.getName() + "("
|
||||
+ actual.getParameters().stream().map(
|
||||
this::getFullyQualifiedName).collect(Collectors.joining(", "))
|
||||
+ ")";
|
||||
}
|
||||
|
||||
private String getFullyQualifiedName(JavaParameter parameter) {
|
||||
return parameter.getType().getFullyQualifiedName();
|
||||
}
|
||||
|
||||
public void withBody(String expected) {
|
||||
assertThat(this.actual.getSourceCode()).as(
|
||||
this.info.description()).isEqualToNormalizingWhitespace(expected);
|
||||
}
|
||||
|
||||
public void withBodyContaining(CharSequence... values) {
|
||||
assertThat(this.actual.getSourceCode()).as(this.info.description()).contains(
|
||||
values);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* 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.generator.file;
|
||||
|
||||
import org.assertj.core.api.AssertProvider;
|
||||
|
||||
/**
|
||||
* {@link DynamicFile} that holds resource file content and provides
|
||||
* {@link ResourceFileAssert} support.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
*/
|
||||
public final class ResourceFile extends DynamicFile
|
||||
implements AssertProvider<ResourceFileAssert> {
|
||||
|
||||
|
||||
private ResourceFile(String path, String content) {
|
||||
super(path, content);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Factory method to create a new {@link ResourceFile} from the given
|
||||
* {@link CharSequence}.
|
||||
* @param path the relative path of the file or {@code null} to have the
|
||||
* path deduced
|
||||
* @param charSequence a file containing the source 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 SourceFile} from the given
|
||||
* {@link WritableContent}.
|
||||
* @param path the relative path of the file or {@code null} to have the
|
||||
* path deduced
|
||||
* @param writableContent the content to write to the file
|
||||
* @return a {@link SourceFile} instance
|
||||
*/
|
||||
public static ResourceFile of(String path, WritableContent writableContent) {
|
||||
return new ResourceFile(path, toString(writableContent));
|
||||
}
|
||||
|
||||
/**
|
||||
* AssertJ {@code assertThat} support.
|
||||
* @deprecated use {@code assertThat(sourceFile)} rather than calling this
|
||||
* method directly.
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
public ResourceFileAssert assertThat() {
|
||||
return new ResourceFileAssert(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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.generator.file;
|
||||
|
||||
/**
|
||||
* Assertion methods for {@code ResourceFile} instances.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
*/
|
||||
public class ResourceFileAssert
|
||||
extends DynamicFileAssert<ResourceFileAssert, ResourceFile> {
|
||||
|
||||
|
||||
ResourceFileAssert(ResourceFile actual) {
|
||||
super(actual, ResourceFileAssert.class);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* 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.generator.file;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* An immutable collection of {@link ResourceFile} instances.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
*/
|
||||
public final class ResourceFiles implements Iterable<ResourceFile> {
|
||||
|
||||
private static final ResourceFiles NONE = new ResourceFiles(DynamicFiles.none());
|
||||
|
||||
|
||||
private final DynamicFiles<ResourceFile> files;
|
||||
|
||||
|
||||
private ResourceFiles(DynamicFiles<ResourceFile> files) {
|
||||
this.files = files;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a {@link DynamicFiles} instance with no items.
|
||||
* @return the empty instance
|
||||
*/
|
||||
public static ResourceFiles none() {
|
||||
return NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method that can be used to create a {@link ResourceFiles}
|
||||
* instance containing the specified files.
|
||||
* @param ResourceFiles the files to include
|
||||
* @return a {@link ResourceFiles} instance
|
||||
*/
|
||||
public static ResourceFiles of(ResourceFile... ResourceFiles) {
|
||||
return none().and(ResourceFiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link ResourceFiles} instance that merges files from
|
||||
* another array of {@link ResourceFile} instances.
|
||||
* @param ResourceFiles the instances to merge
|
||||
* @return a new {@link ResourceFiles} instance containing merged content
|
||||
*/
|
||||
public ResourceFiles and(ResourceFile... ResourceFiles) {
|
||||
return new ResourceFiles(this.files.and(ResourceFiles));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link ResourceFiles} instance that merges files from
|
||||
* another {@link ResourceFiles} instance.
|
||||
* @param ResourceFiles the instance to merge
|
||||
* @return a new {@link ResourceFiles} instance containing merged content
|
||||
*/
|
||||
public ResourceFiles and(ResourceFiles ResourceFiles) {
|
||||
return new ResourceFiles(this.files.and(ResourceFiles.files));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<ResourceFile> iterator() {
|
||||
return this.files.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream the {@link ResourceFile} instances contained in this collection.
|
||||
* @return a stream of file instances
|
||||
*/
|
||||
public Stream<ResourceFile> stream() {
|
||||
return this.files.stream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this collection is empty.
|
||||
* @return if this collection is empty
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return this.files.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link ResourceFile} with the given
|
||||
* {@code DynamicFile#getPath() path}.
|
||||
* @param path the path to find
|
||||
* @return a {@link ResourceFile} instance or {@code null}
|
||||
*/
|
||||
@Nullable
|
||||
public ResourceFile get(String path) {
|
||||
return this.files.get(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the single source file contained in the collection.
|
||||
* @return the single file
|
||||
* @throws IllegalStateException if the collection doesn't contain exactly
|
||||
* one file
|
||||
*/
|
||||
public ResourceFile getSingle() throws IllegalStateException {
|
||||
return this.files.getSingle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
return this.files.equals(((ResourceFiles) obj).files);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.files.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.files.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* 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.generator.file;
|
||||
|
||||
import java.io.StringReader;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.thoughtworks.qdox.JavaProjectBuilder;
|
||||
import com.thoughtworks.qdox.model.JavaClass;
|
||||
import com.thoughtworks.qdox.model.JavaPackage;
|
||||
import com.thoughtworks.qdox.model.JavaSource;
|
||||
import org.assertj.core.api.AssertProvider;
|
||||
import org.assertj.core.util.Strings;
|
||||
|
||||
/**
|
||||
* {@link DynamicFile} that holds Java source code and provides
|
||||
* {@link SourceFileAssert} support. Usually created from an AOT generated type,
|
||||
* for example:
|
||||
*
|
||||
* <pre class="code">
|
||||
* SourceFile.of(generatedFile::writeTo)
|
||||
* </pre>
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
*/
|
||||
public final class SourceFile extends DynamicFile
|
||||
implements AssertProvider<SourceFileAssert> {
|
||||
|
||||
|
||||
private final JavaSource javaSource;
|
||||
|
||||
|
||||
private SourceFile(String path, String content, JavaSource javaSource) {
|
||||
super(path, content);
|
||||
this.javaSource = javaSource;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Factory method to create a new {@link SourceFile} from the given
|
||||
* {@link CharSequence}.
|
||||
* @param charSequence a file containing the source contents
|
||||
* @return a {@link SourceFile} instance
|
||||
*/
|
||||
public static SourceFile of(CharSequence charSequence) {
|
||||
return of(null, appendable -> appendable.append(charSequence));
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create a new {@link SourceFile} from the given
|
||||
* {@link CharSequence}.
|
||||
* @param path the relative path of the file or {@code null} to have the
|
||||
* path deduced
|
||||
* @param charSequence a file containing the source contents
|
||||
* @return a {@link SourceFile} instance
|
||||
*/
|
||||
public static SourceFile of(@Nullable String path, CharSequence charSequence) {
|
||||
return of(path, appendable -> appendable.append(charSequence));
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create a new {@link SourceFile} from the given
|
||||
* {@link WritableContent}.
|
||||
* @param writableContent the content to write to the file
|
||||
* @return a {@link SourceFile} instance
|
||||
*/
|
||||
public static SourceFile of(WritableContent writableContent) {
|
||||
return of(null, writableContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create a new {@link SourceFile} from the given
|
||||
* {@link WritableContent}.
|
||||
* @param path the relative path of the file or {@code null} to have the
|
||||
* path deduced
|
||||
* @param writableContent the content to write to the file
|
||||
* @return a {@link SourceFile} instance
|
||||
*/
|
||||
public static SourceFile of(@Nullable String path, WritableContent writableContent) {
|
||||
String content = toString(writableContent);
|
||||
if (Strings.isNullOrEmpty(content)) {
|
||||
throw new IllegalStateException("WritableContent did not append any content");
|
||||
}
|
||||
JavaSource javaSource = parse(content);
|
||||
if (path == null || path.isEmpty()) {
|
||||
path = deducePath(javaSource);
|
||||
}
|
||||
return new SourceFile(path, content, javaSource);
|
||||
}
|
||||
|
||||
private static JavaSource parse(String content) {
|
||||
JavaProjectBuilder builder = new JavaProjectBuilder();
|
||||
try {
|
||||
JavaSource javaSource = builder.addSource(new StringReader(content));
|
||||
if (javaSource.getClasses().size() != 1) {
|
||||
throw new IllegalStateException("Source must define a single class");
|
||||
}
|
||||
return javaSource;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(
|
||||
"Unable to parse source file content:\n\n" + content, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static String deducePath(JavaSource javaSource) {
|
||||
JavaPackage javaPackage = javaSource.getPackage();
|
||||
JavaClass javaClass = javaSource.getClasses().get(0);
|
||||
String path = javaClass.getName() + ".java";
|
||||
if (javaPackage != null) {
|
||||
path = javaPackage.getName().replace('.', '/') + "/" + path;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
JavaSource getJavaSource() {
|
||||
return this.javaSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the target class for this source file or {@code null}. The target
|
||||
* class can be used if private lookup access is required.
|
||||
* @return the target class
|
||||
*/
|
||||
@Nullable
|
||||
public Class<?> getTarget() {
|
||||
return null; // Not yet supported
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return this.javaSource.getClasses().get(0).getFullyQualifiedName();
|
||||
}
|
||||
|
||||
/**
|
||||
* AssertJ {@code assertThat} support.
|
||||
* @deprecated use {@code assertThat(sourceFile)} rather than calling this
|
||||
* method directly.
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
public SourceFileAssert assertThat() {
|
||||
return new SourceFileAssert(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* 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.generator.file;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.thoughtworks.qdox.model.JavaClass;
|
||||
import com.thoughtworks.qdox.model.JavaMethod;
|
||||
import com.thoughtworks.qdox.model.JavaParameter;
|
||||
import com.thoughtworks.qdox.model.JavaType;
|
||||
import org.assertj.core.error.BasicErrorMessageFactory;
|
||||
import org.assertj.core.internal.Failures;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Assertion methods for {@code SourceFile} instances.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
*/
|
||||
public class SourceFileAssert extends DynamicFileAssert<SourceFileAssert, SourceFile> {
|
||||
|
||||
|
||||
SourceFileAssert(SourceFile actual) {
|
||||
super(actual, SourceFileAssert.class);
|
||||
}
|
||||
|
||||
|
||||
public SourceFileAssert implementsInterface(Class<?> type) {
|
||||
return implementsInterface((type != null) ? type.getName() : (String) null);
|
||||
}
|
||||
|
||||
public SourceFileAssert implementsInterface(String name) {
|
||||
JavaClass javaClass = getJavaClass();
|
||||
assertThat(javaClass.getImplements()).as("implements").map(
|
||||
JavaType::getFullyQualifiedName).contains(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MethodAssert hasMethodNamed(String name) {
|
||||
JavaClass javaClass = getJavaClass();
|
||||
JavaMethod method = null;
|
||||
for (JavaMethod candidate : javaClass.getMethods()) {
|
||||
if (candidate.getName().equals(name)) {
|
||||
if (method != null) {
|
||||
throw Failures.instance().failure(this.info,
|
||||
new BasicErrorMessageFactory(String.format(
|
||||
"%nExpecting actual:%n %s%nto contain unique method:%n %s%n",
|
||||
this.actual.getContent(), name)));
|
||||
}
|
||||
method = candidate;
|
||||
}
|
||||
}
|
||||
if (method == null) {
|
||||
throw Failures.instance().failure(this.info,
|
||||
new BasicErrorMessageFactory(String.format(
|
||||
"%nExpecting actual:%n %s%nto contain method:%n %s%n",
|
||||
this.actual.getContent(), name)));
|
||||
}
|
||||
return new MethodAssert(method);
|
||||
}
|
||||
|
||||
public MethodAssert hasMethod(String name, Class<?>... parameters) {
|
||||
JavaClass javaClass = getJavaClass();
|
||||
JavaMethod method = null;
|
||||
for (JavaMethod candidate : javaClass.getMethods()) {
|
||||
if (candidate.getName().equals(name)
|
||||
&& hasParameters(candidate, parameters)) {
|
||||
if (method != null) {
|
||||
throw Failures.instance().failure(this.info,
|
||||
new BasicErrorMessageFactory(String.format(
|
||||
"%nExpecting actual:%n %s%nto contain unique method:%n %s%n",
|
||||
this.actual.getContent(), name)));
|
||||
}
|
||||
method = candidate;
|
||||
}
|
||||
}
|
||||
if (method == null) {
|
||||
String methodDescription = getMethodDescription(name, parameters);
|
||||
throw Failures.instance().failure(this.info,
|
||||
new BasicErrorMessageFactory(String.format(
|
||||
"%nExpecting actual:%n %s%nto contain method:%n %s%n",
|
||||
this.actual.getContent(), methodDescription)));
|
||||
}
|
||||
return new MethodAssert(method);
|
||||
}
|
||||
|
||||
private boolean hasParameters(JavaMethod method, Class<?>[] requiredParameters) {
|
||||
List<JavaParameter> parameters = method.getParameters();
|
||||
if (parameters.size() != requiredParameters.length) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < requiredParameters.length; i++) {
|
||||
if (!requiredParameters[i].getName().equals(
|
||||
parameters.get(i).getFullyQualifiedName())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private String getMethodDescription(String name, Class<?>... parameters) {
|
||||
return name + "(" + Arrays.stream(parameters).map(Class::getName).collect(
|
||||
Collectors.joining(", ")) + ")";
|
||||
}
|
||||
|
||||
private JavaClass getJavaClass() {
|
||||
return this.actual.getJavaSource().getClasses().get(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* 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.generator.file;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* An immutable collection of {@link SourceFile} instances.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
*/
|
||||
public final class SourceFiles implements Iterable<SourceFile> {
|
||||
|
||||
private static final SourceFiles NONE = new SourceFiles(DynamicFiles.none());
|
||||
|
||||
|
||||
private final DynamicFiles<SourceFile> files;
|
||||
|
||||
|
||||
private SourceFiles(DynamicFiles<SourceFile> files) {
|
||||
this.files = files;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a {@link SourceFiles} instance with no items.
|
||||
* @return the empty instance
|
||||
*/
|
||||
public static SourceFiles none() {
|
||||
return NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method that can be used to create a {@link SourceFiles} instance
|
||||
* containing the specified files.
|
||||
* @param sourceFiles the files to include
|
||||
* @return a {@link SourceFiles} instance
|
||||
*/
|
||||
public static SourceFiles of(SourceFile... sourceFiles) {
|
||||
return none().and(sourceFiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link SourceFiles} instance that merges files from another
|
||||
* array of {@link SourceFile} instances.
|
||||
* @param sourceFiles the instances to merge
|
||||
* @return a new {@link SourceFiles} instance containing merged content
|
||||
*/
|
||||
public SourceFiles and(SourceFile... sourceFiles) {
|
||||
return new SourceFiles(this.files.and(sourceFiles));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link SourceFiles} instance that merges files from another
|
||||
* {@link SourceFiles} instance.
|
||||
* @param sourceFiles the instance to merge
|
||||
* @return a new {@link SourceFiles} instance containing merged content
|
||||
*/
|
||||
public SourceFiles and(SourceFiles sourceFiles) {
|
||||
return new SourceFiles(this.files.and(sourceFiles.files));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<SourceFile> iterator() {
|
||||
return this.files.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream the {@link SourceFile} instances contained in this collection.
|
||||
* @return a stream of file instances
|
||||
*/
|
||||
public Stream<SourceFile> stream() {
|
||||
return this.files.stream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this collection is empty.
|
||||
* @return if this collection is empty
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return this.files.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link SourceFile} with the given
|
||||
* {@code DynamicFile#getPath() path}.
|
||||
* @param path the path to find
|
||||
* @return a {@link SourceFile} instance or {@code null}
|
||||
*/
|
||||
@Nullable
|
||||
public SourceFile get(String path) {
|
||||
return this.files.get(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the single source file contained in the collection.
|
||||
* @return the single file
|
||||
* @throws IllegalStateException if the collection doesn't contain exactly
|
||||
* one file
|
||||
*/
|
||||
public SourceFile getSingle() throws IllegalStateException {
|
||||
return this.files.getSingle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
return this.files.equals(((SourceFiles) obj).files);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.files.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.files.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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.generator.file;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Callback interface used to write file content. Designed to align with
|
||||
* JavaPoet's {@code JavaFile.writeTo} method.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface WritableContent {
|
||||
|
||||
/**
|
||||
* Callback method that should write the content to the given
|
||||
* {@link Appendable}.
|
||||
* @param out the {@link Appendable} used to receive the content
|
||||
* @throws IOException on IO error
|
||||
*/
|
||||
void writeTo(Appendable out) throws IOException;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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.generator.compile;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for {@link CompilationException}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
*/
|
||||
class CompilationExceptionTests {
|
||||
|
||||
@Test
|
||||
void getMessageReturnsMessage() {
|
||||
CompilationException exception = new CompilationException("message");
|
||||
assertThat(exception).hasMessage("message");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* 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.generator.compile;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.test.generator.file.ResourceFile;
|
||||
import org.springframework.aot.test.generator.file.ResourceFiles;
|
||||
import org.springframework.aot.test.generator.file.SourceFile;
|
||||
import org.springframework.aot.test.generator.file.SourceFiles;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
|
||||
/**
|
||||
* Tests for {@link Compiled}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
*/
|
||||
class CompiledTests {
|
||||
|
||||
private static final String HELLO_WORLD = """
|
||||
package com.example;
|
||||
|
||||
public class HelloWorld implements java.util.function.Supplier<String> {
|
||||
|
||||
public String get() {
|
||||
return "Hello World!";
|
||||
}
|
||||
|
||||
}
|
||||
""";
|
||||
|
||||
private static final String HELLO_SPRING = """
|
||||
package com.example;
|
||||
|
||||
public class HelloSpring implements java.util.function.Supplier<String> {
|
||||
|
||||
public String get() {
|
||||
return "Hello Spring!"; // !!
|
||||
}
|
||||
|
||||
}
|
||||
""";
|
||||
|
||||
@Test
|
||||
void getSourceFileWhenSingleReturnsSourceFile() {
|
||||
SourceFile sourceFile = SourceFile.of(HELLO_WORLD);
|
||||
TestCompiler.forSystem().compile(sourceFile,
|
||||
compiled -> assertThat(compiled.getSourceFile()).isSameAs(sourceFile));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getSourceFileWhenMultipleThrowsException() {
|
||||
SourceFiles sourceFiles = SourceFiles.of(SourceFile.of(HELLO_WORLD),
|
||||
SourceFile.of(HELLO_SPRING));
|
||||
TestCompiler.forSystem().compile(sourceFiles,
|
||||
compiled -> assertThatIllegalStateException().isThrownBy(
|
||||
compiled::getSourceFile));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getSourceFileWhenNoneThrowsException() {
|
||||
TestCompiler.forSystem().compile(
|
||||
compiled -> assertThatIllegalStateException().isThrownBy(
|
||||
compiled::getSourceFile));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getSourceFilesReturnsSourceFiles() {
|
||||
SourceFiles sourceFiles = SourceFiles.of(SourceFile.of(HELLO_WORLD),
|
||||
SourceFile.of(HELLO_SPRING));
|
||||
TestCompiler.forSystem().compile(sourceFiles,
|
||||
compiled -> assertThat(compiled.getSourceFiles()).isEqualTo(sourceFiles));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getResourceFileWhenSingleReturnsSourceFile() {
|
||||
ResourceFile resourceFile = ResourceFile.of("META-INF/myfile", "test");
|
||||
TestCompiler.forSystem().withResources(resourceFile).compile(
|
||||
compiled -> assertThat(compiled.getResourceFile()).isSameAs(
|
||||
resourceFile));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getResourceFileWhenMultipleThrowsException() {
|
||||
ResourceFiles resourceFiles = ResourceFiles.of(
|
||||
ResourceFile.of("META-INF/myfile1", "test1"),
|
||||
ResourceFile.of("META-INF/myfile2", "test2"));
|
||||
TestCompiler.forSystem().withResources(resourceFiles).compile(
|
||||
compiled -> assertThatIllegalStateException().isThrownBy(
|
||||
() -> compiled.getResourceFile()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getResourceFileWhenNoneThrowsException() {
|
||||
TestCompiler.forSystem().compile(
|
||||
compiled -> assertThatIllegalStateException().isThrownBy(
|
||||
() -> compiled.getResourceFile()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getResourceFilesReturnsResourceFiles() {
|
||||
ResourceFiles resourceFiles = ResourceFiles.of(
|
||||
ResourceFile.of("META-INF/myfile1", "test1"),
|
||||
ResourceFile.of("META-INF/myfile2", "test2"));
|
||||
TestCompiler.forSystem().withResources(resourceFiles).compile(
|
||||
compiled -> assertThat(compiled.getResourceFiles()).isEqualTo(
|
||||
resourceFiles));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getInstanceWhenNoneMatchesThrowsException() {
|
||||
TestCompiler.forSystem().compile(SourceFile.of(HELLO_WORLD),
|
||||
compiled -> assertThatIllegalStateException().isThrownBy(
|
||||
() -> compiled.getInstance(Callable.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getInstanceWhenMultipleMatchesThrowsException() {
|
||||
SourceFiles sourceFiles = SourceFiles.of(SourceFile.of(HELLO_WORLD),
|
||||
SourceFile.of(HELLO_SPRING));
|
||||
TestCompiler.forSystem().compile(sourceFiles,
|
||||
compiled -> assertThatIllegalStateException().isThrownBy(
|
||||
() -> compiled.getInstance(Supplier.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getInstanceWhenNoDefaultConstructorThrowsException() {
|
||||
SourceFile sourceFile = SourceFile.of("""
|
||||
package com.example;
|
||||
|
||||
public class HelloWorld implements java.util.function.Supplier<String> {
|
||||
|
||||
public HelloWorld(String name) {
|
||||
}
|
||||
|
||||
public String get() {
|
||||
return "Hello World!";
|
||||
}
|
||||
|
||||
}
|
||||
""");
|
||||
TestCompiler.forSystem().compile(sourceFile,
|
||||
compiled -> assertThatIllegalStateException().isThrownBy(
|
||||
() -> compiled.getInstance(Supplier.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getInstanceReturnsInstance() {
|
||||
TestCompiler.forSystem().compile(SourceFile.of(HELLO_WORLD),
|
||||
compiled -> assertThat(compiled.getInstance(Supplier.class)).isNotNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getInstanceByNameReturnsInstance() {
|
||||
SourceFiles sourceFiles = SourceFiles.of(SourceFile.of(HELLO_WORLD),
|
||||
SourceFile.of(HELLO_SPRING));
|
||||
TestCompiler.forSystem().compile(sourceFiles,
|
||||
compiled -> assertThat(compiled.getInstance(Supplier.class,
|
||||
"com.example.HelloWorld")).isNotNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAllCompiledClassesReturnsCompiledClasses() {
|
||||
SourceFiles sourceFiles = SourceFiles.of(SourceFile.of(HELLO_WORLD),
|
||||
SourceFile.of(HELLO_SPRING));
|
||||
TestCompiler.forSystem().compile(sourceFiles, compiled -> {
|
||||
List<Class<?>> classes = compiled.getAllCompiledClasses();
|
||||
assertThat(classes.stream().map(Class::getName)).containsExactlyInAnyOrder(
|
||||
"com.example.HelloWorld", "com.example.HelloSpring");
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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.generator.compile;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import javax.tools.JavaFileObject.Kind;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link DynamicClassFileObject}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
*/
|
||||
class DynamicClassFileObjectTests {
|
||||
|
||||
@Test
|
||||
void getUriReturnsGeneratedUriBasedOnClassName() {
|
||||
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");
|
||||
assertThat(fileObject.getKind()).isEqualTo(Kind.CLASS);
|
||||
}
|
||||
|
||||
@Test
|
||||
void openOutputStreamWritesToBytes() throws Exception {
|
||||
DynamicClassFileObject fileObject = new DynamicClassFileObject(
|
||||
"com.example.MyClass");
|
||||
try(OutputStream outputStream = fileObject.openOutputStream()) {
|
||||
new ByteArrayInputStream("test".getBytes()).transferTo(outputStream);
|
||||
}
|
||||
assertThat(fileObject.getBytes()).isEqualTo("test".getBytes());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* 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.generator.compile;
|
||||
|
||||
import javax.tools.JavaFileManager;
|
||||
import javax.tools.JavaFileManager.Location;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.JavaFileObject.Kind;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.then;
|
||||
|
||||
/**
|
||||
* Tests for {@link DynamicJavaFileManager}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
*/
|
||||
class DynamicJavaFileManagerTests {
|
||||
|
||||
@Mock
|
||||
private JavaFileManager parentFileManager;
|
||||
|
||||
@Mock
|
||||
private Location location;
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
private DynamicJavaFileManager fileManager;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
this.classLoader = new ClassLoader() {
|
||||
};
|
||||
this.fileManager = new DynamicJavaFileManager(this.parentFileManager,
|
||||
this.classLoader);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getClassLoaderReturnsClassLoader() {
|
||||
assertThat(this.fileManager.getClassLoader(this.location)).isSameAs(
|
||||
this.classLoader);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getJavaFileForOutputWhenClassKindReturnsDynamicClassFile() throws Exception {
|
||||
JavaFileObject fileObject = this.fileManager.getJavaFileForOutput(this.location,
|
||||
"com.example.MyClass", Kind.CLASS, null);
|
||||
assertThat(fileObject).isInstanceOf(DynamicClassFileObject.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getJavaFileForOutputWhenClassKindAndAlreadySeenReturnsSameDynamicClassFile()
|
||||
throws Exception {
|
||||
JavaFileObject fileObject1 = this.fileManager.getJavaFileForOutput(this.location,
|
||||
"com.example.MyClass", Kind.CLASS, null);
|
||||
JavaFileObject fileObject2 = this.fileManager.getJavaFileForOutput(this.location,
|
||||
"com.example.MyClass", Kind.CLASS, null);
|
||||
assertThat(fileObject1).isSameAs(fileObject2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getJavaFileForOutputWhenNotClassKindDelegatesToParentFileManager()
|
||||
throws Exception {
|
||||
this.fileManager.getJavaFileForOutput(this.location, "com.example.MyClass",
|
||||
Kind.SOURCE, null);
|
||||
then(this.parentFileManager).should().getJavaFileForOutput(this.location,
|
||||
"com.example.MyClass", Kind.SOURCE, null);
|
||||
}
|
||||
|
||||
@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.getClassFiles()).containsKeys("com.example.MyClass1",
|
||||
"com.example.MyClass2");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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.generator.compile;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.test.generator.file.SourceFile;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link DynamicJavaFileObject}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
*/
|
||||
class DynamicJavaFileObjectTests {
|
||||
|
||||
private static final String CONTENT = "package com.example; public class Hello {}";
|
||||
|
||||
@Test
|
||||
void getUriReturnsPath() {
|
||||
DynamicJavaFileObject fileObject = new DynamicJavaFileObject(SourceFile.of(CONTENT));
|
||||
assertThat(fileObject.toUri()).hasToString("com/example/Hello.java");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getCharContentReturnsContent() throws Exception {
|
||||
DynamicJavaFileObject fileObject = new DynamicJavaFileObject(SourceFile.of(CONTENT));
|
||||
assertThat(fileObject.getCharContent(true)).isEqualTo(CONTENT);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* 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.generator.compile;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.test.generator.file.ResourceFile;
|
||||
import org.springframework.aot.test.generator.file.ResourceFiles;
|
||||
import org.springframework.aot.test.generator.file.SourceFile;
|
||||
import org.springframework.aot.test.generator.file.SourceFiles;
|
||||
import org.springframework.aot.test.generator.file.WritableContent;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Tests for {@link TestCompiler}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class TestCompilerTests {
|
||||
|
||||
private static final String HELLO_WORLD = """
|
||||
package com.example;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Deprecated
|
||||
public class Hello implements Supplier<String> {
|
||||
|
||||
public String get() {
|
||||
return "Hello World!";
|
||||
}
|
||||
|
||||
}
|
||||
""";
|
||||
|
||||
private static final String HELLO_SPRING = """
|
||||
package com.example;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class Hello implements Supplier<String> {
|
||||
|
||||
public String get() {
|
||||
return "Hello Spring!"; // !!
|
||||
}
|
||||
|
||||
}
|
||||
""";
|
||||
|
||||
private static final String HELLO_BAD = """
|
||||
package com.example;
|
||||
|
||||
public class Hello implements Supplier<String> {
|
||||
|
||||
public String get() {
|
||||
return "Missing Import!";
|
||||
}
|
||||
|
||||
}
|
||||
""";
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void compileWhenHasDifferentClassesWithSameClassNameCompilesBoth() {
|
||||
TestCompiler.forSystem().withSources(SourceFile.of(HELLO_WORLD)).compile(
|
||||
compiled -> {
|
||||
Supplier<String> supplier = compiled.getInstance(Supplier.class,
|
||||
"com.example.Hello");
|
||||
assertThat(supplier.get()).isEqualTo("Hello World!");
|
||||
});
|
||||
TestCompiler.forSystem().withSources(SourceFile.of(HELLO_SPRING)).compile(
|
||||
compiled -> {
|
||||
Supplier<String> supplier = compiled.getInstance(Supplier.class,
|
||||
"com.example.Hello");
|
||||
assertThat(supplier.get()).isEqualTo("Hello Spring!");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void compileAndGetSourceFile() {
|
||||
TestCompiler.forSystem().withSources(SourceFile.of(HELLO_SPRING)).compile(
|
||||
compiled -> assertThat(compiled.getSourceFile()).hasMethodNamed(
|
||||
"get").withBodyContaining("// !!"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void compileWhenSourceHasCompileErrors() {
|
||||
assertThatExceptionOfType(CompilationException.class).isThrownBy(
|
||||
() -> TestCompiler.forSystem().withSources(
|
||||
SourceFile.of(HELLO_BAD)).compile(compiled -> {
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void withSourcesArrayAddsSource() {
|
||||
SourceFile sourceFile = SourceFile.of(HELLO_WORLD);
|
||||
TestCompiler.forSystem().withSources(sourceFile).compile(
|
||||
this::assertSuppliesHelloWorld);
|
||||
}
|
||||
|
||||
@Test
|
||||
void withSourcesAddsSource() {
|
||||
SourceFiles sourceFiles = SourceFiles.of(SourceFile.of(HELLO_WORLD));
|
||||
TestCompiler.forSystem().withSources(sourceFiles).compile(
|
||||
this::assertSuppliesHelloWorld);
|
||||
}
|
||||
|
||||
@Test
|
||||
void withResourcesArrayAddsResource() {
|
||||
ResourceFile resourceFile = ResourceFile.of("META-INF/myfile", "test");
|
||||
TestCompiler.forSystem().withResources(resourceFile).compile(
|
||||
this::assertHasResource);
|
||||
}
|
||||
|
||||
@Test
|
||||
void withResourcesAddsResource() {
|
||||
ResourceFiles resourceFiles = ResourceFiles.of(
|
||||
ResourceFile.of("META-INF/myfile", "test"));
|
||||
TestCompiler.forSystem().withResources(resourceFiles).compile(
|
||||
this::assertHasResource);
|
||||
}
|
||||
|
||||
@Test
|
||||
void compileWithWritableContent() {
|
||||
WritableContent content = appendable -> appendable.append(HELLO_WORLD);
|
||||
TestCompiler.forSystem().compile(content, this::assertSuppliesHelloWorld);
|
||||
}
|
||||
|
||||
@Test
|
||||
void compileWithSourceFile() {
|
||||
SourceFile sourceFile = SourceFile.of(HELLO_WORLD);
|
||||
TestCompiler.forSystem().compile(sourceFile, this::assertSuppliesHelloWorld);
|
||||
}
|
||||
|
||||
@Test
|
||||
void compileWithSourceFiles() {
|
||||
SourceFiles sourceFiles = SourceFiles.of(SourceFile.of(HELLO_WORLD));
|
||||
TestCompiler.forSystem().compile(sourceFiles, this::assertSuppliesHelloWorld);
|
||||
}
|
||||
|
||||
@Test
|
||||
void compileWithSourceFilesAndResourceFiles() {
|
||||
SourceFiles sourceFiles = SourceFiles.of(SourceFile.of(HELLO_WORLD));
|
||||
ResourceFiles resourceFiles = ResourceFiles.of(
|
||||
ResourceFile.of("META-INF/myfile", "test"));
|
||||
TestCompiler.forSystem().compile(sourceFiles, resourceFiles, compiled -> {
|
||||
assertSuppliesHelloWorld(compiled);
|
||||
assertHasResource(compiled);
|
||||
});
|
||||
}
|
||||
|
||||
private void assertSuppliesHelloWorld(Compiled compiled) {
|
||||
assertThat(compiled.getInstance(Supplier.class).get()).isEqualTo("Hello World!");
|
||||
}
|
||||
|
||||
private void assertHasResource(Compiled compiled) {
|
||||
assertThat(compiled.getClassLoader().getResourceAsStream(
|
||||
"META-INF/myfile")).hasContent("test");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* 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.generator.file;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Tests for {@link MethodAssert}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class MethodAssertTests {
|
||||
|
||||
private static final String SAMPLE = """
|
||||
package com.example;
|
||||
|
||||
public class Sample {
|
||||
|
||||
public void run() {
|
||||
System.out.println("Hello World!");
|
||||
}
|
||||
|
||||
}
|
||||
""";
|
||||
|
||||
private final SourceFile sourceFile = SourceFile.of(SAMPLE);
|
||||
|
||||
@Test
|
||||
void withBodyWhenMatches() {
|
||||
assertThat(this.sourceFile).hasMethodNamed("run").withBody("""
|
||||
System.out.println("Hello World!");""");
|
||||
}
|
||||
|
||||
@Test
|
||||
void withBodyWhenDoesNotMatchThrowsException() {
|
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(
|
||||
() -> assertThat(this.sourceFile).hasMethodNamed("run").withBody("""
|
||||
System.out.println("Hello Spring!");""")).withMessageContaining(
|
||||
"to be equal to");
|
||||
}
|
||||
|
||||
@Test
|
||||
void withBodyContainingWhenContainsAll() {
|
||||
assertThat(this.sourceFile).hasMethodNamed("run").withBodyContaining("Hello",
|
||||
"World!");
|
||||
}
|
||||
|
||||
@Test
|
||||
void withBodyWhenDoesNotContainOneThrowsException() {
|
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(
|
||||
() -> assertThat(this.sourceFile).hasMethodNamed(
|
||||
"run").withBodyContaining("Hello",
|
||||
"Spring!")).withMessageContaining("to contain");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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.generator.file;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link ResourceFile}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
*/
|
||||
class ResourceFileTests {
|
||||
|
||||
@Test
|
||||
void ofPathAndCharSequenceCreatesResource() {
|
||||
ResourceFile file = ResourceFile.of("path", "test");
|
||||
assertThat(file.getPath()).isEqualTo("path");
|
||||
assertThat(file.getContent()).isEqualTo("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofPathAndWritableContentCreatesResource() {
|
||||
ResourceFile file = ResourceFile.of("path", appendable -> appendable.append("test"));
|
||||
assertThat(file.getPath()).isEqualTo("path");
|
||||
assertThat(file.getContent()).isEqualTo("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("deprecation")
|
||||
void assertThatReturnsResourceFileAssert() {
|
||||
ResourceFile file = ResourceFile.of("path", "test");
|
||||
assertThat(file.assertThat()).isInstanceOf(ResourceFileAssert.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* 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.generator.file;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
import static org.assertj.core.api.Assertions.assertThatObject;
|
||||
|
||||
class ReResourceFilesTests {
|
||||
|
||||
private static final ResourceFile RESOURCE_FILE_1 = ResourceFile.of("path1",
|
||||
"resource1");
|
||||
|
||||
private static final ResourceFile RESOURCE_FILE_2 = ResourceFile.of("path2",
|
||||
"resource2");
|
||||
|
||||
@Test
|
||||
void noneReturnsNone() {
|
||||
ResourceFiles none = ResourceFiles.none();
|
||||
assertThat(none).isNotNull();
|
||||
assertThat(none.isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofCreatesResourceFiles() {
|
||||
ResourceFiles resourceFiles = ResourceFiles.of(RESOURCE_FILE_1, RESOURCE_FILE_2);
|
||||
assertThat(resourceFiles).containsExactly(RESOURCE_FILE_1, RESOURCE_FILE_2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void andAddsResourceFiles() {
|
||||
ResourceFiles resourceFiles = ResourceFiles.of(RESOURCE_FILE_1);
|
||||
ResourceFiles added = resourceFiles.and(RESOURCE_FILE_2);
|
||||
assertThat(resourceFiles).containsExactly(RESOURCE_FILE_1);
|
||||
assertThat(added).containsExactly(RESOURCE_FILE_1, RESOURCE_FILE_2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void andResourceFilesAddsResourceFiles() {
|
||||
ResourceFiles resourceFiles = ResourceFiles.of(RESOURCE_FILE_1);
|
||||
ResourceFiles added = resourceFiles.and(ResourceFiles.of(RESOURCE_FILE_2));
|
||||
assertThat(resourceFiles).containsExactly(RESOURCE_FILE_1);
|
||||
assertThat(added).containsExactly(RESOURCE_FILE_1, RESOURCE_FILE_2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void iteratorIteratesResourceFiles() {
|
||||
ResourceFiles resourceFiles = ResourceFiles.of(RESOURCE_FILE_1, RESOURCE_FILE_2);
|
||||
Iterator<ResourceFile> iterator = resourceFiles.iterator();
|
||||
assertThat(iterator.next()).isEqualTo(RESOURCE_FILE_1);
|
||||
assertThat(iterator.next()).isEqualTo(RESOURCE_FILE_2);
|
||||
assertThat(iterator.hasNext()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void streamStreamsResourceFiles() {
|
||||
ResourceFiles resourceFiles = ResourceFiles.of(RESOURCE_FILE_1, RESOURCE_FILE_2);
|
||||
assertThat(resourceFiles.stream()).containsExactly(RESOURCE_FILE_1,
|
||||
RESOURCE_FILE_2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEmptyWhenEmptyReturnsTrue() {
|
||||
ResourceFiles resourceFiles = ResourceFiles.of();
|
||||
assertThat(resourceFiles.isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEmptyWhenNotEmptyReturnsFalse() {
|
||||
ResourceFiles resourceFiles = ResourceFiles.of(RESOURCE_FILE_1);
|
||||
assertThat(resourceFiles.isEmpty()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getWhenHasFileReturnsFile() {
|
||||
ResourceFiles resourceFiles = ResourceFiles.of(RESOURCE_FILE_1);
|
||||
assertThat(resourceFiles.get("path1")).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getWhenMissingFileReturnsNull() {
|
||||
ResourceFiles resourceFiles = ResourceFiles.of(RESOURCE_FILE_2);
|
||||
assertThatObject(resourceFiles.get("path1")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getSingleWhenHasNoFilesThrowsException() {
|
||||
assertThatIllegalStateException().isThrownBy(
|
||||
() -> ResourceFiles.none().getSingle());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getSingleWhenHasMultipleFilesThrowsException() {
|
||||
ResourceFiles resourceFiles = ResourceFiles.of(RESOURCE_FILE_1, RESOURCE_FILE_2);
|
||||
assertThatIllegalStateException().isThrownBy(() -> resourceFiles.getSingle());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getSingleWhenHasSingleFileReturnsFile() {
|
||||
ResourceFiles resourceFiles = ResourceFiles.of(RESOURCE_FILE_1);
|
||||
assertThat(resourceFiles.getSingle()).isEqualTo(RESOURCE_FILE_1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsAndHashCode() {
|
||||
ResourceFiles s1 = ResourceFiles.of(RESOURCE_FILE_1, RESOURCE_FILE_2);
|
||||
ResourceFiles s2 = ResourceFiles.of(RESOURCE_FILE_1, RESOURCE_FILE_2);
|
||||
ResourceFiles s3 = ResourceFiles.of(RESOURCE_FILE_1);
|
||||
assertThat(s1.hashCode()).isEqualTo(s2.hashCode());
|
||||
assertThatObject(s1).isEqualTo(s2).isNotEqualTo(s3);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* 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.generator.file;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Tests for {@link SourceFileAssert}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class SourceFileAssertTests {
|
||||
|
||||
private static final String SAMPLE = """
|
||||
package com.example;
|
||||
|
||||
import java.lang.Runnable;
|
||||
|
||||
public class Sample implements Runnable {
|
||||
|
||||
void run() {
|
||||
run("Hello World!");
|
||||
}
|
||||
|
||||
void run(String message) {
|
||||
System.out.println(message);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
new Sample().run();
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
private final SourceFile sourceFile = SourceFile.of(SAMPLE);
|
||||
|
||||
@Test
|
||||
void containsWhenContainsAll() {
|
||||
assertThat(this.sourceFile).contains("Sample", "main");
|
||||
}
|
||||
|
||||
@Test
|
||||
void containsWhenMissingOneThrowsException() {
|
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(
|
||||
() -> assertThat(this.sourceFile).contains("Sample",
|
||||
"missing")).withMessageContaining("to contain");
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWhenEqual() {
|
||||
assertThat(this.sourceFile).isEqualTo(SAMPLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEqualToWhenNotEqualThrowsException() {
|
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(
|
||||
() -> assertThat(this.sourceFile).isEqualTo("no")).withMessageContaining(
|
||||
"expected", "but was");
|
||||
}
|
||||
|
||||
@Test
|
||||
void implementsInterfaceWhenImplementsInterface() {
|
||||
assertThat(this.sourceFile).implementsInterface(Runnable.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void implementsInterfaceWhenDoesNotImplementInterfaceThrowsException() {
|
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(
|
||||
() -> assertThat(this.sourceFile).implementsInterface(
|
||||
Callable.class)).withMessageContaining("to contain:");
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasMethodNamedWhenHasName() {
|
||||
MethodAssert methodAssert = assertThat(this.sourceFile).hasMethodNamed("main");
|
||||
assertThat(methodAssert).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasMethodNameWhenDoesNotHaveMethodThrowsException() {
|
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(
|
||||
() -> assertThat(this.sourceFile).hasMethodNamed(
|
||||
"missing")).withMessageContaining("to contain method");
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasMethodNameWhenHasMultipleMethodsWithNameThrowsException() {
|
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(
|
||||
() -> assertThat(this.sourceFile).hasMethodNamed(
|
||||
"run")).withMessageContaining("to contain unique method");
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasMethodWhenHasMethod() {
|
||||
MethodAssert methodAssert = assertThat(this.sourceFile).hasMethod("run",
|
||||
String.class);
|
||||
assertThat(methodAssert).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasMethodWhenDoesNotHaveMethod() {
|
||||
assertThatExceptionOfType(AssertionError.class).isThrownBy(
|
||||
() -> assertThat(this.sourceFile).hasMethod("run",
|
||||
Integer.class)).withMessageContaining(
|
||||
"to contain").withMessageContaining(
|
||||
"run(java.lang.Integer");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* 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.generator.file;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.thoughtworks.qdox.model.JavaSource;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
|
||||
/**
|
||||
* Tests for {@link SourceFile}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class SourceFileTests {
|
||||
|
||||
private static final String HELLO_WORLD = """
|
||||
package com.example.helloworld;
|
||||
|
||||
public class HelloWorld {
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Hello World!");
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
@Test
|
||||
void ofWhenContentIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> SourceFile.of((WritableContent) null)).withMessage(
|
||||
"'writableContent' must not to be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofWhenContentIsEmptyThrowsException() {
|
||||
assertThatIllegalStateException().isThrownBy(() -> SourceFile.of("")).withMessage(
|
||||
"WritableContent did not append any content");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofWhenSourceDefinesNoClassThrowsException() {
|
||||
assertThatIllegalStateException().isThrownBy(
|
||||
() -> SourceFile.of("package com.example;")).withMessageContaining(
|
||||
"Unable to parse").havingCause().withMessage(
|
||||
"Source must define a single class");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofWhenSourceDefinesMultipleClassesThrowsException() {
|
||||
assertThatIllegalStateException().isThrownBy(() -> SourceFile.of(
|
||||
"public class One {}\npublic class Two{}")).withMessageContaining(
|
||||
"Unable to parse").havingCause().withMessage(
|
||||
"Source must define a single class");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofWhenSourceCannotBeParsedThrowsException() {
|
||||
assertThatIllegalStateException().isThrownBy(
|
||||
() -> SourceFile.of("well this is broken {")).withMessageContaining(
|
||||
"Unable to parse source file content");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofWithoutPathDeducesPath() {
|
||||
SourceFile sourceFile = SourceFile.of(HELLO_WORLD);
|
||||
assertThat(sourceFile.getPath()).isEqualTo(
|
||||
"com/example/helloworld/HelloWorld.java");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofWithPathUsesPath() {
|
||||
SourceFile sourceFile = SourceFile.of("com/example/DifferentPath.java",
|
||||
HELLO_WORLD);
|
||||
assertThat(sourceFile.getPath()).isEqualTo("com/example/DifferentPath.java");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getContentReturnsContent() {
|
||||
SourceFile sourceFile = SourceFile.of(HELLO_WORLD);
|
||||
assertThat(sourceFile.getContent()).isEqualTo(HELLO_WORLD);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getJavaSourceReturnsJavaSource() {
|
||||
SourceFile sourceFile = SourceFile.of(HELLO_WORLD);
|
||||
assertThat(sourceFile.getJavaSource()).isInstanceOf(JavaSource.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("deprecation")
|
||||
void assertThatReturnsAssert() {
|
||||
SourceFile sourceFile = SourceFile.of(HELLO_WORLD);
|
||||
assertThat(sourceFile.assertThat()).isInstanceOf(SourceFileAssert.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createFromJavaPoetStyleApi() {
|
||||
JavaFile javaFile = new JavaFile(HELLO_WORLD);
|
||||
SourceFile sourceFile = SourceFile.of(javaFile::writeTo);
|
||||
assertThat(sourceFile.getContent()).isEqualTo(HELLO_WORLD);
|
||||
}
|
||||
|
||||
/**
|
||||
* JavaPoet style API with a {@code writeTo} method.
|
||||
*/
|
||||
static class JavaFile {
|
||||
|
||||
private final String content;
|
||||
|
||||
JavaFile(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
void writeTo(Appendable out) throws IOException {
|
||||
out.append(this.content);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* 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.generator.file;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
import static org.assertj.core.api.Assertions.assertThatObject;
|
||||
|
||||
/**
|
||||
* Tests for {@link SourceFiles}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class SourceFilesTests {
|
||||
|
||||
private static final SourceFile SOURCE_FILE_1 = SourceFile.of(
|
||||
"public class Test1 {}");
|
||||
|
||||
private static final SourceFile SOURCE_FILE_2 = SourceFile.of(
|
||||
"public class Test2 {}");
|
||||
|
||||
@Test
|
||||
void noneReturnsNone() {
|
||||
SourceFiles none = SourceFiles.none();
|
||||
assertThat(none).isNotNull();
|
||||
assertThat(none.isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofCreatesSourceFiles() {
|
||||
SourceFiles sourceFiles = SourceFiles.of(SOURCE_FILE_1, SOURCE_FILE_2);
|
||||
assertThat(sourceFiles).containsExactly(SOURCE_FILE_1, SOURCE_FILE_2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void andAddsSourceFiles() {
|
||||
SourceFiles sourceFiles = SourceFiles.of(SOURCE_FILE_1);
|
||||
SourceFiles added = sourceFiles.and(SOURCE_FILE_2);
|
||||
assertThat(sourceFiles).containsExactly(SOURCE_FILE_1);
|
||||
assertThat(added).containsExactly(SOURCE_FILE_1, SOURCE_FILE_2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void andSourceFilesAddsSourceFiles() {
|
||||
SourceFiles sourceFiles = SourceFiles.of(SOURCE_FILE_1);
|
||||
SourceFiles added = sourceFiles.and(SourceFiles.of(SOURCE_FILE_2));
|
||||
assertThat(sourceFiles).containsExactly(SOURCE_FILE_1);
|
||||
assertThat(added).containsExactly(SOURCE_FILE_1, SOURCE_FILE_2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void iteratorIteratesSourceFiles() {
|
||||
SourceFiles sourceFiles = SourceFiles.of(SOURCE_FILE_1, SOURCE_FILE_2);
|
||||
Iterator<SourceFile> iterator = sourceFiles.iterator();
|
||||
assertThat(iterator.next()).isEqualTo(SOURCE_FILE_1);
|
||||
assertThat(iterator.next()).isEqualTo(SOURCE_FILE_2);
|
||||
assertThat(iterator.hasNext()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void streamStreamsSourceFiles() {
|
||||
SourceFiles sourceFiles = SourceFiles.of(SOURCE_FILE_1, SOURCE_FILE_2);
|
||||
assertThat(sourceFiles.stream()).containsExactly(SOURCE_FILE_1, SOURCE_FILE_2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEmptyWhenEmptyReturnsTrue() {
|
||||
SourceFiles sourceFiles = SourceFiles.of();
|
||||
assertThat(sourceFiles.isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEmptyWhenNotEmptyReturnsFalse() {
|
||||
SourceFiles sourceFiles = SourceFiles.of(SOURCE_FILE_1);
|
||||
assertThat(sourceFiles.isEmpty()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getWhenHasFileReturnsFile() {
|
||||
SourceFiles sourceFiles = SourceFiles.of(SOURCE_FILE_1);
|
||||
assertThat(sourceFiles.get("Test1.java")).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getWhenMissingFileReturnsNull() {
|
||||
SourceFiles sourceFiles = SourceFiles.of(SOURCE_FILE_2);
|
||||
assertThatObject(sourceFiles.get("Test1.java")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getSingleWhenHasNoFilesThrowsException() {
|
||||
assertThatIllegalStateException().isThrownBy(
|
||||
() -> SourceFiles.none().getSingle());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getSingleWhenHasMultipleFilesThrowsException() {
|
||||
SourceFiles sourceFiles = SourceFiles.of(SOURCE_FILE_1, SOURCE_FILE_2);
|
||||
assertThatIllegalStateException().isThrownBy(() -> sourceFiles.getSingle());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getSingleWhenHasSingleFileReturnsFile() {
|
||||
SourceFiles sourceFiles = SourceFiles.of(SOURCE_FILE_1);
|
||||
assertThat(sourceFiles.getSingle()).isEqualTo(SOURCE_FILE_1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsAndHashCode() {
|
||||
SourceFiles s1 = SourceFiles.of(SOURCE_FILE_1, SOURCE_FILE_2);
|
||||
SourceFiles s2 = SourceFiles.of(SOURCE_FILE_1, SOURCE_FILE_2);
|
||||
SourceFiles s3 = SourceFiles.of(SOURCE_FILE_1);
|
||||
assertThat(s1.hashCode()).isEqualTo(s2.hashCode());
|
||||
assertThatObject(s1).isEqualTo(s2).isNotEqualTo(s3);
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue