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