Browse Source
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-28120pull/28170/head
Phillip Webb
3 years ago
committed by
Stephane Nicoll
34 changed files with 3163 additions and 1 deletions
@ -0,0 +1,13 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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