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 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 = (isSDK)
 136                 ? new File((new File(JAVAHOME)).getParentFile(), "bin")
 137                 : new File(JAVAHOME, "bin");
 138         JAVA_BIN = binDir.getAbsolutePath();
 139         JAVA_JRE_BIN = new File(JAVAHOME, "bin").getAbsolutePath();
 140 
 141         File libDir = (isSDK)
 142                 ? new File((new File(JAVAHOME)).getParentFile(), "lib")
 143                 : new File(JAVAHOME, "lib");
 144         JAVA_LIB = libDir.getAbsolutePath();
 145         JAVA_JRE_LIB = new File(JAVAHOME, "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, LIBJVM);
 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, LIBJVM);
 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 }