Skip to content

Commit

Permalink
Merge pull request #34954 from cescoffier/virtual-thread-tests
Browse files Browse the repository at this point in the history
Virtual Thread ITs
  • Loading branch information
cescoffier authored Jul 25, 2023
2 parents 28579aa + 9aedcc5 commit 599879b
Show file tree
Hide file tree
Showing 45 changed files with 1,806 additions and 53 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci-actions-incremental.yml
Original file line number Diff line number Diff line change
Expand Up @@ -636,7 +636,7 @@ jobs:
java-version: ${{ matrix.java.java-version }}
- name: Run tests
run: |
export LANG=en_US && ./mvnw -e -B -fae --settings .github/mvn-settings.xml -f integration-tests/virtual-threads clean verify -Dextra-args=${{matrix.java.extra-args}}
export LANG=en_US && ./mvnw -e -B -fae --settings .github/mvn-settings.xml -f integration-tests/virtual-threads clean verify -Dnative -Dextra-args=${{matrix.java.extra-args}} -Dquarkus.native.container-build=true -Dquarkus.native.builder-image=quay.io/quarkus/ubi-quarkus-mandrel-builder-image:jdk-20
- name: Upload build reports (if build failed)
uses: actions/upload-artifact@v3
if: ${{ failure() || cancelled() }}
Expand Down

This file was deleted.

12 changes: 4 additions & 8 deletions integration-tests/virtual-threads/grpc-virtual-threads/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,10 @@
<plugin>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>generate-code</goal>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
</plugins>
</build>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package io.quarkus.grpc.example.streaming;

import java.time.Duration;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

import com.google.protobuf.ByteString;

import io.grpc.testing.integration.Messages;
import io.grpc.testing.integration.TestService;
import io.quarkus.grpc.GrpcClient;
import io.smallrye.common.annotation.RunOnVirtualThread;

@Path("/endpoint")
public class Endpoint {

@GrpcClient("service")
TestService service;

@GET
@RunOnVirtualThread
public String invokeGrpcService() {
var req = Messages.SimpleRequest.newBuilder()
.setPayload(Messages.Payload.newBuilder().setBody(ByteString.copyFromUtf8("hello")).build())
.build();
return service.unaryCall(req).await().atMost(Duration.ofSeconds(5))
.getPayload().getBody().toStringUtf8();
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
quarkus.grpc.clients.streaming.host=localhost
quarkus.grpc.clients.streaming.port=9001
quarkus.grpc.clients.service.host=localhost
quarkus.grpc.clients.service.port=9001

%vertx.quarkus.grpc.clients.streaming.port=8081
%vertx.quarkus.grpc.clients.streaming.use-quarkus-grpc-client=true
%vertx.quarkus.grpc.server.use-separate-server=false

%n2o.quarkus.grpc.server.use-separate-server=true
%o2n.quarkus.grpc.server.use-separate-server=false
quarkus.native.additional-build-args=--enable-preview

%n2o.quarkus.grpc.clients.streaming.port=9001
%n2o.quarkus.grpc.clients.streaming.use-quarkus-grpc-client=true

%o2n.quarkus.grpc.clients.streaming.port=8081
%o2n.quarkus.grpc.clients.streaming.use-quarkus-grpc-client=false
%vertx.quarkus.grpc.clients.service.port=8081
%vertx.quarkus.grpc.clients.service.use-quarkus-grpc-client=true
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package io.quarkus.grpc.example.streaming;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
* An integration test reading the output of the unit test to verify that no tests where pinning the carrier thread.
* It reads the reports generated by surefire.
*/
public class NoPinningIT {

@Test
void verify() throws IOException, ParserConfigurationException, SAXException {
var reports = new File("target", "surefire-reports");
Assertions.assertTrue(reports.isDirectory(),
"Unable to find " + reports.getAbsolutePath() + ", did you run the tests with Maven before?");
var list = reports.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.startsWith("TEST") && name.endsWith("Test.xml");
}
});
Assertions.assertNotNull(list,
"Unable to find " + reports.getAbsolutePath() + ", did you run the tests with Maven before?");

for (File report : list) {
Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(report);
var suite = document.getFirstChild();
var cases = getChildren(suite.getChildNodes(), "testcase");
for (Node c : cases) {
verify(report, c);
}
}

}

private void verify(File file, Node ca) {
var fullname = ca.getAttributes().getNamedItem("classname").getTextContent() + "."
+ ca.getAttributes().getNamedItem("name").getTextContent();
var output = getChildren(ca.getChildNodes(), "system-out");
if (output.isEmpty()) {
return;
}
var sout = output.get(0).getTextContent();
if (sout.contains("VThreadContinuation.onPinned")) {
throw new AssertionError("The test case " + fullname + " pinned the carrier thread, check " + file.getAbsolutePath()
+ " for details (or the log of the test)");
}

}

private List<Node> getChildren(NodeList nodes, String name) {
List<Node> list = new ArrayList<>();
for (int i = 0; i < nodes.getLength(); i++) {
var node = nodes.item(i);
if (node.getNodeName().equalsIgnoreCase(name)) {
list.add(node);
}
}
return list;
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.quarkus.grpc.example.streaming;

import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.is;

import java.util.concurrent.atomic.AtomicInteger;

Expand All @@ -12,6 +13,7 @@
import io.grpc.testing.integration.Messages;
import io.grpc.testing.integration.TestServiceGrpc;
import io.quarkus.grpc.GrpcClient;
import io.restassured.RestAssured;

@SuppressWarnings("NewClassNamingConvention")
public class VirtualThreadTestBase {
Expand Down Expand Up @@ -45,4 +47,12 @@ void testStreamingOutputCall() {
assertThat(count).hasValue(3);
}

@Test
void testGrpcClient() {
RestAssured.get("/endpoint")
.then()
.statusCode(200)
.body(is("HELLO"));
}

}
95 changes: 95 additions & 0 deletions integration-tests/virtual-threads/mailer-virtual-threads/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?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>
<artifactId>quarkus-virtual-threads-integration-tests-parent</artifactId>
<groupId>io.quarkus</groupId>
<version>999-SNAPSHOT</version>
</parent>

<artifactId>quarkus-integration-test-virtual-threads-mailer</artifactId>
<name>Quarkus - Integration Tests - Virtual Threads - Mailer</name>

<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-mailer</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>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<scope>test</scope>
</dependency>


<!-- Minimal test dependencies to *-deployment artifacts for consistent build order -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-jackson-deployment</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-mailer-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>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package io.quarkus.virtual.mail;

import java.lang.reflect.Method;

import io.quarkus.arc.Arc;
import io.smallrye.common.vertx.VertxContext;
import io.vertx.core.Vertx;

public class AssertHelper {

/**
* Asserts that the current method:
* - runs on a duplicated context
* - runs on a virtual thread
* - has the request scope activated
*/
public static void assertEverything() {
assertThatTheRequestScopeIsActive();
assertThatItRunsOnVirtualThread();
assertThatItRunsOnADuplicatedContext();
}

public static void assertThatTheRequestScopeIsActive() {
if (!Arc.container().requestContext().isActive()) {
throw new AssertionError(("Expected the request scope to be active"));
}
}

public static void assertThatItRunsOnADuplicatedContext() {
var context = Vertx.currentContext();
if (context == null) {
throw new AssertionError("The method does not run on a Vert.x context");
}
if (!VertxContext.isOnDuplicatedContext()) {
throw new AssertionError("The method does not run on a Vert.x **duplicated** context");
}
}

public static void assertThatItRunsOnVirtualThread() {
// We cannot depend on a Java 20.
try {
Method isVirtual = Thread.class.getMethod("isVirtual");
isVirtual.setAccessible(true);
boolean virtual = (Boolean) isVirtual.invoke(Thread.currentThread());
if (!virtual) {
throw new AssertionError("Thread " + Thread.currentThread() + " is not a virtual thread");
}
} catch (Exception e) {
throw new AssertionError(
"Thread " + Thread.currentThread() + " is not a virtual thread - cannot invoke Thread.isVirtual()", e);
}
}

public static void assertNotOnVirtualThread() {
// We cannot depend on a Java 20.
try {
Method isVirtual = Thread.class.getMethod("isVirtual");
isVirtual.setAccessible(true);
boolean virtual = (Boolean) isVirtual.invoke(Thread.currentThread());
if (virtual) {
throw new AssertionError("Thread " + Thread.currentThread() + " is a virtual thread");
}
} catch (Exception e) {
// Trying using Thread name.
var name = Thread.currentThread().toString();
if (name.toLowerCase().contains("virtual")) {
throw new AssertionError("Thread " + Thread.currentThread() + " seems to be a virtual thread");
}
}
}
}
Loading

0 comments on commit 599879b

Please sign in to comment.