8154482: javadoc tool must support legacy doclet and taglet
Reviewed-by: jjg
diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/resources/standard.properties b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/resources/standard.properties
index 0efe7ee..b20f722 100644
--- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/resources/standard.properties
+++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/resources/standard.properties
@@ -1,4 +1,4 @@
-doclet.build_version=Standard Doclet version {0}
+doclet.build_version=Standard Doclet (Old) version {0}
doclet.Contents=Contents
doclet.Overview=Overview
doclet.Window_Overview=Overview List
diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties
index 6356cbf..6eea5f6 100644
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties
@@ -1,4 +1,4 @@
-doclet.build_version=Standard Doclet (Next) version {0}
+doclet.build_version=Standard Doclet version {0}
doclet.Contents=Contents
doclet.Overview=Overview
doclet.Window_Overview=Overview List
diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/DocEnv.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/DocEnv.java
index 674187d..787a22a 100644
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/DocEnv.java
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/DocEnv.java
@@ -550,7 +550,7 @@
// Messager should be replaced by a more general
// compilation environment. This can probably
// subsume DocEnv as well.
- messager.exit();
+ throw new Messager.ExitJavadoc();
}
/**
diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Main.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Main.java
index 5993176..a8725f1 100644
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Main.java
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Main.java
@@ -59,13 +59,6 @@
* @return The return code.
*/
public static int execute(String... args) {
- // NOTE: the following should be removed when the old doclet
- // is removed.
- if (args != null && args.length > 0 && "-Xold".equals(args[0])) {
- String[] nargs = new String[args.length - 1];
- System.arraycopy(args, 1, nargs, 0, nargs.length);
- return com.sun.tools.javadoc.Main.execute(nargs);
- }
Start jdoc = new Start();
return jdoc.begin(args);
}
diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Messager.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Messager.java
index bb798f0..d251569 100644
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Messager.java
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Messager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2016, 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
@@ -139,7 +139,7 @@
}
}
- public class ExitJavadoc extends Error {
+ public static class ExitJavadoc extends Error {
private static final long serialVersionUID = 0;
}
@@ -416,15 +416,6 @@
}
}
- /**
- * Force program exit, e.g., from a fatal error.
- * <p>
- * TODO: This method does not really belong here.
- */
- public void exit() {
- throw new ExitJavadoc();
- }
-
private void report(DiagnosticType type, String pos, String msg) {
switch (type) {
case ERROR:
diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Start.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Start.java
index 824ffc8..f281ee4 100644
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Start.java
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Start.java
@@ -66,6 +66,7 @@
import jdk.javadoc.doclet.DocletEnvironment;
import static com.sun.tools.javac.main.Option.*;
+
/**
* Main program of Javadoc.
* Previously named "Main".
@@ -79,6 +80,12 @@
* @author Neal Gafter (rewrite)
*/
public class Start extends ToolOption.Helper {
+
+ private static final Class<?> OldStdDoclet =
+ com.sun.tools.doclets.standard.Standard.class;
+
+ private static final Class<?> StdDoclet =
+ jdk.javadoc.internal.doclets.standard.Standard.class;
/** Context for this invocation. */
private final Context context;
@@ -193,18 +200,26 @@
if (foot != null)
messager.notice(foot);
- if (exit) exit();
+ if (exit)
+ throw new Messager.ExitJavadoc();
}
- /**
- * Exit
- */
- private void exit() {
- messager.exit();
- }
/**
- * Main program - external wrapper
+ * Main program - external wrapper. In order to maintain backward
+ * CLI compatibility, we dispatch to the old tool or the old doclet's
+ * Start mechanism, based on the options present on the command line
+ * with the following precedence:
+ * 1. presence of -Xold, dispatch to old tool
+ * 2. doclet variant, if old, dispatch to old Start
+ * 3. taglet variant, if old, dispatch to old Start
+ *
+ * Thus the presence of -Xold switches the tool, soon after command files
+ * if any, are expanded, this is performed here, noting that the messager
+ * is available at this point in time.
+ * The doclet/taglet tests are performed in the begin method, further on,
+ * this is to minimize argument processing and most importantly the impact
+ * of class loader creation, needed to detect the doclet/taglet class variants.
*/
int begin(String... argv) {
// Preprocess @file arguments
@@ -212,14 +227,18 @@
argv = CommandLine.parse(argv);
} catch (FileNotFoundException e) {
messager.error("main.cant.read", e.getMessage());
- exit();
+ throw new Messager.ExitJavadoc();
} catch (IOException e) {
e.printStackTrace(System.err);
- exit();
+ throw new Messager.ExitJavadoc();
}
- List<String> argList = Arrays.asList(argv);
- boolean ok = begin(argList, Collections.<JavaFileObject> emptySet());
+ if (argv.length > 0 && "-Xold".equals(argv[0])) {
+ messager.warning("main.legacy_api");
+ String[] nargv = Arrays.copyOfRange(argv, 1, argv.length);
+ return com.sun.tools.javadoc.Main.execute(nargv);
+ }
+ boolean ok = begin(Arrays.asList(argv), Collections.<JavaFileObject> emptySet());
return ok ? 0 : 1;
}
@@ -231,11 +250,11 @@
List<String> opts = new ArrayList<>();
for (String opt: options)
opts.add(opt);
+
return begin(opts, fileObjects);
}
private boolean begin(List<String> options, Iterable<? extends JavaFileObject> fileObjects) {
-
fileManager = context.get(JavaFileManager.class);
if (fileManager == null) {
JavacFileManager.preRegister(context);
@@ -244,9 +263,8 @@
((BaseFileManager) fileManager).autoClose = true;
}
}
- // locale and doclet needs to be determined first
+ // locale, doclet and maybe taglet, needs to be determined first
docletClass = preProcess(fileManager, options);
-
if (jdk.javadoc.doclet.Doclet.class.isAssignableFrom(docletClass)) {
// no need to dispatch to old, safe to init now
initMessager();
@@ -257,7 +275,7 @@
exc.printStackTrace();
if (!apiMode) {
error("main.could_not_instantiate_class", docletClass);
- messager.exit();
+ throw new Messager.ExitJavadoc();
}
throw new ClientCodeException(exc);
}
@@ -267,6 +285,7 @@
= new com.sun.tools.javadoc.Start(context);
return ostart.begin(docletClass, options, fileObjects);
}
+ warn("main.legacy_api");
String[] array = options.toArray(new String[options.size()]);
return com.sun.tools.javadoc.Main.execute(array) == 0;
}
@@ -459,6 +478,11 @@
String userDocletPath = null;
String userDocletName = null;
+ // taglet specifying arguments, since tagletpath is a doclet
+ // functionality, assume they are repeated and inspect all.
+ List<File> userTagletPath = new ArrayList<>();
+ List<String> userTagletNames = new ArrayList<>();
+
// Step 1: loop through the args, set locale early on, if found.
for (int i = 0 ; i < argv.size() ; i++) {
String arg = argv.get(i);
@@ -470,7 +494,7 @@
oneArg(argv, i++);
if (userDocletName != null) {
usageError("main.more_than_one_doclet_specified_0_and_1",
- userDocletName, argv.get(i));
+ userDocletName, argv.get(i));
}
if (docletName != null) {
usageError("main.more_than_one_doclet_specified_0_and_1",
@@ -484,13 +508,20 @@
} else {
userDocletPath += File.pathSeparator + argv.get(i);
}
+ } else if ("-taglet".equals(arg)) {
+ userTagletNames.add(argv.get(i + 1));
+ } else if ("-tagletpath".equals(arg)) {
+ for (String pathname : argv.get(i + 1).split(File.pathSeparator)) {
+ userTagletPath.add(new File(pathname));
+ }
}
}
- // Step 2: a doclet has already been provided,
- // nothing more to do.
+
+ // Step 2: a doclet is provided, nothing more to do.
if (docletClass != null) {
return docletClass;
}
+
// Step 3: doclet name specified ? if so find a ClassLoader,
// and load it.
if (userDocletName != null) {
@@ -506,38 +537,80 @@
try {
((StandardJavaFileManager)fileManager).setLocation(DOCLET_PATH, paths);
} catch (IOException ioe) {
- panic("main.doclet_no_classloader_found", ioe);
- return null; // keep compiler happy
+ error("main.doclet_could_not_set_location", paths);
+ throw new Messager.ExitJavadoc();
}
}
cl = fileManager.getClassLoader(DOCLET_PATH);
if (cl == null) {
// despite doclet specified on cmdline no classloader found!
- panic("main.doclet_no_classloader_found", userDocletName);
- return null; // keep compiler happy
- }
- try {
- Class<?> klass = cl.loadClass(userDocletName);
- ensureReadable(klass);
- return klass;
- } catch (ClassNotFoundException cnfe) {
- panic("main.doclet_class_not_found", userDocletName);
- return null; // keep compiler happy
+ error("main.doclet_no_classloader_found", userDocletName);
+ throw new Messager.ExitJavadoc();
}
}
+ try {
+ Class<?> klass = cl.loadClass(userDocletName);
+ ensureReadable(klass);
+ return klass;
+ } catch (ClassNotFoundException cnfe) {
+ error("main.doclet_class_not_found", userDocletName);
+ throw new Messager.ExitJavadoc();
+ }
}
- // Step 4: we have a doclet, try loading it, otherwise
- // return back the standard doclet
+
+ // Step 4: we have a doclet, try loading it
if (docletName != null) {
try {
return Class.forName(docletName, true, getClass().getClassLoader());
} catch (ClassNotFoundException cnfe) {
- panic("main.doclet_class_not_found", userDocletName);
- return null; // happy compiler, should not happen
+ error("main.doclet_class_not_found", userDocletName);
+ throw new Messager.ExitJavadoc();
}
- } else {
- return jdk.javadoc.internal.doclets.standard.Standard.class;
}
+
+ // Step 5: we don't have a doclet specified, do we have taglets ?
+ if (!userTagletNames.isEmpty() && hasOldTaglet(userTagletNames, userTagletPath)) {
+ // found a bogey, return the old doclet
+ return OldStdDoclet;
+ }
+
+ // finally
+ return StdDoclet;
+ }
+
+ /*
+ * This method returns true iff it finds a legacy taglet, but for
+ * all other conditions including errors it returns false, allowing
+ * nature to take its own course.
+ */
+ private boolean hasOldTaglet(List<String> tagletNames, List<File> tagletPaths) {
+ if (!fileManager.hasLocation(TAGLET_PATH)) {
+ try {
+ ((StandardJavaFileManager) fileManager).setLocation(TAGLET_PATH, tagletPaths);
+ } catch (IOException ioe) {
+ error("main.doclet_could_not_set_location", tagletPaths);
+ throw new Messager.ExitJavadoc();
+ }
+ }
+ ClassLoader cl = fileManager.getClassLoader(TAGLET_PATH);
+ if (cl == null) {
+ // no classloader found!
+ error("main.doclet_no_classloader_found", tagletNames.get(0));
+ throw new Messager.ExitJavadoc();
+ }
+ for (String tagletName : tagletNames) {
+ try {
+ Class<?> klass = cl.loadClass(tagletName);
+ ensureReadable(klass);
+ if (com.sun.tools.doclets.Taglet.class.isAssignableFrom(klass)) {
+ return true;
+ }
+ } catch (ClassNotFoundException cnfe) {
+ error("main.doclet_class_not_found", tagletName);
+ throw new Messager.ExitJavadoc();
+ }
+ }
+ return false;
}
private void parseArgs(List<String> args, List<String> javaNames) {
@@ -595,16 +668,14 @@
usage(true);
}
- // a terminal call, will not return
- void panic(String key, Object... args) {
- error(key, args);
- messager.exit();
- }
-
void error(String key, Object... args) {
messager.error(key, args);
}
+ void warn(String key, Object... args) {
+ messager.warning(key, args);
+ }
+
/**
* indicate an option with no arguments was given.
*/
diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/resources/javadoc.properties b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/resources/javadoc.properties
index f9cab63..71fab2b 100644
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/resources/javadoc.properties
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/resources/javadoc.properties
@@ -73,7 +73,8 @@
\ given module. <other-module> may be ALL-UNNAMED to require\n\
\ the unnamed module.\n\
\ -Xmodule:<module-name> Specify a module to which the classes being compiled belong.\n\
-\ -Xpatch:<path> Specify location of module class files to patch\n
+\ -Xpatch:<path> Specify location of module class files to patch\n\
+\ -Xold Invoke the legacy javadoc tool\n
main.Xusage.foot=\
These options are non-standard and subject to change without notice.
@@ -96,6 +97,7 @@
such as -J-Xmx32m.
main.done_in=[done in {0} ms]
main.more_than_one_doclet_specified_0_and_1=More than one doclet specified ({0} and {1}).
+main.doclet_could_not_set_location=Could not set location for {0}
main.doclet_no_classloader_found=Could not obtain classloader to load {0}
main.could_not_instantiate_class=Could not instantiate class {0}
main.doclet_class_not_found=Cannot find doclet class {0}
@@ -109,10 +111,15 @@
main.unsupported.release.version=release version {0} not supported
main.release.not.standard.file.manager=-release option specified, but the provided JavaFileManager is not a StandardJavaFileManager.
main.unknown.error=an unknown error has occurred
+main.legacy_api=The old Doclet and Taglet APIs in the packages\n\
+ com.sun.javadoc, com.sun.tools.doclets and their implementations\n\
+ are planned to be removed in a future JDK release. These\n\
+ components have been superseded by the new APIs in jdk.javadoc.doclet.\n\
+ Users are strongly recommended to migrate to the new APIs.\n
+
javadoc.class_not_found=Class {0} not found.
javadoc.error=error
javadoc.warning=warning
-
javadoc.error.msg={0}: error - {1}
javadoc.warning.msg={0}: warning - {1}
javadoc.note.msg = {1}
diff --git a/langtools/test/jdk/javadoc/tool/EnsureNewOldDoclet.java b/langtools/test/jdk/javadoc/tool/EnsureNewOldDoclet.java
index 9d9547e..6e56617 100644
--- a/langtools/test/jdk/javadoc/tool/EnsureNewOldDoclet.java
+++ b/langtools/test/jdk/javadoc/tool/EnsureNewOldDoclet.java
@@ -23,96 +23,349 @@
/*
* @test
- * @bug 8035473
- * @summary make sure the new doclet is invoked by default, and -Xold
+ * @bug 8035473 8154482
+ * @summary make sure the javadoc tool responds correctly to Xold,
+ * old doclets and taglets.
+ * @library /tools/lib
+ * @build toolbox.ToolBox toolbox.TestRunner
+ * @run main EnsureNewOldDoclet
*/
import java.io.*;
-import java.util.ArrayList;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import com.sun.javadoc.Tag;
+import com.sun.source.doctree.DocTree;
+
+import toolbox.*;
+
/**
- * Dummy javadoc comment.
+ * This test ensures the doclet responds correctly when given
+ * various conditions that force a fall back to the old javadoc
+ * tool. The following condition in the order described will
+ * force a dispatch to the old tool, -Xold, old doclet and old taglet.
+ *
*/
-public class EnsureNewOldDoclet {
+public class EnsureNewOldDoclet extends TestRunner {
- final File javadoc;
+ final ToolBox tb;
final File testSrc;
- final String thisClassName;
+ final Path javadocPath;
+ final ExecTask task;
+ final String testClasses;
+ final PrintStream ostream;
- final static Pattern Expected1 = Pattern.compile("^Standard Doclet \\(Next\\) version.*");
- final static Pattern Expected2 = Pattern.compile("^Standard Doclet version.*");
+ final static String CLASS_NAME = "EnsureNewOldDoclet";
+ final static String OLD_DOCLET_CLASS_NAME = CLASS_NAME + "$OldDoclet";
+ final static String NEW_DOCLET_CLASS_NAME = CLASS_NAME + "$NewDoclet"; //unused
+ final static String OLD_TAGLET_CLASS_NAME = CLASS_NAME + "$OldTaglet";
+ final static String NEW_TAGLET_CLASS_NAME = CLASS_NAME + "$NewTaglet";
- public EnsureNewOldDoclet() {
- File javaHome = new File(System.getProperty("java.home"));
- if (javaHome.getName().endsWith("jre"))
- javaHome = javaHome.getParentFile();
- javadoc = new File(new File(javaHome, "bin"), "javadoc");
- testSrc = new File(System.getProperty("test.src"));
- thisClassName = EnsureNewOldDoclet.class.getName();
+ final static Pattern OLD_HEADER = Pattern.compile("^Standard Doclet \\(Old\\) version.*");
+ final static Pattern NEW_HEADER = Pattern.compile("^Standard Doclet version.*");
+
+
+ final static String OLD_DOCLET_MARKER = "OLD_DOCLET_MARKER";
+ final static String OLD_TAGLET_MARKER = "Registered: OldTaglet";
+
+ final static String NEW_DOCLET_MARKER = "NEW_DOCLET_MARKER";
+ final static String NEW_TAGLET_MARKER = "Registered Taglet " + CLASS_NAME + "\\$NewTaglet";
+
+ final static Pattern WARN_TEXT = Pattern.compile("Users are strongly recommended to migrate" +
+ " to the new APIs.");
+ final static String OLD_DOCLET_ERROR = "java.lang.NoSuchMethodException: " +
+ CLASS_NAME +"\\$NewTaglet";
+ final static Pattern NEW_DOCLET_ERROR = Pattern.compile(".*java.lang.ClassCastException.*Taglet " +
+ CLASS_NAME + "\\$OldTaglet.*");
+
+ final static String OLD_STDDOCLET = "com.sun.tools.doclets.standard.Standard";
+ final static String NEW_STDDOCLET = "jdk.javadoc.internal.doclets.standard.Standard";
+
+
+ public EnsureNewOldDoclet() throws Exception {
+ super(System.err);
+ ostream = System.err;
+ testClasses = System.getProperty("test.classes");
+ tb = new ToolBox();
+ javadocPath = tb.getJDKTool("javadoc");
+ task = new ExecTask(tb, javadocPath);
+ testSrc = new File("Foo.java");
+ generateSample(testSrc);
+ }
+
+ void generateSample(File testSrc) throws Exception {
+ String nl = System.getProperty("line.separator");
+ String src = Arrays.asList(
+ "/**",
+ " * A test class to test javadoc. Nothing more nothing less.",
+ " */",
+ " public class Foo{}").stream().collect(Collectors.joining(nl));
+ tb.writeFile(testSrc.getPath(), src);
}
public static void main(String... args) throws Exception {
- EnsureNewOldDoclet test = new EnsureNewOldDoclet();
- test.run1();
- test.run2();
+ new EnsureNewOldDoclet().runTests();
}
- // make sure new doclet is invoked by default
- void run1() throws Exception {
- List<String> output = doTest(javadoc.getPath(),
- "-classpath", ".", // insulates us from ambient classpath
- "-Xdoclint:none",
- "-package",
- new File(testSrc, thisClassName + ".java").getPath());
- System.out.println(output);
- for (String x : output) {
- if (Expected1.matcher(x).matches()) {
+ // input: nothing, default mode
+ // outcome: new tool and new doclet
+ @Test
+ public void testDefault() throws Exception {
+ setArgs("-classpath", ".", // insulates us from ambient classpath
+ testSrc.toString());
+ Task.Result tr = task.run(Task.Expect.SUCCESS);
+ List<String> out = tr.getOutputLines(Task.OutputKind.STDOUT);
+ checkOutput(testName, out, NEW_HEADER);
+ }
+
+ // input: -Xold
+ // outcome: old tool
+ @Test
+ public void testXold() throws Exception {
+ setArgs("-Xold",
+ "-classpath", ".", // ambient classpath insulation
+ testSrc.toString());
+ Task.Result tr = task.run(Task.Expect.SUCCESS);
+ List<String> out = tr.getOutputLines(Task.OutputKind.STDOUT);
+ List<String> err = tr.getOutputLines(Task.OutputKind.STDERR);
+ checkOutput(testName, out, OLD_HEADER);
+ checkOutput(testName, err, WARN_TEXT);
+ }
+
+ // input: old doclet
+ // outcome: old tool
+ @Test
+ public void testOldDoclet() throws Exception {
+ setArgs("-classpath", ".", // ambient classpath insulation
+ "-doclet",
+ OLD_DOCLET_CLASS_NAME,
+ "-docletpath",
+ testClasses,
+ testSrc.toString());
+ Task.Result tr = task.run(Task.Expect.SUCCESS);
+ List<String> out = tr.getOutputLines(Task.OutputKind.STDOUT);
+ List<String> err = tr.getOutputLines(Task.OutputKind.STDERR);
+ checkOutput(testName, out, OLD_DOCLET_MARKER);
+ checkOutput(testName, err, WARN_TEXT);
+ }
+
+ // input: old taglet
+ // outcome: old tool
+ @Test
+ public void testOldTaglet() throws Exception {
+ setArgs("-classpath", ".", // ambient classpath insulation
+ "-taglet",
+ OLD_TAGLET_CLASS_NAME,
+ "-tagletpath",
+ testClasses,
+ testSrc.toString());
+ Task.Result tr = task.run(Task.Expect.SUCCESS);
+ List<String> out = tr.getOutputLines(Task.OutputKind.STDOUT);
+ List<String> err = tr.getOutputLines(Task.OutputKind.STDERR);
+ checkOutput(testName, out, OLD_TAGLET_MARKER);
+ checkOutput(testName, err, WARN_TEXT);
+ }
+
+ // input: new doclet and old taglet
+ // outcome: new doclet with failure
+ @Test
+ public void testNewDocletOldTaglet() throws Exception {
+ setArgs("-classpath", ".", // ambient classpath insulation
+ "-doclet",
+ NEW_STDDOCLET,
+ "-taglet",
+ OLD_TAGLET_CLASS_NAME,
+ "-tagletpath",
+ testClasses,
+ testSrc.toString());
+ Task.Result tr = task.run(Task.Expect.FAIL, 1);
+ //Task.Result tr = task.run();
+ List<String> out = tr.getOutputLines(Task.OutputKind.STDOUT);
+ List<String> err = tr.getOutputLines(Task.OutputKind.STDERR);
+ checkOutput(testName, out, NEW_HEADER);
+ checkOutput(testName, err, NEW_DOCLET_ERROR);
+ }
+
+ // input: old doclet and old taglet
+ // outcome: old doclet and old taglet should register
+ @Test
+ public void testOldDocletOldTaglet() throws Exception {
+ setArgs("-classpath", ".", // ambient classpath insulation
+ "-doclet",
+ OLD_STDDOCLET,
+ "-taglet",
+ OLD_TAGLET_CLASS_NAME,
+ "-tagletpath",
+ testClasses,
+ testSrc.toString());
+ Task.Result tr = task.run(Task.Expect.SUCCESS);
+ List<String> out = tr.getOutputLines(Task.OutputKind.STDOUT);
+ List<String> err = tr.getOutputLines(Task.OutputKind.STDERR);
+ checkOutput(testName, out, OLD_HEADER);
+ checkOutput(testName, out, OLD_TAGLET_MARKER);
+ checkOutput(testName, err, WARN_TEXT);
+ }
+
+ // input: new doclet and new taglet
+ // outcome: new doclet and new taglet should register
+ @Test
+ public void testNewDocletNewTaglet() throws Exception {
+ setArgs("-classpath", ".", // ambient classpath insulation
+ "-doclet",
+ NEW_STDDOCLET,
+ "-taglet",
+ NEW_TAGLET_CLASS_NAME,
+ "-tagletpath",
+ testClasses,
+ testSrc.toString());
+ Task.Result tr = task.run(Task.Expect.SUCCESS);
+ List<String> out = tr.getOutputLines(Task.OutputKind.STDOUT);
+ List<String> err = tr.getOutputLines(Task.OutputKind.STDERR);
+ checkOutput(testName, out, NEW_HEADER);
+ checkOutput(testName, out, NEW_TAGLET_MARKER);
+ }
+
+ // input: old doclet and new taglet
+ // outcome: old doclet and error
+ @Test
+ public void testOldDocletNewTaglet() throws Exception {
+ setArgs("-classpath", ".", // ambient classpath insulation
+ "-doclet",
+ OLD_STDDOCLET,
+ "-taglet",
+ NEW_TAGLET_CLASS_NAME,
+ "-tagletpath",
+ testClasses,
+ testSrc.toString());
+ Task.Result tr = task.run(Task.Expect.FAIL, 1);
+ List<String> out = tr.getOutputLines(Task.OutputKind.STDOUT);
+ List<String> err = tr.getOutputLines(Task.OutputKind.STDERR);
+ checkOutput(testName, out, OLD_HEADER);
+ checkOutput(testName, err, WARN_TEXT);
+ checkOutput(testName, err, OLD_DOCLET_ERROR);
+ }
+
+ void setArgs(String... args) {
+ ostream.println("cmds: " + Arrays.asList(args));
+ task.args(args);
+ }
+
+ void checkOutput(String testCase, List<String> content, String toFind) throws Exception {
+ checkOutput(testCase, content, Pattern.compile(".*" + toFind + ".*"));
+ }
+
+ void checkOutput(String testCase, List<String> content, Pattern toFind) throws Exception {
+ ostream.println("---" + testCase + "---");
+ content.stream().forEach(x -> System.out.println(x));
+ for (String x : content) {
+ ostream.println(x);
+ if (toFind.matcher(x).matches()) {
return;
}
}
- throw new Exception("run1: Expected string not found:");
+ throw new Exception(testCase + ": Expected string not found: " + toFind);
}
- // make sure the old doclet is invoked with -Xold
- void run2() throws Exception {
- List<String> output = doTest(javadoc.getPath(),
- "-Xold",
- "-classpath", ".", // insulates us from ambient classpath
- "-Xdoclint:none",
- "-package",
- new File(testSrc, thisClassName + ".java").getPath());
-
- for (String x : output) {
- if (Expected2.matcher(x).matches()) {
- throw new Exception("run2: Expected string not found");
- }
- return;
+ public static class OldDoclet extends com.sun.javadoc.Doclet {
+ public static boolean start(com.sun.javadoc.RootDoc root) {
+ System.out.println(OLD_DOCLET_MARKER);
+ return true;
}
}
- /**
- * More dummy comments.
- */
- List<String> doTest(String... args) throws Exception {
- List<String> output = new ArrayList<>();
- // run javadoc in separate process to ensure doclet executed under
- // normal user conditions w.r.t. classloader
- Process p = new ProcessBuilder()
- .command(args)
- .redirectErrorStream(true)
- .start();
- try (BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
- String line = in.readLine();
- while (line != null) {
- output.add(line.trim());
- line = in.readLine();
- }
+ public static class OldTaglet implements com.sun.tools.doclets.Taglet {
+
+ public static void register(Map map) {
+ EnsureNewOldDoclet.OldTaglet tag = new OldTaglet();
+ com.sun.tools.doclets.Taglet t = (com.sun.tools.doclets.Taglet) map.get(tag.getName());
+ System.out.println(OLD_TAGLET_MARKER);
}
- int rc = p.waitFor();
- if (rc != 0)
- throw new Exception("javadoc failed, rc:" + rc);
- return output;
+
+ @Override
+ public boolean inField() {
+ return true;
+ }
+
+ @Override
+ public boolean inConstructor() {
+ return true;
+ }
+
+ @Override
+ public boolean inMethod() {
+ return true;
+ }
+
+ @Override
+ public boolean inOverview() {
+ return true;
+ }
+
+ @Override
+ public boolean inPackage() {
+ return true;
+ }
+
+ @Override
+ public boolean inType() {
+ return true;
+ }
+
+ @Override
+ public boolean isInlineTag() {
+ return true;
+ }
+
+ @Override
+ public String getName() {
+ return "OldTaglet";
+ }
+
+ @Override
+ public String toString(Tag tag) {
+ return getName();
+ }
+
+ @Override
+ public String toString(Tag[] tags) {
+ return getName();
+ }
+ }
+
+ public static class NewTaglet implements jdk.javadoc.doclet.taglet.Taglet {
+
+ @Override
+ public Set<Location> getAllowedLocations() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public boolean isInlineTag() {
+ return true;
+ }
+
+ @Override
+ public String getName() {
+ return "NewTaglet";
+ }
+
+ @Override
+ public String toString(DocTree tag) {
+ return tag.toString();
+ }
+
+ @Override
+ public String toString(List<? extends DocTree> tags) {
+ return tags.toString();
+ }
+
}
}
diff --git a/langtools/test/tools/lib/toolbox/TestRunner.java b/langtools/test/tools/lib/toolbox/TestRunner.java
new file mode 100644
index 0000000..2203473
--- /dev/null
+++ b/langtools/test/tools/lib/toolbox/TestRunner.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2016, 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.
+ */
+
+package toolbox;
+
+import java.io.PrintStream;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.function.Function;
+
+/**
+ * Utility class to manage and execute sub-tests within a test.
+ *
+ * This class does the following:
+ * i. invokes those test methods annotated with @Test
+ * ii. keeps track of successful and failed tests
+ * iii. throws an Exception if any test fails.
+ * iv. provides a test summary at the end of the run.
+ *
+ * Tests must extend this class, annotate the test methods
+ * with @Test and call one of the runTests method.
+ */
+
+public abstract class TestRunner {
+ /** Marker annotation for test cases. */
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface Test { }
+
+ int testCount = 0;
+ int errorCount = 0;
+
+ public String testName = null;
+
+ final PrintStream out;
+
+ /**
+ * Constructs the Object.
+ * @param out the PrintStream to print output to.
+ */
+ public TestRunner(PrintStream out) {
+ this.out = out;
+ }
+
+ /**
+ * Invoke all methods annotated with @Test.
+ * @throws java.lang.Exception
+ */
+ public void runTests() throws Exception {
+ runTests(f -> new Object[0]);
+ }
+
+ /**
+ * Invoke all methods annotated with @Test.
+ * @param f a lambda expression to specify arguments.
+ * @throws java.lang.Exception
+ */
+ public void runTests(Function<Method, Object[]> f) throws Exception {
+ for (Method m : getClass().getDeclaredMethods()) {
+ Annotation a = m.getAnnotation(Test.class);
+ if (a != null) {
+ testName = m.getName();
+ try {
+ testCount++;
+ out.println("test: " + testName);
+ m.invoke(this, f.apply(m));
+ } catch (InvocationTargetException e) {
+ errorCount++;
+ Throwable cause = e.getCause();
+ out.println("Exception: " + e.getCause());
+ cause.printStackTrace(out);
+ }
+ out.println();
+ }
+ }
+
+ if (testCount == 0) {
+ throw new Error("no tests found");
+ }
+
+ StringBuilder summary = new StringBuilder();
+ if (testCount != 1) {
+ summary.append(testCount).append(" tests");
+ }
+ if (errorCount > 0) {
+ if (summary.length() > 0) {
+ summary.append(", ");
+ }
+ summary.append(errorCount).append(" errors");
+ }
+ out.println(summary);
+ if (errorCount > 0) {
+ throw new Exception(errorCount + " errors found");
+ }
+ }
+}