test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java
branchJDK-8200758-branch
changeset 58648 3bf53ffa9ae7
parent 58464 d82489644b15
child 58696 61c44899b4eb
equal deleted inserted replaced
58647:2c43b89b1679 58648:3bf53ffa9ae7
    20  * or visit www.oracle.com if you need additional information or have any
    20  * or visit www.oracle.com if you need additional information or have any
    21  * questions.
    21  * questions.
    22  */
    22  */
    23 package jdk.jpackage.test;
    23 package jdk.jpackage.test;
    24 
    24 
    25 import java.io.File;
       
    26 import java.io.FileOutputStream;
    25 import java.io.FileOutputStream;
    27 import java.io.IOException;
    26 import java.io.IOException;
    28 import java.io.PrintStream;
    27 import java.io.PrintStream;
    29 import java.lang.reflect.InvocationTargetException;
    28 import java.lang.reflect.InvocationTargetException;
    30 import java.nio.file.FileSystems;
    29 import java.nio.file.*;
    31 import java.nio.file.Files;
       
    32 import java.nio.file.Path;
       
    33 import java.nio.file.StandardWatchEventKinds;
       
    34 import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
    30 import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
    35 import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
    31 import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
    36 import java.nio.file.WatchEvent;
       
    37 import java.nio.file.WatchKey;
       
    38 import java.nio.file.WatchService;
       
    39 import java.util.*;
    32 import java.util.*;
    40 import java.util.concurrent.TimeUnit;
    33 import java.util.concurrent.TimeUnit;
    41 import java.util.concurrent.atomic.AtomicInteger;
    34 import java.util.concurrent.atomic.AtomicInteger;
       
    35 import java.util.function.BiPredicate;
       
    36 import java.util.function.Consumer;
       
    37 import java.util.function.Predicate;
       
    38 import java.util.function.Supplier;
    42 import java.util.stream.Collectors;
    39 import java.util.stream.Collectors;
    43 import java.util.stream.Stream;
    40 import java.util.stream.Stream;
    44 import jdk.jpackage.test.Functional.ExceptionBox;
    41 import jdk.jpackage.test.Functional.ExceptionBox;
    45 import jdk.jpackage.test.Functional.ThrowingConsumer;
    42 import jdk.jpackage.test.Functional.ThrowingConsumer;
    46 import jdk.jpackage.test.Functional.ThrowingFunction;
       
    47 import jdk.jpackage.test.Functional.ThrowingRunnable;
    43 import jdk.jpackage.test.Functional.ThrowingRunnable;
    48 import jdk.jpackage.test.Functional.ThrowingSupplier;
    44 import jdk.jpackage.test.Functional.ThrowingSupplier;
    49 
    45 
    50 final public class TKit {
    46 final public class TKit {
    51 
    47 
    74         if (!test.passed()) {
    70         if (!test.passed()) {
    75             throw new RuntimeException();
    71             throw new RuntimeException();
    76         }
    72         }
    77     }
    73     }
    78 
    74 
       
    75     static void withExtraLogStream(ThrowingRunnable action) {
       
    76         if (extraLogStream != null) {
       
    77             ThrowingRunnable.toRunnable(action).run();
       
    78         } else {
       
    79             try (PrintStream logStream = openLogStream()) {
       
    80                 extraLogStream = logStream;
       
    81                 ThrowingRunnable.toRunnable(action).run();
       
    82             } finally {
       
    83                 extraLogStream = null;
       
    84             }
       
    85         }
       
    86     }
       
    87 
    79     static void runTests(List<TestInstance> tests) {
    88     static void runTests(List<TestInstance> tests) {
    80         if (currentTest != null) {
    89         if (currentTest != null) {
    81             throw new IllegalStateException(
    90             throw new IllegalStateException(
    82                     "Unexpeced nested or concurrent Test.run() call");
    91                     "Unexpeced nested or concurrent Test.run() call");
    83         }
    92         }
    84 
    93 
    85         try (PrintStream logStream = openLogStream()) {
    94         withExtraLogStream(() -> {
    86             extraLogStream = logStream;
       
    87             tests.stream().forEach(test -> {
    95             tests.stream().forEach(test -> {
    88                 currentTest = test;
    96                 currentTest = test;
    89                 try {
    97                 try {
    90                     ignoreExceptions(test).run();
    98                     ignoreExceptions(test).run();
    91                 } finally {
    99                 } finally {
    93                     if (extraLogStream != null) {
   101                     if (extraLogStream != null) {
    94                         extraLogStream.flush();
   102                         extraLogStream.flush();
    95                     }
   103                     }
    96                 }
   104                 }
    97             });
   105             });
    98         } finally {
   106         });
    99             extraLogStream = null;
       
   100         }
       
   101     }
   107     }
   102 
   108 
   103     static Runnable ignoreExceptions(ThrowingRunnable action) {
   109     static Runnable ignoreExceptions(ThrowingRunnable action) {
   104         return () -> {
   110         return () -> {
   105             try {
   111             try {
   107                     action.run();
   113                     action.run();
   108                 } catch (Throwable ex) {
   114                 } catch (Throwable ex) {
   109                     unbox(ex);
   115                     unbox(ex);
   110                 }
   116                 }
   111             } catch (Throwable throwable) {
   117             } catch (Throwable throwable) {
   112                 if (extraLogStream != null) {
   118                 printStackTrace(throwable);
   113                     throwable.printStackTrace(extraLogStream);
       
   114                 }
       
   115                 throwable.printStackTrace();
       
   116             }
   119             }
   117         };
   120         };
   118     }
   121     }
   119 
   122 
   120     static void unbox(Throwable throwable) throws Throwable {
   123     static void unbox(Throwable throwable) throws Throwable {
   124             unbox(ex.getCause());
   127             unbox(ex.getCause());
   125         }
   128         }
   126     }
   129     }
   127 
   130 
   128     public static Path workDir() {
   131     public static Path workDir() {
   129         Path result = Path.of(".");
   132         return currentTest.workDir();
   130         String testFunctionName = currentTest.functionName();
       
   131         if (testFunctionName != null) {
       
   132             result = result.resolve(testFunctionName);
       
   133         }
       
   134         return result;
       
   135     }
   133     }
   136 
   134 
   137     static Path defaultInputDir() {
   135     static Path defaultInputDir() {
   138         return workDir().resolve("input");
   136         return workDir().resolve("input");
   139     }
   137     }
   172             extraLogStream.println(v);
   170             extraLogStream.println(v);
   173         }
   171         }
   174     }
   172     }
   175 
   173 
   176     public static void createTextFile(Path propsFilename, Collection<String> lines) {
   174     public static void createTextFile(Path propsFilename, Collection<String> lines) {
       
   175         createTextFile(propsFilename, lines.stream());
       
   176     }
       
   177 
       
   178     public static void createTextFile(Path propsFilename, Stream<String> lines) {
   177         trace(String.format("Create [%s] text file...",
   179         trace(String.format("Create [%s] text file...",
   178                 propsFilename.toAbsolutePath().normalize()));
   180                 propsFilename.toAbsolutePath().normalize()));
   179         ThrowingRunnable.toRunnable(() -> Files.write(propsFilename,
   181         ThrowingRunnable.toRunnable(() -> Files.write(propsFilename,
   180                 lines.stream().peek(TKit::trace).collect(Collectors.toList()))).run();
   182                 lines.peek(TKit::trace).collect(Collectors.toList()))).run();
   181         trace("Done");
   183         trace("Done");
   182     }
   184     }
   183 
   185 
   184     public static void createPropertiesFile(Path propsFilename,
   186     public static void createPropertiesFile(Path propsFilename,
   185             Collection<Map.Entry<String, String>> props) {
   187             Collection<Map.Entry<String, String>> props) {
   261             return Files.createTempFile(workDir(), TEMP_FILE_PREFIX, suffix);
   263             return Files.createTempFile(workDir(), TEMP_FILE_PREFIX, suffix);
   262         }
   264         }
   263         return Files.createFile(createUniqueFileName(role));
   265         return Files.createFile(createUniqueFileName(role));
   264     }
   266     }
   265 
   267 
   266     public static void withTempFile(String role, String suffix,
   268     public static Path withTempFile(String role, String suffix,
   267             ThrowingConsumer<Path> action) {
   269             ThrowingConsumer<Path> action) {
   268         final Path tempFile = ThrowingSupplier.toSupplier(() -> createTempFile(
   270         final Path tempFile = ThrowingSupplier.toSupplier(() -> createTempFile(
   269                 role, suffix)).get();
   271                 role, suffix)).get();
   270         boolean keepIt = true;
   272         boolean keepIt = true;
   271         try {
   273         try {
   272             ThrowingConsumer.toConsumer(action).accept(tempFile);
   274             ThrowingConsumer.toConsumer(action).accept(tempFile);
   273             keepIt = false;
   275             keepIt = false;
       
   276             return tempFile;
   274         } finally {
   277         } finally {
   275             if (tempFile != null && !keepIt) {
   278             if (tempFile != null && !keepIt) {
   276                 ThrowingRunnable.toRunnable(() -> Files.deleteIfExists(tempFile)).run();
   279                 ThrowingRunnable.toRunnable(() -> Files.deleteIfExists(tempFile)).run();
   277             }
   280             }
   278         }
   281         }
   279     }
   282     }
   280 
   283 
   281     public static void withTempDirectory(String role,
   284     public static Path withTempDirectory(String role,
   282             ThrowingConsumer<Path> action) {
   285             ThrowingConsumer<Path> action) {
   283         final Path tempDir = ThrowingSupplier.toSupplier(
   286         final Path tempDir = ThrowingSupplier.toSupplier(
   284                 () -> createTempDirectory(role)).get();
   287                 () -> createTempDirectory(role)).get();
   285         boolean keepIt = true;
   288         boolean keepIt = true;
   286         try {
   289         try {
   287             ThrowingConsumer.toConsumer(action).accept(tempDir);
   290             ThrowingConsumer.toConsumer(action).accept(tempDir);
   288             keepIt = false;
   291             keepIt = false;
       
   292             return tempDir;
   289         } finally {
   293         } finally {
   290             if (tempDir != null && tempDir.toFile().isDirectory() && !keepIt) {
   294             if (tempDir != null && tempDir.toFile().isDirectory() && !keepIt) {
   291                 deleteDirectoryRecursive(tempDir, "");
   295                 deleteDirectoryRecursive(tempDir, "");
   292             }
   296             }
   293         }
   297         }
       
   298     }
       
   299 
       
   300     private static class DirectoryCleaner implements Consumer<Path> {
       
   301         DirectoryCleaner traceMessage(String v) {
       
   302             msg = v;
       
   303             return this;
       
   304         }
       
   305 
       
   306         DirectoryCleaner contentsOnly(boolean v) {
       
   307             contentsOnly = v;
       
   308             return this;
       
   309         }
       
   310 
       
   311         @Override
       
   312         public void accept(Path root) {
       
   313             if (msg == null) {
       
   314                 if (contentsOnly) {
       
   315                     msg = String.format("Cleaning [%s] directory recursively",
       
   316                             root);
       
   317                 } else {
       
   318                     msg = String.format("Deleting [%s] directory recursively",
       
   319                             root);
       
   320                 }
       
   321             }
       
   322 
       
   323             if (!msg.isEmpty()) {
       
   324                 trace(msg);
       
   325             }
       
   326 
       
   327             List<Throwable> errors = new ArrayList<>();
       
   328             try {
       
   329                 final List<Path> paths;
       
   330                 if (contentsOnly) {
       
   331                     try (var pathStream = Files.walk(root, 0)) {
       
   332                         paths = pathStream.collect(Collectors.toList());
       
   333                     }
       
   334                 } else {
       
   335                     paths = List.of(root);
       
   336                 }
       
   337 
       
   338                 for (var path : paths) {
       
   339                     try (var pathStream = Files.walk(path)) {
       
   340                         pathStream
       
   341                         .sorted(Comparator.reverseOrder())
       
   342                         .sequential()
       
   343                         .forEachOrdered(file -> {
       
   344                             try {
       
   345                                 if (isWindows()) {
       
   346                                     Files.setAttribute(file, "dos:readonly", false);
       
   347                                 }
       
   348                                 Files.delete(file);
       
   349                             } catch (IOException ex) {
       
   350                                 errors.add(ex);
       
   351                             }
       
   352                         });
       
   353                     }
       
   354                 }
       
   355 
       
   356             } catch (IOException ex) {
       
   357                 errors.add(ex);
       
   358             }
       
   359             errors.forEach(error -> trace(error.toString()));
       
   360         }
       
   361 
       
   362         private String msg;
       
   363         private boolean contentsOnly;
   294     }
   364     }
   295 
   365 
   296     /**
   366     /**
   297      * Deletes contents of the given directory recursively. Shortcut for
   367      * Deletes contents of the given directory recursively. Shortcut for
   298      * <code>deleteDirectoryContentsRecursive(path, null)</code>
   368      * <code>deleteDirectoryContentsRecursive(path, null)</code>
   311      * @param msg log message. If null, the default log message is used. If
   381      * @param msg log message. If null, the default log message is used. If
   312      * empty string, no log message will be saved.
   382      * empty string, no log message will be saved.
   313      */
   383      */
   314     public static void deleteDirectoryContentsRecursive(Path path, String msg) {
   384     public static void deleteDirectoryContentsRecursive(Path path, String msg) {
   315         if (path.toFile().isDirectory()) {
   385         if (path.toFile().isDirectory()) {
   316             if (msg == null) {
   386             new DirectoryCleaner().contentsOnly(true).traceMessage(msg).accept(
   317                 msg = String.format("Cleaning [%s] directory recursively", path);
   387                     path);
   318             }
       
   319 
       
   320             if (!msg.isEmpty()) {
       
   321                 TKit.trace(msg);
       
   322             }
       
   323 
       
   324             // Walk all children of `path` in sorted order to hit files first
       
   325             // and directories last and delete each item.
       
   326             ThrowingRunnable.toRunnable(() -> Stream.of(
       
   327                     path.toFile().listFiles()).map(File::toPath).map(
       
   328                     ThrowingFunction.toFunction(Files::walk)).flatMap(x -> x).sorted(
       
   329                     Comparator.reverseOrder()).map(Path::toFile).forEach(
       
   330                     File::delete)).run();
       
   331         }
   388         }
   332     }
   389     }
   333 
   390 
   334     /**
   391     /**
   335      * Deletes the given directory recursively. Shortcut for
   392      * Deletes the given directory recursively. Shortcut for
   349      * @param msg log message. If null, the default log message is used. If
   406      * @param msg log message. If null, the default log message is used. If
   350      * empty string, no log message will be saved.
   407      * empty string, no log message will be saved.
   351      */
   408      */
   352     public static void deleteDirectoryRecursive(Path path, String msg) {
   409     public static void deleteDirectoryRecursive(Path path, String msg) {
   353         if (path.toFile().isDirectory()) {
   410         if (path.toFile().isDirectory()) {
   354             if (msg == null) {
   411             new DirectoryCleaner().traceMessage(msg).accept(path);
   355                 msg = String.format("Deleting [%s] directory recursively", path);
       
   356             }
       
   357             deleteDirectoryContentsRecursive(path, msg);
       
   358             ThrowingConsumer.toConsumer(Files::delete).accept(path);
       
   359         }
   412         }
   360     }
   413     }
   361 
   414 
   362     public static RuntimeException throwUnknownPlatformError() {
   415     public static RuntimeException throwUnknownPlatformError() {
   363         if (isWindows() || isLinux() || isOSX()) {
   416         if (isWindows() || isLinux() || isOSX()) {
   373                 () -> (RuntimeException) Class.forName("jtreg.SkippedException").getConstructor(
   426                 () -> (RuntimeException) Class.forName("jtreg.SkippedException").getConstructor(
   374                         String.class).newInstance(reason)).get();
   427                         String.class).newInstance(reason)).get();
   375 
   428 
   376         currentTest.notifySkipped(ex);
   429         currentTest.notifySkipped(ex);
   377         throw ex;
   430         throw ex;
       
   431     }
       
   432 
       
   433     public static Path createRelativePathCopy(final Path file) {
       
   434         Path fileCopy = workDir().resolve(file.getFileName()).toAbsolutePath().normalize();
       
   435 
       
   436         ThrowingRunnable.toRunnable(() -> Files.copy(file, fileCopy,
       
   437                 StandardCopyOption.REPLACE_EXISTING)).run();
       
   438 
       
   439         final Path basePath = Path.of(".").toAbsolutePath().normalize();
       
   440         try {
       
   441             return basePath.relativize(fileCopy);
       
   442         } catch (IllegalArgumentException ex) {
       
   443             // May happen on Windows: java.lang.IllegalArgumentException: 'other' has different root
       
   444             trace(String.format("Failed to relativize [%s] at [%s]", fileCopy,
       
   445                     basePath));
       
   446             printStackTrace(ex);
       
   447         }
       
   448         return file;
   378     }
   449     }
   379 
   450 
   380     static void waitForFileCreated(Path fileToWaitFor,
   451     static void waitForFileCreated(Path fileToWaitFor,
   381             long timeoutSeconds) throws IOException {
   452             long timeoutSeconds) throws IOException {
   382 
   453 
   421                 assertUnexpected("Watch key invalidated");
   492                 assertUnexpected("Watch key invalidated");
   422             }
   493             }
   423         }
   494         }
   424     }
   495     }
   425 
   496 
       
   497     static void printStackTrace(Throwable throwable) {
       
   498         if (extraLogStream != null) {
       
   499             throwable.printStackTrace(extraLogStream);
       
   500         }
       
   501         throwable.printStackTrace();
       
   502     }
       
   503 
   426     private static String concatMessages(String msg, String msg2) {
   504     private static String concatMessages(String msg, String msg2) {
   427         if (msg2 != null && !msg2.isBlank()) {
   505         if (msg2 != null && !msg2.isBlank()) {
   428             return msg + ": " + msg2;
   506             return msg + ": " + msg2;
   429         }
   507         }
   430         return msg;
   508         return msg;
   600                     "Actual list is longer than expected by %d elements",
   678                     "Actual list is longer than expected by %d elements",
   601                     expected.size() - actual.size()), msg));
   679                     expected.size() - actual.size()), msg));
   602         }
   680         }
   603     }
   681     }
   604 
   682 
       
   683     public final static class TextStreamAsserter {
       
   684         TextStreamAsserter(String value) {
       
   685             this.value = value;
       
   686             predicate(String::contains);
       
   687         }
       
   688 
       
   689         public TextStreamAsserter label(String v) {
       
   690             label = v;
       
   691             return this;
       
   692         }
       
   693 
       
   694         public TextStreamAsserter predicate(BiPredicate<String, String> v) {
       
   695             predicate = v;
       
   696             return this;
       
   697         }
       
   698 
       
   699         public TextStreamAsserter negate() {
       
   700             negate = true;
       
   701             return this;
       
   702         }
       
   703 
       
   704         public TextStreamAsserter orElseThrow(RuntimeException v) {
       
   705             return orElseThrow(() -> v);
       
   706         }
       
   707 
       
   708         public TextStreamAsserter orElseThrow(Supplier<RuntimeException> v) {
       
   709             createException = v;
       
   710             return this;
       
   711         }
       
   712 
       
   713         public void apply(Stream<String> lines) {
       
   714             String matchedStr = lines.filter(line -> predicate.test(line, value)).findFirst().orElse(
       
   715                     null);
       
   716             String labelStr = Optional.ofNullable(label).orElse("output");
       
   717             if (negate) {
       
   718                 String msg = String.format(
       
   719                         "Check %s doesn't contain [%s] string", labelStr, value);
       
   720                 if (createException == null) {
       
   721                     assertNull(matchedStr, msg);
       
   722                 } else {
       
   723                     trace(msg);
       
   724                     if (matchedStr != null) {
       
   725                         throw createException.get();
       
   726                     }
       
   727                 }
       
   728             } else {
       
   729                 String msg = String.format("Check %s contains [%s] string",
       
   730                         labelStr, value);
       
   731                 if (createException == null) {
       
   732                     assertNotNull(matchedStr, msg);
       
   733                 } else {
       
   734                     trace(msg);
       
   735                     if (matchedStr == null) {
       
   736                         throw createException.get();
       
   737                     }
       
   738                 }
       
   739             }
       
   740         }
       
   741 
       
   742         private BiPredicate<String, String> predicate;
       
   743         private String label;
       
   744         private boolean negate;
       
   745         private Supplier<RuntimeException> createException;
       
   746         final private String value;
       
   747     }
       
   748 
       
   749     public static TextStreamAsserter assertTextStream(String what) {
       
   750         return new TextStreamAsserter(what);
       
   751     }
       
   752 
   605     private static PrintStream openLogStream() {
   753     private static PrintStream openLogStream() {
   606         if (LOG_FILE == null) {
   754         if (LOG_FILE == null) {
   607             return null;
   755             return null;
   608         }
   756         }
   609 
   757 
   624         return System.getProperty(getConfigPropertyName(propertyName));
   772         return System.getProperty(getConfigPropertyName(propertyName));
   625     }
   773     }
   626 
   774 
   627     static String getConfigPropertyName(String propertyName) {
   775     static String getConfigPropertyName(String propertyName) {
   628         return "jpackage.test." + propertyName;
   776         return "jpackage.test." + propertyName;
       
   777     }
       
   778 
       
   779     static Set<String> tokenizeConfigProperty(String propertyName) {
       
   780         final String val = TKit.getConfigProperty(propertyName);
       
   781         if (val == null) {
       
   782             return null;
       
   783         }
       
   784         return Stream.of(val.toLowerCase().split(",")).map(String::strip).filter(
       
   785                 Predicate.not(String::isEmpty)).collect(Collectors.toSet());
   629     }
   786     }
   630 
   787 
   631     static final Path LOG_FILE = Functional.identity(() -> {
   788     static final Path LOG_FILE = Functional.identity(() -> {
   632         String val = getConfigProperty("logfile");
   789         String val = getConfigProperty("logfile");
   633         if (val == null) {
   790         if (val == null) {
   635         }
   792         }
   636         return Path.of(val);
   793         return Path.of(val);
   637     }).get();
   794     }).get();
   638 
   795 
   639     static {
   796     static {
   640         String val = getConfigProperty("suppress-logging");
   797         Set<String> logOptions = tokenizeConfigProperty("suppress-logging");
   641         if (val == null) {
   798         if (logOptions == null) {
   642             TRACE = true;
   799             TRACE = true;
   643             TRACE_ASSERTS = true;
   800             TRACE_ASSERTS = true;
   644             VERBOSE_JPACKAGE = true;
   801             VERBOSE_JPACKAGE = true;
   645             VERBOSE_TEST_SETUP = true;
   802             VERBOSE_TEST_SETUP = true;
   646         } else if ("all".equals(val.toLowerCase())) {
   803         } else if (logOptions.contains("all")) {
   647             TRACE = false;
   804             TRACE = false;
   648             TRACE_ASSERTS = false;
   805             TRACE_ASSERTS = false;
   649             VERBOSE_JPACKAGE = false;
   806             VERBOSE_JPACKAGE = false;
   650             VERBOSE_TEST_SETUP = false;
   807             VERBOSE_TEST_SETUP = false;
   651         } else {
   808         } else {
   652             Set<String> logOptions = Set.of(val.toLowerCase().split(","));
   809             Predicate<Set<String>> isNonOf = options -> {
   653             TRACE = !(logOptions.contains("trace") || logOptions.contains("t"));
   810                 return Collections.disjoint(logOptions, options);
   654             TRACE_ASSERTS = !(logOptions.contains("assert") || logOptions.contains(
   811             };
   655                     "a"));
   812 
   656             VERBOSE_JPACKAGE = !(logOptions.contains("jpackage") || logOptions.contains(
   813             TRACE = isNonOf.test(Set.of("trace", "t"));
   657                     "jp"));
   814             TRACE_ASSERTS = isNonOf.test(Set.of("assert", "a"));
   658             VERBOSE_TEST_SETUP = !logOptions.contains("init");
   815             VERBOSE_JPACKAGE = isNonOf.test(Set.of("jpackage", "jp"));
       
   816             VERBOSE_TEST_SETUP = isNonOf.test(Set.of("init", "i"));
   659         }
   817         }
   660     }
   818     }
   661 
   819 
   662     private static final String OS = System.getProperty("os.name").toLowerCase();
   820     private static final String OS = System.getProperty("os.name").toLowerCase();
   663 }
   821 }