--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/jdk/javadoc/doclet/lib/JavadocTester.java Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,840 @@
+/*
+ * Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.BufferedWriter;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.SoftReference;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+
+/**
+ * Test framework for running javadoc and performing tests on the resulting output.
+ *
+ * <p>
+ * Tests are typically written as subtypes of JavadocTester, with a main
+ * method that creates an instance of the test class and calls the runTests()
+ * method. The runTests() methods calls all the test methods declared in the class,
+ * and then calls a method to print a summary, and throw an exception if
+ * any of the test methods reported a failure.
+ *
+ * <p>
+ * Test methods are identified with a @Test annotation. They have no parameters.
+ * The name of the method is not important, but if you have more than one, it is
+ * recommended that the names be meaningful and suggestive of the test case
+ * contained therein.
+ *
+ * <p>
+ * Typically, a test method will invoke javadoc, and then perform various
+ * checks on the results. The standard checks are:
+ *
+ * <dl>
+ * <dt>checkExitCode
+ * <dd>Check the exit code returned from javadoc.
+ * <dt>checkOutput
+ * <dd>Perform a series of checks on the contents on a file or output stream
+ * generated by javadoc.
+ * The checks can be either that a series of strings are found or are not found.
+ * <dt>checkFiles
+ * <dd>Perform a series of checks on the files generated by javadoc.
+ * The checks can be that a series of files are found or are not found.
+ * </dl>
+ *
+ * <pre><code>
+ * public class MyTester extends JavadocTester {
+ * public static void main(String... args) throws Exception {
+ * MyTester tester = new MyTester();
+ * tester.runTests();
+ * }
+ *
+ * // test methods...
+ * @Test
+ * void test() {
+ * javadoc(<i>args</i>);
+ * checkExit(Exit.OK);
+ * checkOutput(<i>file</i>, true,
+ * <i>strings-to-find</i>);
+ * checkOutput(<i>file</i>, false,
+ * <i>strings-to-not-find</i>);
+ * }
+ * }
+ * </code></pre>
+ *
+ * <p>
+ * If javadoc is run more than once in a test method, you can compare the
+ * results that are generated with the diff method. Since files written by
+ * javadoc typically contain a timestamp, you may want to use the -notimestamp
+ * option if you are going to compare the results from two runs of javadoc.
+ *
+ * <p>
+ * If you have many calls of checkOutput that are very similar, you can write
+ * your own check... method to reduce the amount of duplication. For example,
+ * if you want to check that many files contain the same string, you could
+ * write a method that takes a varargs list of files and calls checkOutput
+ * on each file in turn with the string to be checked.
+ *
+ * <p>
+ * You can also write you own custom check methods, which can use
+ * readFile to get the contents of a file generated by javadoc,
+ * and then use pass(...) or fail(...) to report whether the check
+ * succeeded or not.
+ *
+ * <p>
+ * You can have many separate test methods, each identified with a @Test
+ * annotation. However, you should <b>not</b> assume they will be called
+ * in the order declared in your source file. If the order of a series
+ * of javadoc invocations is important, do that within a single method.
+ * If the invocations are independent, for better clarity, use separate
+ * test methods, each with their own set of checks on the results.
+ *
+ * @author Doug Kramer
+ * @author Jamie Ho
+ * @author Jonathan Gibbons (rewrite)
+ */
+public abstract class JavadocTester {
+
+ public static final String FS = System.getProperty("file.separator");
+ public static final String PS = System.getProperty("path.separator");
+ public static final String NL = System.getProperty("line.separator");
+
+ public enum Output {
+ /** The name of the output stream from javadoc. */
+ OUT,
+ /** The name for any output written to System.out. */
+ STDOUT,
+ /** The name for any output written to System.err. */
+ STDERR
+ }
+
+ /** The output directory used in the most recent call of javadoc. */
+ protected File outputDir;
+
+ /** The exit code of the most recent call of javadoc. */
+ private int exitCode;
+
+ /** The output generated by javadoc to the various writers and streams. */
+ private final Map<Output, String> outputMap = new EnumMap<>(Output.class);
+
+ /** A cache of file content, to avoid reading files unnecessarily. */
+ private final Map<File,SoftReference<String>> fileContentCache = new HashMap<>();
+
+ /** Stream used for logging messages. */
+ protected final PrintStream out = System.out;
+
+ /** The directory containing the source code for the test. */
+ public static final String testSrc = System.getProperty("test.src");
+
+ /**
+ * Get the path for a source file in the test source directory.
+ * @param path the path of a file or directory in the source directory
+ * @return the full path of the specified file
+ */
+ public static String testSrc(String path) {
+ return new File(testSrc, path).getPath();
+ }
+
+ /**
+ * Alternatives for checking the contents of a directory.
+ */
+ public enum DirectoryCheck {
+ /**
+ * Check that the directory is empty.
+ */
+ EMPTY((file, name) -> true),
+ /**
+ * Check that the directory does not contain any HTML files,
+ * such as may have been generated by a prior run of javadoc
+ * using this directory.
+ * For now, the check is only performed on the top level directory.
+ */
+ NO_HTML_FILES((file, name) -> name.endsWith(".html")),
+ /**
+ * No check is performed on the directory contents.
+ */
+ NONE(null) { @Override void check(File dir) { } };
+
+ /** The filter used to detect that files should <i>not</i> be present. */
+ FilenameFilter filter;
+
+ DirectoryCheck(FilenameFilter f) {
+ filter = f;
+ }
+
+ void check(File dir) {
+ if (dir.isDirectory()) {
+ String[] contents = dir.list(filter);
+ if (contents == null)
+ throw new Error("cannot list directory: " + dir);
+ if (contents.length > 0) {
+ System.err.println("Found extraneous files in dir:" + dir.getAbsolutePath());
+ for (String x : contents) {
+ System.err.println(x);
+ }
+ throw new Error("directory has unexpected content: " + dir);
+ }
+ }
+ }
+ }
+
+ private DirectoryCheck outputDirectoryCheck = DirectoryCheck.EMPTY;
+
+ /** The current subtest number. Incremented when checking(...) is called. */
+ private int numTestsRun = 0;
+
+ /** The number of subtests passed. Incremented when passed(...) is called. */
+ private int numTestsPassed = 0;
+
+ /** The current run of javadoc. Incremented when javadoc is called. */
+ private int javadocRunNum = 0;
+
+ /** Marker annotation for test methods to be invoked by runTests. */
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface Test { }
+
+ /**
+ * Run all methods annotated with @Test, followed by printSummary.
+ * Typically called on a tester object in main()
+ * @throws Exception if any errors occurred
+ */
+ public void runTests() throws Exception {
+ runTests(m -> new Object[0]);
+ }
+
+ /**
+ * Run all methods annotated with @Test, followed by printSummary.
+ * Typically called on a tester object in main()
+ * @param f a function which will be used to provide arguments to each
+ * invoked method
+ * @throws Exception if any errors occurred
+ */
+ public void runTests(Function<Method, Object[]> f) throws Exception {
+ for (Method m: getClass().getDeclaredMethods()) {
+ Annotation a = m.getAnnotation(Test.class);
+ if (a != null) {
+ try {
+ out.println("Running test " + m.getName());
+ m.invoke(this, f.apply(m));
+ } catch (InvocationTargetException e) {
+ Throwable cause = e.getCause();
+ throw (cause instanceof Exception) ? ((Exception) cause) : e;
+ }
+ out.println();
+ }
+ }
+ printSummary();
+ }
+
+ /**
+ * Run javadoc.
+ * The output directory used by this call and the final exit code
+ * will be saved for later use.
+ * To aid the reader, it is recommended that calls to this method
+ * put each option and the arguments it takes on a separate line.
+ *
+ * Example:
+ * <pre><code>
+ * javadoc("-d", "out",
+ * "-sourcepath", testSrc,
+ * "-notimestamp",
+ * "pkg1", "pkg2", "pkg3/C.java");
+ * </code></pre>
+ *
+ * @param args the arguments to pass to javadoc
+ */
+ public void javadoc(String... args) {
+ outputMap.clear();
+ fileContentCache.clear();
+
+ javadocRunNum++;
+ if (javadocRunNum == 1) {
+ out.println("Running javadoc...");
+ } else {
+ out.println("Running javadoc (run "
+ + javadocRunNum + ")...");
+ }
+ outputDir = new File(".");
+ for (int i = 0; i < args.length - 2; i++) {
+ if (args[i].equals("-d")) {
+ outputDir = new File(args[++i]);
+ break;
+ }
+ }
+ out.println("args: " + Arrays.toString(args));
+// log.setOutDir(outputDir);
+
+ outputDirectoryCheck.check(outputDir);
+
+ // This is the sole stream used by javadoc
+ WriterOutput outOut = new WriterOutput();
+
+ // These are to catch output to System.out and System.err,
+ // in case these are used instead of the primary streams
+ StreamOutput sysOut = new StreamOutput(System.out, System::setOut);
+ StreamOutput sysErr = new StreamOutput(System.err, System::setErr);
+
+ try {
+ exitCode = jdk.javadoc.internal.tool.Main.execute(args, outOut.pw);
+ } finally {
+ outputMap.put(Output.STDOUT, sysOut.close());
+ outputMap.put(Output.STDERR, sysErr.close());
+ outputMap.put(Output.OUT, outOut.close());
+ }
+
+ outputMap.forEach((name, text) -> {
+ if (!text.isEmpty()) {
+ out.println("javadoc " + name + ":");
+ out.println(text);
+ }
+ });
+ }
+
+ /**
+ * Set the kind of check for the initial contents of the output directory
+ * before javadoc is run.
+ * The filter should return true for files that should <b>not</b> appear.
+ * @param c the kind of check to perform
+ */
+ public void setOutputDirectoryCheck(DirectoryCheck c) {
+ outputDirectoryCheck = c;
+ }
+
+ /**
+ * The exit codes returned by the javadoc tool.
+ * @see jdk.javadoc.internal.tool.Main.Result
+ */
+ public enum Exit {
+ OK(0), // Javadoc completed with no errors.
+ ERROR(1), // Completed but reported errors.
+ CMDERR(2), // Bad command-line arguments
+ SYSERR(3), // System error or resource exhaustion.
+ ABNORMAL(4); // Javadoc terminated abnormally
+
+ Exit(int code) {
+ this.code = code;
+ }
+
+ final int code;
+
+ @Override
+ public String toString() {
+ return name() + '(' + code + ')';
+ }
+ }
+
+ /**
+ * Check the exit code of the most recent call of javadoc.
+ *
+ * @param expected the exit code that is required for the test
+ * to pass.
+ */
+ public void checkExit(Exit expected) {
+ checking("check exit code");
+ if (exitCode == expected.code) {
+ passed("return code " + exitCode);
+ } else {
+ failed("return code " + exitCode +"; expected " + expected);
+ }
+ }
+
+ /**
+ * Check for content in (or not in) the generated output.
+ * Within the search strings, the newline character \n
+ * will be translated to the platform newline character sequence.
+ * @param path a path within the most recent output directory
+ * or the name of one of the output buffers, identifying
+ * where to look for the search strings.
+ * @param expectedFound true if all of the search strings are expected
+ * to be found, or false if the file is not expected to be found
+ * @param strings the strings to be searched for
+ */
+ public void checkFileAndOutput(String path, boolean expectedFound, String... strings) {
+ if (expectedFound) {
+ checkOutput(path, true, strings);
+ } else {
+ checkFiles(false, path);
+ }
+ }
+
+ /**
+ * Check for content in (or not in) the generated output.
+ * Within the search strings, the newline character \n
+ * will be translated to the platform newline character sequence.
+ * @param path a path within the most recent output directory
+ * or the name of one of the output buffers, identifying
+ * where to look for the search strings.
+ * @param expectedFound true if all of the search strings are expected
+ * to be found, or false if all of the strings are expected to be
+ * not found
+ * @param strings the strings to be searched for
+ */
+ public void checkOutput(String path, boolean expectedFound, String... strings) {
+ // Read contents of file
+ String fileString;
+ try {
+ fileString = readFile(outputDir, path);
+ } catch (Error e) {
+ checking("Read file");
+ failed("Error reading file: " + e);
+ return;
+ }
+ checkOutput(path, fileString, expectedFound, strings);
+ }
+
+ /**
+ * Check for content in (or not in) the one of the output streams written by
+ * javadoc. Within the search strings, the newline character \n
+ * will be translated to the platform newline character sequence.
+ * @param output the output stream to check
+ * @param expectedFound true if all of the search strings are expected
+ * to be found, or false if all of the strings are expected to be
+ * not found
+ * @param strings the strings to be searched for
+ */
+ public void checkOutput(Output output, boolean expectedFound, String... strings) {
+ checkOutput(output.toString(), outputMap.get(output), expectedFound, strings);
+ }
+
+ private void checkOutput(String path, String fileString, boolean expectedFound, String... strings) {
+ for (String stringToFind : strings) {
+// log.logCheckOutput(path, expectedFound, stringToFind);
+ checking("checkOutput");
+ // Find string in file's contents
+ boolean isFound = findString(fileString, stringToFind);
+ if (isFound == expectedFound) {
+ passed(path + ": following text " + (isFound ? "found:" : "not found:") + "\n"
+ + stringToFind + "\n");
+ } else {
+ failed(path + ": following text " + (isFound ? "found:" : "not found:") + "\n"
+ + stringToFind + "\n");
+ }
+ }
+ }
+
+ /**
+ * Get the content of the one of the output streams written by
+ * javadoc.
+ */
+ public String getOutput(Output output) {
+ return outputMap.get(output);
+ }
+
+ /**
+ * Get the content of the one of the output streams written by
+ * javadoc.
+ */
+ public List<String> getOutputLines(Output output) {
+ String text = outputMap.get(output);
+ return (text == null) ? Collections.emptyList() : Arrays.asList(text.split(NL));
+ }
+
+ /**
+ * Check for files in (or not in) the generated output.
+ * @param expectedFound true if all of the files are expected
+ * to be found, or false if all of the files are expected to be
+ * not found
+ * @param paths the files to check, within the most recent output directory.
+ * */
+ public void checkFiles(boolean expectedFound, String... paths) {
+ checkFiles(expectedFound, Arrays.asList(paths));
+ }
+
+ /**
+ * Check for files in (or not in) the generated output.
+ * @param expectedFound true if all of the files are expected
+ * to be found, or false if all of the files are expected to be
+ * not found
+ * @param paths the files to check, within the most recent output directory.
+ * */
+ public void checkFiles(boolean expectedFound, Collection<String> paths) {
+ for (String path: paths) {
+// log.logCheckFile(path, expectedFound);
+ checking("checkFile");
+ File file = new File(outputDir, path);
+ boolean isFound = file.exists();
+ if (isFound == expectedFound) {
+ passed(path + ": file " + (isFound ? "found:" : "not found:") + "\n");
+ } else {
+ failed(path + ": file " + (isFound ? "found:" : "not found:") + "\n");
+ }
+ }
+ }
+
+ /**
+ * Check that a series of strings are found in order in a file in
+ * the generated output.
+ * @param path the file to check
+ * @param strings the strings whose order to check
+ */
+ public void checkOrder(String path, String... strings) {
+ String fileString = readOutputFile(path);
+ int prevIndex = -1;
+ for (String s : strings) {
+ s = s.replace("\n", NL); // normalize new lines
+ int currentIndex = fileString.indexOf(s);
+ checking(s + " at index " + currentIndex);
+ if (currentIndex == -1) {
+ failed(s + " not found.");
+ continue;
+ }
+ if (currentIndex > prevIndex) {
+ passed(s + " is in the correct order");
+ } else {
+ failed("file: " + path + ": " + s + " is in the wrong order.");
+ }
+ prevIndex = currentIndex;
+ }
+ }
+
+ /**
+ * Ensures that a series of strings appear only once, in the generated output,
+ * noting that, this test does not exhaustively check for all other possible
+ * duplicates once one is found.
+ * @param path the file to check
+ * @param strings ensure each are unique
+ */
+ public void checkUnique(String path, String... strings) {
+ String fileString = readOutputFile(path);
+ for (String s : strings) {
+ int currentIndex = fileString.indexOf(s);
+ checking(s + " at index " + currentIndex);
+ if (currentIndex == -1) {
+ failed(s + " not found.");
+ continue;
+ }
+ int nextindex = fileString.indexOf(s, currentIndex + s.length());
+ if (nextindex == -1) {
+ passed(s + " is unique");
+ } else {
+ failed(s + " is not unique, found at " + nextindex);
+ }
+ }
+ }
+
+ /**
+ * Compare a set of files in each of two directories.
+ *
+ * @param baseDir1 the directory containing the first set of files
+ * @param baseDir2 the directory containing the second set of files
+ * @param files the set of files to be compared
+ */
+ public void diff(String baseDir1, String baseDir2, String... files) {
+ File bd1 = new File(baseDir1);
+ File bd2 = new File(baseDir2);
+ for (String file : files) {
+ diff(bd1, bd2, file);
+ }
+ }
+
+ /**
+ * A utility to copy a directory from one place to another.
+ *
+ * @param targetDir the directory to copy.
+ * @param destDir the destination to copy the directory to.
+ */
+ // TODO: convert to using java.nio.Files.walkFileTree
+ public void copyDir(String targetDir, String destDir) {
+ try {
+ File targetDirObj = new File(targetDir);
+ File destDirParentObj = new File(destDir);
+ File destDirObj = new File(destDirParentObj, targetDirObj.getName());
+ if (! destDirParentObj.exists()) {
+ destDirParentObj.mkdir();
+ }
+ if (! destDirObj.exists()) {
+ destDirObj.mkdir();
+ }
+ String[] files = targetDirObj.list();
+ for (String file : files) {
+ File srcFile = new File(targetDirObj, file);
+ File destFile = new File(destDirObj, file);
+ if (srcFile.isFile()) {
+ out.println("Copying " + srcFile + " to " + destFile);
+ copyFile(destFile, srcFile);
+ } else if(srcFile.isDirectory()) {
+ copyDir(srcFile.getAbsolutePath(), destDirObj.getAbsolutePath());
+ }
+ }
+ } catch (IOException exc) {
+ throw new Error("Could not copy " + targetDir + " to " + destDir);
+ }
+ }
+
+ /**
+ * Copy source file to destination file.
+ *
+ * @param destfile the destination file
+ * @param srcfile the source file
+ * @throws IOException
+ */
+ public void copyFile(File destfile, File srcfile) throws IOException {
+ Files.copy(srcfile.toPath(), destfile.toPath());
+ }
+
+ /**
+ * Read a file from the output directory.
+ *
+ * @param fileName the name of the file to read
+ * @return the file in string format
+ */
+ public String readOutputFile(String fileName) throws Error {
+ return readFile(outputDir, fileName);
+ }
+
+ protected String readFile(String fileName) throws Error {
+ return readFile(outputDir, fileName);
+ }
+
+ protected String readFile(String baseDir, String fileName) throws Error {
+ return readFile(new File(baseDir), fileName);
+ }
+
+ /**
+ * Read the file and return it as a string.
+ *
+ * @param baseDir the directory in which to locate the file
+ * @param fileName the name of the file to read
+ * @return the file in string format
+ */
+ private String readFile(File baseDir, String fileName) throws Error {
+ try {
+ File file = new File(baseDir, fileName);
+ SoftReference<String> ref = fileContentCache.get(file);
+ String content = (ref == null) ? null : ref.get();
+ if (content != null)
+ return content;
+
+ content = new String(Files.readAllBytes(file.toPath()));
+ fileContentCache.put(file, new SoftReference<>(content));
+ return content;
+ } catch (FileNotFoundException e) {
+ throw new Error("File not found: " + fileName + ": " + e);
+ } catch (IOException e) {
+ throw new Error("Error reading file: " + fileName + ": " + e);
+ }
+ }
+
+ protected void checking(String message) {
+ numTestsRun++;
+ print("Starting subtest " + numTestsRun, message);
+ }
+
+ protected void passed(String message) {
+ numTestsPassed++;
+ print("Passed", message);
+ }
+
+ protected void failed(String message) {
+ print("FAILED", message);
+ }
+
+ private void print(String prefix, String message) {
+ if (message.isEmpty())
+ out.println(prefix);
+ else {
+ out.print(prefix);
+ out.print(": ");
+ out.println(message.replace("\n", NL));
+ }
+ }
+
+ /**
+ * Print a summary of the test results.
+ */
+ protected void printSummary() {
+ String javadocRuns = (javadocRunNum <= 1) ? ""
+ : ", in " + javadocRunNum + " runs of javadoc";
+
+ if (numTestsRun != 0 && numTestsPassed == numTestsRun) {
+ // Test passed
+ out.println();
+ out.println("All " + numTestsPassed + " subtests passed" + javadocRuns);
+ } else {
+ // Test failed
+ throw new Error((numTestsRun - numTestsPassed)
+ + " of " + (numTestsRun)
+ + " subtests failed"
+ + javadocRuns);
+ }
+ }
+
+ /**
+ * Search for the string in the given file and return true
+ * if the string was found.
+ *
+ * @param fileString the contents of the file to search through
+ * @param stringToFind the string to search for
+ * @return true if the string was found
+ */
+ private boolean findString(String fileString, String stringToFind) {
+ // javadoc (should) always use the platform newline sequence,
+ // but in the strings to find it is more convenient to use the Java
+ // newline character. So we translate \n to NL before we search.
+ stringToFind = stringToFind.replace("\n", NL);
+ return fileString.contains(stringToFind);
+ }
+
+ /**
+ * Compare the two given files.
+ *
+ * @param baseDir1 the directory in which to locate the first file
+ * @param baseDir2 the directory in which to locate the second file
+ * @param file the file to compare in the two base directories
+ * @param throwErrorIFNoMatch flag to indicate whether or not to throw
+ * an error if the files do not match.
+ * @return true if the files are the same and false otherwise.
+ */
+ private void diff(File baseDir1, File baseDir2, String file) {
+ String file1Contents = readFile(baseDir1, file);
+ String file2Contents = readFile(baseDir2, file);
+ checking("diff " + new File(baseDir1, file) + ", " + new File(baseDir2, file));
+ if (file1Contents.trim().compareTo(file2Contents.trim()) == 0) {
+ passed("files are equal");
+ } else {
+ failed("files differ");
+ }
+ }
+
+ /**
+ * Utility class to simplify the handling of temporarily setting a
+ * new stream for System.out or System.err.
+ */
+ private static class StreamOutput {
+ // functional interface to set a stream.
+ private interface Initializer {
+ void set(PrintStream s);
+ }
+
+ private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ private final PrintStream ps = new PrintStream(baos);
+ private final PrintStream prev;
+ private final Initializer init;
+
+ StreamOutput(PrintStream s, Initializer init) {
+ prev = s;
+ init.set(ps);
+ this.init = init;
+ }
+
+ String close() {
+ init.set(prev);
+ ps.close();
+ return baos.toString();
+ }
+ }
+
+ /**
+ * Utility class to simplify the handling of creating an in-memory PrintWriter.
+ */
+ private static class WriterOutput {
+ private final StringWriter sw = new StringWriter();
+ final PrintWriter pw = new PrintWriter(sw);
+ String close() {
+ pw.close();
+ return sw.toString();
+ }
+ }
+
+
+// private final Logger log = new Logger();
+
+ //--------- Logging --------------------------------------------------------
+ //
+ // This class writes out the details of calls to checkOutput and checkFile
+ // in a canonical way, so that the resulting file can be checked against
+ // similar files from other versions of JavadocTester using the same logging
+ // facilities.
+
+ static class Logger {
+ private static final int PREFIX = 40;
+ private static final int SUFFIX = 20;
+ private static final int MAX = PREFIX + SUFFIX;
+ List<String> tests = new ArrayList<>();
+ String outDir;
+ String rootDir = rootDir();
+
+ static String rootDir() {
+ File f = new File(".").getAbsoluteFile();
+ while (!new File(f, ".hg").exists())
+ f = f.getParentFile();
+ return f.getPath();
+ }
+
+ void setOutDir(File outDir) {
+ this.outDir = outDir.getPath();
+ }
+
+ void logCheckFile(String file, boolean positive) {
+ // Strip the outdir because that will typically not be the same
+ if (file.startsWith(outDir + "/"))
+ file = file.substring(outDir.length() + 1);
+ tests.add(file + " " + positive);
+ }
+
+ void logCheckOutput(String file, boolean positive, String text) {
+ // Compress the string to be displayed in the log file
+ String simpleText = text.replaceAll("\\s+", " ").replace(rootDir, "[ROOT]");
+ if (simpleText.length() > MAX)
+ simpleText = simpleText.substring(0, PREFIX)
+ + "..." + simpleText.substring(simpleText.length() - SUFFIX);
+ // Strip the outdir because that will typically not be the same
+ if (file.startsWith(outDir + "/"))
+ file = file.substring(outDir.length() + 1);
+ // The use of text.hashCode ensure that all of "text" is taken into account
+ tests.add(file + " " + positive + " " + text.hashCode() + " " + simpleText);
+ }
+
+ void write() {
+ // sort the log entries because the subtests may not be executed in the same order
+ tests.sort((a, b) -> a.compareTo(b));
+ try (BufferedWriter bw = new BufferedWriter(new FileWriter("tester.log"))) {
+ for (String t: tests) {
+ bw.write(t);
+ bw.newLine();
+ }
+ } catch (IOException e) {
+ throw new Error("problem writing log: " + e);
+ }
+ }
+ }
+}