test/jdk/tools/jpackage/helpers/jdk/jpackage/test/HelloApp.java
branchJDK-8200758-branch
changeset 58416 f09bf58c1f17
parent 58301 e0efb29609bd
child 58648 3bf53ffa9ae7
equal deleted inserted replaced
58415:73f8e557549a 58416:f09bf58c1f17
    24 
    24 
    25 import java.io.File;
    25 import java.io.File;
    26 import java.io.IOException;
    26 import java.io.IOException;
    27 import java.nio.file.Files;
    27 import java.nio.file.Files;
    28 import java.nio.file.Path;
    28 import java.nio.file.Path;
    29 import java.util.Collections;
    29 import java.util.ArrayList;
    30 import java.util.Enumeration;
       
    31 import java.util.List;
    30 import java.util.List;
    32 import java.util.concurrent.atomic.AtomicInteger;
    31 import java.util.concurrent.atomic.AtomicBoolean;
    33 import java.util.function.Consumer;
    32 import java.util.regex.Matcher;
       
    33 import java.util.regex.Pattern;
       
    34 import java.util.stream.Collectors;
       
    35 import jdk.jpackage.test.Functional.ThrowingFunction;
       
    36 import jdk.jpackage.test.Functional.ThrowingSupplier;
    34 
    37 
    35 public class HelloApp {
    38 public class HelloApp {
    36 
    39 
    37     private static final String MAIN_CLASS = "Hello";
    40     private HelloApp() {
    38     private static final String JAR_FILENAME = "hello.jar";
    41         setClassName(CLASS_NAME).setJarFileName("hello.jar");
    39     private static final Consumer<JPackageCommand> CREATE_JAR_ACTION = (cmd) -> {
    42     }
    40         new JarBuilder()
    43 
    41                 .setOutputJar(cmd.inputDir().resolve(JAR_FILENAME).toFile())
    44     /**
    42                 .setMainClass(MAIN_CLASS)
    45      * Set fully qualified class name. E.g: foo.bar.Buzz.
    43                 .addSourceFile(Test.TEST_SRC_ROOT.resolve(
    46      */
    44                         Path.of("apps", "image", MAIN_CLASS + ".java")))
    47     private HelloApp setClassName(String v) {
    45                 .create();
    48         qualifiedClassName = v;
    46     };
    49         return this;
    47 
    50     }
    48     static void addTo(JPackageCommand cmd) {
    51 
    49         cmd.addAction(CREATE_JAR_ACTION);
    52     private HelloApp setModuleName(String v) {
    50         cmd.addArguments("--main-jar", JAR_FILENAME);
    53         moduleName = v;
    51         cmd.addArguments("--main-class", MAIN_CLASS);
    54         return this;
    52         if (PackageType.WINDOWS.contains(cmd.packageType())) {
    55     }
       
    56 
       
    57     private HelloApp setJarFileName(String v) {
       
    58         jarFileName = v;
       
    59         return this;
       
    60     }
       
    61 
       
    62     private HelloApp setModuleVersion(String v) {
       
    63         moduleVersion = v;
       
    64         return this;
       
    65     }
       
    66 
       
    67     private JarBuilder prepareSources(Path srcDir) throws IOException {
       
    68         final String className = qualifiedClassName.substring(
       
    69                 qualifiedClassName.lastIndexOf('.') + 1);
       
    70         final String packageName = packageName();
       
    71 
       
    72         final Path srcFile = srcDir.resolve(Path.of(String.join(
       
    73                 File.separator, qualifiedClassName.split("\\.")) + ".java"));
       
    74         Files.createDirectories(srcFile.getParent());
       
    75 
       
    76         JarBuilder jarBuilder = createJarBuilder().addSourceFile(srcFile);
       
    77         if (moduleName != null) {
       
    78             Path moduleInfoFile = srcDir.resolve("module-info.java");
       
    79             TKit.createTextFile(moduleInfoFile, List.of(
       
    80                     String.format("module %s {", moduleName),
       
    81                     String.format("    exports %s;", packageName),
       
    82                     "}"
       
    83             ));
       
    84             jarBuilder.addSourceFile(moduleInfoFile);
       
    85             if (moduleVersion != null) {
       
    86                 jarBuilder.setModuleVersion(moduleVersion);
       
    87             }
       
    88         }
       
    89 
       
    90         // Add package directive and replace class name in java source file.
       
    91         // Works with simple test Hello.java.
       
    92         // Don't expect too much from these regexps!
       
    93         Pattern classDeclaration = Pattern.compile("(^.*\\bclass\\s+)Hello(.*$)");
       
    94         Pattern importDirective = Pattern.compile(
       
    95                 "(?<=import (?:static )?+)[^;]+");
       
    96         AtomicBoolean classDeclared = new AtomicBoolean();
       
    97         AtomicBoolean packageInserted = new AtomicBoolean(packageName == null);
       
    98 
       
    99         var packageInserter = Functional.identityFunction((line) -> {
       
   100             packageInserted.setPlain(true);
       
   101             return String.format("package %s;%s%s", packageName,
       
   102                     System.lineSeparator(), line);
       
   103         });
       
   104 
       
   105         Files.write(srcFile, Files.readAllLines(HELLO_JAVA).stream().map(line -> {
       
   106             if (classDeclared.getPlain()) {
       
   107                 return line;
       
   108             }
       
   109 
       
   110             Matcher m;
       
   111             if (!packageInserted.getPlain() && importDirective.matcher(line).find()) {
       
   112                 line = packageInserter.apply(line);
       
   113             } else if ((m = classDeclaration.matcher(line)).find()) {
       
   114                 classDeclared.setPlain(true);
       
   115                 line = m.group(1) + className + m.group(2);
       
   116                 if (!packageInserted.getPlain()) {
       
   117                     line = packageInserter.apply(line);
       
   118                 }
       
   119             }
       
   120             return line;
       
   121         }).collect(Collectors.toList()));
       
   122 
       
   123         return jarBuilder;
       
   124     }
       
   125 
       
   126     private JarBuilder createJarBuilder() {
       
   127         return new JarBuilder().setMainClass(qualifiedClassName);
       
   128     }
       
   129 
       
   130     private void addTo(JPackageCommand cmd) {
       
   131         if (moduleName != null && packageName() == null) {
       
   132             throw new IllegalArgumentException(String.format(
       
   133                     "Module [%s] with default package", moduleName));
       
   134         }
       
   135 
       
   136         if (moduleName == null && CLASS_NAME.equals(qualifiedClassName)) {
       
   137             // Use Hello.java as is.
       
   138             cmd.addAction((self) -> {
       
   139                 File jarFile = self.inputDir().resolve(jarFileName).toFile();
       
   140                 createJarBuilder().setOutputJar(jarFile).addSourceFile(
       
   141                         HELLO_JAVA).create();
       
   142             });
       
   143         } else {
       
   144             cmd.addAction((self) -> {
       
   145                 final Path jarFile;
       
   146                 if (moduleName == null) {
       
   147                     jarFile = self.inputDir().resolve(jarFileName);
       
   148                 } else {
       
   149                     // `--module-path` option should be set by the moment
       
   150                     // when this action is being executed.
       
   151                     jarFile = Path.of(self.getArgumentValue("--module-path",
       
   152                             () -> self.inputDir().toString()), jarFileName);
       
   153                     Files.createDirectories(jarFile.getParent());
       
   154                 }
       
   155 
       
   156                 TKit.withTempDirectory("src",
       
   157                         workDir -> prepareSources(workDir).setOutputJar(
       
   158                                 jarFile.toFile()).create());
       
   159             });
       
   160         }
       
   161 
       
   162         if (moduleName == null) {
       
   163             cmd.addArguments("--main-jar", jarFileName);
       
   164             cmd.addArguments("--main-class", qualifiedClassName);
       
   165         } else {
       
   166             cmd.addArguments("--module-path", TKit.workDir().resolve(
       
   167                     "input-modules"));
       
   168             cmd.addArguments("--module", String.join("/", moduleName,
       
   169                     qualifiedClassName));
       
   170             // For modular app assume nothing will go in input directory and thus
       
   171             // nobody will create input directory, so remove corresponding option
       
   172             // from jpackage command line.
       
   173             cmd.removeArgumentWithValue("--input");
       
   174         }
       
   175         if (TKit.isWindows()) {
    53             cmd.addArguments("--win-console");
   176             cmd.addArguments("--win-console");
    54         }
   177         }
    55     }
   178     }
    56 
   179 
    57     static void verifyOutputFile(Path outputFile, String... args) {
   180     private String packageName() {
       
   181         int lastDotIdx = qualifiedClassName.lastIndexOf('.');
       
   182         if (lastDotIdx == -1) {
       
   183             return null;
       
   184         }
       
   185         return qualifiedClassName.substring(0, lastDotIdx);
       
   186     }
       
   187 
       
   188     /**
       
   189      * Configures Java application to be used with the given jpackage command.
       
   190      * Syntax of encoded Java application description is
       
   191      * [jar_file:][module_name/]qualified_class_name[@module_version].
       
   192      *
       
   193      * E.g.: duke.jar:com.other/com.other.foo.bar.Buz@3.7 encodes modular
       
   194      * application. Module name is `com.other`. Main class is
       
   195      * `com.other.foo.bar.Buz`. Module version is `3.7`. Application will be
       
   196      * compiled and packed in `duke.jar` jar file.
       
   197      *
       
   198      * @param cmd jpackage command to configure
       
   199      * @param javaAppDesc encoded Java application description
       
   200      */
       
   201     static void addTo(JPackageCommand cmd, String javaAppDesc) {
       
   202         HelloApp helloApp = new HelloApp();
       
   203         if (javaAppDesc != null) {
       
   204             String moduleNameAndOther = Functional.identity(() -> {
       
   205                 String[] components = javaAppDesc.split(":", 2);
       
   206                 if (components.length == 2) {
       
   207                     helloApp.setJarFileName(components[0]);
       
   208                 }
       
   209                 return components[components.length - 1];
       
   210             }).get();
       
   211 
       
   212             String classNameAndOther = Functional.identity(() -> {
       
   213                 String[] components = moduleNameAndOther.split("/", 2);
       
   214                 if (components.length == 2) {
       
   215                     helloApp.setModuleName(components[0]);
       
   216                 }
       
   217                 return components[components.length - 1];
       
   218             }).get();
       
   219 
       
   220             Functional.identity(() -> {
       
   221                 String[] components = classNameAndOther.split("@", 2);
       
   222                 helloApp.setClassName(components[0]);
       
   223                 if (components.length == 2) {
       
   224                     helloApp.setModuleVersion(components[1]);
       
   225                 }
       
   226             }).run();
       
   227         }
       
   228         helloApp.addTo(cmd);
       
   229     }
       
   230 
       
   231     static void verifyOutputFile(Path outputFile, List<String> args) {
    58         if (!outputFile.isAbsolute()) {
   232         if (!outputFile.isAbsolute()) {
    59             verifyOutputFile(outputFile.toAbsolutePath().normalize(), args);
   233             verifyOutputFile(outputFile.toAbsolutePath().normalize(), args);
    60             return;
   234             return;
    61         }
   235         }
    62 
   236 
    63         Test.assertFileExists(outputFile, true);
   237         TKit.assertFileExists(outputFile);
    64 
   238 
    65         List<String> output = null;
   239         List<String> contents = ThrowingSupplier.toSupplier(
    66         try {
   240                 () -> Files.readAllLines(outputFile)).get();
    67             output = Files.readAllLines(outputFile);
   241 
    68         } catch (IOException ex) {
   242         List<String> expected = new ArrayList<>(List.of(
    69             throw new RuntimeException(ex);
   243                 "jpackage test application",
    70         }
   244                 String.format("args.length: %d", args.size())
    71 
   245         ));
    72         final int expectedNumberOfLines = 2 + args.length;
   246         expected.addAll(args);
    73         Test.assertEquals(expectedNumberOfLines, output.size(), String.format(
   247 
    74                 "Check file [%s] contains %d text lines", outputFile,
   248         TKit.assertStringListEquals(expected, contents, String.format(
    75                 expectedNumberOfLines));
   249                 "Check contents of [%s] file", outputFile));
    76 
       
    77         Test.assertEquals("jpackage test application", output.get(0),
       
    78                 String.format(
       
    79                         "Check contents of the first text line in [%s] file",
       
    80                         outputFile));
       
    81 
       
    82         Test.assertEquals(String.format("args.length: %d", args.length),
       
    83                 output.get(1), String.format(
       
    84                 "Check contents of the second text line in [%s] file",
       
    85                 outputFile));
       
    86 
       
    87         Enumeration<String> argsEnum = Collections.enumeration(List.of(args));
       
    88         AtomicInteger counter = new AtomicInteger(2);
       
    89         output.stream().skip(2).sequential().forEach(line -> Test.assertEquals(
       
    90                 argsEnum.nextElement(), line, String.format(
       
    91                 "Check contents of %d text line in [%s] file",
       
    92                 counter.incrementAndGet(), outputFile)));
       
    93     }
   250     }
    94 
   251 
    95     public static void executeLauncherAndVerifyOutput(JPackageCommand cmd) {
   252     public static void executeLauncherAndVerifyOutput(JPackageCommand cmd) {
    96         final Path launcherPath;
   253         final Path launcherPath;
    97         if (cmd.packageType() == PackageType.IMAGE) {
   254         if (cmd.packageType() == PackageType.IMAGE) {
   113                 "--arguments"));
   270                 "--arguments"));
   114     }
   271     }
   115 
   272 
   116     public static void executeAndVerifyOutput(Path helloAppLauncher,
   273     public static void executeAndVerifyOutput(Path helloAppLauncher,
   117             String... defaultLauncherArgs) {
   274             String... defaultLauncherArgs) {
   118         File outputFile = Test.workDir().resolve(OUTPUT_FILENAME).toFile();
   275         executeAndVerifyOutput(helloAppLauncher, List.of(defaultLauncherArgs));
   119         try {
   276     }
   120             Files.deleteIfExists(outputFile.toPath());
   277 
   121         } catch (IOException ex) {
   278     public static void executeAndVerifyOutput(Path helloAppLauncher,
   122             throw new RuntimeException(ex);
   279             List<String> defaultLauncherArgs) {
   123         }
   280         // Output file will be created in the current directory.
       
   281         Path outputFile = Path.of(OUTPUT_FILENAME);
       
   282         ThrowingFunction.toFunction(Files::deleteIfExists).apply(outputFile);
   124         new Executor()
   283         new Executor()
   125                 .setDirectory(outputFile.getParentFile().toPath())
   284                 .setDirectory(outputFile.getParent())
   126                 .setExecutable(helloAppLauncher.toString())
   285                 .setExecutable(helloAppLauncher)
   127                 .execute()
   286                 .execute()
   128                 .assertExitCodeIsZero();
   287                 .assertExitCodeIsZero();
   129 
   288 
   130         verifyOutputFile(outputFile.toPath(), defaultLauncherArgs);
   289         verifyOutputFile(outputFile, defaultLauncherArgs);
   131     }
   290     }
   132 
   291 
   133     final static String OUTPUT_FILENAME = "appOutput.txt";
   292     final static String OUTPUT_FILENAME = "appOutput.txt";
       
   293 
       
   294     private String qualifiedClassName;
       
   295     private String moduleName;
       
   296     private String jarFileName;
       
   297     private String moduleVersion;
       
   298 
       
   299     private static final Path HELLO_JAVA = TKit.TEST_SRC_ROOT.resolve(
       
   300             "apps/image/Hello.java");
       
   301 
       
   302     private final static String CLASS_NAME = HELLO_JAVA.getFileName().toString().split(
       
   303             "\\.", 2)[0];
   134 }
   304 }