TestFramesNoFrames.java revision 3595:81692f730015
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
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            "-modulesourcepath", 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
288        private void checkFrameFiles() {
289            // these files are all only generated in frames mode
290
291            // <module>-frame.html and <module>-type-frame.html files
292            checkFiles(frames, classes.stream()
293                .filter(c -> isInModule(c))
294                .map(c -> modulePart(c))
295                .flatMap(m -> Arrays.asList(
296                        m + "-frame.html",
297                        m + "-type-frame.html").stream())
298                .collect(Collectors.toSet()));
299
300            // <package>/package-frame.html files
301            checkFiles(frames, classes.stream()
302                    .map(c -> packagePart(c) + "/package-frame.html")
303                    .collect(Collectors.toSet()));
304        }
305
306        private void checkHelpDoc() {
307            // the Help page only describes Frame/NoFrames in frames mode
308            checkOutput("help-doc.html", frames,
309                        "<h2>Frames/No Frames</h2>");
310        }
311
312        private void checkIndex() {
313            // the index.html page only contains frames in frames mode
314            checkOutput("index.html", frames,
315                    "<iframe ",
316                    "</iframe>");
317
318            // the index.html contains the overview if one
319            // has been given, and not in frames mode
320            checkOutput("index.html", !frames && oKind == OverviewKind.OVERVIEW,
321                    "This is the overview file");
322
323            // the index.html file contains a summary table
324            // if an overview was generated and not in frames mode
325            checkOutput("index.html", !frames && overview,
326                    "<table class=\"overviewSummary\"");
327
328            // the index.html file contains a redirect if
329            // no frames and no overview
330            checkOutput("index.html", !frames && !overview,
331                    "<meta http-equiv=\"Refresh\" content=\"0;",
332                    "<script type=\"text/javascript\">window.location.replace(");
333
334            // the index.html file <meta> refresh should only use <noscript> in HTML 5
335            if (!frames && !overview) {
336                checkOutput("index.html", hKind == HtmlKind.HTML5,
337                        "<noscript>\n<meta http-equiv=\"Refresh\" content=\"0;");
338            }
339        }
340
341        private void checkNavBar() {
342            // the files containing a navigation bar should only
343            // contain FRAMES/NO-FRAMES links in frames mode
344            List<String> navbarFiles = new ArrayList<>();
345            navbarFiles.addAll(classes.stream()
346                    .map(c -> toHtml(packageClassPart(c)))
347                    .collect(Collectors.toSet()));
348            for (String f : navbarFiles) {
349                checkOutput(f, frames,
350                        "target=\"_top\">Frames</a>",
351                        "target=\"_top\">No&nbsp;Frames</a>");
352            }
353        }
354
355        private void checkOverviewSummary() {
356            // the overview-summary.html file only appears if
357            // in frames mode and (overview requested or multiple packages)
358            checkFiles(frames && overview,
359                    "overview-summary.html");
360        }
361
362        private long getPackageCount() {
363            return this.classes.stream()
364                .filter(name -> name.contains("."))
365                .map(name -> name.substring(0, name.lastIndexOf(".")))
366                .distinct()
367                .count();
368        }
369
370        private String packagePart(String className) {
371            int slash = className.indexOf("/");
372            int lastDot = className.lastIndexOf(".");
373            return className.substring(slash + 1, lastDot);
374        }
375
376        private String packageClassPart(String className) {
377            int slash = className.indexOf("/");
378            return className.substring(slash + 1);
379        }
380
381        private boolean isInModule(String className) {
382            return className.contains("/");
383        }
384
385        private String modulePart(String className) {
386            int slash = className.indexOf("/");
387            return className.substring(0, slash);
388        }
389
390        private String toHtml(String className) {
391            return className.replace(".", "/") + ".html";
392        }
393    }
394}
395