changeset 4442:1689a0912ebe

8006413: Add utility classes for writing better multiprocess tests in jtreg Summary: Add a few utility classes to test/testlibrary to support multi process testing in jtreg tests. Added a test case for one of the utility classes. Also reviewed by Vitaly Davidovich Reviewed-by: brutisso, dholmes, vlivanov, nloodin, mgerdin
author ctornqvi
date Mon, 04 Feb 2013 08:40:19 +0100
parents 607dd575f464
children 77726262b76f
files test/testlibrary/OutputAnalyzerTest.java test/testlibrary/com/oracle/java/testlibrary/JDKToolFinder.java test/testlibrary/com/oracle/java/testlibrary/OutputAnalyzer.java test/testlibrary/com/oracle/java/testlibrary/OutputBuffer.java test/testlibrary/com/oracle/java/testlibrary/ProcessTools.java test/testlibrary/com/oracle/java/testlibrary/StreamPumper.java
diffstat 6 files changed, 625 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/testlibrary/OutputAnalyzerTest.java	Mon Feb 04 08:40:19 2013 +0100
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary Test the OutputAnalyzer utility class
+ * @library /testlibrary
+ */
+
+import com.oracle.java.testlibrary.OutputAnalyzer;
+
+public class OutputAnalyzerTest {
+
+  public static void main(String args[]) throws Exception {
+
+    String stdout = "aaaaaa";
+    String stderr = "bbbbbb";
+
+    OutputAnalyzer output = new OutputAnalyzer(stdout, stderr);
+
+    if (!stdout.equals(output.getStdout())) {
+      throw new Exception("getStdout() returned '" + output.getStdout() + "', expected '" + stdout + "'");
+    }
+
+    if (!stderr.equals(output.getStderr())) {
+      throw new Exception("getStderr() returned '" + output.getStderr() + "', expected '" + stderr + "'");
+    }
+
+    try {
+      output.shouldContain(stdout);
+      output.stdoutShouldContain(stdout);
+      output.shouldContain(stderr);
+      output.stderrShouldContain(stderr);
+    } catch (RuntimeException e) {
+      throw new Exception("shouldContain() failed", e);
+    }
+
+    try {
+      output.shouldContain("cccc");
+      throw new Exception("shouldContain() failed to throw exception");
+    } catch (RuntimeException e) {
+      // expected
+    }
+
+    try {
+      output.stdoutShouldContain(stderr);
+      throw new Exception("stdoutShouldContain() failed to throw exception");
+    } catch (RuntimeException e) {
+      // expected
+    }
+
+    try {
+      output.stderrShouldContain(stdout);
+      throw new Exception("stdoutShouldContain() failed to throw exception");
+    } catch (RuntimeException e) {
+      // expected
+    }
+
+    try {
+      output.shouldNotContain("cccc");
+      output.stdoutShouldNotContain("cccc");
+      output.stderrShouldNotContain("cccc");
+    } catch (RuntimeException e) {
+      throw new Exception("shouldNotContain() failed", e);
+    }
+
+    try {
+      output.shouldNotContain(stdout);
+      throw new Exception("shouldContain() failed to throw exception");
+    } catch (RuntimeException e) {
+      // expected
+    }
+
+    try {
+      output.stdoutShouldNotContain(stdout);
+      throw new Exception("shouldContain() failed to throw exception");
+    } catch (RuntimeException e) {
+      // expected
+    }
+
+    try {
+      output.stderrShouldNotContain(stderr);
+      throw new Exception("shouldContain() failed to throw exception");
+    } catch (RuntimeException e) {
+      // expected
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/testlibrary/com/oracle/java/testlibrary/JDKToolFinder.java	Mon Feb 04 08:40:19 2013 +0100
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.oracle.java.testlibrary;
+
+import java.io.File;
+
+public final class JDKToolFinder {
+
+  private JDKToolFinder() {
+  }
+
+  /**
+   * Returns the full path to an executable in jdk/bin based on System property
+   * test.jdk (set by jtreg test suite)
+   *
+   * @return Full path to an executable in jdk/bin
+   */
+  public static String getJDKTool(String tool) {
+    String binPath = System.getProperty("test.jdk");
+    if (binPath == null) {
+      throw new RuntimeException("System property 'test.jdk' not set. This property is normally set by jtreg. "
+          + "When running test separately, set this property using '-Dtest.jdk=/path/to/jdk'.");
+    }
+
+    binPath += File.separatorChar + "bin" + File.separatorChar + tool;
+
+    return binPath;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/testlibrary/com/oracle/java/testlibrary/OutputAnalyzer.java	Mon Feb 04 08:40:19 2013 +0100
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.oracle.java.testlibrary;
+
+import java.io.IOException;
+
+public final class OutputAnalyzer {
+
+  private final String stdout;
+  private final String stderr;
+  private final int exitValue;
+
+  /**
+   * Create an OutputAnalyzer, a utility class for verifying output and exit
+   * value from a Process
+   *
+   * @param process Process to analyze
+   * @throws IOException If an I/O error occurs.
+   */
+  public OutputAnalyzer(Process process) throws IOException {
+    OutputBuffer output = ProcessTools.getOutput(process);
+    exitValue = process.exitValue();
+    this.stdout = output.getStdout();
+    this.stderr = output.getStderr();
+  }
+
+  /**
+   * Create an OutputAnalyzer, a utility class for verifying output
+   *
+   * @param buf String buffer to analyze
+   */
+  public OutputAnalyzer(String buf) {
+    this(buf, buf);
+  }
+
+  /**
+   * Create an OutputAnalyzer, a utility class for verifying output
+   *
+   * @param stdout stdout buffer to analyze
+   * @param stderr stderr buffer to analyze
+   */
+  public OutputAnalyzer(String stdout, String stderr) {
+    this.stdout = stdout;
+    this.stderr = stderr;
+    exitValue = -1;
+  }
+
+  /**
+   * Verify that the stdout and stderr contents of output buffer contains the string
+   *
+   * @param expectedString String that buffer should contain
+   * @throws RuntimeException If the string was not found
+   */
+  public void shouldContain(String expectedString) {
+    if (!stdout.contains(expectedString) && !stderr.contains(expectedString)) {
+      throw new RuntimeException("'" + expectedString + "' missing from stdout/stderr: [" + stdout + stderr + "]\n");
+    }
+  }
+
+  /**
+   * Verify that the stdout contents of output buffer contains the string
+   *
+   * @param expectedString String that buffer should contain
+   * @throws RuntimeException If the string was not found
+   */
+  public void stdoutShouldContain(String expectedString) {
+    if (!stdout.contains(expectedString)) {
+      throw new RuntimeException("'" + expectedString + "' missing from stdout: [" + stdout + "]\n");
+    }
+  }
+
+  /**
+   * Verify that the stderr contents of output buffer contains the string
+   *
+   * @param expectedString String that buffer should contain
+   * @throws RuntimeException If the string was not found
+   */
+  public void stderrShouldContain(String expectedString) {
+    if (!stderr.contains(expectedString)) {
+      throw new RuntimeException("'" + expectedString + "' missing from stderr: [" + stderr + "]\n");
+    }
+  }
+
+  /**
+   * Verify that the stdout and stderr contents of output buffer does not contain the string
+   *
+   * @param expectedString String that the buffer should not contain
+   * @throws RuntimeException If the string was found
+   */
+  public void shouldNotContain(String notExpectedString) {
+    if (stdout.contains(notExpectedString)) {
+      throw new RuntimeException("'" + notExpectedString + "' found in stdout: [" + stdout + "]\n");
+    }
+    if (stderr.contains(notExpectedString)) {
+      throw new RuntimeException("'" + notExpectedString + "' found in stderr: [" + stderr + "]\n");
+    }
+  }
+
+  /**
+   * Verify that the stdout contents of output buffer does not contain the string
+   *
+   * @param expectedString String that the buffer should not contain
+   * @throws RuntimeException If the string was found
+   */
+  public void stdoutShouldNotContain(String notExpectedString) {
+    if (stdout.contains(notExpectedString)) {
+      throw new RuntimeException("'" + notExpectedString + "' found in stdout: [" + stdout + "]\n");
+    }
+  }
+
+  /**
+   * Verify that the stderr contents of output buffer does not contain the string
+   *
+   * @param expectedString String that the buffer should not contain
+   * @throws RuntimeException If the string was found
+   */
+  public void stderrShouldNotContain(String notExpectedString) {
+    if (stderr.contains(notExpectedString)) {
+      throw new RuntimeException("'" + notExpectedString + "' found in stderr: [" + stderr + "]\n");
+    }
+  }
+
+  /**
+   * Verifiy the exit value of the process
+   *
+   * @param expectedExitValue Expected exit value from process
+   * @throws RuntimeException If the exit value from the process did not match the expected value
+   */
+  public void shouldHaveExitValue(int expectedExitValue) {
+    if (getExitValue() != expectedExitValue) {
+      throw new RuntimeException("Exit value " + getExitValue() + " , expected to get " + expectedExitValue);
+    }
+  }
+
+  /**
+   * Get the contents of the output buffer (stdout and stderr)
+   *
+   * @return Content of the output buffer
+   */
+  public String getOutput() {
+    return stdout + stderr;
+  }
+
+  /**
+   * Get the contents of the stdout buffer
+   *
+   * @return Content of the stdout buffer
+   */
+  public String getStdout() {
+    return stdout;
+  }
+
+  /**
+   * Get the contents of the stderr buffer
+   *
+   * @return Content of the stderr buffer
+   */
+  public String getStderr() {
+    return stderr;
+  }
+
+  /**
+   * Get the process exit value
+   *
+   * @return Process exit value
+   */
+  public int getExitValue() {
+    return exitValue;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/testlibrary/com/oracle/java/testlibrary/OutputBuffer.java	Mon Feb 04 08:40:19 2013 +0100
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.oracle.java.testlibrary;
+
+public class OutputBuffer {
+  private final String stdout;
+  private final String stderr;
+
+  /**
+   * Create an OutputBuffer, a class for storing and managing stdout and stderr
+   * results separately
+   *
+   * @param stdout stdout result
+   * @param stderr stderr result
+   */
+  public OutputBuffer(String stdout, String stderr) {
+    this.stdout = stdout;
+    this.stderr = stderr;
+  }
+
+  /**
+   * Returns the stdout result
+   *
+   * @return stdout result
+   */
+  public String getStdout() {
+    return stdout;
+  }
+
+  /**
+   * Returns the stderr result
+   *
+   * @return stderr result
+   */
+  public String getStderr() {
+    return stderr;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/testlibrary/com/oracle/java/testlibrary/ProcessTools.java	Mon Feb 04 08:40:19 2013 +0100
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.oracle.java.testlibrary;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.lang.management.RuntimeMXBean;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+
+import sun.management.VMManagement;
+
+public final class ProcessTools {
+
+  private ProcessTools() {
+  }
+
+  /**
+   * Pumps stdout and stderr from running the process into a String.
+   *
+   * @param processHandler ProcessHandler to run.
+   * @return Output from process.
+   * @throws IOException If an I/O error occurs.
+   */
+  public static OutputBuffer getOutput(ProcessBuilder processBuilder) throws IOException {
+    return getOutput(processBuilder.start());
+  }
+
+  /**
+   * Pumps stdout and stderr the running process into a String.
+   *
+   * @param process Process to pump.
+   * @return Output from process.
+   * @throws IOException If an I/O error occurs.
+   */
+  public static OutputBuffer getOutput(Process process) throws IOException {
+    ByteArrayOutputStream stderrBuffer = new ByteArrayOutputStream();
+    ByteArrayOutputStream stdoutBuffer = new ByteArrayOutputStream();
+    StreamPumper outPumper = new StreamPumper(process.getInputStream(), stdoutBuffer);
+    StreamPumper errPumper = new StreamPumper(process.getErrorStream(), stderrBuffer);
+    Thread outPumperThread = new Thread(outPumper);
+    Thread errPumperThread = new Thread(errPumper);
+
+    outPumperThread.setDaemon(true);
+    errPumperThread.setDaemon(true);
+
+    outPumperThread.start();
+    errPumperThread.start();
+
+    try {
+      process.waitFor();
+      outPumperThread.join();
+      errPumperThread.join();
+    } catch (InterruptedException e) {
+      Thread.currentThread().interrupt();
+      return null;
+    }
+
+    return new OutputBuffer(stdoutBuffer.toString(), stderrBuffer.toString());
+  }
+
+  /**
+   * Get the process id of the current running Java process
+   *
+   * @return Process id
+   */
+  public static int getProcessId() throws Exception {
+
+    // Get the current process id using a reflection hack
+    RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
+    Field jvm = runtime.getClass().getDeclaredField("jvm");
+
+    jvm.setAccessible(true);
+    VMManagement mgmt = (sun.management.VMManagement) jvm.get(runtime);
+
+    Method pid_method = mgmt.getClass().getDeclaredMethod("getProcessId");
+
+    pid_method.setAccessible(true);
+
+    int pid = (Integer) pid_method.invoke(mgmt);
+
+    return pid;
+  }
+
+  /**
+   * Get platform specific VM arguments (e.g. -d64 on 64bit Solaris)
+   *
+   * @return String[] with platform specific arguments, empty if there are none
+   */
+  public static String[] getPlatformSpecificVMArgs() {
+    String osName = System.getProperty("os.name");
+    String dataModel = System.getProperty("sun.arch.data.model");
+
+    if (osName.equals("SunOS") && dataModel.equals("64")) {
+      return new String[] { "-d64" };
+    }
+
+    return new String[] {};
+  }
+
+  /**
+   * Create ProcessBuilder using the java launcher from the jdk to be tested and
+   * with any platform specific arguments prepended
+   */
+  public static ProcessBuilder createJavaProcessBuilder(String... command) throws Exception {
+    String javapath = JDKToolFinder.getJDKTool("java");
+
+    ArrayList<String> args = new ArrayList<>();
+    args.add(javapath);
+    Collections.addAll(args, getPlatformSpecificVMArgs());
+    Collections.addAll(args, command);
+
+    return new ProcessBuilder(args.toArray(new String[args.size()]));
+
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/testlibrary/com/oracle/java/testlibrary/StreamPumper.java	Mon Feb 04 08:40:19 2013 +0100
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.oracle.java.testlibrary;
+
+import java.io.OutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+
+public final class StreamPumper implements Runnable {
+
+  private static final int BUF_SIZE = 256;
+
+  private final OutputStream out;
+  private final InputStream in;
+
+  /**
+   * Create a StreamPumper that reads from in and writes to out.
+   *
+   * @param in The stream to read from.
+   * @param out The stream to write to.
+   */
+  public StreamPumper(InputStream in, OutputStream out) {
+    this.in = in;
+    this.out = out;
+  }
+
+  /**
+   * Implements Thread.run(). Continuously read from <code>in</code> and write
+   * to <code>out</code> until <code>in</code> has reached end of stream. Abort
+   * on interruption. Abort on IOExceptions.
+   */
+  @Override
+  public void run() {
+    int length;
+    InputStream localIn = in;
+    OutputStream localOut = out;
+    byte[] buffer = new byte[BUF_SIZE];
+
+    try {
+      while (!Thread.interrupted() && (length = localIn.read(buffer)) > 0) {
+        localOut.write(buffer, 0, length);
+      }
+    } catch (IOException e) {
+      // Just abort if something like this happens.
+      e.printStackTrace();
+    } finally {
+      try {
+        localOut.flush();
+        in.close();
+      } catch (IOException e) {
+        e.printStackTrace();
+      }
+    }
+  }
+}