Skip to content

Commit 20137ae

Browse files
Add test to run multiple instances of jpackage tool provider asynchronously
1 parent 3dc11cb commit 20137ae

File tree

1 file changed

+200
-0
lines changed

1 file changed

+200
-0
lines changed
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
import java.io.ByteArrayOutputStream;
25+
import java.io.PrintStream;
26+
import java.io.PrintWriter;
27+
import java.nio.charset.StandardCharsets;
28+
import java.util.Collection;
29+
import java.util.List;
30+
import java.util.Objects;
31+
import java.util.Optional;
32+
import java.util.concurrent.Callable;
33+
import java.util.concurrent.Executors;
34+
import java.util.concurrent.TimeUnit;
35+
import java.util.function.Predicate;
36+
import java.util.spi.ToolProvider;
37+
import java.util.stream.IntStream;
38+
import jdk.jpackage.internal.util.function.ThrowingRunnable;
39+
import jdk.jpackage.test.Annotations.ParameterSupplier;
40+
import jdk.jpackage.test.Annotations.Test;
41+
import jdk.jpackage.test.JPackageCommand;
42+
import jdk.jpackage.test.JavaTool;
43+
import jdk.jpackage.test.Main;
44+
import jdk.jpackage.test.PackageTest;
45+
import jdk.jpackage.test.RunnablePackageTest.Action;
46+
import jdk.jpackage.test.TKit;
47+
48+
/*
49+
* @test
50+
* @summary Runs multiple jpackage tool provider instances asynchronously
51+
* @library /test/jdk/tools/jpackage/helpers
52+
* @build jdk.jpackage.test.*
53+
* @compile -Xlint:all -Werror AsyncTest.java
54+
* @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main
55+
* --jpt-run=AsyncTest
56+
*/
57+
public class AsyncTest {
58+
59+
@Test
60+
public void test() throws Throwable {
61+
//
62+
// Run test cases from InternalAsyncTest class asynchronously.
63+
// Spawn a thread for every test case.
64+
// Input data for test cases will be cooked asynchronously but in a safe way because every test case has an isolated work directory.
65+
// Multiple jpackage tool provider instances will be invoked asynchronously.
66+
//
67+
68+
var executor = Executors.newVirtualThreadPerTaskExecutor();
69+
70+
var testFuncNames = List.of("testAppImage", "testNativeBundle");
71+
72+
var runArg = String.format("--jpt-run=%s", InternalAsyncTest.class.getName());
73+
74+
var futures = executor.invokeAll(IntStream.range(0, JOB_COUNT).mapToObj(Integer::toString).<Workload>mapMulti((idx, consumer) -> {
75+
for (var testFuncName : testFuncNames) {
76+
var id = String.format("%s(%s)", testFuncName, idx);
77+
consumer.accept(new Workload(() -> {
78+
Main.main(runArg, String.format("--jpt-include=%s", id));
79+
}, id));
80+
}
81+
}).toList());
82+
83+
Throwable[] fatalError = new Throwable[1];
84+
85+
for (var future : futures) {
86+
var result = future.get(3, TimeUnit.MINUTES);
87+
TKit.trace(String.format("[%s] STDOUT BEGIN\n%s", result.id(), result.stdoutBuffer()));
88+
TKit.trace(String.format("[%s] STDOUT END", result.id()));
89+
TKit.trace(String.format("[%s] STDERR BEGIN\n%s", result.id(), result.stderrBuffer()));
90+
TKit.trace(String.format("[%s] STDERR END", result.id()));
91+
result.throwable().filter(Predicate.not(TKit::isSkippedException)).ifPresent(t -> {
92+
fatalError[0] = t;
93+
});
94+
}
95+
96+
if (fatalError[0] != null) {
97+
throw fatalError[0];
98+
}
99+
}
100+
101+
public static final class InternalAsyncTest {
102+
103+
@Test
104+
@ParameterSupplier("ids")
105+
public void testAppImage(int id) throws Exception {
106+
init(JPackageCommand.helloAppImage()).executeAndAssertHelloAppImageCreated();
107+
}
108+
109+
@Test
110+
@ParameterSupplier("ids")
111+
public void testNativeBundle(int id) throws Exception {
112+
new PackageTest().configureHelloApp().addInitializer(cmd -> {
113+
init(cmd);
114+
}).run(Action.CREATE_AND_UNPACK);
115+
}
116+
117+
public static Collection<Object[]> ids() {
118+
return IntStream.range(0, JOB_COUNT).mapToObj(Integer::valueOf).map(v -> {
119+
return new Object[] {v};
120+
}).toList();
121+
}
122+
}
123+
124+
private static JPackageCommand init(JPackageCommand cmd) {
125+
return cmd.useToolProvider(true).setFakeRuntime().setArgumentValue("--name", "Foo");
126+
}
127+
128+
129+
private record Result(String stdoutBuffer, String stderrBuffer, String id, Optional<Throwable> throwable) {
130+
131+
Result {
132+
Objects.requireNonNull(stdoutBuffer);
133+
Objects.requireNonNull(stderrBuffer);
134+
Objects.requireNonNull(id);
135+
Objects.requireNonNull(throwable);
136+
}
137+
}
138+
139+
140+
private record Workload(
141+
ByteArrayOutputStream stdoutBuffer,
142+
ByteArrayOutputStream stderrBuffer,
143+
ThrowingRunnable runnable,
144+
String id) implements Callable<Result> {
145+
146+
Workload {
147+
Objects.requireNonNull(stdoutBuffer);
148+
Objects.requireNonNull(stderrBuffer);
149+
Objects.requireNonNull(runnable);
150+
Objects.requireNonNull(id);
151+
}
152+
153+
Workload(ThrowingRunnable runnable, String id) {
154+
this(new ByteArrayOutputStream(), new ByteArrayOutputStream(), runnable, id);
155+
}
156+
157+
private String stdoutBufferAsString() {
158+
return new String(stdoutBuffer.toByteArray(), StandardCharsets.UTF_8);
159+
}
160+
161+
private String stderrBufferAsString() {
162+
return new String(stderrBuffer.toByteArray(), StandardCharsets.UTF_8);
163+
}
164+
165+
@Override
166+
public Result call() {
167+
var defaultToolProvider = JavaTool.JPACKAGE.asToolProvider();
168+
169+
JPackageCommand.useToolProviderByDefault(new ToolProvider() {
170+
171+
@Override
172+
public int run(PrintWriter out, PrintWriter err, String... args) {
173+
try (var bufOut = new PrintWriter(stdoutBuffer, true, StandardCharsets.UTF_8);
174+
var bufErr = new PrintWriter(stderrBuffer, true, StandardCharsets.UTF_8)) {
175+
return defaultToolProvider.run(bufOut, bufErr, args);
176+
}
177+
}
178+
179+
@Override
180+
public String name() {
181+
return defaultToolProvider.name();
182+
}
183+
});
184+
185+
Optional<Throwable> err = Optional.empty();
186+
try (var bufOut = new PrintStream(stdoutBuffer, true, StandardCharsets.UTF_8);
187+
var bufErr = new PrintStream(stderrBuffer, true, StandardCharsets.UTF_8)) {
188+
TKit.withStackTraceStream(() -> {
189+
TKit.withMainLogStream(runnable, bufOut);
190+
}, bufErr);
191+
} catch (Throwable t) {
192+
err = Optional.of(t);
193+
}
194+
return new Result(stdoutBufferAsString(), stderrBufferAsString(), id, err);
195+
}
196+
}
197+
198+
199+
private static final int JOB_COUNT = 10;
200+
}

0 commit comments

Comments
 (0)