mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-20 12:53:29 +00:00
8276892: Provide a way to emulate exceptional situations in FileManager when using JavadocTester
Reviewed-by: prappo
This commit is contained in:
parent
22c15ddae5
commit
d52392c15e
@ -45,7 +45,7 @@ doclet.exception.write.file=Error writing file: {0}\n\
|
||||
\t({1})
|
||||
doclet.exception.read.resource=Error reading system resource: {0}\n\
|
||||
\t({1})
|
||||
doclet.internal.exception=An internal exception has occurred. \n\
|
||||
doclet.internal.exception=An internal exception has occurred.\n\
|
||||
\t({0})
|
||||
doclet.internal.report.bug=\
|
||||
Please file a bug against the javadoc tool via the Java bug reporting page\n\
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2021, 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
|
||||
@ -24,7 +24,12 @@
|
||||
*/
|
||||
package jdk.javadoc.internal.tool;
|
||||
|
||||
import javax.tools.JavaFileManager;
|
||||
import javax.tools.StandardJavaFileManager;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.sun.tools.javac.util.Context;
|
||||
|
||||
/**
|
||||
* Provides external entry points (tool and programmatic) for the javadoc program.
|
||||
@ -37,11 +42,6 @@ import java.io.PrintWriter;
|
||||
|
||||
public class Main {
|
||||
|
||||
/**
|
||||
* This constructor should never be called.
|
||||
*/
|
||||
private Main() { throw new AssertionError(); }
|
||||
|
||||
/**
|
||||
* The main entry point called by the launcher. This will call
|
||||
* System.exit with an appropriate return value.
|
||||
@ -88,6 +88,65 @@ public class Main {
|
||||
return jdoc.begin(args).exitCode;
|
||||
}
|
||||
|
||||
|
||||
// builder-style API to run javadoc
|
||||
|
||||
private PrintWriter outWriter;
|
||||
private PrintWriter errWriter;
|
||||
private StandardJavaFileManager fileManager;
|
||||
|
||||
/**
|
||||
* Creates a default builder to run javadoc.
|
||||
*/
|
||||
public Main() { }
|
||||
|
||||
/**
|
||||
* Sets the output and error streams to be used when running javadoc.
|
||||
* The streams may be the same; they must not be {@code null}.
|
||||
*
|
||||
* @param outWriter the output stream
|
||||
* @param errWriter the error stream
|
||||
*
|
||||
* @return this object
|
||||
*/
|
||||
public Main setStreams(PrintWriter outWriter, PrintWriter errWriter) {
|
||||
this.outWriter = Objects.requireNonNull(outWriter);
|
||||
this.errWriter = Objects.requireNonNull(errWriter);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the file manager to be used when running javadoc.
|
||||
* A value of {@code null} means to use the default file manager.
|
||||
*
|
||||
* @param fileManager the file manager to use
|
||||
*
|
||||
* @return this object
|
||||
*/
|
||||
public Main setFileManager(StandardJavaFileManager fileManager) {
|
||||
this.fileManager = fileManager;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs javadoc with preconfigured values and a given set of arguments.
|
||||
* Any errors will be reported to the error stream, or to {@link System#err}
|
||||
* if no error stream has been specified with {@code setStreams}.
|
||||
*
|
||||
* @param args the arguments
|
||||
*
|
||||
* @return a value indicating the success or otherwise of the run
|
||||
*/
|
||||
public Result run(String... args) {
|
||||
Context context = null;
|
||||
if (fileManager != null) {
|
||||
context = new Context();
|
||||
context.put(JavaFileManager.class, fileManager);
|
||||
}
|
||||
Start jdoc = new Start(context, null, outWriter, errWriter, null, null);
|
||||
return jdoc.begin(args);
|
||||
}
|
||||
|
||||
public enum Result {
|
||||
/** completed with no errors */
|
||||
OK(0),
|
||||
|
||||
@ -36,6 +36,7 @@ import java.util.Comparator;
|
||||
import java.util.IllformedLocaleException;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
@ -520,7 +521,21 @@ public class Start {
|
||||
}
|
||||
|
||||
if (fileManager instanceof BaseFileManager bfm) {
|
||||
// standard file manager: use direct support for handling options
|
||||
bfm.handleOptions(options.fileManagerOptions());
|
||||
} else {
|
||||
// unrecognized file manager:
|
||||
for (Map.Entry<com.sun.tools.javac.main.Option, String> e: options.fileManagerOptions().entrySet()) {
|
||||
String optName = e.getKey().getPrimaryName();
|
||||
String optValue = e.getValue();
|
||||
try {
|
||||
if (!fileManager.handleOption(optName, List.of(optValue).iterator())) {
|
||||
log.error("main.unknown.option.for.filemanager", optName);
|
||||
}
|
||||
} catch (IllegalArgumentException ex) {
|
||||
log.error("main.bad.arg.for.filemanager.option", optName, ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String mr = com.sun.tools.javac.main.Option.MULTIRELEASE.primaryName;
|
||||
|
||||
@ -302,6 +302,8 @@ main.warnings.Werror=warnings found and -Werror specified
|
||||
main.unknown.error=an unknown error has occurred
|
||||
main.internal.error=an internal error has occurred
|
||||
main.unexpected.exception=an unexpected exception was caught: {0}
|
||||
main.unknown.option.for.filemanager=option not supported by file manager: {0}
|
||||
main.bad.arg.for.filemanager.option=bad value for file manager option {0}: "{1}"
|
||||
doclet.internal.report.bug=\
|
||||
Please file a bug against the javadoc tool via the Java bug reporting page\n\
|
||||
(http://bugreport.java.com) after checking the Bug Database (http://bugs.java.com)\n\
|
||||
|
||||
@ -58,6 +58,7 @@ import java.util.function.Function;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.tools.StandardJavaFileManager;
|
||||
|
||||
|
||||
/**
|
||||
@ -245,6 +246,7 @@ public abstract class JavadocTester {
|
||||
private boolean automaticCheckLinks = true;
|
||||
private boolean automaticCheckUniqueOUT = true;
|
||||
private boolean useStandardStreams = false;
|
||||
private StandardJavaFileManager fileManager = null;
|
||||
|
||||
/** The current subtest number. Incremented when checking(...) is called. */
|
||||
private int numTestsRun = 0;
|
||||
@ -371,9 +373,17 @@ public abstract class JavadocTester {
|
||||
StreamOutput sysErr = new StreamOutput(System.err, System::setErr);
|
||||
|
||||
try {
|
||||
exitCode = useStandardStreams
|
||||
? jdk.javadoc.internal.tool.Main.execute(args) // use sysOut, sysErr
|
||||
: jdk.javadoc.internal.tool.Main.execute(args, outOut.pw); // default
|
||||
jdk.javadoc.internal.tool.Main main = new jdk.javadoc.internal.tool.Main();
|
||||
if (useStandardStreams) {
|
||||
// use sysOut, sysErr
|
||||
} else {
|
||||
// default: use single explicit stream
|
||||
main.setStreams(outOut.pw, outOut.pw);
|
||||
}
|
||||
if (fileManager != null) {
|
||||
main.setFileManager(fileManager);
|
||||
}
|
||||
exitCode = main.run(args).exitCode;
|
||||
} finally {
|
||||
outputMap.put(Output.STDOUT, sysOut.close());
|
||||
outputMap.put(Output.STDERR, sysErr.close());
|
||||
@ -442,6 +452,15 @@ public abstract class JavadocTester {
|
||||
useStandardStreams = b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the file manager to use for subsequent invocations of javadoc.
|
||||
* If {@code null}, a default file manager will be created and used
|
||||
* for each invocation.
|
||||
*/
|
||||
public void setFileManager(StandardJavaFileManager fm) {
|
||||
fileManager = fm;
|
||||
}
|
||||
|
||||
/**
|
||||
* The exit codes returned by the javadoc tool.
|
||||
* @see jdk.javadoc.internal.tool.Main.Result
|
||||
|
||||
@ -0,0 +1,306 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 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 javadoc.tester;
|
||||
|
||||
import javax.tools.FileObject;
|
||||
import javax.tools.JavaFileManager;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.StandardJavaFileManager;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.lang.reflect.UndeclaredThrowableException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* A builder to create "test file managers" that can return "test file objects".
|
||||
* All such objects can throw user-provided exceptions when specified methods
|
||||
* are called. This is done by registering "handlers" to be associated with individual
|
||||
* methods.
|
||||
*
|
||||
* The file objects that are returned as "test file objects" are filtered by a predicate
|
||||
* on the file object.
|
||||
*
|
||||
* Note that "test file objects" passed as arguments to methods on the "test file manager"
|
||||
* that created them are unwrapped, and replaced by the original file object.
|
||||
* This ensures that the underlying file manager sees the underlying file objects,
|
||||
* for cases when the identity of the file objects is important.
|
||||
* However, it does mean that methods on file objects called internally by a
|
||||
* file manager will not throw any user-provided exceptions.
|
||||
*
|
||||
* For now, the handlers for a file object are simply grouped by predicate and then by
|
||||
* method, and the group of methods used for a "test file object" is determined by the
|
||||
* first predicate that matches.
|
||||
* An alternative, more expensive, implementation would be to group the handlers
|
||||
* by method and predicate and then dynamically build the set of methods to be used for
|
||||
* a file object by filtering the methods by their applicable predicate.
|
||||
*/
|
||||
public class TestJavaFileManagerBuilder {
|
||||
private final StandardJavaFileManager fm;
|
||||
private Map<Method, BiFunction<JavaFileManager, Object[], Throwable>> fileManagerHandlers;
|
||||
|
||||
private record FileObjectHandlers(Predicate<JavaFileObject> filter,
|
||||
Map<Method, BiFunction<JavaFileObject, Object[], Throwable>> handlers) { }
|
||||
private final List<FileObjectHandlers> fileObjectHandlers;
|
||||
|
||||
public TestJavaFileManagerBuilder(StandardJavaFileManager fm) {
|
||||
this.fm = fm;
|
||||
fileManagerHandlers = Collections.emptyMap();
|
||||
fileObjectHandlers = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides functions to be called when given file manager methods are called.
|
||||
* The function should either return an exception to be thrown, or {@code null}
|
||||
* to indicate that no exception should be thrown.
|
||||
*
|
||||
* <p>It is an error for any function to return a checked exception that is not
|
||||
* declared by the method. This error will result in {@link UndeclaredThrowableException}
|
||||
* being thrown when the method is called.
|
||||
*
|
||||
* @param handlers a map giving the function to be called before a file manager method is invoked
|
||||
*
|
||||
* @return this object
|
||||
*
|
||||
* @throws IllegalArgumentException if any key in the map of handlers is a method that is not
|
||||
* declared in {@code JavaFileManager}
|
||||
*/
|
||||
public TestJavaFileManagerBuilder handle(Map<Method, BiFunction<JavaFileManager, Object[], Throwable>> handlers) {
|
||||
handlers.forEach((m, h) -> {
|
||||
if (!JavaFileManager.class.isAssignableFrom(m.getDeclaringClass())) {
|
||||
throw new IllegalArgumentException(("not a method on JavaFileManager: " + m));
|
||||
}
|
||||
});
|
||||
|
||||
fileManagerHandlers = handlers;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides functions to be called when given file object methods are called,
|
||||
* for file objects that match a given predicate.
|
||||
* The function should either return an exception to be thrown, or {@code null}
|
||||
* to indicate that no exception should be thrown.
|
||||
*
|
||||
* <p>It is an error for the function to return a checked exception that is not
|
||||
* declared by the method. This error will result in {@link UndeclaredThrowableException}
|
||||
* being thrown when the method is called.
|
||||
*
|
||||
* <p>When subsequently finding the handlers to be used for a particular file object, the various
|
||||
* predicates passed to this method will be tested in the order that they were registered.
|
||||
* The handlers associated with the first matching predicate will be used.
|
||||
*
|
||||
* @apiNote Examples of predicates include:
|
||||
* <ul>
|
||||
* <li>using {@code .equals} or {@link JavaFileObject#isNameCompatible(String, JavaFileObject.Kind)}
|
||||
* to match a specific file object,
|
||||
* <li>using string or regular expression operations on the name or URI of the file object,
|
||||
* <li>using {@code Path} operations on the file object's {@link StandardJavaFileManager#asPath(FileObject) path}.
|
||||
* </ul>
|
||||
*
|
||||
* @param filter the predicate used to identify file objects for which the handlers are applicable
|
||||
* @param handlers a map giving the function to be called before a file object method is invoked
|
||||
*
|
||||
* @return this object
|
||||
*
|
||||
* @throws IllegalArgumentException if any key in the map is a method that is not declared in a class
|
||||
* that is assignable to {@code FileObject}
|
||||
*/
|
||||
public TestJavaFileManagerBuilder handle(Predicate<JavaFileObject> filter,
|
||||
Map<Method, BiFunction<JavaFileObject, Object[], Throwable>> handlers) {
|
||||
handlers.forEach((m, h) -> {
|
||||
if (!FileObject.class.isAssignableFrom(m.getDeclaringClass())) {
|
||||
throw new IllegalArgumentException(("not a method on FileObject: " + m));
|
||||
}
|
||||
});
|
||||
|
||||
fileObjectHandlers.add(new FileObjectHandlers(filter, handlers));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return a file manager configured with the given handlers}
|
||||
*/
|
||||
public StandardJavaFileManager build() {
|
||||
return (StandardJavaFileManager) Proxy.newProxyInstance(getClass().getClassLoader(),
|
||||
new Class<?>[] { StandardJavaFileManager.class },
|
||||
new JavaFileManager_InvocationHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
* An invocation handler for "test file managers", which provides "test file objects"
|
||||
* that may be configured to invoke functions to handle selected methods.
|
||||
*/
|
||||
private class JavaFileManager_InvocationHandler implements InvocationHandler {
|
||||
// a cache of "real file object" -> "proxy file object".
|
||||
Map<JavaFileObject, JavaFileObject> cache = new WeakHashMap<>();
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
Object result = handleMethod(fm, method, unwrap(args));
|
||||
|
||||
if (result instanceof Iterable iterable) {
|
||||
// All methods on StandardJavaFileManager that return Iterable<T> for some T
|
||||
// are such that T is one of ? extends [Java]FileObject, ? extends File, ? extends Path.
|
||||
// If the result is empty, return it unchanged; otherwise check the first
|
||||
// element to determine the type of the iterable, and if it is an iterable of
|
||||
// file objects, post-process the result to use proxy file objects where appropriate.
|
||||
// Note 1: this assumes that no methods return a mixture of FileObject and JavaFileObject.
|
||||
// Note 2: all file objects returned by the standard file manager are instances of javaFileObject
|
||||
Iterator<?> iter = iterable.iterator();
|
||||
if (iter.hasNext() && iter.next() instanceof JavaFileObject) {
|
||||
List<JavaFileObject> list = new ArrayList<>();
|
||||
for (JavaFileObject jfo : (Iterable<JavaFileObject>) iterable) {
|
||||
list.add(wrap(jfo));
|
||||
}
|
||||
return list;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
} else if (result instanceof JavaFileObject jfo) {
|
||||
return wrap(jfo);
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a proxy file object that either calls handler functions for specific methods
|
||||
* or delegates to an underlying file object.
|
||||
*
|
||||
* @param jfo the underlying file object
|
||||
*
|
||||
* @return the proxy file object
|
||||
*/
|
||||
private JavaFileObject wrap(JavaFileObject jfo) {
|
||||
return fileObjectHandlers.stream()
|
||||
.filter(e -> e.filter().test(jfo))
|
||||
.findFirst()
|
||||
.map(e -> cache.computeIfAbsent(jfo, jfo_ -> createProxyFileObject(jfo_, e.handlers())))
|
||||
.orElse(jfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a proxy file object that either calls handler functions for specific methods
|
||||
* or delegates to an underlying file object.
|
||||
*
|
||||
* @param jfo the underlying file object
|
||||
* @param handlers the handlers
|
||||
*
|
||||
* @return the proxy file object
|
||||
*/
|
||||
private JavaFileObject createProxyFileObject(JavaFileObject jfo,
|
||||
Map<Method, BiFunction<JavaFileObject, Object[], Throwable>> handlers) {
|
||||
return (JavaFileObject) Proxy.newProxyInstance(getClass().getClassLoader(),
|
||||
new Class<?>[] { JavaFileObject.class },
|
||||
new JavaFileObject_InvocationHandler(jfo, handlers));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return an array of objects with any proxy file objects replaced by their underlying
|
||||
* delegate value}
|
||||
*
|
||||
* If there are no proxy objects in the array, the original array is returned.
|
||||
*
|
||||
* @param args the array of values
|
||||
*/
|
||||
private Object[] unwrap(Object[] args) {
|
||||
if (!containsProxyFileObject(args)) {
|
||||
return args;
|
||||
}
|
||||
|
||||
Object[] uArgs = new Object[args.length];
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
Object arg = args[i];
|
||||
uArgs[i] = (Proxy.isProxyClass(arg.getClass())
|
||||
&& Proxy.getInvocationHandler(arg) instanceof JavaFileObject_InvocationHandler ih)
|
||||
? ih.jfo
|
||||
: arg;
|
||||
}
|
||||
return uArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return {@code true} if an array of objects contains any proxy file objects,
|
||||
* and {@code false} otherwise}
|
||||
*
|
||||
* @param args the array of objects
|
||||
*/
|
||||
private boolean containsProxyFileObject(Object[] args) {
|
||||
for (Object arg : args) {
|
||||
if (arg != null && Proxy.isProxyClass(arg.getClass())
|
||||
&& Proxy.getInvocationHandler(arg) instanceof JavaFileObject_InvocationHandler) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Object handleMethod(JavaFileManager fm, Method method, Object[] args) throws Throwable {
|
||||
var handler = fileManagerHandlers.get(method);
|
||||
if (handler != null) {
|
||||
Throwable t = handler.apply(fm, args);
|
||||
if (t != null) {
|
||||
throw t;
|
||||
}
|
||||
}
|
||||
|
||||
return method.invoke(fm, args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An invocation handler for "test file objects" which can be configured to call functions
|
||||
* to handle the calls for individual methods.
|
||||
* It is expected that a common use case is to throw an exception in circumstances that
|
||||
* would otherwise be hard to create.
|
||||
*/
|
||||
private record JavaFileObject_InvocationHandler(JavaFileObject jfo,
|
||||
Map<Method, BiFunction<JavaFileObject, Object[], Throwable>> handlers)
|
||||
implements InvocationHandler {
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
return handleMethod(jfo, method, args);
|
||||
}
|
||||
|
||||
private Object handleMethod(JavaFileObject jfo, Method method, Object[] args) throws Throwable {
|
||||
var handler = handlers.get(method);
|
||||
if (handler != null) {
|
||||
Throwable t = handler.apply(jfo, args);
|
||||
if (t != null) {
|
||||
throw t;
|
||||
}
|
||||
}
|
||||
return method.invoke(jfo, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
242
test/langtools/jdk/javadoc/testTFMBuilder/TestTFMBuilder.java
Normal file
242
test/langtools/jdk/javadoc/testTFMBuilder/TestTFMBuilder.java
Normal file
@ -0,0 +1,242 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 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.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8276892
|
||||
* @summary Provide a way to emulate exceptional situations in FileManager when using JavadocTester
|
||||
* @library /tools/lib/ ../lib
|
||||
* @modules jdk.javadoc/jdk.javadoc.internal.tool
|
||||
* @build toolbox.ToolBox javadoc.tester.*
|
||||
* @run main TestTFMBuilder
|
||||
*/
|
||||
|
||||
|
||||
import javax.tools.DocumentationTool;
|
||||
import javax.tools.JavaFileManager;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.StandardJavaFileManager;
|
||||
import javax.tools.StandardLocation;
|
||||
import javax.tools.ToolProvider;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javadoc.tester.JavadocTester;
|
||||
import javadoc.tester.TestJavaFileManagerBuilder;
|
||||
import toolbox.ToolBox;
|
||||
|
||||
/**
|
||||
* Tests the {@link TestJavaFileManagerBuilder} class.
|
||||
*
|
||||
*/
|
||||
// The use of the contraction TFMBuilder is deliberate, to avoid using
|
||||
// the confusing but otherwise logical name of TestTestJavaFileManagerBuilder
|
||||
public class TestTFMBuilder extends JavadocTester {
|
||||
public static class TestException extends RuntimeException {
|
||||
TestException(JavaFileObject jfo) {
|
||||
this(jfo.getName());
|
||||
}
|
||||
|
||||
TestException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
TestTFMBuilder tester = new TestTFMBuilder();
|
||||
tester.setup().runTests(m -> new Object[] { Path.of(m.getName()) });
|
||||
}
|
||||
|
||||
private Path srcDir = Path.of("src");
|
||||
private Class<?> thisClass = TestTFMBuilder.class;
|
||||
private String thisClassName = thisClass.getName();
|
||||
|
||||
TestTFMBuilder setup() throws Exception {
|
||||
ToolBox tb = new ToolBox();
|
||||
tb.writeJavaFiles(srcDir, """
|
||||
package p;
|
||||
/** Dummy class, to be read by javadoc. {@snippet file="C.properties" } */
|
||||
public class C {
|
||||
private C() { }
|
||||
}""");
|
||||
tb.writeFile(srcDir.resolve("p").resolve("snippet-files").resolve("C.properties"), """
|
||||
dummy content
|
||||
""");
|
||||
return this;
|
||||
}
|
||||
|
||||
StandardJavaFileManager getFileManager() {
|
||||
DocumentationTool dt = ToolProvider.getSystemDocumentationTool();
|
||||
return dt.getStandardFileManager(null, null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleDirectUse(Path base) throws Exception {
|
||||
try (StandardJavaFileManager fm = getFileManager()) {
|
||||
fm.setLocation(StandardLocation.SOURCE_PATH, List.of(Path.of(testSrc).toFile()));
|
||||
|
||||
// obtain a normal file object from the standard file manager
|
||||
JavaFileObject someFileObject =
|
||||
fm.getJavaFileForInput(StandardLocation.SOURCE_PATH, thisClassName, JavaFileObject.Kind.SOURCE);
|
||||
|
||||
// build a file manager that throws an exception when someFileObject is read
|
||||
StandardJavaFileManager tfm = new TestJavaFileManagerBuilder(fm)
|
||||
.handle(jfo -> jfo.equals(someFileObject),
|
||||
Map.of(JavaFileObject.class.getMethod("getCharContent", boolean.class),
|
||||
(fo, args) -> new TestException(fo.getName())))
|
||||
.build();
|
||||
|
||||
// access the "same" file object via the test file manager
|
||||
JavaFileObject someTestFileObject =
|
||||
tfm.getJavaFileForInput(StandardLocation.SOURCE_PATH, thisClassName, JavaFileObject.Kind.SOURCE);
|
||||
|
||||
checking("non-trapped method");
|
||||
try {
|
||||
out.println("someTestFileObject.getName: " + someTestFileObject.getName());
|
||||
passed("method returned normally, as expected");
|
||||
} catch (Throwable t) {
|
||||
failed("method threw unexpected exception: " + t);
|
||||
}
|
||||
|
||||
checking ("trapped method");
|
||||
try {
|
||||
someTestFileObject.getCharContent(true);
|
||||
failed("method returned normally, without throwing an exception");
|
||||
} catch (TestException e) {
|
||||
String expect = someFileObject.getName();
|
||||
String found = e.getMessage();
|
||||
if (found.equals(expect)) {
|
||||
passed("method threw exception as expected");
|
||||
} else {
|
||||
failed("method throw exception with unexpected message:\n"
|
||||
+ "expected: " + expect + "\n"
|
||||
+ " found: " + found);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
failed("method threw unexpected exception: " + t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFileManagerAccess(Path base) throws Exception {
|
||||
try (StandardJavaFileManager fm = getFileManager()) {
|
||||
|
||||
// build a file manager that throws an exception when a specific source file is accessed
|
||||
Method getFileForInput_method = JavaFileManager.class.getMethod("getFileForInput",
|
||||
JavaFileManager.Location.class, String.class, String.class);
|
||||
StandardJavaFileManager tfm = new TestJavaFileManagerBuilder(fm)
|
||||
.handle(Map.of(getFileForInput_method,
|
||||
(fm_, args) -> {
|
||||
var relativeName = (String) args[2];
|
||||
return (relativeName.endsWith("C.properties"))
|
||||
? new TestException("getFileForInput: " + Arrays.asList(args))
|
||||
: null;
|
||||
}))
|
||||
.build();
|
||||
|
||||
try {
|
||||
setFileManager(tfm);
|
||||
javadoc("-d", base.resolve("api").toString(),
|
||||
"-sourcepath", srcDir.toString(),
|
||||
"p");
|
||||
checkExit((Exit.ERROR)); // Ideally, this should be ABNORMAL, but right now, the doclet has no way to indicate that
|
||||
checkOutput(Output.OUT, true,
|
||||
"""
|
||||
error: An internal exception has occurred.
|
||||
\t(##EXC##: getFileForInput: [SOURCE_PATH, p, snippet-files/C.properties])
|
||||
1 error"""
|
||||
.replace("##EXC##", TestException.class.getName()));
|
||||
} finally {
|
||||
setFileManager(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFileObjectRead(Path base) throws Exception {
|
||||
try (StandardJavaFileManager fm = getFileManager()) {
|
||||
|
||||
// build a file manager that throws an exception when any *.java is read
|
||||
StandardJavaFileManager tfm = new TestJavaFileManagerBuilder(fm)
|
||||
.handle(jfo -> jfo.getName().endsWith(".java"),
|
||||
Map.of(JavaFileObject.class.getMethod("getCharContent", boolean.class),
|
||||
(fo, args) -> new TestException(fo.getName())))
|
||||
.build();
|
||||
|
||||
try {
|
||||
setFileManager(tfm);
|
||||
javadoc("-d", base.resolve("api").toString(),
|
||||
"-sourcepath", srcDir.toString(),
|
||||
"p");
|
||||
checkExit((Exit.ABNORMAL));
|
||||
checkOutput(Output.OUT, true,
|
||||
"""
|
||||
Loading source files for package p...
|
||||
error: fatal error encountered: ##EXC##: ##FILE##
|
||||
error: Please file a bug against the javadoc tool via the Java bug reporting page"""
|
||||
.replace("##EXC##", TestException.class.getName())
|
||||
.replace("##FILE##", srcDir.resolve("p").resolve("C.java").toString()));
|
||||
} finally {
|
||||
setFileManager(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFileObjectWrite(Path base) throws Exception {
|
||||
try (StandardJavaFileManager fm = getFileManager()) {
|
||||
Path outDir = base.resolve("api");
|
||||
|
||||
// build a file manager that throws an exception when any file is generated
|
||||
StandardJavaFileManager tfm = new TestJavaFileManagerBuilder(fm)
|
||||
.handle(jfo -> fm.asPath(jfo).startsWith(outDir.toAbsolutePath())
|
||||
&& jfo.getName().endsWith(".html"),
|
||||
Map.of(JavaFileObject.class.getMethod("openOutputStream"),
|
||||
(fo, args) -> new TestException(fo.getName())))
|
||||
.build();
|
||||
|
||||
try {
|
||||
setFileManager(tfm);
|
||||
javadoc("-d", outDir.toString(),
|
||||
"-sourcepath", srcDir.toString(),
|
||||
"p");
|
||||
checkExit((Exit.ERROR));
|
||||
checkOutput(Output.OUT, true,
|
||||
"""
|
||||
Generating ##FILE##...
|
||||
error: An internal exception has occurred.
|
||||
\t(##EXC##: ##FILE##)
|
||||
1 error"""
|
||||
.replace("##EXC##", TestException.class.getName())
|
||||
.replace("##FILE##", outDir.resolve("p").resolve("C.html").toString()));
|
||||
} finally {
|
||||
setFileManager(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user