1 /* 2 * Copyright (c) 2013, 2018, 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 jdk.test.lib.Asserts; 27 28 import java.io.IOException; 29 import java.io.PrintStream; 30 import java.util.Arrays; 31 import java.util.List; 32 import java.util.stream.Collectors; 33 import java.util.regex.Matcher; 34 import java.util.regex.Pattern; 35 36 public final class OutputAnalyzer { 37 38 private final OutputBuffer buffer; 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 buffer = OutputBuffer.of(process); 48 } 49 50 /** 51 * Create an OutputAnalyzer, a utility class for verifying output 52 * 53 * @param buf String buffer to analyze 54 */ 55 public OutputAnalyzer(String buf) { 56 buffer = OutputBuffer.of(buf, buf); 57 } 58 59 /** 60 * Create an OutputAnalyzer, a utility class for verifying output 61 * 62 * @param stdout stdout buffer to analyze 63 * @param stderr stderr buffer to analyze 64 */ 65 public OutputAnalyzer(String stdout, String stderr) { 66 buffer = OutputBuffer.of(stdout, stderr); 67 } 68 69 /** 70 * Verify that the stdout contents of output buffer is empty 71 * 72 * @throws RuntimeException 73 * If stdout was not empty 74 */ 75 public void stdoutShouldBeEmpty() { 76 if (!getStdout().isEmpty()) { 77 reportDiagnosticSummary(); 78 throw new RuntimeException("stdout was not empty"); 79 } 80 } 81 82 /** 83 * Verify that the stderr contents of output buffer is empty 84 * 85 * @throws RuntimeException 86 * If stderr was not empty 87 */ 88 public void stderrShouldBeEmpty() { 89 if (!getStderr().isEmpty()) { 90 reportDiagnosticSummary(); 91 throw new RuntimeException("stderr was not empty"); 92 } 93 } 94 95 /** 96 * Verify that the stderr contents of output buffer is empty, 97 * after filtering out the Hotspot warning messages 98 * 99 * @throws RuntimeException 100 * If stderr was not empty 101 */ 102 public void stderrShouldBeEmptyIgnoreVMWarnings() { 103 if (!getStderr().replaceAll(jvmwarningmsg + "\\R", "").isEmpty()) { 104 reportDiagnosticSummary(); 105 throw new RuntimeException("stderr was not empty"); 106 } 107 } 108 109 /** 110 * Verify that the stdout contents of output buffer is not empty 111 * 112 * @throws RuntimeException 113 * If stdout was empty 114 */ 115 public void stdoutShouldNotBeEmpty() { 116 if (getStdout().isEmpty()) { 117 reportDiagnosticSummary(); 118 throw new RuntimeException("stdout was empty"); 119 } 120 } 121 122 /** 123 * Verify that the stderr contents of output buffer is not empty 124 * 125 * @throws RuntimeException 126 * If stderr was empty 127 */ 128 public void stderrShouldNotBeEmpty() { 129 if (getStderr().isEmpty()) { 130 reportDiagnosticSummary(); 131 throw new RuntimeException("stderr was empty"); 132 } 133 } 134 135 /** 136 * Verify that the stdout and stderr contents of output buffer contains the string 137 * 138 * @param expectedString String that buffer should contain 139 * @throws RuntimeException If the string was not found 140 */ 141 public OutputAnalyzer shouldContain(String expectedString) { 142 String stdout = getStdout(); 143 String stderr = getStderr(); 144 if (!stdout.contains(expectedString) && !stderr.contains(expectedString)) { 145 reportDiagnosticSummary(); 146 throw new RuntimeException("'" + expectedString + "' missing from stdout/stderr \n"); 147 } 148 return this; 149 } 150 151 /** 152 * Verify that the stdout contents of output buffer contains the string 153 * 154 * @param expectedString String that buffer should contain 155 * @throws RuntimeException If the string was not found 156 */ 157 public OutputAnalyzer stdoutShouldContain(String expectedString) { 158 String stdout = getStdout(); 159 if (!stdout.contains(expectedString)) { 160 reportDiagnosticSummary(); 161 throw new RuntimeException("'" + expectedString + "' missing from stdout \n"); 162 } 163 return this; 164 } 165 166 /** 167 * Verify that the stderr contents of output buffer contains the string 168 * 169 * @param expectedString String that buffer should contain 170 * @throws RuntimeException If the string was not found 171 */ 172 public OutputAnalyzer stderrShouldContain(String expectedString) { 173 String stderr = getStderr(); 174 if (!stderr.contains(expectedString)) { 175 reportDiagnosticSummary(); 176 throw new RuntimeException("'" + expectedString + "' missing from stderr \n"); 177 } 178 return this; 179 } 180 181 /** 182 * Verify that the stdout and stderr contents of output buffer does not contain the string 183 * 184 * @param notExpectedString String that the buffer should not contain 185 * @throws RuntimeException If the string was found 186 */ 187 public OutputAnalyzer shouldNotContain(String notExpectedString) { 188 String stdout = getStdout(); 189 String stderr = getStderr(); 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 and stderr contents of output buffer are empty 203 * 204 * @throws RuntimeException If the stdout and stderr are not empty 205 */ 206 public OutputAnalyzer shouldBeEmpty() { 207 String stdout = getStdout(); 208 String stderr = getStderr(); 209 if (!stdout.isEmpty()) { 210 reportDiagnosticSummary(); 211 throw new RuntimeException("stdout was not empty"); 212 } 213 if (!stderr.isEmpty()) { 214 reportDiagnosticSummary(); 215 throw new RuntimeException("stderr was not empty"); 216 } 217 return this; 218 } 219 220 /** 221 * Verify that the stdout contents of output buffer does not contain the string 222 * 223 * @param notExpectedString String that the buffer should not contain 224 * @throws RuntimeException If the string was found 225 */ 226 public OutputAnalyzer stdoutShouldNotContain(String notExpectedString) { 227 String stdout = getStdout(); 228 if (stdout.contains(notExpectedString)) { 229 reportDiagnosticSummary(); 230 throw new RuntimeException("'" + notExpectedString + "' found in stdout \n"); 231 } 232 return this; 233 } 234 235 /** 236 * Verify that the stderr contents of output buffer does not contain the string 237 * 238 * @param notExpectedString String that the buffer should not contain 239 * @throws RuntimeException If the string was found 240 */ 241 public OutputAnalyzer stderrShouldNotContain(String notExpectedString) { 242 String stderr = getStderr(); 243 if (stderr.contains(notExpectedString)) { 244 reportDiagnosticSummary(); 245 throw new RuntimeException("'" + notExpectedString + "' found in stderr \n"); 246 } 247 return this; 248 } 249 250 /** 251 * Verify that the stdout and stderr contents of output buffer matches 252 * the pattern 253 * 254 * @param regexp 255 * @throws RuntimeException If the pattern was not found 256 */ 257 public OutputAnalyzer shouldMatch(String regexp) { 258 String stdout = getStdout(); 259 String stderr = getStderr(); 260 Pattern pattern = Pattern.compile(regexp, Pattern.MULTILINE); 261 Matcher stdoutMatcher = pattern.matcher(stdout); 262 Matcher stderrMatcher = pattern.matcher(stderr); 263 if (!stdoutMatcher.find() && !stderrMatcher.find()) { 264 reportDiagnosticSummary(); 265 throw new RuntimeException("'" + regexp 266 + "' missing from stdout/stderr \n"); 267 } 268 return this; 269 } 270 271 /** 272 * Verify that the stdout contents of output buffer matches the 273 * pattern 274 * 275 * @param regexp 276 * @throws RuntimeException If the pattern was not found 277 */ 278 public OutputAnalyzer stdoutShouldMatch(String regexp) { 279 String stdout = getStdout(); 280 Matcher matcher = Pattern.compile(regexp, Pattern.MULTILINE).matcher(stdout); 281 if (!matcher.find()) { 282 reportDiagnosticSummary(); 283 throw new RuntimeException("'" + regexp 284 + "' missing from stdout \n"); 285 } 286 return this; 287 } 288 289 /** 290 * Verify that the stderr contents of output buffer matches the 291 * pattern 292 * 293 * @param pattern 294 * @throws RuntimeException If the pattern was not found 295 */ 296 public OutputAnalyzer stderrShouldMatch(String pattern) { 297 String stderr = getStderr(); 298 Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stderr); 299 if (!matcher.find()) { 300 reportDiagnosticSummary(); 301 throw new RuntimeException("'" + pattern 302 + "' missing from stderr \n"); 303 } 304 return this; 305 } 306 307 /** 308 * Verify that the stdout and stderr contents of output buffer does not 309 * match the pattern 310 * 311 * @param regexp 312 * @throws RuntimeException If the pattern was found 313 */ 314 public OutputAnalyzer shouldNotMatch(String regexp) { 315 String stdout = getStdout(); 316 Pattern pattern = Pattern.compile(regexp, Pattern.MULTILINE); 317 Matcher matcher = pattern.matcher(stdout); 318 if (matcher.find()) { 319 reportDiagnosticSummary(); 320 throw new RuntimeException("'" + regexp 321 + "' found in stdout: '" + matcher.group() + "' \n"); 322 } 323 324 String stderr = getStderr(); 325 matcher = pattern.matcher(stderr); 326 if (matcher.find()) { 327 reportDiagnosticSummary(); 328 throw new RuntimeException("'" + regexp 329 + "' found in stderr: '" + matcher.group() + "' \n"); 330 } 331 332 return this; 333 } 334 335 /** 336 * Verify that the stdout contents of output buffer does not match the 337 * pattern 338 * 339 * @param regexp 340 * @throws RuntimeException If the pattern was found 341 */ 342 public OutputAnalyzer stdoutShouldNotMatch(String regexp) { 343 String stdout = getStdout(); 344 Matcher matcher = Pattern.compile(regexp, Pattern.MULTILINE).matcher(stdout); 345 if (matcher.find()) { 346 reportDiagnosticSummary(); 347 throw new RuntimeException("'" + regexp 348 + "' found in stdout \n"); 349 } 350 return this; 351 } 352 353 /** 354 * Verify that the stderr contents of output buffer does not match the 355 * pattern 356 * 357 * @param regexp 358 * @throws RuntimeException If the pattern was found 359 */ 360 public OutputAnalyzer stderrShouldNotMatch(String regexp) { 361 String stderr = getStderr(); 362 Matcher matcher = Pattern.compile(regexp, Pattern.MULTILINE).matcher(stderr); 363 if (matcher.find()) { 364 reportDiagnosticSummary(); 365 throw new RuntimeException("'" + regexp 366 + "' found in stderr \n"); 367 } 368 return this; 369 } 370 371 /** 372 * Get the captured group of the first string matching the pattern. 373 * stderr is searched before stdout. 374 * 375 * @param regexp The multi-line pattern to match 376 * @param group The group to capture 377 * @return The matched string or null if no match was found 378 */ 379 public String firstMatch(String regexp, int group) { 380 Pattern pattern = Pattern.compile(regexp, Pattern.MULTILINE); 381 String stderr = getStderr(); 382 Matcher stderrMatcher = pattern.matcher(stderr); 383 if (stderrMatcher.find()) { 384 return stderrMatcher.group(group); 385 } 386 String stdout = getStdout(); 387 Matcher stdoutMatcher = pattern.matcher(stdout); 388 if (stdoutMatcher.find()) { 389 return stdoutMatcher.group(group); 390 } 391 return null; 392 } 393 394 /** 395 * Get the first string matching the pattern. 396 * stderr is searched before stdout. 397 * 398 * @param pattern The multi-line pattern to match 399 * @return The matched string or null if no match was found 400 */ 401 public String firstMatch(String pattern) { 402 return firstMatch(pattern, 0); 403 } 404 405 /** 406 * Verify the exit value of the process 407 * 408 * @param expectedExitValue Expected exit value from process 409 * @throws RuntimeException If the exit value from the process did not match the expected value 410 */ 411 public OutputAnalyzer shouldHaveExitValue(int expectedExitValue) { 412 if (getExitValue() != expectedExitValue) { 413 reportDiagnosticSummary(); 414 throw new RuntimeException("Expected to get exit value of [" 415 + expectedExitValue + "]\n"); 416 } 417 return this; 418 } 419 420 /** 421 * Verify the exit value of the process 422 * 423 * @param notExpectedExitValue Unexpected exit value from process 424 * @throws RuntimeException If the exit value from the process did match the expected value 425 */ 426 public OutputAnalyzer shouldNotHaveExitValue(int notExpectedExitValue) { 427 if (getExitValue() == notExpectedExitValue) { 428 reportDiagnosticSummary(); 429 throw new RuntimeException("Unexpected to get exit value of [" 430 + notExpectedExitValue + "]\n"); 431 } 432 return this; 433 } 434 435 436 /** 437 * Report summary that will help to diagnose the problem 438 * Currently includes: 439 * - standard input produced by the process under test 440 * - standard output 441 * - exit code 442 * Note: the command line is printed by the ProcessTools 443 */ 444 public void reportDiagnosticSummary() { 445 String msg = 446 " stdout: [" + getStdout() + "];\n" + 447 " stderr: [" + getStderr() + "]\n" + 448 " exitValue = " + getExitValue() + "\n"; 449 450 System.err.println(msg); 451 } 452 453 /** 454 * Print the stdout buffer to the given {@code PrintStream}. 455 * 456 * @return this OutputAnalyzer 457 */ 458 public OutputAnalyzer outputTo(PrintStream out) { 459 out.println(getStdout()); 460 return this; 461 } 462 463 /** 464 * Print the stderr buffer to the given {@code PrintStream}. 465 * 466 * @return this OutputAnalyzer 467 */ 468 public OutputAnalyzer errorTo(PrintStream out) { 469 out.println(getStderr()); 470 return this; 471 } 472 473 /** 474 * Get the contents of the output buffer (stdout and stderr) 475 * 476 * @return Content of the output buffer 477 */ 478 public String getOutput() { 479 return getStdout() + getStderr(); 480 } 481 482 /** 483 * Get the contents of the stdout buffer 484 * 485 * @return Content of the stdout buffer 486 */ 487 public String getStdout() { 488 return buffer.getStdout(); 489 } 490 491 /** 492 * Get the contents of the stderr buffer 493 * 494 * @return Content of the stderr buffer 495 */ 496 public String getStderr() { 497 return buffer.getStderr(); 498 } 499 500 /** 501 * Get the process exit value 502 * 503 * @return Process exit value 504 */ 505 public int getExitValue() { 506 return buffer.getExitValue(); 507 } 508 509 /** 510 * Get the contents of the output buffer (stdout and stderr) as list of strings. 511 * Output will be split by newlines. 512 * 513 * @return Contents of the output buffer as list of strings 514 */ 515 public List<String> asLines() { 516 return asLines(getOutput()); 517 } 518 519 private List<String> asLines(String buffer) { 520 return Arrays.asList(buffer.split("\\R")); 521 } 522 523 524 private static final String jvmwarningmsg = ".* VM warning:.*"; 525 526 /** 527 * Verifies that the stdout and stderr contents of output buffer are empty, after 528 * filtering out the HotSpot warning messages. 529 * 530 * @throws RuntimeException If the stdout and stderr are not empty 531 */ 532 public OutputAnalyzer shouldBeEmptyIgnoreVMWarnings() { 533 String stdout = getStdout(); 534 String stderr = getStderr(); 535 if (!stdout.isEmpty()) { 536 reportDiagnosticSummary(); 537 throw new RuntimeException("stdout was not empty"); 538 } 539 if (!stderr.replaceAll(jvmwarningmsg + "\\R", "").isEmpty()) { 540 reportDiagnosticSummary(); 541 throw new RuntimeException("stderr was not empty"); 542 } 543 return this; 544 } 545 546 /** 547 * Verify that the stderr contents of output buffer matches the pattern, 548 * after filtering out the Hotespot warning messages 549 * 550 * @param pattern 551 * @throws RuntimeException If the pattern was not found 552 */ 553 public OutputAnalyzer stderrShouldMatchIgnoreVMWarnings(String pattern) { 554 String stderr = getStderr().replaceAll(jvmwarningmsg + "\\R", ""); 555 Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stderr); 556 if (!matcher.find()) { 557 reportDiagnosticSummary(); 558 throw new RuntimeException("'" + pattern 559 + "' missing from stderr \n"); 560 } 561 return this; 562 } 563 564 /** 565 * Returns the contents of the output buffer (stdout and stderr), without those 566 * JVM warning msgs, as list of strings. Output is split by newlines. 567 * 568 * @return Contents of the output buffer as list of strings 569 */ 570 public List<String> asLinesWithoutVMWarnings() { 571 return Arrays.stream(getOutput().split("\\R")) 572 .filter(Pattern.compile(jvmwarningmsg).asPredicate().negate()) 573 .collect(Collectors.toList()); 574 } 575 576 /** 577 * @see #shouldMatchByLine(String, String, String) 578 */ 579 public OutputAnalyzer shouldMatchByLine(String pattern) { 580 return shouldMatchByLine(null, null, pattern); 581 } 582 583 /** 584 * @see #stdoutShouldMatchByLine(String, String, String) 585 */ 586 public OutputAnalyzer stdoutShouldMatchByLine(String pattern) { 587 return stdoutShouldMatchByLine(null, null, pattern); 588 } 589 590 /** 591 * @see #shouldMatchByLine(String, String, String) 592 */ 593 public OutputAnalyzer shouldMatchByLineFrom(String from, String pattern) { 594 return shouldMatchByLine(from, null, pattern); 595 } 596 597 /** 598 * @see #shouldMatchByLine(String, String, String) 599 */ 600 public OutputAnalyzer shouldMatchByLineTo(String to, String pattern) { 601 return shouldMatchByLine(null, to, pattern); 602 } 603 604 /** 605 * Verify that the stdout and stderr contents of output buffer match the 606 * {@code pattern} line by line. The whole output could be matched or 607 * just a subset of it. 608 * 609 * @param from 610 * The line from where output will be matched. 611 * Set {@code from} to null for matching from the first line. 612 * @param to 613 * The line until where output will be matched. 614 * Set {@code to} to null for matching until the last line. 615 * @param pattern 616 * Matching pattern 617 */ 618 public OutputAnalyzer shouldMatchByLine(String from, String to, String pattern) { 619 return shouldMatchByLine(getOutput(), from, to, pattern); 620 } 621 622 /** 623 * Verify that the stdout contents of output buffer matches the 624 * {@code pattern} line by line. The whole stdout could be matched or 625 * just a subset of it. 626 * 627 * @param from 628 * The line from where stdout will be matched. 629 * Set {@code from} to null for matching from the first line. 630 * @param to 631 * The line until where stdout will be matched. 632 * Set {@code to} to null for matching until the last line. 633 * @param pattern 634 * Matching pattern 635 */ 636 public OutputAnalyzer stdoutShouldMatchByLine(String from, String to, String pattern) { 637 return shouldMatchByLine(getStdout(), from, to, pattern); 638 } 639 640 private OutputAnalyzer shouldMatchByLine(String buffer, String from, String to, String pattern) { 641 List<String> lines = asLines(buffer); 642 643 int fromIndex = 0; 644 if (from != null) { 645 fromIndex = indexOf(lines, from); 646 Asserts.assertGreaterThan(fromIndex, -1, 647 "The line/pattern '" + from + "' from where the output should match can not be found"); 648 } 649 650 int toIndex = lines.size(); 651 if (to != null) { 652 toIndex = indexOf(lines, to); 653 Asserts.assertGreaterThan(toIndex, -1, 654 "The line/pattern '" + to + "' until where the output should match can not be found"); 655 } 656 657 List<String> subList = lines.subList(fromIndex, toIndex); 658 Asserts.assertFalse(subList.isEmpty(), "There is no lines to check"); 659 660 subList.stream() 661 .filter(Pattern.compile(pattern).asPredicate().negate()) 662 .findAny() 663 .ifPresent(line -> Asserts.assertTrue(false, 664 "The line '" + line + "' does not match pattern '" + pattern + "'")); 665 666 return this; 667 } 668 669 /** 670 * Check if there is a line matching {@code regexp} and return its index 671 * 672 * @param regexp Matching pattern 673 * @return Index of first matching line 674 */ 675 private int indexOf(List<String> lines, String regexp) { 676 Pattern pattern = Pattern.compile(regexp); 677 for (int i = 0; i < lines.size(); i++) { 678 if (pattern.matcher(lines.get(i)).matches()) { 679 return i; 680 } 681 } 682 return -1; 683 } 684 685 }