TestFramesNoFrames.java revision 3618:64182008b2d0
1/*
2 * Copyright (c) 2016, 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 8162353 8164747
27 * @summary javadoc should provide a way to disable use of frames
28 * @library /tools/lib ../lib
29 * @modules
30 *      jdk.compiler/com.sun.tools.javac.api
31 *      jdk.compiler/com.sun.tools.javac.main
32 *      jdk.javadoc/jdk.javadoc.internal.tool
33 * @build toolbox.ModuleBuilder toolbox.ToolBox
34 * @build JavadocTester
35 * @run main TestFramesNoFrames
36 */
37
38import java.io.*;
39import java.lang.annotation.Annotation;
40import java.lang.reflect.InvocationTargetException;
41import java.lang.reflect.Method;
42import java.nio.file.*;
43import java.util.*;
44import java.util.stream.Collectors;
45
46import toolbox.ModuleBuilder;
47import toolbox.ToolBox;
48
49public class TestFramesNoFrames extends JavadocTester {
50
51    public static void main(String... args) throws Exception {
52        TestFramesNoFrames tester = new TestFramesNoFrames();
53        tester.generateSource();
54        tester.runTests();
55    }
56
57    ToolBox tb = new ToolBox();
58    Path gensrcModules = Paths.get("gensrc/modules");
59    Path gensrcPackages = Paths.get("gensrc/packages");
60
61    void generateSource() throws IOException {
62        String[] modules = { "", "m1", "m2", "m3" };
63        String[] packages = { "p1", "p2", "p3" };
64        String[] classes = { "C1", "C2", "C3" };
65        for (String m: modules) {
66            ModuleBuilder mb = m.equals("") ? null : new ModuleBuilder(tb, m);
67            for (String p: packages) {
68                Path pkgRoot;
69                if (m.equals("")) {
70                    pkgRoot = gensrcPackages;
71                } else {
72                    pkgRoot = gensrcModules.resolve(m);
73                    mb.exports(m + p);
74                }
75                for (String c: classes) {
76                    tb.writeJavaFiles(pkgRoot,
77                        "package " + (m + p) + ";\n"
78                        + "/** class " + (m + p + c).toUpperCase() + ". */\n"
79                        + "public class " + (m + p + c).toUpperCase() + " { }"
80                    );
81                }
82            }
83            if (!m.equals("")) {
84                mb.write(gensrcModules);
85            }
86        }
87        tb.writeFile("overview.html",
88                "<html><body>This is the overview file</body></html>");
89    }
90
91    enum FrameKind {
92        DEFAULT(),
93        FRAMES("--frames"),
94        NO_FRAMES("--no-frames");
95        FrameKind(String... opts) {
96            this.opts = Arrays.asList(opts);
97        }
98        final List<String> opts;
99    }
100
101    enum OverviewKind {
102        DEFAULT(),
103        OVERVIEW("-overview", "overview.html"),
104        NO_OVERVIEW("-nooverview");
105        OverviewKind(String... opts) {
106            this.opts = Arrays.asList(opts);
107        }
108        final List<String> opts;
109    }
110
111    enum HtmlKind {
112        HTML4("-html4"),
113        HTML5("-html5");
114        HtmlKind(String... opts) {
115            this.opts = Arrays.asList(opts);
116        }
117        final List<String> opts;
118    }
119
120    @Override
121    public void runTests() throws Exception {
122        for (Method m : getClass().getDeclaredMethods()) {
123            Annotation a = m.getAnnotation(Test.class);
124            if (a != null) {
125                for (FrameKind fk : FrameKind.values()) {
126                    for (OverviewKind ok : OverviewKind.values()) {
127                        for (HtmlKind hk : HtmlKind.values()) {
128                            try {
129                                out.println("Running test " + m.getName() + " " + fk + " " + ok + " " + hk);
130                                Path base = Paths.get(m.getName() + "_" + fk + "_" + ok + "_" + hk);
131                                Files.createDirectories(base);
132                                m.invoke(this, new Object[]{base, fk, ok, hk});
133                            } catch (InvocationTargetException e) {
134                                Throwable cause = e.getCause();
135                                throw (cause instanceof Exception) ? ((Exception) cause) : e;
136                            }
137                            out.println();
138                        }
139                    }
140                }
141            }
142        }
143        printSummary();
144    }
145
146    void javadoc(Path outDir, FrameKind fKind, OverviewKind oKind, HtmlKind hKind, String... rest) {
147        List<String> args = new ArrayList<>();
148        args.add("-d");
149        args.add(outDir.toString());
150        args.addAll(fKind.opts);
151        args.addAll(oKind.opts);
152        args.addAll(hKind.opts);
153        args.addAll(Arrays.asList(rest));
154        javadoc(args.toArray(new String[0]));
155        checkExit(Exit.OK);
156    }
157
158    @Test
159    void testClass(Path base, FrameKind fKind, OverviewKind oKind, HtmlKind hKind) throws Exception {
160        javadoc(base, fKind, oKind, hKind,
161            gensrcPackages.resolve("p1/P1C1.java").toString());
162
163        new Checker(fKind, oKind, hKind)
164            .classes("p1.P1C1")
165            .check();
166    }
167
168    @Test
169    void testClasses(Path base, FrameKind fKind, OverviewKind oKind, HtmlKind hKind) throws IOException {
170        javadoc(base, fKind, oKind, hKind,
171            gensrcPackages.resolve("p1/P1C1.java").toString(),
172            gensrcPackages.resolve("p1/P1C2.java").toString(),
173            gensrcPackages.resolve("p1/P1C3.java").toString());
174
175        new Checker(fKind, oKind, hKind)
176            .classes("p1.P1C1", "p1.P1C2", "p1.P1C3")
177            .check();
178    }
179
180    @Test
181    void testPackage(Path base, FrameKind fKind, OverviewKind oKind, HtmlKind hKind) throws IOException {
182        javadoc(base, fKind, oKind, hKind,
183            "-sourcepath", gensrcPackages.toString(),
184            "p1");
185
186        new Checker(fKind, oKind, hKind)
187            .classes("p1.P1C1", "p1.P1C2", "p1.P1C3")
188            .check();
189    }
190
191    @Test
192    void testPackages(Path base, FrameKind fKind, OverviewKind oKind, HtmlKind hKind) throws IOException {
193        javadoc(base, fKind, oKind, hKind,
194            "-sourcepath", gensrcPackages.toString(),
195            "p1", "p2", "p3");
196
197        new Checker(fKind, oKind, hKind)
198            .classes("p1.P1C1", "p1.P1C2", "p1.P1C3",
199                    "p2.P2C1", "p2.P2C2", "p2.P2C3",
200                    "p3.P3C1", "p3.P3C2", "p3.P3C3")
201            .check();
202    }
203
204    @Test
205    void testModules(Path base, FrameKind fKind, OverviewKind oKind, HtmlKind hKind) throws IOException {
206        javadoc(base, fKind, oKind, hKind,
207            "--module-source-path", gensrcModules.toString(),
208            "--module", "m1,m2,m3");
209
210        new Checker(fKind, oKind, hKind)
211            .classes("m1/m1p1.M1P1C1", "m1/m1p1.M1P1C2", "m1/m1p1.M1P1C3",
212                    "m2/m2p1.M2P1C1", "m2/m2p1.M2P1C2", "m2/m2p1.M2P1C3",
213                    "m3/m3p1.M3P1C1", "m3/m3p1.M3P1C2", "m3/m3p1.M3P1C3")
214            .check();
215    }
216
217    /**
218     * Check the contents of the generated output, according to the
219     * specified options.
220     */
221    class Checker {
222        private final FrameKind fKind;
223        private final OverviewKind oKind;
224        private final HtmlKind hKind;
225        List<String> classes;
226
227        private boolean frames;
228        private boolean overview;
229
230        Checker(FrameKind fKind, OverviewKind oKind, HtmlKind hKind) {
231            this.fKind = fKind;
232            this.oKind = oKind;
233            this.hKind = hKind;
234        }
235
236        Checker classes(String... classes) {
237            this.classes = Arrays.asList(classes);
238            return this;
239        }
240
241        void check() throws IOException {
242            switch (fKind) {
243                case DEFAULT:
244                case FRAMES:
245                    frames = true;
246                    break;
247
248                case NO_FRAMES:
249                    frames = false;
250                    break;
251            }
252
253            switch (oKind) {
254                case DEFAULT:
255                    overview = (getPackageCount() > 1);
256                    break;
257
258                case OVERVIEW:
259                    overview = true;
260                    break;
261
262                case NO_OVERVIEW:
263                    overview = false;
264                    break;
265            }
266
267            checkAllClassesFiles();
268            checkFrameFiles();
269            checkOverviewSummary();
270
271            checkIndex();
272            checkNavBar();
273            checkHelpDoc();
274
275        }
276
277        private void checkAllClassesFiles() {
278            // these files are only generated in frames mode
279            checkFiles(frames,
280                    "allclasses-frame.html",
281                    "allclasses-noframe.html");
282
283            // this file is only generated when not in frames mode
284            checkFiles(!frames,
285                    "allclasses.html");
286
287            if (frames) {
288                checkOutput("allclasses-frame.html", true,
289                        classes.stream()
290                            .map(c -> "title=\"class in " + packagePart(c) + "\" target=\"classFrame\">" + classPart(c) + "</a>")
291                            .toArray(String[]::new));
292                checkOutput("allclasses-noframe.html", false,
293                            "target=\"classFrame\">");
294            } else {
295                checkOutput("allclasses.html", false,
296                            "target=\"classFrame\">");
297
298            }
299        }
300
301        private void checkFrameFiles() {
302            // these files are all only generated in frames mode
303
304            // <module>-frame.html and <module>-type-frame.html files
305            checkFiles(frames, classes.stream()
306                .filter(c -> isInModule(c))
307                .map(c -> modulePart(c))
308                .flatMap(m -> Arrays.asList(
309                        m + "-frame.html",
310                        m + "-type-frame.html").stream())
311                .collect(Collectors.toSet()));
312
313            // <package>/package-frame.html files
314            checkFiles(frames, classes.stream()
315                    .map(c -> packagePart(c) + "/package-frame.html")
316                    .collect(Collectors.toSet()));
317        }
318
319        private void checkHelpDoc() {
320            // the Help page only describes Frame/NoFrames in frames mode
321            checkOutput("help-doc.html", frames,
322                        "<h2>Frames/No Frames</h2>");
323        }
324
325        private void checkIndex() {
326            // the index.html page only contains frames in frames mode
327            checkOutput("index.html", frames,
328                    "<iframe ",
329                    "</iframe>");
330
331            // the index.html contains the overview if one
332            // has been given, and not in frames mode
333            checkOutput("index.html", !frames && oKind == OverviewKind.OVERVIEW,
334                    "This is the overview file");
335
336            // the index.html file contains a summary table
337            // if an overview was generated and not in frames mode
338            checkOutput("index.html", !frames && overview,
339                    "<table class=\"overviewSummary\"");
340
341            // the index.html file contains a redirect if
342            // no frames and no overview
343            checkOutput("index.html", !frames && !overview,
344                    "<meta http-equiv=\"Refresh\" content=\"0;",
345                    "<script type=\"text/javascript\">window.location.replace(");
346
347            // the index.html file <meta> refresh should only use <noscript> in HTML 5
348            if (!frames && !overview) {
349                checkOutput("index.html", hKind == HtmlKind.HTML5,
350                        "<noscript>\n<meta http-equiv=\"Refresh\" content=\"0;");
351            }
352        }
353
354        private void checkNavBar() {
355            // the files containing a navigation bar should only
356            // contain FRAMES/NO-FRAMES links in frames mode
357            List<String> navbarFiles = new ArrayList<>();
358            navbarFiles.addAll(classes.stream()
359                    .map(c -> toHtml(packageClassPart(c)))
360                    .collect(Collectors.toSet()));
361            for (String f : navbarFiles) {
362                checkOutput(f, frames,
363                        "target=\"_top\">Frames</a>",
364                        "target=\"_top\">No&nbsp;Frames</a>");
365            }
366        }
367
368        private void checkOverviewSummary() {
369            // the overview-summary.html file only appears if
370            // in frames mode and (overview requested or multiple packages)
371            checkFiles(frames && overview,
372                    "overview-summary.html");
373        }
374
375        private long getPackageCount() {
376            return this.classes.stream()
377                .filter(name -> name.contains("."))
378                .map(name -> name.substring(0, name.lastIndexOf(".")))
379                .distinct()
380                .count();
381        }
382
383        private String classPart(String className) {
384            int lastDot = className.lastIndexOf(".");
385            return className.substring(lastDot + 1);
386        }
387
388        private String packagePart(String className) {
389            int slash = className.indexOf("/");
390            int lastDot = className.lastIndexOf(".");
391            return className.substring(slash + 1, lastDot);
392        }
393
394        private String packageClassPart(String className) {
395            int slash = className.indexOf("/");
396            return className.substring(slash + 1);
397        }
398
399        private boolean isInModule(String className) {
400            return className.contains("/");
401        }
402
403        private String modulePart(String className) {
404            int slash = className.indexOf("/");
405            return className.substring(0, slash);
406        }
407
408        private String toHtml(String className) {
409            return className.replace(".", "/") + ".html";
410        }
411    }
412}
413