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