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