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 String stderr = getStderr(); 317 Pattern pattern = Pattern.compile(regexp, Pattern.MULTILINE); 318 Matcher matcher = pattern.matcher(stdout); 319 if (matcher.find()) { 320 reportDiagnosticSummary(); 321 throw new RuntimeException("'" + regexp 322 + "' found in stdout: '" + matcher.group() + "' \n"); 323 } 324 matcher = pattern.matcher(stderr); 325 if (matcher.find()) { 326 reportDiagnosticSummary(); 327 throw new RuntimeException("'" + regexp 328 + "' found in stderr: '" + matcher.group() + "' \n"); 329 } 330 return this; 331 } 332 333 /** 334 * Verify that the stdout contents of output buffer does not match the 335 * pattern 336 * 337 * @param regexp 338 * @throws RuntimeException If the pattern was found 339 */ 340 public OutputAnalyzer stdoutShouldNotMatch(String regexp) { 341 String stdout = getStdout(); 342 Matcher matcher = Pattern.compile(regexp, Pattern.MULTILINE).matcher(stdout); 343 if (matcher.find()) { 344 reportDiagnosticSummary(); 345 throw new RuntimeException("'" + regexp 346 + "' found in stdout \n"); 347 } 348 return this; 349 } 350 351 /** 352 * Verify that the stderr contents of output buffer does not match the 353 * pattern 354 * 355 * @param regexp 356 * @throws RuntimeException If the pattern was found 357 */ 358 public OutputAnalyzer stderrShouldNotMatch(String regexp) { 359 String stderr = getStderr(); 360 Matcher matcher = Pattern.compile(regexp, Pattern.MULTILINE).matcher(stderr); 361 if (matcher.find()) { 362 reportDiagnosticSummary(); 363 throw new RuntimeException("'" + regexp 364 + "' found in stderr \n"); 365 } 366 return this; 367 } 368 369 /** 370 * Get the captured group of the first string matching the pattern. 371 * stderr is searched before stdout. 372 * 373 * @param regexp The multi-line pattern to match 374 * @param group The group to capture 375 * @return The matched string or null if no match was found 376 */ 377 public String firstMatch(String regexp, int group) { 378 String stdout = getStdout(); 379 String stderr = getStderr(); 380 Pattern pattern = Pattern.compile(regexp, Pattern.MULTILINE); 381 Matcher stderrMatcher = pattern.matcher(stderr); 382 Matcher stdoutMatcher = pattern.matcher(stdout); 383 if (stderrMatcher.find()) { 384 return stderrMatcher.group(group); 385 } 386 if (stdoutMatcher.find()) { 387 return stdoutMatcher.group(group); 388 } 389 return null; 390 } 391 392 /** 393 * Get the first string matching the pattern. 394 * stderr is searched before stdout. 395 * 396 * @param pattern The multi-line pattern to match 397 * @return The matched string or null if no match was found 398 */ 399 public String firstMatch(String pattern) { 400 return firstMatch(pattern, 0); 401 } 402 403 /** 404 * Verify the exit value of the process 405 * 406 * @param expectedExitValue Expected exit value from process 407 * @throws RuntimeException If the exit value from the process did not match the expected value 408 */ 409 public OutputAnalyzer shouldHaveExitValue(int expectedExitValue) { 410 if (getExitValue() != expectedExitValue) { 411 reportDiagnosticSummary(); 412 throw new RuntimeException("Expected to get exit value of [" 413 + expectedExitValue + "]\n"); 414 } 415 return this; 416 } 417 418 /** 419 * Verify the exit value of the process 420 * 421 * @param notExpectedExitValue Unexpected exit value from process 422 * @throws RuntimeException If the exit value from the process did match the expected value 423 */ 424 public OutputAnalyzer shouldNotHaveExitValue(int notExpectedExitValue) { 425 if (getExitValue() == notExpectedExitValue) { 426 reportDiagnosticSummary(); 427 throw new RuntimeException("Unexpected to get exit value of [" 428 + notExpectedExitValue + "]\n"); 429 } 430 return this; 431 } 432 433 434 /** 435 * Report summary that will help to diagnose the problem 436 * Currently includes: 437 * - standard input produced by the process under test 438 * - standard output 439 * - exit code 440 * Note: the command line is printed by the ProcessTools 441 */ 442 public void reportDiagnosticSummary() { 443 String msg = 444 " stdout: [" + getStdout() + "];\n" + 445 " stderr: [" + getStderr() + "]\n" + 446 " exitValue = " + getExitValue() + "\n"; 447 448 System.err.println(msg); 449 } 450 451 /** 452 * Print the stdout buffer to the given {@code PrintStream}. 453 * 454 * @return this OutputAnalyzer 455 */ 456 public OutputAnalyzer outputTo(PrintStream out) { 457 out.println(getStdout()); 458 return this; 459 } 460 461 /** 462 * Print the stderr buffer to the given {@code PrintStream}. 463 * 464 * @return this OutputAnalyzer 465 */ 466 public OutputAnalyzer errorTo(PrintStream out) { 467 out.println(getStderr()); 468 return this; 469 } 470 471 /** 472 * Get the contents of the output buffer (stdout and stderr) 473 * 474 * @return Content of the output buffer 475 */ 476 public String getOutput() { 477 return getStdout() + getStderr(); 478 } 479 480 /** 481 * Get the contents of the stdout buffer 482 * 483 * @return Content of the stdout buffer 484 */ 485 public String getStdout() { 486 return buffer.getStdout(); 487 } 488 489 /** 490 * Get the contents of the stderr buffer 491 * 492 * @return Content of the stderr buffer 493 */ 494 public String getStderr() { 495 return buffer.getStderr(); 496 } 497 498 /** 499 * Get the process exit value 500 * 501 * @return Process exit value 502 */ 503 public int getExitValue() { 504 return buffer.getExitValue(); 505 } 506 507 /** 508 * Get the contents of the output buffer (stdout and stderr) as list of strings. 509 * Output will be split by newlines. 510 * 511 * @return Contents of the output buffer as list of strings 512 */ 513 public List<String> asLines() { 514 return asLines(getOutput()); 515 } 516 517 private List<String> asLines(String buffer) { 518 return Arrays.asList(buffer.split("(\\r\\n|\\n|\\r)")); 519 } 520 521 522 private static final String jvmwarningmsg = ".* VM warning:.*"; 523 524 /** 525 * Verifies that the stdout and stderr contents of output buffer are empty, after 526 * filtering out the HotSpot warning messages. 527 * 528 * @throws RuntimeException If the stdout and stderr are not empty 529 */ 530 public OutputAnalyzer shouldBeEmptyIgnoreVMWarnings() { 531 String stdout = getStdout(); 532 String stderr = getStderr(); 533 if (!stdout.isEmpty()) { 534 reportDiagnosticSummary(); 535 throw new RuntimeException("stdout was not empty"); 536 } 537 if (!stderr.replaceAll(jvmwarningmsg + "\\R", "").isEmpty()) { 538 reportDiagnosticSummary(); 539 throw new RuntimeException("stderr was not empty"); 540 } 541 return this; 542 } 543 544 /** 545 * Verify that the stderr contents of output buffer matches the pattern, 546 * after filtering out the Hotespot warning messages 547 * 548 * @param pattern 549 * @throws RuntimeException If the pattern was not found 550 */ 551 public OutputAnalyzer stderrShouldMatchIgnoreVMWarnings(String pattern) { 552 String stderr = getStderr().replaceAll(jvmwarningmsg + "\\R", ""); 553 Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stderr); 554 if (!matcher.find()) { 555 reportDiagnosticSummary(); 556 throw new RuntimeException("'" + pattern 557 + "' missing from stderr \n"); 558 } 559 return this; 560 } 561 562 /** 563 * Returns the contents of the output buffer (stdout and stderr), without those 564 * JVM warning msgs, as list of strings. Output is split by newlines. 565 * 566 * @return Contents of the output buffer as list of strings 567 */ 568 public List<String> asLinesWithoutVMWarnings() { 569 return Arrays.stream(getOutput().split("\\R")) 570 .filter(Pattern.compile(jvmwarningmsg).asPredicate().negate()) 571 .collect(Collectors.toList()); 572 } 573 574 575 /** 576 * @see #shouldMatchByLine(String, String, String) 577 */ 578 public int shouldMatchByLine(String pattern) { 579 return shouldMatchByLine(null, null, pattern); 580 } 581 582 /** 583 * @see #stdoutShouldMatchByLine(String, String, String) 584 */ 585 public int stdoutShouldMatchByLine(String pattern) { 586 return stdoutShouldMatchByLine(null, null, pattern); 587 } 588 589 /** 590 * @see #shouldMatchByLine(String, String, String) 591 */ 592 public int shouldMatchByLineFrom(String from, String pattern) { 593 return shouldMatchByLine(from, null, pattern); 594 } 595 596 /** 597 * @see #shouldMatchByLine(String, String, String) 598 */ 599 public int shouldMatchByLineTo(String to, String pattern) { 600 return shouldMatchByLine(null, to, pattern); 601 } 602 603 /** 604 * Verify that the stdout and stderr contents of output buffer match the 605 * {@code pattern} line by line. The whole output could be matched or 606 * just a subset of it. 607 * 608 * @param from 609 * The line from where output will be matched. 610 * Set {@code from} to null for matching from the first line. 611 * @param to 612 * The line until where output will be matched. 613 * Set {@code to} to null for matching until the last line. 614 * @param pattern 615 * Matching pattern 616 * @return Count of lines which match the {@code pattern} 617 */ 618 public int 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 * @return Count of lines which match the {@code pattern} 636 */ 637 public int stdoutShouldMatchByLine(String from, String to, String pattern) { 638 return shouldMatchByLine(getStdout(), from, to, pattern); 639 } 640 641 private int shouldMatchByLine(String buffer, String from, String to, String pattern) { 642 List<String> lines = asLines(buffer); 643 644 int fromIndex = 0; 645 if (from != null) { 646 fromIndex = indexOf(lines, from); 647 Asserts.assertGreaterThan(fromIndex, -1, 648 "The line/pattern '" + from + "' from where the output should match can not be found"); 649 } 650 651 int toIndex = lines.size(); 652 if (to != null) { 653 toIndex = indexOf(lines, to); 654 Asserts.assertGreaterThan(toIndex, -1, 655 "The line/pattern '" + to + "' until where the output should match can not be found"); 656 } 657 658 List<String> subList = lines.subList(fromIndex, toIndex); 659 int matchedCount = 0; 660 for (String line : subList) { 661 Asserts.assertTrue(line.matches(pattern), 662 "The line '" + line + "' does not match pattern '" + pattern + "'"); 663 matchedCount++; 664 } 665 666 return matchedCount; 667 } 668 669 /** 670 * Check if there is a line matching {@code pattern} and return its index 671 * 672 * @param pattern Matching pattern 673 * @return Index of first matching line 674 */ 675 private int indexOf(List<String> lines, String pattern) { 676 for (int i = 0; i < lines.size(); i++) { 677 if (lines.get(i).matches(pattern)) { 678 return i; 679 } 680 } 681 return -1; 682 } 683 684 }