1 /*
   2  * Copyright (c) 2013, 2016, 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 package jdk.test.lib.process;
  25 
  26 import java.io.IOException;
  27 import java.io.PrintStream;
  28 import java.util.Arrays;
  29 import java.util.List;
  30 import java.util.regex.Matcher;
  31 import java.util.regex.Pattern;
  32 
  33 public final class OutputAnalyzer {
  34 
  35   private final String stdout;
  36   private final String stderr;
  37   private final int exitValue;
  38 
  39   /**
  40    * Create an OutputAnalyzer, a utility class for verifying output and exit
  41    * value from a Process
  42    *
  43    * @param process Process to analyze
  44    * @throws IOException If an I/O error occurs.
  45    */
  46   public OutputAnalyzer(Process process) throws IOException {
  47     OutputBuffer output = ProcessTools.getOutput(process);
  48     exitValue = process.exitValue();
  49     this.stdout = output.getStdout();
  50     this.stderr = output.getStderr();
  51   }
  52 
  53   /**
  54    * Create an OutputAnalyzer, a utility class for verifying output
  55    *
  56    * @param buf String buffer to analyze
  57    */
  58   public OutputAnalyzer(String buf) {
  59     this(buf, buf);
  60   }
  61 
  62   /**
  63    * Create an OutputAnalyzer, a utility class for verifying output
  64    *
  65    * @param stdout stdout buffer to analyze
  66    * @param stderr stderr buffer to analyze
  67    */
  68   public OutputAnalyzer(String stdout, String stderr) {
  69     this.stdout = stdout;
  70     this.stderr = stderr;
  71     exitValue = -1;
  72   }
  73 
  74   /**
  75    * Verify that the stdout contents of output buffer is empty
  76    *
  77    * @throws RuntimeException
  78    *             If stdout was not empty
  79    */
  80   public void stdoutShouldBeEmpty() {
  81     if (!getStdout().isEmpty()) {
  82       reportDiagnosticSummary();
  83       throw new RuntimeException("stdout was not empty");
  84     }
  85   }
  86 
  87   /**
  88    * Verify that the stderr contents of output buffer is empty
  89    *
  90    * @throws RuntimeException
  91    *             If stderr was not empty
  92    */
  93   public void stderrShouldBeEmpty() {
  94     if (!getStderr().isEmpty()) {
  95       reportDiagnosticSummary();
  96       throw new RuntimeException("stderr was not empty");
  97     }
  98   }
  99 
 100   /**
 101    * Verify that the stdout contents of output buffer is not empty
 102    *
 103    * @throws RuntimeException
 104    *             If stdout was empty
 105    */
 106   public void stdoutShouldNotBeEmpty() {
 107     if (getStdout().isEmpty()) {
 108       reportDiagnosticSummary();
 109       throw new RuntimeException("stdout was empty");
 110     }
 111   }
 112 
 113   /**
 114    * Verify that the stderr contents of output buffer is not empty
 115    *
 116    * @throws RuntimeException
 117    *             If stderr was empty
 118    */
 119   public void stderrShouldNotBeEmpty() {
 120     if (getStderr().isEmpty()) {
 121       reportDiagnosticSummary();
 122       throw new RuntimeException("stderr was empty");
 123     }
 124   }
 125 
 126     /**
 127    * Verify that the stdout and stderr contents of output buffer contains the string
 128    *
 129    * @param expectedString String that buffer should contain
 130    * @throws RuntimeException If the string was not found
 131    */
 132   public OutputAnalyzer shouldContain(String expectedString) {
 133     if (!stdout.contains(expectedString) && !stderr.contains(expectedString)) {
 134         reportDiagnosticSummary();
 135         throw new RuntimeException("'" + expectedString + "' missing from stdout/stderr \n");
 136     }
 137     return this;
 138   }
 139 
 140   /**
 141    * Verify that the stdout contents of output buffer contains the string
 142    *
 143    * @param expectedString String that buffer should contain
 144    * @throws RuntimeException If the string was not found
 145    */
 146   public OutputAnalyzer stdoutShouldContain(String expectedString) {
 147     if (!stdout.contains(expectedString)) {
 148         reportDiagnosticSummary();
 149         throw new RuntimeException("'" + expectedString + "' missing from stdout \n");
 150     }
 151     return this;
 152   }
 153 
 154   /**
 155    * Verify that the stderr contents of output buffer contains the string
 156    *
 157    * @param expectedString String that buffer should contain
 158    * @throws RuntimeException If the string was not found
 159    */
 160   public OutputAnalyzer stderrShouldContain(String expectedString) {
 161     if (!stderr.contains(expectedString)) {
 162         reportDiagnosticSummary();
 163         throw new RuntimeException("'" + expectedString + "' missing from stderr \n");
 164     }
 165     return this;
 166   }
 167 
 168   /**
 169    * Verify that the stdout and stderr contents of output buffer does not contain the string
 170    *
 171    * @param expectedString String that the buffer should not contain
 172    * @throws RuntimeException If the string was found
 173    */
 174   public OutputAnalyzer shouldNotContain(String notExpectedString) {
 175     if (stdout.contains(notExpectedString)) {
 176         reportDiagnosticSummary();
 177         throw new RuntimeException("'" + notExpectedString + "' found in stdout \n");
 178     }
 179     if (stderr.contains(notExpectedString)) {
 180         reportDiagnosticSummary();
 181         throw new RuntimeException("'" + notExpectedString + "' found in stderr \n");
 182     }
 183     return this;
 184   }
 185 
 186   /**
 187    * Verify that the stdout and stderr contents of output buffer does not contain the string
 188    *
 189    * @throws RuntimeException If the string was found
 190    */
 191   public OutputAnalyzer shouldBeEmpty() {
 192     if (!stdout.isEmpty()) {
 193         reportDiagnosticSummary();
 194         throw new RuntimeException("stdout was not empty");
 195     }
 196     if (!stderr.isEmpty()) {
 197         reportDiagnosticSummary();
 198         throw new RuntimeException("stderr was not empty");
 199     }
 200     return this;
 201   }
 202 
 203   /**
 204    * Verify that the stdout contents of output buffer does not contain the string
 205    *
 206    * @param expectedString String that the buffer should not contain
 207    * @throws RuntimeException If the string was found
 208    */
 209   public OutputAnalyzer stdoutShouldNotContain(String notExpectedString) {
 210     if (stdout.contains(notExpectedString)) {
 211         reportDiagnosticSummary();
 212         throw new RuntimeException("'" + notExpectedString + "' found in stdout \n");
 213     }
 214     return this;
 215   }
 216 
 217   /**
 218    * Verify that the stderr contents of output buffer does not contain the string
 219    *
 220    * @param expectedString String that the buffer should not contain
 221    * @throws RuntimeException If the string was found
 222    */
 223   public OutputAnalyzer stderrShouldNotContain(String notExpectedString) {
 224     if (stderr.contains(notExpectedString)) {
 225         reportDiagnosticSummary();
 226         throw new RuntimeException("'" + notExpectedString + "' found in stderr \n");
 227     }
 228     return this;
 229   }
 230 
 231   /**
 232    * Verify that the stdout and stderr contents of output buffer matches
 233    * the pattern
 234    *
 235    * @param pattern
 236    * @throws RuntimeException If the pattern was not found
 237    */
 238   public OutputAnalyzer shouldMatch(String pattern) {
 239       Matcher stdoutMatcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stdout);
 240       Matcher stderrMatcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stderr);
 241       if (!stdoutMatcher.find() && !stderrMatcher.find()) {
 242           reportDiagnosticSummary();
 243           throw new RuntimeException("'" + pattern
 244                 + "' missing from stdout/stderr \n");
 245       }
 246       return this;
 247   }
 248 
 249   /**
 250    * Verify that the stdout contents of output buffer matches the
 251    * pattern
 252    *
 253    * @param pattern
 254    * @throws RuntimeException If the pattern was not found
 255    */
 256   public OutputAnalyzer stdoutShouldMatch(String pattern) {
 257       Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stdout);
 258       if (!matcher.find()) {
 259           reportDiagnosticSummary();
 260           throw new RuntimeException("'" + pattern
 261                 + "' missing from stdout \n");
 262       }
 263       return this;
 264   }
 265 
 266   /**
 267    * Verify that the stderr contents of output buffer matches the
 268    * pattern
 269    *
 270    * @param pattern
 271    * @throws RuntimeException If the pattern was not found
 272    */
 273   public OutputAnalyzer stderrShouldMatch(String pattern) {
 274       Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stderr);
 275       if (!matcher.find()) {
 276           reportDiagnosticSummary();
 277           throw new RuntimeException("'" + pattern
 278                 + "' missing from stderr \n");
 279       }
 280       return this;
 281   }
 282 
 283   /**
 284    * Verify that the stdout and stderr contents of output buffer does not
 285    * match the pattern
 286    *
 287    * @param pattern
 288    * @throws RuntimeException If the pattern was found
 289    */
 290   public OutputAnalyzer shouldNotMatch(String pattern) {
 291       Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stdout);
 292       if (matcher.find()) {
 293           reportDiagnosticSummary();
 294           throw new RuntimeException("'" + pattern
 295                   + "' found in stdout: '" + matcher.group() + "' \n");
 296       }
 297       matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stderr);
 298       if (matcher.find()) {
 299           reportDiagnosticSummary();
 300           throw new RuntimeException("'" + pattern
 301                   + "' found in stderr: '" + matcher.group() + "' \n");
 302       }
 303       return this;
 304   }
 305 
 306   /**
 307    * Verify that the stdout contents of output buffer does not match the
 308    * pattern
 309    *
 310    * @param pattern
 311    * @throws RuntimeException If the pattern was found
 312    */
 313   public OutputAnalyzer stdoutShouldNotMatch(String pattern) {
 314       Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stdout);
 315       if (matcher.find()) {
 316           reportDiagnosticSummary();
 317           throw new RuntimeException("'" + pattern
 318                   + "' found in stdout \n");
 319       }
 320       return this;
 321   }
 322 
 323   /**
 324    * Verify that the stderr contents of output buffer does not match the
 325    * pattern
 326    *
 327    * @param pattern
 328    * @throws RuntimeException If the pattern was found
 329    */
 330   public OutputAnalyzer stderrShouldNotMatch(String pattern) {
 331       Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stderr);
 332       if (matcher.find()) {
 333           reportDiagnosticSummary();
 334           throw new RuntimeException("'" + pattern
 335                   + "' found in stderr \n");
 336       }
 337       return this;
 338   }
 339 
 340   /**
 341    * Get the captured group of the first string matching the pattern.
 342    * stderr is searched before stdout.
 343    *
 344    * @param pattern The multi-line pattern to match
 345    * @param group The group to capture
 346    * @return The matched string or null if no match was found
 347    */
 348   public String firstMatch(String pattern, int group) {
 349     Matcher stderrMatcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stderr);
 350     Matcher stdoutMatcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stdout);
 351     if (stderrMatcher.find()) {
 352       return stderrMatcher.group(group);
 353     }
 354     if (stdoutMatcher.find()) {
 355       return stdoutMatcher.group(group);
 356     }
 357     return null;
 358   }
 359 
 360   /**
 361    * Get the first string matching the pattern.
 362    * stderr is searched before stdout.
 363    *
 364    * @param pattern The multi-line pattern to match
 365    * @return The matched string or null if no match was found
 366    */
 367   public String firstMatch(String pattern) {
 368     return firstMatch(pattern, 0);
 369   }
 370 
 371   /**
 372    * Verify the exit value of the process
 373    *
 374    * @param expectedExitValue Expected exit value from process
 375    * @throws RuntimeException If the exit value from the process did not match the expected value
 376    */
 377   public OutputAnalyzer shouldHaveExitValue(int expectedExitValue) {
 378       if (getExitValue() != expectedExitValue) {
 379           reportDiagnosticSummary();
 380           throw new RuntimeException("Expected to get exit value of ["
 381                   + expectedExitValue + "]\n");
 382       }
 383       return this;
 384   }
 385 
 386   /**
 387    * Verify the exit value of the process
 388    *
 389    * @param notExpectedExitValue Unexpected exit value from process
 390    * @throws RuntimeException If the exit value from the process did match the expected value
 391    */
 392   public OutputAnalyzer shouldNotHaveExitValue(int notExpectedExitValue) {
 393       if (getExitValue() == notExpectedExitValue) {
 394           reportDiagnosticSummary();
 395           throw new RuntimeException("Unexpected to get exit value of ["
 396                   + notExpectedExitValue + "]\n");
 397       }
 398       return this;
 399   }
 400 
 401 
 402   /**
 403    * Report summary that will help to diagnose the problem
 404    * Currently includes:
 405    *  - standard input produced by the process under test
 406    *  - standard output
 407    *  - exit code
 408    *  Note: the command line is printed by the ProcessTools
 409    */
 410   public void reportDiagnosticSummary() {
 411       String msg =
 412           " stdout: [" + stdout + "];\n" +
 413           " stderr: [" + stderr + "]\n" +
 414           " exitValue = " + getExitValue() + "\n";
 415 
 416       System.err.println(msg);
 417   }
 418 
 419   /**
 420    * Print the stdout buffer to the given {@code PrintStream}.
 421    *
 422    * @return this OutputAnalyzer
 423    */
 424   public OutputAnalyzer outputTo(PrintStream out) {
 425       out.println(getStdout());
 426       return this;
 427   }
 428 
 429   /**
 430    * Print the stderr buffer to the given {@code PrintStream}.
 431    *
 432    * @return this OutputAnalyzer
 433    */
 434   public OutputAnalyzer errorTo(PrintStream out) {
 435       out.println(getStderr());
 436       return this;
 437   }
 438 
 439   /**
 440    * Get the contents of the output buffer (stdout and stderr)
 441    *
 442    * @return Content of the output buffer
 443    */
 444   public String getOutput() {
 445     return stdout + stderr;
 446   }
 447 
 448   /**
 449    * Get the contents of the stdout buffer
 450    *
 451    * @return Content of the stdout buffer
 452    */
 453   public String getStdout() {
 454     return stdout;
 455   }
 456 
 457   /**
 458    * Get the contents of the stderr buffer
 459    *
 460    * @return Content of the stderr buffer
 461    */
 462   public String getStderr() {
 463     return stderr;
 464   }
 465 
 466   /**
 467    * Get the process exit value
 468    *
 469    * @return Process exit value
 470    */
 471   public int getExitValue() {
 472     return exitValue;
 473   }
 474 
 475   /**
 476    * Get the contents of the output buffer (stdout and stderr) as list of strings.
 477    * Output will be split by newlines.
 478    *
 479    * @return Contents of the output buffer as list of strings
 480    */
 481   public List<String> asLines() {
 482     return asLines(getOutput());
 483   }
 484 
 485   private List<String> asLines(String buffer) {
 486     return Arrays.asList(buffer.split("(\\r\\n|\\n|\\r)"));
 487   }
 488 }