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