1/*
2 * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/*
25 * @test
26 * @bug 8035473 8154482 8154399 8159096 8176131 8176331
27 * @summary make sure the javadoc tool responds correctly to Xold,
28 *          old doclets and taglets.
29 * @library /tools/lib
30 * @build toolbox.ToolBox toolbox.TestRunner
31 * @run main EnsureNewOldDoclet
32 */
33
34import java.io.*;
35import java.nio.file.Path;
36import java.util.Arrays;
37import java.util.Collections;
38import java.util.List;
39import java.util.Map;
40import java.util.Set;
41import java.util.regex.Pattern;
42import java.util.stream.Collectors;
43import javax.lang.model.element.Element;
44
45import com.sun.javadoc.Tag;
46import com.sun.source.doctree.DocTree;
47
48import toolbox.*;
49
50
51/**
52 * This test ensures the doclet responds correctly when given
53 * various conditions that force a fall back to the old javadoc
54 * tool. The following condition in the order described will
55 * force a dispatch to the old tool, -Xold, old doclet and old taglet.
56 *
57 */
58public class EnsureNewOldDoclet extends TestRunner {
59
60    final ToolBox tb;
61    final File testSrc;
62    final Path javadocPath;
63    final ExecTask task;
64    final String testClasses;
65    final PrintStream ostream;
66
67    final static String CLASS_NAME = "EnsureNewOldDoclet";
68    final static String OLD_DOCLET_CLASS_NAME = CLASS_NAME + "$OldDoclet";
69    final static String NEW_DOCLET_CLASS_NAME = CLASS_NAME + "$NewDoclet"; //unused
70    final static String OLD_TAGLET_CLASS_NAME = CLASS_NAME + "$OldTaglet";
71    final static String NEW_TAGLET_CLASS_NAME = CLASS_NAME + "$NewTaglet";
72
73    final static Pattern OLD_HEADER = Pattern.compile("^Standard Doclet \\(Old\\) version.*");
74    final static Pattern NEW_HEADER = Pattern.compile("^Standard Doclet version.*");
75
76
77    final static String OLD_DOCLET_MARKER = "OLD_DOCLET_MARKER";
78    final static String OLD_TAGLET_MARKER = "Registered: OldTaglet";
79
80    final static String NEW_DOCLET_MARKER = "NEW_DOCLET_MARKER";
81    final static String NEW_TAGLET_MARKER = "Registered Taglet " + CLASS_NAME + "\\$NewTaglet";
82
83    final static Pattern WARN_TEXT = Pattern.compile("Users are strongly recommended to migrate" +
84                                                    " to the new APIs.");
85    final static String OLD_DOCLET_ERROR = "java.lang.NoSuchMethodException: " +
86            CLASS_NAME +"\\$NewTaglet";
87    final static Pattern NEW_DOCLET_ERROR = Pattern.compile(".*java.lang.ClassCastException.*Taglet " +
88            CLASS_NAME + "\\$OldTaglet.*");
89
90    final static String OLD_STDDOCLET = "com.sun.tools.doclets.standard.Standard";
91    final static String NEW_STDDOCLET = "jdk.javadoc.doclet.StandardDoclet";
92
93
94    public EnsureNewOldDoclet() throws Exception {
95        super(System.err);
96        ostream = System.err;
97        testClasses = System.getProperty("test.classes");
98        tb = new ToolBox();
99        javadocPath = tb.getJDKTool("javadoc");
100        task = new ExecTask(tb, javadocPath);
101        testSrc = new File("Foo.java");
102        generateSample(testSrc);
103    }
104
105    void generateSample(File testSrc) throws Exception {
106        String nl = System.getProperty("line.separator");
107        String src = Arrays.asList(
108            "/**",
109            " * A test class to test javadoc. Nothing more nothing less.",
110            " */",
111            " public class Foo{}").stream().collect(Collectors.joining(nl));
112        tb.writeFile(testSrc.getPath(), src);
113    }
114
115    public static void main(String... args) throws Exception {
116        new EnsureNewOldDoclet().runTests();
117    }
118
119    // input: nothing, default mode
120    // outcome: new tool and new doclet
121    @Test
122    public void testDefault() throws Exception {
123        setArgs("-classpath", ".", // insulates us from ambient classpath
124                  testSrc.toString());
125        Task.Result tr = task.run(Task.Expect.SUCCESS);
126        List<String> out = tr.getOutputLines(Task.OutputKind.STDOUT);
127        checkOutput(testName, out, NEW_HEADER);
128    }
129
130    // input: -Xold
131    // outcome: old tool
132    @Test
133    public void testXold() throws Exception {
134        setArgs("-Xold",
135                "-classpath", ".", // ambient classpath insulation
136                testSrc.toString());
137        Task.Result tr = task.run(Task.Expect.SUCCESS);
138        List<String> out = tr.getOutputLines(Task.OutputKind.STDOUT);
139        List<String> err = tr.getOutputLines(Task.OutputKind.STDERR);
140        checkOutput(testName, out, OLD_HEADER);
141        checkOutput(testName, err, WARN_TEXT);
142    }
143
144    // input: old doclet
145    // outcome: old tool
146    @Test
147    public void testOldDoclet() throws Exception {
148        setArgs("-classpath", ".", // ambient classpath insulation
149                "-doclet",
150                OLD_DOCLET_CLASS_NAME,
151                "-docletpath",
152                testClasses,
153                testSrc.toString());
154        Task.Result tr = task.run(Task.Expect.SUCCESS);
155        List<String> out = tr.getOutputLines(Task.OutputKind.STDOUT);
156        List<String> err = tr.getOutputLines(Task.OutputKind.STDERR);
157        checkOutput(testName, out, OLD_DOCLET_MARKER);
158        checkOutput(testName, err, WARN_TEXT);
159    }
160
161    // input: old taglet
162    // outcome: old tool
163    @Test
164    public void testOldTaglet() throws Exception {
165        setArgs("-classpath", ".", // ambient classpath insulation
166            "-taglet",
167            OLD_TAGLET_CLASS_NAME,
168            "-tagletpath",
169            testClasses,
170            testSrc.toString());
171        Task.Result tr = task.run(Task.Expect.SUCCESS);
172        List<String> out = tr.getOutputLines(Task.OutputKind.STDOUT);
173        List<String> err = tr.getOutputLines(Task.OutputKind.STDERR);
174        checkOutput(testName, out, OLD_TAGLET_MARKER);
175        checkOutput(testName, err, WARN_TEXT);
176    }
177
178    // input: new doclet and old taglet
179    // outcome: new doclet with failure
180    @Test
181    public void testNewDocletOldTaglet() throws Exception {
182        setArgs("-classpath", ".", // ambient classpath insulation
183                "-doclet",
184                NEW_STDDOCLET,
185                "-taglet",
186                OLD_TAGLET_CLASS_NAME,
187                "-tagletpath",
188                testClasses,
189                testSrc.toString());
190        Task.Result tr = task.run(Task.Expect.FAIL, 1).writeAll();
191        //Task.Result tr = task.run();
192        List<String> out = tr.getOutputLines(Task.OutputKind.STDOUT);
193        List<String> err = tr.getOutputLines(Task.OutputKind.STDERR);
194        checkOutput(testName, out, NEW_HEADER);
195        checkOutput(testName, err, NEW_DOCLET_ERROR);
196    }
197
198    // input: old doclet and old taglet
199    // outcome: old doclet and old taglet should register
200    @Test
201    public void testOldDocletOldTaglet() throws Exception {
202        setArgs("-classpath", ".", // ambient classpath insulation
203                "-doclet",
204                OLD_STDDOCLET,
205                "-taglet",
206                OLD_TAGLET_CLASS_NAME,
207                "-tagletpath",
208                testClasses,
209                testSrc.toString());
210        Task.Result tr = task.run(Task.Expect.SUCCESS);
211        List<String> out = tr.getOutputLines(Task.OutputKind.STDOUT);
212        List<String> err = tr.getOutputLines(Task.OutputKind.STDERR);
213        checkOutput(testName, out, OLD_HEADER);
214        checkOutput(testName, out, OLD_TAGLET_MARKER);
215        checkOutput(testName, err, WARN_TEXT);
216    }
217
218    // input: new doclet and new taglet
219    // outcome: new doclet and new taglet should register
220    @Test
221    public void testNewDocletNewTaglet() throws Exception {
222        setArgs("-classpath", ".", // ambient classpath insulation
223                "-doclet",
224                NEW_STDDOCLET,
225                "-taglet",
226                NEW_TAGLET_CLASS_NAME,
227                "-tagletpath",
228                testClasses,
229                testSrc.toString());
230        Task.Result tr = task.run(Task.Expect.SUCCESS);
231        List<String> out = tr.getOutputLines(Task.OutputKind.STDOUT);
232        List<String> err = tr.getOutputLines(Task.OutputKind.STDERR);
233        checkOutput(testName, out, NEW_HEADER);
234        checkOutput(testName, out, NEW_TAGLET_MARKER);
235    }
236
237    // input: old doclet and new taglet
238    // outcome: old doclet and error
239    @Test
240    public void testOldDocletNewTaglet() throws Exception {
241        setArgs("-classpath", ".", // ambient classpath insulation
242                "-doclet",
243                OLD_STDDOCLET,
244                "-taglet",
245                NEW_TAGLET_CLASS_NAME,
246                "-tagletpath",
247                testClasses,
248                testSrc.toString());
249        Task.Result tr = task.run(Task.Expect.FAIL, 1);
250        List<String> out = tr.getOutputLines(Task.OutputKind.STDOUT);
251        List<String> err = tr.getOutputLines(Task.OutputKind.STDERR);
252        checkOutput(testName, out, OLD_HEADER);
253        checkOutput(testName, err, WARN_TEXT);
254        checkOutput(testName, err, OLD_DOCLET_ERROR);
255    }
256
257    void setArgs(String... args) {
258        ostream.println("cmds: " + Arrays.asList(args));
259        task.args(args);
260    }
261
262    void checkOutput(String testCase, List<String> content, String toFind) throws Exception {
263        checkOutput(testCase, content, Pattern.compile(".*" + toFind + ".*"));
264    }
265
266    void checkOutput(String testCase, List<String> content, Pattern toFind) throws Exception {
267        ostream.println("---" + testCase + "---");
268        content.stream().forEach(x -> System.out.println(x));
269        for (String x : content) {
270            ostream.println(x);
271            if (toFind.matcher(x).matches()) {
272                return;
273            }
274        }
275        throw new Exception(testCase + ": Expected string not found: " +  toFind);
276    }
277
278    public static class OldDoclet extends com.sun.javadoc.Doclet {
279        public static boolean start(com.sun.javadoc.RootDoc root) {
280            System.out.println(OLD_DOCLET_MARKER);
281            return true;
282        }
283    }
284
285    public static class OldTaglet implements com.sun.tools.doclets.Taglet {
286
287        public static void register(Map map) {
288            EnsureNewOldDoclet.OldTaglet tag = new OldTaglet();
289            com.sun.tools.doclets.Taglet t = (com.sun.tools.doclets.Taglet) map.get(tag.getName());
290            System.out.println(OLD_TAGLET_MARKER);
291        }
292
293        @Override
294        public boolean inField() {
295            return true;
296        }
297
298        @Override
299        public boolean inConstructor() {
300            return true;
301        }
302
303        @Override
304        public boolean inMethod() {
305            return true;
306        }
307
308        @Override
309        public boolean inOverview() {
310            return true;
311        }
312
313        @Override
314        public boolean inPackage() {
315            return true;
316        }
317
318        @Override
319        public boolean inType() {
320            return true;
321        }
322
323        @Override
324        public boolean isInlineTag() {
325            return true;
326        }
327
328        @Override
329        public String getName() {
330            return "OldTaglet";
331        }
332
333        @Override
334        public String toString(Tag tag) {
335            return getName();
336        }
337
338        @Override
339        public String toString(Tag[] tags) {
340            return getName();
341        }
342    }
343
344    public static class NewTaglet implements jdk.javadoc.doclet.Taglet {
345
346        @Override
347        public Set<Location> getAllowedLocations() {
348            return Collections.emptySet();
349        }
350
351        @Override
352        public boolean isInlineTag() {
353            return true;
354        }
355
356        @Override
357        public String getName() {
358            return "NewTaglet";
359        }
360
361        @Override
362        public String toString(List<? extends DocTree> tags, Element element) {
363            return tags.toString();
364        }
365
366    }
367}
368