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