8022865: [TESTBUG] Compressed Oops testing needs to be revised
authorctornqvi
Thu, 11 Sep 2014 17:54:22 -0700
changeset 26688 bb0cdd873e51
parent 26687 eb6c5d0d1d6b
child 26689 231048b0dfbd
8022865: [TESTBUG] Compressed Oops testing needs to be revised Summary: Rewrote compressed oops tests Reviewed-by: kvn, coleenp, mseledtsov
hotspot/src/share/vm/memory/universe.cpp
hotspot/src/share/vm/memory/universe.hpp
hotspot/test/runtime/CompressedOops/CompressedClassSpaceSize.java
hotspot/test/runtime/CompressedOops/ObjectAlignment.java
hotspot/test/runtime/CompressedOops/UseCompressedOops.java
hotspot/test/testlibrary/com/oracle/java/testlibrary/OutputAnalyzer.java
--- a/hotspot/src/share/vm/memory/universe.cpp	Thu Sep 11 10:46:32 2014 -0400
+++ b/hotspot/src/share/vm/memory/universe.cpp	Thu Sep 11 17:54:22 2014 -0700
@@ -838,12 +838,6 @@
     // See needs_explicit_null_check.
     // Only set the heap base for compressed oops because it indicates
     // compressed oops for pstack code.
-    bool verbose = PrintCompressedOopsMode || (PrintMiscellaneous && Verbose);
-    if (verbose) {
-      tty->cr();
-      tty->print("heap address: " PTR_FORMAT ", size: " SIZE_FORMAT " MB",
-                 Universe::heap()->base(), Universe::heap()->reserved_region().byte_size()/M);
-    }
     if (((uint64_t)Universe::heap()->reserved_region().end() > OopEncodingHeapMax)) {
       // Can't reserve heap below 32Gb.
       // keep the Universe::narrow_oop_base() set in Universe::reserve_heap()
@@ -853,16 +847,8 @@
       // are decoded so that NULL is preserved, so this page will not be accessed.
       Universe::set_narrow_oop_use_implicit_null_checks(false);
 #endif
-      if (verbose) {
-        tty->print(", %s: "PTR_FORMAT,
-            narrow_oop_mode_to_string(HeapBasedNarrowOop),
-            Universe::narrow_oop_base());
-      }
     } else {
       Universe::set_narrow_oop_base(0);
-      if (verbose) {
-        tty->print(", %s", narrow_oop_mode_to_string(ZeroBasedNarrowOop));
-      }
 #ifdef _WIN64
       if (!Universe::narrow_oop_use_implicit_null_checks()) {
         // Don't need guard page for implicit checks in indexed addressing
@@ -875,17 +861,14 @@
         Universe::set_narrow_oop_shift(LogMinObjAlignmentInBytes);
       } else {
         Universe::set_narrow_oop_shift(0);
-        if (verbose) {
-          tty->print(", %s", narrow_oop_mode_to_string(UnscaledNarrowOop));
-        }
       }
     }
 
-    if (verbose) {
-      tty->cr();
-      tty->cr();
+    Universe::set_narrow_ptrs_base(Universe::narrow_oop_base());
+
+    if (PrintCompressedOopsMode || (PrintMiscellaneous && Verbose)) {
+      Universe::print_compressed_oops_mode();
     }
-    Universe::set_narrow_ptrs_base(Universe::narrow_oop_base());
   }
   // Universe::narrow_oop_base() is one page below the heap.
   assert((intptr_t)Universe::narrow_oop_base() <= (intptr_t)(Universe::heap()->base() -
@@ -906,6 +889,24 @@
   return JNI_OK;
 }
 
+void Universe::print_compressed_oops_mode() {
+  tty->cr();
+  tty->print("heap address: " PTR_FORMAT ", size: " SIZE_FORMAT " MB",
+              Universe::heap()->base(), Universe::heap()->reserved_region().byte_size()/M);
+
+  tty->print(", Compressed Oops mode: %s", narrow_oop_mode_to_string(narrow_oop_mode()));
+
+  if (Universe::narrow_oop_base() != 0) {
+    tty->print(":" PTR_FORMAT, Universe::narrow_oop_base());
+  }
+
+  if (Universe::narrow_oop_shift() != 0) {
+    tty->print(", Oop shift amount: %d", Universe::narrow_oop_shift());
+  }
+
+  tty->cr();
+  tty->cr();
+}
 
 // Reserve the Java heap, which is now the same for all GCs.
 ReservedSpace Universe::reserve_heap(size_t heap_size, size_t alignment) {
@@ -975,11 +976,11 @@
 const char* Universe::narrow_oop_mode_to_string(Universe::NARROW_OOP_MODE mode) {
   switch (mode) {
     case UnscaledNarrowOop:
-      return "32-bits Oops";
+      return "32-bit";
     case ZeroBasedNarrowOop:
-      return "zero based Compressed Oops";
+      return "Zero based";
     case HeapBasedNarrowOop:
-      return "Compressed Oops with base";
+      return "Non-zero based";
   }
 
   ShouldNotReachHere();
--- a/hotspot/src/share/vm/memory/universe.hpp	Thu Sep 11 10:46:32 2014 -0400
+++ b/hotspot/src/share/vm/memory/universe.hpp	Thu Sep 11 17:54:22 2014 -0700
@@ -368,6 +368,8 @@
   static void     set_narrow_ptrs_base(address a)         { _narrow_ptrs_base = a; }
   static address  narrow_ptrs_base()                      { return _narrow_ptrs_base; }
 
+  static void     print_compressed_oops_mode();
+
   // this is set in vm_version on sparc (and then reset in universe afaict)
   static void     set_narrow_oop_shift(int shift)         {
     _narrow_oop._shift   = shift;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/CompressedOops/CompressedClassSpaceSize.java	Thu Sep 11 17:54:22 2014 -0700
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2014, 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
+ * @bug 8022865
+ * @summary Tests for the -XX:CompressedClassSpaceSize command line option
+ * @library /testlibrary
+ * @run main CompressedClassSpaceSize
+ */
+import com.oracle.java.testlibrary.*;
+
+public class CompressedClassSpaceSize {
+
+    public static void main(String[] args) throws Exception {
+        ProcessBuilder pb;
+        OutputAnalyzer output;
+        if (Platform.is64bit()) {
+            // Minimum size is 1MB
+            pb = ProcessTools.createJavaProcessBuilder("-XX:CompressedClassSpaceSize=0",
+                                                       "-version");
+            output = new OutputAnalyzer(pb.start());
+            output.shouldContain("CompressedClassSpaceSize of 0 is invalid")
+                  .shouldHaveExitValue(1);
+
+            // Invalid size of -1 should be handled correctly
+            pb = ProcessTools.createJavaProcessBuilder("-XX:CompressedClassSpaceSize=-1",
+                                                       "-version");
+            output = new OutputAnalyzer(pb.start());
+            output.shouldContain("Improperly specified VM option 'CompressedClassSpaceSize=-1'")
+                  .shouldHaveExitValue(1);
+
+
+            // Maximum size is 3GB
+            pb = ProcessTools.createJavaProcessBuilder("-XX:CompressedClassSpaceSize=4g",
+                                                       "-version");
+            output = new OutputAnalyzer(pb.start());
+            output.shouldContain("CompressedClassSpaceSize of 4294967296 is invalid")
+                  .shouldHaveExitValue(1);
+
+
+            // Make sure the minimum size is set correctly and printed
+            pb = ProcessTools.createJavaProcessBuilder("-XX:+UnlockDiagnosticVMOptions",
+                                                       "-XX:CompressedClassSpaceSize=1m",
+                                                       "-XX:+PrintCompressedOopsMode",
+                                                       "-version");
+            output = new OutputAnalyzer(pb.start());
+            output.shouldContain("Compressed class space size: 1048576")
+                  .shouldHaveExitValue(0);
+
+
+            // Make sure the maximum size is set correctly and printed
+            pb = ProcessTools.createJavaProcessBuilder("-XX:+UnlockDiagnosticVMOptions",
+                                                       "-XX:CompressedClassSpaceSize=3g",
+                                                       "-XX:+PrintCompressedOopsMode",
+                                                       "-version");
+            output = new OutputAnalyzer(pb.start());
+            output.shouldContain("Compressed class space size: 3221225472")
+                  .shouldHaveExitValue(0);
+
+
+            pb = ProcessTools.createJavaProcessBuilder("-XX:-UseCompressedOops",
+                                                       "-XX:CompressedClassSpaceSize=1m",
+                                                       "-version");
+            output = new OutputAnalyzer(pb.start());
+            output.shouldContain("Setting CompressedClassSpaceSize has no effect when compressed class pointers are not used")
+                  .shouldHaveExitValue(0);
+
+
+            pb = ProcessTools.createJavaProcessBuilder("-XX:-UseCompressedClassPointers",
+                                                       "-XX:CompressedClassSpaceSize=1m",
+                                                       "-version");
+            output = new OutputAnalyzer(pb.start());
+            output.shouldContain("Setting CompressedClassSpaceSize has no effect when compressed class pointers are not used")
+                  .shouldHaveExitValue(0);
+        } else {
+            // 32bit platforms doesn't have compressed oops
+            pb = ProcessTools.createJavaProcessBuilder("-XX:CompressedClassSpaceSize=1m",
+                                                       "-version");
+            output = new OutputAnalyzer(pb.start());
+            output.shouldContain("Setting CompressedClassSpaceSize has no effect when compressed class pointers are not used")
+                  .shouldHaveExitValue(0);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/CompressedOops/ObjectAlignment.java	Thu Sep 11 17:54:22 2014 -0700
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2014, 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
+ * @bug 8022865
+ * @summary Tests for the -XX:ObjectAlignmentInBytes command line option
+ * @library /testlibrary
+ * @run main ObjectAlignment
+ */
+import com.oracle.java.testlibrary.*;
+
+public class ObjectAlignment {
+
+    public static void main(String[] args) throws Exception {
+
+        if (Platform.is64bit()) {
+            // Minimum alignment should be 8
+            testObjectAlignment(4)
+                .shouldContain("error: ObjectAlignmentInBytes=4 must be greater or equal 8")
+                .shouldHaveExitValue(1);
+
+            // Alignment has to be a power of 2
+            testObjectAlignment(9)
+                .shouldContain("error: ObjectAlignmentInBytes=9 must be power of 2")
+                .shouldHaveExitValue(1);
+
+            testObjectAlignment(-1)
+                .shouldContain("error: ObjectAlignmentInBytes=-1 must be power of 2")
+                .shouldHaveExitValue(1);
+
+            // Maximum alignment allowed is 256
+            testObjectAlignment(512)
+                .shouldContain("error: ObjectAlignmentInBytes=512 must not be greater than 256")
+                .shouldHaveExitValue(1);
+
+            // Valid alignments should work
+            testObjectAlignment(8).shouldHaveExitValue(0);
+            testObjectAlignment(16).shouldHaveExitValue(0);
+            testObjectAlignment(256).shouldHaveExitValue(0);
+
+        } else {
+            // For a 32bit JVM the option isn't there, make sure it's not silently ignored
+            testObjectAlignment(8)
+                .shouldContain("Unrecognized VM option 'ObjectAlignmentInBytes=8'")
+                .shouldHaveExitValue(1);
+        }
+    }
+
+    private static OutputAnalyzer testObjectAlignment(int alignment) throws Exception {
+        ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-XX:ObjectAlignmentInBytes=" + alignment,
+                                                                  "-version");
+        return new OutputAnalyzer(pb.start());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/CompressedOops/UseCompressedOops.java	Thu Sep 11 17:54:22 2014 -0700
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2014, 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
+ * @bug 8022865
+ * @summary Tests for different combination of UseCompressedOops options
+ * @library /testlibrary
+ * @run main UseCompressedOops
+ */
+import java.util.ArrayList;
+import java.util.Collections;
+import com.oracle.java.testlibrary.*;
+
+public class UseCompressedOops {
+
+    public static void main(String[] args) throws Exception {
+
+        if (Platform.is64bit()) {
+            // Explicitly turn of compressed oops
+            testCompressedOops("-XX:-UseCompressedOops", "-Xmx32m")
+                .shouldNotContain("Compressed Oops")
+                .shouldHaveExitValue(0);
+
+            // Compressed oops should be on by default
+            testCompressedOops("-Xmx32m")
+                .shouldContain("Compressed Oops mode")
+                .shouldHaveExitValue(0);
+
+            // Explicly enabling compressed oops
+            testCompressedOops("-XX:+UseCompressedOops", "-Xmx32m")
+                .shouldContain("Compressed Oops mode")
+                .shouldHaveExitValue(0);
+
+            // Larger than 4gb heap should result in zero based with shift 3
+            testCompressedOops("-XX:+UseCompressedOops", "-Xmx5g")
+                .shouldContain("Zero based")
+                .shouldContain("Oop shift amount: 3")
+                .shouldHaveExitValue(0);
+
+            // Skip the following three test cases if we're on OSX or Solaris Sparc.
+            //
+            // OSX doesn't seem to care about HeapBaseMinAddress and Solaris Sparc
+            // puts the heap way up, forcing different behaviour.
+
+            if (!Platform.isOSX() && !(Platform.isSolaris() && Platform.isSparc())) {
+                // Small heap above 4gb should result in zero based with shift 3
+                testCompressedOops("-XX:+UseCompressedOops", "-Xmx32m", "-XX:HeapBaseMinAddress=4g")
+                    .shouldContain("Zero based")
+                    .shouldContain("Oop shift amount: 3")
+                    .shouldHaveExitValue(0);
+
+                // Small heap above 32gb should result in non-zero based with shift 3
+                testCompressedOops("-XX:+UseCompressedOops", "-Xmx32m", "-XX:HeapBaseMinAddress=32g")
+                    .shouldContain("Non-zero based")
+                    .shouldContain("Oop shift amount: 3")
+                    .shouldHaveExitValue(0);
+
+                // 32gb heap with heap base above 64gb and object alignment set to 16 bytes should result
+                // in non-zero based with shift 4
+                testCompressedOops("-XX:+UseCompressedOops", "-Xmx32g", "-XX:ObjectAlignmentInBytes=16",
+                               "-XX:HeapBaseMinAddress=64g")
+                    .shouldContain("Non-zero based")
+                    .shouldContain("Oop shift amount: 4")
+                    .shouldHaveExitValue(0);
+            }
+
+            // Explicitly enabling compressed oops with 32gb heap should result a warning
+            testCompressedOops("-XX:+UseCompressedOops", "-Xmx32g")
+                .shouldContain("Max heap size too large for Compressed Oops")
+                .shouldHaveExitValue(0);
+
+            // 32gb heap should not result a warning
+            testCompressedOops("-Xmx32g")
+                .shouldNotContain("Max heap size too large for Compressed Oops")
+                .shouldHaveExitValue(0);
+
+            // Explicitly enabling compressed oops with 32gb heap and object
+            // alignment set to 8 byte should result a warning
+            testCompressedOops("-XX:+UseCompressedOops", "-Xmx32g", "-XX:ObjectAlignmentInBytes=8")
+                .shouldContain("Max heap size too large for Compressed Oops")
+                .shouldHaveExitValue(0);
+
+            // 64gb heap and object alignment set to 16 bytes should result in a warning
+            testCompressedOops("-XX:+UseCompressedOops", "-Xmx64g", "-XX:ObjectAlignmentInBytes=16")
+                .shouldContain("Max heap size too large for Compressed Oops")
+                .shouldHaveExitValue(0);
+
+            // 32gb heap with object alignment set to 16 bytes should result in zero based with shift 4
+            testCompressedOops("-XX:+UseCompressedOops", "-Xmx32g", "-XX:ObjectAlignmentInBytes=16")
+                .shouldContain("Zero based")
+                .shouldContain("Oop shift amount: 4")
+                .shouldHaveExitValue(0);
+
+        } else {
+            // Compressed oops should only apply to 64bit platforms
+            testCompressedOops("-XX:+UseCompressedOops", "-Xmx32m")
+                .shouldContain("Unrecognized VM option 'UseCompressedOops'")
+                .shouldHaveExitValue(1);
+        }
+    }
+
+    private static OutputAnalyzer testCompressedOops(String... flags) throws Exception {
+        ArrayList<String> args = new ArrayList<>();
+
+        // Always run with these three:
+        args.add("-XX:+UnlockDiagnosticVMOptions");
+        args.add("-XX:+PrintCompressedOopsMode");
+        args.add("-Xms32m");
+
+        // Add the extra flags
+        Collections.addAll(args, flags);
+
+        args.add("-version");
+
+        ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(args.toArray(new String[0]));
+        return new OutputAnalyzer(pb.start());
+    }
+}
--- a/hotspot/test/testlibrary/com/oracle/java/testlibrary/OutputAnalyzer.java	Thu Sep 11 10:46:32 2014 -0400
+++ b/hotspot/test/testlibrary/com/oracle/java/testlibrary/OutputAnalyzer.java	Thu Sep 11 17:54:22 2014 -0700
@@ -74,11 +74,12 @@
    * @param expectedString String that buffer should contain
    * @throws RuntimeException If the string was not found
    */
-  public void shouldContain(String expectedString) {
+  public OutputAnalyzer shouldContain(String expectedString) {
     if (!stdout.contains(expectedString) && !stderr.contains(expectedString)) {
         reportDiagnosticSummary();
         throw new RuntimeException("'" + expectedString + "' missing from stdout/stderr \n");
     }
+    return this;
   }
 
   /**
@@ -87,11 +88,12 @@
    * @param expectedString String that buffer should contain
    * @throws RuntimeException If the string was not found
    */
-  public void stdoutShouldContain(String expectedString) {
+  public OutputAnalyzer stdoutShouldContain(String expectedString) {
     if (!stdout.contains(expectedString)) {
         reportDiagnosticSummary();
         throw new RuntimeException("'" + expectedString + "' missing from stdout \n");
     }
+    return this;
   }
 
   /**
@@ -100,11 +102,12 @@
    * @param expectedString String that buffer should contain
    * @throws RuntimeException If the string was not found
    */
-  public void stderrShouldContain(String expectedString) {
+  public OutputAnalyzer stderrShouldContain(String expectedString) {
     if (!stderr.contains(expectedString)) {
         reportDiagnosticSummary();
         throw new RuntimeException("'" + expectedString + "' missing from stderr \n");
     }
+    return this;
   }
 
   /**
@@ -113,7 +116,7 @@
    * @param expectedString String that the buffer should not contain
    * @throws RuntimeException If the string was found
    */
-  public void shouldNotContain(String notExpectedString) {
+  public OutputAnalyzer shouldNotContain(String notExpectedString) {
     if (stdout.contains(notExpectedString)) {
         reportDiagnosticSummary();
         throw new RuntimeException("'" + notExpectedString + "' found in stdout \n");
@@ -122,6 +125,7 @@
         reportDiagnosticSummary();
         throw new RuntimeException("'" + notExpectedString + "' found in stderr \n");
     }
+    return this;
   }
 
   /**
@@ -130,11 +134,12 @@
    * @param expectedString String that the buffer should not contain
    * @throws RuntimeException If the string was found
    */
-  public void stdoutShouldNotContain(String notExpectedString) {
+  public OutputAnalyzer stdoutShouldNotContain(String notExpectedString) {
     if (stdout.contains(notExpectedString)) {
         reportDiagnosticSummary();
         throw new RuntimeException("'" + notExpectedString + "' found in stdout \n");
     }
+    return this;
   }
 
   /**
@@ -143,11 +148,12 @@
    * @param expectedString String that the buffer should not contain
    * @throws RuntimeException If the string was found
    */
-  public void stderrShouldNotContain(String notExpectedString) {
+  public OutputAnalyzer stderrShouldNotContain(String notExpectedString) {
     if (stderr.contains(notExpectedString)) {
         reportDiagnosticSummary();
         throw new RuntimeException("'" + notExpectedString + "' found in stderr \n");
     }
+    return this;
   }
 
   /**
@@ -157,7 +163,7 @@
    * @param pattern
    * @throws RuntimeException If the pattern was not found
    */
-  public void shouldMatch(String pattern) {
+  public OutputAnalyzer shouldMatch(String pattern) {
       Matcher stdoutMatcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stdout);
       Matcher stderrMatcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stderr);
       if (!stdoutMatcher.find() && !stderrMatcher.find()) {
@@ -165,6 +171,7 @@
           throw new RuntimeException("'" + pattern
                 + "' missing from stdout/stderr \n");
       }
+      return this;
   }
 
   /**
@@ -174,13 +181,14 @@
    * @param pattern
    * @throws RuntimeException If the pattern was not found
    */
-  public void stdoutShouldMatch(String pattern) {
+  public OutputAnalyzer stdoutShouldMatch(String pattern) {
       Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stdout);
       if (!matcher.find()) {
           reportDiagnosticSummary();
           throw new RuntimeException("'" + pattern
                 + "' missing from stdout \n");
       }
+      return this;
   }
 
   /**
@@ -190,13 +198,14 @@
    * @param pattern
    * @throws RuntimeException If the pattern was not found
    */
-  public void stderrShouldMatch(String pattern) {
+  public OutputAnalyzer stderrShouldMatch(String pattern) {
       Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stderr);
       if (!matcher.find()) {
           reportDiagnosticSummary();
           throw new RuntimeException("'" + pattern
                 + "' missing from stderr \n");
       }
+      return this;
   }
 
   /**
@@ -206,7 +215,7 @@
    * @param pattern
    * @throws RuntimeException If the pattern was found
    */
-  public void shouldNotMatch(String pattern) {
+  public OutputAnalyzer shouldNotMatch(String pattern) {
       Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stdout);
       if (matcher.find()) {
           reportDiagnosticSummary();
@@ -219,6 +228,7 @@
           throw new RuntimeException("'" + pattern
                   + "' found in stderr: '" + matcher.group() + "' \n");
       }
+      return this;
   }
 
   /**
@@ -228,13 +238,14 @@
    * @param pattern
    * @throws RuntimeException If the pattern was found
    */
-  public void stdoutShouldNotMatch(String pattern) {
+  public OutputAnalyzer stdoutShouldNotMatch(String pattern) {
       Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stdout);
       if (matcher.find()) {
           reportDiagnosticSummary();
           throw new RuntimeException("'" + pattern
                   + "' found in stdout \n");
       }
+      return this;
   }
 
   /**
@@ -244,13 +255,14 @@
    * @param pattern
    * @throws RuntimeException If the pattern was found
    */
-  public void stderrShouldNotMatch(String pattern) {
+  public OutputAnalyzer stderrShouldNotMatch(String pattern) {
       Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stderr);
       if (matcher.find()) {
           reportDiagnosticSummary();
           throw new RuntimeException("'" + pattern
                   + "' found in stderr \n");
       }
+      return this;
   }
 
   /**
@@ -290,12 +302,13 @@
    * @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) {
+  public OutputAnalyzer shouldHaveExitValue(int expectedExitValue) {
       if (getExitValue() != expectedExitValue) {
           reportDiagnosticSummary();
           throw new RuntimeException("Expected to get exit value of ["
                   + expectedExitValue + "]\n");
       }
+      return this;
   }