Disable DevTools' post-processors and auto-config when running tests

Closes gh-5307
pull/16593/head
Madhura Bhave 6 years ago
parent b164b16c21
commit ac2b0093c7

@ -0,0 +1,68 @@
/*
* 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;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* Utility to deduce if Devtools should be enabled in the current context.
*
* @author Madhura Bhave
*/
public final class DevtoolsEnablementDeducer {
private static final Set<String> SKIPPED_STACK_ELEMENTS;
static {
Set<String> skipped = new LinkedHashSet<>();
skipped.add("org.junit.runners.");
skipped.add("org.junit.platform.");
skipped.add("org.springframework.boot.test.");
skipped.add("cucumber.runtime.");
SKIPPED_STACK_ELEMENTS = Collections.unmodifiableSet(skipped);
}
private DevtoolsEnablementDeducer() {
}
/**
* Checks if a specific {@link StackTraceElement} in the current thread's stacktrace
* should cause devtools to be disabled.
* @param thread the current thread
* @return {@code true} if devtools should be enabled skipped
*/
public static boolean shouldEnable(Thread thread) {
for (StackTraceElement element : thread.getStackTrace()) {
if (isSkippedStackElement(element)) {
return false;
}
}
return true;
}
private static boolean isSkippedStackElement(StackTraceElement element) {
for (String skipped : SKIPPED_STACK_ELEMENTS) {
if (element.getClassName().startsWith(skipped)) {
return true;
}
}
return false;
}
}

@ -55,7 +55,7 @@ import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
* @since 1.3.3
*/
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
@Conditional(DevToolsDataSourceCondition.class)
@Conditional({ OnEnabledDevtoolsCondition.class, DevToolsDataSourceCondition.class })
@Configuration(proxyBeanMethods = false)
public class DevToolsDataSourceAutoConfiguration {

@ -0,0 +1,46 @@
/*
* 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.autoconfigure;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.devtools.DevtoolsEnablementDeducer;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* A condition that checks if DevTools should be enabled.
*
* @author Madhura Bhave
*/
public class OnEnabledDevtoolsCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage.forCondition("Devtools");
boolean shouldEnable = DevtoolsEnablementDeducer
.shouldEnable(Thread.currentThread());
if (!shouldEnable) {
return ConditionOutcome.noMatch(
message.because("devtools is disabled for current context."));
}
return ConditionOutcome.match(message.because("devtools enabled."));
}
}

@ -43,6 +43,7 @@ import org.springframework.boot.devtools.restart.server.HttpRestartServer;
import org.springframework.boot.devtools.restart.server.HttpRestartServerHandler;
import org.springframework.boot.devtools.restart.server.SourceFolderUrlFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.server.ServerHttpRequest;
@ -55,6 +56,7 @@ import org.springframework.http.server.ServerHttpRequest;
* @since 1.3.0
*/
@Configuration(proxyBeanMethods = false)
@Conditional(OnEnabledDevtoolsCondition.class)
@ConditionalOnProperty(prefix = "spring.devtools.remote", name = "secret")
@ConditionalOnClass({ Filter.class, ServerHttpRequest.class })
@EnableConfigurationProperties({ ServerProperties.class, DevToolsProperties.class })

@ -21,6 +21,7 @@ import java.io.IOException;
import java.util.Properties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.devtools.DevtoolsEnablementDeducer;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertiesPropertySource;
@ -43,6 +44,7 @@ public class DevToolsHomePropertiesPostProcessor implements EnvironmentPostProce
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
if (DevtoolsEnablementDeducer.shouldEnable(Thread.currentThread())) {
File home = getHomeFolder();
File propertyFile = (home != null) ? new File(home, FILE_NAME) : null;
if (propertyFile != null && propertyFile.exists() && propertyFile.isFile()) {
@ -58,6 +60,7 @@ public class DevToolsHomePropertiesPostProcessor implements EnvironmentPostProce
}
}
}
}
protected File getHomeFolder() {
String home = System.getProperty("user.home");

@ -23,6 +23,7 @@ import java.util.Map;
import org.apache.commons.logging.Log;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.devtools.DevtoolsEnablementDeducer;
import org.springframework.boot.devtools.logger.DevToolsLogFactory;
import org.springframework.boot.devtools.restart.Restarter;
import org.springframework.boot.env.EnvironmentPostProcessor;
@ -79,7 +80,8 @@ public class DevToolsPropertyDefaultsPostProcessor implements EnvironmentPostPro
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
if (isLocalApplication(environment)) {
if (DevtoolsEnablementDeducer.shouldEnable(Thread.currentThread())
&& isLocalApplication(environment)) {
if (canAddProperties(environment)) {
logger.info("Devtools property defaults active! Set '" + ENABLED
+ "' to 'false' to disable");

@ -17,9 +17,8 @@
package org.springframework.boot.devtools.restart;
import java.net.URL;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import org.springframework.boot.devtools.DevtoolsEnablementDeducer;
/**
* Default {@link RestartInitializer} that only enable initial restart when running a
@ -32,27 +31,14 @@ import java.util.Set;
*/
public class DefaultRestartInitializer implements RestartInitializer {
private static final Set<String> SKIPPED_STACK_ELEMENTS;
static {
Set<String> skipped = new LinkedHashSet<>();
skipped.add("org.junit.runners.");
skipped.add("org.junit.platform.");
skipped.add("org.springframework.boot.test.");
skipped.add("cucumber.runtime.");
SKIPPED_STACK_ELEMENTS = Collections.unmodifiableSet(skipped);
}
@Override
public URL[] getInitialUrls(Thread thread) {
if (!isMain(thread)) {
return null;
}
for (StackTraceElement element : thread.getStackTrace()) {
if (isSkippedStackElement(element)) {
if (!DevtoolsEnablementDeducer.shouldEnable(thread)) {
return null;
}
}
return getUrls(thread);
}
@ -67,22 +53,6 @@ public class DefaultRestartInitializer implements RestartInitializer {
.getClass().getName().contains("AppClassLoader");
}
/**
* Checks if a specific {@link StackTraceElement} should cause the initializer to be
* skipped.
* @param element the stack element to check
* @return {@code true} if the stack element means that the initializer should be
* skipped
*/
private boolean isSkippedStackElement(StackTraceElement element) {
for (String skipped : SKIPPED_STACK_ELEMENTS) {
if (element.getClassName().startsWith(skipped)) {
return true;
}
}
return false;
}
/**
* Return the URLs that should be used with initialization.
* @param thread the source thread

@ -20,6 +20,9 @@ import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collection;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import javax.sql.DataSource;
@ -49,18 +52,18 @@ import static org.mockito.Mockito.verify;
public abstract class AbstractDevToolsDataSourceAutoConfigurationTests {
@Test
public void singleManuallyConfiguredDataSourceIsNotClosed() throws SQLException {
ConfigurableApplicationContext context = createContext(
SingleDataSourceConfiguration.class);
public void singleManuallyConfiguredDataSourceIsNotClosed() throws Exception {
ConfigurableApplicationContext context = getContext(
() -> createContext(SingleDataSourceConfiguration.class));
DataSource dataSource = context.getBean(DataSource.class);
Statement statement = configureDataSourceBehavior(dataSource);
verify(statement, never()).execute("SHUTDOWN");
}
@Test
public void multipleDataSourcesAreIgnored() throws SQLException {
ConfigurableApplicationContext context = createContext(
MultipleDataSourcesConfiguration.class);
public void multipleDataSourcesAreIgnored() throws Exception {
ConfigurableApplicationContext context = getContext(
() -> createContext(MultipleDataSourcesConfiguration.class));
Collection<DataSource> dataSources = context.getBeansOfType(DataSource.class)
.values();
for (DataSource dataSource : dataSources) {
@ -90,6 +93,20 @@ public abstract class AbstractDevToolsDataSourceAutoConfigurationTests {
return statement;
}
protected ConfigurableApplicationContext getContext(
Supplier<ConfigurableApplicationContext> supplier) throws Exception {
CountDownLatch latch = new CountDownLatch(1);
AtomicReference<ConfigurableApplicationContext> atomicReference = new AtomicReference<>();
Thread thread = new Thread(() -> {
ConfigurableApplicationContext context = supplier.get();
latch.countDown();
atomicReference.getAndSet(context);
});
thread.start();
thread.join();
return atomicReference.get();
}
protected final ConfigurableApplicationContext createContext(Class<?>... classes) {
return this.createContext(null, classes);
}

@ -17,7 +17,6 @@
package org.springframework.boot.devtools.autoconfigure;
import java.io.IOException;
import java.sql.SQLException;
import java.sql.Statement;
import javax.sql.DataSource;
@ -58,9 +57,9 @@ public class DevToolsPooledDataSourceAutoConfigurationTests
}
@Test
public void autoConfiguredInMemoryDataSourceIsShutdown() throws SQLException {
ConfigurableApplicationContext context = createContext(
DataSourceAutoConfiguration.class, DataSourceSpyConfiguration.class);
public void autoConfiguredInMemoryDataSourceIsShutdown() throws Exception {
ConfigurableApplicationContext context = getContext(() -> createContext(
DataSourceAutoConfiguration.class, DataSourceSpyConfiguration.class));
Statement statement = configureDataSourceBehavior(
context.getBean(DataSource.class));
context.close();
@ -68,9 +67,10 @@ public class DevToolsPooledDataSourceAutoConfigurationTests
}
@Test
public void autoConfiguredExternalDataSourceIsNotShutdown() throws SQLException {
ConfigurableApplicationContext context = createContext("org.postgresql.Driver",
DataSourceAutoConfiguration.class, DataSourceSpyConfiguration.class);
public void autoConfiguredExternalDataSourceIsNotShutdown() throws Exception {
ConfigurableApplicationContext context = getContext(() -> createContext(
"org.postgresql.Driver", DataSourceAutoConfiguration.class,
DataSourceSpyConfiguration.class));
Statement statement = configureDataSourceBehavior(
context.getBean(DataSource.class));
context.close();
@ -78,10 +78,10 @@ public class DevToolsPooledDataSourceAutoConfigurationTests
}
@Test
public void h2ServerIsNotShutdown() throws SQLException {
ConfigurableApplicationContext context = createContext("org.h2.Driver",
"jdbc:h2:hsql://localhost", DataSourceAutoConfiguration.class,
DataSourceSpyConfiguration.class);
public void h2ServerIsNotShutdown() throws Exception {
ConfigurableApplicationContext context = getContext(() -> createContext(
"org.h2.Driver", "jdbc:h2:hsql://localhost",
DataSourceAutoConfiguration.class, DataSourceSpyConfiguration.class));
Statement statement = configureDataSourceBehavior(
context.getBean(DataSource.class));
context.close();
@ -89,10 +89,10 @@ public class DevToolsPooledDataSourceAutoConfigurationTests
}
@Test
public void inMemoryH2IsShutdown() throws SQLException {
ConfigurableApplicationContext context = createContext("org.h2.Driver",
"jdbc:h2:mem:test", DataSourceAutoConfiguration.class,
DataSourceSpyConfiguration.class);
public void inMemoryH2IsShutdown() throws Exception {
ConfigurableApplicationContext context = getContext(() -> createContext(
"org.h2.Driver", "jdbc:h2:mem:test", DataSourceAutoConfiguration.class,
DataSourceSpyConfiguration.class));
Statement statement = configureDataSourceBehavior(
context.getBean(DataSource.class));
context.close();
@ -100,10 +100,10 @@ public class DevToolsPooledDataSourceAutoConfigurationTests
}
@Test
public void hsqlServerIsNotShutdown() throws SQLException {
ConfigurableApplicationContext context = createContext("org.hsqldb.jdbcDriver",
"jdbc:hsqldb:hsql://localhost", DataSourceAutoConfiguration.class,
DataSourceSpyConfiguration.class);
public void hsqlServerIsNotShutdown() throws Exception {
ConfigurableApplicationContext context = getContext(() -> createContext(
"org.hsqldb.jdbcDriver", "jdbc:hsqldb:hsql://localhost",
DataSourceAutoConfiguration.class, DataSourceSpyConfiguration.class));
Statement statement = configureDataSourceBehavior(
context.getBean(DataSource.class));
context.close();
@ -111,10 +111,10 @@ public class DevToolsPooledDataSourceAutoConfigurationTests
}
@Test
public void inMemoryHsqlIsShutdown() throws SQLException {
ConfigurableApplicationContext context = createContext("org.hsqldb.jdbcDriver",
"jdbc:hsqldb:mem:test", DataSourceAutoConfiguration.class,
DataSourceSpyConfiguration.class);
public void inMemoryHsqlIsShutdown() throws Exception {
ConfigurableApplicationContext context = getContext(() -> createContext(
"org.hsqldb.jdbcDriver", "jdbc:hsqldb:mem:test",
DataSourceAutoConfiguration.class, DataSourceSpyConfiguration.class));
Statement statement = configureDataSourceBehavior(
context.getBean(DataSource.class));
context.close();
@ -122,10 +122,10 @@ public class DevToolsPooledDataSourceAutoConfigurationTests
}
@Test
public void derbyClientIsNotShutdown() throws SQLException {
ConfigurableApplicationContext context = createContext(
public void derbyClientIsNotShutdown() throws Exception {
ConfigurableApplicationContext context = getContext(() -> createContext(
"org.apache.derby.jdbc.ClientDriver", "jdbc:derby://localhost",
DataSourceAutoConfiguration.class, DataSourceSpyConfiguration.class);
DataSourceAutoConfiguration.class, DataSourceSpyConfiguration.class));
Statement statement = configureDataSourceBehavior(
context.getBean(DataSource.class));
context.close();
@ -133,13 +133,14 @@ public class DevToolsPooledDataSourceAutoConfigurationTests
}
@Test
public void inMemoryDerbyIsShutdown() throws SQLException {
ConfigurableApplicationContext context = createContext(
"org.apache.derby.jdbc.EmbeddedDriver", "jdbc:derby:memory:test",
DataSourceAutoConfiguration.class, DataSourceSpyConfiguration.class);
public void inMemoryDerbyIsShutdown() throws Exception {
ConfigurableApplicationContext configurableApplicationContext = getContext(
() -> createContext("org.apache.derby.jdbc.EmbeddedDriver",
"jdbc:derby:memory:test", DataSourceAutoConfiguration.class,
DataSourceSpyConfiguration.class));
Statement statement = configureDataSourceBehavior(
context.getBean(DataSource.class));
context.close();
configurableApplicationContext.getBean(DataSource.class));
configurableApplicationContext.close();
verify(statement, times(1)).execute("SHUTDOWN");
}

@ -21,6 +21,9 @@ import java.time.Duration;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import org.apache.catalina.Container;
import org.apache.catalina.core.StandardWrapper;
@ -84,28 +87,29 @@ public class LocalDevToolsAutoConfigurationTests {
}
@Test
public void thymeleafCacheIsFalse() {
this.context = initializeAndRun(Config.class);
public void thymeleafCacheIsFalse() throws Exception {
this.context = getContext(() -> initializeAndRun(Config.class));
SpringResourceTemplateResolver resolver = this.context
.getBean(SpringResourceTemplateResolver.class);
assertThat(resolver.isCacheable()).isFalse();
}
@Test
public void defaultPropertyCanBeOverriddenFromCommandLine() {
this.context = initializeAndRun(Config.class, "--spring.thymeleaf.cache=true");
public void defaultPropertyCanBeOverriddenFromCommandLine() throws Exception {
this.context = getContext(
() -> initializeAndRun(Config.class, "--spring.thymeleaf.cache=true"));
SpringResourceTemplateResolver resolver = this.context
.getBean(SpringResourceTemplateResolver.class);
assertThat(resolver.isCacheable()).isTrue();
}
@Test
public void defaultPropertyCanBeOverriddenFromUserHomeProperties() {
public void defaultPropertyCanBeOverriddenFromUserHomeProperties() throws Exception {
String userHome = System.getProperty("user.home");
System.setProperty("user.home",
new File("src/test/resources/user-home").getAbsolutePath());
try {
this.context = initializeAndRun(Config.class);
this.context = getContext(() -> initializeAndRun(Config.class));
SpringResourceTemplateResolver resolver = this.context
.getBean(SpringResourceTemplateResolver.class);
assertThat(resolver.isCacheable()).isTrue();
@ -116,22 +120,22 @@ public class LocalDevToolsAutoConfigurationTests {
}
@Test
public void resourceCachePeriodIsZero() {
this.context = initializeAndRun(WebResourcesConfig.class);
public void resourceCachePeriodIsZero() throws Exception {
this.context = getContext(() -> initializeAndRun(WebResourcesConfig.class));
ResourceProperties properties = this.context.getBean(ResourceProperties.class);
assertThat(properties.getCache().getPeriod()).isEqualTo(Duration.ZERO);
}
@Test
public void liveReloadServer() {
this.context = initializeAndRun(Config.class);
public void liveReloadServer() throws Exception {
this.context = getContext(() -> initializeAndRun(Config.class));
LiveReloadServer server = this.context.getBean(LiveReloadServer.class);
assertThat(server.isStarted()).isTrue();
}
@Test
public void liveReloadTriggeredOnContextRefresh() {
this.context = initializeAndRun(ConfigWithMockLiveReload.class);
public void liveReloadTriggeredOnContextRefresh() throws Exception {
this.context = getContext(() -> initializeAndRun(ConfigWithMockLiveReload.class));
LiveReloadServer server = this.context.getBean(LiveReloadServer.class);
reset(server);
this.context.publishEvent(new ContextRefreshedEvent(this.context));
@ -139,8 +143,8 @@ public class LocalDevToolsAutoConfigurationTests {
}
@Test
public void liveReloadTriggeredOnClassPathChangeWithoutRestart() {
this.context = initializeAndRun(ConfigWithMockLiveReload.class);
public void liveReloadTriggeredOnClassPathChangeWithoutRestart() throws Exception {
this.context = getContext(() -> initializeAndRun(ConfigWithMockLiveReload.class));
LiveReloadServer server = this.context.getBean(LiveReloadServer.class);
reset(server);
ClassPathChangedEvent event = new ClassPathChangedEvent(this.context,
@ -150,8 +154,8 @@ public class LocalDevToolsAutoConfigurationTests {
}
@Test
public void liveReloadNotTriggeredOnClassPathChangeWithRestart() {
this.context = initializeAndRun(ConfigWithMockLiveReload.class);
public void liveReloadNotTriggeredOnClassPathChangeWithRestart() throws Exception {
this.context = getContext(() -> initializeAndRun(ConfigWithMockLiveReload.class));
LiveReloadServer server = this.context.getBean(LiveReloadServer.class);
reset(server);
ClassPathChangedEvent event = new ClassPathChangedEvent(this.context,
@ -161,17 +165,17 @@ public class LocalDevToolsAutoConfigurationTests {
}
@Test
public void liveReloadDisabled() {
public void liveReloadDisabled() throws Exception {
Map<String, Object> properties = new HashMap<>();
properties.put("spring.devtools.livereload.enabled", false);
this.context = initializeAndRun(Config.class, properties);
this.context = getContext(() -> initializeAndRun(Config.class, properties));
assertThatExceptionOfType(NoSuchBeanDefinitionException.class)
.isThrownBy(() -> this.context.getBean(OptionalLiveReloadServer.class));
}
@Test
public void restartTriggeredOnClassPathChangeWithRestart() {
this.context = initializeAndRun(Config.class);
public void restartTriggeredOnClassPathChangeWithRestart() throws Exception {
this.context = getContext(() -> initializeAndRun(Config.class));
ClassPathChangedEvent event = new ClassPathChangedEvent(this.context,
Collections.emptySet(), true);
this.context.publishEvent(event);
@ -179,8 +183,8 @@ public class LocalDevToolsAutoConfigurationTests {
}
@Test
public void restartNotTriggeredOnClassPathChangeWithRestart() {
this.context = initializeAndRun(Config.class);
public void restartNotTriggeredOnClassPathChangeWithRestart() throws Exception {
this.context = getContext(() -> initializeAndRun(Config.class));
ClassPathChangedEvent event = new ClassPathChangedEvent(this.context,
Collections.emptySet(), false);
this.context.publishEvent(event);
@ -188,27 +192,27 @@ public class LocalDevToolsAutoConfigurationTests {
}
@Test
public void restartWatchingClassPath() {
this.context = initializeAndRun(Config.class);
public void restartWatchingClassPath() throws Exception {
this.context = getContext(() -> initializeAndRun(Config.class));
ClassPathFileSystemWatcher watcher = this.context
.getBean(ClassPathFileSystemWatcher.class);
assertThat(watcher).isNotNull();
}
@Test
public void restartDisabled() {
public void restartDisabled() throws Exception {
Map<String, Object> properties = new HashMap<>();
properties.put("spring.devtools.restart.enabled", false);
this.context = initializeAndRun(Config.class, properties);
this.context = getContext(() -> initializeAndRun(Config.class, properties));
assertThatExceptionOfType(NoSuchBeanDefinitionException.class)
.isThrownBy(() -> this.context.getBean(ClassPathFileSystemWatcher.class));
}
@Test
public void restartWithTriggerFile() {
public void restartWithTriggerFile() throws Exception {
Map<String, Object> properties = new HashMap<>();
properties.put("spring.devtools.restart.trigger-file", "somefile.txt");
this.context = initializeAndRun(Config.class, properties);
this.context = getContext(() -> initializeAndRun(Config.class, properties));
ClassPathFileSystemWatcher classPathWatcher = this.context
.getBean(ClassPathFileSystemWatcher.class);
Object watcher = ReflectionTestUtils.getField(classPathWatcher,
@ -218,11 +222,11 @@ public class LocalDevToolsAutoConfigurationTests {
}
@Test
public void watchingAdditionalPaths() {
public void watchingAdditionalPaths() throws Exception {
Map<String, Object> properties = new HashMap<>();
properties.put("spring.devtools.restart.additional-paths",
"src/main/java,src/test/java");
this.context = initializeAndRun(Config.class, properties);
this.context = getContext(() -> initializeAndRun(Config.class, properties));
ClassPathFileSystemWatcher classPathWatcher = this.context
.getBean(ClassPathFileSystemWatcher.class);
Object watcher = ReflectionTestUtils.getField(classPathWatcher,
@ -236,8 +240,8 @@ public class LocalDevToolsAutoConfigurationTests {
}
@Test
public void devToolsSwitchesJspServletToDevelopmentMode() {
this.context = initializeAndRun(Config.class);
public void devToolsSwitchesJspServletToDevelopmentMode() throws Exception {
this.context = getContext(() -> initializeAndRun(Config.class));
TomcatWebServer tomcatContainer = (TomcatWebServer) ((ServletWebServerApplicationContext) this.context)
.getWebServer();
Container context = tomcatContainer.getTomcat().getHost().findChildren()[0];
@ -247,6 +251,20 @@ public class LocalDevToolsAutoConfigurationTests {
assertThat(options.getDevelopment()).isTrue();
}
private ConfigurableApplicationContext getContext(
Supplier<ConfigurableApplicationContext> supplier) throws Exception {
CountDownLatch latch = new CountDownLatch(1);
AtomicReference<ConfigurableApplicationContext> atomicReference = new AtomicReference<>();
Thread thread = new Thread(() -> {
ConfigurableApplicationContext context = supplier.get();
latch.countDown();
atomicReference.getAndSet(context);
});
thread.start();
thread.join();
return atomicReference.get();
}
private ConfigurableApplicationContext initializeAndRun(Class<?> config,
String... args) {
return initializeAndRun(config, Collections.emptyMap(), args);

@ -0,0 +1,73 @@
/*
* 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.autoconfigure;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link OnEnabledDevtoolsCondition}.
*
* @author Madhura Bhave
*/
public class OnEnabledDevtoolsConditionTests {
private AnnotationConfigApplicationContext context;
@Before
public void setup() {
this.context = new AnnotationConfigApplicationContext();
this.context.register(TestConfiguration.class);
}
@Test
public void outcomeWhenDevtoolsShouldBeEnabledIsTrueShouldMatch() throws Exception {
Thread thread = new Thread(() -> {
OnEnabledDevtoolsConditionTests.this.context.refresh();
assertThat(OnEnabledDevtoolsConditionTests.this.context.containsBean("test"))
.isTrue();
});
thread.start();
thread.join();
}
@Test
public void outcomeWhenDevtoolsShouldBeEnabledIsFalseShouldNotMatch() {
OnEnabledDevtoolsConditionTests.this.context.refresh();
assertThat(OnEnabledDevtoolsConditionTests.this.context.containsBean("test"))
.isFalse();
}
@Configuration(proxyBeanMethods = false)
static class TestConfiguration {
@Bean
@Conditional(OnEnabledDevtoolsCondition.class)
public String test() {
return "hello";
}
}
}

@ -16,6 +16,10 @@
package org.springframework.boot.devtools.autoconfigure;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@ -81,15 +85,16 @@ public class RemoteDevToolsAutoConfigurationTests {
}
@Test
public void disabledIfRemoteSecretIsMissing() {
loadContext("a:b");
public void disabledIfRemoteSecretIsMissing() throws Exception {
this.context = getContext(() -> loadContext("a:b"));
assertThatExceptionOfType(NoSuchBeanDefinitionException.class)
.isThrownBy(() -> this.context.getBean(DispatcherFilter.class));
}
@Test
public void ignoresUnmappedUrl() throws Exception {
loadContext("spring.devtools.remote.secret:supersecret");
this.context = getContext(
() -> loadContext("spring.devtools.remote.secret:supersecret"));
DispatcherFilter filter = this.context.getBean(DispatcherFilter.class);
this.request.setRequestURI("/restart");
this.request.addHeader(DEFAULT_SECRET_HEADER_NAME, "supersecret");
@ -99,7 +104,8 @@ public class RemoteDevToolsAutoConfigurationTests {
@Test
public void ignoresIfMissingSecretFromRequest() throws Exception {
loadContext("spring.devtools.remote.secret:supersecret");
this.context = getContext(
() -> loadContext("spring.devtools.remote.secret:supersecret"));
DispatcherFilter filter = this.context.getBean(DispatcherFilter.class);
this.request.setRequestURI(DEFAULT_CONTEXT_PATH + "/restart");
filter.doFilter(this.request, this.response, this.chain);
@ -108,7 +114,8 @@ public class RemoteDevToolsAutoConfigurationTests {
@Test
public void ignoresInvalidSecretInRequest() throws Exception {
loadContext("spring.devtools.remote.secret:supersecret");
this.context = getContext(
() -> loadContext("spring.devtools.remote.secret:supersecret"));
DispatcherFilter filter = this.context.getBean(DispatcherFilter.class);
this.request.setRequestURI(DEFAULT_CONTEXT_PATH + "/restart");
this.request.addHeader(DEFAULT_SECRET_HEADER_NAME, "invalid");
@ -118,7 +125,8 @@ public class RemoteDevToolsAutoConfigurationTests {
@Test
public void invokeRestartWithDefaultSetup() throws Exception {
loadContext("spring.devtools.remote.secret:supersecret");
this.context = getContext(
() -> loadContext("spring.devtools.remote.secret:supersecret"));
DispatcherFilter filter = this.context.getBean(DispatcherFilter.class);
this.request.setRequestURI(DEFAULT_CONTEXT_PATH + "/restart");
this.request.addHeader(DEFAULT_SECRET_HEADER_NAME, "supersecret");
@ -128,8 +136,9 @@ public class RemoteDevToolsAutoConfigurationTests {
@Test
public void invokeRestartWithCustomServerContextPath() throws Exception {
loadContext("spring.devtools.remote.secret:supersecret",
"server.servlet.context-path:/test");
this.context = getContext(
() -> loadContext("spring.devtools.remote.secret:supersecret",
"server.servlet.context-path:/test"));
DispatcherFilter filter = this.context.getBean(DispatcherFilter.class);
this.request.setRequestURI("/test" + DEFAULT_CONTEXT_PATH + "/restart");
this.request.addHeader(DEFAULT_SECRET_HEADER_NAME, "supersecret");
@ -138,16 +147,18 @@ public class RemoteDevToolsAutoConfigurationTests {
}
@Test
public void disableRestart() {
loadContext("spring.devtools.remote.secret:supersecret",
"spring.devtools.remote.restart.enabled:false");
public void disableRestart() throws Exception {
this.context = getContext(
() -> loadContext("spring.devtools.remote.secret:supersecret",
"spring.devtools.remote.restart.enabled:false"));
assertThatExceptionOfType(NoSuchBeanDefinitionException.class)
.isThrownBy(() -> this.context.getBean("remoteRestartHandlerMapper"));
}
@Test
public void devToolsHealthReturns200() throws Exception {
loadContext("spring.devtools.remote.secret:supersecret");
this.context = getContext(
() -> loadContext("spring.devtools.remote.secret:supersecret"));
DispatcherFilter filter = this.context.getBean(DispatcherFilter.class);
this.request.setRequestURI(DEFAULT_CONTEXT_PATH);
this.request.addHeader(DEFAULT_SECRET_HEADER_NAME, "supersecret");
@ -158,8 +169,9 @@ public class RemoteDevToolsAutoConfigurationTests {
@Test
public void devToolsHealthWithCustomServerContextPathReturns200() throws Exception {
loadContext("spring.devtools.remote.secret:supersecret",
"server.servlet.context-path:/test");
this.context = getContext(
() -> loadContext("spring.devtools.remote.secret:supersecret",
"server.servlet.context-path:/test"));
DispatcherFilter filter = this.context.getBean(DispatcherFilter.class);
this.request.setRequestURI("/test" + DEFAULT_CONTEXT_PATH);
this.request.addHeader(DEFAULT_SECRET_HEADER_NAME, "supersecret");
@ -168,17 +180,34 @@ public class RemoteDevToolsAutoConfigurationTests {
assertThat(this.response.getStatus()).isEqualTo(200);
}
private AnnotationConfigServletWebApplicationContext getContext(
Supplier<AnnotationConfigServletWebApplicationContext> supplier)
throws Exception {
CountDownLatch latch = new CountDownLatch(1);
AtomicReference<AnnotationConfigServletWebApplicationContext> atomicReference = new AtomicReference<>();
Thread thread = new Thread(() -> {
AnnotationConfigServletWebApplicationContext context = supplier.get();
latch.countDown();
atomicReference.getAndSet(context);
});
thread.start();
thread.join();
return atomicReference.get();
}
private void assertRestartInvoked(boolean value) {
assertThat(this.context.getBean(MockHttpRestartServer.class).invoked)
.isEqualTo(value);
}
private void loadContext(String... properties) {
this.context = new AnnotationConfigServletWebApplicationContext();
this.context.setServletContext(new MockServletContext());
this.context.register(Config.class, PropertyPlaceholderAutoConfiguration.class);
TestPropertyValues.of(properties).applyTo(this.context);
this.context.refresh();
private AnnotationConfigServletWebApplicationContext loadContext(
String... properties) {
AnnotationConfigServletWebApplicationContext context = new AnnotationConfigServletWebApplicationContext();
context.setServletContext(new MockServletContext());
context.register(Config.class, PropertyPlaceholderAutoConfiguration.class);
TestPropertyValues.of(properties).applyTo(context);
context.refresh();
return context;
}
@Configuration(proxyBeanMethods = false)

@ -18,6 +18,9 @@ package org.springframework.boot.devtools.env;
import java.net.URL;
import java.util.Collections;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import org.junit.After;
import org.junit.Before;
@ -61,37 +64,40 @@ public class DevToolPropertiesIntegrationTests {
}
@Test
public void classPropertyConditionIsAffectedByDevToolProperties() {
public void classPropertyConditionIsAffectedByDevToolProperties() throws Exception {
SpringApplication application = new SpringApplication(
ClassConditionConfiguration.class);
application.setWebApplicationType(WebApplicationType.NONE);
this.context = application.run();
this.context = getContext(application::run);
this.context.getBean(ClassConditionConfiguration.class);
}
@Test
public void beanMethodPropertyConditionIsAffectedByDevToolProperties() {
public void beanMethodPropertyConditionIsAffectedByDevToolProperties()
throws Exception {
SpringApplication application = new SpringApplication(
BeanConditionConfiguration.class);
application.setWebApplicationType(WebApplicationType.NONE);
this.context = application.run();
this.context = getContext(application::run);
this.context.getBean(MyBean.class);
}
@Test
public void postProcessWhenRestarterDisabledAndRemoteSecretNotSetShouldNotAddPropertySource() {
public void postProcessWhenRestarterDisabledAndRemoteSecretNotSetShouldNotAddPropertySource()
throws Exception {
Restarter.clearInstance();
Restarter.disable();
SpringApplication application = new SpringApplication(
BeanConditionConfiguration.class);
application.setWebApplicationType(WebApplicationType.NONE);
this.context = application.run();
this.context = getContext(application::run);
assertThatExceptionOfType(NoSuchBeanDefinitionException.class)
.isThrownBy(() -> this.context.getBean(MyBean.class));
}
@Test
public void postProcessWhenRestarterDisabledAndRemoteSecretSetShouldAddPropertySource() {
public void postProcessWhenRestarterDisabledAndRemoteSecretSetShouldAddPropertySource()
throws Exception {
Restarter.clearInstance();
Restarter.disable();
SpringApplication application = new SpringApplication(
@ -99,21 +105,35 @@ public class DevToolPropertiesIntegrationTests {
application.setWebApplicationType(WebApplicationType.NONE);
application.setDefaultProperties(
Collections.singletonMap("spring.devtools.remote.secret", "donttell"));
this.context = application.run();
this.context = getContext(application::run);
this.context.getBean(MyBean.class);
}
@Test
public void postProcessEnablesIncludeStackTraceProperty() {
public void postProcessEnablesIncludeStackTraceProperty() throws Exception {
SpringApplication application = new SpringApplication(TestConfiguration.class);
application.setWebApplicationType(WebApplicationType.NONE);
this.context = application.run();
this.context = getContext(application::run);
ConfigurableEnvironment environment = this.context.getEnvironment();
String property = environment.getProperty("server.error.include-stacktrace");
assertThat(property)
.isEqualTo(ErrorProperties.IncludeStacktrace.ALWAYS.toString());
}
protected ConfigurableApplicationContext getContext(
Supplier<ConfigurableApplicationContext> supplier) throws Exception {
CountDownLatch latch = new CountDownLatch(1);
AtomicReference<ConfigurableApplicationContext> atomicReference = new AtomicReference<>();
Thread thread = new Thread(() -> {
ConfigurableApplicationContext context = supplier.get();
latch.countDown();
atomicReference.getAndSet(context);
});
thread.start();
thread.join();
return atomicReference.get();
}
@Configuration(proxyBeanMethods = false)
static class TestConfiguration {

@ -21,6 +21,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
import org.junit.Before;
import org.junit.Rule;
@ -60,18 +61,28 @@ public class DevToolsHomePropertiesPostProcessorTests {
out.close();
ConfigurableEnvironment environment = new MockEnvironment();
MockDevToolHomePropertiesPostProcessor postProcessor = new MockDevToolHomePropertiesPostProcessor();
postProcessor.postProcessEnvironment(environment, null);
runPostProcessor(() -> postProcessor.postProcessEnvironment(environment, null));
assertThat(environment.getProperty("abc")).isEqualTo("def");
}
@Test
public void ignoresMissingHomeProperties() {
public void ignoresMissingHomeProperties() throws Exception {
ConfigurableEnvironment environment = new MockEnvironment();
MockDevToolHomePropertiesPostProcessor postProcessor = new MockDevToolHomePropertiesPostProcessor();
postProcessor.postProcessEnvironment(environment, null);
runPostProcessor(() -> postProcessor.postProcessEnvironment(environment, null));
assertThat(environment.getProperty("abc")).isNull();
}
protected void runPostProcessor(Runnable runnable) throws Exception {
CountDownLatch latch = new CountDownLatch(1);
Thread thread = new Thread(() -> {
runnable.run();
latch.countDown();
});
thread.start();
thread.join();
}
private class MockDevToolHomePropertiesPostProcessor
extends DevToolsHomePropertiesPostProcessor {

Loading…
Cancel
Save