From d49d39076e48eefa3935816135e2d802104b8e83 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 6 Nov 2024 11:21:05 +0000 Subject: [PATCH] Prefer modified resources over the originals in TestCompiler Previously, when the test compiler had been seeded with a resource file, any modifications to this resource performed during compilation would be lost as this original content would always be returned. This commit updates the DynamicJavaFileManager to always store the dynamic resource in the dynamicResourceFiles map, irrespective of whether it's being created afresh or from some existing resource content. This ensures that any modifications made to the resource can be retrieved later on. Similarly, DynamicClassLoader has been updated to prefer dynamic resource files over any original resource files. This ensures that the resource that it finds reflects any modifications that have been made to it. --- .../core/test/tools/DynamicClassLoader.java | 10 ++--- .../test/tools/DynamicJavaFileManager.java | 15 ++++---- .../tools/DynamicJavaFileManagerTests.java | 20 +++++++++- .../core/test/tools/TestCompilerTests.java | 37 ++++++++++++++++++- 4 files changed, 67 insertions(+), 15 deletions(-) diff --git a/spring-core-test/src/main/java/org/springframework/core/test/tools/DynamicClassLoader.java b/spring-core-test/src/main/java/org/springframework/core/test/tools/DynamicClassLoader.java index 73b88758a007..c55d66401192 100644 --- a/spring-core-test/src/main/java/org/springframework/core/test/tools/DynamicClassLoader.java +++ b/spring-core-test/src/main/java/org/springframework/core/test/tools/DynamicClassLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -121,14 +121,14 @@ protected URL findResource(String name) { return createResourceUrl(name, () -> classBytes); } } - ResourceFile resourceFile = this.resourceFiles.get(name); - if (resourceFile != null) { - return createResourceUrl(resourceFile.getPath(), resourceFile::getBytes); - } DynamicResourceFileObject dynamicResourceFile = this.dynamicResourceFiles.get(name); if (dynamicResourceFile != null && dynamicResourceFile.getBytes() != null) { return createResourceUrl(dynamicResourceFile.getName(), dynamicResourceFile::getBytes); } + ResourceFile resourceFile = this.resourceFiles.get(name); + if (resourceFile != null) { + return createResourceUrl(resourceFile.getPath(), resourceFile::getBytes); + } return super.findResource(name); } diff --git a/spring-core-test/src/main/java/org/springframework/core/test/tools/DynamicJavaFileManager.java b/spring-core-test/src/main/java/org/springframework/core/test/tools/DynamicJavaFileManager.java index f359069e0cc6..4ac7da9ef3d5 100644 --- a/spring-core-test/src/main/java/org/springframework/core/test/tools/DynamicJavaFileManager.java +++ b/spring-core-test/src/main/java/org/springframework/core/test/tools/DynamicJavaFileManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -70,13 +70,12 @@ public ClassLoader getClassLoader(Location location) { } @Override - public FileObject getFileForOutput(Location location, String packageName, - String relativeName, FileObject sibling) { - ResourceFile resourceFile = this.resourceFiles.get(relativeName); - if (resourceFile != null) { - return new DynamicResourceFileObject(relativeName, resourceFile.getContent()); - } - return this.dynamicResourceFiles.computeIfAbsent(relativeName, DynamicResourceFileObject::new); + public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling) { + return this.dynamicResourceFiles.computeIfAbsent(relativeName, name -> { + ResourceFile resourceFile = this.resourceFiles.get(name); + return (resourceFile != null) ? new DynamicResourceFileObject(name, resourceFile.getContent()) + : new DynamicResourceFileObject(name); + }); } @Override diff --git a/spring-core-test/src/test/java/org/springframework/core/test/tools/DynamicJavaFileManagerTests.java b/spring-core-test/src/test/java/org/springframework/core/test/tools/DynamicJavaFileManagerTests.java index 474c506bf702..b32813b26a13 100644 --- a/spring-core-test/src/test/java/org/springframework/core/test/tools/DynamicJavaFileManagerTests.java +++ b/spring-core-test/src/test/java/org/springframework/core/test/tools/DynamicJavaFileManagerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 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. @@ -17,6 +17,7 @@ package org.springframework.core.test.tools; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; import java.util.EnumSet; @@ -154,6 +155,23 @@ void getFileForOutputReturnsFile() throws Exception { "META-INF/second.properties"); } + @Test + void existingResourceFileCanBeUpdated() throws IOException { + try (InputStream input = getResourceOne().openInputStream()) { + assertThat(input).hasContent("a"); + } + try (OutputStream output = getResourceOne().openOutputStream()) { + output.write('b'); + } + try (InputStream input = getResourceOne().openInputStream()) { + assertThat(input).hasContent("b"); + } + } + + private FileObject getResourceOne() { + return this.fileManager.getFileForOutput(this.location, "", "com/example/one/resource.one", null); + } + private void writeDummyBytecode(JavaFileObject fileObject) throws IOException { try (OutputStream outputStream = fileObject.openOutputStream()) { StreamUtils.copy(DUMMY_BYTECODE, outputStream); diff --git a/spring-core-test/src/test/java/org/springframework/core/test/tools/TestCompilerTests.java b/spring-core-test/src/test/java/org/springframework/core/test/tools/TestCompilerTests.java index 48bd7e8df73e..504c56995598 100644 --- a/spring-core-test/src/test/java/org/springframework/core/test/tools/TestCompilerTests.java +++ b/spring-core-test/src/test/java/org/springframework/core/test/tools/TestCompilerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -16,7 +16,10 @@ package org.springframework.core.test.tools; +import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; +import java.io.UncheckedIOException; import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -27,6 +30,8 @@ import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.element.TypeElement; +import javax.tools.FileObject; +import javax.tools.StandardLocation; import com.example.PublicInterface; import org.junit.jupiter.api.Test; @@ -367,6 +372,14 @@ void getResourceForCompiledBytecode() { }); } + @Test + void getUpdatedResourceAsStream() { + SourceFile sourceFile = SourceFile.of(HELLO_WORLD); + TestCompiler.forSystem().withResources(ResourceFile.of("com/example/resource", new byte[] { 'a' })) + .withProcessors(new ResourceModifyingProcessor()).compile(sourceFile, compiled -> assertThat( + compiled.getClassLoader().getResourceAsStream("com/example/resource")).hasContent("b")); + } + private void assertSuppliesHelloWorld(Compiled compiled) { assertThat(compiled.getInstance(Supplier.class).get()).isEqualTo("Hello World!"); } @@ -392,4 +405,26 @@ public List getProcessedAnnotations() { } } + @SupportedAnnotationTypes("java.lang.Deprecated") + static class ResourceModifyingProcessor extends AbstractProcessor { + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (roundEnv.processingOver()) { + try { + FileObject resource = this.processingEnv.getFiler() + .createResource(StandardLocation.CLASS_OUTPUT, "", "com/example/resource"); + try (OutputStream output = resource.openOutputStream()) { + output.write('b'); + } + } + catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + return true; + } + + } + }