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