test/jdk/tools/jpackage/share/jdk/jpackage/tests/MainClassTest.java
branchJDK-8200758-branch
changeset 58648 3bf53ffa9ae7
child 58695 64adf683bc7b
--- /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;
+}