Skip to content

Commit

Permalink
Introduce integration tests to cover the production classloader
Browse files Browse the repository at this point in the history
  • Loading branch information
Sanne committed Oct 7, 2021
1 parent 5b25046 commit 997c3ce
Show file tree
Hide file tree
Showing 8 changed files with 255 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .github/quarkus-bot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -328,8 +328,10 @@ triage:
- core/deployment/src/main/java/io/quarkus/deployment/configuration/
- core/runtime/src/main/java/io/quarkus/runtime/configuration/
- labels: [area/core]
notify: [aloubyansky, gsmet, geoand, radcortez, Sanne, stuartwdouglas]
directories:
- core/
- integration-tests/production-mode
- labels: [area/dependencies]
directories:
- .github/dependabot.yml
Expand Down
1 change: 1 addition & 0 deletions integration-tests/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@
</property>
</activation>
<modules>
<module>production-mode</module>
<module>avro-reload</module>
<module>awt</module>
<module>bouncycastle</module>
Expand Down
7 changes: 7 additions & 0 deletions integration-tests/production-mode/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
The purpose of this module is to run integration tests
on the core state assumptions of a Quarkus application
booted in production mode (on JVM).

These same tests are not expected to be run in other modes,
and the same assumptions are not expected to hold in
native-image mode either.
1 change: 1 addition & 0 deletions integration-tests/production-mode/disable-native-profile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This file disables the native profile in the parent pom.xml of this module.
81 changes: 81 additions & 0 deletions integration-tests/production-mode/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>quarkus-integration-tests-parent</artifactId>
<groupId>io.quarkus</groupId>
<version>999-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>production-mode</artifactId>
<name>Quarkus - Integration Tests - Quarkus Production Mode</name>
<description>Integration tests specifically requiring to run Quarkus in prod mode</description>

<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5-internal</artifactId>
<scope>test</scope>
</dependency>

<!-- Minimal test dependencies to *-deployment artifacts for consistent build order -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-deployment</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>

</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package io.quarkus.it.prodmode;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
* Helper to collect assertions related to the JDK ForkJoinPool
* state.
*/
public class ForkJoinPoolAssertions {

/**
* We test which Classloader is being used by each thread
* in the common pool of ForkJoinPool.
* It is expected that a Quarkus application running in production mode
* will have set the io.quarkus.bootstrap.runner.RunnerClassLoader as
* context classloader on each of them, to prevent problems at runtime.
*
* @return true only if all checks are successful.
*/
static boolean isEachFJThreadUsingQuarkusClassloader() {
AtomicInteger testedOk = new AtomicInteger();
final int poolParallelism = ForkJoinPool.getCommonPoolParallelism();
CountDownLatch allDone = new CountDownLatch(poolParallelism);
CountDownLatch taskRelease = new CountDownLatch(1);
if (poolParallelism < 1) {
System.err
.println("Can't test this when ForkJoinPool.getCommonPoolParallelism() has been forced to less than one.");
return false;
}
for (int i = 0; i < poolParallelism; ++i) {
ForkJoinPool.commonPool().execute(new Runnable() {
@Override
public void run() {
final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
final String classLoaderImplementationName = contextClassLoader.getClass().getName();
if (classLoaderImplementationName.equals(io.quarkus.bootstrap.runner.RunnerClassLoader.class.getName())) {
testedOk.incrementAndGet();
} else {
System.out.println("Unexpected classloader name used in production: " + classLoaderImplementationName);
}
allDone.countDown();
try {
taskRelease.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
try {
if (!allDone.await(10, TimeUnit.SECONDS)) {
throw new IllegalStateException("Timed out while trying to scale up the fork join pool");
}
} catch (InterruptedException e) {
e.printStackTrace();
return false;
} finally {
taskRelease.countDown();
}
return testedOk.get() == poolParallelism;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package io.quarkus.it.prodmode;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import io.quarkus.runtime.Quarkus;

@Path("/production-mode-tests")
public class ProductionModeTestsEndpoint {

@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return "hello";
}

@GET
@Path("areExpectedSystemPropertiesSet")
@Produces(MediaType.TEXT_PLAIN)
public String areExpectedSystemPropertiesSet() {
if (!"org.jboss.logmanager.LogManager".equals(System.getProperty("java.util.logging.manager"))) {
return "no";
}
if (!"io.quarkus.bootstrap.forkjoin.QuarkusForkJoinWorkerThreadFactory"
.equals(System.getProperty("java.util.concurrent.ForkJoinPool.common.threadFactory"))) {
return "no";
}
return "yes";
}

@GET
@Path("isForkJoinPoolUsingExpectedClassloader")
@Produces(MediaType.TEXT_PLAIN)
public String isForkJoinPoolUsingExpectedClassloader() {
return ForkJoinPoolAssertions.isEachFJThreadUsingQuarkusClassloader() ? "yes" : "no";
}

@GET
@Path("shutdown")
@Produces(MediaType.TEXT_PLAIN)
public String shutdown() {
System.out.println("Terminating Quarkus app!");
Quarkus.asyncExit();
return "quitting";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package io.quarkus.it.prodmode;

import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.is;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusProdModeTest;

public class ProdModeTest {

@RegisterExtension
static final QuarkusProdModeTest config = new QuarkusProdModeTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(ProductionModeTestsEndpoint.class, ForkJoinPoolAssertions.class))
.setApplicationName("prod-mode-quarkus")
.setApplicationVersion("0.1-SNAPSHOT")
.setRun(true);

@Test
public void basicApplicationAliveTest() {
given()
.when().get("/production-mode-tests")
.then()
.statusCode(200)
.body(is("hello"));
}

@Test
public void areExpectedSystemPropertiesSet() {
given()
.when().get("/production-mode-tests/areExpectedSystemPropertiesSet")
.then()
.statusCode(200)
.body(is("yes"));
}

@Test
public void isForkJoinPoolUsingExpectedClassloader() {
given()
.when().get("/production-mode-tests/isForkJoinPoolUsingExpectedClassloader")
.then()
.statusCode(200)
.body(is("yes"));
}

}

0 comments on commit 997c3ce

Please sign in to comment.