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