From 0714114fe3e0ac01657053164c61cbb702c0f9a0 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Tue, 19 Nov 2024 13:53:45 +0000 Subject: [PATCH] 8344322: Improve capabilities of jpackage test lib to validate error output of jpackage Reviewed-by: almatvee --- .../jpackage/test/CannedFormattedString.java | 53 +++++++++++++ .../jdk/jpackage/test/JPackageCommand.java | 24 ++++++ .../jpackage/test/JPackageStringBundle.java | 67 ++++++++++++++++ .../helpers/jdk/jpackage/test/TKit.java | 34 +++++++- test/jdk/tools/jpackage/share/ErrorTest.java | 78 ++++++++++--------- 5 files changed, 218 insertions(+), 38 deletions(-) create mode 100644 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CannedFormattedString.java create mode 100644 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageStringBundle.java diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CannedFormattedString.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CannedFormattedString.java new file mode 100644 index 00000000000..8b28049b7b1 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CannedFormattedString.java @@ -0,0 +1,53 @@ +/* + * 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.List; +import java.util.function.BiFunction; + +public final class CannedFormattedString { + + CannedFormattedString(BiFunction formatter, + String key, Object[] args) { + this.formatter = formatter; + this.key = key; + this.args = args; + } + + public String getValue() { + return formatter.apply(key, args); + } + + @Override + public String toString() { + if (args.length == 0) { + return String.format("%s", key); + } else { + return String.format("%s+%s", key, List.of(args)); + } + } + + private final BiFunction formatter; + private final String key; + private final Object[] args; +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java index 9b25c9058d1..49eeb25a00e 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java @@ -78,6 +78,7 @@ public final class JPackageCommand extends CommandArguments { prerequisiteActions = new Actions(cmd.prerequisiteActions); verifyActions = new Actions(cmd.verifyActions); appLayoutAsserts = cmd.appLayoutAsserts; + outputValidator = cmd.outputValidator; executeInDirectory = cmd.executeInDirectory; } @@ -739,6 +740,24 @@ public final class JPackageCommand extends CommandArguments { return this; } + public JPackageCommand validateOutput(TKit.TextStreamVerifier validator) { + return JPackageCommand.this.validateOutput(validator::apply); + } + + public JPackageCommand validateOutput(Consumer> validator) { + if (validator != null) { + saveConsoleOutput(true); + outputValidator = validator; + } else { + outputValidator = null; + } + return this; + } + + public JPackageCommand validateOutput(CannedFormattedString str) { + return JPackageCommand.this.validateOutput(TKit.assertTextStream(str.getValue())); + } + public boolean isWithToolProvider() { return Optional.ofNullable(withToolProvider).orElse( defaultWithToolProvider); @@ -817,6 +836,10 @@ public final class JPackageCommand extends CommandArguments { .createExecutor() .execute(expectedExitCode); + if (outputValidator != null) { + outputValidator.accept(result.getOutput().stream()); + } + if (result.exitCode == 0) { executeVerifyActions(); } @@ -1187,6 +1210,7 @@ public final class JPackageCommand extends CommandArguments { private final Actions verifyActions; private Path executeInDirectory; private Set appLayoutAsserts = Set.of(AppLayoutAssert.values()); + private Consumer> outputValidator; private static boolean defaultWithToolProvider; private static final Map PACKAGE_TYPES = Functional.identity( diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageStringBundle.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageStringBundle.java new file mode 100644 index 00000000000..565822d4504 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageStringBundle.java @@ -0,0 +1,67 @@ +/* + * 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.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.text.MessageFormat; + +public enum JPackageStringBundle { + + MAIN("jdk.jpackage.internal.I18N"), + ; + + JPackageStringBundle(String i18nClassName) { + try { + i18nClass = Class.forName(i18nClassName); + + i18nClass_getString = i18nClass.getDeclaredMethod("getString", String.class); + i18nClass_getString.setAccessible(true); + } catch (ClassNotFoundException|NoSuchMethodException ex) { + throw Functional.rethrowUnchecked(ex); + } + } + + /** + * Return a string value of the given key from jpackage resources. + */ + private String getString(String key) { + try { + return (String)i18nClass_getString.invoke(i18nClass, key); + } catch (IllegalAccessException|InvocationTargetException ex) { + throw Functional.rethrowUnchecked(ex); + } + } + + private String getFormattedString(String key, Object[] args) { + return MessageFormat.format(getString(key), args); + } + + public CannedFormattedString cannedFormattedString(String key, String ... args) { + return new CannedFormattedString(this::getFormattedString, key, args); + } + + private final Class i18nClass; + private final Method i18nClass_getString; +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java index 957449697a5..8f91d581ef1 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java @@ -946,6 +946,16 @@ public final class TKit { return this; } + public TextStreamVerifier andThen(Consumer> anotherVerifier) { + this.anotherVerifier = anotherVerifier; + return this; + } + + public TextStreamVerifier andThen(TextStreamVerifier anotherVerifier) { + this.anotherVerifier = anotherVerifier::apply; + return this; + } + public TextStreamVerifier orElseThrow(RuntimeException v) { return orElseThrow(() -> v); } @@ -956,9 +966,22 @@ public final class TKit { } public void apply(Stream lines) { - String matchedStr = lines.filter(line -> predicate.test(line, value)).findFirst().orElse( - null); - String labelStr = Optional.ofNullable(label).orElse("output"); + final String matchedStr; + + lines = lines.dropWhile(line -> !predicate.test(line, value)); + if (anotherVerifier == null) { + matchedStr = lines.findFirst().orElse(null); + } else { + var tail = lines.toList(); + if (tail.isEmpty()) { + matchedStr = null; + } else { + matchedStr = tail.get(0); + } + lines = tail.stream().skip(1); + } + + final String labelStr = Optional.ofNullable(label).orElse("output"); if (negate) { String msg = String.format( "Check %s doesn't contain [%s] string", labelStr, value); @@ -982,12 +1005,17 @@ public final class TKit { } } } + + if (anotherVerifier != null) { + anotherVerifier.accept(lines); + } } private BiPredicate predicate; private String label; private boolean negate; private Supplier createException; + private Consumer> anotherVerifier; private final String value; } diff --git a/test/jdk/tools/jpackage/share/ErrorTest.java b/test/jdk/tools/jpackage/share/ErrorTest.java index f012eac1ced..ed7338a76dd 100644 --- a/test/jdk/tools/jpackage/share/ErrorTest.java +++ b/test/jdk/tools/jpackage/share/ErrorTest.java @@ -24,9 +24,13 @@ import java.util.Collection; import java.util.List; -import jdk.jpackage.test.Annotations.Parameters; +import java.util.Optional; +import java.util.stream.Stream; +import jdk.jpackage.test.Annotations.ParameterSupplier; import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.CannedFormattedString; import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.JPackageStringBundle; import jdk.jpackage.test.TKit; /* @@ -53,81 +57,85 @@ import jdk.jpackage.test.TKit; public final class ErrorTest { - private final String expectedError; - private final JPackageCommand cmd; - - @Parameters public static Collection input() { return List.of(new Object[][]{ // non-existent arg {"Hello", new String[]{"--no-such-argument"}, null, - "Invalid Option: [--no-such-argument]"}, + JPackageStringBundle.MAIN.cannedFormattedString("ERR_InvalidOption", "--no-such-argument")}, // no main jar {"Hello", null, new String[]{"--main-jar"}, - "--main-jar or --module"}, + JPackageStringBundle.MAIN.cannedFormattedString("ERR_NoEntryPoint")}, // no main-class {"Hello", null, new String[]{"--main-class"}, - "main class was not specified"}, + JPackageStringBundle.MAIN.cannedFormattedString("error.no-main-class-with-main-jar", "hello.jar"), + JPackageStringBundle.MAIN.cannedFormattedString("error.no-main-class-with-main-jar.advice", "hello.jar")}, // non-existent main jar {"Hello", new String[]{"--main-jar", "non-existent.jar"}, null, - "main jar does not exist"}, + JPackageStringBundle.MAIN.cannedFormattedString("error.main-jar-does-not-exist", "non-existent.jar")}, // non-existent runtime {"Hello", new String[]{"--runtime-image", "non-existent.runtime"}, null, - "does not exist"}, + JPackageStringBundle.MAIN.cannedFormattedString("message.runtime-image-dir-does-not-exist", "runtime-image", "non-existent.runtime")}, // non-existent resource-dir {"Hello", new String[]{"--resource-dir", "non-existent.dir"}, null, - "does not exist"}, + JPackageStringBundle.MAIN.cannedFormattedString("message.resource-dir-does-not-exist", "resource-dir", "non-existent.dir")}, // invalid type {"Hello", new String[]{"--type", "invalid-type"}, null, - "Invalid or unsupported type: [invalid-type]"}, + JPackageStringBundle.MAIN.cannedFormattedString("ERR_InvalidInstallerType", "invalid-type")}, // no --input {"Hello", null, new String[]{"--input"}, - "Missing argument: --input"}, + JPackageStringBundle.MAIN.cannedFormattedString("ERR_MissingArgument", "--input")}, // no --module-path {"com.other/com.other.Hello", null, new String[]{"--module-path"}, - "Missing argument: --runtime-image or --module-path"}, + JPackageStringBundle.MAIN.cannedFormattedString("ERR_MissingArgument", "--runtime-image or --module-path")}, }); } - public ErrorTest(String javaAppDesc, String[] jpackageArgs, - String[] removeArgs, - String expectedError) { - this.expectedError = expectedError; - - cmd = JPackageCommand.helloAppImage(javaAppDesc) - .saveConsoleOutput(true).dumpOutput(true); - if (jpackageArgs != null) { - cmd.addArguments(jpackageArgs); - } if (removeArgs != null) { - for (String arg : removeArgs) { - cmd.removeArgumentWithValue(arg); - } - } - } - @Test - public void test() { - List output = cmd.execute(1).getOutput(); - TKit.assertNotNull(output, "output is null"); - TKit.assertTextStream(expectedError).apply(output.stream()); - } + @ParameterSupplier("input") + public static void test(String javaAppDesc, String[] jpackageArgs, + String[] removeArgs, CannedFormattedString... expectedErrors) { + // Init default jpackage test command line. + var cmd = JPackageCommand.helloAppImage(javaAppDesc) + // Disable default logic adding `--verbose` option + // to jpackage command line. + // It will affect jpackage error messages if the command line is malformed. + .ignoreDefaultVerbose(true) + // Ignore external runtime as it will interfer + // with jpackage arguments in this test. + .ignoreDefaultRuntime(true); + // Add arguments if requested. + Optional.ofNullable(jpackageArgs).ifPresent(cmd::addArguments); + + // Remove arguments if requested. + Optional.ofNullable(removeArgs).map(List::of).ifPresent( + args -> args.forEach(cmd::removeArgumentWithValue)); + + // Configure jpackage output verifier to look up the list of provided + // errors in the order they specified. + cmd.validateOutput(Stream.of(expectedErrors) + .map(CannedFormattedString::getValue) + .map(TKit::assertTextStream) + .reduce(TKit.TextStreamVerifier::andThen).get()); + + cmd.execute(1); + } }