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 package jdk.jpackage.test; 24 25 import java.io.BufferedOutputStream; 26 import java.io.FileNotFoundException; 27 import java.io.FileOutputStream; 28 import java.io.IOException; 29 import java.io.PrintStream; 30 import java.nio.file.FileSystems; 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; 35 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.Collection; 40 import java.util.List; 41 import java.util.Map; 42 import java.util.Set; 43 import java.util.concurrent.TimeUnit; 44 import java.util.function.Consumer; 45 import java.util.function.Supplier; 46 import java.util.stream.Collectors; 47 import jdk.jpackage.internal.IOUtils; 48 49 final public class Test { 50 51 public static final Path TEST_SRC_ROOT = new Supplier<Path>() { 52 @Override 53 public Path get() { 54 Path root = Path.of(System.getProperty("test.src")); 55 56 for (int i = 0; i != 10; ++i) { 57 if (root.resolve("apps").toFile().isDirectory()) { 58 return root.toAbsolutePath(); 59 } 60 root = root.resolve(".."); 61 } 62 63 throw new RuntimeException("Failed to locate apps directory"); 64 } 65 }.get(); 66 67 private static class Instance implements AutoCloseable { 68 Instance(String args[]) { 69 assertCount = 0; 70 71 name = enclosingMainMethodClass().getSimpleName(); 72 extraLogStream = openLogStream(); 73 74 currentTest = this; 75 76 log(String.format("[ RUN ] %s", name)); 77 } 78 79 @Override 80 public void close() { 81 log(String.format("%s %s; checks=%d", 82 success ? "[ OK ]" : "[ FAILED ]", name, assertCount)); 83 84 if (extraLogStream != null) { 85 extraLogStream.close(); 86 } 87 } 88 89 void notifyAssert() { 90 assertCount++; 91 } 92 93 void notifySuccess() { 94 success = true; 95 } 96 97 private int assertCount; 98 private boolean success; 99 private final String name; 100 private final PrintStream extraLogStream; 101 } 102 103 public static void run(String args[], TestBody action) { 104 if (currentTest != null) { 105 throw new IllegalStateException( 106 "Unexpeced nested or concurrent Test.run() call"); 107 } 108 109 try (Instance instance = new Instance(args)) { 110 action.run(); 111 instance.notifySuccess(); 112 } catch (Exception ex) { 113 throw new RuntimeException(ex); 114 } finally { 115 currentTest = null; 116 } 117 } 118 119 public static interface TestBody { 120 public void run() throws Exception; 121 } 122 123 public static Path workDir() { 124 return Path.of("."); 125 } 126 127 static Path defaultInputDir() { 128 return workDir().resolve("input"); 129 } 130 131 static Path defaultOutputDir() { 132 return workDir().resolve("output"); 133 } 134 135 static Class enclosingMainMethodClass() { 136 StackTraceElement st[] = Thread.currentThread().getStackTrace(); 137 for (StackTraceElement ste : st) { 138 if ("main".equals(ste.getMethodName())) { 139 try { 140 return Class.forName(ste.getClassName()); 141 } catch (ClassNotFoundException ex) { 142 throw new RuntimeException(ex); 143 } 144 } 145 } 146 return null; 147 } 148 149 static boolean isWindows() { 150 return (OS.contains("win")); 151 } 152 153 static boolean isOSX() { 154 return (OS.contains("mac")); 155 } 156 157 static boolean isLinux() { 158 return ((OS.contains("nix") || OS.contains("nux"))); 159 } 160 161 static private void log(String v) { 162 System.out.println(v); 163 if (currentTest != null && currentTest.extraLogStream != null) { 164 currentTest.extraLogStream.println(v); 165 } 166 } 167 168 public static Class getTestClass () { 169 return enclosingMainMethodClass(); 170 } 171 172 public static void createPropertiesFile(Path propsFilename, 173 Collection<Map.Entry<String, String>> props) { 174 trace(String.format("Create [%s] properties file...", 175 propsFilename.toAbsolutePath().normalize())); 176 try { 177 Files.write(propsFilename, props.stream().peek(e -> trace( 178 String.format("%s=%s", e.getKey(), e.getValue()))).map( 179 e -> String.format("%s=%s", e.getKey(), e.getValue())).collect( 180 Collectors.toList())); 181 } catch (IOException ex) { 182 throw new RuntimeException(ex); 183 } 184 trace("Done"); 185 } 186 187 public static void createPropertiesFile(Path propsFilename, 188 Map.Entry<String, String>... props) { 189 createPropertiesFile(propsFilename, List.of(props)); 190 } 191 192 public static void createPropertiesFile(Path propsFilename, 193 Map<String, String> props) { 194 createPropertiesFile(propsFilename, props.entrySet()); 195 } 196 197 public static void trace(String v) { 198 if (TRACE) { 199 log("TRACE: " + v); 200 } 201 } 202 203 private static void traceAssert(String v) { 204 if (TRACE_ASSERTS) { 205 log("TRACE: " + v); 206 } 207 } 208 209 public static void error(String v) { 210 log("ERROR: " + v); 211 throw new AssertionError(v); 212 } 213 214 private static final String TEMP_FILE_PREFIX = null; 215 216 public static Path createTempDirectory() throws IOException { 217 return Files.createTempDirectory(workDir(), TEMP_FILE_PREFIX); 218 } 219 220 public static Path createTempFile(String suffix) throws IOException { 221 return Files.createTempFile(workDir(), TEMP_FILE_PREFIX, suffix); 222 } 223 224 public static void withTempFile(String suffix, Consumer<Path> action) { 225 Path tempFile = null; 226 boolean keepIt = true; 227 try { 228 tempFile = createTempFile(suffix); 229 action.accept(tempFile); 230 keepIt = false; 231 } catch (IOException ex) { 232 throw new RuntimeException(ex); 233 } finally { 234 if (tempFile != null && !keepIt) { 235 try { 236 Files.deleteIfExists(tempFile); 237 } catch (IOException ex) { 238 throw new RuntimeException(ex); 239 } 240 } 241 } 242 } 243 244 public static void withTempDirectory(Consumer<Path> action) { 245 Path tempDir = null; 246 boolean keepIt = true; 247 try { 248 tempDir = createTempDirectory(); 249 action.accept(tempDir); 250 keepIt = false; 251 } catch (IOException ex) { 252 throw new RuntimeException(ex); 253 } finally { 254 try { 255 if (tempDir != null && tempDir.toFile().isDirectory() && !keepIt) { 256 IOUtils.deleteRecursive(tempDir.toFile()); 257 } 258 } catch (IOException ex) { 259 throw new RuntimeException(ex); 260 } 261 } 262 } 263 264 public static void waitForFileCreated(Path fileToWaitFor, 265 long timeoutSeconds) throws IOException { 266 267 trace(String.format("Wait for file [%s] to be available", fileToWaitFor)); 268 269 WatchService ws = FileSystems.getDefault().newWatchService(); 270 271 Path watchDirectory = fileToWaitFor.toAbsolutePath().getParent(); 272 watchDirectory.register(ws, ENTRY_CREATE, ENTRY_MODIFY); 273 274 long waitUntil = System.currentTimeMillis() + timeoutSeconds * 1000; 275 for (;;) { 276 long timeout = waitUntil - System.currentTimeMillis(); 277 assertTrue(timeout > 0, String.format( 278 "Check timeout value %d is positive", timeout)); 279 280 WatchKey key = null; 281 try { 282 key = ws.poll(timeout, TimeUnit.MILLISECONDS); 283 } catch (InterruptedException ex) { 284 throw new RuntimeException(ex); 285 } 286 287 if (key == null) { 288 if (fileToWaitFor.toFile().exists()) { 289 trace(String.format( 290 "File [%s] is available after poll timeout expired", 291 fileToWaitFor)); 292 return; 293 } 294 assertUnexpected(String.format("Timeout expired", timeout)); 295 } 296 297 for (WatchEvent<?> event : key.pollEvents()) { 298 if (event.kind() == StandardWatchEventKinds.OVERFLOW) { 299 continue; 300 } 301 Path contextPath = (Path) event.context(); 302 if (Files.isSameFile(watchDirectory.resolve(contextPath), 303 fileToWaitFor)) { 304 trace(String.format("File [%s] is available", fileToWaitFor)); 305 return; 306 } 307 } 308 309 if (!key.reset()) { 310 assertUnexpected("Watch key invalidated"); 311 } 312 } 313 } 314 315 private static String concatMessages(String msg, String msg2) { 316 if (msg2 != null && !msg2.isBlank()) { 317 return msg + ": " + msg2; 318 } 319 return msg; 320 } 321 322 public static void assertEquals(long expected, long actual, String msg) { 323 currentTest.notifyAssert(); 324 if (expected != actual) { 325 error(concatMessages(String.format( 326 "Expected [%d]. Actual [%d]", expected, actual), 327 msg)); 328 } 329 330 traceAssert(String.format("assertEquals(%d): %s", expected, msg)); 331 } 332 333 public static void assertNotEquals(long expected, long actual, String msg) { 334 currentTest.notifyAssert(); 335 if (expected == actual) { 336 error(concatMessages(String.format("Unexpected [%d] value", actual), 337 msg)); 338 } 339 340 traceAssert(String.format("assertNotEquals(%d, %d): %s", expected, 341 actual, msg)); 342 } 343 344 public static void assertEquals(String expected, String actual, String msg) { 345 currentTest.notifyAssert(); 346 if ((actual != null && !actual.equals(expected)) 347 || (expected != null && !expected.equals(actual))) { 348 error(concatMessages(String.format( 349 "Expected [%s]. Actual [%s]", expected, actual), 350 msg)); 351 } 352 353 traceAssert(String.format("assertEquals(%s): %s", expected, msg)); 354 } 355 356 public static void assertNotEquals(String expected, String actual, String msg) { 357 currentTest.notifyAssert(); 358 if ((actual != null && !actual.equals(expected)) 359 || (expected != null && !expected.equals(actual))) { 360 361 traceAssert(String.format("assertNotEquals(%s, %s): %s", expected, 362 actual, msg)); 363 return; 364 } 365 366 error(concatMessages(String.format("Unexpected [%s] value", actual), msg)); 367 } 368 369 public static void assertNull(Object value, String msg) { 370 currentTest.notifyAssert(); 371 if (value != null) { 372 error(concatMessages(String.format("Unexpected not null value [%s]", 373 value), msg)); 374 } 375 376 traceAssert(String.format("assertNull(): %s", msg)); 377 } 378 379 public static void assertNotNull(Object value, String msg) { 380 currentTest.notifyAssert(); 381 if (value == null) { 382 error(concatMessages("Unexpected null value", msg)); 383 } 384 385 traceAssert(String.format("assertNotNull(%s): %s", value, msg)); 386 } 387 388 public static void assertTrue(boolean actual, String msg) { 389 currentTest.notifyAssert(); 390 if (!actual) { 391 error(concatMessages("Unexpected FALSE", msg)); 392 } 393 394 traceAssert(String.format("assertTrue(): %s", msg)); 395 } 396 397 public static void assertFalse(boolean actual, String msg) { 398 currentTest.notifyAssert(); 399 if (actual) { 400 error(concatMessages("Unexpected TRUE", msg)); 401 } 402 403 traceAssert(String.format("assertFalse(): %s", msg)); 404 } 405 406 public static void assertPathExists(Path path, boolean exists) { 407 if (exists) { 408 assertTrue(path.toFile().exists(), String.format( 409 "Check [%s] path exists", path)); 410 } else { 411 assertFalse(path.toFile().exists(), String.format( 412 "Check [%s] path doesn't exist", path)); 413 } 414 } 415 416 public static void assertDirectoryExists(Path path, boolean exists) { 417 assertPathExists(path, exists); 418 if (exists) { 419 assertTrue(path.toFile().isDirectory(), String.format( 420 "Check [%s] is a directory", path)); 421 } 422 } 423 424 public static void assertFileExists(Path path, boolean exists) { 425 assertPathExists(path, exists); 426 if (exists) { 427 assertTrue(path.toFile().isFile(), String.format( 428 "Check [%s] is a file", path)); 429 } 430 } 431 432 public static void assertExecutableFileExists(Path path, boolean exists) { 433 assertFileExists(path, exists); 434 if (exists) { 435 assertTrue(path.toFile().canExecute(), String.format( 436 "Check [%s] file is executable", path)); 437 } 438 } 439 440 public static void assertUnexpected(String msg) { 441 currentTest.notifyAssert(); 442 error(concatMessages("Unexpected", msg)); 443 } 444 445 private static PrintStream openLogStream() { 446 if (LOG_FILE == null) { 447 return null; 448 } 449 450 try { 451 return new PrintStream(new FileOutputStream(LOG_FILE.toFile(), true)); 452 } catch (FileNotFoundException ex) { 453 throw new RuntimeException(ex); 454 } 455 } 456 457 private static Instance currentTest; 458 459 private static final boolean TRACE; 460 private static final boolean TRACE_ASSERTS; 461 462 static final boolean VERBOSE_JPACKAGE; 463 464 static String getConfigProperty(String propertyName) { 465 return System.getProperty(getConfigPropertyName(propertyName)); 466 } 467 468 static String getConfigPropertyName(String propertyName) { 469 return "jpackage.test." + propertyName; 470 } 471 472 static final Path LOG_FILE = new Supplier<Path>() { 473 @Override 474 public Path get() { 475 String val = getConfigProperty("logfile"); 476 if (val == null) { 477 return null; 478 } 479 return Path.of(val); 480 } 481 }.get(); 482 483 static { 484 String val = getConfigProperty("suppress-logging"); 485 if (val == null) { 486 TRACE = true; 487 TRACE_ASSERTS = true; 488 VERBOSE_JPACKAGE = true; 489 } else if ("all".equals(val.toLowerCase())) { 490 TRACE = false; 491 TRACE_ASSERTS = false; 492 VERBOSE_JPACKAGE = false; 493 } else { 494 Set<String> logOptions = Set.of(val.toLowerCase().split(",")); 495 TRACE = !(logOptions.contains("trace") || logOptions.contains("t")); 496 TRACE_ASSERTS = !(logOptions.contains("assert") || logOptions.contains( 497 "a")); 498 VERBOSE_JPACKAGE = !(logOptions.contains("jpackage") || logOptions.contains( 499 "jp")); 500 } 501 } 502 503 private static final String OS = System.getProperty("os.name").toLowerCase(); 504 }