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 java.io.IOException; 27 import java.io.PrintStream; 28 import java.util.Arrays; 29 import java.util.List; 30 import java.util.stream.Collectors; 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 * 57 * @param buf String buffer to analyze 58 */ 59 public OutputAnalyzer(String buf) { 60 this(buf, buf); 61 } 62 63 /** 64 * Create an OutputAnalyzer, a utility class for verifying output 65 * 66 * @param stdout stdout buffer to analyze 67 * @param stderr stderr buffer to analyze 68 */ 69 public OutputAnalyzer(String stdout, String stderr) { 70 this.stdout = stdout; 71 this.stderr = stderr; 72 exitValue = -1; 73 } 74 75 /** 76 * Verify that the stdout contents of output buffer is empty 77 * 78 * @throws RuntimeException 79 * If stdout was not empty 80 */ 81 public void stdoutShouldBeEmpty() { 82 if (!getStdout().isEmpty()) { 83 reportDiagnosticSummary(); 84 throw new RuntimeException("stdout was not empty"); 85 } 86 } 87 88 /** 89 * Verify that the stderr contents of output buffer is empty 90 * 91 * @throws RuntimeException 92 * If stderr was not empty 93 */ 94 public void stderrShouldBeEmpty() { 95 if (!getStderr().isEmpty()) { 96 reportDiagnosticSummary(); 97 throw new RuntimeException("stderr was not empty"); 98 } 99 } 100 101 /** 102 * Verify that the stderr contents of output buffer is empty, 103 * after filtering out the Hotspot warning messages 104 * 105 * @throws RuntimeException 106 * If stderr was not empty 107 */ 108 public void stderrShouldBeEmptyIgnoreVMWarnings() { 109 if (!getStderr().replaceAll(jvmwarningmsg + "\\R", "").isEmpty()) { 110 reportDiagnosticSummary(); 111 throw new RuntimeException("stderr was not empty"); 112 } 113 } 114 115 /** 116 * Verify that the stdout contents of output buffer is not empty 117 * 118 * @throws RuntimeException 119 * If stdout was empty 120 */ 121 public void stdoutShouldNotBeEmpty() { 122 if (getStdout().isEmpty()) { 123 reportDiagnosticSummary(); 124 throw new RuntimeException("stdout was empty"); 125 } 126 } 127 128 /** 129 * Verify that the stderr contents of output buffer is not empty 130 * 131 * @throws RuntimeException 132 * If stderr was empty 133 */ 134 public void stderrShouldNotBeEmpty() { 135 if (getStderr().isEmpty()) { 136 reportDiagnosticSummary(); 137 throw new RuntimeException("stderr was empty"); 138 } 139 } 140 141 /** 142 * Verify that the stdout and stderr contents of output buffer contains the string 143 * 144 * @param expectedString String that buffer should contain 145 * @throws RuntimeException If the string was not found 146 */ 147 public OutputAnalyzer shouldContain(String expectedString) { 148 if (!stdout.contains(expectedString) && !stderr.contains(expectedString)) { 149 reportDiagnosticSummary(); 150 throw new RuntimeException("'" + expectedString + "' missing from stdout/stderr \n"); 151 } 152 return this; 153 } 154 155 /** 156 * Verify that the stdout contents of output buffer contains the string 157 * 158 * @param expectedString String that buffer should contain 159 * @throws RuntimeException If the string was not found 160 */ 161 public OutputAnalyzer stdoutShouldContain(String expectedString) { 162 if (!stdout.contains(expectedString)) { 163 reportDiagnosticSummary(); 164 throw new RuntimeException("'" + expectedString + "' missing from stdout \n"); 165 } 166 return this; 167 } 168 169 /** 170 * Verify that the stderr contents of output buffer contains the string 171 * 172 * @param expectedString String that buffer should contain 173 * @throws RuntimeException If the string was not found 174 */ 175 public OutputAnalyzer stderrShouldContain(String expectedString) { 176 if (!stderr.contains(expectedString)) { 177 reportDiagnosticSummary(); 178 throw new RuntimeException("'" + expectedString + "' missing from stderr \n"); 179 } 180 return this; 181 } 182 183 /** 184 * Verify that the stdout and stderr contents of output buffer does not contain the string 185 * 186 * @param expectedString String that the buffer should not contain 187 * @throws RuntimeException If the string was found 188 */ 189 public OutputAnalyzer shouldNotContain(String notExpectedString) { 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 if (!stdout.isEmpty()) { 208 reportDiagnosticSummary(); 209 throw new RuntimeException("stdout was not empty"); 210 } 211 if (!stderr.isEmpty()) { 212 reportDiagnosticSummary(); 213 throw new RuntimeException("stderr was not empty"); 214 } 215 return this; 216 } 217 218 /** 219 * Verify that the stdout contents of output buffer does not contain the string 220 * 221 * @param expectedString String that the buffer should not contain 222 * @throws RuntimeException If the string was found 223 */ 224 public OutputAnalyzer stdoutShouldNotContain(String notExpectedString) { 225 if (stdout.contains(notExpectedString)) { 226 reportDiagnosticSummary(); 227 throw new RuntimeException("'" + notExpectedString + "' found in stdout \n"); 228 } 229 return this; 230 } 231 232 /** 233 * Verify that the stderr contents of output buffer does not contain the string 234 * 235 * @param expectedString String that the buffer should not contain 236 * @throws RuntimeException If the string was found 237 */ 238 public OutputAnalyzer stderrShouldNotContain(String notExpectedString) { 239 if (stderr.contains(notExpectedString)) { 240 reportDiagnosticSummary(); 241 throw new RuntimeException("'" + notExpectedString + "' found in stderr \n"); 242 } 243 return this; 244 } 245 246 /** 247 * Verify that the stdout and stderr contents of output buffer matches 248 * the pattern 249 * 250 * @param pattern 251 * @throws RuntimeException If the pattern was not found 252 */ 253 public OutputAnalyzer shouldMatch(String pattern) { 254 Matcher stdoutMatcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stdout); 255 Matcher stderrMatcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stderr); 256 if (!stdoutMatcher.find() && !stderrMatcher.find()) { 257 reportDiagnosticSummary(); 258 throw new RuntimeException("'" + pattern 259 + "' missing from stdout/stderr \n"); 260 } 261 return this; 262 } 263 264 /** 265 * Verify that the stdout contents of output buffer matches the 266 * pattern 267 * 268 * @param pattern 269 * @throws RuntimeException If the pattern was not found 270 */ 271 public OutputAnalyzer stdoutShouldMatch(String pattern) { 272 Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stdout); 273 if (!matcher.find()) { 274 reportDiagnosticSummary(); 275 throw new RuntimeException("'" + pattern 276 + "' missing from stdout \n"); 277 } 278 return this; 279 } 280 281 /** 282 * Verify that the stderr contents of output buffer matches the 283 * pattern 284 * 285 * @param pattern 286 * @throws RuntimeException If the pattern was not found 287 */ 288 public OutputAnalyzer stderrShouldMatch(String pattern) { 289 290 Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stderr); 291 if (!matcher.find()) { 292 reportDiagnosticSummary(); 293 throw new RuntimeException("'" + pattern 294 + "' missing from stderr \n"); 295 } 296 return this; 297 } 298 299 /** 300 * Verify that the stdout and stderr contents of output buffer does not 301 * match the pattern 302 * 303 * @param pattern 304 * @throws RuntimeException If the pattern was found 305 */ 306 public OutputAnalyzer shouldNotMatch(String pattern) { 307 Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stdout); 308 if (matcher.find()) { 309 reportDiagnosticSummary(); 310 throw new RuntimeException("'" + pattern 311 + "' found in stdout: '" + matcher.group() + "' \n"); 312 } 313 matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stderr); 314 if (matcher.find()) { 315 reportDiagnosticSummary(); 316 throw new RuntimeException("'" + pattern 317 + "' found in stderr: '" + matcher.group() + "' \n"); 318 } 319 return this; 320 } 321 322 /** 323 * Verify that the stdout contents of output buffer does not match the 324 * pattern 325 * 326 * @param pattern 327 * @throws RuntimeException If the pattern was found 328 */ 329 public OutputAnalyzer stdoutShouldNotMatch(String pattern) { 330 Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stdout); 331 if (matcher.find()) { 332 reportDiagnosticSummary(); 333 throw new RuntimeException("'" + pattern 334 + "' found in stdout \n"); 335 } 336 return this; 337 } 338 339 /** 340 * Verify that the stderr contents of output buffer does not match the 341 * pattern 342 * 343 * @param pattern 344 * @throws RuntimeException If the pattern was found 345 */ 346 public OutputAnalyzer stderrShouldNotMatch(String pattern) { 347 Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stderr); 348 if (matcher.find()) { 349 reportDiagnosticSummary(); 350 throw new RuntimeException("'" + pattern 351 + "' found in stderr \n"); 352 } 353 return this; 354 } 355 356 /** 357 * Get the captured group of the first string matching the pattern. 358 * stderr is searched before stdout. 359 * 360 * @param pattern The multi-line pattern to match 361 * @param group The group to capture 362 * @return The matched string or null if no match was found 363 */ 364 public String firstMatch(String pattern, int group) { 365 Matcher stderrMatcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stderr); 366 Matcher stdoutMatcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stdout); 367 if (stderrMatcher.find()) { 368 return stderrMatcher.group(group); 369 } 370 if (stdoutMatcher.find()) { 371 return stdoutMatcher.group(group); 372 } 373 return null; 374 } 375 376 /** 377 * Get the first string matching the pattern. 378 * stderr is searched before stdout. 379 * 380 * @param pattern The multi-line pattern to match 381 * @return The matched string or null if no match was found 382 */ 383 public String firstMatch(String pattern) { 384 return firstMatch(pattern, 0); 385 } 386 387 /** 388 * Verify the exit value of the process 389 * 390 * @param expectedExitValue Expected exit value from process 391 * @throws RuntimeException If the exit value from the process did not match the expected value 392 */ 393 public OutputAnalyzer shouldHaveExitValue(int expectedExitValue) { 394 if (getExitValue() != expectedExitValue) { 395 reportDiagnosticSummary(); 396 throw new RuntimeException("Expected to get exit value of [" 397 + expectedExitValue + "]\n"); 398 } 399 return this; 400 } 401 402 /** 403 * Verify the exit value of the process 404 * 405 * @param notExpectedExitValue Unexpected exit value from process 406 * @throws RuntimeException If the exit value from the process did match the expected value 407 */ 408 public OutputAnalyzer shouldNotHaveExitValue(int notExpectedExitValue) { 409 if (getExitValue() == notExpectedExitValue) { 410 reportDiagnosticSummary(); 411 throw new RuntimeException("Unexpected to get exit value of [" 412 + notExpectedExitValue + "]\n"); 413 } 414 return this; 415 } 416 417 418 /** 419 * Report summary that will help to diagnose the problem 420 * Currently includes: 421 * - standard input produced by the process under test 422 * - standard output 423 * - exit code 424 * Note: the command line is printed by the ProcessTools 425 */ 426 public void reportDiagnosticSummary() { 427 String msg = 428 " stdout: [" + stdout + "];\n" + 429 " stderr: [" + stderr + "]\n" + 430 " exitValue = " + getExitValue() + "\n"; 431 432 System.err.println(msg); 433 } 434 435 /** 436 * Print the stdout buffer to the given {@code PrintStream}. 437 * 438 * @return this OutputAnalyzer 439 */ 440 public OutputAnalyzer outputTo(PrintStream out) { 441 out.println(getStdout()); 442 return this; 443 } 444 445 /** 446 * Print the stderr buffer to the given {@code PrintStream}. 447 * 448 * @return this OutputAnalyzer 449 */ 450 public OutputAnalyzer errorTo(PrintStream out) { 451 out.println(getStderr()); 452 return this; 453 } 454 455 /** 456 * Get the contents of the output buffer (stdout and stderr) 457 * 458 * @return Content of the output buffer 459 */ 460 public String getOutput() { 461 return stdout + stderr; 462 } 463 464 /** 465 * Get the contents of the stdout buffer 466 * 467 * @return Content of the stdout buffer 468 */ 469 public String getStdout() { 470 return stdout; 471 } 472 473 /** 474 * Get the contents of the stderr buffer 475 * 476 * @return Content of the stderr buffer 477 */ 478 public String getStderr() { 479 return stderr; 480 } 481 482 /** 483 * Get the process exit value 484 * 485 * @return Process exit value 486 */ 487 public int getExitValue() { 488 return exitValue; 489 } 490 491 /** 492 * Get the contents of the output buffer (stdout and stderr) as list of strings. 493 * Output will be split by newlines. 494 * 495 * @return Contents of the output buffer as list of strings 496 */ 497 public List<String> asLines() { 498 return asLines(getOutput()); 499 } 500 501 private List<String> asLines(String buffer) { 502 return Arrays.asList(buffer.split("(\\r\\n|\\n|\\r)")); 503 } 504 505 506 private static final String jvmwarningmsg = ".* VM warning:.*"; 507 508 /** 509 * Verifies that the stdout and stderr contents of output buffer are empty, after 510 * filtering out the HotSpot warning messages. 511 * 512 * @throws RuntimeException If the stdout and stderr are not empty 513 */ 514 public OutputAnalyzer shouldBeEmptyIgnoreVMWarnings() { 515 if (!stdout.isEmpty()) { 516 reportDiagnosticSummary(); 517 throw new RuntimeException("stdout was not empty"); 518 } 519 if (!stderr.replaceAll(jvmwarningmsg + "\\R", "").isEmpty()) { 520 reportDiagnosticSummary(); 521 throw new RuntimeException("stderr was not empty"); 522 } 523 return this; 524 } 525 526 /** 527 * Verify that the stderr contents of output buffer matches the pattern, 528 * after filtering out the Hotespot warning messages 529 * 530 * @param pattern 531 * @throws RuntimeException If the pattern was not found 532 */ 533 public OutputAnalyzer stderrShouldMatchIgnoreVMWarnings(String pattern) { 534 String stderr = this.stderr.replaceAll(jvmwarningmsg + "\\R", ""); 535 Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stderr); 536 if (!matcher.find()) { 537 reportDiagnosticSummary(); 538 throw new RuntimeException("'" + pattern 539 + "' missing from stderr \n"); 540 } 541 return this; 542 } 543 544 /** 545 * Returns the contents of the output buffer (stdout and stderr), without those 546 * JVM warning msgs, as list of strings. Output is split by newlines. 547 * 548 * @return Contents of the output buffer as list of strings 549 */ 550 public List<String> asLinesWithoutVMWarnings() { 551 return Arrays.asList(getOutput().split("\\R")) 552 .stream() 553 .filter(Pattern.compile(jvmwarningmsg).asPredicate().negate()) 554 .collect(Collectors.toList()); 555 } 556 557 }