1 /* 2 * Copyright (c) 2008, 2014, 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 import java.io.OutputStream; 25 import java.io.InputStream; 26 import java.lang.annotation.ElementType; 27 import java.lang.annotation.Retention; 28 import java.lang.annotation.RetentionPolicy; 29 import java.lang.annotation.Target; 30 import java.lang.reflect.Method; 31 import java.util.regex.Pattern; 32 import java.io.StringWriter; 33 import java.io.PrintWriter; 34 import java.util.Set; 35 import java.io.BufferedReader; 36 import java.io.File; 37 import java.io.FileFilter; 38 import java.io.FileNotFoundException; 39 import java.io.FileOutputStream; 40 import java.io.IOException; 41 import java.io.InputStreamReader; 42 import java.io.PrintStream; 43 import java.nio.charset.Charset; 44 import java.nio.file.attribute.BasicFileAttributes; 45 import java.nio.file.Files; 46 import java.nio.file.FileVisitResult; 47 import java.nio.file.SimpleFileVisitor; 48 import java.nio.file.Path; 49 import java.util.ArrayList; 50 import java.util.List; 51 import java.util.Locale; 52 import java.util.Map; 53 import java.util.Arrays; 54 import javax.tools.JavaCompiler; 55 import javax.tools.ToolProvider; 56 57 import static java.nio.file.StandardCopyOption.*; 58 import static java.nio.file.StandardOpenOption.*; 59 60 /** 61 * This class provides some common utilities for the launcher tests. 62 */ 63 public class TestHelper { 64 // commonly used jtreg constants 65 static final File TEST_CLASSES_DIR; 66 static final File TEST_SOURCES_DIR; 67 68 static final String JAVAHOME = System.getProperty("java.home"); 69 static final String JAVA_BIN; 70 static final String JAVA_LIB; 71 static final String javaCmd; 72 static final String javawCmd; 73 static final String javacCmd; 74 static final String jarCmd; 75 static final boolean haveServerVM; 76 static final boolean haveClientVM; 77 78 static final JavaCompiler compiler; 79 80 static final boolean debug = Boolean.getBoolean("TestHelper.Debug"); 81 static final boolean isWindows = 82 System.getProperty("os.name", "unknown").startsWith("Windows"); 83 static final boolean isMacOSX = 84 System.getProperty("os.name", "unknown").contains("OS X"); 85 static final boolean is64Bit = 86 System.getProperty("sun.arch.data.model").equals("64"); 87 static final boolean is32Bit = 88 System.getProperty("sun.arch.data.model").equals("32"); 89 static final boolean isSolaris = 90 System.getProperty("os.name", "unknown").startsWith("SunOS"); 91 static final boolean isLinux = 92 System.getProperty("os.name", "unknown").startsWith("Linux"); 93 static final boolean isAIX = 94 System.getProperty("os.name", "unknown").startsWith("AIX"); 95 static final String LIBJVM = isWindows 96 ? "jvm.dll" 97 : "libjvm" + (isMacOSX ? ".dylib" : ".so"); 98 99 static final boolean isSparc = System.getProperty("os.arch").startsWith("sparc"); 100 101 // make a note of the golden default locale 102 static final Locale DefaultLocale = Locale.getDefault(); 103 104 static final String JAVA_FILE_EXT = ".java"; 105 static final String CLASS_FILE_EXT = ".class"; 106 static final String JAR_FILE_EXT = ".jar"; 107 static final String EXE_FILE_EXT = ".exe"; 108 static final String JLDEBUG_KEY = "_JAVA_LAUNCHER_DEBUG"; 109 static final String EXPECTED_MARKER = "TRACER_MARKER:About to EXEC"; 110 static final String TEST_PREFIX = "###TestError###: "; 111 112 static int testExitValue = 0; 113 114 static { 115 String tmp = System.getProperty("test.classes", null); 116 if (tmp == null) { 117 throw new Error("property test.classes not defined ??"); 118 } 119 TEST_CLASSES_DIR = new File(tmp).getAbsoluteFile(); 120 121 tmp = System.getProperty("test.src", null); 122 if (tmp == null) { 123 throw new Error("property test.src not defined ??"); 124 } 125 TEST_SOURCES_DIR = new File(tmp).getAbsoluteFile(); 126 127 if (is64Bit && is32Bit) { 128 throw new RuntimeException("arch model cannot be both 32 and 64 bit"); 129 } 130 if (!is64Bit && !is32Bit) { 131 throw new RuntimeException("arch model is not 32 or 64 bit ?"); 132 } 133 compiler = ToolProvider.getSystemJavaCompiler(); 134 135 File binDir = new File(JAVAHOME, "bin"); 136 JAVA_BIN = binDir.getAbsolutePath(); 137 File libDir = new File(JAVAHOME, "lib"); 138 JAVA_LIB = libDir.getAbsolutePath(); 139 140 File javaCmdFile = (isWindows) 141 ? new File(binDir, "java.exe") 142 : new File(binDir, "java"); 143 javaCmd = javaCmdFile.getAbsolutePath(); 144 if (!javaCmdFile.canExecute()) { 145 throw new RuntimeException("java <" + TestHelper.javaCmd + 146 "> must exist and should be executable"); 147 } 148 149 File javacCmdFile = (isWindows) 150 ? new File(binDir, "javac.exe") 151 : new File(binDir, "javac"); 152 javacCmd = javacCmdFile.getAbsolutePath(); 153 154 File jarCmdFile = (isWindows) 155 ? new File(binDir, "jar.exe") 156 : new File(binDir, "jar"); 157 jarCmd = jarCmdFile.getAbsolutePath(); 158 if (!jarCmdFile.canExecute()) { 159 throw new RuntimeException("java <" + TestHelper.jarCmd + 160 "> must exist and should be executable"); 161 } 162 163 if (isWindows) { 164 File javawCmdFile = new File(binDir, "javaw.exe"); 165 javawCmd = javawCmdFile.getAbsolutePath(); 166 if (!javawCmdFile.canExecute()) { 167 throw new RuntimeException("java <" + javawCmd + 168 "> must exist and should be executable"); 169 } 170 } else { 171 javawCmd = null; 172 } 173 174 if (!javacCmdFile.canExecute()) { 175 throw new RuntimeException("java <" + javacCmd + 176 "> must exist and should be executable"); 177 } 178 179 haveClientVM = haveVmVariant("client"); 180 haveServerVM = haveVmVariant("server"); 181 } 182 private static boolean haveVmVariant(String type) { 183 if (isWindows) { 184 File vmDir = new File(JAVA_BIN, type); 185 File jvmFile = new File(vmDir, LIBJVM); 186 return jvmFile.exists(); 187 } else { 188 File vmDir = new File(JAVA_LIB, type); 189 File vmArchDir = new File(vmDir, getJreArch()); 190 File jvmFile = new File(vmArchDir, LIBJVM); 191 return jvmFile.exists(); 192 } 193 } 194 void run(String[] args) throws Exception { 195 int passed = 0, failed = 0; 196 final Pattern p = (args != null && args.length > 0) 197 ? Pattern.compile(args[0]) 198 : null; 199 for (Method m : this.getClass().getDeclaredMethods()) { 200 boolean selected = (p == null) 201 ? m.isAnnotationPresent(Test.class) 202 : p.matcher(m.getName()).matches(); 203 if (selected) { 204 try { 205 m.invoke(this, (Object[]) null); 206 System.out.println(m.getName() + ": OK"); 207 passed++; 208 System.out.printf("Passed: %d, Failed: %d, ExitValue: %d%n", 209 passed, failed, testExitValue); 210 } catch (Throwable ex) { 211 System.out.printf("Test %s failed: %s %n", m, ex); 212 System.out.println("----begin detailed exceptions----"); 213 ex.printStackTrace(System.out); 214 for (Throwable t : ex.getSuppressed()) { 215 t.printStackTrace(System.out); 216 } 217 System.out.println("----end detailed exceptions----"); 218 failed++; 219 } 220 } 221 } 222 System.out.printf("Total: Passed: %d, Failed %d%n", passed, failed); 223 if (failed > 0) { 224 throw new RuntimeException("Tests failed: " + failed); 225 } 226 if (passed == 0 && failed == 0) { 227 throw new AssertionError("No test(s) selected: passed = " + 228 passed + ", failed = " + failed + " ??????????"); 229 } 230 } 231 232 /* 233 * usually the jre/lib/arch-name is the same as os.arch, except for x86. 234 */ 235 static String getJreArch() { 236 String arch = System.getProperty("os.arch"); 237 return arch.equals("x86") ? "i386" : arch; 238 } 239 static String getArch() { 240 return System.getProperty("os.arch"); 241 } 242 static File getClassFile(File javaFile) { 243 String s = javaFile.getAbsolutePath().replace(JAVA_FILE_EXT, CLASS_FILE_EXT); 244 return new File(s); 245 } 246 247 static File getJavaFile(File classFile) { 248 String s = classFile.getAbsolutePath().replace(CLASS_FILE_EXT, JAVA_FILE_EXT); 249 return new File(s); 250 } 251 252 static String baseName(File f) { 253 String s = f.getName(); 254 return s.substring(0, s.indexOf(".")); 255 } 256 257 /* 258 * A convenience method to create a jar with jar file name and defs 259 */ 260 static void createJar(File jarName, String... mainDefs) 261 throws FileNotFoundException{ 262 createJar(null, jarName, new File("Foo"), mainDefs); 263 } 264 265 /* 266 * A convenience method to create a java file, compile and jar it up, using 267 * the sole class file name in the jar, as the Main-Class attribute value. 268 */ 269 static void createJar(File jarName, File mainClass, String... mainDefs) 270 throws FileNotFoundException { 271 createJar(null, jarName, mainClass, mainDefs); 272 } 273 274 /* 275 * A convenience method to compile java files. 276 */ 277 static void compile(String... compilerArgs) { 278 if (compiler.run(null, null, null, compilerArgs) != 0) { 279 String sarg = ""; 280 for (String x : compilerArgs) { 281 sarg.concat(x + " "); 282 } 283 throw new Error("compilation failed: " + sarg); 284 } 285 } 286 287 /* 288 * A generic jar file creator to create a java file, compile it 289 * and jar it up, a specific Main-Class entry name in the 290 * manifest can be specified or a null to use the sole class file name 291 * as the Main-Class attribute value. 292 */ 293 static void createJar(String mEntry, File jarName, File mainClass, 294 String... mainDefs) throws FileNotFoundException { 295 if (jarName.exists()) { 296 jarName.delete(); 297 } 298 try (PrintStream ps = new PrintStream(new FileOutputStream(mainClass + ".java"))) { 299 ps.println("public class Foo {"); 300 if (mainDefs != null) { 301 for (String x : mainDefs) { 302 ps.println(x); 303 } 304 } 305 ps.println("}"); 306 } 307 308 String compileArgs[] = { 309 mainClass + ".java" 310 }; 311 if (compiler.run(null, null, null, compileArgs) != 0) { 312 throw new RuntimeException("compilation failed " + mainClass + ".java"); 313 } 314 if (mEntry == null) { 315 mEntry = mainClass.getName(); 316 } 317 String jarArgs[] = { 318 (debug) ? "cvfe" : "cfe", 319 jarName.getAbsolutePath(), 320 mEntry, 321 mainClass.getName() + ".class" 322 }; 323 createJar(jarArgs); 324 } 325 326 static void createJar(String... args) { 327 List<String> cmdList = new ArrayList<>(); 328 cmdList.add(jarCmd); 329 cmdList.addAll(Arrays.asList(args)); 330 doExec(cmdList.toArray(new String[cmdList.size()])); 331 } 332 333 static void copyStream(InputStream in, OutputStream out) throws IOException { 334 byte[] buf = new byte[8192]; 335 int n = in.read(buf); 336 while (n > 0) { 337 out.write(buf, 0, n); 338 n = in.read(buf); 339 } 340 } 341 342 static void copyFile(File src, File dst) throws IOException { 343 Path parent = dst.toPath().getParent(); 344 if (parent != null) { 345 Files.createDirectories(parent); 346 } 347 Files.copy(src.toPath(), dst.toPath(), COPY_ATTRIBUTES, REPLACE_EXISTING); 348 } 349 350 /** 351 * Attempt to create a file at the given location. If an IOException 352 * occurs then back off for a moment and try again. When a number of 353 * attempts fail, give up and throw an exception. 354 */ 355 void createAFile(File aFile, List<String> contents) throws IOException { 356 IOException cause = null; 357 for (int attempts = 0; attempts < 10; attempts++) { 358 try { 359 Files.write(aFile.getAbsoluteFile().toPath(), contents, 360 Charset.defaultCharset(), CREATE, TRUNCATE_EXISTING, WRITE); 361 if (cause != null) { 362 /* 363 * report attempts and errors that were encountered 364 * for diagnostic purposes 365 */ 366 System.err.println("Created batch file " + 367 aFile + " in " + (attempts + 1) + 368 " attempts"); 369 System.err.println("Errors encountered: " + cause); 370 cause.printStackTrace(); 371 } 372 return; 373 } catch (IOException ioe) { 374 if (cause != null) { 375 // chain the exceptions so they all get reported for diagnostics 376 cause.addSuppressed(ioe); 377 } else { 378 cause = ioe; 379 } 380 } 381 382 try { 383 Thread.sleep(500); 384 } catch (InterruptedException ie) { 385 if (cause != null) { 386 // cause should alway be non-null here 387 ie.addSuppressed(cause); 388 } 389 throw new RuntimeException("Interrupted while creating batch file", ie); 390 } 391 } 392 throw new RuntimeException("Unable to create batch file", cause); 393 } 394 395 static void createFile(File outFile, List<String> content) throws IOException { 396 Files.write(outFile.getAbsoluteFile().toPath(), content, 397 Charset.defaultCharset(), CREATE_NEW); 398 } 399 400 static void recursiveDelete(File target) throws IOException { 401 if (!target.exists()) { 402 return; 403 } 404 Files.walkFileTree(target.toPath(), new SimpleFileVisitor<Path>() { 405 @Override 406 public FileVisitResult postVisitDirectory(Path dir, IOException exc) { 407 try { 408 Files.deleteIfExists(dir); 409 } catch (IOException ex) { 410 System.out.println("Error: could not delete: " + dir.toString()); 411 System.out.println(ex.getMessage()); 412 return FileVisitResult.TERMINATE; 413 } 414 return FileVisitResult.CONTINUE; 415 } 416 @Override 417 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { 418 try { 419 Files.deleteIfExists(file); 420 } catch (IOException ex) { 421 System.out.println("Error: could not delete: " + file.toString()); 422 System.out.println(ex.getMessage()); 423 return FileVisitResult.TERMINATE; 424 } 425 return FileVisitResult.CONTINUE; 426 } 427 }); 428 } 429 430 static TestResult doExec(String...cmds) { 431 return doExec(null, null, cmds); 432 } 433 434 static TestResult doExec(Map<String, String> envToSet, String...cmds) { 435 return doExec(envToSet, null, cmds); 436 } 437 /* 438 * A method which executes a java cmd and returns the results in a container 439 */ 440 static TestResult doExec(Map<String, String> envToSet, 441 Set<String> envToRemove, String...cmds) { 442 String cmdStr = ""; 443 for (String x : cmds) { 444 cmdStr = cmdStr.concat(x + " "); 445 } 446 ProcessBuilder pb = new ProcessBuilder(cmds); 447 Map<String, String> env = pb.environment(); 448 if (envToRemove != null) { 449 for (String key : envToRemove) { 450 env.remove(key); 451 } 452 } 453 if (envToSet != null) { 454 env.putAll(envToSet); 455 } 456 BufferedReader rdr = null; 457 try { 458 List<String> outputList = new ArrayList<>(); 459 pb.redirectErrorStream(true); 460 Process p = pb.start(); 461 rdr = new BufferedReader(new InputStreamReader(p.getInputStream())); 462 String in = rdr.readLine(); 463 while (in != null) { 464 outputList.add(in); 465 in = rdr.readLine(); 466 } 467 p.waitFor(); 468 p.destroy(); 469 470 return new TestHelper.TestResult(cmdStr, p.exitValue(), outputList, 471 env, new Throwable("current stack of the test")); 472 } catch (Exception ex) { 473 ex.printStackTrace(); 474 throw new RuntimeException(ex.getMessage()); 475 } 476 } 477 478 static FileFilter createFilter(final String extension) { 479 return new FileFilter() { 480 @Override 481 public boolean accept(File pathname) { 482 String name = pathname.getName(); 483 if (name.endsWith(extension)) { 484 return true; 485 } 486 return false; 487 } 488 }; 489 } 490 491 static boolean isEnglishLocale() { 492 return Locale.getDefault().getLanguage().equals("en"); 493 } 494 495 /* 496 * A class to encapsulate the test results and stuff, with some ease 497 * of use methods to check the test results. 498 */ 499 static class TestResult { 500 PrintWriter status; 501 StringWriter sw; 502 int exitValue; 503 List<String> testOutput; 504 Map<String, String> env; 505 Throwable t; 506 boolean testStatus; 507 508 public TestResult(String str, int rv, List<String> oList, 509 Map<String, String> env, Throwable t) { 510 sw = new StringWriter(); 511 status = new PrintWriter(sw); 512 status.println("Executed command: " + str + "\n"); 513 exitValue = rv; 514 testOutput = oList; 515 this.env = env; 516 this.t = t; 517 testStatus = true; 518 } 519 520 void appendError(String x) { 521 testStatus = false; 522 testExitValue++; 523 status.println(TEST_PREFIX + x); 524 } 525 526 void indentStatus(String x) { 527 status.println(" " + x); 528 } 529 530 void checkNegative() { 531 if (exitValue == 0) { 532 appendError("test must not return 0 exit value"); 533 } 534 } 535 536 void checkPositive() { 537 if (exitValue != 0) { 538 appendError("test did not return 0 exit value"); 539 } 540 } 541 542 boolean isOK() { 543 return exitValue == 0; 544 } 545 546 boolean isZeroOutput() { 547 if (!testOutput.isEmpty()) { 548 appendError("No message from cmd please"); 549 return false; 550 } 551 return true; 552 } 553 554 boolean isNotZeroOutput() { 555 if (testOutput.isEmpty()) { 556 appendError("Missing message"); 557 return false; 558 } 559 return true; 560 } 561 562 @Override 563 public String toString() { 564 status.println("++++Begin Test Info++++"); 565 status.println("Test Status: " + (testStatus ? "PASS" : "FAIL")); 566 status.println("++++Test Environment++++"); 567 for (String x : env.keySet()) { 568 indentStatus(x + "=" + env.get(x)); 569 } 570 status.println("++++Test Output++++"); 571 for (String x : testOutput) { 572 indentStatus(x); 573 } 574 status.println("++++Test Stack Trace++++"); 575 status.println(t.toString()); 576 for (StackTraceElement e : t.getStackTrace()) { 577 indentStatus(e.toString()); 578 } 579 status.println("++++End of Test Info++++"); 580 status.flush(); 581 String out = sw.toString(); 582 status.close(); 583 return out; 584 } 585 586 boolean contains(String str) { 587 for (String x : testOutput) { 588 if (x.contains(str)) { 589 return true; 590 } 591 } 592 appendError("string <" + str + "> not found"); 593 return false; 594 } 595 596 boolean notContains(String str) { 597 for (String x : testOutput) { 598 if (x.contains(str)) { 599 appendError("string <" + str + "> found"); 600 return false; 601 } 602 } 603 return true; 604 } 605 606 boolean matches(String stringToMatch) { 607 for (String x : testOutput) { 608 if (x.matches(stringToMatch)) { 609 return true; 610 } 611 } 612 appendError("string <" + stringToMatch + "> not found"); 613 return false; 614 } 615 616 boolean notMatches(String stringToMatch) { 617 for (String x : testOutput) { 618 if (!x.matches(stringToMatch)) { 619 return true; 620 } 621 } 622 appendError("string <" + stringToMatch + "> found"); 623 return false; 624 } 625 } 626 /** 627 * Indicates that the annotated method is a test method. 628 */ 629 @Retention(RetentionPolicy.RUNTIME) 630 @Target(ElementType.METHOD) 631 public @interface Test {} 632 }