diff --git a/spring-boot-project/spring-boot-actuator/pom.xml b/spring-boot-project/spring-boot-actuator/pom.xml index e667322d927..65878a5de92 100644 --- a/spring-boot-project/spring-boot-actuator/pom.xml +++ b/spring-boot-project/spring-boot-actuator/pom.xml @@ -345,6 +345,11 @@ jersey-media-json-jackson test + + org.awaitility + awaitility + test + com.jayway.jsonpath json-path diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/management/HeapDumpWebEndpointWebIntegrationTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/management/HeapDumpWebEndpointWebIntegrationTests.java index 07b95102f81..baaf6e1a18c 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/management/HeapDumpWebEndpointWebIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/management/HeapDumpWebEndpointWebIntegrationTests.java @@ -18,8 +18,10 @@ package org.springframework.boot.actuate.management; import java.io.File; import java.io.IOException; +import java.time.Duration; import java.util.concurrent.TimeUnit; +import org.awaitility.Awaitility; import org.junit.jupiter.api.BeforeEach; import org.springframework.boot.actuate.endpoint.web.test.WebEndpointTest; @@ -31,7 +33,7 @@ import org.springframework.http.MediaType; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.util.FileCopyUtils; -import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.is; /** * Integration tests for {@link HeapDumpWebEndpoint} exposed by Jersey, Spring MVC, and @@ -64,11 +66,7 @@ class HeapDumpWebEndpointWebIntegrationTests { } private void assertHeapDumpFileIsDeleted() throws InterruptedException { - long end = System.currentTimeMillis() + 5000; - while (System.currentTimeMillis() < end && this.endpoint.file.exists()) { - Thread.sleep(100); - } - assertThat(this.endpoint.file.exists()).isFalse(); + Awaitility.waitAtMost(Duration.ofSeconds(5)).until(this.endpoint.file::exists, is(false)); } @Configuration(proxyBeanMethods = false) diff --git a/spring-boot-project/spring-boot-autoconfigure/pom.xml b/spring-boot-project/spring-boot-autoconfigure/pom.xml index 1237d4dd153..9942914b50d 100755 --- a/spring-boot-project/spring-boot-autoconfigure/pom.xml +++ b/spring-boot-project/spring-boot-autoconfigure/pom.xml @@ -897,6 +897,11 @@ tomcat-embed-jasper test + + org.awaitility + awaitility + test + org.hsqldb hsqldb diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java index 4e0b4f7d001..266b281eef9 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java @@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.orm.jpa; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -27,6 +28,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Consumer; +import java.util.stream.Collectors; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; @@ -37,6 +39,7 @@ import javax.transaction.TransactionManager; import javax.transaction.UserTransaction; import com.zaxxer.hikari.HikariDataSource; +import org.awaitility.Awaitility; import org.hibernate.boot.model.naming.ImplicitNamingStrategy; import org.hibernate.boot.model.naming.PhysicalNamingStrategy; import org.hibernate.cfg.AvailableSettings; @@ -77,6 +80,7 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; +import static org.hamcrest.Matchers.hasSize; import static org.mockito.Mockito.mock; /** @@ -387,12 +391,8 @@ class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigurationTes assertThat(context).hasNotFailed(); EventCapturingApplicationListener listener = context .getBean(EventCapturingApplicationListener.class); - long end = System.currentTimeMillis() + 30000; - while ((System.currentTimeMillis() < end) && !dataSourceSchemaCreatedEventReceived(listener)) { - Thread.sleep(100); - } - assertThat(listener.events.stream().filter(DataSourceSchemaCreatedEvent.class::isInstance)) - .hasSize(1); + Awaitility.waitAtMost(Duration.ofSeconds(30)) + .until(() -> dataSourceSchemaCreatedEventsReceivedBy(listener), hasSize(1)); }); } @@ -408,13 +408,9 @@ class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigurationTes }); } - private boolean dataSourceSchemaCreatedEventReceived(EventCapturingApplicationListener listener) { - for (ApplicationEvent event : listener.events) { - if (event instanceof DataSourceSchemaCreatedEvent) { - return true; - } - } - return false; + private List dataSourceSchemaCreatedEventsReceivedBy(EventCapturingApplicationListener listener) { + return listener.events.stream().filter(DataSourceSchemaCreatedEvent.class::isInstance) + .collect(Collectors.toList()); } @Configuration(proxyBeanMethods = false) diff --git a/spring-boot-project/spring-boot-devtools/pom.xml b/spring-boot-project/spring-boot-devtools/pom.xml index 15b3df397c9..c1816165c17 100644 --- a/spring-boot-project/spring-boot-devtools/pom.xml +++ b/spring-boot-project/spring-boot-devtools/pom.xml @@ -130,6 +130,11 @@ HikariCP test + + org.awaitility + awaitility + test + org.springframework spring-webmvc diff --git a/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/livereload/LiveReloadServerTests.java b/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/livereload/LiveReloadServerTests.java index 7424351f755..268466edc48 100644 --- a/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/livereload/LiveReloadServerTests.java +++ b/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/livereload/LiveReloadServerTests.java @@ -20,12 +20,14 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URI; +import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.tomcat.websocket.WsWebSocketContainer; +import org.awaitility.Awaitility; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; @@ -43,6 +45,9 @@ import org.springframework.web.socket.client.standard.StandardWebSocketClient; import org.springframework.web.socket.handler.TextWebSocketHandler; import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; /** * Tests for {@link LiveReloadServer}. @@ -107,10 +112,7 @@ class LiveReloadServerTests { } private void awaitClosedException() throws InterruptedException { - long startTime = System.currentTimeMillis(); - while (this.server.getClosedExceptions().isEmpty() && System.currentTimeMillis() - startTime < 10000) { - Thread.sleep(100); - } + Awaitility.waitAtMost(Duration.ofSeconds(10)).until(this.server::getClosedExceptions, is(not(empty()))); } @Test diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-loader/pom.xml index 1e86875b6df..b7d34cec884 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/pom.xml @@ -21,14 +21,19 @@ true + + org.springframework.boot + spring-boot-test-support + test + ch.qos.logback logback-classic test - org.springframework - spring-webmvc + org.awaitility + awaitility test @@ -39,8 +44,8 @@ test - org.springframework.boot - spring-boot-test-support + org.springframework + spring-webmvc test diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/PropertiesLauncherTests.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/PropertiesLauncherTests.java index e0753dbac8c..26c1b006f3c 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/PropertiesLauncherTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/PropertiesLauncherTests.java @@ -21,6 +21,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -28,6 +29,7 @@ import java.util.jar.Attributes; import java.util.jar.Manifest; import org.assertj.core.api.Condition; +import org.awaitility.Awaitility; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -44,6 +46,7 @@ import org.springframework.test.util.ReflectionTestUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.hamcrest.Matchers.containsString; /** * Tests for {@link PropertiesLauncher}. @@ -339,14 +342,7 @@ class PropertiesLauncherTests { } private void waitFor(String value) throws Exception { - int count = 0; - boolean timeout = false; - while (!timeout && count < 100) { - count++; - Thread.sleep(50L); - timeout = this.output.toString().contains(value); - } - assertThat(timeout).as("Timed out waiting for (" + value + ")").isTrue(); + Awaitility.waitAtMost(Duration.ofSeconds(5)).until(this.output::toString, containsString(value)); } private Condition endingWith(String value) { diff --git a/spring-boot-project/spring-boot/pom.xml b/spring-boot-project/spring-boot/pom.xml index 33e2bc86321..90483e4be4b 100644 --- a/spring-boot-project/spring-boot/pom.xml +++ b/spring-boot-project/spring-boot/pom.xml @@ -421,6 +421,11 @@ httpasyncclient test + + org.awaitility + awaitility + test + org.firebirdsql.jdbc jaybird-jdk18 diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/UndertowReactiveWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/UndertowReactiveWebServerFactoryTests.java index 0a3ec122d87..dc237447d58 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/UndertowReactiveWebServerFactoryTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/UndertowReactiveWebServerFactoryTests.java @@ -23,6 +23,7 @@ import java.time.Duration; import java.util.Arrays; import io.undertow.Undertow; +import org.awaitility.Awaitility; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import org.mockito.InOrder; @@ -36,6 +37,7 @@ import org.springframework.web.reactive.function.client.WebClient; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.hamcrest.Matchers.is; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; @@ -124,11 +126,8 @@ class UndertowReactiveWebServerFactoryTests extends AbstractReactiveWebServerFac assertThat(accessLogDirectory.listFiles()).contains(accessLog); } - private void awaitFile(File file) throws InterruptedException { - long end = System.currentTimeMillis() + 10000; - while (!file.exists() && System.currentTimeMillis() < end) { - Thread.sleep(100); - } + private void awaitFile(File file) { + Awaitility.waitAtMost(Duration.ofSeconds(10)).until(file::exists, is(true)); } } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServerFactoryTests.java index 2023d32fd75..199ae783926 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServerFactoryTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServerFactoryTests.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.net.SocketException; import java.net.URISyntaxException; import java.nio.charset.Charset; +import java.time.Duration; import java.util.Arrays; import java.util.Locale; import java.util.Map; @@ -34,6 +35,7 @@ import io.undertow.Undertow.Builder; import io.undertow.servlet.api.DeploymentInfo; import io.undertow.servlet.api.ServletContainer; import org.apache.jasper.servlet.JspServlet; +import org.awaitility.Awaitility; import org.junit.jupiter.api.Test; import org.mockito.InOrder; @@ -49,6 +51,7 @@ import org.springframework.test.util.ReflectionTestUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIOException; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.hamcrest.Matchers.is; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; @@ -234,11 +237,8 @@ class UndertowServletWebServerFactoryTests extends AbstractServletWebServerFacto return null; // Undertow does not support JSPs } - private void awaitFile(File file) throws InterruptedException { - long end = System.currentTimeMillis() + 10000; - while (!file.exists() && System.currentTimeMillis() < end) { - Thread.sleep(100); - } + private void awaitFile(File file) { + Awaitility.waitAtMost(Duration.ofSeconds(10)).until(file::exists, is(true)); } private ServletContainer getServletContainerFromNewFactory() { diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-devtools-tests/pom.xml b/spring-boot-tests/spring-boot-integration-tests/spring-boot-devtools-tests/pom.xml index e6a48093f9a..1eae491ae05 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-devtools-tests/pom.xml +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-devtools-tests/pom.xml @@ -21,6 +21,10 @@ org.springframework.boot spring-boot-starter-web + + net.bytebuddy + byte-buddy + org.springframework.boot spring-boot-starter-test @@ -32,8 +36,9 @@ test - net.bytebuddy - byte-buddy + org.awaitility + awaitility + test diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-devtools-tests/src/test/java/org/springframework/boot/devtools/tests/AbstractDevToolsIntegrationTests.java b/spring-boot-tests/spring-boot-integration-tests/spring-boot-devtools-tests/src/test/java/org/springframework/boot/devtools/tests/AbstractDevToolsIntegrationTests.java new file mode 100644 index 00000000000..4c816949a4c --- /dev/null +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-devtools-tests/src/test/java/org/springframework/boot/devtools/tests/AbstractDevToolsIntegrationTests.java @@ -0,0 +1,114 @@ +/* + * Copyright 2012-2019 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.boot.devtools.tests; + +import java.io.File; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.description.annotation.AnnotationDescription; +import net.bytebuddy.description.modifier.Visibility; +import net.bytebuddy.dynamic.DynamicType; +import net.bytebuddy.implementation.FixedValue; +import org.awaitility.Awaitility; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.io.TempDir; + +import org.springframework.boot.testsupport.BuildOutput; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * Base class for DevTools integration tests. + * + * @author Andy Wilkinson + */ +abstract class AbstractDevToolsIntegrationTests { + + protected static final BuildOutput buildOutput = new BuildOutput(AbstractDevToolsIntegrationTests.class); + + protected final File serverPortFile = new File(buildOutput.getRootLocation(), "server.port"); + + @RegisterExtension + protected final JvmLauncher javaLauncher = new JvmLauncher(); + + @TempDir + protected static File temp; + + protected LaunchedApplication launchedApplication; + + protected void launchApplication(ApplicationLauncher applicationLauncher, String... args) throws Exception { + this.serverPortFile.delete(); + this.launchedApplication = applicationLauncher.launchApplication(this.javaLauncher, this.serverPortFile, args); + } + + @AfterEach + void stopApplication() throws InterruptedException { + this.launchedApplication.stop(); + } + + protected int awaitServerPort() throws Exception { + int port = Awaitility.waitAtMost(Duration.ofSeconds(30)) + .until(() -> new ApplicationState(this.serverPortFile, this.launchedApplication), + ApplicationState::hasServerPort) + .getServerPort(); + this.serverPortFile.delete(); + System.out.println("Got port " + port); + this.launchedApplication.restartRemote(port); + Thread.sleep(1000); + return port; + } + + protected ControllerBuilder controller(String name) { + return new ControllerBuilder(name, this.launchedApplication.getClassesDirectory()); + } + + protected static final class ControllerBuilder { + + private final List mappings = new ArrayList<>(); + + private final String name; + + private final File classesDirectory; + + protected ControllerBuilder(String name, File classesDirectory) { + this.name = name; + this.classesDirectory = classesDirectory; + } + + protected ControllerBuilder withRequestMapping(String mapping) { + this.mappings.add(mapping); + return this; + } + + protected void build() throws Exception { + DynamicType.Builder builder = new ByteBuddy().subclass(Object.class).name(this.name) + .annotateType(AnnotationDescription.Builder.ofType(RestController.class).build()); + for (String mapping : this.mappings) { + builder = builder.defineMethod(mapping, String.class, Visibility.PUBLIC) + .intercept(FixedValue.value(mapping)).annotateMethod(AnnotationDescription.Builder + .ofType(RequestMapping.class).defineArray("value", mapping).build()); + } + builder.make().saveIn(this.classesDirectory); + } + + } + +} diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-devtools-tests/src/test/java/org/springframework/boot/devtools/tests/ApplicationState.java b/spring-boot-tests/spring-boot-integration-tests/spring-boot-devtools-tests/src/test/java/org/springframework/boot/devtools/tests/ApplicationState.java new file mode 100644 index 00000000000..5e53ab962f1 --- /dev/null +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-devtools-tests/src/test/java/org/springframework/boot/devtools/tests/ApplicationState.java @@ -0,0 +1,63 @@ +/* + * Copyright 2012-2019 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.boot.devtools.tests; + +import java.io.File; + +import org.springframework.boot.devtools.tests.JvmLauncher.LaunchedJvm; + +/** + * State of an application. + * + * @author Andy Wilkinson + */ +final class ApplicationState { + + private final Integer serverPort; + + private final FileContents out; + + private final FileContents err; + + ApplicationState(File serverPortFile, LaunchedJvm jvm) { + this(serverPortFile, jvm.getStandardOut(), jvm.getStandardError()); + } + + ApplicationState(File serverPortFile, LaunchedApplication application) { + this(serverPortFile, application.getStandardOut(), application.getStandardError()); + } + + private ApplicationState(File serverPortFile, File out, File err) { + this.serverPort = new FileContents(serverPortFile).get(Integer::parseInt); + this.out = new FileContents(out); + this.err = new FileContents(err); + } + + boolean hasServerPort() { + return this.serverPort != null; + } + + int getServerPort() { + return this.serverPort; + } + + @Override + public String toString() { + return String.format("Application output:%n%s%n%s", this.out, this.err); + } + +} diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-devtools-tests/src/test/java/org/springframework/boot/devtools/tests/DevToolsIntegrationTests.java b/spring-boot-tests/spring-boot-integration-tests/spring-boot-devtools-tests/src/test/java/org/springframework/boot/devtools/tests/DevToolsIntegrationTests.java index 4264ef9bd72..43a2942eae9 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-devtools-tests/src/test/java/org/springframework/boot/devtools/tests/DevToolsIntegrationTests.java +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-devtools-tests/src/test/java/org/springframework/boot/devtools/tests/DevToolsIntegrationTests.java @@ -17,29 +17,13 @@ package org.springframework.boot.devtools.tests; import java.io.File; -import java.io.FileReader; import java.io.IOException; -import java.time.Duration; -import java.util.ArrayList; -import java.util.List; -import net.bytebuddy.ByteBuddy; -import net.bytebuddy.description.annotation.AnnotationDescription; -import net.bytebuddy.description.modifier.Visibility; -import net.bytebuddy.dynamic.DynamicType.Builder; -import net.bytebuddy.implementation.FixedValue; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.testsupport.BuildOutput; import org.springframework.http.HttpStatus; -import org.springframework.util.FileCopyUtils; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; import static org.assertj.core.api.Assertions.assertThat; @@ -48,29 +32,7 @@ import static org.assertj.core.api.Assertions.assertThat; * * @author Andy Wilkinson */ -public class DevToolsIntegrationTests { - - @TempDir - static File temp; - - private static final BuildOutput buildOutput = new BuildOutput(DevToolsIntegrationTests.class); - - private LaunchedApplication launchedApplication; - - private final File serverPortFile = new File(buildOutput.getRootLocation(), "server.port"); - - @RegisterExtension - final JvmLauncher javaLauncher = new JvmLauncher(); - - private void launchApplication(ApplicationLauncher applicationLauncher) throws Exception { - this.serverPortFile.delete(); - this.launchedApplication = applicationLauncher.launchApplication(this.javaLauncher, this.serverPortFile); - } - - @AfterEach - void stopApplication() throws InterruptedException { - this.launchedApplication.stop(); - } +public class DevToolsIntegrationTests extends AbstractDevToolsIntegrationTests { @ParameterizedTest(name = "{0}") @MethodSource("parameters") @@ -191,33 +153,6 @@ public class DevToolsIntegrationTests { .isEqualTo(HttpStatus.NOT_FOUND); } - private int awaitServerPort() throws Exception { - Duration timeToWait = Duration.ofSeconds(40); - long end = System.currentTimeMillis() + timeToWait.toMillis(); - System.out.println("Reading server port from '" + this.serverPortFile + "'"); - while (this.serverPortFile.length() == 0) { - if (System.currentTimeMillis() > end) { - throw new IllegalStateException(String.format( - "server.port file '" + this.serverPortFile + "' was not written within " + timeToWait.toMillis() - + "ms. Application output:%n%s%s", - FileCopyUtils.copyToString(new FileReader(this.launchedApplication.getStandardOut())), - FileCopyUtils.copyToString(new FileReader(this.launchedApplication.getStandardError())))); - } - Thread.sleep(100); - } - FileReader portReader = new FileReader(this.serverPortFile); - int port = Integer.valueOf(FileCopyUtils.copyToString(portReader)); - this.serverPortFile.delete(); - System.out.println("Got port " + port); - this.launchedApplication.restartRemote(port); - Thread.sleep(1000); - return port; - } - - private ControllerBuilder controller(String name) { - return new ControllerBuilder(name, this.launchedApplication.getClassesDirectory()); - } - static Object[] parameters() throws IOException { Directories directories = new Directories(buildOutput, temp); return new Object[] { new Object[] { new LocalApplicationLauncher(directories) }, @@ -225,35 +160,4 @@ public class DevToolsIntegrationTests { new Object[] { new JarFileRemoteApplicationLauncher(directories) } }; } - private static final class ControllerBuilder { - - private final List mappings = new ArrayList<>(); - - private final String name; - - private final File classesDirectory; - - private ControllerBuilder(String name, File classesDirectory) { - this.name = name; - this.classesDirectory = classesDirectory; - } - - ControllerBuilder withRequestMapping(String mapping) { - this.mappings.add(mapping); - return this; - } - - void build() throws Exception { - Builder builder = new ByteBuddy().subclass(Object.class).name(this.name) - .annotateType(AnnotationDescription.Builder.ofType(RestController.class).build()); - for (String mapping : this.mappings) { - builder = builder.defineMethod(mapping, String.class, Visibility.PUBLIC) - .intercept(FixedValue.value(mapping)).annotateMethod(AnnotationDescription.Builder - .ofType(RequestMapping.class).defineArray("value", mapping).build()); - } - builder.make().saveIn(this.classesDirectory); - } - - } - } diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-devtools-tests/src/test/java/org/springframework/boot/devtools/tests/DevToolsWithLazyInitializationIntegrationTests.java b/spring-boot-tests/spring-boot-integration-tests/spring-boot-devtools-tests/src/test/java/org/springframework/boot/devtools/tests/DevToolsWithLazyInitializationIntegrationTests.java index 8b307435f6b..5cdc3347e4f 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-devtools-tests/src/test/java/org/springframework/boot/devtools/tests/DevToolsWithLazyInitializationIntegrationTests.java +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-devtools-tests/src/test/java/org/springframework/boot/devtools/tests/DevToolsWithLazyInitializationIntegrationTests.java @@ -16,30 +16,13 @@ package org.springframework.boot.devtools.tests; -import java.io.File; -import java.io.FileReader; import java.io.IOException; -import java.time.Duration; -import java.util.ArrayList; -import java.util.List; -import net.bytebuddy.ByteBuddy; -import net.bytebuddy.description.annotation.AnnotationDescription; -import net.bytebuddy.description.modifier.Visibility; -import net.bytebuddy.dynamic.DynamicType; -import net.bytebuddy.implementation.FixedValue; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.testsupport.BuildOutput; import org.springframework.http.HttpStatus; -import org.springframework.util.FileCopyUtils; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; import static org.assertj.core.api.Assertions.assertThat; @@ -48,36 +31,13 @@ import static org.assertj.core.api.Assertions.assertThat; * * @author Madhura Bhave */ -public class DevToolsWithLazyInitializationIntegrationTests { - - @TempDir - static File temp; - - private static final BuildOutput buildOutput = new BuildOutput(DevToolsIntegrationTests.class); - - private LaunchedApplication launchedApplication; - - private final File serverPortFile = new File(buildOutput.getRootLocation(), "server.port"); - - @RegisterExtension - final JvmLauncher javaLauncher = new JvmLauncher(); - - private void launchApplication(ApplicationLauncher applicationLauncher) throws Exception { - this.serverPortFile.delete(); - this.launchedApplication = applicationLauncher.launchApplication(this.javaLauncher, this.serverPortFile, - "--spring.main.lazy-initialization=true"); - } - - @AfterEach - void stopApplication() throws InterruptedException { - this.launchedApplication.stop(); - } +public class DevToolsWithLazyInitializationIntegrationTests extends AbstractDevToolsIntegrationTests { @ParameterizedTest(name = "{0}") @MethodSource("parameters") public void addARequestMappingToAnExistingControllerWhenLazyInit(ApplicationLauncher applicationLauncher) throws Exception { - launchApplication(applicationLauncher); + launchApplication(applicationLauncher, "--spring.main.lazy-initialization=true"); TestRestTemplate template = new TestRestTemplate(); String urlBase = "http://localhost:" + awaitServerPort(); assertThat(template.getForObject(urlBase + "/one", String.class)).isEqualTo("one"); @@ -89,33 +49,6 @@ public class DevToolsWithLazyInitializationIntegrationTests { assertThat(template.getForObject(urlBase + "/two", String.class)).isEqualTo("two"); } - private int awaitServerPort() throws Exception { - Duration timeToWait = Duration.ofSeconds(40); - long end = System.currentTimeMillis() + timeToWait.toMillis(); - System.out.println("Reading server port from '" + this.serverPortFile + "'"); - while (this.serverPortFile.length() == 0) { - if (System.currentTimeMillis() > end) { - throw new IllegalStateException(String.format( - "server.port file '" + this.serverPortFile + "' was not written within " + timeToWait.toMillis() - + "ms. Application output:%n%s%s", - FileCopyUtils.copyToString(new FileReader(this.launchedApplication.getStandardOut())), - FileCopyUtils.copyToString(new FileReader(this.launchedApplication.getStandardError())))); - } - Thread.sleep(100); - } - FileReader portReader = new FileReader(this.serverPortFile); - int port = Integer.valueOf(FileCopyUtils.copyToString(portReader)); - this.serverPortFile.delete(); - System.out.println("Got port " + port); - this.launchedApplication.restartRemote(port); - Thread.sleep(1000); - return port; - } - - private ControllerBuilder controller(String name) { - return new ControllerBuilder(name, this.launchedApplication.getClassesDirectory()); - } - static Object[] parameters() throws IOException { Directories directories = new Directories(buildOutput, temp); return new Object[] { new Object[] { new LocalApplicationLauncher(directories) }, @@ -124,35 +57,4 @@ public class DevToolsWithLazyInitializationIntegrationTests { } - private static final class ControllerBuilder { - - private final List mappings = new ArrayList<>(); - - private final String name; - - private final File classesDirectory; - - private ControllerBuilder(String name, File classesDirectory) { - this.name = name; - this.classesDirectory = classesDirectory; - } - - ControllerBuilder withRequestMapping(String mapping) { - this.mappings.add(mapping); - return this; - } - - void build() throws Exception { - DynamicType.Builder builder = new ByteBuddy().subclass(Object.class).name(this.name) - .annotateType(AnnotationDescription.Builder.ofType(RestController.class).build()); - for (String mapping : this.mappings) { - builder = builder.defineMethod(mapping, String.class, Visibility.PUBLIC) - .intercept(FixedValue.value(mapping)).annotateMethod(AnnotationDescription.Builder - .ofType(RequestMapping.class).defineArray("value", mapping).build()); - } - builder.make().saveIn(this.classesDirectory); - } - - } - } diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-devtools-tests/src/test/java/org/springframework/boot/devtools/tests/FileContents.java b/spring-boot-tests/spring-boot-integration-tests/spring-boot-devtools-tests/src/test/java/org/springframework/boot/devtools/tests/FileContents.java new file mode 100644 index 00000000000..a4a0956f208 --- /dev/null +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-devtools-tests/src/test/java/org/springframework/boot/devtools/tests/FileContents.java @@ -0,0 +1,60 @@ +/* + * Copyright 2012-2019 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.boot.devtools.tests; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.function.Function; + +import org.springframework.util.FileCopyUtils; + +/** + * Provides access to the contents of a file. + * + * @author Andy Wilkinson + */ +class FileContents { + + private final File file; + + FileContents(File file) { + this.file = file; + } + + String get() { + return get(Function.identity()); + } + + T get(Function transformer) { + if ((!this.file.exists()) || this.file.length() == 0) { + return null; + } + try { + return transformer.apply(FileCopyUtils.copyToString(new FileReader(this.file))); + } + catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + @Override + public String toString() { + return get(); + } + +} diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-devtools-tests/src/test/java/org/springframework/boot/devtools/tests/RemoteApplicationLauncher.java b/spring-boot-tests/spring-boot-integration-tests/spring-boot-devtools-tests/src/test/java/org/springframework/boot/devtools/tests/RemoteApplicationLauncher.java index d6e4a0afe43..e4079d4e56e 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-devtools-tests/src/test/java/org/springframework/boot/devtools/tests/RemoteApplicationLauncher.java +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-devtools-tests/src/test/java/org/springframework/boot/devtools/tests/RemoteApplicationLauncher.java @@ -17,17 +17,20 @@ package org.springframework.boot.devtools.tests; import java.io.File; -import java.io.FileReader; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.function.BiFunction; +import org.awaitility.Awaitility; + import org.springframework.boot.devtools.RemoteSpringApplication; import org.springframework.boot.devtools.tests.JvmLauncher.LaunchedJvm; -import org.springframework.util.FileCopyUtils; import org.springframework.util.StringUtils; +import static org.hamcrest.Matchers.containsString; + /** * Base class for {@link ApplicationLauncher} implementations that use * {@link RemoteSpringApplication}. @@ -45,7 +48,7 @@ abstract class RemoteApplicationLauncher extends AbstractApplicationLauncher { LaunchedJvm applicationJvm = javaLauncher.launch("app", createApplicationClassPath(), "com.example.DevToolsTestApplication", serverPortFile.getAbsolutePath(), "--server.port=0", "--spring.devtools.remote.secret=secret"); - int port = awaitServerPort(applicationJvm.getStandardOut(), serverPortFile); + int port = awaitServerPort(applicationJvm, serverPortFile); BiFunction remoteRestarter = getRemoteRestarter(javaLauncher); return new LaunchedApplication(getDirectories().getRemoteAppDirectory(), applicationJvm.getStandardOut(), applicationJvm.getStandardError(), applicationJvm.getProcess(), remoteRestarter.apply(port, null), @@ -60,7 +63,7 @@ abstract class RemoteApplicationLauncher extends AbstractApplicationLauncher { args.addAll(Arrays.asList(additionalArgs)); LaunchedJvm applicationJvm = javaLauncher.launch("app", createApplicationClassPath(), args.toArray(new String[] {})); - int port = awaitServerPort(applicationJvm.getStandardOut(), serverPortFile); + int port = awaitServerPort(applicationJvm, serverPortFile); BiFunction remoteRestarter = getRemoteRestarter(javaLauncher); return new LaunchedApplication(getDirectories().getRemoteAppDirectory(), applicationJvm.getStandardOut(), applicationJvm.getStandardError(), applicationJvm.getProcess(), remoteRestarter.apply(port, null), @@ -96,35 +99,16 @@ abstract class RemoteApplicationLauncher extends AbstractApplicationLauncher { return StringUtils.collectionToDelimitedString(entries, File.pathSeparator); } - private int awaitServerPort(File standardOut, File serverPortFile) throws Exception { - long end = System.currentTimeMillis() + 30000; - while (serverPortFile.length() == 0) { - if (System.currentTimeMillis() > end) { - throw new IllegalStateException( - String.format("server.port file was not written within 30 seconds. Application output:%n%s", - FileCopyUtils.copyToString(new FileReader(standardOut)))); - } - Thread.sleep(100); - } - FileReader portReader = new FileReader(serverPortFile); - int port = Integer.valueOf(FileCopyUtils.copyToString(portReader)); - return port; + private int awaitServerPort(LaunchedJvm jvm, File serverPortFile) throws Exception { + return Awaitility.waitAtMost(Duration.ofSeconds(30)) + .until(() -> new ApplicationState(serverPortFile, jvm), ApplicationState::hasServerPort) + .getServerPort(); } private void awaitRemoteSpringApplication(File standardOut) throws Exception { - long end = System.currentTimeMillis() + 30000; - while (!standardOut.exists()) { - if (System.currentTimeMillis() > end) { - throw new IllegalStateException("Standard out file was not written within 30 seconds"); - } - Thread.sleep(100); - } - while (!FileCopyUtils.copyToString(new FileReader(standardOut)).contains("Started RemoteSpringApplication")) { - if (System.currentTimeMillis() > end) { - throw new IllegalStateException("RemoteSpringApplication did not start within 30 seconds"); - } - Thread.sleep(100); - } + FileContents contents = new FileContents(standardOut); + Awaitility.waitAtMost(Duration.ofSeconds(30)).until(contents::get, + containsString("Started RemoteSpringApplication")); } } diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/pom.xml b/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/pom.xml index b0e2bb8b831..95ddd77d9c5 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/pom.xml +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/pom.xml @@ -45,6 +45,11 @@ 3.0.0 test + + org.awaitility + awaitility + test + org.eclipse.jetty jetty-webapp 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 89c446882af..42caaae4d62 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 @@ -21,9 +21,11 @@ import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; +import java.time.Duration; 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; @@ -93,16 +95,12 @@ abstract class AbstractApplicationLauncher implements BeforeEachCallback, AfterE } private int awaitServerPort(Process process, File serverPortFile) throws Exception { - long end = System.currentTimeMillis() + 30000; - while (serverPortFile.length() == 0) { - if (System.currentTimeMillis() > end) { - throw new IllegalStateException("server.port file was not written within 30 seconds"); - } + Awaitility.waitAtMost(Duration.ofSeconds(30)).until(serverPortFile::length, (length) -> { if (!process.isAlive()) { - throw new IllegalStateException("Application failed to launch"); + throw new IllegalStateException("Application failed to start"); } - Thread.sleep(100); - } + return length > 0; + }); return Integer.parseInt(FileCopyUtils.copyToString(new FileReader(serverPortFile))); } diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-integration/pom.xml b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-integration/pom.xml index 5dc33e6206c..9c2026d12b6 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-integration/pom.xml +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-integration/pom.xml @@ -35,6 +35,11 @@ spring-boot-starter-test test + + org.awaitility + awaitility + test + diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-integration/src/test/java/smoketest/integration/consumer/SampleIntegrationApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-integration/src/test/java/smoketest/integration/consumer/SampleIntegrationApplicationTests.java index bb72a087119..192732281c4 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-integration/src/test/java/smoketest/integration/consumer/SampleIntegrationApplicationTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-integration/src/test/java/smoketest/integration/consumer/SampleIntegrationApplicationTests.java @@ -20,11 +20,9 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.file.Path; -import java.util.concurrent.Callable; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; +import java.time.Duration; +import org.awaitility.Awaitility; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -39,7 +37,7 @@ import org.springframework.core.io.Resource; import org.springframework.core.io.support.ResourcePatternUtils; import org.springframework.util.StreamUtils; -import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.containsString; /** * Basic integration tests for service demo application. @@ -66,8 +64,7 @@ class SampleIntegrationApplicationTests { "--service.output-dir=" + outputDir); SpringApplication.run(ProducerApplication.class, "World", "--service.input-dir=" + inputDir, "--service.output-dir=" + outputDir); - String output = getOutput(outputDir); - assertThat(output).contains("Hello World"); + awaitOutputContaining(outputDir, "Hello World"); } @Test @@ -76,41 +73,35 @@ class SampleIntegrationApplicationTests { File outputDir = new File(temp.toFile(), "output"); this.context = SpringApplication.run(SampleIntegrationApplication.class, "testviamg", "--service.input-dir=" + inputDir, "--service.output-dir=" + outputDir); - String output = getOutput(this.context.getBean(ServiceProperties.class).getOutputDir()); - assertThat(output).contains("testviamg"); + awaitOutputContaining(this.context.getBean(ServiceProperties.class).getOutputDir(), "testviamg"); } - private String getOutput(File outputDir) throws Exception { - Future future = Executors.newSingleThreadExecutor().submit(new Callable() { - @Override - public String call() throws Exception { - Resource[] resources = getResourcesWithContent(outputDir); - while (resources.length == 0) { - Thread.sleep(200); - resources = getResourcesWithContent(outputDir); - } - StringBuilder builder = new StringBuilder(); - for (Resource resource : resources) { - try (InputStream inputStream = resource.getInputStream()) { - builder.append(new String(StreamUtils.copyToByteArray(inputStream))); - } - } - return builder.toString(); - } - }); - return future.get(30, TimeUnit.SECONDS); + private void awaitOutputContaining(File outputDir, String requiredContents) throws Exception { + Awaitility.waitAtMost(Duration.ofSeconds(30)).until(() -> outputIn(outputDir), + containsString(requiredContents)); } - private Resource[] getResourcesWithContent(File outputDir) throws IOException { - Resource[] candidates = ResourcePatternUtils.getResourcePatternResolver(new DefaultResourceLoader()) - .getResources("file:" + outputDir.getAbsolutePath() + "/**"); - for (Resource candidate : candidates) { - if ((candidate.getFilename() != null && candidate.getFilename().endsWith(".writing")) - || candidate.contentLength() == 0) { - return new Resource[0]; + private String outputIn(File outputDir) throws IOException { + Resource[] resources = findResources(outputDir); + if (resources.length == 0) { + return null; + } + return readResources(resources); + } + + private Resource[] findResources(File outputDir) throws IOException { + return ResourcePatternUtils.getResourcePatternResolver(new DefaultResourceLoader()) + .getResources("file:" + outputDir.getAbsolutePath() + "/*.txt"); + } + + private String readResources(Resource[] resources) throws IOException { + StringBuilder builder = new StringBuilder(); + for (Resource resource : resources) { + try (InputStream input = resource.getInputStream()) { + builder.append(new String(StreamUtils.copyToByteArray(input))); } } - return candidates; + return builder.toString(); } } diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-kafka/pom.xml b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-kafka/pom.xml index abf2bd1d70c..2b0b0fd109a 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-kafka/pom.xml +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-kafka/pom.xml @@ -29,6 +29,11 @@ spring-boot-starter-test test + + org.awaitility + awaitility + test + org.springframework.kafka spring-kafka-test diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-kafka/src/test/java/smoketest/kafka/SampleKafkaApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-kafka/src/test/java/smoketest/kafka/SampleKafkaApplicationTests.java index 574573b14ef..d9dcd729df0 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-kafka/src/test/java/smoketest/kafka/SampleKafkaApplicationTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-kafka/src/test/java/smoketest/kafka/SampleKafkaApplicationTests.java @@ -15,6 +15,9 @@ */ package smoketest.kafka; +import java.time.Duration; + +import org.awaitility.Awaitility; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -22,6 +25,8 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.kafka.test.context.EmbeddedKafka; import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.not; /** * Integration tests for demo application. @@ -39,10 +44,7 @@ class SampleKafkaApplicationTests { @Test void testVanillaExchange() throws Exception { - long end = System.currentTimeMillis() + 10000; - while (this.consumer.getMessages().isEmpty() && System.currentTimeMillis() < end) { - Thread.sleep(250); - } + Awaitility.waitAtMost(Duration.ofSeconds(30)).until(this.consumer::getMessages, not(empty())); assertThat(this.consumer.getMessages()).extracting("message").containsOnly("A simple test message"); } diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-parent-context/pom.xml b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-parent-context/pom.xml index 01980157ed9..970261a078c 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-parent-context/pom.xml +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-parent-context/pom.xml @@ -34,6 +34,11 @@ spring-boot-starter-test test + + org.awaitility + awaitility + test + diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-parent-context/src/test/java/smoketest/parent/consumer/SampleIntegrationParentApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-parent-context/src/test/java/smoketest/parent/consumer/SampleIntegrationParentApplicationTests.java index 7c6eebfe146..00557f959d4 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-parent-context/src/test/java/smoketest/parent/consumer/SampleIntegrationParentApplicationTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-parent-context/src/test/java/smoketest/parent/consumer/SampleIntegrationParentApplicationTests.java @@ -20,7 +20,9 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.file.Path; +import java.time.Duration; +import org.awaitility.Awaitility; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import smoketest.parent.SampleParentContextApplication; @@ -33,7 +35,7 @@ import org.springframework.core.io.Resource; import org.springframework.core.io.support.ResourcePatternUtils; import org.springframework.util.StreamUtils; -import static org.junit.jupiter.api.Assertions.fail; +import static org.hamcrest.Matchers.containsString; /** * Basic integration tests for service demo application. @@ -65,26 +67,16 @@ class SampleIntegrationParentApplicationTests { } private void awaitOutputContaining(File outputDir, String requiredContents) throws Exception { - long endTime = System.currentTimeMillis() + 30000; - String output = null; - while (System.currentTimeMillis() < endTime) { - Resource[] resources = findResources(outputDir); - if (resources.length == 0) { - Thread.sleep(200); - resources = findResources(outputDir); - } - else { - output = readResources(resources); - if (output != null && output.contains(requiredContents)) { - return; - } - else { - Thread.sleep(200); - output = readResources(resources); - } - } + Awaitility.waitAtMost(Duration.ofSeconds(30)).until(() -> outputIn(outputDir), + containsString(requiredContents)); + } + + private String outputIn(File outputDir) throws IOException { + Resource[] resources = findResources(outputDir); + if (resources.length == 0) { + return null; } - fail("Timed out awaiting output containing '" + requiredContents + "'. Output was '" + output + "'"); + return readResources(resources); } private Resource[] findResources(File outputDir) throws IOException { diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-quartz/pom.xml b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-quartz/pom.xml index 01241bd3818..a31b639a035 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-quartz/pom.xml +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-quartz/pom.xml @@ -35,6 +35,11 @@ spring-boot-starter-test test + + org.awaitility + awaitility + test + diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-quartz/src/test/java/smoketest/quartz/SampleQuartzApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-quartz/src/test/java/smoketest/quartz/SampleQuartzApplicationTests.java index 605e04e59f6..dd0a9740d42 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-quartz/src/test/java/smoketest/quartz/SampleQuartzApplicationTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-quartz/src/test/java/smoketest/quartz/SampleQuartzApplicationTests.java @@ -16,6 +16,9 @@ package smoketest.quartz; +import java.time.Duration; + +import org.awaitility.Awaitility; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -24,7 +27,7 @@ import org.springframework.boot.test.system.CapturedOutput; import org.springframework.boot.test.system.OutputCaptureExtension; import org.springframework.context.ConfigurableApplicationContext; -import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.containsString; /** * Tests for {@link SampleQuartzApplication}. @@ -37,11 +40,7 @@ class SampleQuartzApplicationTests { @Test void quartzJobIsTriggered(CapturedOutput output) throws InterruptedException { try (ConfigurableApplicationContext context = SpringApplication.run(SampleQuartzApplication.class)) { - long end = System.currentTimeMillis() + 5000; - while ((!output.toString().contains("Hello World!")) && System.currentTimeMillis() < end) { - Thread.sleep(100); - } - assertThat(output).contains("Hello World!"); + Awaitility.waitAtMost(Duration.ofSeconds(5)).until(output::toString, containsString("Hello World!")); } }