--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/share/jdk/jpackage/tests/MainClassTest.java Wed Oct 16 10:32:08 2019 -0400
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2019, 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.tests;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Collection;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.jar.JarFile;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.nio.file.Path;
+import java.util.function.Predicate;
+import java.util.jar.JarEntry;
+import jdk.jpackage.test.Annotations.Parameters;
+import jdk.jpackage.test.Annotations.Test;
+import jdk.jpackage.test.*;
+import jdk.jpackage.test.Functional.ThrowingConsumer;
+import static jdk.jpackage.tests.MainClassTest.Script.MainClassType.*;
+
+
+/*
+ * @test
+ * @summary test different settings of main class name for jpackage
+ * @library ../../../../helpers
+ * @build jdk.jpackage.test.*
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @compile MainClassTest.java
+ * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main
+ * --jpt-run=jdk.jpackage.tests.MainClassTest
+ * --jpt-space-subst=_
+ * --jpt-exclude=modular=y;_main-class=n;_jar-main-class=b;_jlink=y
+ * --jpt-exclude=modular=y;_main-class=n;_jar-main-class=n;_jlink=n
+ */
+
+public final class MainClassTest {
+
+ static final class Script {
+ Script() {
+ appDesc = JavaAppDesc.parse("test.Hello");
+ }
+
+ Script modular(boolean v) {
+ appDesc.setModuleName(v ? null : "com.other");
+ return this;
+ }
+
+ Script withJLink(boolean v) {
+ withJLink = v;
+ return this;
+ }
+
+ Script withMainClass(MainClassType v) {
+ mainClass = v;
+ return this;
+ }
+
+ Script withJarMainClass(MainClassType v) {
+ appDesc.setJarWithMainClass(v != NotSet);
+ jarMainClass = v;
+ return this;
+ }
+
+ Script expectedErrorMessage(String v) {
+ expectedErrorMessage = v;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return Stream.of(
+ format("modular", appDesc.moduleName() != null ? 'y' : 'n'),
+ format("main-class", mainClass),
+ format("jar-main-class", jarMainClass),
+ format("jlink", withJLink ? 'y' : 'n'),
+ format("error", expectedErrorMessage)
+ ).filter(Objects::nonNull).collect(Collectors.joining("; "));
+ }
+
+ private static String format(String key, Object value) {
+ if (value == null) {
+ return null;
+ }
+ return String.join("=", key, value.toString());
+ }
+
+ enum MainClassType {
+ NotSet("n"),
+ SetWrong("b"),
+ SetRight("y");
+
+ MainClassType(String label) {
+ this.label = label;
+ }
+
+ @Override
+ public String toString() {
+ return label;
+ }
+
+ private final String label;
+ };
+
+ private JavaAppDesc appDesc;
+ private boolean withJLink;
+ private MainClassType mainClass;
+ private MainClassType jarMainClass;
+ private String expectedErrorMessage;
+ }
+
+ public MainClassTest(Script script) {
+ this.script = script;
+
+ nonExistingMainClass = Stream.of(
+ script.appDesc.packageName(), "ThereIsNoSuchClass").filter(
+ Objects::nonNull).collect(Collectors.joining("."));
+
+ cmd = JPackageCommand.helloAppImage(script.appDesc);
+ if (!script.withJLink) {
+ cmd.setFakeRuntime();
+ }
+
+ final String moduleName = script.appDesc.moduleName();
+ switch (script.mainClass) {
+ case NotSet:
+ if (moduleName != null) {
+ // Don't specify class name, only module name.
+ cmd.setArgumentValue("--module", moduleName);
+ } else {
+ cmd.removeArgumentWithValue("--main-class");
+ }
+ break;
+
+ case SetWrong:
+ if (moduleName != null) {
+ cmd.setArgumentValue("--module",
+ String.join("/", moduleName, nonExistingMainClass));
+ } else {
+ cmd.setArgumentValue("--main-class", nonExistingMainClass);
+ }
+ }
+ }
+
+ @Parameters
+ public static Collection scripts() {
+ final var withMainClass = Set.of(SetWrong, SetRight);
+
+ List<Script[]> scripts = new ArrayList<>();
+ for (var withJLink : List.of(true, false)) {
+ for (var modular : List.of(true, false)) {
+ for (var mainClass : Script.MainClassType.values()) {
+ for (var jarMainClass : Script.MainClassType.values()) {
+ if (!withJLink && (jarMainClass == SetWrong || mainClass
+ == SetWrong)) {
+ // Without runtime can't run app to verify it will fail, so
+ // there is no point in such testing.
+ continue;
+ }
+
+ Script script = new Script()
+ .modular(modular)
+ .withJLink(withJLink)
+ .withMainClass(mainClass)
+ .withJarMainClass(jarMainClass);
+
+ if (withMainClass.contains(jarMainClass)
+ || withMainClass.contains(mainClass)) {
+ } else if (modular) {
+ script.expectedErrorMessage(
+ "A main class was not specified nor was one found in the jar");
+ } else {
+ script.expectedErrorMessage(
+ "Error: Main application class is missing");
+ }
+
+ scripts.add(new Script[]{script});
+ }
+ }
+ }
+ }
+ return scripts;
+ }
+
+ @Test
+ public void test() throws IOException {
+ if (script.jarMainClass == SetWrong) {
+ initJarWithWrongMainClass();
+ }
+
+ if (script.expectedErrorMessage != null) {
+ // This is the case when main class is not found nor in jar
+ // file nor on command line.
+ List<String> output = cmd
+ .saveConsoleOutput(true)
+ .execute()
+ .assertExitCodeIs(1)
+ .getOutput();
+ TKit.assertTextStream(script.expectedErrorMessage).apply(output.stream());
+ return;
+ }
+
+ // Get here only if main class is specified.
+ boolean appShouldSucceed = false;
+
+ // Should succeed if valid main class is set on the command line.
+ appShouldSucceed |= (script.mainClass == SetRight);
+
+ // Should succeed if main class is not set on the command line but set
+ // to valid value in the jar.
+ appShouldSucceed |= (script.mainClass == NotSet && script.jarMainClass == SetRight);
+
+ if (appShouldSucceed) {
+ cmd.executeAndAssertHelloAppImageCreated();
+ } else {
+ cmd.executeAndAssertImageCreated();
+ if (!cmd.isFakeRuntime(String.format("Not running [%s]",
+ cmd.appLauncherPath()))) {
+ List<String> output = new Executor()
+ .setDirectory(cmd.outputDir())
+ .setExecutable(cmd.appLauncherPath())
+ .dumpOutput().saveOutput()
+ .execute().assertExitCodeIs(1).getOutput();
+ TKit.assertTextStream(String.format(
+ "Error: Could not find or load main class %s",
+ nonExistingMainClass)).apply(output.stream());
+ }
+ }
+ }
+
+ private void initJarWithWrongMainClass() {
+ // Call JPackageCommand.executePrerequisiteActions() to build app's jar.
+ // executePrerequisiteActions() is called by JPackageCommand instance
+ // only once.
+ cmd.executePrerequisiteActions();
+
+ Path jarFile;
+ if (script.appDesc.moduleName() != null) {
+ jarFile = Path.of(cmd.getArgumentValue("--module-path"),
+ script.appDesc.jarFileName());
+ } else {
+ jarFile = cmd.inputDir().resolve(cmd.getArgumentValue("--main-jar"));
+ }
+
+ // Create jar file with the main class attribute in manifest set to
+ // non-existing class.
+ TKit.withTempDirectory("repack-jar", workDir -> {
+ Path manifestFile = workDir.resolve("META-INF/MANIFEST.MF");
+ try (var jar = new JarFile(jarFile.toFile())) {
+ jar.stream()
+ .filter(Predicate.not(JarEntry::isDirectory))
+ .sequential().forEachOrdered(ThrowingConsumer.toConsumer(
+ jarEntry -> {
+ try (var in = jar.getInputStream(jarEntry)) {
+ Path fileName = workDir.resolve(jarEntry.getName());
+ Files.createDirectories(fileName.getParent());
+ Files.copy(in, fileName);
+ }
+ }));
+ }
+
+ Files.delete(jarFile);
+
+ // Adjust manifest.
+ TKit.createTextFile(manifestFile, Files.readAllLines(
+ manifestFile).stream().map(line -> line.replace(
+ script.appDesc.className(), nonExistingMainClass)));
+
+ new Executor().setToolProvider(JavaTool.JAR)
+ .addArguments("-c", "-M", "-f", jarFile.toString())
+ .addArguments("-C", workDir.toString(), ".")
+ .execute().assertExitCodeIsZero();
+ });
+ }
+
+ private final JPackageCommand cmd;
+ private final Script script;
+ private final String nonExistingMainClass;
+}