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