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