Skip to content

Commit a5f7a2e

Browse files
Enable BlockHound for integration tests and stability tests (#2863)
* Enable BlockHound for integration tests and stability tests This PR enables [BlockHound](https://github.com/reactor/BlockHound) for the SDK integration test and stability tests. BlockHound can be used to detect blocking behavior in threads that must be treated as non-blocking, like Netty's EventLoop threads. If BlockHound detects an illegal blocking operation being performed, it will throw a BlockingOperationError. Doing so in an integration test would typically fail the integration test and allow us to investigate the behavior. This PR enables BlockHound by leveraging BlockHound's provided [BlockHoundTestExecutionListener](https://github .com/reactor/BlockHound/blob/2071ca8dbb06eba9ffa65596f327e3182d3b9730 /junit-platform/src/main/java/reactor/blockhound/junit/platform /BlockHoundTestExecutionListener.java), which uses @autoservice to automatically register with JUnit 5's ServiceLoader support: https://junit.org/junit5/docs/current/user-guide/#launcher-api-listeners-custom
1 parent ee3d583 commit a5f7a2e

File tree

8 files changed

+194
-0
lines changed

8 files changed

+194
-0
lines changed

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@
130130
<netty-open-ssl-version>2.0.43.Final</netty-open-ssl-version>
131131
<dynamodb-local.version>1.16.0</dynamodb-local.version>
132132
<sqllite.version>1.0.392</sqllite.version>
133+
<blockhound.version>1.0.6.RELEASE</blockhound.version>
133134

134135
<!-- build plugin dependencies-->
135136
<maven.surefire.version>3.0.0-M5</maven.surefire.version>

services/pom.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,18 @@
377377
<version>${awsjavasdk.version}</version>
378378
<scope>runtime</scope>
379379
</dependency>
380+
<dependency>
381+
<groupId>io.projectreactor.tools</groupId>
382+
<artifactId>blockhound</artifactId>
383+
<version>${blockhound.version}</version>
384+
<scope>test</scope>
385+
</dependency>
386+
<dependency>
387+
<groupId>io.projectreactor.tools</groupId>
388+
<artifactId>blockhound-junit-platform</artifactId>
389+
<version>${blockhound.version}</version>
390+
<scope>test</scope>
391+
</dependency>
380392
<dependency>
381393
<artifactId>service-test-utils</artifactId>
382394
<groupId>software.amazon.awssdk</groupId>

services/s3/pom.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,15 @@
4242
</archive>
4343
</configuration>
4444
</plugin>
45+
<plugin>
46+
<groupId>org.apache.maven.plugins</groupId>
47+
<artifactId>maven-dependency-plugin</artifactId>
48+
<configuration>
49+
<ignoredUsedUndeclaredDependencies>
50+
<ignoredUnusedDeclaredDependency>org.junit.jupiter:*</ignoredUnusedDeclaredDependency>
51+
</ignoredUsedUndeclaredDependencies>
52+
</configuration>
53+
</plugin>
4554
</plugins>
4655
</build>
4756

@@ -117,5 +126,10 @@
117126
<version>${awsjavasdk.version}</version>
118127
<scope>test</scope>
119128
</dependency>
129+
<dependency>
130+
<groupId>io.netty</groupId>
131+
<artifactId>netty-transport</artifactId>
132+
<scope>test</scope>
133+
</dependency>
120134
</dependencies>
121135
</project>
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.services;
17+
18+
19+
import static org.assertj.core.api.Assertions.assertThat;
20+
21+
import io.netty.bootstrap.ServerBootstrap;
22+
import io.netty.channel.Channel;
23+
import io.netty.channel.ChannelDuplexHandler;
24+
import io.netty.channel.ChannelHandlerContext;
25+
import io.netty.channel.EventLoop;
26+
import io.netty.channel.EventLoopGroup;
27+
import io.netty.channel.nio.NioEventLoopGroup;
28+
import io.netty.channel.socket.nio.NioServerSocketChannel;
29+
import java.io.IOException;
30+
import java.net.ServerSocket;
31+
import java.net.Socket;
32+
import java.util.concurrent.CountDownLatch;
33+
import java.util.concurrent.TimeUnit;
34+
import java.util.concurrent.atomic.AtomicReference;
35+
import org.junit.jupiter.api.AfterEach;
36+
import org.junit.jupiter.api.BeforeEach;
37+
import org.junit.jupiter.api.Test;
38+
import reactor.blockhound.BlockHound;
39+
import reactor.blockhound.BlockingOperationError;
40+
import reactor.blockhound.integration.BlockHoundIntegration;
41+
import reactor.blockhound.junit.platform.BlockHoundTestExecutionListener;
42+
import software.amazon.awssdk.testutils.service.AwsIntegrationTestBase;
43+
import software.amazon.awssdk.testutils.service.AwsTestBase;
44+
import software.amazon.awssdk.utils.Logger;
45+
46+
/**
47+
* This test ensures that BlockHound is correctly installed for integration tests. The test is somewhat arbitrarily placed in the
48+
* {@code s3} module in order to assert against the configuration of all service integration tests.
49+
* <p>
50+
* BlockHound is installed in one of two ways:
51+
* <ol>
52+
* <li>Using BlockHound's provided {@link BlockHoundTestExecutionListener}, which will be automatically detected by the
53+
* JUnit 5 platform upon initialization.</li>
54+
* <li>Manually calling {@link BlockHound#install(BlockHoundIntegration...)}. This is done as part of static initialization in
55+
* {@link AwsIntegrationTestBase} and {@link AwsTestBase}, which most integration/stability tests extend.
56+
* </ol>
57+
* <p>
58+
* This test ensures BlockHound is correctly installed by intentionally performing a blocking operation on the Netty
59+
* {@link EventLoop} and asserting that a {@link BlockingOperationError} is thrown to forbid it.
60+
*/
61+
class BlockHoundInstalledTest {
62+
private static final Logger log = Logger.loggerFor(BlockHoundInstalledTest.class);
63+
64+
AtomicReference<Throwable> throwableReference;
65+
CountDownLatch latch;
66+
EventLoopGroup bossGroup;
67+
EventLoopGroup workerGroup;
68+
Socket clientSocket;
69+
Channel serverChannel;
70+
71+
@BeforeEach
72+
public void setup() {
73+
throwableReference = new AtomicReference<>();
74+
latch = new CountDownLatch(1);
75+
bossGroup = new NioEventLoopGroup();
76+
workerGroup = new NioEventLoopGroup();
77+
}
78+
79+
@AfterEach
80+
public void teardown() throws Exception {
81+
if (clientSocket != null) {
82+
clientSocket.close();
83+
}
84+
if (serverChannel != null) {
85+
serverChannel.close();
86+
}
87+
workerGroup.shutdownGracefully();
88+
bossGroup.shutdownGracefully();
89+
}
90+
91+
@Test
92+
void testBlockHoundInstalled() throws Exception {
93+
ServerBootstrap bootstrap = new ServerBootstrap();
94+
bootstrap.group(bossGroup, workerGroup)
95+
.channel(NioServerSocketChannel.class)
96+
.childHandler(new ChannelDuplexHandler() {
97+
@Override
98+
public void channelActive(ChannelHandlerContext ctx) {
99+
log.info(() -> "Preparing to sleep on the EventLoop to test if BlockHound is installed");
100+
try {
101+
Thread.sleep(1000);
102+
log.info(() -> "BlockHound does not appear to be successfully installed");
103+
} catch (Throwable t) {
104+
log.info(() -> "BlockHound is successfully installed", t);
105+
throwableReference.set(t);
106+
}
107+
latch.countDown();
108+
ctx.fireChannelActive();
109+
}
110+
});
111+
112+
int port = getUnusedPort();
113+
serverChannel = bootstrap.bind(port).sync().channel();
114+
clientSocket = new Socket("localhost", port);
115+
116+
latch.await(5, TimeUnit.SECONDS);
117+
assertThat(throwableReference.get())
118+
.withFailMessage("BlockHound does not appear to be successfully installed. Ensure that either BlockHound.install() "
119+
+ "is called prior to all test executions or that BlockHoundTestExecutionListener is available on "
120+
+ "the class path and correctly detected by JUnit: "
121+
+ "https://github.com/reactor/BlockHound/blob/master/docs/supported_testing_frameworks.md")
122+
.isInstanceOf(BlockingOperationError.class);
123+
}
124+
125+
private static int getUnusedPort() throws IOException {
126+
try (ServerSocket socket = new ServerSocket(0)) {
127+
socket.setReuseAddress(true);
128+
return socket.getLocalPort();
129+
}
130+
}
131+
}

test/service-test-utils/pom.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,18 @@
8383
<artifactId>junit</artifactId>
8484
<scope>compile</scope>
8585
</dependency>
86+
<dependency>
87+
<groupId>io.projectreactor.tools</groupId>
88+
<artifactId>blockhound</artifactId>
89+
<version>${blockhound.version}</version>
90+
<scope>compile</scope>
91+
</dependency>
92+
<dependency>
93+
<groupId>io.projectreactor.tools</groupId>
94+
<artifactId>blockhound-junit-platform</artifactId>
95+
<version>${blockhound.version}</version>
96+
<scope>compile</scope>
97+
</dependency>
8698
<dependency>
8799
<groupId>org.hamcrest</groupId>
88100
<artifactId>hamcrest-core</artifactId>
@@ -101,6 +113,7 @@
101113
<ignoredUnusedDeclaredDependency>software.amazon.awssdk:test-utils:*</ignoredUnusedDeclaredDependency>
102114
<ignoredUnusedDeclaredDependency>org.junit.jupiter:*</ignoredUnusedDeclaredDependency>
103115
<ignoredUnusedDeclaredDependency>org.junit.vintage:*</ignoredUnusedDeclaredDependency>
116+
<ignoredUnusedDeclaredDependency>io.projectreactor.tools:blockhound-junit-platform:*</ignoredUnusedDeclaredDependency>
104117
</ignoredUnusedDeclaredDependencies>
105118
</configuration>
106119
</plugin>

test/service-test-utils/src/main/java/software/amazon/awssdk/testutils/service/AwsIntegrationTestBase.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import java.io.IOException;
1919
import java.io.InputStream;
20+
import reactor.blockhound.BlockHound;
2021
import software.amazon.awssdk.auth.credentials.AwsCredentials;
2122
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
2223
import software.amazon.awssdk.auth.credentials.AwsCredentialsProviderChain;
@@ -27,6 +28,10 @@
2728

2829
public abstract class AwsIntegrationTestBase {
2930

31+
static {
32+
BlockHound.install();
33+
}
34+
3035
/** Default Properties Credentials file path. */
3136
private static final String TEST_CREDENTIALS_PROFILE_NAME = "aws-test-account";
3237

test/service-test-utils/src/main/java/software/amazon/awssdk/testutils/service/AwsTestBase.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,19 @@
2323
import org.hamcrest.Description;
2424
import org.hamcrest.Matcher;
2525
import org.hamcrest.TypeSafeMatcher;
26+
import reactor.blockhound.BlockHound;
2627
import software.amazon.awssdk.auth.credentials.AwsCredentialsProviderChain;
2728
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
2829
import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider;
2930
import software.amazon.awssdk.core.exception.SdkServiceException;
3031
import software.amazon.awssdk.utils.IoUtils;
3132

3233
public abstract class AwsTestBase {
34+
35+
static {
36+
BlockHound.install();
37+
}
38+
3339
/** Default Properties Credentials file path. */
3440
private static final String TEST_CREDENTIALS_PROFILE_NAME = "aws-test-account";
3541

test/stability-tests/pom.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,18 @@
153153
<version>${junit5.version}</version>
154154
<scope>test</scope>
155155
</dependency>
156+
<dependency>
157+
<groupId>io.projectreactor.tools</groupId>
158+
<artifactId>blockhound</artifactId>
159+
<version>${blockhound.version}</version>
160+
<scope>test</scope>
161+
</dependency>
162+
<dependency>
163+
<groupId>io.projectreactor.tools</groupId>
164+
<artifactId>blockhound-junit-platform</artifactId>
165+
<version>${blockhound.version}</version>
166+
<scope>test</scope>
167+
</dependency>
156168
<dependency>
157169
<groupId>org.assertj</groupId>
158170
<artifactId>assertj-core</artifactId>

0 commit comments

Comments
 (0)