/* * Copyright (c) 2021, 2022, 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 { var tester = new TestTFMBuilder(); tester.setup().runTests(); } 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); setAutomaticCheckNoStacktrace(false); 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); setAutomaticCheckNoStacktrace(true); } } } @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); setAutomaticCheckNoStacktrace(false); 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); setAutomaticCheckNoStacktrace(true); } } } @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); setAutomaticCheckNoStacktrace(false); 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); setAutomaticCheckNoStacktrace(true); } } } }