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