From b0a6e92fb6e38c721bd8a68736d7660440f157ac Mon Sep 17 00:00:00 2001 From: dreis2211 Date: Sat, 27 Feb 2021 08:04:17 +0100 Subject: [PATCH] Speed up spring-boot-server-tests See gh-25457 --- .../embedded/AbstractApplicationLauncher.java | 18 ++++---- ...verContainerInvocationContextProvider.java | 44 ++++++++++++++++--- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/src/test/java/org/springframework/boot/context/embedded/AbstractApplicationLauncher.java b/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/src/test/java/org/springframework/boot/context/embedded/AbstractApplicationLauncher.java index 90b3cf41156..508e9452d9b 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/src/test/java/org/springframework/boot/context/embedded/AbstractApplicationLauncher.java +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/src/test/java/org/springframework/boot/context/embedded/AbstractApplicationLauncher.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -26,7 +26,6 @@ import java.util.ArrayList; import java.util.List; import org.awaitility.Awaitility; -import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; @@ -40,7 +39,7 @@ import org.springframework.util.StringUtils; * * @author Andy Wilkinson */ -abstract class AbstractApplicationLauncher implements BeforeEachCallback, AfterEachCallback { +abstract class AbstractApplicationLauncher implements BeforeEachCallback { private final ApplicationBuilder applicationBuilder; @@ -56,15 +55,16 @@ abstract class AbstractApplicationLauncher implements BeforeEachCallback, AfterE } @Override - public void afterEach(ExtensionContext context) throws Exception { - if (this.process != null) { - this.process.destroy(); + public void beforeEach(ExtensionContext context) throws Exception { + if (this.process == null) { + this.process = startApplication(); } } - @Override - public void beforeEach(ExtensionContext context) throws Exception { - this.process = startApplication(); + void destroyProcess() { + if (this.process != null) { + this.process.destroy(); + } } final int getHttpPort() { diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/src/test/java/org/springframework/boot/context/embedded/EmbeddedServerContainerInvocationContextProvider.java b/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/src/test/java/org/springframework/boot/context/embedded/EmbeddedServerContainerInvocationContextProvider.java index d65293581c7..4b8692f2d3b 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/src/test/java/org/springframework/boot/context/embedded/EmbeddedServerContainerInvocationContextProvider.java +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/src/test/java/org/springframework/boot/context/embedded/EmbeddedServerContainerInvocationContextProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -21,6 +21,7 @@ import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -62,6 +63,10 @@ class EmbeddedServerContainerInvocationContextProvider private static final BuildOutput buildOutput = new BuildOutput( EmbeddedServerContainerInvocationContextProvider.class); + private final Map builderCache = new HashMap<>(); + + private final Map launcherCache = new HashMap<>(); + private final Path tempDir; EmbeddedServerContainerInvocationContextProvider() throws IOException { @@ -77,14 +82,13 @@ class EmbeddedServerContainerInvocationContextProvider public Stream provideTestTemplateInvocationContexts(ExtensionContext context) { EmbeddedServletContainerTest annotation = context.getRequiredTestClass() .getAnnotation(EmbeddedServletContainerTest.class); - return CONTAINERS.stream() - .map((container) -> new ApplicationBuilder(this.tempDir, annotation.packaging(), - container)) + return CONTAINERS + .stream().map( + (container) -> getApplicationBuilder(annotation, container)) .flatMap( (builder) -> Stream .of(annotation.launchers()).map( - (launcherClass) -> ReflectionUtils.newInstance(launcherClass, builder, - buildOutput)) + (launcherClass) -> getAbstractApplicationLauncher(builder, launcherClass)) .map((launcher) -> new EmbeddedServletContainerInvocationContext( StringUtils.capitalize(builder.getContainer()) + ": " + launcher.getDescription(builder.getPackaging()), @@ -94,6 +98,34 @@ class EmbeddedServerContainerInvocationContextProvider @Override public void afterAll(ExtensionContext context) throws Exception { FileSystemUtils.deleteRecursively(this.tempDir); + cleanupCaches(); + } + + private void cleanupCaches() { + this.launcherCache.values().forEach(AbstractApplicationLauncher::destroyProcess); + this.launcherCache.clear(); + this.builderCache.clear(); + } + + private AbstractApplicationLauncher getAbstractApplicationLauncher(ApplicationBuilder builder, + Class launcherClass) { + String cacheKey = builder.getContainer() + ":" + builder.getPackaging() + ":" + launcherClass.getName(); + if (this.launcherCache.containsKey(cacheKey)) { + return this.launcherCache.get(cacheKey); + } + AbstractApplicationLauncher launcher = ReflectionUtils.newInstance(launcherClass, builder, buildOutput); + this.launcherCache.put(cacheKey, launcher); + return launcher; + } + + private ApplicationBuilder getApplicationBuilder(EmbeddedServletContainerTest annotation, String container) { + String cacheKey = container + ":" + annotation.packaging(); + if (this.builderCache.containsKey(cacheKey)) { + return this.builderCache.get(cacheKey); + } + ApplicationBuilder builder = new ApplicationBuilder(this.tempDir, annotation.packaging(), container); + this.builderCache.put(cacheKey, builder); + return builder; } static class EmbeddedServletContainerInvocationContext implements TestTemplateInvocationContext, ParameterResolver {