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