Merge pull request #45202 from nosan
Build and Deploy Snapshot / Build and Deploy Snapshot (push) Waiting to run
Details
Build and Deploy Snapshot / Trigger Docs Build (push) Blocked by required conditions
Details
Build and Deploy Snapshot / Verify (push) Blocked by required conditions
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:false version:17], map[id:${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} name:Linux]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:false version:17], map[id:windows-latest name:Windows]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:21], map[id:${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} name:Linux]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:21], map[id:windows-latest name:Windows]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:22], map[id:${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} name:Linux]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:22], map[id:windows-latest name:Windows]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:23], map[id:${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} name:Linux]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:23], map[id:windows-latest name:Windows]) (push) Waiting to run
Details
Run System Tests / Java ${{ matrix.java.version}} (map[toolchain:false version:17]) (push) Waiting to run
Details
Run System Tests / Java ${{ matrix.java.version}} (map[toolchain:true version:21]) (push) Waiting to run
Details
Build and Deploy Snapshot / Build and Deploy Snapshot (push) Waiting to run
Details
Build and Deploy Snapshot / Trigger Docs Build (push) Blocked by required conditions
Details
Build and Deploy Snapshot / Verify (push) Blocked by required conditions
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:false version:17], map[id:${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} name:Linux]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:false version:17], map[id:windows-latest name:Windows]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:21], map[id:${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} name:Linux]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:21], map[id:windows-latest name:Windows]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:22], map[id:${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} name:Linux]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:22], map[id:windows-latest name:Windows]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:23], map[id:${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} name:Linux]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:23], map[id:windows-latest name:Windows]) (push) Waiting to run
Details
Run System Tests / Java ${{ matrix.java.version}} (map[toolchain:false version:17]) (push) Waiting to run
Details
Run System Tests / Java ${{ matrix.java.version}} (map[toolchain:true version:21]) (push) Waiting to run
Details
* gh-45202: Polish "Use ClassLoader with ArchitectureCheck" Use ClassLoader with ArchitectureCheck Closes gh-45202
This commit is contained in:
commit
3aa352a2cd
|
|
@ -18,11 +18,15 @@ package org.springframework.boot.build.architecture;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
|
@ -33,11 +37,13 @@ import com.tngtech.archunit.lang.EvaluationResult;
|
|||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.Transformer;
|
||||
import org.gradle.api.file.ConfigurableFileCollection;
|
||||
import org.gradle.api.file.DirectoryProperty;
|
||||
import org.gradle.api.file.FileCollection;
|
||||
import org.gradle.api.file.FileTree;
|
||||
import org.gradle.api.provider.ListProperty;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.tasks.Classpath;
|
||||
import org.gradle.api.tasks.IgnoreEmptyDirectories;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.InputFiles;
|
||||
|
|
@ -58,6 +64,7 @@ import org.gradle.api.tasks.VerificationException;
|
|||
* @author Scott Frederick
|
||||
* @author Ivan Malutin
|
||||
* @author Phillip Webb
|
||||
* @author Dmytro Nosan
|
||||
*/
|
||||
public abstract class ArchitectureCheck extends DefaultTask {
|
||||
|
||||
|
|
@ -80,14 +87,17 @@ public abstract class ArchitectureCheck extends DefaultTask {
|
|||
}
|
||||
|
||||
@TaskAction
|
||||
void checkArchitecture() throws IOException {
|
||||
JavaClasses javaClasses = new ClassFileImporter().importPaths(classFilesPaths());
|
||||
List<EvaluationResult> violations = evaluate(javaClasses).filter(EvaluationResult::hasViolation).toList();
|
||||
File outputFile = getOutputDirectory().file("failure-report.txt").get().getAsFile();
|
||||
writeViolationReport(violations, outputFile);
|
||||
if (!violations.isEmpty()) {
|
||||
throw new VerificationException("Architecture check failed. See '" + outputFile + "' for details.");
|
||||
}
|
||||
void checkArchitecture() throws Exception {
|
||||
withCompileClasspath(() -> {
|
||||
JavaClasses javaClasses = new ClassFileImporter().importPaths(classFilesPaths());
|
||||
List<EvaluationResult> violations = evaluate(javaClasses).filter(EvaluationResult::hasViolation).toList();
|
||||
File outputFile = getOutputDirectory().file("failure-report.txt").get().getAsFile();
|
||||
writeViolationReport(violations, outputFile);
|
||||
if (!violations.isEmpty()) {
|
||||
throw new VerificationException("Architecture check failed. See '" + outputFile + "' for details.");
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private List<Path> classFilesPaths() {
|
||||
|
|
@ -98,6 +108,22 @@ public abstract class ArchitectureCheck extends DefaultTask {
|
|||
return getRules().get().stream().map((rule) -> rule.evaluate(javaClasses));
|
||||
}
|
||||
|
||||
private void withCompileClasspath(Callable<?> callable) throws Exception {
|
||||
ClassLoader previous = Thread.currentThread().getContextClassLoader();
|
||||
try {
|
||||
List<URL> urls = new ArrayList<>();
|
||||
for (File file : getCompileClasspath().getFiles()) {
|
||||
urls.add(file.toURI().toURL());
|
||||
}
|
||||
ClassLoader classLoader = new URLClassLoader(urls.toArray(new URL[0]), getClass().getClassLoader());
|
||||
Thread.currentThread().setContextClassLoader(classLoader);
|
||||
callable.call();
|
||||
}
|
||||
finally {
|
||||
Thread.currentThread().setContextClassLoader(previous);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeViolationReport(List<EvaluationResult> violations, File outputFile) throws IOException {
|
||||
outputFile.getParentFile().mkdirs();
|
||||
StringBuilder report = new StringBuilder();
|
||||
|
|
@ -126,6 +152,10 @@ public abstract class ArchitectureCheck extends DefaultTask {
|
|||
return this.classes.getAsFileTree();
|
||||
}
|
||||
|
||||
@InputFiles
|
||||
@Classpath
|
||||
public abstract ConfigurableFileCollection getCompileClasspath();
|
||||
|
||||
@Optional
|
||||
@InputFiles
|
||||
@PathSensitive(PathSensitivity.RELATIVE)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
* Copyright 2012-2025 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.
|
||||
|
|
@ -49,6 +49,7 @@ public class ArchitecturePlugin implements Plugin<Project> {
|
|||
TaskProvider<ArchitectureCheck> checkPackageTangles = project.getTasks()
|
||||
.register("checkArchitecture" + StringUtils.capitalize(sourceSet.getName()), ArchitectureCheck.class,
|
||||
(task) -> {
|
||||
task.getCompileClasspath().from(sourceSet.getCompileClasspath());
|
||||
task.setClasses(sourceSet.getOutput().getClassesDirs());
|
||||
task.getResourcesDirectory().set(sourceSet.getOutput().getResourcesDir());
|
||||
task.dependsOn(sourceSet.getProcessResourcesTaskName());
|
||||
|
|
|
|||
|
|
@ -16,22 +16,20 @@
|
|||
|
||||
package org.springframework.boot.build.architecture;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.gradle.api.GradleException;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.testfixtures.ProjectBuilder;
|
||||
import org.gradle.testkit.runner.GradleRunner;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.util.FileSystemUtils;
|
||||
import org.springframework.util.function.ThrowingConsumer;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Tests for {@link ArchitectureCheck}.
|
||||
|
|
@ -39,188 +37,206 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
|||
* @author Andy Wilkinson
|
||||
* @author Scott Frederick
|
||||
* @author Ivan Malutin
|
||||
* @author Dmytro Nosan
|
||||
*/
|
||||
class ArchitectureCheckTests {
|
||||
|
||||
@TempDir
|
||||
File temp;
|
||||
private Path projectDir;
|
||||
|
||||
@Test
|
||||
void whenPackagesAreTangledTaskFailsAndWritesAReport() throws Exception {
|
||||
prepareTask("tangled", (architectureCheck) -> {
|
||||
assertThatExceptionOfType(GradleException.class).isThrownBy(architectureCheck::checkArchitecture);
|
||||
assertThat(failureReport(architectureCheck)).isNotEmpty();
|
||||
});
|
||||
private Path buildFile;
|
||||
|
||||
@BeforeEach
|
||||
void setup(@TempDir Path projectDir) {
|
||||
this.projectDir = projectDir;
|
||||
this.buildFile = projectDir.resolve("build.gradle");
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenPackagesAreNotTangledTaskSucceedsAndWritesAnEmptyReport() throws Exception {
|
||||
prepareTask("untangled", (architectureCheck) -> {
|
||||
architectureCheck.checkArchitecture();
|
||||
assertThat(failureReport(architectureCheck)).isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
File failureReport(ArchitectureCheck architectureCheck) {
|
||||
return architectureCheck.getProject()
|
||||
.getLayout()
|
||||
.getBuildDirectory()
|
||||
.file("checkArchitecture/failure-report.txt")
|
||||
.get()
|
||||
.getAsFile();
|
||||
void whenPackagesAreTangledTaskFailsAndWritesAReport() throws IOException {
|
||||
runGradleWithCompiledClasses("tangled",
|
||||
shouldHaveFailureReportWithMessage("slices matching '(**)' should be free of cycles"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenBeanPostProcessorBeanMethodIsNotStaticTaskFailsAndWritesAReport() throws Exception {
|
||||
prepareTask("bpp/nonstatic", (architectureCheck) -> {
|
||||
assertThatExceptionOfType(GradleException.class).isThrownBy(architectureCheck::checkArchitecture);
|
||||
assertThat(failureReport(architectureCheck)).isNotEmpty();
|
||||
});
|
||||
void whenPackagesAreNotTangledTaskSucceedsAndWritesAnEmptyReport() throws IOException {
|
||||
runGradleWithCompiledClasses("untangled", shouldHaveEmptyFailureReport());
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenBeanPostProcessorBeanMethodIsStaticAndHasUnsafeParametersTaskFailsAndWritesAReport() throws Exception {
|
||||
prepareTask("bpp/unsafeparameters", (architectureCheck) -> {
|
||||
assertThatExceptionOfType(GradleException.class).isThrownBy(architectureCheck::checkArchitecture);
|
||||
assertThat(failureReport(architectureCheck)).isNotEmpty();
|
||||
});
|
||||
void whenBeanPostProcessorBeanMethodIsNotStaticTaskFailsAndWritesAReport() throws IOException {
|
||||
runGradleWithCompiledClasses("bpp/nonstatic",
|
||||
shouldHaveFailureReportWithMessage(
|
||||
"methods that are annotated with @Bean and have raw return type assignable "
|
||||
+ "to org.springframework.beans.factory.config.BeanPostProcessor"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenBeanPostProcessorBeanMethodIsStaticAndHasUnsafeParametersTaskFailsAndWritesAReport() throws IOException {
|
||||
runGradleWithCompiledClasses("bpp/unsafeparameters",
|
||||
shouldHaveFailureReportWithMessage(
|
||||
"methods that are annotated with @Bean and have raw return type assignable "
|
||||
+ "to org.springframework.beans.factory.config.BeanPostProcessor"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenBeanPostProcessorBeanMethodIsStaticAndHasSafeParametersTaskSucceedsAndWritesAnEmptyReport()
|
||||
throws Exception {
|
||||
prepareTask("bpp/safeparameters", (architectureCheck) -> {
|
||||
architectureCheck.checkArchitecture();
|
||||
assertThat(failureReport(architectureCheck)).isEmpty();
|
||||
});
|
||||
throws IOException {
|
||||
runGradleWithCompiledClasses("bpp/safeparameters", shouldHaveEmptyFailureReport());
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenBeanPostProcessorBeanMethodIsStaticAndHasNoParametersTaskSucceedsAndWritesAnEmptyReport()
|
||||
throws Exception {
|
||||
prepareTask("bpp/noparameters", (architectureCheck) -> {
|
||||
architectureCheck.checkArchitecture();
|
||||
assertThat(failureReport(architectureCheck)).isEmpty();
|
||||
});
|
||||
throws IOException {
|
||||
runGradleWithCompiledClasses("bpp/noparameters", shouldHaveEmptyFailureReport());
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenBeanFactoryPostProcessorBeanMethodIsNotStaticTaskFailsAndWritesAReport() throws Exception {
|
||||
prepareTask("bfpp/nonstatic", (architectureCheck) -> {
|
||||
assertThatExceptionOfType(GradleException.class).isThrownBy(architectureCheck::checkArchitecture);
|
||||
assertThat(failureReport(architectureCheck)).isNotEmpty();
|
||||
});
|
||||
void whenBeanFactoryPostProcessorBeanMethodIsNotStaticTaskFailsAndWritesAReport() throws IOException {
|
||||
runGradleWithCompiledClasses("bfpp/nonstatic",
|
||||
shouldHaveFailureReportWithMessage("methods that are annotated with @Bean and have raw return "
|
||||
+ "type assignable to org.springframework.beans.factory.config.BeanFactoryPostProcessor"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenBeanFactoryPostProcessorBeanMethodIsStaticAndHasParametersTaskFailsAndWritesAReport() throws Exception {
|
||||
prepareTask("bfpp/parameters", (architectureCheck) -> {
|
||||
assertThatExceptionOfType(GradleException.class).isThrownBy(architectureCheck::checkArchitecture);
|
||||
assertThat(failureReport(architectureCheck)).isNotEmpty();
|
||||
});
|
||||
void whenBeanFactoryPostProcessorBeanMethodIsStaticAndHasParametersTaskFailsAndWritesAReport() throws IOException {
|
||||
runGradleWithCompiledClasses("bfpp/parameters",
|
||||
shouldHaveFailureReportWithMessage("methods that are annotated with @Bean and have raw return "
|
||||
+ "type assignable to org.springframework.beans.factory.config.BeanFactoryPostProcessor"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenBeanFactoryPostProcessorBeanMethodIsStaticAndHasNoParametersTaskSucceedsAndWritesAnEmptyReport()
|
||||
throws Exception {
|
||||
prepareTask("bfpp/noparameters", (architectureCheck) -> {
|
||||
architectureCheck.checkArchitecture();
|
||||
assertThat(failureReport(architectureCheck)).isEmpty();
|
||||
});
|
||||
throws IOException {
|
||||
runGradleWithCompiledClasses("bfpp/noparameters", shouldHaveEmptyFailureReport());
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenClassLoadsResourceUsingResourceUtilsTaskFailsAndWritesReport() throws Exception {
|
||||
prepareTask("resources/loads", (architectureCheck) -> {
|
||||
assertThatExceptionOfType(GradleException.class).isThrownBy(architectureCheck::checkArchitecture);
|
||||
assertThat(failureReport(architectureCheck)).isNotEmpty();
|
||||
});
|
||||
void whenClassLoadsResourceUsingResourceUtilsTaskFailsAndWritesReport() throws IOException {
|
||||
runGradleWithCompiledClasses("resources/loads", shouldHaveFailureReportWithMessage(
|
||||
"no classes should call method where target owner type org.springframework.util.ResourceUtils and target name 'getURL'"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenClassUsesResourceUtilsWithoutLoadingResourcesTaskSucceedsAndWritesAnEmptyReport() throws Exception {
|
||||
prepareTask("resources/noloads", (architectureCheck) -> {
|
||||
architectureCheck.checkArchitecture();
|
||||
assertThat(failureReport(architectureCheck)).isEmpty();
|
||||
});
|
||||
void whenClassUsesResourceUtilsWithoutLoadingResourcesTaskSucceedsAndWritesAnEmptyReport() throws IOException {
|
||||
runGradleWithCompiledClasses("resources/noloads", shouldHaveEmptyFailureReport());
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenClassDoesNotCallObjectsRequireNonNullTaskSucceedsAndWritesAnEmptyReport() throws Exception {
|
||||
prepareTask("objects/noRequireNonNull", (architectureCheck) -> {
|
||||
architectureCheck.checkArchitecture();
|
||||
assertThat(failureReport(architectureCheck)).isEmpty();
|
||||
});
|
||||
void whenClassDoesNotCallObjectsRequireNonNullTaskSucceedsAndWritesAnEmptyReport() throws IOException {
|
||||
runGradleWithCompiledClasses("objects/noRequireNonNull", shouldHaveEmptyFailureReport());
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenClassCallsObjectsRequireNonNullWithMessageTaskFailsAndWritesReport() throws Exception {
|
||||
prepareTask("objects/requireNonNullWithString", (architectureCheck) -> {
|
||||
assertThatExceptionOfType(GradleException.class).isThrownBy(architectureCheck::checkArchitecture);
|
||||
assertThat(failureReport(architectureCheck)).isNotEmpty();
|
||||
});
|
||||
void whenClassCallsObjectsRequireNonNullWithMessageTaskFailsAndWritesReport() throws IOException {
|
||||
runGradleWithCompiledClasses("objects/requireNonNullWithString", shouldHaveFailureReportWithMessage(
|
||||
"no classes should call method Objects.requireNonNull(Object, String)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenClassCallsObjectsRequireNonNullWithSupplierTaskFailsAndWritesReport() throws Exception {
|
||||
prepareTask("objects/requireNonNullWithSupplier", (architectureCheck) -> {
|
||||
assertThatExceptionOfType(GradleException.class).isThrownBy(architectureCheck::checkArchitecture);
|
||||
assertThat(failureReport(architectureCheck)).isNotEmpty();
|
||||
});
|
||||
void whenClassCallsObjectsRequireNonNullWithSupplierTaskFailsAndWritesReport() throws IOException {
|
||||
runGradleWithCompiledClasses("objects/requireNonNullWithSupplier", shouldHaveFailureReportWithMessage(
|
||||
"no classes should call method Objects.requireNonNull(Object, Supplier)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenClassCallsStringToUpperCaseWithoutLocaleFailsAndWritesReport() throws Exception {
|
||||
prepareTask("string/toUpperCase", (architectureCheck) -> {
|
||||
assertThatExceptionOfType(GradleException.class).isThrownBy(architectureCheck::checkArchitecture);
|
||||
assertThat(failureReport(architectureCheck)).isNotEmpty()
|
||||
.content()
|
||||
.contains("because String.toUpperCase(Locale.ROOT) should be used instead");
|
||||
});
|
||||
void whenClassCallsStringToUpperCaseWithoutLocaleFailsAndWritesReport() throws IOException {
|
||||
runGradleWithCompiledClasses("string/toUpperCase",
|
||||
shouldHaveFailureReportWithMessage("because String.toUpperCase(Locale.ROOT) should be used instead"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenClassCallsStringToLowerCaseWithoutLocaleFailsAndWritesReport() throws Exception {
|
||||
prepareTask("string/toLowerCase", (architectureCheck) -> {
|
||||
assertThatExceptionOfType(GradleException.class).isThrownBy(architectureCheck::checkArchitecture);
|
||||
assertThat(failureReport(architectureCheck)).isNotEmpty()
|
||||
.content()
|
||||
.contains("because String.toLowerCase(Locale.ROOT) should be used instead");
|
||||
});
|
||||
void whenClassCallsStringToLowerCaseWithoutLocaleFailsAndWritesReport() throws IOException {
|
||||
runGradleWithCompiledClasses("string/toLowerCase",
|
||||
shouldHaveFailureReportWithMessage("because String.toLowerCase(Locale.ROOT) should be used instead"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenClassCallsStringToLowerCaseWithLocaleShouldNotFail() throws Exception {
|
||||
prepareTask("string/toLowerCaseWithLocale", (architectureCheck) -> {
|
||||
architectureCheck.checkArchitecture();
|
||||
assertThat(failureReport(architectureCheck)).isEmpty();
|
||||
});
|
||||
void whenClassCallsStringToLowerCaseWithLocaleShouldNotFail() throws IOException {
|
||||
runGradleWithCompiledClasses("string/toLowerCaseWithLocale", shouldHaveEmptyFailureReport());
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenClassCallsStringToUpperCaseWithLocaleShouldNotFail() throws Exception {
|
||||
prepareTask("string/toUpperCaseWithLocale", (architectureCheck) -> {
|
||||
architectureCheck.checkArchitecture();
|
||||
assertThat(failureReport(architectureCheck)).isEmpty();
|
||||
});
|
||||
void whenClassCallsStringToUpperCaseWithLocaleShouldNotFail() throws IOException {
|
||||
runGradleWithCompiledClasses("string/toUpperCaseWithLocale", shouldHaveEmptyFailureReport());
|
||||
}
|
||||
|
||||
private void prepareTask(String classes, ThrowingConsumer<ArchitectureCheck> callback) throws Exception {
|
||||
File projectDir = new File(this.temp, "project");
|
||||
projectDir.mkdirs();
|
||||
copyClasses(classes, projectDir);
|
||||
Project project = ProjectBuilder.builder().withProjectDir(projectDir).build();
|
||||
project.getTasks().register("checkArchitecture", ArchitectureCheck.class, (task) -> {
|
||||
task.setClasses(project.files("classes"));
|
||||
callback.accept(task);
|
||||
});
|
||||
@Test
|
||||
void whenBeanPostProcessorBeanMethodIsNotStaticWithExternalClass() throws IOException {
|
||||
Files.writeString(this.buildFile, """
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot.architecture'
|
||||
}
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
java {
|
||||
sourceCompatibility = 17
|
||||
}
|
||||
dependencies {
|
||||
implementation("org.springframework.integration:spring-integration-jmx:6.3.9")
|
||||
}
|
||||
""");
|
||||
Path testClass = this.projectDir.resolve("src/main/java/boot/architecture/bpp/external/TestClass.java");
|
||||
Files.createDirectories(testClass.getParent());
|
||||
Files.writeString(testClass, """
|
||||
package org.springframework.boot.build.architecture.bpp.external;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.integration.monitor.IntegrationMBeanExporter;
|
||||
public class TestClass {
|
||||
@Bean
|
||||
IntegrationMBeanExporter integrationMBeanExporter() {
|
||||
return new IntegrationMBeanExporter();
|
||||
}
|
||||
}
|
||||
""");
|
||||
runGradle(shouldHaveFailureReportWithMessage("methods that are annotated with @Bean and have raw return "
|
||||
+ "type assignable to org.springframework.beans.factory.config.BeanPostProcessor "));
|
||||
}
|
||||
|
||||
private void copyClasses(String name, File projectDir) throws IOException {
|
||||
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
|
||||
Resource root = resolver.getResource("classpath:org/springframework/boot/build/architecture/" + name);
|
||||
FileSystemUtils.copyRecursively(root.getFile(),
|
||||
new File(projectDir, "classes/org/springframework/boot/build/architecture/" + name));
|
||||
private Consumer<GradleRunner> shouldHaveEmptyFailureReport() {
|
||||
return (gradleRunner) -> {
|
||||
assertThat(gradleRunner.build().getOutput()).contains("BUILD SUCCESSFUL")
|
||||
.contains("Task :checkArchitectureMain");
|
||||
assertThat(failureReport()).isEmptyFile();
|
||||
};
|
||||
}
|
||||
|
||||
private Consumer<GradleRunner> shouldHaveFailureReportWithMessage(String message) {
|
||||
return (gradleRunner) -> {
|
||||
assertThat(gradleRunner.buildAndFail().getOutput()).contains("BUILD FAILED")
|
||||
.contains("Task :checkArchitectureMain FAILED");
|
||||
assertThat(failureReport()).content().contains(message);
|
||||
};
|
||||
}
|
||||
|
||||
private void runGradleWithCompiledClasses(String path, Consumer<GradleRunner> callback) throws IOException {
|
||||
ClassPathResource classPathResource = new ClassPathResource(path, getClass());
|
||||
FileSystemUtils.copyRecursively(classPathResource.getFile().toPath(),
|
||||
this.projectDir.resolve("classes").resolve(classPathResource.getPath()));
|
||||
Files.writeString(this.buildFile, """
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot.architecture'
|
||||
}
|
||||
sourceSets {
|
||||
main {
|
||||
output.classesDirs.setFrom(file("classes"))
|
||||
}
|
||||
}
|
||||
""");
|
||||
runGradle(callback);
|
||||
}
|
||||
|
||||
private void runGradle(Consumer<GradleRunner> callback) {
|
||||
callback.accept(GradleRunner.create()
|
||||
.withProjectDir(this.projectDir.toFile())
|
||||
.withArguments("checkArchitectureMain")
|
||||
.withPluginClasspath());
|
||||
}
|
||||
|
||||
private Path failureReport() {
|
||||
return this.projectDir.resolve("build/checkArchitectureMain/failure-report.txt");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ class FilterOrderingIntegrationTests {
|
|||
}
|
||||
|
||||
@Bean
|
||||
WebServerFactoryCustomizerBeanPostProcessor ServletWebServerCustomizerBeanPostProcessor() {
|
||||
static WebServerFactoryCustomizerBeanPostProcessor servletWebServerCustomizerBeanPostProcessor() {
|
||||
return new WebServerFactoryCustomizerBeanPostProcessor();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -211,12 +211,12 @@ class HttpEncodingAutoConfigurationTests {
|
|||
static class MinimalWebAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
MockServletWebServerFactory MockServletWebServerFactory() {
|
||||
MockServletWebServerFactory mockServletWebServerFactory() {
|
||||
return new MockServletWebServerFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
WebServerFactoryCustomizerBeanPostProcessor ServletWebServerCustomizerBeanPostProcessor() {
|
||||
static WebServerFactoryCustomizerBeanPostProcessor servletWebServerCustomizerBeanPostProcessor() {
|
||||
return new WebServerFactoryCustomizerBeanPostProcessor();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1098,7 +1098,7 @@ class WebMvcAutoConfigurationTests {
|
|||
}
|
||||
|
||||
@Bean
|
||||
WebServerFactoryCustomizerBeanPostProcessor ServletWebServerCustomizerBeanPostProcessor() {
|
||||
static WebServerFactoryCustomizerBeanPostProcessor servletWebServerCustomizerBeanPostProcessor() {
|
||||
return new WebServerFactoryCustomizerBeanPostProcessor();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
* Copyright 2012-2025 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.
|
||||
|
|
@ -183,7 +183,7 @@ class WebSocketServletAutoConfigurationTests {
|
|||
}
|
||||
|
||||
@Bean
|
||||
WebServerFactoryCustomizerBeanPostProcessor ServletWebServerCustomizerBeanPostProcessor() {
|
||||
static WebServerFactoryCustomizerBeanPostProcessor servletWebServerCustomizerBeanPostProcessor() {
|
||||
return new WebServerFactoryCustomizerBeanPostProcessor();
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue