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