1 /* 2 * Copyright (c) 2002, 2013, 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 import com.sun.javadoc.*; 25 import java.util.*; 26 import java.io.*; 27 28 29 /** 30 * Runs javadoc and then runs regression tests on the resulting output. 31 * This class currently contains three tests: 32 * <ul> 33 * <li> String search: Reads each file, complete with newlines, 34 * into a string. Lets you search for strings that contain 35 * newlines. String matching is case-sensitive. 36 * You can run javadoc multiple times with different arguments, 37 * generating output into different destination directories, and 38 * then perform a different array of tests on each one. 39 * To do this, the run method accepts a test array for testing 40 * that a string is found, and a negated test array for testing 41 * that a string is not found. 42 * <li> Run diffs: Iterate through the list of given file pairs 43 * and diff the pairs. 44 * <li> Check exit code: Check the exit code of Javadoc and 45 * record whether the test passed or failed. 46 * </ul> 47 * 48 * @author Doug Kramer 49 * @author Jamie Ho 50 * @since 1.4.2 51 */ 52 public abstract class JavadocTester { 53 54 protected static final String FS = System.getProperty("file.separator"); 55 protected static final String PS = System.getProperty("path.separator"); 56 protected static final String NL = System.getProperty("line.separator"); 57 protected static final String SRC_DIR = System.getProperty("test.src", "."); 58 protected static final String JAVA_VERSION = System.getProperty("java.version"); 59 protected static final String[][] NO_TEST = new String[][] {}; 60 protected static final String[] NO_FILE_TEST = new String[] {}; 61 62 /** 63 * Use this as the file name in the test array when you want to search 64 * for a string in the error output. 65 */ 66 public static final String ERROR_OUTPUT = "ERROR_OUTPUT"; 67 68 /** 69 * Use this as the file name in the test array when you want to search 70 * for a string in the notice output. 71 */ 72 public static final String NOTICE_OUTPUT = "NOTICE_OUTPUT"; 73 74 /** 75 * Use this as the file name in the test array when you want to search 76 * for a string in the warning output. 77 */ 78 public static final String WARNING_OUTPUT = "WARNING_OUTPUT"; 79 80 /** 81 * Use this as the file name in the test array when you want to search 82 * for a string in standard output. 83 */ 84 public static final String STANDARD_OUTPUT = "STANDARD_OUTPUT"; 85 86 /** 87 * The default doclet. 88 */ 89 public static final String DEFAULT_DOCLET_CLASS = "com.sun.tools.doclets.formats.html.HtmlDoclet"; 90 public static final String DEFAULT_DOCLET_CLASS_OLD = "com.sun.tools.doclets.standard.Standard"; 91 92 /** 93 * The writer to write error messages. 94 */ 95 public StringWriter errors; 96 97 /** 98 * The writer to write notices. 99 */ 100 public StringWriter notices; 101 102 /** 103 * The writer to write warnings. 104 */ 105 public StringWriter warnings; 106 107 /** 108 * The buffer of warning output.. 109 */ 110 public StringBuffer standardOut; 111 112 /** 113 * The current subtest number. 114 */ 115 private static int numTestsRun = 0; 116 117 /** 118 * The number of subtests passed. 119 */ 120 private static int numTestsPassed = 0; 121 122 /** 123 * The current run of javadoc 124 */ 125 private static int javadocRunNum = 0; 126 127 /** 128 * Whether or not to match newlines exactly. 129 * Set this value to false if the match strings 130 * contain text from javadoc comments containing 131 * non-platform newlines. 132 */ 133 protected boolean exactNewlineMatch = true; 134 135 /** 136 * Construct a JavadocTester. 137 */ JavadocTester()138 public JavadocTester() { 139 } 140 141 /** 142 * Return the bug id. 143 * @return the bug id 144 */ getBugId()145 public abstract String getBugId(); 146 147 /** 148 * Return the name of the bug. 149 * @return the name of the bug 150 */ getBugName()151 public abstract String getBugName(); 152 153 /** 154 * Execute the tests. 155 * 156 * @param tester the tester to execute 157 * @param args the arguments to pass to Javadoc 158 * @param testArray the array of tests 159 * @param negatedTestArray the array of negated tests 160 * @return the return code for the execution of Javadoc 161 */ run(JavadocTester tester, String[] args, String[][] testArray, String[][] negatedTestArray)162 public static int run(JavadocTester tester, String[] args, 163 String[][] testArray, String[][] negatedTestArray) { 164 int returnCode = tester.runJavadoc(args); 165 tester.runTestsOnHTML(testArray, negatedTestArray); 166 return returnCode; 167 } 168 169 /** 170 * Execute the tests. 171 * 172 * @param tester the tester to execute 173 * @param args the arguments to pass to Javadoc 174 * @param testArray the array of tests 175 * @param negatedTestArray the array of negated tests 176 * @param fileTestArray the array of file tests 177 * @param negatedFileTestArray the array of negated file tests 178 * @return the return code for the execution of Javadoc 179 */ run(JavadocTester tester, String[] args, String[][] testArray, String[][] negatedTestArray, String[] fileTestArray, String[] negatedFileTestArray)180 public static int run(JavadocTester tester, String[] args, 181 String[][] testArray, String[][] negatedTestArray, String[] fileTestArray, 182 String[] negatedFileTestArray) { 183 int returnCode = tester.runJavadoc(args); 184 tester.runTestsOnHTML(testArray, negatedTestArray); 185 tester.runTestsOnFile(fileTestArray, negatedFileTestArray); 186 return returnCode; 187 } 188 189 /** 190 * Execute Javadoc using the default doclet. 191 * 192 * @param args the arguments to pass to Javadoc 193 * @return the return code from the execution of Javadoc 194 */ runJavadoc(String[] args)195 public int runJavadoc(String[] args) { 196 float javaVersion = Float.parseFloat(JAVA_VERSION.substring(0,3)); 197 String docletClass = javaVersion < 1.5 ? 198 DEFAULT_DOCLET_CLASS_OLD : DEFAULT_DOCLET_CLASS; 199 return runJavadoc(docletClass, args); 200 } 201 202 203 /** 204 * Execute Javadoc. 205 * 206 * @param docletClass the doclet being tested. 207 * @param args the arguments to pass to Javadoc 208 * @return the return code from the execution of Javadoc 209 */ 210 public int runJavadoc(String docletClass, String[] args) { 211 javadocRunNum++; 212 if (javadocRunNum == 1) { 213 System.out.println("\n" + "Running javadoc..."); 214 } else { 215 System.out.println("\n" + "Running javadoc (run " 216 + javadocRunNum + ")..."); 217 } 218 initOutputBuffers(); 219 220 ByteArrayOutputStream stdout = new ByteArrayOutputStream(); 221 PrintStream prevOut = System.out; 222 System.setOut(new PrintStream(stdout)); 223 224 ByteArrayOutputStream stderr = new ByteArrayOutputStream(); 225 PrintStream prevErr = System.err; 226 System.setErr(new PrintStream(stderr)); 227 228 int returnCode = com.sun.tools.javadoc.Main.execute( 229 getBugName(), 230 new PrintWriter(errors, true), 231 new PrintWriter(warnings, true), 232 new PrintWriter(notices, true), 233 docletClass, 234 getClass().getClassLoader(), 235 args); 236 System.setOut(prevOut); 237 standardOut = new StringBuffer(stdout.toString()); 238 System.setErr(prevErr); 239 errors.write(NL + stderr.toString()); 240 241 printJavadocOutput(); 242 return returnCode; 243 } 244 245 /** 246 * Create new string writer buffers 247 */ 248 private void initOutputBuffers() { 249 errors = new StringWriter(); 250 notices = new StringWriter(); 251 warnings = new StringWriter(); 252 } 253 254 /** 255 * Run array of tests on the resulting HTML. 256 * This method accepts a testArray for testing that a string is found 257 * and a negatedTestArray for testing that a string is not found. 258 * 259 * @param testArray the array of tests 260 * @param negatedTestArray the array of negated tests 261 */ 262 public void runTestsOnHTML(String[][] testArray, String[][] negatedTestArray) { 263 runTestsOnHTML(testArray, false); 264 runTestsOnHTML(negatedTestArray, true); 265 } 266 267 /** 268 * Run array of tests on the generated files. 269 * This method accepts a fileTestArray for testing if a file is generated 270 * and a negatedFileTestArray for testing if a file is not found. 271 * 272 * @param testArray the array of file tests 273 * @param negatedTestArray the array of negated file tests 274 */ 275 public void runTestsOnFile(String[] fileTestArray, String[] negatedFileTestArray) { 276 runTestsOnFile(fileTestArray, false); 277 runTestsOnFile(negatedFileTestArray, true); 278 } 279 280 /** 281 * Run the array of tests on the resulting HTML. 282 * 283 * @param testArray the array of tests 284 * @param isNegated true if test is negated; false otherwise 285 */ 286 private void runTestsOnHTML(String[][] testArray , boolean isNegated) { 287 for (int i = 0; i < testArray.length; i++) { 288 289 numTestsRun++; 290 291 System.out.print("Running subtest #" + numTestsRun + "... "); 292 293 // Get string to find 294 String stringToFind = testArray[i][1]; 295 296 // Read contents of file into a string 297 String fileString; 298 try { 299 fileString = readFileToString(testArray[i][0]); 300 } catch (Error e) { 301 if (isNegated) { 302 System.out.println( "FAILED" + "\n" 303 + "for bug " + getBugId() 304 + " (" + getBugName() + ") " 305 + "due to " 306 + e + "\n"); 307 continue; 308 } 309 throw e; 310 } 311 // Find string in file's contents 312 boolean isFound = findString(fileString, stringToFind); 313 if ((isNegated && !isFound) || (!isNegated && isFound) ) { 314 numTestsPassed += 1; 315 System.out.println( "Passed" + "\n" 316 + (isNegated ? "not found:" : "found:") + "\n" 317 + stringToFind + " in " + testArray[i][0] + "\n"); 318 } else { 319 System.out.println( "FAILED" + "\n" 320 + "for bug " + getBugId() 321 + " (" + getBugName() + ")" + "\n" 322 + "when searching for:" + "\n" 323 + stringToFind 324 + " in " + testArray[i][0] + "\n"); 325 } 326 } 327 } 328 329 /** 330 * Run the array of file tests on the generated files. 331 * 332 * @param testArray the array of file tests 333 * @param isNegated true if test is negated; false otherwise 334 */ 335 private void runTestsOnFile(String[] testArray, boolean isNegated) { 336 String fileName; 337 String failedString; 338 String passedString; 339 for (int i = 0; i < testArray.length; i++) { 340 numTestsRun++; 341 fileName = testArray[i]; 342 failedString = "FAILED" + "\n" 343 + "for bug " + getBugId() + " (" + getBugName() + ") " 344 + "file (" + fileName + ") found" + "\n"; 345 passedString = "Passed" + "\n" + 346 "file (" + fileName + ") not found" + "\n"; 347 System.out.print("Running subtest #" + numTestsRun + "... "); 348 try { 349 File file = new File(fileName); 350 if ((file.exists() && !isNegated) || (!file.exists() && isNegated)) { 351 numTestsPassed += 1; 352 System.out.println(passedString); 353 } else { 354 System.out.println(failedString); 355 } 356 } catch (Error e) { 357 System.err.println(e); 358 } 359 } 360 } 361 362 /** 363 * Iterate through the list of given file pairs and diff each file. 364 * 365 * @param filePairs the pairs of files to diff. 366 * @throws an Error is thrown if any differences are found between 367 * file pairs. 368 */ 369 public void runDiffs(String[][] filePairs) throws Error { 370 runDiffs(filePairs, true); 371 } 372 373 /** 374 * Iterate through the list of given file pairs and diff each file. 375 * 376 * @param filePairs the pairs of files to diff. 377 * @param throwErrorIFNoMatch flag to indicate whether or not to throw 378 * an error if the files do not match. 379 * 380 * @throws an Error is thrown if any differences are found between 381 * file pairs and throwErrorIFNoMatch is true. 382 */ 383 public void runDiffs(String[][] filePairs, boolean throwErrorIfNoMatch) throws Error { 384 for (int i = 0; i < filePairs.length; i++) { 385 diff(filePairs[i][0], filePairs[i][1], throwErrorIfNoMatch); 386 } 387 } 388 389 /** 390 * Check the exit code of Javadoc and record whether the test passed 391 * or failed. 392 * 393 * @param expectedExitCode The exit code that is required for the test 394 * to pass. 395 * @param actualExitCode The actual exit code from the previous run of 396 * Javadoc. 397 */ 398 public void checkExitCode(int expectedExitCode, int actualExitCode) { 399 numTestsRun++; 400 if (expectedExitCode == actualExitCode) { 401 System.out.println( "Passed" + "\n" + " got return code " + 402 actualExitCode); 403 numTestsPassed++; 404 } else { 405 System.out.println( "FAILED" + "\n" + "for bug " + getBugId() 406 + " (" + getBugName() + ")" + "\n" + "Expected return code " + 407 expectedExitCode + " but got " + actualExitCode); 408 } 409 } 410 411 /** 412 * Print a summary of the test results. 413 */ 414 protected void printSummary() { 415 if ( numTestsRun != 0 && numTestsPassed == numTestsRun ) { 416 // Test passed 417 System.out.println("\n" + "All " + numTestsPassed 418 + " subtests passed"); 419 } else { 420 // Test failed 421 throw new Error("\n" + (numTestsRun - numTestsPassed) 422 + " of " + (numTestsRun) 423 + " subtests failed for bug " + getBugId() 424 + " (" + getBugName() + ")" + "\n"); 425 } 426 } 427 428 /** 429 * Print the output stored in the buffers. 430 */ 431 protected void printJavadocOutput() { 432 System.out.println(STANDARD_OUTPUT + " : \n" + getStandardOutput()); 433 System.err.println(ERROR_OUTPUT + " : \n" + getErrorOutput()); 434 System.err.println(WARNING_OUTPUT + " : \n" + getWarningOutput()); 435 System.out.println(NOTICE_OUTPUT + " : \n" + getNoticeOutput()); 436 } 437 438 /** 439 * Read the file and return it as a string. 440 * 441 * @param fileName the name of the file to read 442 * @return the file in string format 443 */ 444 public String readFileToString(String fileName) throws Error { 445 if (fileName.equals(ERROR_OUTPUT)) { 446 return getErrorOutput(); 447 } else if (fileName.equals(NOTICE_OUTPUT)) { 448 return getNoticeOutput(); 449 } else if (fileName.equals(WARNING_OUTPUT)) { 450 return getWarningOutput(); 451 } else if (fileName.equals(STANDARD_OUTPUT)) { 452 return getStandardOutput(); 453 } 454 try { 455 File file = new File(fileName); 456 if ( !file.exists() ) { 457 System.out.println("\n" + "FILE DOES NOT EXIST: " + fileName); 458 } 459 BufferedReader in = new BufferedReader(new FileReader(file)); 460 461 // Create an array of characters the size of the file 462 char[] allChars = new char[(int)file.length()]; 463 464 // Read the characters into the allChars array 465 in.read(allChars, 0, (int)file.length()); 466 in.close(); 467 468 // Convert to a string 469 String allCharsString = new String(allChars); 470 return allCharsString; 471 } catch (FileNotFoundException e) { 472 System.err.println(e); 473 throw new Error("File not found: " + fileName); 474 } catch (IOException e) { 475 System.err.println(e); 476 throw new Error("Error reading file: " + fileName); 477 } 478 } 479 480 /** 481 * Compare the two given files. 482 * 483 * @param file1 the first file to compare. 484 * @param file2 the second file to compare. 485 * @param throwErrorIFNoMatch flag to indicate whether or not to throw 486 * an error if the files do not match. 487 * @return true if the files are the same and false otherwise. 488 */ 489 public boolean diff(String file1, String file2, boolean throwErrorIFNoMatch) throws Error { 490 String file1Contents = readFileToString(file1); 491 String file2Contents = readFileToString(file2); 492 numTestsRun++; 493 if (file1Contents.trim().compareTo(file2Contents.trim()) == 0) { 494 System.out.println("Diff successful: " + file1 + ", " + file2); 495 numTestsPassed++; 496 return true; 497 } else if (throwErrorIFNoMatch) { 498 throw new Error("Diff failed: " + file1 + ", " + file2); 499 } else { 500 return false; 501 } 502 } 503 504 /** 505 * Search for the string in the given file and return true 506 * if the string was found. 507 * If exactNewlineMatch is false, newlines will be normalized 508 * before the comparison. 509 * 510 * @param fileString the contents of the file to search through 511 * @param stringToFind the string to search for 512 * @return true if the string was found 513 */ 514 private boolean findString(String fileString, String stringToFind) { 515 if (exactNewlineMatch) { 516 return fileString.indexOf(stringToFind) >= 0; 517 } else { 518 return fileString.replace(NL, "\n").indexOf(stringToFind.replace(NL, "\n")) >= 0; 519 } 520 } 521 522 523 /** 524 * Return the standard output. 525 * @return the standard output 526 */ 527 public String getStandardOutput() { 528 return standardOut.toString(); 529 } 530 531 /** 532 * Return the error output. 533 * @return the error output 534 */ 535 public String getErrorOutput() { 536 return errors.getBuffer().toString(); 537 } 538 539 /** 540 * Return the notice output. 541 * @return the notice output 542 */ 543 public String getNoticeOutput() { 544 return notices.getBuffer().toString(); 545 } 546 547 /** 548 * Return the warning output. 549 * @return the warning output 550 */ 551 public String getWarningOutput() { 552 return warnings.getBuffer().toString(); 553 } 554 555 /** 556 * A utility to copy a directory from one place to another. 557 * We may possibly want to move this to our doclet toolkit in 558 * the near future and maintain it from there. 559 * 560 * @param targetDir the directory to copy. 561 * @param destDir the destination to copy the directory to. 562 */ 563 public static void copyDir(String targetDir, String destDir) { 564 if (targetDir.endsWith("SCCS")) { 565 return; 566 } 567 try { 568 File targetDirObj = new File(targetDir); 569 File destDirParentObj = new File(destDir); 570 File destDirObj = new File(destDirParentObj, targetDirObj.getName()); 571 if (! destDirParentObj.exists()) { 572 destDirParentObj.mkdir(); 573 } 574 if (! destDirObj.exists()) { 575 destDirObj.mkdir(); 576 } 577 String[] files = targetDirObj.list(); 578 for (int i = 0; i < files.length; i++) { 579 File srcFile = new File(targetDirObj, files[i]); 580 File destFile = new File(destDirObj, files[i]); 581 if (srcFile.isFile()) { 582 System.out.println("Copying " + srcFile + " to " + destFile); 583 copyFile(destFile, srcFile); 584 } else if(srcFile.isDirectory()) { 585 copyDir(srcFile.getAbsolutePath(), destDirObj.getAbsolutePath()); 586 } 587 } 588 } catch (IOException exc) { 589 throw new Error("Could not copy " + targetDir + " to " + destDir); 590 } 591 } 592 593 /** 594 * Copy source file to destination file. 595 * 596 * @throws SecurityException 597 * @throws IOException 598 */ 599 public static void copyFile(File destfile, File srcfile) 600 throws IOException { 601 byte[] bytearr = new byte[512]; 602 int len = 0; 603 FileInputStream input = new FileInputStream(srcfile); 604 File destDir = destfile.getParentFile(); 605 destDir.mkdirs(); 606 FileOutputStream output = new FileOutputStream(destfile); 607 try { 608 while ((len = input.read(bytearr)) != -1) { 609 output.write(bytearr, 0, len); 610 } 611 } catch (FileNotFoundException exc) { 612 } catch (SecurityException exc) { 613 } finally { 614 input.close(); 615 output.close(); 616 } 617 } 618 } 619