Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate @ShouldPin/@ShouldNotPin into QuarkusTests #35992

Merged
merged 1 commit into from
Sep 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions bom/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3094,6 +3094,11 @@
<artifactId>quarkus-project-core-extension-codestarts</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus.junit5</groupId>
<artifactId>junit5-virtual-threads</artifactId>
<version>${project.version}</version>
</dependency>

<!-- External dependencies -->

Expand Down
67 changes: 67 additions & 0 deletions docs/src/main/asciidoc/virtual-threads.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,73 @@ quarkus.virtual-threads.name-prefix=

----

== Testing virtual thread applications

As mentioned above, virtual threads have a few limitations that can drastically affect your application performance and memory usage.
The _junit5-virtual-threads_ extension provides a way to detect pinned carrier threads while running your tests.
Thus, you can eliminate one of the most prominent limitations or be aware of the problem.

To enable this detection:

* 1) Add the `junit5-virtual-threads` dependency to your project:
[source, xml]
----
<dependency>
<groupId>io.quarkus.junit5</groupId>
<artifactId>junit5-virtual-threads</artifactId>
<scope>test</scope>
</dependency>
----

* 2) In your test case, add the `io.quarkus.test.junit5.virtual.VirtualThreadUnit` and `io.quarkus.test.junit.virtual.ShouldNotPin` annotations:
[source, java]
----
@QuarkusTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@VirtualThreadUnit // Use the extension
@ShouldNotPin // Detect pinned carrier thread
class TodoResourceTest {
// ...
}
----

When you run your test (remember to use Java 21+), Quarkus detects pinned carrier threads.
When it happens, the test fails.

The `@ShouldNotPin` can also be used on methods directly.

The _junit5-virtual-threads_ also provides a `@ShouldPin` annotation for cases where pinning is unavoidable.
The following snippet demonstrates the `@ShouldPin` annotation usage and the possibility to inject a `ThreadPinnedEvents` instance in your test to verify when the carrier thread was pinned manually.

[source, java]
----
@VirtualThreadUnit // Use the extension
public class LoomUnitExampleTest {

CodeUnderTest codeUnderTest = new CodeUnderTest();

@Test
@ShouldNotPin
public void testThatShouldNotPin() {
// ...
}

@Test
@ShouldPin(atMost = 1)
public void testThatShouldPinAtMostOnce() {
codeUnderTest.pin();
}

@Test
public void testThatShouldNotPin(ThreadPinnedEvents events) { // Inject an object to check the pin events
Assertions.assertTrue(events.getEvents().isEmpty());
codeUnderTest.pin();
await().until(() -> events.getEvents().size() > 0);
Assertions.assertEquals(events.getEvents().size(), 1);
}

}
----

== Additional references

Expand Down
298 changes: 298 additions & 0 deletions independent-projects/junit5-virtual-threads/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,298 @@
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-parent</artifactId>
<version>999-SNAPSHOT</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>

<groupId>io.quarkus.junit5</groupId>
<artifactId>junit5-virtual-threads</artifactId>

<name>Quarkus - JUnit 5 Extension - Virtual Threads</name>
<description>Module that allows detecting virtual threads pinning</description>
<url>https://github.com/quarkusio/quarkus</url>

<licenses>
<license>
<name>Apache License, Version 2.0</name>
<distribution>repo</distribution>
<url>https://www.apache.org/licenses/LICENSE-2.0.html</url>
</license>
</licenses>

<scm child.scm.connection.inherit.append.path="false"
child.scm.developerConnection.inherit.append.path="false"
child.scm.url.inherit.append.path="false">
<url>https://github.com/quarkusio/quarkus</url>
<connection>scm:git:[email protected]:quarkusio/quarkus.git</connection>
<developerConnection>scm:git:[email protected]:quarkusio/quarkus.git</developerConnection>
<tag>HEAD</tag>
</scm>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.release>11</maven.compiler.release>

<compiler.plugin.version>3.11.0</compiler.plugin.version>
<enforcer.plugin.version>3.2.1</enforcer.plugin.version>
<surefire.plugin.version>3.1.2</surefire.plugin.version>
<jandex.version>3.1.3</jandex.version>
<formatter-maven-plugin.version>2.23.0</formatter-maven-plugin.version>
<impsort-maven-plugin.version>1.9.0</impsort-maven-plugin.version>

<junit.jupiter.version>5.9.3</junit.jupiter.version>
</properties>


<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>compile</scope>
<version>${junit.jupiter.version}</version>
</dependency>
</dependencies>

<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler.plugin.version}</version>
</plugin>
<plugin>
<groupId>io.smallrye</groupId>
<artifactId>jandex-maven-plugin</artifactId>
<version>${jandex.version}</version>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<quiet>true</quiet>
<doclint>none</doclint>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-enforcer-rules</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<executions>
<execution>
<id>enforce</id>
<configuration>
<rules>
<dependencyConvergence/>
<externalRules>
<location>classpath:enforcer-rules/quarkus-require-java-version.xml</location>
</externalRules>
<externalRules>
<location>classpath:enforcer-rules/quarkus-require-maven-version.xml</location>
</externalRules>
<externalRules>
<location>classpath:enforcer-rules/quarkus-banned-dependencies.xml</location>
</externalRules>
<bannedDependencies>
<excludes>
<!-- findbugs is not required at runtime -->
<exclude>com.google.code.findbugs:jsr305</exclude>
<!-- com.google.guava:listenablefuture is empty and the ListenableFuture class is available in Guava -->
<exclude>com.google.guava:listenablefuture</exclude>
</excludes>
</bannedDependencies>
</rules>
</configuration>
<goals>
<goal>enforce</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire.plugin.version}</version>
<configuration>
<!-- combine.self suppresses warnings about java.io.tmpdir being defined twice -->
<systemPropertyVariables combine.self="override"/>
<!-- set tmpdir as early as possible because failsafe sets it too late for JDK16 -->
<argLine>-Djava.io.tmpdir="${project.build.directory}"</argLine>
<excludedEnvironmentVariables>MAVEN_OPTS</excludedEnvironmentVariables>
</configuration>
</plugin>
<!-- Replicate what's in parent, since this pom doesn't inherit parent but IDE settings will be common -->
<plugin>
<groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<version>${formatter-maven-plugin.version}</version>
<dependencies>
<dependency>
<artifactId>quarkus-ide-config</artifactId>
<groupId>io.quarkus</groupId>
<version>${project.version}</version>
</dependency>
</dependencies>
<configuration>
<!-- store outside of target to speed up formatting when mvn clean is used -->
<cachedir>.cache/formatter-maven-plugin-${formatter-maven-plugin.version}</cachedir>
<configFile>eclipse-format.xml</configFile>
<lineEnding>LF</lineEnding>
<skip>${format.skip}</skip>
</configuration>
</plugin>
<plugin>
<groupId>net.revelc.code</groupId>
<artifactId>impsort-maven-plugin</artifactId>
<version>${impsort-maven-plugin.version}</version>
<configuration>
<!-- store outside of target to speed up formatting when mvn clean is used -->
<cachedir>.cache/impsort-maven-plugin-${impsort-maven-plugin.version}</cachedir>
<groups>java.,javax.,jakarta.,org.,com.</groups>
<staticGroups>*</staticGroups>
<skip>${format.skip}</skip>
<removeUnused>true</removeUnused>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>

<distributionManagement>
<snapshotRepository>
<id>sonatype-nexus-snapshots</id>
<url>https://s01.oss.sonatype.org/content/repositories/snapshots</url>
</snapshotRepository>
<repository>
<id>sonatype-nexus-release</id>
<url>https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
</distributionManagement>

<profiles>
<profile>
<id>quick-build</id>
<activation>
<property>
<name>quickly</name>
</property>
</activation>
<properties>
<skipTests>true</skipTests>
<skipITs>true</skipITs>
<enforcer.skip>true</enforcer.skip>
</properties>
<build>
<defaultGoal>clean install</defaultGoal>
</build>
</profile>

<profile>
<!-- separate "quickly" profile for CI to keep local "quickly" demands separated from CI demands -->
<id>quick-build-ci</id>
<activation>
<property>
<name>quickly-ci</name>
</property>
</activation>
<properties>
<skipTests>true</skipTests>
<skipITs>true</skipITs>
<enforcer.skip>true</enforcer.skip>
<format.skip>true</format.skip>
</properties>
</profile>
<profile>
<id>format</id>
<activation>
<activeByDefault>true</activeByDefault>
<property>
<name>!no-format</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<executions>
<execution>
<phase>process-sources</phase>
<goals>
<goal>format</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>net.revelc.code</groupId>
<artifactId>impsort-maven-plugin</artifactId>
<executions>
<execution>
<id>sort-imports</id>
<goals>
<goal>sort</goal>
</goals>
</execution>
</executions>
<configuration>
<removeUnused>true</removeUnused>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>validate</id>
<activation>
<activeByDefault>true</activeByDefault>
<property>
<name>no-format</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<executions>
<execution>
<phase>process-sources</phase>
<goals>
<goal>validate</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>net.revelc.code</groupId>
<artifactId>impsort-maven-plugin</artifactId>
<configuration>
<removeUnused>true</removeUnused>
</configuration>
<executions>
<execution>
<id>check-imports</id>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>

</project>
Loading
Loading