test/jdk/tools/jpackage/share/jdk/jpackage/tests/MainClassTest.java
branchJDK-8200758-branch
changeset 58648 3bf53ffa9ae7
child 58695 64adf683bc7b
equal deleted inserted replaced
58647:2c43b89b1679 58648:3bf53ffa9ae7
       
     1 /*
       
     2  * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  */
       
    23 
       
    24 package jdk.jpackage.tests;
       
    25 
       
    26 import java.io.IOException;
       
    27 import java.nio.file.Files;
       
    28 import java.util.Collection;
       
    29 import java.util.ArrayList;
       
    30 import java.util.List;
       
    31 import java.util.Set;
       
    32 import java.util.jar.JarFile;
       
    33 import java.util.Objects;
       
    34 import java.util.stream.Collectors;
       
    35 import java.util.stream.Stream;
       
    36 import java.nio.file.Path;
       
    37 import java.util.function.Predicate;
       
    38 import java.util.jar.JarEntry;
       
    39 import jdk.jpackage.test.Annotations.Parameters;
       
    40 import jdk.jpackage.test.Annotations.Test;
       
    41 import jdk.jpackage.test.*;
       
    42 import jdk.jpackage.test.Functional.ThrowingConsumer;
       
    43 import static jdk.jpackage.tests.MainClassTest.Script.MainClassType.*;
       
    44 
       
    45 
       
    46 /*
       
    47  * @test
       
    48  * @summary test different settings of main class name for jpackage
       
    49  * @library ../../../../helpers
       
    50  * @build jdk.jpackage.test.*
       
    51  * @modules jdk.jpackage/jdk.jpackage.internal
       
    52  * @compile MainClassTest.java
       
    53  * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main
       
    54  *  --jpt-run=jdk.jpackage.tests.MainClassTest
       
    55  *  --jpt-space-subst=_
       
    56  *  --jpt-exclude=modular=y;_main-class=n;_jar-main-class=b;_jlink=y
       
    57  *  --jpt-exclude=modular=y;_main-class=n;_jar-main-class=n;_jlink=n
       
    58  */
       
    59 
       
    60 public final class MainClassTest {
       
    61 
       
    62     static final class Script {
       
    63         Script() {
       
    64             appDesc = JavaAppDesc.parse("test.Hello");
       
    65         }
       
    66 
       
    67         Script modular(boolean v) {
       
    68             appDesc.setModuleName(v ? null : "com.other");
       
    69             return this;
       
    70         }
       
    71 
       
    72         Script withJLink(boolean v) {
       
    73             withJLink = v;
       
    74             return this;
       
    75         }
       
    76 
       
    77         Script withMainClass(MainClassType v) {
       
    78             mainClass = v;
       
    79             return this;
       
    80         }
       
    81 
       
    82         Script withJarMainClass(MainClassType v) {
       
    83             appDesc.setJarWithMainClass(v != NotSet);
       
    84             jarMainClass = v;
       
    85             return this;
       
    86         }
       
    87 
       
    88         Script expectedErrorMessage(String v) {
       
    89             expectedErrorMessage = v;
       
    90             return this;
       
    91         }
       
    92 
       
    93         @Override
       
    94         public String toString() {
       
    95             return Stream.of(
       
    96                     format("modular", appDesc.moduleName() != null ? 'y' : 'n'),
       
    97                     format("main-class", mainClass),
       
    98                     format("jar-main-class", jarMainClass),
       
    99                     format("jlink", withJLink ? 'y' : 'n'),
       
   100                     format("error", expectedErrorMessage)
       
   101             ).filter(Objects::nonNull).collect(Collectors.joining("; "));
       
   102         }
       
   103 
       
   104         private static String format(String key, Object value) {
       
   105             if (value == null) {
       
   106                 return null;
       
   107             }
       
   108             return String.join("=", key, value.toString());
       
   109         }
       
   110 
       
   111         enum MainClassType {
       
   112             NotSet("n"),
       
   113             SetWrong("b"),
       
   114             SetRight("y");
       
   115 
       
   116             MainClassType(String label) {
       
   117                 this.label = label;
       
   118             }
       
   119 
       
   120             @Override
       
   121             public String toString() {
       
   122                 return label;
       
   123             }
       
   124 
       
   125             private final String label;
       
   126         };
       
   127 
       
   128         private JavaAppDesc appDesc;
       
   129         private boolean withJLink;
       
   130         private MainClassType mainClass;
       
   131         private MainClassType jarMainClass;
       
   132         private String expectedErrorMessage;
       
   133     }
       
   134 
       
   135     public MainClassTest(Script script) {
       
   136         this.script = script;
       
   137 
       
   138         nonExistingMainClass = Stream.of(
       
   139                 script.appDesc.packageName(), "ThereIsNoSuchClass").filter(
       
   140                 Objects::nonNull).collect(Collectors.joining("."));
       
   141 
       
   142         cmd = JPackageCommand.helloAppImage(script.appDesc);
       
   143         if (!script.withJLink) {
       
   144             cmd.setFakeRuntime();
       
   145         }
       
   146 
       
   147         final String moduleName = script.appDesc.moduleName();
       
   148         switch (script.mainClass) {
       
   149             case NotSet:
       
   150                 if (moduleName != null) {
       
   151                     // Don't specify class name, only module name.
       
   152                     cmd.setArgumentValue("--module", moduleName);
       
   153                 } else {
       
   154                     cmd.removeArgumentWithValue("--main-class");
       
   155                 }
       
   156                 break;
       
   157 
       
   158             case SetWrong:
       
   159                 if (moduleName != null) {
       
   160                     cmd.setArgumentValue("--module",
       
   161                             String.join("/", moduleName, nonExistingMainClass));
       
   162                 } else {
       
   163                     cmd.setArgumentValue("--main-class", nonExistingMainClass);
       
   164                 }
       
   165         }
       
   166     }
       
   167 
       
   168     @Parameters
       
   169     public static Collection scripts() {
       
   170         final var withMainClass = Set.of(SetWrong, SetRight);
       
   171 
       
   172         List<Script[]> scripts = new ArrayList<>();
       
   173         for (var withJLink : List.of(true, false)) {
       
   174             for (var modular : List.of(true, false)) {
       
   175                 for (var mainClass : Script.MainClassType.values()) {
       
   176                     for (var jarMainClass : Script.MainClassType.values()) {
       
   177                         if (!withJLink && (jarMainClass == SetWrong || mainClass
       
   178                                 == SetWrong)) {
       
   179                             // Without runtime can't run app to verify it will fail, so
       
   180                             // there is no point in such testing.
       
   181                             continue;
       
   182                         }
       
   183 
       
   184                         Script script = new Script()
       
   185                             .modular(modular)
       
   186                             .withJLink(withJLink)
       
   187                             .withMainClass(mainClass)
       
   188                             .withJarMainClass(jarMainClass);
       
   189 
       
   190                         if (withMainClass.contains(jarMainClass)
       
   191                                 || withMainClass.contains(mainClass)) {
       
   192                         } else if (modular) {
       
   193                             script.expectedErrorMessage(
       
   194                                     "A main class was not specified nor was one found in the jar");
       
   195                         } else {
       
   196                             script.expectedErrorMessage(
       
   197                                     "Error: Main application class is missing");
       
   198                         }
       
   199 
       
   200                         scripts.add(new Script[]{script});
       
   201                     }
       
   202                 }
       
   203             }
       
   204         }
       
   205         return scripts;
       
   206     }
       
   207 
       
   208     @Test
       
   209     public void test() throws IOException {
       
   210         if (script.jarMainClass == SetWrong) {
       
   211             initJarWithWrongMainClass();
       
   212         }
       
   213 
       
   214         if (script.expectedErrorMessage != null) {
       
   215             // This is the case when main class is not found nor in jar
       
   216             // file nor on command line.
       
   217             List<String> output = cmd
       
   218                     .saveConsoleOutput(true)
       
   219                     .execute()
       
   220                     .assertExitCodeIs(1)
       
   221                     .getOutput();
       
   222             TKit.assertTextStream(script.expectedErrorMessage).apply(output.stream());
       
   223             return;
       
   224         }
       
   225 
       
   226         // Get here only if main class is specified.
       
   227         boolean appShouldSucceed = false;
       
   228 
       
   229         // Should succeed if valid main class is set on the command line.
       
   230         appShouldSucceed |= (script.mainClass == SetRight);
       
   231 
       
   232         // Should succeed if main class is not set on the command line but set
       
   233         // to valid value in the jar.
       
   234         appShouldSucceed |= (script.mainClass == NotSet && script.jarMainClass == SetRight);
       
   235 
       
   236         if (appShouldSucceed) {
       
   237             cmd.executeAndAssertHelloAppImageCreated();
       
   238         } else {
       
   239             cmd.executeAndAssertImageCreated();
       
   240             if (!cmd.isFakeRuntime(String.format("Not running [%s]",
       
   241                     cmd.appLauncherPath()))) {
       
   242                 List<String> output = new Executor()
       
   243                     .setDirectory(cmd.outputDir())
       
   244                     .setExecutable(cmd.appLauncherPath())
       
   245                     .dumpOutput().saveOutput()
       
   246                     .execute().assertExitCodeIs(1).getOutput();
       
   247                 TKit.assertTextStream(String.format(
       
   248                         "Error: Could not find or load main class %s",
       
   249                         nonExistingMainClass)).apply(output.stream());
       
   250             }
       
   251         }
       
   252     }
       
   253 
       
   254     private void initJarWithWrongMainClass() {
       
   255         // Call JPackageCommand.executePrerequisiteActions() to build app's jar.
       
   256         // executePrerequisiteActions() is called by JPackageCommand instance
       
   257         // only once.
       
   258         cmd.executePrerequisiteActions();
       
   259 
       
   260         Path jarFile;
       
   261         if (script.appDesc.moduleName() != null) {
       
   262             jarFile = Path.of(cmd.getArgumentValue("--module-path"),
       
   263                     script.appDesc.jarFileName());
       
   264         } else {
       
   265             jarFile = cmd.inputDir().resolve(cmd.getArgumentValue("--main-jar"));
       
   266         }
       
   267 
       
   268         // Create jar file with the main class attribute in manifest set to
       
   269         // non-existing class.
       
   270         TKit.withTempDirectory("repack-jar", workDir -> {
       
   271             Path manifestFile = workDir.resolve("META-INF/MANIFEST.MF");
       
   272             try (var jar = new JarFile(jarFile.toFile())) {
       
   273                 jar.stream()
       
   274                 .filter(Predicate.not(JarEntry::isDirectory))
       
   275                 .sequential().forEachOrdered(ThrowingConsumer.toConsumer(
       
   276                     jarEntry -> {
       
   277                         try (var in = jar.getInputStream(jarEntry)) {
       
   278                             Path fileName = workDir.resolve(jarEntry.getName());
       
   279                             Files.createDirectories(fileName.getParent());
       
   280                             Files.copy(in, fileName);
       
   281                         }
       
   282                     }));
       
   283             }
       
   284 
       
   285             Files.delete(jarFile);
       
   286 
       
   287             // Adjust manifest.
       
   288             TKit.createTextFile(manifestFile, Files.readAllLines(
       
   289                     manifestFile).stream().map(line -> line.replace(
       
   290                     script.appDesc.className(), nonExistingMainClass)));
       
   291 
       
   292             new Executor().setToolProvider(JavaTool.JAR)
       
   293             .addArguments("-c", "-M", "-f", jarFile.toString())
       
   294             .addArguments("-C", workDir.toString(), ".")
       
   295             .execute().assertExitCodeIsZero();
       
   296         });
       
   297     }
       
   298 
       
   299     private final JPackageCommand cmd;
       
   300     private final Script script;
       
   301     private final String nonExistingMainClass;
       
   302 }