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