1 /* 2 * Copyright (c) 2013, 2014, 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.testlibrary; 25 26 import static jdk.testlibrary.Asserts.*; 27 28 import java.io.IOException; 29 import java.util.ArrayList; 30 import java.util.List; 31 import java.util.regex.Matcher; 32 import java.util.regex.Pattern; 33 34 /** 35 * Utility class for verifying output and exit value from a {@code Process}. 36 */ 37 public final class OutputAnalyzer { 38 private final OutputBuffer output; 39 private final String stdout; 40 private final String stderr; 41 private final int exitValue; 42 43 /** 44 * Create an OutputAnalyzer, a utility class for verifying output and exit 45 * value from a Process. 46 * <p> 47 * OutputAnalyzer should never be instantiated directly - 48 * use {@linkplain ProcessTools#executeProcess(p)} instead 49 * 50 * @param process 51 * Process to analyze 52 * @throws IOException 53 * If an I/O error occurs. 54 */ 55 OutputAnalyzer(Process process) throws IOException { 56 output = new OutputBuffer(process); 57 exitValue = -1; 58 this.stdout = null; 59 this.stderr = null; 60 } 61 62 /** 63 * Create an OutputAnalyzer, a utility class for verifying output. 64 * 65 * @param buf 66 * String buffer to analyze 67 */ 68 OutputAnalyzer(String buf) { 69 this(buf, buf); 70 } 71 72 /** 73 * Create an OutputAnalyzer, a utility class for verifying output 74 * 75 * @param stdout 76 * stdout buffer to analyze 77 * @param stderr 78 * stderr buffer to analyze 79 */ 80 OutputAnalyzer(String stdout, String stderr) { 81 this.output = null; 82 this.stdout = stdout; 83 this.stderr = stderr; 84 exitValue = -1; 85 } 86 87 /** 88 * Verify that the stdout and stderr contents of output buffer contains the 89 * string 90 * 91 * @param expectedString 92 * String that buffer should contain 93 * @throws RuntimeException 94 * If the string was not found 95 */ 96 public void shouldContain(String expectedString) { 97 if (!getStdout().contains(expectedString) 98 && !getStderr().contains(expectedString)) { 99 reportDiagnosticSummary(); 100 throw new RuntimeException("'" + expectedString 101 + "' missing from stdout/stderr \n"); 102 } 103 } 104 105 /** 106 * Verify that the stdout contents of output buffer contains the string 107 * 108 * @param expectedString 109 * String that buffer should contain 110 * @throws RuntimeException 111 * If the string was not found 112 */ 113 public void stdoutShouldContain(String expectedString) { 114 if (!getStdout().contains(expectedString)) { 115 reportDiagnosticSummary(); 116 throw new RuntimeException("'" + expectedString 117 + "' missing from stdout \n"); 118 } 119 } 120 121 /** 122 * Verify that the stderr contents of output buffer contains the string 123 * 124 * @param expectedString 125 * String that buffer should contain 126 * @throws RuntimeException 127 * If the string was not found 128 */ 129 public void stderrShouldContain(String expectedString) { 130 if (!getStderr().contains(expectedString)) { 131 reportDiagnosticSummary(); 132 throw new RuntimeException("'" + expectedString 133 + "' missing from stderr \n"); 134 } 135 } 136 137 /** 138 * Verify that the stdout and stderr contents of output buffer does not 139 * contain the string 140 * 141 * @param expectedString 142 * String that the buffer should not contain 143 * @throws RuntimeException 144 * If the string was found 145 */ 146 public void shouldNotContain(String notExpectedString) { 147 if (getStdout().contains(notExpectedString)) { 148 reportDiagnosticSummary(); 149 throw new RuntimeException("'" + notExpectedString 150 + "' found in stdout \n"); 151 } 152 if (getStderr().contains(notExpectedString)) { 153 reportDiagnosticSummary(); 154 throw new RuntimeException("'" + notExpectedString 155 + "' found in stderr \n"); 156 } 157 } 158 159 /** 160 * Verify that the stdout contents of output buffer does not contain the 161 * string 162 * 163 * @param expectedString 164 * String that the buffer should not contain 165 * @throws RuntimeException 166 * If the string was found 167 */ 168 public void stdoutShouldNotContain(String notExpectedString) { 169 if (getStdout().contains(notExpectedString)) { 170 reportDiagnosticSummary(); 171 throw new RuntimeException("'" + notExpectedString 172 + "' found in stdout \n"); 173 } 174 } 175 176 /** 177 * Verify that the stderr contents of output buffer does not contain the 178 * string 179 * 180 * @param expectedString 181 * String that the buffer should not contain 182 * @throws RuntimeException 183 * If the string was found 184 */ 185 public void stderrShouldNotContain(String notExpectedString) { 186 if (getStderr().contains(notExpectedString)) { 187 reportDiagnosticSummary(); 188 throw new RuntimeException("'" + notExpectedString 189 + "' found in stderr \n"); 190 } 191 } 192 193 /** 194 * Verify that the stdout and stderr contents of output buffer matches the 195 * pattern 196 * 197 * @param pattern 198 * @throws RuntimeException 199 * If the pattern was not found 200 */ 201 public void shouldMatch(String pattern) { 202 Matcher stdoutMatcher = Pattern.compile(pattern, Pattern.MULTILINE) 203 .matcher(getStdout()); 204 Matcher stderrMatcher = Pattern.compile(pattern, Pattern.MULTILINE) 205 .matcher(getStderr()); 206 if (!stdoutMatcher.find() && !stderrMatcher.find()) { 207 reportDiagnosticSummary(); 208 throw new RuntimeException("'" + pattern 209 + "' missing from stdout/stderr \n"); 210 } 211 } 212 213 /** 214 * Verify that the stdout contents of output buffer matches the pattern 215 * 216 * @param pattern 217 * @throws RuntimeException 218 * If the pattern was not found 219 */ 220 public void stdoutShouldMatch(String pattern) { 221 Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher( 222 getStdout()); 223 if (!matcher.find()) { 224 reportDiagnosticSummary(); 225 throw new RuntimeException("'" + pattern 226 + "' missing from stdout \n"); 227 } 228 } 229 230 /** 231 * Verify that the stderr contents of output buffer matches the pattern 232 * 233 * @param pattern 234 * @throws RuntimeException 235 * If the pattern was not found 236 */ 237 public void stderrShouldMatch(String pattern) { 238 Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher( 239 getStderr()); 240 if (!matcher.find()) { 241 reportDiagnosticSummary(); 242 throw new RuntimeException("'" + pattern 243 + "' missing from stderr \n"); 244 } 245 } 246 247 /** 248 * Verify that the stdout and stderr contents of output buffer does not 249 * match the pattern 250 * 251 * @param pattern 252 * @throws RuntimeException 253 * If the pattern was found 254 */ 255 public void shouldNotMatch(String pattern) { 256 Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher( 257 getStdout()); 258 if (matcher.find()) { 259 reportDiagnosticSummary(); 260 throw new RuntimeException("'" + pattern + "' found in stdout: '" 261 + matcher.group() + "' \n"); 262 } 263 matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(getStderr()); 264 if (matcher.find()) { 265 reportDiagnosticSummary(); 266 throw new RuntimeException("'" + pattern + "' found in stderr: '" 267 + matcher.group() + "' \n"); 268 } 269 } 270 271 /** 272 * Verify that the stdout contents of output buffer does not match the 273 * pattern 274 * 275 * @param pattern 276 * @throws RuntimeException 277 * If the pattern was found 278 */ 279 public void stdoutShouldNotMatch(String pattern) { 280 Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher( 281 getStdout()); 282 if (matcher.find()) { 283 reportDiagnosticSummary(); 284 throw new RuntimeException("'" + pattern + "' found in stdout \n"); 285 } 286 } 287 288 /** 289 * Verify that the stderr contents of output buffer does not match the 290 * pattern 291 * 292 * @param pattern 293 * @throws RuntimeException 294 * If the pattern was found 295 */ 296 public void stderrShouldNotMatch(String pattern) { 297 Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher( 298 getStderr()); 299 if (matcher.find()) { 300 reportDiagnosticSummary(); 301 throw new RuntimeException("'" + pattern + "' found in stderr \n"); 302 } 303 } 304 305 /** 306 * Get the captured group of the first string matching the pattern. stderr 307 * is searched before stdout. 308 * 309 * @param pattern 310 * The multi-line pattern to match 311 * @param group 312 * The group to capture 313 * @return The matched string or null if no match was found 314 */ 315 public String firstMatch(String pattern, int group) { 316 Matcher stderrMatcher = Pattern.compile(pattern, Pattern.MULTILINE) 317 .matcher(getStderr()); 318 Matcher stdoutMatcher = Pattern.compile(pattern, Pattern.MULTILINE) 319 .matcher(getStdout()); 320 if (stderrMatcher.find()) { 321 return stderrMatcher.group(group); 322 } 323 if (stdoutMatcher.find()) { 324 return stdoutMatcher.group(group); 325 } 326 return null; 327 } 328 329 /** 330 * Get the first string matching the pattern. stderr is searched before 331 * stdout. 332 * 333 * @param pattern 334 * The multi-line pattern to match 335 * @return The matched string or null if no match was found 336 */ 337 public String firstMatch(String pattern) { 338 return firstMatch(pattern, 0); 339 } 340 341 /** 342 * Verify the exit value of the process 343 * 344 * @param expectedExitValue 345 * Expected exit value from process 346 * @throws RuntimeException 347 * If the exit value from the process did not match the expected 348 * value 349 */ 350 public void shouldHaveExitValue(int expectedExitValue) { 351 if (getExitValue() != expectedExitValue) { 352 reportDiagnosticSummary(); 353 throw new RuntimeException("Expected to get exit value of [" 354 + expectedExitValue + "]\n"); 355 } 356 } 357 358 /** 359 * Report summary that will help to diagnose the problem Currently includes: 360 * - standard input produced by the process under test - standard output - 361 * exit code Note: the command line is printed by the ProcessTools 362 */ 363 private void reportDiagnosticSummary() { 364 String msg = " stdout: [" + getStdout() + "];\n" + " stderr: [" + getStderr() 365 + "]\n" + " exitValue = " + getExitValue() + "\n"; 366 367 System.err.println(msg); 368 } 369 370 /** 371 * Get the contents of the output buffer (stdout and stderr) 372 * 373 * @return Content of the output buffer 374 */ 375 public String getOutput() { 376 return getStdout() + getStderr(); 377 } 378 379 /** 380 * Get the contents of the stdout buffer 381 * 382 * @return Content of the stdout buffer 383 */ 384 public String getStdout() { 385 return output == null ? stdout : output.getStdout(); 386 } 387 388 /** 389 * Get the contents of the stderr buffer 390 * 391 * @return Content of the stderr buffer 392 */ 393 public String getStderr() { 394 return output == null ? stderr : output.getStderr(); 395 } 396 397 /** 398 * Get the process exit value 399 * 400 * @return Process exit value 401 */ 402 public int getExitValue() { 403 return output == null ? exitValue : output.getExitValue(); 404 } 405 406 /** 407 * Get the contents of the output buffer (stdout and stderr) as list of strings. 408 * Output will be split by system property 'line.separator'. 409 * 410 * @return Contents of the output buffer as list of strings 411 */ 412 public List<String> asLines() { 413 return asLines(getOutput()); 414 } 415 416 private List<String> asLines(String buffer) { 417 List<String> l = new ArrayList<>(); 418 String[] a = buffer.split(Utils.NEW_LINE); 419 for (String string : a) { 420 l.add(string); 421 } 422 return l; 423 } 424 425 /** 426 * Check if there is a line matching {@code pattern} and return its index 427 * 428 * @param pattern Matching pattern 429 * @return Index of first matching line 430 */ 431 private int indexOf(List<String> lines, String pattern) { 432 for (int i = 0; i < lines.size(); i++) { 433 if (lines.get(i).matches(pattern)) { 434 return i; 435 } 436 } 437 return -1; 438 } 439 440 /** 441 * @see #shouldMatchByLine(String, String, String) 442 */ 443 public int shouldMatchByLine(String pattern) { 444 return shouldMatchByLine(null, null, pattern); 445 } 446 447 /** 448 * @see #stdoutShouldMatchByLine(String, String, String) 449 */ 450 public int stdoutShouldMatchByLine(String pattern) { 451 return stdoutShouldMatchByLine(null, null, pattern); 452 } 453 454 /** 455 * @see #shouldMatchByLine(String, String, String) 456 */ 457 public int shouldMatchByLineFrom(String from, String pattern) { 458 return shouldMatchByLine(from, null, pattern); 459 } 460 461 /** 462 * @see #shouldMatchByLine(String, String, String) 463 */ 464 public int shouldMatchByLineTo(String to, String pattern) { 465 return shouldMatchByLine(null, to, pattern); 466 } 467 468 /** 469 * Verify that the stdout and stderr contents of output buffer match the 470 * {@code pattern} line by line. The whole output could be matched or 471 * just a subset of it. 472 * 473 * @param from 474 * The line from where output will be matched. 475 * Set {@code from} to null for matching from the first line. 476 * @param to 477 * The line until where output will be matched. 478 * Set {@code to} to null for matching until the last line. 479 * @param pattern 480 * Matching pattern 481 * @return Count of lines which match the {@code pattern} 482 */ 483 public int shouldMatchByLine(String from, String to, String pattern) { 484 return shouldMatchByLine(getOutput(), from, to, pattern); 485 } 486 487 /** 488 * Verify that the stdout contents of output buffer matches the 489 * {@code pattern} line by line. The whole stdout could be matched or 490 * just a subset of it. 491 * 492 * @param from 493 * The line from where stdout will be matched. 494 * Set {@code from} to null for matching from the first line. 495 * @param to 496 * The line until where stdout will be matched. 497 * Set {@code to} to null for matching until the last line. 498 * @param pattern 499 * Matching pattern 500 * @return Count of lines which match the {@code pattern} 501 */ 502 public int stdoutShouldMatchByLine(String from, String to, String pattern) { 503 return shouldMatchByLine(getStdout(), from, to, pattern); 504 } 505 506 private int shouldMatchByLine(String buffer, String from, String to, String pattern) { 507 List<String> lines = asLines(buffer); 508 509 int fromIndex = 0; 510 if (from != null) { 511 fromIndex = indexOf(lines, from); 512 assertGreaterThan(fromIndex, -1, 513 "The line/pattern '" + from + "' from where the output should match can not be found"); 514 } 515 516 int toIndex = lines.size(); 517 if (to != null) { 518 toIndex = indexOf(lines, to); 519 assertGreaterThan(toIndex, -1, 520 "The line/pattern '" + to + "' until where the output should match can not be found"); 521 } 522 523 List<String> subList = lines.subList(fromIndex, toIndex); 524 int matchedCount = 0; 525 for (String line : subList) { 526 assertTrue(line.matches(pattern), 527 "The line '" + line + "' does not match pattern '" + pattern + "'"); 528 matchedCount++; 529 } 530 531 return matchedCount; 532 } 533 534 }