Skip to content

Commit a2e63a8

Browse files
committed
test
1 parent 3b099b5 commit a2e63a8

4 files changed

Lines changed: 233 additions & 32 deletions

File tree

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Eclipse RDF4J contributors.
3+
*
4+
* All rights reserved. This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Distribution License v1.0
6+
* which accompanies this distribution, and is available at
7+
* http://www.eclipse.org/org/documents/edl-v10.php.
8+
*
9+
* SPDX-License-Identifier: BSD-3-Clause
10+
*******************************************************************************/
11+
// Some portions generated by Codex
12+
13+
package org.eclipse.rdf4j.common.io;
14+
15+
import static org.assertj.core.api.Assertions.assertThat;
16+
17+
import java.io.IOException;
18+
import java.nio.file.Files;
19+
import java.nio.file.Path;
20+
import java.util.List;
21+
import java.util.stream.Collectors;
22+
23+
import org.junit.jupiter.api.Test;
24+
25+
class CiThreadDumpWorkflowTest {
26+
27+
@Test
28+
void threadDumpWrapperUsesExecInWorkflow() throws IOException {
29+
Path workflow = findWorkflow();
30+
List<String> offenders = Files.readAllLines(workflow)
31+
.stream()
32+
.filter(line -> line.contains("run-with-thread-dump.sh"))
33+
.filter(line -> !line.contains("exec "))
34+
.collect(Collectors.toList());
35+
36+
assertThat(offenders)
37+
.withFailMessage("Expected exec wrapper for run-with-thread-dump.sh in %s but found:%n%s",
38+
workflow, String.join(System.lineSeparator(), offenders))
39+
.isEmpty();
40+
}
41+
42+
@Test
43+
void registersThreadDumpPostBeforeTests() throws IOException {
44+
Path workflow = findWorkflow();
45+
List<String> lines = Files.readAllLines(workflow);
46+
int registrationIndex = indexOfLineContaining(lines, "uses: ./.github/actions/thread-dump-post");
47+
int firstRunIndex = indexOfLineContaining(lines, "run-with-thread-dump.sh");
48+
49+
assertThat(registrationIndex)
50+
.withFailMessage("Expected thread dump post action registration in %s", workflow)
51+
.isGreaterThanOrEqualTo(0);
52+
assertThat(firstRunIndex)
53+
.withFailMessage("Expected run-with-thread-dump.sh invocation in %s", workflow)
54+
.isGreaterThanOrEqualTo(0);
55+
assertThat(registrationIndex)
56+
.withFailMessage("Expected thread dump post action to be registered before tests in %s", workflow)
57+
.isLessThan(firstRunIndex);
58+
}
59+
60+
private Path findWorkflow() {
61+
Path current = Path.of("").toAbsolutePath();
62+
while (current != null) {
63+
Path candidate = current.resolve(".github/workflows/pr-verify.yml");
64+
if (Files.exists(candidate)) {
65+
return candidate;
66+
}
67+
current = current.getParent();
68+
}
69+
throw new IllegalStateException("Could not locate .github/workflows/pr-verify.yml");
70+
}
71+
72+
private int indexOfLineContaining(List<String> lines, String needle) {
73+
for (int i = 0; i < lines.size(); i++) {
74+
if (lines.get(i).contains(needle)) {
75+
return i;
76+
}
77+
}
78+
return -1;
79+
}
80+
}

pom.xml

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,38 @@
4949
<!-- for maven Central deployment it is important that bom is the last module -->
5050
<module>bom</module>
5151
</modules>
52+
<properties>
53+
<java.version>11</java.version>
54+
<maven.compiler.release>${java.version}</maven.compiler.release>
55+
<maven.compiler.source>${java.version}</maven.compiler.source>
56+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
57+
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
58+
<enforce-javaee-provided.fail>true</enforce-javaee-provided.fail>
59+
<slf4j.version>1.7.36</slf4j.version>
60+
<logback.version>1.2.13</logback.version>
61+
<log4j.version>2.17.2</log4j.version>
62+
<httpclient.version>4.5.14</httpclient.version>
63+
<jackson.version>2.13.5</jackson.version>
64+
<httpcore.version>4.4.16</httpcore.version>
65+
<jsonldjava.version>0.13.4</jsonldjava.version>
66+
<last.japicmp.compare.version>5.0.0</last.japicmp.compare.version>
67+
<jaxb.version>2.3.8</jaxb.version>
68+
<lwjgl.version>3.3.6</lwjgl.version>
69+
<lucene.version>8.9.0</lucene.version>
70+
<solr.version>8.9.0</solr.version>
71+
<elasticsearch.version>7.15.2</elasticsearch.version>
72+
<spring.version>5.3.39</spring.version>
73+
<guava.version>32.1.3-jre</guava.version>
74+
<jmhVersion>1.37</jmhVersion>
75+
<servlet.version>4.0.0</servlet.version>
76+
<junit.version>5.9.3</junit.version>
77+
<jetty.version>9.4.54.v20240208</jetty.version>
78+
<netty.version>4.1.111.Final</netty.version>
79+
<test.timeout.seconds>14400</test.timeout.seconds>
80+
<test.timeout.agent.jar>${maven.multiModuleProjectDirectory}/test-timeout-agent/target/rdf4j-test-timeout-agent-${project.version}.jar</test.timeout.agent.jar>
81+
<testcontainers.version>1.20.6</testcontainers.version>
82+
<argLine/>
83+
</properties>
5284
<profiles>
5385
<profile>
5486
<id>ossrh</id>
@@ -378,38 +410,6 @@
378410
</build>
379411
</profile>
380412
</profiles>
381-
<properties>
382-
<java.version>11</java.version>
383-
<maven.compiler.release>${java.version}</maven.compiler.release>
384-
<maven.compiler.source>${java.version}</maven.compiler.source>
385-
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
386-
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
387-
<enforce-javaee-provided.fail>true</enforce-javaee-provided.fail>
388-
<slf4j.version>1.7.36</slf4j.version>
389-
<logback.version>1.2.13</logback.version>
390-
<log4j.version>2.17.2</log4j.version>
391-
<httpclient.version>4.5.14</httpclient.version>
392-
<jackson.version>2.13.5</jackson.version>
393-
<httpcore.version>4.4.16</httpcore.version>
394-
<jsonldjava.version>0.13.4</jsonldjava.version>
395-
<last.japicmp.compare.version>5.0.0</last.japicmp.compare.version>
396-
<jaxb.version>2.3.8</jaxb.version>
397-
<lwjgl.version>3.3.6</lwjgl.version>
398-
<lucene.version>8.9.0</lucene.version>
399-
<solr.version>8.9.0</solr.version>
400-
<elasticsearch.version>7.15.2</elasticsearch.version>
401-
<spring.version>5.3.39</spring.version>
402-
<guava.version>32.1.3-jre</guava.version>
403-
<jmhVersion>1.37</jmhVersion>
404-
<servlet.version>4.0.0</servlet.version>
405-
<junit.version>5.9.3</junit.version>
406-
<jetty.version>9.4.54.v20240208</jetty.version>
407-
<netty.version>4.1.111.Final</netty.version>
408-
<test.timeout.seconds>14400</test.timeout.seconds>
409-
<test.timeout.agent.jar>${maven.multiModuleProjectDirectory}/test-timeout-agent/target/rdf4j-test-timeout-agent-${project.version}.jar</test.timeout.agent.jar>
410-
<testcontainers.version>1.20.6</testcontainers.version>
411-
<argLine/>
412-
</properties>
413413
<dependencyManagement>
414414
<dependencies>
415415
<!-- Jackson Bill-of-Materials -->

test-timeout-agent/pom.xml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<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/maven-v4_0_0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
<parent>
5+
<groupId>org.eclipse.rdf4j</groupId>
6+
<artifactId>rdf4j</artifactId>
7+
<version>5.3.0-SNAPSHOT</version>
8+
</parent>
9+
<artifactId>rdf4j-test-timeout-agent</artifactId>
10+
<name>RDF4J: Test Timeout Agent</name>
11+
<description>Java agent that prints thread dumps when tests time out.</description>
12+
<build>
13+
<plugins>
14+
<plugin>
15+
<groupId>org.apache.maven.plugins</groupId>
16+
<artifactId>maven-jar-plugin</artifactId>
17+
<configuration>
18+
<archive>
19+
<manifestEntries>
20+
<Premain-Class>org.eclipse.rdf4j.build.TestTimeoutAgent</Premain-Class>
21+
</manifestEntries>
22+
</archive>
23+
</configuration>
24+
</plugin>
25+
</plugins>
26+
</build>
27+
</project>
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Eclipse RDF4J contributors.
3+
*
4+
* All rights reserved. This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Distribution License v1.0
6+
* which accompanies this distribution, and is available at
7+
* http://www.eclipse.org/org/documents/edl-v10.php.
8+
*
9+
* SPDX-License-Identifier: BSD-3-Clause
10+
*******************************************************************************/
11+
// Some portions generated by Codex
12+
package org.eclipse.rdf4j.build;
13+
14+
import java.lang.management.ManagementFactory;
15+
import java.lang.management.ThreadInfo;
16+
import java.lang.management.ThreadMXBean;
17+
import java.util.concurrent.Executors;
18+
import java.util.concurrent.ScheduledExecutorService;
19+
import java.util.concurrent.ThreadFactory;
20+
import java.util.concurrent.TimeUnit;
21+
22+
public final class TestTimeoutAgent {
23+
private static final String TIMEOUT_PROPERTY = "test.timeout.seconds";
24+
25+
private TestTimeoutAgent() {
26+
}
27+
28+
public static void premain(String agentArgs) {
29+
long timeoutSeconds = parseTimeoutSeconds(agentArgs);
30+
if (timeoutSeconds <= 0) {
31+
return;
32+
}
33+
34+
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new TimeoutThreadFactory());
35+
scheduler.schedule(() -> dumpAndHalt(timeoutSeconds), timeoutSeconds, TimeUnit.SECONDS);
36+
}
37+
38+
private static long parseTimeoutSeconds(String agentArgs) {
39+
Long parsed = parseLong(agentArgs);
40+
if (parsed != null) {
41+
return parsed;
42+
}
43+
Long propertyValue = parseLong(System.getProperty(TIMEOUT_PROPERTY));
44+
return propertyValue == null ? 0L : propertyValue;
45+
}
46+
47+
private static Long parseLong(String value) {
48+
if (value == null) {
49+
return null;
50+
}
51+
String trimmed = value.trim();
52+
if (trimmed.isEmpty()) {
53+
return null;
54+
}
55+
try {
56+
return Long.parseLong(trimmed);
57+
} catch (NumberFormatException e) {
58+
return null;
59+
}
60+
}
61+
62+
private static void dumpAndHalt(long timeoutSeconds) {
63+
try {
64+
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
65+
boolean includeMonitors = threadMXBean.isObjectMonitorUsageSupported();
66+
boolean includeSynchronizers = threadMXBean.isSynchronizerUsageSupported();
67+
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(includeMonitors, includeSynchronizers);
68+
StringBuilder dump = new StringBuilder(16384);
69+
dump.append("=== Test timeout after ").append(timeoutSeconds).append(" seconds ===\n");
70+
if (threadInfos != null) {
71+
for (ThreadInfo threadInfo : threadInfos) {
72+
if (threadInfo != null) {
73+
dump.append(threadInfo.toString());
74+
}
75+
}
76+
}
77+
System.err.print(dump.toString());
78+
System.err.flush();
79+
} catch (Throwable throwable) {
80+
throwable.printStackTrace(System.err);
81+
} finally {
82+
Runtime.getRuntime().halt(1);
83+
}
84+
}
85+
86+
private static final class TimeoutThreadFactory implements ThreadFactory {
87+
@Override
88+
public Thread newThread(Runnable runnable) {
89+
Thread thread = new Thread(runnable, "test-timeout-watchdog");
90+
thread.setDaemon(true);
91+
return thread;
92+
}
93+
}
94+
}

0 commit comments

Comments
 (0)