8343876: Enhancements to jpackage test lib
Reviewed-by: almatvee
This commit is contained in:
parent
aa10ec7c96
commit
41a627b789
@ -0,0 +1,408 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
package jdk.jpackage.test;
|
||||||
|
|
||||||
|
import static java.lang.StackWalker.Option.RETAIN_CLASS_REFERENCE;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import static java.util.stream.Collectors.toMap;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import jdk.internal.util.OperatingSystem;
|
||||||
|
import static jdk.internal.util.OperatingSystem.LINUX;
|
||||||
|
import jdk.jpackage.test.Annotations.Parameter;
|
||||||
|
import jdk.jpackage.test.Annotations.ParameterSupplier;
|
||||||
|
import jdk.jpackage.test.Annotations.Parameters;
|
||||||
|
import jdk.jpackage.test.Annotations.Test;
|
||||||
|
import static jdk.jpackage.test.Functional.ThrowingSupplier.toSupplier;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @summary Test jpackage test library's annotation processor
|
||||||
|
* @library /test/jdk/tools/jpackage/helpers
|
||||||
|
* @build jdk.jpackage.test.*
|
||||||
|
* @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.AnnotationsTest
|
||||||
|
*/
|
||||||
|
public class AnnotationsTest {
|
||||||
|
|
||||||
|
public static void main(String... args) {
|
||||||
|
runTests(BasicTest.class, ParameterizedInstanceTest.class);
|
||||||
|
for (var os : OperatingSystem.values()) {
|
||||||
|
try {
|
||||||
|
TestBuilderConfig.setOperatingSystem(os);
|
||||||
|
TKit.log("Current operating system: " + os);
|
||||||
|
runTests(IfOSTest.class);
|
||||||
|
} finally {
|
||||||
|
TestBuilderConfig.setDefaults();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BasicTest extends TestExecutionRecorder {
|
||||||
|
@Test
|
||||||
|
public void testNoArg() {
|
||||||
|
recordTestCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Parameter("TRUE")
|
||||||
|
public int testNoArg(boolean v) {
|
||||||
|
recordTestCase(v);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Parameter({})
|
||||||
|
@Parameter("a")
|
||||||
|
@Parameter({"b", "c"})
|
||||||
|
public void testVarArg(Path ... paths) {
|
||||||
|
recordTestCase((Object[]) paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Parameter({"12", "foo"})
|
||||||
|
@Parameter({"-89", "bar", "more"})
|
||||||
|
@Parameter({"-89", "bar", "more", "moore"})
|
||||||
|
public void testVarArg2(int a, String b, String ... other) {
|
||||||
|
recordTestCase(a, b, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@ParameterSupplier("dateSupplier")
|
||||||
|
@ParameterSupplier("jdk.jpackage.test.AnnotationsTest.dateSupplier")
|
||||||
|
public void testDates(LocalDate v) {
|
||||||
|
recordTestCase(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<String> getExpectedTestDescs() {
|
||||||
|
return Set.of(
|
||||||
|
"().testNoArg()",
|
||||||
|
"().testNoArg(true)",
|
||||||
|
"().testVarArg()",
|
||||||
|
"().testVarArg(a)",
|
||||||
|
"().testVarArg(b, c)",
|
||||||
|
"().testVarArg2(-89, bar, [more, moore](length=2))",
|
||||||
|
"().testVarArg2(-89, bar, [more](length=1))",
|
||||||
|
"().testVarArg2(12, foo, [](length=0))",
|
||||||
|
"().testDates(2018-05-05)",
|
||||||
|
"().testDates(2018-07-11)",
|
||||||
|
"().testDates(2034-05-05)",
|
||||||
|
"().testDates(2056-07-11)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Collection<Object[]> dateSupplier() {
|
||||||
|
return List.of(new Object[][] {
|
||||||
|
{ LocalDate.parse("2018-05-05") },
|
||||||
|
{ LocalDate.parse("2018-07-11") },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ParameterizedInstanceTest extends TestExecutionRecorder {
|
||||||
|
public ParameterizedInstanceTest(String... args) {
|
||||||
|
super((Object[]) args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ParameterizedInstanceTest(int o) {
|
||||||
|
super(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ParameterizedInstanceTest(int a, Boolean[] b, String c, String ... other) {
|
||||||
|
super(a, b, c, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoArgs() {
|
||||||
|
recordTestCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@ParameterSupplier("jdk.jpackage.test.AnnotationsTest.dateSupplier")
|
||||||
|
public void testDates(LocalDate v) {
|
||||||
|
recordTestCase(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Parameter("a")
|
||||||
|
public static void staticTest(String arg) {
|
||||||
|
staticRecorder.recordTestCase(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Parameters
|
||||||
|
public static Collection<Object[]> input() {
|
||||||
|
return List.of(new Object[][] {
|
||||||
|
{},
|
||||||
|
{55, new Boolean[]{false, true, false}, "foo", "bar"},
|
||||||
|
{78},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Parameters
|
||||||
|
public static Collection<Object[]> input2() {
|
||||||
|
return List.of(new Object[][] {
|
||||||
|
{51, new boolean[]{true, true, true}, "foo"},
|
||||||
|
{33},
|
||||||
|
{55, null, null },
|
||||||
|
{55, null, null, "1" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<String> getExpectedTestDescs() {
|
||||||
|
return Set.of(
|
||||||
|
"().testNoArgs()",
|
||||||
|
"(33).testNoArgs()",
|
||||||
|
"(78).testNoArgs()",
|
||||||
|
"(55, [false, true, false](length=3), foo, [bar](length=1)).testNoArgs()",
|
||||||
|
"(51, [true, true, true](length=3), foo, [](length=0)).testNoArgs()",
|
||||||
|
"().testDates(2034-05-05)",
|
||||||
|
"().testDates(2056-07-11)",
|
||||||
|
"(33).testDates(2034-05-05)",
|
||||||
|
"(33).testDates(2056-07-11)",
|
||||||
|
"(51, [true, true, true](length=3), foo, [](length=0)).testDates(2034-05-05)",
|
||||||
|
"(51, [true, true, true](length=3), foo, [](length=0)).testDates(2056-07-11)",
|
||||||
|
"(55, [false, true, false](length=3), foo, [bar](length=1)).testDates(2034-05-05)",
|
||||||
|
"(55, [false, true, false](length=3), foo, [bar](length=1)).testDates(2056-07-11)",
|
||||||
|
"(78).testDates(2034-05-05)",
|
||||||
|
"(78).testDates(2056-07-11)",
|
||||||
|
"(55, null, null, [1](length=1)).testDates(2034-05-05)",
|
||||||
|
"(55, null, null, [1](length=1)).testDates(2056-07-11)",
|
||||||
|
"(55, null, null, [1](length=1)).testNoArgs()",
|
||||||
|
"(55, null, null, [](length=0)).testDates(2034-05-05)",
|
||||||
|
"(55, null, null, [](length=0)).testDates(2056-07-11)",
|
||||||
|
"(55, null, null, [](length=0)).testNoArgs()",
|
||||||
|
"().staticTest(a)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static TestExecutionRecorder staticRecorder = new TestExecutionRecorder(ParameterizedInstanceTest.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class IfOSTest extends TestExecutionRecorder {
|
||||||
|
public IfOSTest(int a, String b) {
|
||||||
|
super(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(ifOS = OperatingSystem.LINUX)
|
||||||
|
public void testNoArgs() {
|
||||||
|
recordTestCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(ifNotOS = OperatingSystem.LINUX)
|
||||||
|
public void testNoArgs2() {
|
||||||
|
recordTestCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Parameter(value = "foo", ifOS = OperatingSystem.LINUX)
|
||||||
|
@Parameter(value = {"foo", "bar"}, ifOS = { OperatingSystem.LINUX, OperatingSystem.MACOS })
|
||||||
|
@Parameter(value = {}, ifNotOS = { OperatingSystem.WINDOWS })
|
||||||
|
public void testVarArgs(String ... args) {
|
||||||
|
recordTestCase((Object[]) args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@ParameterSupplier(value = "jdk.jpackage.test.AnnotationsTest.dateSupplier", ifOS = OperatingSystem.WINDOWS)
|
||||||
|
public void testDates(LocalDate v) {
|
||||||
|
recordTestCase(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Parameters(ifOS = OperatingSystem.LINUX)
|
||||||
|
public static Collection<Object[]> input() {
|
||||||
|
return Set.of(new Object[][] {
|
||||||
|
{7, null},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Parameters(ifNotOS = {OperatingSystem.LINUX, OperatingSystem.MACOS})
|
||||||
|
public static Collection<Object[]> input2() {
|
||||||
|
return Set.of(new Object[][] {
|
||||||
|
{10, "hello"},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Parameters(ifNotOS = OperatingSystem.LINUX)
|
||||||
|
public static Collection<Object[]> input3() {
|
||||||
|
return Set.of(new Object[][] {
|
||||||
|
{15, "bye"},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<String> getExpectedTestDescs() {
|
||||||
|
switch (TestBuilderConfig.getDefault().getOperatingSystem()) {
|
||||||
|
case LINUX -> {
|
||||||
|
return Set.of(
|
||||||
|
"(7, null).testNoArgs()",
|
||||||
|
"(7, null).testVarArgs()",
|
||||||
|
"(7, null).testVarArgs(foo)",
|
||||||
|
"(7, null).testVarArgs(foo, bar)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
case MACOS -> {
|
||||||
|
return Set.of(
|
||||||
|
"(15, bye).testNoArgs2()",
|
||||||
|
"(15, bye).testVarArgs()",
|
||||||
|
"(15, bye).testVarArgs(foo, bar)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
case WINDOWS -> {
|
||||||
|
return Set.of(
|
||||||
|
"(15, bye).testDates(2034-05-05)",
|
||||||
|
"(15, bye).testDates(2056-07-11)",
|
||||||
|
"(15, bye).testNoArgs2()",
|
||||||
|
"(10, hello).testDates(2034-05-05)",
|
||||||
|
"(10, hello).testDates(2056-07-11)",
|
||||||
|
"(10, hello).testNoArgs2()"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
case AIX -> {
|
||||||
|
return Set.of(
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Collection<Object[]> dateSupplier() {
|
||||||
|
return List.of(new Object[][] {
|
||||||
|
{ LocalDate.parse("2034-05-05") },
|
||||||
|
{ LocalDate.parse("2056-07-11") },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void runTests(Class<? extends TestExecutionRecorder>... tests) {
|
||||||
|
ACTUAL_TEST_DESCS.get().clear();
|
||||||
|
|
||||||
|
var expectedTestDescs = Stream.of(tests)
|
||||||
|
.map(AnnotationsTest::getExpectedTestDescs)
|
||||||
|
.flatMap(x -> x)
|
||||||
|
// Collect in the map to check for collisions for free
|
||||||
|
.collect(toMap(x -> x, x -> ""))
|
||||||
|
.keySet();
|
||||||
|
|
||||||
|
var args = Stream.of(tests).map(test -> {
|
||||||
|
return String.format("--jpt-run=%s", test.getName());
|
||||||
|
}).toArray(String[]::new);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Main.main(args);
|
||||||
|
assertRecordedTestDescs(expectedTestDescs);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
t.printStackTrace(System.err);
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Stream<String> getExpectedTestDescs(Class<?> type) {
|
||||||
|
return toSupplier(() -> {
|
||||||
|
var method = type.getMethod("getExpectedTestDescs");
|
||||||
|
var testDescPefix = type.getName();
|
||||||
|
return ((Set<String>)method.invoke(null)).stream().map(desc -> {
|
||||||
|
return testDescPefix + desc;
|
||||||
|
});
|
||||||
|
}).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertRecordedTestDescs(Set<String> expectedTestDescs) {
|
||||||
|
var comm = Comm.compare(expectedTestDescs, ACTUAL_TEST_DESCS.get());
|
||||||
|
if (!comm.unique1().isEmpty()) {
|
||||||
|
System.err.println("Missing test case signatures:");
|
||||||
|
comm.unique1().stream().sorted().sequential().forEachOrdered(System.err::println);
|
||||||
|
System.err.println("<>");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!comm.unique2().isEmpty()) {
|
||||||
|
System.err.println("Unexpected test case signatures:");
|
||||||
|
comm.unique2().stream().sorted().sequential().forEachOrdered(System.err::println);
|
||||||
|
System.err.println("<>");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!comm.unique2().isEmpty() || !comm.unique1().isEmpty()) {
|
||||||
|
// Don't use TKit asserts as this call is outside the test execution
|
||||||
|
throw new AssertionError("Test case signatures mismatched");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TestExecutionRecorder {
|
||||||
|
protected TestExecutionRecorder(Object ... args) {
|
||||||
|
this.testClass = getClass();
|
||||||
|
this.testDescBuilder = TestInstance.TestDesc.createBuilder().ctorArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
TestExecutionRecorder(Class<?> testClass) {
|
||||||
|
this.testClass = testClass;
|
||||||
|
this.testDescBuilder = TestInstance.TestDesc.createBuilder().ctorArgs();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void recordTestCase(Object ... args) {
|
||||||
|
testDescBuilder.methodArgs(args).method(getCurrentTestCase());
|
||||||
|
var testCaseDescs = ACTUAL_TEST_DESCS.get();
|
||||||
|
var testCaseDesc = testDescBuilder.get().testFullName();
|
||||||
|
TKit.assertTrue(!testCaseDescs.contains(testCaseDesc), String.format(
|
||||||
|
"Check this test case is executed for the first time",
|
||||||
|
testCaseDesc));
|
||||||
|
TKit.assertTrue(!executed, "Check this test case instance is not reused");
|
||||||
|
executed = true;
|
||||||
|
testCaseDescs.add(testCaseDesc);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Method getCurrentTestCase() {
|
||||||
|
return StackWalker.getInstance(RETAIN_CLASS_REFERENCE).walk(frames -> {
|
||||||
|
return frames.map(frame -> {
|
||||||
|
var methodType = frame.getMethodType();
|
||||||
|
var methodName = frame.getMethodName();
|
||||||
|
var methodReturn = methodType.returnType();
|
||||||
|
var methodParameters = methodType.parameterArray();
|
||||||
|
return Stream.of(testClass.getDeclaredMethods()).filter(method -> {
|
||||||
|
return method.getName().equals(methodName)
|
||||||
|
&& method.getReturnType().equals(methodReturn)
|
||||||
|
&& Arrays.equals(method.getParameterTypes(), methodParameters)
|
||||||
|
&& method.isAnnotationPresent(Test.class);
|
||||||
|
}).findFirst();
|
||||||
|
}).dropWhile(Optional::isEmpty).map(Optional::get).findFirst();
|
||||||
|
}).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean executed;
|
||||||
|
private final TestInstance.TestDesc.Builder testDescBuilder;
|
||||||
|
private final Class<?> testClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final ThreadLocal<Set<String>> ACTUAL_TEST_DESCS = new ThreadLocal<>() {
|
||||||
|
@Override
|
||||||
|
protected Set<String> initialValue() {
|
||||||
|
return new HashSet<>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -27,6 +27,7 @@ import java.lang.annotation.Repeatable;
|
|||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
import jdk.internal.util.OperatingSystem;
|
||||||
|
|
||||||
public class Annotations {
|
public class Annotations {
|
||||||
|
|
||||||
@ -43,6 +44,14 @@ public class Annotations {
|
|||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
public @interface Test {
|
public @interface Test {
|
||||||
|
|
||||||
|
OperatingSystem[] ifOS() default {
|
||||||
|
OperatingSystem.LINUX,
|
||||||
|
OperatingSystem.WINDOWS,
|
||||||
|
OperatingSystem.MACOS
|
||||||
|
};
|
||||||
|
|
||||||
|
OperatingSystem[] ifNotOS() default {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@ -51,6 +60,14 @@ public class Annotations {
|
|||||||
public @interface Parameter {
|
public @interface Parameter {
|
||||||
|
|
||||||
String[] value();
|
String[] value();
|
||||||
|
|
||||||
|
OperatingSystem[] ifOS() default {
|
||||||
|
OperatingSystem.LINUX,
|
||||||
|
OperatingSystem.WINDOWS,
|
||||||
|
OperatingSystem.MACOS
|
||||||
|
};
|
||||||
|
|
||||||
|
OperatingSystem[] ifNotOS() default {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@ -60,8 +77,39 @@ public class Annotations {
|
|||||||
Parameter[] value();
|
Parameter[] value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Repeatable(ParameterSupplierGroup.class)
|
||||||
|
public @interface ParameterSupplier {
|
||||||
|
|
||||||
|
String value();
|
||||||
|
|
||||||
|
OperatingSystem[] ifOS() default {
|
||||||
|
OperatingSystem.LINUX,
|
||||||
|
OperatingSystem.WINDOWS,
|
||||||
|
OperatingSystem.MACOS
|
||||||
|
};
|
||||||
|
|
||||||
|
OperatingSystem[] ifNotOS() default {};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
public @interface ParameterSupplierGroup {
|
||||||
|
|
||||||
|
ParameterSupplier[] value();
|
||||||
|
}
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
public @interface Parameters {
|
public @interface Parameters {
|
||||||
|
|
||||||
|
OperatingSystem[] ifOS() default {
|
||||||
|
OperatingSystem.LINUX,
|
||||||
|
OperatingSystem.WINDOWS,
|
||||||
|
OperatingSystem.MACOS
|
||||||
|
};
|
||||||
|
|
||||||
|
OperatingSystem[] ifNotOS() default {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
39
test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Comm.java
Normal file
39
test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Comm.java
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
package jdk.jpackage.test;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
record Comm<T>(Set<T> common, Set<T> unique1, Set<T> unique2) {
|
||||||
|
|
||||||
|
static <T> Comm<T> compare(Set<T> a, Set<T> b) {
|
||||||
|
Set<T> common = new HashSet<>(a);
|
||||||
|
common.retainAll(b);
|
||||||
|
Set<T> unique1 = new HashSet<>(a);
|
||||||
|
unique1.removeAll(common);
|
||||||
|
Set<T> unique2 = new HashSet<>(b);
|
||||||
|
unique2.removeAll(common);
|
||||||
|
return new Comm(common, unique1, unique2);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -40,6 +40,7 @@ import java.util.regex.Matcher;
|
|||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
import jdk.jpackage.internal.ApplicationLayout;
|
||||||
import jdk.jpackage.internal.IOUtils;
|
import jdk.jpackage.internal.IOUtils;
|
||||||
import jdk.jpackage.test.Functional.ThrowingConsumer;
|
import jdk.jpackage.test.Functional.ThrowingConsumer;
|
||||||
import jdk.jpackage.test.PackageTest.PackageHandlers;
|
import jdk.jpackage.test.PackageTest.PackageHandlers;
|
||||||
@ -399,6 +400,15 @@ public final class LinuxHelper {
|
|||||||
private static void verifyDesktopFile(JPackageCommand cmd, Path desktopFile)
|
private static void verifyDesktopFile(JPackageCommand cmd, Path desktopFile)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
TKit.trace(String.format("Check [%s] file BEGIN", desktopFile));
|
TKit.trace(String.format("Check [%s] file BEGIN", desktopFile));
|
||||||
|
|
||||||
|
var launcherName = Stream.of(List.of(cmd.name()), cmd.addLauncherNames()).flatMap(List::stream).filter(name -> {
|
||||||
|
return getDesktopFile(cmd, name).equals(desktopFile);
|
||||||
|
}).findAny();
|
||||||
|
if (!cmd.hasArgument("--app-image")) {
|
||||||
|
TKit.assertTrue(launcherName.isPresent(),
|
||||||
|
"Check the desktop file corresponds to one of app launchers");
|
||||||
|
}
|
||||||
|
|
||||||
List<String> lines = Files.readAllLines(desktopFile);
|
List<String> lines = Files.readAllLines(desktopFile);
|
||||||
TKit.assertEquals("[Desktop Entry]", lines.get(0), "Check file header");
|
TKit.assertEquals("[Desktop Entry]", lines.get(0), "Check file header");
|
||||||
|
|
||||||
@ -428,7 +438,7 @@ public final class LinuxHelper {
|
|||||||
"Check value of [%s] key", key));
|
"Check value of [%s] key", key));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify value of `Exec` property in .desktop files are escaped if required
|
// Verify the value of `Exec` key is escaped if required
|
||||||
String launcherPath = data.get("Exec");
|
String launcherPath = data.get("Exec");
|
||||||
if (Pattern.compile("\\s").matcher(launcherPath).find()) {
|
if (Pattern.compile("\\s").matcher(launcherPath).find()) {
|
||||||
TKit.assertTrue(launcherPath.startsWith("\"")
|
TKit.assertTrue(launcherPath.startsWith("\"")
|
||||||
@ -437,10 +447,25 @@ public final class LinuxHelper {
|
|||||||
launcherPath = launcherPath.substring(1, launcherPath.length() - 1);
|
launcherPath = launcherPath.substring(1, launcherPath.length() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream.of(launcherPath, data.get("Icon"))
|
if (launcherName.isPresent()) {
|
||||||
.map(Path::of)
|
TKit.assertEquals(launcherPath, cmd.pathToPackageFile(
|
||||||
.map(cmd::pathToUnpackedPackageFile)
|
cmd.appLauncherPath(launcherName.get())).toString(),
|
||||||
.forEach(TKit::assertFileExists);
|
String.format(
|
||||||
|
"Check the value of [Exec] key references [%s] app launcher",
|
||||||
|
launcherName.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var e : List.<Map.Entry<Map.Entry<String, Optional<String>>, Function<ApplicationLayout, Path>>>of(
|
||||||
|
Map.entry(Map.entry("Exec", Optional.of(launcherPath)), ApplicationLayout::launchersDirectory),
|
||||||
|
Map.entry(Map.entry("Icon", Optional.empty()), ApplicationLayout::destktopIntegrationDirectory))) {
|
||||||
|
var path = e.getKey().getValue().or(() -> Optional.of(data.get(
|
||||||
|
e.getKey().getKey()))).map(Path::of).get();
|
||||||
|
TKit.assertFileExists(cmd.pathToUnpackedPackageFile(path));
|
||||||
|
Path expectedDir = cmd.pathToPackageFile(e.getValue().apply(cmd.appLayout()));
|
||||||
|
TKit.assertTrue(path.getParent().equals(expectedDir), String.format(
|
||||||
|
"Check the value of [%s] key references a file in [%s] folder",
|
||||||
|
e.getKey().getKey(), expectedDir));
|
||||||
|
}
|
||||||
|
|
||||||
TKit.trace(String.format("Check [%s] file END", desktopFile));
|
TKit.trace(String.format("Check [%s] file END", desktopFile));
|
||||||
}
|
}
|
||||||
@ -725,10 +750,10 @@ public final class LinuxHelper {
|
|||||||
|
|
||||||
private static Map<PackageType, String> archs;
|
private static Map<PackageType, String> archs;
|
||||||
|
|
||||||
private final static Pattern XDG_CMD_ICON_SIZE_PATTERN = Pattern.compile("\\s--size\\s+(\\d+)\\b");
|
private static final Pattern XDG_CMD_ICON_SIZE_PATTERN = Pattern.compile("\\s--size\\s+(\\d+)\\b");
|
||||||
|
|
||||||
// Values grabbed from https://linux.die.net/man/1/xdg-icon-resource
|
// Values grabbed from https://linux.die.net/man/1/xdg-icon-resource
|
||||||
private final static Set<Integer> XDG_CMD_VALID_ICON_SIZES = Set.of(16, 22, 32, 48, 64, 128);
|
private static final Set<Integer> XDG_CMD_VALID_ICON_SIZES = Set.of(16, 22, 32, 48, 64, 128);
|
||||||
|
|
||||||
private final static Method getServiceUnitFileName = initGetServiceUnitFileName();
|
private static final Method getServiceUnitFileName = initGetServiceUnitFileName();
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -23,11 +23,17 @@
|
|||||||
|
|
||||||
package jdk.jpackage.test;
|
package jdk.jpackage.test;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Deque;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Collectors;
|
import static java.util.stream.Collectors.toCollection;
|
||||||
|
import java.util.stream.Stream;
|
||||||
import static jdk.jpackage.test.TestBuilder.CMDLINE_ARG_PREFIX;
|
import static jdk.jpackage.test.TestBuilder.CMDLINE_ARG_PREFIX;
|
||||||
|
|
||||||
|
|
||||||
@ -36,7 +42,9 @@ public final class Main {
|
|||||||
boolean listTests = false;
|
boolean listTests = false;
|
||||||
List<TestInstance> tests = new ArrayList<>();
|
List<TestInstance> tests = new ArrayList<>();
|
||||||
try (TestBuilder testBuilder = new TestBuilder(tests::add)) {
|
try (TestBuilder testBuilder = new TestBuilder(tests::add)) {
|
||||||
for (var arg : args) {
|
Deque<String> argsAsList = new ArrayDeque<>(List.of(args));
|
||||||
|
while (!argsAsList.isEmpty()) {
|
||||||
|
var arg = argsAsList.pop();
|
||||||
TestBuilder.trace(String.format("Parsing [%s]...", arg));
|
TestBuilder.trace(String.format("Parsing [%s]...", arg));
|
||||||
|
|
||||||
if ((CMDLINE_ARG_PREFIX + "list").equals(arg)) {
|
if ((CMDLINE_ARG_PREFIX + "list").equals(arg)) {
|
||||||
@ -44,6 +52,29 @@ public final class Main {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (arg.startsWith("@")) {
|
||||||
|
// Command file
|
||||||
|
// @=args will read arguments from the "args" file, one argument per line
|
||||||
|
// @args will read arguments from the "args" file, splitting lines into arguments at whitespaces
|
||||||
|
arg = arg.substring(1);
|
||||||
|
var oneArgPerLine = arg.startsWith("=");
|
||||||
|
if (oneArgPerLine) {
|
||||||
|
arg = arg.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var newArgsStream = Files.readAllLines(Path.of(arg)).stream();
|
||||||
|
if (!oneArgPerLine) {
|
||||||
|
newArgsStream.map(line -> {
|
||||||
|
return Stream.of(line.split("\\s+"));
|
||||||
|
}).flatMap(x -> x);
|
||||||
|
}
|
||||||
|
|
||||||
|
var newArgs = newArgsStream.collect(toCollection(ArrayDeque::new));
|
||||||
|
newArgs.addAll(argsAsList);
|
||||||
|
argsAsList = newArgs;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
try {
|
try {
|
||||||
testBuilder.processCmdLineArg(arg);
|
testBuilder.processCmdLineArg(arg);
|
||||||
@ -62,12 +93,11 @@ public final class Main {
|
|||||||
|
|
||||||
// Order tests by their full names to have stable test sequence.
|
// Order tests by their full names to have stable test sequence.
|
||||||
List<TestInstance> orderedTests = tests.stream()
|
List<TestInstance> orderedTests = tests.stream()
|
||||||
.sorted((a, b) -> a.fullName().compareTo(b.fullName()))
|
.sorted(Comparator.comparing(TestInstance::fullName)).toList();
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
if (listTests) {
|
if (listTests) {
|
||||||
// Just list the tests
|
// Just list the tests
|
||||||
orderedTests.stream().forEach(test -> System.out.println(String.format(
|
orderedTests.forEach(test -> System.out.println(String.format(
|
||||||
"%s; workDir=[%s]", test.fullName(), test.workDir())));
|
"%s; workDir=[%s]", test.fullName(), test.workDir())));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -22,34 +22,33 @@
|
|||||||
*/
|
*/
|
||||||
package jdk.jpackage.test;
|
package jdk.jpackage.test;
|
||||||
|
|
||||||
|
import java.lang.reflect.Array;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Executable;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.IntStream;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import jdk.jpackage.test.Functional.ThrowingConsumer;
|
import jdk.jpackage.test.Functional.ThrowingConsumer;
|
||||||
import jdk.jpackage.test.TestInstance.TestDesc;
|
import jdk.jpackage.test.TestInstance.TestDesc;
|
||||||
|
|
||||||
class MethodCall implements ThrowingConsumer {
|
class MethodCall implements ThrowingConsumer {
|
||||||
|
|
||||||
MethodCall(Object[] instanceCtorArgs, Method method) {
|
MethodCall(Object[] instanceCtorArgs, Method method, Object ... args) {
|
||||||
this.ctorArgs = Optional.ofNullable(instanceCtorArgs).orElse(
|
Objects.requireNonNull(instanceCtorArgs);
|
||||||
DEFAULT_CTOR_ARGS);
|
Objects.requireNonNull(method);
|
||||||
this.method = method;
|
|
||||||
this.methodArgs = new Object[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
MethodCall(Object[] instanceCtorArgs, Method method, Object arg) {
|
this.ctorArgs = instanceCtorArgs;
|
||||||
this.ctorArgs = Optional.ofNullable(instanceCtorArgs).orElse(
|
|
||||||
DEFAULT_CTOR_ARGS);
|
|
||||||
this.method = method;
|
this.method = method;
|
||||||
this.methodArgs = new Object[]{arg};
|
this.methodArgs = args;
|
||||||
}
|
}
|
||||||
|
|
||||||
TestDesc createDescription() {
|
TestDesc createDescription() {
|
||||||
@ -76,68 +75,35 @@ class MethodCall implements ThrowingConsumer {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Constructor ctor = findRequiredConstructor(method.getDeclaringClass(),
|
var ctor = findMatchingConstructor(method.getDeclaringClass(), ctorArgs);
|
||||||
ctorArgs);
|
|
||||||
if (ctor.isVarArgs()) {
|
return ctor.newInstance(mapArgs(ctor, ctorArgs));
|
||||||
// Assume constructor doesn't have fixed, only variable parameters.
|
|
||||||
return ctor.newInstance(new Object[]{ctorArgs});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctor.newInstance(ctorArgs);
|
static Object[] mapArgs(Executable executable, final Object ... args) {
|
||||||
|
return mapPrimitiveTypeArgs(executable, mapVarArgs(executable, args));
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkRequiredConstructor() throws NoSuchMethodException {
|
void checkRequiredConstructor() throws NoSuchMethodException {
|
||||||
if ((method.getModifiers() & Modifier.STATIC) == 0) {
|
if ((method.getModifiers() & Modifier.STATIC) == 0) {
|
||||||
findRequiredConstructor(method.getDeclaringClass(), ctorArgs);
|
findMatchingConstructor(method.getDeclaringClass(), ctorArgs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Constructor findVarArgConstructor(Class type) {
|
private static Constructor findMatchingConstructor(Class type, Object... ctorArgs)
|
||||||
return Stream.of(type.getConstructors()).filter(
|
|
||||||
Constructor::isVarArgs).findFirst().orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Constructor findRequiredConstructor(Class type, Object... ctorArgs)
|
|
||||||
throws NoSuchMethodException {
|
throws NoSuchMethodException {
|
||||||
|
|
||||||
Supplier<NoSuchMethodException> notFoundException = () -> {
|
var ctors = filterMatchingExecutablesForParameterValues(Stream.of(
|
||||||
return new NoSuchMethodException(String.format(
|
type.getConstructors()), ctorArgs).toList();
|
||||||
|
|
||||||
|
if (ctors.size() != 1) {
|
||||||
|
// No public constructors that can handle the given arguments.
|
||||||
|
throw new NoSuchMethodException(String.format(
|
||||||
"No public contructor in %s for %s arguments", type,
|
"No public contructor in %s for %s arguments", type,
|
||||||
Arrays.deepToString(ctorArgs)));
|
Arrays.deepToString(ctorArgs)));
|
||||||
};
|
|
||||||
|
|
||||||
if (Stream.of(ctorArgs).allMatch(Objects::nonNull)) {
|
|
||||||
// No `null` in constructor args, take easy path
|
|
||||||
try {
|
|
||||||
return type.getConstructor(Stream.of(ctorArgs).map(
|
|
||||||
Object::getClass).collect(Collectors.toList()).toArray(
|
|
||||||
Class[]::new));
|
|
||||||
} catch (NoSuchMethodException ex) {
|
|
||||||
// Failed to find ctor that can take the given arguments.
|
|
||||||
Constructor varArgCtor = findVarArgConstructor(type);
|
|
||||||
if (varArgCtor != null) {
|
|
||||||
// There is one with variable number of arguments. Use it.
|
|
||||||
return varArgCtor;
|
|
||||||
}
|
|
||||||
throw notFoundException.get();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Constructor> ctors = Stream.of(type.getConstructors())
|
return ctors.get(0);
|
||||||
.filter(ctor -> ctor.getParameterCount() == ctorArgs.length)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
if (ctors.isEmpty()) {
|
|
||||||
// No public constructors that can handle the given arguments.
|
|
||||||
throw notFoundException.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctors.size() == 1) {
|
|
||||||
return ctors.iterator().next();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Revisit this tricky case when it will start bothering.
|
|
||||||
throw notFoundException.get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -145,9 +111,159 @@ class MethodCall implements ThrowingConsumer {
|
|||||||
method.invoke(thiz, methodArgs);
|
method.invoke(thiz, methodArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Object[] mapVarArgs(Executable executable, final Object ... args) {
|
||||||
|
if (executable.isVarArgs()) {
|
||||||
|
var paramTypes = executable.getParameterTypes();
|
||||||
|
Class varArgParamType = paramTypes[paramTypes.length - 1];
|
||||||
|
|
||||||
|
Object[] newArgs;
|
||||||
|
if (paramTypes.length - args.length == 1) {
|
||||||
|
// Empty var args
|
||||||
|
|
||||||
|
// "args" can be of type String[] if the "executable" is "foo(String ... str)"
|
||||||
|
newArgs = Arrays.copyOf(args, args.length + 1, Object[].class);
|
||||||
|
newArgs[newArgs.length - 1] = Array.newInstance(varArgParamType.componentType(), 0);
|
||||||
|
} else {
|
||||||
|
var varArgs = Arrays.copyOfRange(args, paramTypes.length - 1,
|
||||||
|
args.length, varArgParamType);
|
||||||
|
|
||||||
|
// "args" can be of type String[] if the "executable" is "foo(String ... str)"
|
||||||
|
newArgs = Arrays.copyOfRange(args, 0, paramTypes.length, Object[].class);
|
||||||
|
newArgs[newArgs.length - 1] = varArgs;
|
||||||
|
}
|
||||||
|
return newArgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object[] mapPrimitiveTypeArgs(Executable executable, final Object ... args) {
|
||||||
|
var paramTypes = executable.getParameterTypes();
|
||||||
|
if (paramTypes.length != args.length) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"The number of arguments must be equal to the number of parameters of the executable");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IntStream.range(0, args.length).allMatch(idx -> {
|
||||||
|
return Optional.ofNullable(args[idx]).map(Object::getClass).map(paramTypes[idx]::isAssignableFrom).orElse(true);
|
||||||
|
})) {
|
||||||
|
return args;
|
||||||
|
} else {
|
||||||
|
final var newArgs = Arrays.copyOf(args, args.length, Object[].class);
|
||||||
|
for (var idx = 0; idx != args.length; ++idx) {
|
||||||
|
final var paramType = paramTypes[idx];
|
||||||
|
final var argValue = args[idx];
|
||||||
|
newArgs[idx] = Optional.ofNullable(argValue).map(Object::getClass).map(argType -> {
|
||||||
|
if(argType.isArray() && !paramType.isAssignableFrom(argType)) {
|
||||||
|
var length = Array.getLength(argValue);
|
||||||
|
var newArray = Array.newInstance(paramType.getComponentType(), length);
|
||||||
|
for (var arrayIdx = 0; arrayIdx != length; ++arrayIdx) {
|
||||||
|
Array.set(newArray, arrayIdx, Array.get(argValue, arrayIdx));
|
||||||
|
}
|
||||||
|
return newArray;
|
||||||
|
} else {
|
||||||
|
return argValue;
|
||||||
|
}
|
||||||
|
}).orElse(argValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newArgs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T extends Executable> Stream<T> filterMatchingExecutablesForParameterValues(
|
||||||
|
Stream<T> executables, Object... args) {
|
||||||
|
return filterMatchingExecutablesForParameterTypes(
|
||||||
|
executables,
|
||||||
|
Stream.of(args)
|
||||||
|
.map(arg -> arg != null ? arg.getClass() : null)
|
||||||
|
.toArray(Class[]::new));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T extends Executable> Stream<T> filterMatchingExecutablesForParameterTypes(
|
||||||
|
Stream<T> executables, Class<?>... argTypes) {
|
||||||
|
return executables.filter(executable -> {
|
||||||
|
var parameterTypes = executable.getParameterTypes();
|
||||||
|
|
||||||
|
final int checkArgTypeCount;
|
||||||
|
if (parameterTypes.length <= argTypes.length) {
|
||||||
|
checkArgTypeCount = parameterTypes.length;
|
||||||
|
} else if (parameterTypes.length - argTypes.length == 1 && executable.isVarArgs()) {
|
||||||
|
// Empty optional arguments.
|
||||||
|
checkArgTypeCount = argTypes.length;
|
||||||
|
} else {
|
||||||
|
// Not enough mandatory arguments.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var unmatched = IntStream.range(0, checkArgTypeCount).dropWhile(idx -> {
|
||||||
|
return new ParameterTypeMatcher(parameterTypes[idx]).test(argTypes[idx]);
|
||||||
|
}).toArray();
|
||||||
|
|
||||||
|
if (argTypes.length == parameterTypes.length && unmatched.length == 0) {
|
||||||
|
// Number of argument types equals to the number of parameters
|
||||||
|
// of the executable and all types match.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (executable.isVarArgs()) {
|
||||||
|
var varArgType = parameterTypes[parameterTypes.length - 1].componentType();
|
||||||
|
return IntStream.of(unmatched).allMatch(idx -> {
|
||||||
|
return new ParameterTypeMatcher(varArgType).test(argTypes[idx]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ParameterTypeMatcher implements Predicate<Class<?>> {
|
||||||
|
ParameterTypeMatcher(Class<?> parameterType) {
|
||||||
|
Objects.requireNonNull(parameterType);
|
||||||
|
this.parameterType = NORM_TYPES.getOrDefault(parameterType, parameterType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(Class<?> paramaterValueType) {
|
||||||
|
if (paramaterValueType == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
paramaterValueType = NORM_TYPES.getOrDefault(paramaterValueType, paramaterValueType);
|
||||||
|
return parameterType.isAssignableFrom(paramaterValueType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Class<?> parameterType;
|
||||||
|
}
|
||||||
|
|
||||||
private final Object[] methodArgs;
|
private final Object[] methodArgs;
|
||||||
private final Method method;
|
private final Method method;
|
||||||
private final Object[] ctorArgs;
|
private final Object[] ctorArgs;
|
||||||
|
|
||||||
final static Object[] DEFAULT_CTOR_ARGS = new Object[0];
|
private static final Map<Class<?>, Class<?>> NORM_TYPES;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Map<Class<?>, Class<?>> primitives = Map.of(
|
||||||
|
boolean.class, Boolean.class,
|
||||||
|
byte.class, Byte.class,
|
||||||
|
short.class, Short.class,
|
||||||
|
int.class, Integer.class,
|
||||||
|
long.class, Long.class,
|
||||||
|
float.class, Float.class,
|
||||||
|
double.class, Double.class);
|
||||||
|
|
||||||
|
Map<Class<?>, Class<?>> primitiveArrays = Map.of(
|
||||||
|
boolean[].class, Boolean[].class,
|
||||||
|
byte[].class, Byte[].class,
|
||||||
|
short[].class, Short[].class,
|
||||||
|
int[].class, Integer[].class,
|
||||||
|
long[].class, Long[].class,
|
||||||
|
float[].class, Float[].class,
|
||||||
|
double[].class, Double[].class);
|
||||||
|
|
||||||
|
Map<Class<?>, Class<?>> combined = new HashMap<>(primitives);
|
||||||
|
combined.putAll(primitiveArrays);
|
||||||
|
|
||||||
|
NORM_TYPES = Collections.unmodifiableMap(combined);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -781,18 +781,18 @@ public final class TKit {
|
|||||||
currentTest.notifyAssert();
|
currentTest.notifyAssert();
|
||||||
|
|
||||||
var comm = Comm.compare(content, expected);
|
var comm = Comm.compare(content, expected);
|
||||||
if (!comm.unique1.isEmpty() && !comm.unique2.isEmpty()) {
|
if (!comm.unique1().isEmpty() && !comm.unique2().isEmpty()) {
|
||||||
error(String.format(
|
error(String.format(
|
||||||
"assertDirectoryContentEquals(%s): Some expected %s. Unexpected %s. Missing %s",
|
"assertDirectoryContentEquals(%s): Some expected %s. Unexpected %s. Missing %s",
|
||||||
baseDir, format(comm.common), format(comm.unique1), format(comm.unique2)));
|
baseDir, format(comm.common()), format(comm.unique1()), format(comm.unique2())));
|
||||||
} else if (!comm.unique1.isEmpty()) {
|
} else if (!comm.unique1().isEmpty()) {
|
||||||
error(String.format(
|
error(String.format(
|
||||||
"assertDirectoryContentEquals(%s): Expected %s. Unexpected %s",
|
"assertDirectoryContentEquals(%s): Expected %s. Unexpected %s",
|
||||||
baseDir, format(comm.common), format(comm.unique1)));
|
baseDir, format(comm.common()), format(comm.unique1())));
|
||||||
} else if (!comm.unique2.isEmpty()) {
|
} else if (!comm.unique2().isEmpty()) {
|
||||||
error(String.format(
|
error(String.format(
|
||||||
"assertDirectoryContentEquals(%s): Some expected %s. Missing %s",
|
"assertDirectoryContentEquals(%s): Some expected %s. Missing %s",
|
||||||
baseDir, format(comm.common), format(comm.unique2)));
|
baseDir, format(comm.common()), format(comm.unique2())));
|
||||||
} else {
|
} else {
|
||||||
traceAssert(String.format(
|
traceAssert(String.format(
|
||||||
"assertDirectoryContentEquals(%s): Expected %s",
|
"assertDirectoryContentEquals(%s): Expected %s",
|
||||||
@ -808,10 +808,10 @@ public final class TKit {
|
|||||||
currentTest.notifyAssert();
|
currentTest.notifyAssert();
|
||||||
|
|
||||||
var comm = Comm.compare(content, expected);
|
var comm = Comm.compare(content, expected);
|
||||||
if (!comm.unique2.isEmpty()) {
|
if (!comm.unique2().isEmpty()) {
|
||||||
error(String.format(
|
error(String.format(
|
||||||
"assertDirectoryContentContains(%s): Some expected %s. Missing %s",
|
"assertDirectoryContentContains(%s): Some expected %s. Missing %s",
|
||||||
baseDir, format(comm.common), format(comm.unique2)));
|
baseDir, format(comm.common()), format(comm.unique2())));
|
||||||
} else {
|
} else {
|
||||||
traceAssert(String.format(
|
traceAssert(String.format(
|
||||||
"assertDirectoryContentContains(%s): Expected %s",
|
"assertDirectoryContentContains(%s): Expected %s",
|
||||||
@ -838,21 +838,6 @@ public final class TKit {
|
|||||||
this.content = contents;
|
this.content = contents;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static record Comm(Set<Path> common, Set<Path> unique1, Set<Path> unique2) {
|
|
||||||
static Comm compare(Set<Path> a, Set<Path> b) {
|
|
||||||
Set<Path> common = new HashSet<>(a);
|
|
||||||
common.retainAll(b);
|
|
||||||
|
|
||||||
Set<Path> unique1 = new HashSet<>(a);
|
|
||||||
unique1.removeAll(common);
|
|
||||||
|
|
||||||
Set<Path> unique2 = new HashSet<>(b);
|
|
||||||
unique2.removeAll(common);
|
|
||||||
|
|
||||||
return new Comm(common, unique1, unique2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String format(Set<Path> paths) {
|
private static String format(Set<Path> paths) {
|
||||||
return Arrays.toString(
|
return Arrays.toString(
|
||||||
paths.stream().sorted().map(Path::toString).toArray(
|
paths.stream().sorted().map(Path::toString).toArray(
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -23,32 +23,28 @@
|
|||||||
|
|
||||||
package jdk.jpackage.test;
|
package jdk.jpackage.test;
|
||||||
|
|
||||||
import java.lang.reflect.Array;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Comparator;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.function.UnaryOperator;
|
import java.util.function.UnaryOperator;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import jdk.jpackage.test.Annotations.AfterEach;
|
import jdk.jpackage.test.Annotations.AfterEach;
|
||||||
import jdk.jpackage.test.Annotations.BeforeEach;
|
import jdk.jpackage.test.Annotations.BeforeEach;
|
||||||
import jdk.jpackage.test.Annotations.Parameter;
|
|
||||||
import jdk.jpackage.test.Annotations.ParameterGroup;
|
|
||||||
import jdk.jpackage.test.Annotations.Parameters;
|
|
||||||
import jdk.jpackage.test.Annotations.Test;
|
import jdk.jpackage.test.Annotations.Test;
|
||||||
import jdk.jpackage.test.Functional.ThrowingConsumer;
|
import jdk.jpackage.test.Functional.ThrowingConsumer;
|
||||||
|
import static jdk.jpackage.test.Functional.ThrowingConsumer.toConsumer;
|
||||||
import jdk.jpackage.test.Functional.ThrowingFunction;
|
import jdk.jpackage.test.Functional.ThrowingFunction;
|
||||||
|
import jdk.jpackage.test.TestMethodSupplier.InvalidAnnotationException;
|
||||||
|
import static jdk.jpackage.test.TestMethodSupplier.MethodQuery.fromQualifiedMethodName;
|
||||||
|
|
||||||
final class TestBuilder implements AutoCloseable {
|
final class TestBuilder implements AutoCloseable {
|
||||||
|
|
||||||
@ -58,6 +54,7 @@ final class TestBuilder implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TestBuilder(Consumer<TestInstance> testConsumer) {
|
TestBuilder(Consumer<TestInstance> testConsumer) {
|
||||||
|
this.testMethodSupplier = TestBuilderConfig.getDefault().createTestMethodSupplier();
|
||||||
argProcessors = Map.of(
|
argProcessors = Map.of(
|
||||||
CMDLINE_ARG_PREFIX + "after-run",
|
CMDLINE_ARG_PREFIX + "after-run",
|
||||||
arg -> getJavaMethodsFromArg(arg).map(
|
arg -> getJavaMethodsFromArg(arg).map(
|
||||||
@ -70,7 +67,7 @@ final class TestBuilder implements AutoCloseable {
|
|||||||
CMDLINE_ARG_PREFIX + "run",
|
CMDLINE_ARG_PREFIX + "run",
|
||||||
arg -> addTestGroup(getJavaMethodsFromArg(arg).map(
|
arg -> addTestGroup(getJavaMethodsFromArg(arg).map(
|
||||||
ThrowingFunction.toFunction(
|
ThrowingFunction.toFunction(
|
||||||
TestBuilder::toMethodCalls)).flatMap(s -> s).collect(
|
this::toMethodCalls)).flatMap(s -> s).collect(
|
||||||
Collectors.toList())),
|
Collectors.toList())),
|
||||||
|
|
||||||
CMDLINE_ARG_PREFIX + "exclude",
|
CMDLINE_ARG_PREFIX + "exclude",
|
||||||
@ -219,23 +216,29 @@ final class TestBuilder implements AutoCloseable {
|
|||||||
.filter(m -> m.getParameterCount() == 0)
|
.filter(m -> m.getParameterCount() == 0)
|
||||||
.filter(m -> !m.isAnnotationPresent(Test.class))
|
.filter(m -> !m.isAnnotationPresent(Test.class))
|
||||||
.filter(m -> m.isAnnotationPresent(annotationType))
|
.filter(m -> m.isAnnotationPresent(annotationType))
|
||||||
.sorted((a, b) -> a.getName().compareTo(b.getName()));
|
.sorted(Comparator.comparing(Method::getName));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Stream<String> cmdLineArgValueToMethodNames(String v) {
|
private Stream<String> cmdLineArgValueToMethodNames(String v) {
|
||||||
List<String> result = new ArrayList<>();
|
List<String> result = new ArrayList<>();
|
||||||
String defaultClassName = null;
|
String defaultClassName = null;
|
||||||
for (String token : v.split(",")) {
|
for (String token : v.split(",")) {
|
||||||
Class testSet = probeClass(token);
|
Class testSet = probeClass(token);
|
||||||
if (testSet != null) {
|
if (testSet != null) {
|
||||||
|
if (testMethodSupplier.isTestClass(testSet)) {
|
||||||
|
toConsumer(testMethodSupplier::verifyTestClass).accept(testSet);
|
||||||
|
}
|
||||||
|
|
||||||
// Test set class specified. Pull in all public methods
|
// Test set class specified. Pull in all public methods
|
||||||
// from the class with @Test annotation removing name duplicates.
|
// from the class with @Test annotation removing name duplicates.
|
||||||
// Overloads will be handled at the next phase of processing.
|
// Overloads will be handled at the next phase of processing.
|
||||||
defaultClassName = token;
|
defaultClassName = token;
|
||||||
Stream.of(testSet.getMethods()).filter(
|
result.addAll(Stream.of(testSet.getMethods())
|
||||||
m -> m.isAnnotationPresent(Test.class)).map(
|
.filter(m -> m.isAnnotationPresent(Test.class))
|
||||||
Method::getName).distinct().forEach(
|
.filter(testMethodSupplier::isEnabled)
|
||||||
name -> result.add(String.join(".", token, name)));
|
.map(Method::getName).distinct()
|
||||||
|
.map(name -> String.join(".", token, name))
|
||||||
|
.toList());
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -246,7 +249,7 @@ final class TestBuilder implements AutoCloseable {
|
|||||||
qualifiedMethodName = token;
|
qualifiedMethodName = token;
|
||||||
defaultClassName = token.substring(0, lastDotIdx);
|
defaultClassName = token.substring(0, lastDotIdx);
|
||||||
} else if (defaultClassName == null) {
|
} else if (defaultClassName == null) {
|
||||||
throw new ParseException("Default class name not found in");
|
throw new ParseException("Missing default class name in");
|
||||||
} else {
|
} else {
|
||||||
qualifiedMethodName = String.join(".", defaultClassName, token);
|
qualifiedMethodName = String.join(".", defaultClassName, token);
|
||||||
}
|
}
|
||||||
@ -255,106 +258,31 @@ final class TestBuilder implements AutoCloseable {
|
|||||||
return result.stream();
|
return result.stream();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean filterMethod(String expectedMethodName, Method method) {
|
private List<Method> getJavaMethodFromString(String qualifiedMethodName) {
|
||||||
if (!method.getName().equals(expectedMethodName)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
switch (method.getParameterCount()) {
|
|
||||||
case 0:
|
|
||||||
return !isParametrized(method);
|
|
||||||
case 1:
|
|
||||||
return isParametrized(method);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isParametrized(Method method) {
|
|
||||||
return method.isAnnotationPresent(ParameterGroup.class) || method.isAnnotationPresent(
|
|
||||||
Parameter.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<Method> getJavaMethodFromString(
|
|
||||||
String qualifiedMethodName) {
|
|
||||||
int lastDotIdx = qualifiedMethodName.lastIndexOf('.');
|
int lastDotIdx = qualifiedMethodName.lastIndexOf('.');
|
||||||
if (lastDotIdx == -1) {
|
if (lastDotIdx == -1) {
|
||||||
throw new ParseException("Class name not found in");
|
throw new ParseException("Missing class name in");
|
||||||
}
|
}
|
||||||
String className = qualifiedMethodName.substring(0, lastDotIdx);
|
|
||||||
String methodName = qualifiedMethodName.substring(lastDotIdx + 1);
|
|
||||||
Class methodClass;
|
|
||||||
try {
|
try {
|
||||||
methodClass = Class.forName(className);
|
return testMethodSupplier.findNullaryLikeMethods(
|
||||||
} catch (ClassNotFoundException ex) {
|
fromQualifiedMethodName(qualifiedMethodName));
|
||||||
throw new ParseException(String.format("Class [%s] not found;",
|
} catch (NoSuchMethodException ex) {
|
||||||
className));
|
throw new ParseException(ex.getMessage() + ";", ex);
|
||||||
}
|
}
|
||||||
// Get the list of all public methods as need to deal with overloads.
|
|
||||||
List<Method> methods = Stream.of(methodClass.getMethods()).filter(
|
|
||||||
(m) -> filterMethod(methodName, m)).collect(Collectors.toList());
|
|
||||||
if (methods.isEmpty()) {
|
|
||||||
throw new ParseException(String.format(
|
|
||||||
"Method [%s] not found in [%s] class;",
|
|
||||||
methodName, className));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
trace(String.format("%s -> %s", qualifiedMethodName, methods));
|
private Stream<Method> getJavaMethodsFromArg(String argValue) {
|
||||||
return methods;
|
var methods = cmdLineArgValueToMethodNames(argValue)
|
||||||
|
.map(this::getJavaMethodFromString)
|
||||||
|
.flatMap(List::stream).toList();
|
||||||
|
trace(String.format("%s -> %s", argValue, methods));
|
||||||
|
return methods.stream();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Stream<Method> getJavaMethodsFromArg(String argValue) {
|
private Stream<MethodCall> toMethodCalls(Method method) throws
|
||||||
return cmdLineArgValueToMethodNames(argValue).map(
|
IllegalAccessException, InvocationTargetException, InvalidAnnotationException {
|
||||||
ThrowingFunction.toFunction(
|
return testMethodSupplier.mapToMethodCalls(method).peek(methodCall -> {
|
||||||
TestBuilder::getJavaMethodFromString)).flatMap(
|
|
||||||
List::stream).sequential();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Parameter[] getMethodParameters(Method method) {
|
|
||||||
if (method.isAnnotationPresent(ParameterGroup.class)) {
|
|
||||||
return ((ParameterGroup) method.getAnnotation(ParameterGroup.class)).value();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (method.isAnnotationPresent(Parameter.class)) {
|
|
||||||
return new Parameter[]{(Parameter) method.getAnnotation(
|
|
||||||
Parameter.class)};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unexpected
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Stream<Object[]> toCtorArgs(Method method) throws
|
|
||||||
IllegalAccessException, InvocationTargetException {
|
|
||||||
Class type = method.getDeclaringClass();
|
|
||||||
List<Method> paremetersProviders = Stream.of(type.getMethods())
|
|
||||||
.filter(m -> m.getParameterCount() == 0)
|
|
||||||
.filter(m -> (m.getModifiers() & Modifier.STATIC) != 0)
|
|
||||||
.filter(m -> m.isAnnotationPresent(Parameters.class))
|
|
||||||
.sorted()
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
if (paremetersProviders.isEmpty()) {
|
|
||||||
// Single instance using the default constructor.
|
|
||||||
return Stream.ofNullable(MethodCall.DEFAULT_CTOR_ARGS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pick the first method from the list.
|
|
||||||
Method paremetersProvider = paremetersProviders.iterator().next();
|
|
||||||
if (paremetersProviders.size() > 1) {
|
|
||||||
trace(String.format(
|
|
||||||
"Found %d public static methods without arguments with %s annotation. Will use %s",
|
|
||||||
paremetersProviders.size(), Parameters.class,
|
|
||||||
paremetersProvider));
|
|
||||||
paremetersProviders.stream().map(Method::toString).forEach(
|
|
||||||
TestBuilder::trace);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct collection of arguments for test class instances.
|
|
||||||
return ((Collection) paremetersProvider.invoke(null)).stream();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Stream<MethodCall> toMethodCalls(Method method) throws
|
|
||||||
IllegalAccessException, InvocationTargetException {
|
|
||||||
return toCtorArgs(method).map(v -> toMethodCalls(v, method)).flatMap(
|
|
||||||
s -> s).peek(methodCall -> {
|
|
||||||
// Make sure required constructor is accessible if the one is needed.
|
// Make sure required constructor is accessible if the one is needed.
|
||||||
// Need to probe all methods as some of them might be static
|
// Need to probe all methods as some of them might be static
|
||||||
// and some class members.
|
// and some class members.
|
||||||
@ -362,48 +290,11 @@ final class TestBuilder implements AutoCloseable {
|
|||||||
try {
|
try {
|
||||||
methodCall.checkRequiredConstructor();
|
methodCall.checkRequiredConstructor();
|
||||||
} catch (NoSuchMethodException ex) {
|
} catch (NoSuchMethodException ex) {
|
||||||
throw new ParseException(ex.getMessage() + ".");
|
throw new ParseException(ex.getMessage() + ".", ex);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Stream<MethodCall> toMethodCalls(Object[] ctorArgs, Method method) {
|
|
||||||
if (!isParametrized(method)) {
|
|
||||||
return Stream.of(new MethodCall(ctorArgs, method));
|
|
||||||
}
|
|
||||||
Parameter[] annotations = getMethodParameters(method);
|
|
||||||
if (annotations.length == 0) {
|
|
||||||
return Stream.of(new MethodCall(ctorArgs, method));
|
|
||||||
}
|
|
||||||
return Stream.of(annotations).map((a) -> {
|
|
||||||
Class paramType = method.getParameterTypes()[0];
|
|
||||||
final Object annotationValue;
|
|
||||||
if (!paramType.isArray()) {
|
|
||||||
annotationValue = fromString(a.value()[0], paramType);
|
|
||||||
} else {
|
|
||||||
Class paramComponentType = paramType.getComponentType();
|
|
||||||
annotationValue = Array.newInstance(paramComponentType, a.value().length);
|
|
||||||
var idx = new AtomicInteger(-1);
|
|
||||||
Stream.of(a.value()).map(v -> fromString(v, paramComponentType)).sequential().forEach(
|
|
||||||
v -> Array.set(annotationValue, idx.incrementAndGet(), v));
|
|
||||||
}
|
|
||||||
return new MethodCall(ctorArgs, method, annotationValue);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Object fromString(String value, Class toType) {
|
|
||||||
if (toType.isEnum()) {
|
|
||||||
return Enum.valueOf(toType, value);
|
|
||||||
}
|
|
||||||
Function<String, Object> converter = conv.get(toType);
|
|
||||||
if (converter == null) {
|
|
||||||
throw new RuntimeException(String.format(
|
|
||||||
"Failed to find a conversion of [%s] string to %s type",
|
|
||||||
value, toType));
|
|
||||||
}
|
|
||||||
return converter.apply(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wraps Method.invike() into ThrowingRunnable.run()
|
// Wraps Method.invike() into ThrowingRunnable.run()
|
||||||
private ThrowingConsumer wrap(Method method) {
|
private ThrowingConsumer wrap(Method method) {
|
||||||
return (test) -> {
|
return (test) -> {
|
||||||
@ -427,6 +318,10 @@ final class TestBuilder implements AutoCloseable {
|
|||||||
super(msg);
|
super(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ParseException(String msg, Exception ex) {
|
||||||
|
super(msg, ex);
|
||||||
|
}
|
||||||
|
|
||||||
void setContext(String badCmdLineArg) {
|
void setContext(String badCmdLineArg) {
|
||||||
this.badCmdLineArg = badCmdLineArg;
|
this.badCmdLineArg = badCmdLineArg;
|
||||||
}
|
}
|
||||||
@ -448,8 +343,9 @@ final class TestBuilder implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final TestMethodSupplier testMethodSupplier;
|
||||||
private final Map<String, ThrowingConsumer<String>> argProcessors;
|
private final Map<String, ThrowingConsumer<String>> argProcessors;
|
||||||
private Consumer<TestInstance> testConsumer;
|
private final Consumer<TestInstance> testConsumer;
|
||||||
private List<MethodCall> testGroup;
|
private List<MethodCall> testGroup;
|
||||||
private List<ThrowingConsumer> beforeActions;
|
private List<ThrowingConsumer> beforeActions;
|
||||||
private List<ThrowingConsumer> afterActions;
|
private List<ThrowingConsumer> afterActions;
|
||||||
@ -458,14 +354,5 @@ final class TestBuilder implements AutoCloseable {
|
|||||||
private String spaceSubstitute;
|
private String spaceSubstitute;
|
||||||
private boolean dryRun;
|
private boolean dryRun;
|
||||||
|
|
||||||
private final static Map<Class, Function<String, Object>> conv = Map.of(
|
static final String CMDLINE_ARG_PREFIX = "--jpt-";
|
||||||
boolean.class, Boolean::valueOf,
|
|
||||||
Boolean.class, Boolean::valueOf,
|
|
||||||
int.class, Integer::valueOf,
|
|
||||||
Integer.class, Integer::valueOf,
|
|
||||||
long.class, Long::valueOf,
|
|
||||||
Long.class, Long::valueOf,
|
|
||||||
String.class, String::valueOf);
|
|
||||||
|
|
||||||
final static String CMDLINE_ARG_PREFIX = "--jpt-";
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.jpackage.test;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import jdk.internal.util.OperatingSystem;
|
||||||
|
|
||||||
|
final class TestBuilderConfig {
|
||||||
|
TestBuilderConfig() {
|
||||||
|
}
|
||||||
|
|
||||||
|
TestMethodSupplier createTestMethodSupplier() {
|
||||||
|
return new TestMethodSupplier(os);
|
||||||
|
}
|
||||||
|
|
||||||
|
OperatingSystem getOperatingSystem() {
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
static TestBuilderConfig getDefault() {
|
||||||
|
return DEFAULT.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setOperatingSystem(OperatingSystem os) {
|
||||||
|
Objects.requireNonNull(os);
|
||||||
|
DEFAULT.get().os = os;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setDefaults() {
|
||||||
|
DEFAULT.set(new TestBuilderConfig());
|
||||||
|
}
|
||||||
|
|
||||||
|
private OperatingSystem os = OperatingSystem.current();
|
||||||
|
|
||||||
|
private static final ThreadLocal<TestBuilderConfig> DEFAULT = new ThreadLocal<>() {
|
||||||
|
@Override
|
||||||
|
protected TestBuilderConfig initialValue() {
|
||||||
|
return new TestBuilderConfig();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -32,8 +32,10 @@ import java.util.Arrays;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -50,7 +52,7 @@ final class TestInstance implements ThrowingRunnable {
|
|||||||
|
|
||||||
String testFullName() {
|
String testFullName() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(clazz.getSimpleName());
|
sb.append(clazz.getName());
|
||||||
if (instanceArgs != null) {
|
if (instanceArgs != null) {
|
||||||
sb.append('(').append(instanceArgs).append(')');
|
sb.append('(').append(instanceArgs).append(')');
|
||||||
}
|
}
|
||||||
@ -78,12 +80,12 @@ final class TestInstance implements ThrowingRunnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Builder ctorArgs(Object... v) {
|
Builder ctorArgs(Object... v) {
|
||||||
ctorArgs = ofNullable(v);
|
ctorArgs = Arrays.asList(v);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Builder methodArgs(Object... v) {
|
Builder methodArgs(Object... v) {
|
||||||
methodArgs = ofNullable(v);
|
methodArgs = Arrays.asList(v);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,22 +109,18 @@ final class TestInstance implements ThrowingRunnable {
|
|||||||
}
|
}
|
||||||
return values.stream().map(v -> {
|
return values.stream().map(v -> {
|
||||||
if (v != null && v.getClass().isArray()) {
|
if (v != null && v.getClass().isArray()) {
|
||||||
return String.format("%s(length=%d)",
|
String asString;
|
||||||
Arrays.deepToString((Object[]) v),
|
if (v.getClass().getComponentType().isPrimitive()) {
|
||||||
Array.getLength(v));
|
asString = PRIMITIVE_ARRAY_FORMATTERS.get(v.getClass()).apply(v);
|
||||||
|
} else {
|
||||||
|
asString = Arrays.deepToString((Object[]) v);
|
||||||
|
}
|
||||||
|
return String.format("%s(length=%d)", asString, Array.getLength(v));
|
||||||
}
|
}
|
||||||
return String.format("%s", v);
|
return String.format("%s", v);
|
||||||
}).collect(Collectors.joining(", "));
|
}).collect(Collectors.joining(", "));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Object> ofNullable(Object... values) {
|
|
||||||
List<Object> result = new ArrayList();
|
|
||||||
for (var v: values) {
|
|
||||||
result.add(v);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Object> ctorArgs;
|
private List<Object> ctorArgs;
|
||||||
private List<Object> methodArgs;
|
private List<Object> methodArgs;
|
||||||
private Method method;
|
private Method method;
|
||||||
@ -331,7 +329,7 @@ final class TestInstance implements ThrowingRunnable {
|
|||||||
private final boolean dryRun;
|
private final boolean dryRun;
|
||||||
private final Path workDir;
|
private final Path workDir;
|
||||||
|
|
||||||
private final static Set<Status> KEEP_WORK_DIR = Functional.identity(
|
private static final Set<Status> KEEP_WORK_DIR = Functional.identity(
|
||||||
() -> {
|
() -> {
|
||||||
final String propertyName = "keep-work-dir";
|
final String propertyName = "keep-work-dir";
|
||||||
Set<String> keepWorkDir = TKit.tokenizeConfigProperty(
|
Set<String> keepWorkDir = TKit.tokenizeConfigProperty(
|
||||||
@ -355,4 +353,15 @@ final class TestInstance implements ThrowingRunnable {
|
|||||||
return Collections.unmodifiableSet(result);
|
return Collections.unmodifiableSet(result);
|
||||||
}).get();
|
}).get();
|
||||||
|
|
||||||
|
private static final Map<Class<?>, Function<Object, String>> PRIMITIVE_ARRAY_FORMATTERS = Map.of(
|
||||||
|
boolean[].class, v -> Arrays.toString((boolean[])v),
|
||||||
|
byte[].class, v -> Arrays.toString((byte[])v),
|
||||||
|
char[].class, v -> Arrays.toString((char[])v),
|
||||||
|
short[].class, v -> Arrays.toString((short[])v),
|
||||||
|
int[].class, v -> Arrays.toString((int[])v),
|
||||||
|
long[].class, v -> Arrays.toString((long[])v),
|
||||||
|
float[].class, v -> Arrays.toString((float[])v),
|
||||||
|
double[].class, v -> Arrays.toString((double[])v)
|
||||||
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,510 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.jpackage.test;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Executable;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import jdk.internal.util.OperatingSystem;
|
||||||
|
import jdk.jpackage.test.Annotations.Parameter;
|
||||||
|
import jdk.jpackage.test.Annotations.ParameterGroup;
|
||||||
|
import jdk.jpackage.test.Annotations.ParameterSupplier;
|
||||||
|
import jdk.jpackage.test.Annotations.ParameterSupplierGroup;
|
||||||
|
import jdk.jpackage.test.Annotations.Parameters;
|
||||||
|
import jdk.jpackage.test.Annotations.Test;
|
||||||
|
import static jdk.jpackage.test.Functional.ThrowingFunction.toFunction;
|
||||||
|
import static jdk.jpackage.test.Functional.ThrowingSupplier.toSupplier;
|
||||||
|
import static jdk.jpackage.test.MethodCall.mapArgs;
|
||||||
|
|
||||||
|
final class TestMethodSupplier {
|
||||||
|
|
||||||
|
TestMethodSupplier(OperatingSystem os) {
|
||||||
|
Objects.requireNonNull(os);
|
||||||
|
this.os = os;
|
||||||
|
}
|
||||||
|
|
||||||
|
record MethodQuery(String className, String methodName) {
|
||||||
|
|
||||||
|
List<Method> lookup() throws ClassNotFoundException {
|
||||||
|
final Class methodClass = Class.forName(className);
|
||||||
|
|
||||||
|
// Get the list of all public methods as need to deal with overloads.
|
||||||
|
return Stream.of(methodClass.getMethods()).filter(method -> {
|
||||||
|
return method.getName().equals(methodName);
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
static MethodQuery fromQualifiedMethodName(String qualifiedMethodName) {
|
||||||
|
int lastDotIdx = qualifiedMethodName.lastIndexOf('.');
|
||||||
|
if (lastDotIdx == -1) {
|
||||||
|
throw new IllegalArgumentException("Class name not specified");
|
||||||
|
}
|
||||||
|
|
||||||
|
var className = qualifiedMethodName.substring(0, lastDotIdx);
|
||||||
|
var methodName = qualifiedMethodName.substring(lastDotIdx + 1);
|
||||||
|
|
||||||
|
return new MethodQuery(className, methodName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Method> findNullaryLikeMethods(MethodQuery query) throws NoSuchMethodException {
|
||||||
|
List<Method> methods;
|
||||||
|
|
||||||
|
try {
|
||||||
|
methods = query.lookup();
|
||||||
|
} catch (ClassNotFoundException ex) {
|
||||||
|
throw new NoSuchMethodException(
|
||||||
|
String.format("Class [%s] not found", query.className()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (methods.isEmpty()) {
|
||||||
|
throw new NoSuchMethodException(String.format(
|
||||||
|
"Public method [%s] not found in [%s] class",
|
||||||
|
query.methodName(), query.className()));
|
||||||
|
}
|
||||||
|
|
||||||
|
methods = methods.stream().filter(method -> {
|
||||||
|
if (isParameterized(method) && isTest(method)) {
|
||||||
|
// Always accept test method with annotations producing arguments for its invocation.
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return method.getParameterCount() == 0;
|
||||||
|
}
|
||||||
|
}).filter(this::isEnabled).toList();
|
||||||
|
|
||||||
|
if (methods.isEmpty()) {
|
||||||
|
throw new NoSuchMethodException(String.format(
|
||||||
|
"Suitable public method [%s] not found in [%s] class",
|
||||||
|
query.methodName(), query.className()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return methods;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isTestClass(Class<?> type) {
|
||||||
|
var typeStatus = processedTypes.get(type);
|
||||||
|
if (typeStatus == null) {
|
||||||
|
typeStatus = Verifier.isTestClass(type) ? TypeStatus.TEST_CLASS : TypeStatus.NOT_TEST_CLASS;
|
||||||
|
processedTypes.put(type, typeStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
return !TypeStatus.NOT_TEST_CLASS.equals(typeStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
void verifyTestClass(Class<?> type) throws InvalidAnnotationException {
|
||||||
|
var typeStatus = processedTypes.get(type);
|
||||||
|
if (typeStatus == null) {
|
||||||
|
// The "type" has not been verified yet.
|
||||||
|
try {
|
||||||
|
Verifier.verifyTestClass(type);
|
||||||
|
processedTypes.put(type, TypeStatus.VALID_TEST_CLASS);
|
||||||
|
return;
|
||||||
|
} catch (InvalidAnnotationException ex) {
|
||||||
|
processedTypes.put(type, TypeStatus.TEST_CLASS);
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (typeStatus) {
|
||||||
|
case NOT_TEST_CLASS -> Verifier.throwNotTestClassException(type);
|
||||||
|
case TEST_CLASS -> Verifier.verifyTestClass(type);
|
||||||
|
case VALID_TEST_CLASS -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isEnabled(Method method) {
|
||||||
|
return Stream.of(Test.class, Parameters.class)
|
||||||
|
.filter(method::isAnnotationPresent)
|
||||||
|
.findFirst()
|
||||||
|
.map(method::getAnnotation)
|
||||||
|
.map(this::canRunOnTheOperatingSystem)
|
||||||
|
.orElse(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<MethodCall> mapToMethodCalls(Method method) throws
|
||||||
|
IllegalAccessException, InvocationTargetException {
|
||||||
|
return toCtorArgs(method).map(v -> toMethodCalls(v, method)).flatMap(x -> x);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stream<Object[]> toCtorArgs(Method method) throws
|
||||||
|
IllegalAccessException, InvocationTargetException {
|
||||||
|
|
||||||
|
if ((method.getModifiers() & Modifier.STATIC) != 0) {
|
||||||
|
// Static method, no instance
|
||||||
|
return Stream.ofNullable(DEFAULT_CTOR_ARGS);
|
||||||
|
}
|
||||||
|
|
||||||
|
final var type = method.getDeclaringClass();
|
||||||
|
|
||||||
|
final var paremeterSuppliers = filterParameterSuppliers(type)
|
||||||
|
.filter(m -> m.isAnnotationPresent(Parameters.class))
|
||||||
|
.filter(this::isEnabled)
|
||||||
|
.sorted(Comparator.comparing(Method::getName)).toList();
|
||||||
|
if (paremeterSuppliers.isEmpty()) {
|
||||||
|
// Single instance using the default constructor.
|
||||||
|
return Stream.ofNullable(DEFAULT_CTOR_ARGS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct collection of arguments for test class instances.
|
||||||
|
return createArgs(paremeterSuppliers.toArray(Method[]::new));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stream<MethodCall> toMethodCalls(Object[] ctorArgs, Method method) {
|
||||||
|
if (!isParameterized(method)) {
|
||||||
|
return Stream.of(new MethodCall(ctorArgs, method));
|
||||||
|
}
|
||||||
|
|
||||||
|
var fromParameter = Stream.of(getMethodParameters(method)).map(a -> {
|
||||||
|
return createArgsForAnnotation(method, a);
|
||||||
|
}).flatMap(List::stream);
|
||||||
|
|
||||||
|
var fromParameterSupplier = Stream.of(getMethodParameterSuppliers(method)).map(a -> {
|
||||||
|
return toSupplier(() -> createArgsForAnnotation(method, a)).get();
|
||||||
|
}).flatMap(List::stream);
|
||||||
|
|
||||||
|
return Stream.concat(fromParameter, fromParameterSupplier).map(args -> {
|
||||||
|
return new MethodCall(ctorArgs, method, args);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Object[]> createArgsForAnnotation(Executable exec, Parameter a) {
|
||||||
|
if (!canRunOnTheOperatingSystem(a)) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
final var annotationArgs = a.value();
|
||||||
|
final var execParameterTypes = exec.getParameterTypes();
|
||||||
|
|
||||||
|
if (execParameterTypes.length > annotationArgs.length) {
|
||||||
|
if (execParameterTypes.length - annotationArgs.length == 1 && exec.isVarArgs()) {
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException(String.format(
|
||||||
|
"Not enough annotation values %s for [%s]",
|
||||||
|
List.of(annotationArgs), exec));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final Class<?>[] argTypes;
|
||||||
|
if (exec.isVarArgs()) {
|
||||||
|
List<Class<?>> argTypesBuilder = new ArrayList<>();
|
||||||
|
var lastExecParameterTypeIdx = execParameterTypes.length - 1;
|
||||||
|
argTypesBuilder.addAll(List.of(execParameterTypes).subList(0,
|
||||||
|
lastExecParameterTypeIdx));
|
||||||
|
argTypesBuilder.addAll(Collections.nCopies(
|
||||||
|
Integer.max(0, annotationArgs.length - lastExecParameterTypeIdx),
|
||||||
|
execParameterTypes[lastExecParameterTypeIdx].componentType()));
|
||||||
|
argTypes = argTypesBuilder.toArray(Class[]::new);
|
||||||
|
} else {
|
||||||
|
argTypes = execParameterTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argTypes.length < annotationArgs.length) {
|
||||||
|
throw new RuntimeException(String.format(
|
||||||
|
"Too many annotation values %s for [%s]",
|
||||||
|
List.of(annotationArgs), exec));
|
||||||
|
}
|
||||||
|
|
||||||
|
var args = mapArgs(exec, IntStream.range(0, argTypes.length).mapToObj(idx -> {
|
||||||
|
return fromString(annotationArgs[idx], argTypes[idx]);
|
||||||
|
}).toArray(Object[]::new));
|
||||||
|
|
||||||
|
return List.<Object[]>of(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Object[]> createArgsForAnnotation(Executable exec,
|
||||||
|
ParameterSupplier a) throws IllegalAccessException,
|
||||||
|
InvocationTargetException {
|
||||||
|
if (!canRunOnTheOperatingSystem(a)) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
final Class<?> execClass = exec.getDeclaringClass();
|
||||||
|
final var supplierFuncName = a.value();
|
||||||
|
|
||||||
|
final MethodQuery methodQuery;
|
||||||
|
if (!a.value().contains(".")) {
|
||||||
|
// No class name specified
|
||||||
|
methodQuery = new MethodQuery(execClass.getName(), a.value());
|
||||||
|
} else {
|
||||||
|
methodQuery = MethodQuery.fromQualifiedMethodName(supplierFuncName);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Method supplierMethod;
|
||||||
|
try {
|
||||||
|
final var parameterSupplierCandidates = findNullaryLikeMethods(methodQuery);
|
||||||
|
final Function<String, Class> classForName = toFunction(Class::forName);
|
||||||
|
final var supplierMethodClass = classForName.apply(methodQuery.className());
|
||||||
|
if (parameterSupplierCandidates.isEmpty()) {
|
||||||
|
throw new RuntimeException(String.format(
|
||||||
|
"No parameter suppliers in [%s] class",
|
||||||
|
supplierMethodClass.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
var allParameterSuppliers = filterParameterSuppliers(supplierMethodClass).toList();
|
||||||
|
|
||||||
|
supplierMethod = findNullaryLikeMethods(methodQuery)
|
||||||
|
.stream()
|
||||||
|
.filter(allParameterSuppliers::contains)
|
||||||
|
.findFirst().orElseThrow(() -> {
|
||||||
|
var msg = String.format(
|
||||||
|
"No suitable parameter supplier found for %s(%s) annotation",
|
||||||
|
a, supplierFuncName);
|
||||||
|
trace(String.format(
|
||||||
|
"%s. Parameter suppliers of %s class:", msg,
|
||||||
|
execClass.getName()));
|
||||||
|
IntStream.range(0, allParameterSuppliers.size()).mapToObj(idx -> {
|
||||||
|
return String.format(" [%d/%d] %s()", idx + 1,
|
||||||
|
allParameterSuppliers.size(),
|
||||||
|
allParameterSuppliers.get(idx).getName());
|
||||||
|
}).forEachOrdered(TestMethodSupplier::trace);
|
||||||
|
|
||||||
|
return new RuntimeException(msg);
|
||||||
|
});
|
||||||
|
} catch (NoSuchMethodException ex) {
|
||||||
|
throw new RuntimeException(String.format(
|
||||||
|
"Method not found for %s(%s) annotation", a, supplierFuncName));
|
||||||
|
}
|
||||||
|
|
||||||
|
return createArgs(supplierMethod).map(args -> {
|
||||||
|
return mapArgs(exec, args);
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean canRunOnTheOperatingSystem(Annotation a) {
|
||||||
|
switch (a) {
|
||||||
|
case Test t -> {
|
||||||
|
return canRunOnTheOperatingSystem(os, t.ifOS(), t.ifNotOS());
|
||||||
|
}
|
||||||
|
case Parameters t -> {
|
||||||
|
return canRunOnTheOperatingSystem(os, t.ifOS(), t.ifNotOS());
|
||||||
|
}
|
||||||
|
case Parameter t -> {
|
||||||
|
return canRunOnTheOperatingSystem(os, t.ifOS(), t.ifNotOS());
|
||||||
|
}
|
||||||
|
case ParameterSupplier t -> {
|
||||||
|
return canRunOnTheOperatingSystem(os, t.ifOS(), t.ifNotOS());
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isParameterized(Method method) {
|
||||||
|
return Stream.of(
|
||||||
|
Parameter.class, ParameterGroup.class,
|
||||||
|
ParameterSupplier.class, ParameterSupplierGroup.class
|
||||||
|
).anyMatch(method::isAnnotationPresent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isTest(Method method) {
|
||||||
|
return method.isAnnotationPresent(Test.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean canRunOnTheOperatingSystem(OperatingSystem value,
|
||||||
|
OperatingSystem[] include, OperatingSystem[] exclude) {
|
||||||
|
Set<OperatingSystem> suppordOperatingSystems = new HashSet<>();
|
||||||
|
suppordOperatingSystems.addAll(List.of(include));
|
||||||
|
suppordOperatingSystems.removeAll(List.of(exclude));
|
||||||
|
return suppordOperatingSystems.contains(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Parameter[] getMethodParameters(Method method) {
|
||||||
|
if (method.isAnnotationPresent(ParameterGroup.class)) {
|
||||||
|
return ((ParameterGroup) method.getAnnotation(ParameterGroup.class)).value();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method.isAnnotationPresent(Parameter.class)) {
|
||||||
|
return new Parameter[]{(Parameter) method.getAnnotation(Parameter.class)};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Parameter[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ParameterSupplier[] getMethodParameterSuppliers(Method method) {
|
||||||
|
if (method.isAnnotationPresent(ParameterSupplierGroup.class)) {
|
||||||
|
return ((ParameterSupplierGroup) method.getAnnotation(ParameterSupplierGroup.class)).value();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method.isAnnotationPresent(ParameterSupplier.class)) {
|
||||||
|
return new ParameterSupplier[]{(ParameterSupplier) method.getAnnotation(
|
||||||
|
ParameterSupplier.class)};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ParameterSupplier[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Stream<Method> filterParameterSuppliers(Class<?> type) {
|
||||||
|
return Stream.of(type.getMethods())
|
||||||
|
.filter(m -> m.getParameterCount() == 0)
|
||||||
|
.filter(m -> (m.getModifiers() & Modifier.STATIC) != 0)
|
||||||
|
.sorted(Comparator.comparing(Method::getName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Stream<Object[]> createArgs(Method ... parameterSuppliers) throws
|
||||||
|
IllegalAccessException, InvocationTargetException {
|
||||||
|
List<Object[]> args = new ArrayList<>();
|
||||||
|
for (var parameterSupplier : parameterSuppliers) {
|
||||||
|
args.addAll((Collection) parameterSupplier.invoke(null));
|
||||||
|
}
|
||||||
|
return args.stream();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object fromString(String value, Class toType) {
|
||||||
|
if (toType.isEnum()) {
|
||||||
|
return Enum.valueOf(toType, value);
|
||||||
|
}
|
||||||
|
Function<String, Object> converter = FROM_STRING.get(toType);
|
||||||
|
if (converter == null) {
|
||||||
|
throw new RuntimeException(String.format(
|
||||||
|
"Failed to find a conversion of [%s] string to %s type",
|
||||||
|
value, toType.getName()));
|
||||||
|
}
|
||||||
|
return converter.apply(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void trace(String msg) {
|
||||||
|
if (TKit.VERBOSE_TEST_SETUP) {
|
||||||
|
TKit.log(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class InvalidAnnotationException extends Exception {
|
||||||
|
InvalidAnnotationException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Verifier {
|
||||||
|
static boolean isTestClass(Class<?> type) {
|
||||||
|
for (var method : type.getDeclaredMethods()) {
|
||||||
|
if (isParameterized(method) || isTest(method)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void verifyTestClass(Class<?> type) throws InvalidAnnotationException {
|
||||||
|
boolean withTestAnnotations = false;
|
||||||
|
for (var method : type.getDeclaredMethods()) {
|
||||||
|
if (!withTestAnnotations && (isParameterized(method) || isTest(method))) {
|
||||||
|
withTestAnnotations = true;
|
||||||
|
}
|
||||||
|
verifyAnnotationsCorrect(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!withTestAnnotations) {
|
||||||
|
throwNotTestClassException(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void throwNotTestClassException(Class<?> type) throws InvalidAnnotationException {
|
||||||
|
throw new InvalidAnnotationException(String.format(
|
||||||
|
"Type [%s] is not a test class", type.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void verifyAnnotationsCorrect(Method method) throws
|
||||||
|
InvalidAnnotationException {
|
||||||
|
var parameterized = isParameterized(method);
|
||||||
|
if (parameterized && !isTest(method)) {
|
||||||
|
throw new InvalidAnnotationException(String.format(
|
||||||
|
"Missing %s annotation on [%s] method", Test.class.getName(), method));
|
||||||
|
}
|
||||||
|
|
||||||
|
var isPublic = Modifier.isPublic(method.getModifiers());
|
||||||
|
|
||||||
|
if (isTest(method) && !isPublic) {
|
||||||
|
throw new InvalidAnnotationException(String.format(
|
||||||
|
"Non-public method [%s] with %s annotation",
|
||||||
|
method, Test.class.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method.isAnnotationPresent(Parameters.class) && !isPublic) {
|
||||||
|
throw new InvalidAnnotationException(String.format(
|
||||||
|
"Non-public method [%s] with %s annotation",
|
||||||
|
method, Test.class.getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum TypeStatus {
|
||||||
|
NOT_TEST_CLASS,
|
||||||
|
TEST_CLASS,
|
||||||
|
VALID_TEST_CLASS,
|
||||||
|
}
|
||||||
|
|
||||||
|
private final OperatingSystem os;
|
||||||
|
private final Map<Class<?>, TypeStatus> processedTypes = new HashMap<>();
|
||||||
|
|
||||||
|
private static final Object[] DEFAULT_CTOR_ARGS = new Object[0];
|
||||||
|
|
||||||
|
private static final Map<Class, Function<String, Object>> FROM_STRING;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Map<Class, Function<String, Object>> primitives = Map.of(
|
||||||
|
boolean.class, Boolean::valueOf,
|
||||||
|
byte.class, Byte::valueOf,
|
||||||
|
short.class, Short::valueOf,
|
||||||
|
int.class, Integer::valueOf,
|
||||||
|
long.class, Long::valueOf,
|
||||||
|
float.class, Float::valueOf,
|
||||||
|
double.class, Double::valueOf);
|
||||||
|
|
||||||
|
Map<Class, Function<String, Object>> boxed = Map.of(
|
||||||
|
Boolean.class, Boolean::valueOf,
|
||||||
|
Byte.class, Byte::valueOf,
|
||||||
|
Short.class, Short::valueOf,
|
||||||
|
Integer.class, Integer::valueOf,
|
||||||
|
Long.class, Long::valueOf,
|
||||||
|
Float.class, Float::valueOf,
|
||||||
|
Double.class, Double::valueOf);
|
||||||
|
|
||||||
|
Map<Class, Function<String, Object>> other = Map.of(
|
||||||
|
String.class, String::valueOf,
|
||||||
|
Path.class, Path::of);
|
||||||
|
|
||||||
|
Map<Class, Function<String, Object>> combined = new HashMap<>(primitives);
|
||||||
|
combined.putAll(other);
|
||||||
|
combined.putAll(boxed);
|
||||||
|
|
||||||
|
FROM_STRING = Collections.unmodifiableMap(combined);
|
||||||
|
}
|
||||||
|
}
|
@ -29,12 +29,12 @@ import java.util.Collection;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import static java.util.stream.Collectors.toSet;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
import jdk.internal.util.OperatingSystem;
|
||||||
import jdk.jpackage.internal.AppImageFile;
|
import jdk.jpackage.internal.AppImageFile;
|
||||||
import jdk.jpackage.internal.ApplicationLayout;
|
import jdk.jpackage.internal.ApplicationLayout;
|
||||||
import jdk.jpackage.internal.PackageFile;
|
import jdk.jpackage.internal.PackageFile;
|
||||||
import jdk.jpackage.test.Annotations;
|
import jdk.jpackage.test.Annotations.Parameters;
|
||||||
import jdk.jpackage.test.Annotations.Test;
|
import jdk.jpackage.test.Annotations.Test;
|
||||||
import jdk.jpackage.test.Functional.ThrowingConsumer;
|
import jdk.jpackage.test.Functional.ThrowingConsumer;
|
||||||
import jdk.jpackage.test.JPackageCommand;
|
import jdk.jpackage.test.JPackageCommand;
|
||||||
@ -55,47 +55,51 @@ import jdk.jpackage.test.TKit;
|
|||||||
*/
|
*/
|
||||||
public final class InOutPathTest {
|
public final class InOutPathTest {
|
||||||
|
|
||||||
@Annotations.Parameters
|
@Parameters
|
||||||
public static Collection input() {
|
public static Collection input() {
|
||||||
List<Object[]> data = new ArrayList<>();
|
List<Object[]> data = new ArrayList<>();
|
||||||
|
|
||||||
for (var packageTypes : List.of(PackageType.IMAGE.toString(), ALL_NATIVE_PACKAGE_TYPES)) {
|
for (var packageTypeAlias : PackageTypeAlias.values()) {
|
||||||
data.addAll(List.of(new Object[][]{
|
data.addAll(List.of(new Object[][]{
|
||||||
{packageTypes, wrap(InOutPathTest::outputDirInInputDir, "--dest in --input")},
|
{packageTypeAlias, wrap(InOutPathTest::outputDirInInputDir, "--dest in --input")},
|
||||||
{packageTypes, wrap(InOutPathTest::outputDirSameAsInputDir, "--dest same as --input")},
|
{packageTypeAlias, wrap(InOutPathTest::outputDirSameAsInputDir, "--dest same as --input")},
|
||||||
{packageTypes, wrap(InOutPathTest::tempDirInInputDir, "--temp in --input")},
|
{packageTypeAlias, wrap(InOutPathTest::tempDirInInputDir, "--temp in --input")},
|
||||||
{packageTypes, wrap(cmd -> {
|
{packageTypeAlias, wrap(cmd -> {
|
||||||
outputDirInInputDir(cmd);
|
outputDirInInputDir(cmd);
|
||||||
tempDirInInputDir(cmd);
|
tempDirInInputDir(cmd);
|
||||||
}, "--dest and --temp in --input")},
|
}, "--dest and --temp in --input")},
|
||||||
}));
|
}));
|
||||||
data.addAll(additionalContentInput(packageTypes, "--app-content"));
|
data.addAll(additionalContentInput(packageTypeAlias, "--app-content"));
|
||||||
}
|
|
||||||
|
|
||||||
if (!TKit.isOSX()) {
|
|
||||||
data.addAll(List.of(new Object[][]{
|
|
||||||
{PackageType.IMAGE.toString(), wrap(cmd -> {
|
|
||||||
additionalContent(cmd, "--app-content", cmd.outputBundle());
|
|
||||||
}, "--app-content same as output bundle")},
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
var contentsFolder = "Contents/MacOS";
|
|
||||||
data.addAll(List.of(new Object[][]{
|
|
||||||
{PackageType.IMAGE.toString(), wrap(cmd -> {
|
|
||||||
additionalContent(cmd, "--app-content", cmd.outputBundle().resolve(contentsFolder));
|
|
||||||
}, String.format("--app-content same as the \"%s\" folder in the output bundle", contentsFolder))},
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TKit.isOSX()) {
|
|
||||||
data.addAll(additionalContentInput(PackageType.MAC_DMG.toString(),
|
|
||||||
"--mac-dmg-content"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Object[]> additionalContentInput(String packageTypes, String argName) {
|
@Parameters(ifNotOS = OperatingSystem.MACOS)
|
||||||
|
public static Collection<Object[]> appContentInputOther() {
|
||||||
|
return List.of(new Object[][]{
|
||||||
|
{PackageTypeAlias.IMAGE, wrap(cmd -> {
|
||||||
|
additionalContent(cmd, "--app-content", cmd.outputBundle());
|
||||||
|
}, "--app-content same as output bundle")},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Parameters(ifOS = OperatingSystem.MACOS)
|
||||||
|
public static Collection<Object[]> appContentInputOSX() {
|
||||||
|
var contentsFolder = "Contents/MacOS";
|
||||||
|
return List.of(new Object[][]{
|
||||||
|
{PackageTypeAlias.IMAGE, wrap(cmd -> {
|
||||||
|
additionalContent(cmd, "--app-content", cmd.outputBundle().resolve(contentsFolder));
|
||||||
|
}, String.format("--app-content same as the \"%s\" folder in the output bundle", contentsFolder))},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Parameters(ifOS = OperatingSystem.MACOS)
|
||||||
|
public static Collection<Object[]> inputOSX() {
|
||||||
|
return List.of(additionalContentInput(PackageType.MAC_DMG, "--mac-dmg-content").toArray(Object[][]::new));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Object[]> additionalContentInput(Object packageTypes, String argName) {
|
||||||
List<Object[]> data = new ArrayList<>();
|
List<Object[]> data = new ArrayList<>();
|
||||||
|
|
||||||
data.addAll(List.of(new Object[][]{
|
data.addAll(List.of(new Object[][]{
|
||||||
@ -127,13 +131,16 @@ public final class InOutPathTest {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InOutPathTest(String packageTypes, Envelope configure) {
|
public InOutPathTest(PackageTypeAlias packageTypeAlias, Envelope configure) {
|
||||||
if (ALL_NATIVE_PACKAGE_TYPES.equals(packageTypes)) {
|
this(packageTypeAlias.packageTypes, configure);
|
||||||
this.packageTypes = PackageType.NATIVE;
|
|
||||||
} else {
|
|
||||||
this.packageTypes = Stream.of(packageTypes.split(",")).map(
|
|
||||||
PackageType::valueOf).collect(toSet());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public InOutPathTest(PackageType packageType, Envelope configure) {
|
||||||
|
this(Set.of(packageType), configure);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InOutPathTest(Set<PackageType> packageTypes, Envelope configure) {
|
||||||
|
this.packageTypes = packageTypes;
|
||||||
this.configure = configure.value;
|
this.configure = configure.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,6 +278,18 @@ public final class InOutPathTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enum PackageTypeAlias {
|
||||||
|
IMAGE(Set.of(PackageType.IMAGE)),
|
||||||
|
NATIVE(PackageType.NATIVE),
|
||||||
|
;
|
||||||
|
|
||||||
|
PackageTypeAlias(Set<PackageType> packageTypes) {
|
||||||
|
this.packageTypes = packageTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Set<PackageType> packageTypes;
|
||||||
|
}
|
||||||
|
|
||||||
private final Set<PackageType> packageTypes;
|
private final Set<PackageType> packageTypes;
|
||||||
private final ThrowingConsumer<JPackageCommand> configure;
|
private final ThrowingConsumer<JPackageCommand> configure;
|
||||||
|
|
||||||
@ -279,6 +298,4 @@ public final class InOutPathTest {
|
|||||||
// For other platforms it doesn't matter. Keep it the same across
|
// For other platforms it doesn't matter. Keep it the same across
|
||||||
// all platforms for simplicity.
|
// all platforms for simplicity.
|
||||||
private static final Path JAR_PATH = Path.of("Resources/duke.jar");
|
private static final Path JAR_PATH = Path.of("Resources/duke.jar");
|
||||||
|
|
||||||
private static final String ALL_NATIVE_PACKAGE_TYPES = "NATIVE";
|
|
||||||
}
|
}
|
||||||
|
@ -22,13 +22,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.HashMap;
|
import jdk.internal.util.OperatingSystem;
|
||||||
import java.util.Map;
|
|
||||||
import jdk.jpackage.test.TKit;
|
import jdk.jpackage.test.TKit;
|
||||||
import jdk.jpackage.test.PackageTest;
|
import jdk.jpackage.test.PackageTest;
|
||||||
import jdk.jpackage.test.PackageType;
|
import jdk.jpackage.test.PackageType;
|
||||||
import jdk.jpackage.test.Functional;
|
|
||||||
import jdk.jpackage.test.Annotations.Parameter;
|
import jdk.jpackage.test.Annotations.Parameter;
|
||||||
|
import jdk.jpackage.test.Annotations.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test --install-dir parameter. Output of the test should be
|
* Test --install-dir parameter. Output of the test should be
|
||||||
@ -76,28 +75,18 @@ import jdk.jpackage.test.Annotations.Parameter;
|
|||||||
*/
|
*/
|
||||||
public class InstallDirTest {
|
public class InstallDirTest {
|
||||||
|
|
||||||
public static void testCommon() {
|
@Test
|
||||||
final Map<PackageType, Path> INSTALL_DIRS = Functional.identity(() -> {
|
@Parameter(value = "TestVendor\\InstallDirTest1234", ifOS = OperatingSystem.WINDOWS)
|
||||||
Map<PackageType, Path> reply = new HashMap<>();
|
@Parameter(value = "/opt/jpackage", ifOS = OperatingSystem.LINUX)
|
||||||
reply.put(PackageType.WIN_MSI, Path.of("TestVendor\\InstallDirTest1234"));
|
@Parameter(value = "/Applications/jpackage", ifOS = OperatingSystem.MACOS)
|
||||||
reply.put(PackageType.WIN_EXE, reply.get(PackageType.WIN_MSI));
|
public static void testCommon(Path installDir) {
|
||||||
|
|
||||||
reply.put(PackageType.LINUX_DEB, Path.of("/opt/jpackage"));
|
|
||||||
reply.put(PackageType.LINUX_RPM, reply.get(PackageType.LINUX_DEB));
|
|
||||||
|
|
||||||
reply.put(PackageType.MAC_PKG, Path.of("/Applications/jpackage"));
|
|
||||||
reply.put(PackageType.MAC_DMG, reply.get(PackageType.MAC_PKG));
|
|
||||||
|
|
||||||
return reply;
|
|
||||||
}).get();
|
|
||||||
|
|
||||||
new PackageTest().configureHelloApp()
|
new PackageTest().configureHelloApp()
|
||||||
.addInitializer(cmd -> {
|
.addInitializer(cmd -> {
|
||||||
cmd.addArguments("--install-dir", INSTALL_DIRS.get(
|
cmd.addArguments("--install-dir", installDir);
|
||||||
cmd.packageType()));
|
|
||||||
}).run();
|
}).run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(ifOS = OperatingSystem.LINUX)
|
||||||
@Parameter("/")
|
@Parameter("/")
|
||||||
@Parameter(".")
|
@Parameter(".")
|
||||||
@Parameter("foo")
|
@Parameter("foo")
|
||||||
|
Loading…
Reference in New Issue
Block a user