Allow test compilation to reference existing generated classes
Previously, the generated classes from an InMemoryGeneratedFiles were not taken into account and if generated code refers to any of them, compilation failed. This commit introduces a ClasFile abstraction, similar to ResourceFile for resources that represents an existing generated class. Closes gh-29141 Co-authored-by: Andy Wilkinson <wilkinsona@vmware.com>
This commit is contained in:
@@ -16,6 +16,9 @@
|
||||
|
||||
package org.springframework.aot.test.generate.compile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.EnumSet;
|
||||
|
||||
import javax.tools.JavaFileManager;
|
||||
import javax.tools.JavaFileManager.Location;
|
||||
import javax.tools.JavaFileObject;
|
||||
@@ -26,6 +29,9 @@ import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import org.springframework.aot.test.generate.file.ClassFile;
|
||||
import org.springframework.aot.test.generate.file.ClassFiles;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.then;
|
||||
|
||||
@@ -36,6 +42,8 @@ import static org.mockito.BDDMockito.then;
|
||||
*/
|
||||
class DynamicJavaFileManagerTests {
|
||||
|
||||
private static final byte[] DUMMY_BYTECODE = new byte[] { 'a' };
|
||||
|
||||
@Mock
|
||||
private JavaFileManager parentFileManager;
|
||||
|
||||
@@ -49,10 +57,10 @@ class DynamicJavaFileManagerTests {
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
this.classLoader = new ClassLoader() {
|
||||
};
|
||||
this.fileManager = new DynamicJavaFileManager(this.parentFileManager,
|
||||
this.classLoader);
|
||||
this.classLoader = new ClassLoader() {};
|
||||
this.fileManager = new DynamicJavaFileManager(this.parentFileManager, this.classLoader,
|
||||
ClassFiles.of(ClassFile.of("com.example.one.ClassOne", DUMMY_BYTECODE),
|
||||
ClassFile.of("com.example.two.ClassTwo", DUMMY_BYTECODE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -93,8 +101,32 @@ class DynamicJavaFileManagerTests {
|
||||
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");
|
||||
assertThat(this.fileManager.getClassFiles().stream().map(ClassFile::getName))
|
||||
.contains("com.example.MyClass1", "com.example.MyClass2");
|
||||
}
|
||||
|
||||
@Test
|
||||
void listWithoutRecurseReturnsClassesInRequestedPackage() throws IOException {
|
||||
Iterable<JavaFileObject> listed = this.fileManager.list(
|
||||
this.location, "com.example.one", EnumSet.allOf(Kind.class), false);
|
||||
assertThat(listed).hasSize(1);
|
||||
assertThat(listed).extracting(JavaFileObject::getName).containsExactly("/com/example/one/ClassOne.class");
|
||||
}
|
||||
|
||||
@Test
|
||||
void listWithRecurseReturnsClassesInRequestedPackageAndSubpackages() throws IOException {
|
||||
Iterable<JavaFileObject> listed = this.fileManager.list(
|
||||
this.location, "com.example", EnumSet.allOf(Kind.class), true);
|
||||
assertThat(listed).hasSize(2);
|
||||
assertThat(listed).extracting(JavaFileObject::getName)
|
||||
.containsExactly("/com/example/one/ClassOne.class", "/com/example/two/ClassTwo.class");
|
||||
}
|
||||
|
||||
@Test
|
||||
void listWithoutClassKindDoesNotReturnClasses() throws IOException {
|
||||
Iterable<JavaFileObject> listed = this.fileManager.list(
|
||||
this.location, "com.example", EnumSet.of(Kind.SOURCE), true);
|
||||
assertThat(listed).hasSize(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,11 +30,13 @@ import javax.lang.model.element.TypeElement;
|
||||
import com.example.PublicInterface;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.test.generate.file.ClassFile;
|
||||
import org.springframework.aot.test.generate.file.ResourceFile;
|
||||
import org.springframework.aot.test.generate.file.ResourceFiles;
|
||||
import org.springframework.aot.test.generate.file.SourceFile;
|
||||
import org.springframework.aot.test.generate.file.SourceFiles;
|
||||
import org.springframework.aot.test.generate.file.WritableContent;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -241,6 +243,46 @@ class TestCompilerTests {
|
||||
.withMessageContaining(ClassUtils.getShortName(CompileWithTargetClassAccess.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void compiledCodeCanReferenceAdditionalClassInSamePackage() {
|
||||
SourceFiles sourceFiles = SourceFiles.of(SourceFile.of("""
|
||||
package com.example;
|
||||
|
||||
public class Test implements PublicInterface {
|
||||
|
||||
public String perform() {
|
||||
return Messages.HELLO;
|
||||
}
|
||||
|
||||
}
|
||||
"""));
|
||||
ClassFile messagesClass = ClassFile.of("com.example.Messages",
|
||||
new ClassPathResource("com.example.Messages"));
|
||||
TestCompiler.forSystem().withClasses(List.of(messagesClass)).compile(sourceFiles, compiled ->
|
||||
assertThat(compiled.getInstance(PublicInterface.class, "com.example.Test").perform())
|
||||
.isEqualTo("Hello"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void compiledCodeCanReferenceAdditionalClassInDifferentPackage() {
|
||||
SourceFiles sourceFiles = SourceFiles.of(SourceFile.of("""
|
||||
package com.example;
|
||||
|
||||
import com.example.subpackage.Messages;
|
||||
|
||||
public class Test implements PublicInterface {
|
||||
|
||||
public String perform() {
|
||||
return Messages.HELLO;
|
||||
}
|
||||
|
||||
}
|
||||
"""));
|
||||
ClassFile messagesClass = ClassFile.of("com.example.subpackage.Messages",
|
||||
new ClassPathResource("com.example.subpackage.Messages"));
|
||||
TestCompiler.forSystem().withClasses(List.of(messagesClass)).compile(sourceFiles, compiled -> assertThat(
|
||||
compiled.getInstance(PublicInterface.class, "com.example.Test").perform()).isEqualTo("Hello from subpackage"));
|
||||
}
|
||||
|
||||
private void assertSuppliesHelloWorld(Compiled compiled) {
|
||||
assertThat(compiled.getInstance(Supplier.class).get()).isEqualTo("Hello World!");
|
||||
|
||||
@@ -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.generate.file;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Tests for {@link ClassFile}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class ClassFileTests {
|
||||
|
||||
private final static byte[] TEST_CONTENT = new byte[]{'a'};
|
||||
|
||||
@Test
|
||||
void ofNameAndByteArrayCreatesClass() {
|
||||
ClassFile classFile = ClassFile.of("com.example.Test", TEST_CONTENT);
|
||||
assertThat(classFile.getName()).isEqualTo("com.example.Test");
|
||||
assertThat(classFile.getContent()).isEqualTo(TEST_CONTENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofNameAndInputStreamResourceCreatesClass() {
|
||||
ClassFile classFile = ClassFile.of("com.example.Test",
|
||||
new ByteArrayResource(TEST_CONTENT));
|
||||
assertThat(classFile.getName()).isEqualTo("com.example.Test");
|
||||
assertThat(classFile.getContent()).isEqualTo(TEST_CONTENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void toClassNameWithPathToClassFile() {
|
||||
assertThat(ClassFile.toClassName("com/example/Test.class")).isEqualTo("com.example.Test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void toClassNameWithPathToTextFile() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> ClassFile.toClassName("com/example/Test.txt"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.aot.test.generate.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.assertThatObject;
|
||||
|
||||
/**
|
||||
* Tests for {@link ClassFiles}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class ClassFilesTests {
|
||||
|
||||
private static final ClassFile CLASS_FILE_1 = ClassFile.of(
|
||||
"com.example.Test1", new byte[] { 'a' });
|
||||
|
||||
private static final ClassFile CLASS_FILE_2 = ClassFile.of(
|
||||
"com.example.Test2", new byte[] { 'b' });
|
||||
|
||||
@Test
|
||||
void noneReturnsNone() {
|
||||
ClassFiles none = ClassFiles.none();
|
||||
assertThat(none).isNotNull();
|
||||
assertThat(none.isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofCreatesClassFiles() {
|
||||
ClassFiles classFiles = ClassFiles.of(CLASS_FILE_1, CLASS_FILE_2);
|
||||
assertThat(classFiles).containsExactly(CLASS_FILE_1, CLASS_FILE_2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void andAddsClassFiles() {
|
||||
ClassFiles classFiles = ClassFiles.of(CLASS_FILE_1);
|
||||
ClassFiles added = classFiles.and(CLASS_FILE_2);
|
||||
assertThat(classFiles).containsExactly(CLASS_FILE_1);
|
||||
assertThat(added).containsExactly(CLASS_FILE_1, CLASS_FILE_2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void andClassFilesAddsClassFiles() {
|
||||
ClassFiles classFiles = ClassFiles.of(CLASS_FILE_1);
|
||||
ClassFiles added = classFiles.and(ClassFiles.of(CLASS_FILE_2));
|
||||
assertThat(classFiles).containsExactly(CLASS_FILE_1);
|
||||
assertThat(added).containsExactly(CLASS_FILE_1, CLASS_FILE_2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void iteratorIteratesClassFiles() {
|
||||
ClassFiles classFiles = ClassFiles.of(CLASS_FILE_1, CLASS_FILE_2);
|
||||
Iterator<ClassFile> iterator = classFiles.iterator();
|
||||
assertThat(iterator.next()).isEqualTo(CLASS_FILE_1);
|
||||
assertThat(iterator.next()).isEqualTo(CLASS_FILE_2);
|
||||
assertThat(iterator.hasNext()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void streamStreamsClassFiles() {
|
||||
ClassFiles classFiles = ClassFiles.of(CLASS_FILE_1, CLASS_FILE_2);
|
||||
assertThat(classFiles.stream()).containsExactly(CLASS_FILE_1, CLASS_FILE_2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEmptyWhenEmptyReturnsTrue() {
|
||||
ClassFiles classFiles = ClassFiles.of();
|
||||
assertThat(classFiles.isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEmptyWhenNotEmptyReturnsFalse() {
|
||||
ClassFiles classFiles = ClassFiles.of(CLASS_FILE_1);
|
||||
assertThat(classFiles.isEmpty()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getWhenHasFileReturnsFile() {
|
||||
ClassFiles classFiles = ClassFiles.of(CLASS_FILE_1);
|
||||
assertThat(classFiles.get("com.example.Test1")).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getWhenMissingFileReturnsNull() {
|
||||
ClassFiles classFiles = ClassFiles.of(CLASS_FILE_2);
|
||||
assertThatObject(classFiles.get("com.example.another.Test2")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsAndHashCode() {
|
||||
ClassFiles s1 = ClassFiles.of(CLASS_FILE_1, CLASS_FILE_2);
|
||||
ClassFiles s2 = ClassFiles.of(CLASS_FILE_1, CLASS_FILE_2);
|
||||
ClassFiles s3 = ClassFiles.of(CLASS_FILE_1);
|
||||
assertThat(s1.hashCode()).isEqualTo(s2.hashCode());
|
||||
assertThatObject(s1).isEqualTo(s2).isNotEqualTo(s3);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user