1 /*
2 * Copyright (c) 2016, 2020, 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 8178070 8196201 8184205
27 * @summary Test packages table in module summary pages
28 * @library /tools/lib ../../lib
29 * @modules jdk.compiler/com.sun.tools.javac.api
30 * jdk.compiler/com.sun.tools.javac.main
31 * jdk.javadoc/jdk.javadoc.internal.tool
32 * @build toolbox.ModuleBuilder toolbox.ToolBox javadoc.tester.*
33 * @run main TestModulePackages
34 */
35
36 import java.io.IOException;
37 import java.nio.file.Path;
38 import java.nio.file.Paths;
39 import java.util.Set;
40
41 import javadoc.tester.JavadocTester;
42 import toolbox.ModuleBuilder;
43 import toolbox.ToolBox;
44
45 public class TestModulePackages extends JavadocTester {
46 enum TabKind { EXPORTS, OPENS, CONCEALED };
47 enum ColKind { EXPORTED_TO, OPENED_TO };
48
49 public static void main(String... args) throws Exception {
50 TestModulePackages tester = new TestModulePackages();
51 tester.runTests(m -> new Object[] { Paths.get(m.getName()) });
52 }
53
54 private final ToolBox tb;
55
56 public TestModulePackages() {
57 tb = new ToolBox();
58 }
59
60 // @Test: See: https://bugs.openjdk.java.net/browse/JDK-8193107
61 public void empty(Path base) throws Exception {
62 Path src = base.resolve("src");
63 new ModuleBuilder(tb, "m")
64 .comment("empty module")
65 .write(src);
66
67 javadoc("-d", base.resolve("out").toString(),
68 "-quiet",
69 "-noindex",
70 "--module-source-path", src.toString(),
71 "--module", "m");
72
73 checkExit(Exit.OK);
74 checkOutput("m/module-summary.html", false,
75 """
76 <h3>Packages</h3>
77 <table class="packages-summary" summary="Packages table, listing packages, and an explanation">""");
78 }
79
80 @Test
81 public void exportSingle(Path base) throws Exception {
82 Path src = base.resolve("src");
83 new ModuleBuilder(tb, "m")
84 .comment("exports single package to all")
85 .exports("p")
86 .classes("package p; public class C { }")
87 .write(src);
88
89 javadoc("-d", base.resolve("out").toString(),
90 "-quiet",
91 "-noindex",
92 "--module-source-path", src.toString(),
93 "--module", "m");
94
95 checkExit(Exit.OK);
96 checkCaption("m", TabKind.EXPORTS);
97 checkTableHead("m");
98 checkPackageRow("m", "p", "i0", null, null, " ");
99 }
100
101 @Test
102 public void exportMultiple(Path base) throws Exception {
103 Path src = base.resolve("src");
104 new ModuleBuilder(tb, "m")
105 .comment("exports multiple packages to all")
106 .exports("p")
107 .exports("q")
108 .classes("package p; public class C { }")
109 .classes("package q; public class D { }")
110 .write(src);
111
112 javadoc("-d", base.resolve("out").toString(),
113 "-quiet",
114 "-noindex",
115 "--module-source-path", src.toString(),
116 "--module", "m");
117
118 checkExit(Exit.OK);
119 checkCaption("m", TabKind.EXPORTS);
120 checkTableHead("m");
121 checkPackageRow("m", "p", "i0", null, null, " ");
122 checkPackageRow("m", "q", "i1", null, null, " ");
123 }
124
125 @Test
126 public void exportSomeQualified(Path base) throws Exception {
127 Path src = base.resolve("src");
128 new ModuleBuilder(tb, "m")
129 .comment("exports multiple packages, some qualified")
130 .exports("p")
131 .exportsTo("q", "other")
132 .classes("package p; public class C { }")
133 .classes("package q; public class D { }")
134 .write(src);
135
136 new ModuleBuilder(tb, "other")
137 .comment("dummy module for target of export")
138 .write(src);
139
140 javadoc("-d", base.resolve("out-api").toString(),
141 "-quiet",
142 "-noindex",
143 "--module-source-path", src.toString(),
144 "--module", "m,other");
145
146 checkExit(Exit.OK);
147 checkCaption("m", TabKind.EXPORTS);
148 checkTableHead("m");
149 checkPackageRow("m", "p", "i0", null, null, " ");
150
151 javadoc("-d", base.resolve("out-all").toString(),
152 "-quiet",
153 "-noindex",
154 "--show-module-contents", "all",
155 "--module-source-path", src.toString(),
156 "--module", "m,other");
157
158 checkExit(Exit.OK);
159 checkCaption("m", TabKind.EXPORTS);
160 checkTableHead("m", ColKind.EXPORTED_TO);
161 checkPackageRow("m", "p", "i0", "All Modules", null, " ");
162 checkPackageRow("m", "q", "i1",
163 """
164 <a href="../other/module-summary.html">other</a>""", null, " ");
165 }
166
167 @Test
168 public void exportWithConcealed(Path base) throws Exception {
169 Path src = base.resolve("src");
170 new ModuleBuilder(tb, "m")
171 .comment("exports package, has concealed package")
172 .exports("p")
173 .classes("package p; public class C { }")
174 .classes("package q; public class D { }")
175 .write(src);
176
177 javadoc("-d", base.resolve("out-api").toString(),
178 "-quiet",
179 "-noindex",
180 "--module-source-path", src.toString(),
181 "--module", "m");
182
183 checkExit(Exit.OK);
184 checkCaption("m", TabKind.EXPORTS);
185 checkTableHead("m");
186 checkPackageRow("m", "p", "i0", null, null, " ");
187
188 javadoc("-d", base.resolve("out-all").toString(),
189 "-quiet",
190 "-noindex",
191 "--show-module-contents", "all",
192 "--show-packages", "all",
193 "--module-source-path", src.toString(),
194 "--module", "m");
195
196 checkExit(Exit.OK);
197 checkCaption("m", TabKind.EXPORTS, TabKind.CONCEALED);
198 checkTableHead("m", ColKind.EXPORTED_TO);
199 checkPackageRow("m", "p", "i0", "All Modules", null, " ");
200 checkPackageRow("m", "q", "i1", "None", null, " ");
201 }
202
203 @Test
204 public void exportOpenWithConcealed(Path base) throws Exception {
205 Path src = base.resolve("src");
206 new ModuleBuilder(tb, "m")
207 .comment("exports and opens qual and unqual, with concealed")
208 .exports("e.all")
209 .exportsTo("e.other", "other")
210 .opens("o.all")
211 .opensTo("o.other", "other")
212 .exports("eo")
213 .opens("eo")
214 .classes("package e.all; public class CEAll { }")
215 .classes("package e.other; public class CEOther { }")
216 .classes("package o.all; public class COAll { }")
217 .classes("package o.other; public class COOther { }")
218 .classes("package eo; public class CEO { }")
219 .classes("package c; public class C { }")
220 .write(src);
221
222 new ModuleBuilder(tb, "other")
223 .comment("dummy module for target of export and open")
224 .write(src);
225
226 javadoc("-d", base.resolve("out-api").toString(),
227 "-quiet",
228 "-noindex",
229 "--module-source-path", src.toString(),
230 "--module", "m,other");
231
232 checkExit(Exit.OK);
233 checkCaption("m", TabKind.EXPORTS, TabKind.OPENS);
234 checkTableHead("m", ColKind.EXPORTED_TO, ColKind.OPENED_TO);
235 checkPackageRow("m", "e.all", "i0", "All Modules", "None", " ");
236 checkPackageRow("m", "eo", "i1", "All Modules", "All Modules", " ");
237
238 javadoc("-d", base.resolve("out-all").toString(),
239 "-quiet",
240 "-noindex",
241 "--show-module-contents", "all",
242 "--show-packages", "all",
243 "--module-source-path", src.toString(),
244 "--module", "m,other");
245
246 checkExit(Exit.OK);
247 checkCaption("m", TabKind.EXPORTS, TabKind.OPENS, TabKind.CONCEALED);
248 checkTableHead("m", ColKind.EXPORTED_TO, ColKind.OPENED_TO);
249 checkPackageRow("m", "c", "i0", "None", "None", " ");
250 checkPackageRow("m", "e.all", "i1", "All Modules", "None", " ");
251 checkPackageRow("m", "e.other", "i2",
252 """
253 <a href="../other/module-summary.html">other</a>""", "None", " ");
254 checkPackageRow("m", "eo", "i3", "All Modules", "All Modules", " ");
255 checkPackageRow("m", "o.all", "i4", "None", "All Modules", " ");
256 checkPackageRow("m", "o.other", "i5", "None",
257 """
258 <a href="../other/module-summary.html">other</a>""", " ");
259 }
260
261 @Test
262 public void openModule(Path base) throws Exception {
263 Path src = base.resolve("src");
264 new ModuleBuilder(tb, true, "m")
265 .comment("open module")
266 .classes("/** implicitly open package */ package p;")
267 .classes("package p; public class C { } ")
268 .classes("/** implicitly open package */ package q;")
269 .classes("package q; public class D { }")
270 .write(src);
271
272 javadoc("-d", base.resolve("out").toString(),
273 "-quiet",
274 "-noindex",
275 "--show-packages", "all", // required, to show open packages; see JDK-8193107
276 "--module-source-path", src.toString(),
277 "--module", "m");
278
279 checkExit(Exit.OK);
280 checkCaption("m", TabKind.OPENS);
281 checkTableHead("m");
282 checkPackageRow("m", "p", "i0", null, null,
283 """
284
285 <div class="block">implicitly open package</div>
286 """);
287 checkPackageRow("m", "q", "i1", null, null,
288 """
289
290 <div class="block">implicitly open package</div>
291 """);
292 }
293 @Test
294 public void openSingle(Path base) throws Exception {
295 Path src = base.resolve("src");
296 new ModuleBuilder(tb, "m")
297 .comment("opens single package to all")
298 .opens("p")
299 .classes("package p; public class C { }")
300 .write(src);
301
302 javadoc("-d", base.resolve("out").toString(),
303 "-quiet",
304 "-noindex",
305 "--show-packages", "all", // required, to show open packages; see JDK-8193107
306 "--module-source-path", src.toString(),
307 "--module", "m");
308
309 checkExit(Exit.OK);
310 checkCaption("m", TabKind.OPENS);
311 checkTableHead("m");
312 checkPackageRow("m", "p", "i0", null, null, " ");
313 }
314
315 @Test
316 public void openMultiple(Path base) throws Exception {
317 Path src = base.resolve("src");
318 new ModuleBuilder(tb, "m")
319 .comment("opens multiple packages to all")
320 .opens("p")
321 .opens("q")
322 .classes("package p; public class C { }")
323 .classes("package q; public class D { }")
324 .write(src);
325
326 javadoc("-d", base.resolve("out").toString(),
327 "-quiet",
328 "-noindex",
329 "--show-packages", "all", // required, to show open packages; see JDK-8193107
330 "--module-source-path", src.toString(),
331 "--module", "m");
332
333 checkExit(Exit.OK);
334 checkCaption("m", TabKind.OPENS);
335 checkTableHead("m");
336 checkPackageRow("m", "p", "i0", null, null, " ");
337 checkPackageRow("m", "q", "i1", null, null, " ");
338 }
339
340 @Test
341 public void openSomeQualified(Path base) throws Exception {
342 Path src = base.resolve("src");
343 new ModuleBuilder(tb, "m")
344 .comment("opens multiple packages, some qualified")
345 .opens("p")
346 .opensTo("q", "other")
347 .classes("package p; public class C { }")
348 .classes("package q; public class D { }")
349 .write(src);
350
351 new ModuleBuilder(tb, "other")
352 .comment("dummy module for target of export")
353 .write(src);
354
355 javadoc("-d", base.resolve("out-api").toString(),
356 "-quiet",
357 "-noindex",
358 "--show-packages", "all", // required, to show open packages; see JDK-8193107
359 "--module-source-path", src.toString(),
360 "--module", "m,other");
361
362 checkExit(Exit.OK);
363 checkCaption("m", TabKind.OPENS);
364 checkTableHead("m");
365 checkPackageRow("m", "p", "i0", null, null, " ");
366
367 javadoc("-d", base.resolve("out-all").toString(),
368 "-quiet",
369 "-noindex",
370 "--show-packages", "all", // required, to show open packages; see JDK-8193107
371 "--show-module-contents", "all",
372 "--module-source-path", src.toString(),
373 "--module", "m,other");
374
375 checkExit(Exit.OK);
376 checkCaption("m", TabKind.OPENS);
377 checkTableHead("m", ColKind.OPENED_TO);
378 checkPackageRow("m", "p", "i0", null, "All Modules", " ");
379 checkPackageRow("m", "q", "i1", null,
380 """
381 <a href="../other/module-summary.html">other</a>""", " ");
382 }
383
384 @Test
385 public void openWithConcealed(Path base) throws Exception {
386 Path src = base.resolve("src");
387 new ModuleBuilder(tb, "m")
388 .comment("opens package, has concealed package")
389 .opens("p")
390 .classes("package p; public class C { }")
391 .classes("package q; public class D { }")
392 .write(src);
393
394 javadoc("-d", base.resolve("out-api").toString(),
395 "-quiet",
396 "-noindex",
397 "--show-packages", "all", // required, to show open packages; see JDK-8193107
398 "--module-source-path", src.toString(),
399 "--module", "m");
400
401 checkExit(Exit.OK);
402 checkCaption("m", TabKind.OPENS);
403 checkTableHead("m");
404 checkPackageRow("m", "p", "i0", null, null, " ");
405
406 javadoc("-d", base.resolve("out-all").toString(),
407 "-quiet",
408 "-noindex",
409 "--show-module-contents", "all",
410 "--show-packages", "all",
411 "--module-source-path", src.toString(),
412 "--module", "m");
413
414 checkExit(Exit.OK);
415 checkCaption("m", TabKind.OPENS, TabKind.CONCEALED);
416 checkTableHead("m", ColKind.OPENED_TO);
417 checkPackageRow("m", "p", "i0", null, "All Modules", " ");
418 checkPackageRow("m", "q", "i1", null, "None", " ");
419 }
420
421
422 private void checkCaption(String moduleName, TabKind... kinds) {
423 String expect;
424 if (kinds.length > 1) {
425 Set<TabKind> kindSet = Set.of(kinds);
426 StringBuilder sb = new StringBuilder();
427 sb.append("""
428 <div class="table-tabs" role="tablist" aria-orientation="horizontal"><button rol\
429 e="tab" aria-selected="true" aria-controls="package-summary-table.tabpanel" tabi\
430 ndex="0" onkeydown="switchTab(event)" id="t0" class="active-table-tab">All Packa\
431 ges</button>""");
432 if (kindSet.contains(TabKind.EXPORTS)) {
433 sb.append("""
434 <button role="tab" aria-selected="false" aria-controls="package-summary-table.ta\
435 bpanel" tabindex="-1" onkeydown="switchTab(event)" id="t1" class="table-tab" onc\
436 lick="show(1);">Exports</button>""");
437 }
438 if (kindSet.contains(TabKind.OPENS)) {
439 sb.append("""
440 <button role="tab" aria-selected="false" aria-controls="package-summary-table.ta\
441 bpanel" tabindex="-1" onkeydown="switchTab(event)" id="t2" class="table-tab" onc\
442 lick="show(2);">Opens</button>""");
443 }
444 if (kindSet.contains(TabKind.CONCEALED)) {
445 sb.append("""
446 <button role="tab" aria-selected="false" aria-controls="package-summary-table.ta\
447 bpanel" tabindex="-1" onkeydown="switchTab(event)" id="t3" class="table-tab" onc\
448 lick="show(4);">Concealed</button>""");
449 }
450 sb.append("</div>");
451 expect = sb.toString();
452 } else {
453 TabKind k = kinds[0];
454 String name = k.toString().charAt(0) + k.toString().substring(1).toLowerCase();
455 expect = "<caption><span>" + name + "</span></caption>";
456 }
457
458 checkOutput(moduleName + "/module-summary.html", true, expect);
459 }
460
461
462 private void checkTableHead(String moduleName, ColKind... kinds) {
463 Set<ColKind> kindSet = Set.of(kinds);
464 StringBuilder sb = new StringBuilder();
465 sb.append("""
466 <tr>
467 <th class="col-first" scope="col">Package</th>
468 """);
469 if (kindSet.contains(ColKind.EXPORTED_TO)) {
470 sb.append("""
471 <th class="col-second" scope="col">Exported To Modules</th>
472 """);
473 }
474 if (kindSet.contains(ColKind.OPENED_TO)) {
475 sb.append("""
476 <th class="col-second" scope="col">Opened To Modules</th>
477 """);
478 }
479 sb.append("""
480 <th class="col-last" scope="col">Description</th>
481 </tr>""");
482
483 checkOutput(moduleName + "/module-summary.html", true, sb.toString());
484 }
485
486 private void checkPackageRow(String moduleName, String packageName,
487 String id, String exportedTo, String openedTo, String desc) {
488 StringBuilder sb = new StringBuilder();
489 int idNum = Integer.parseInt(id.substring(1));
490 String color = (idNum % 2 == 1 ? "row-color" : "alt-color");
491 sb.append("<tr class=\"" + color + "\" id=\"" + id + """
492 ">
493 <th class="col-first" scope="row"><a href=\"""" + packageName.replace('.', '/') + "/package-summary.html\">"
494 + packageName + "</a></th>\n");
495 if (exportedTo != null) {
496 sb.append("<td class=\"col-second\">" + exportedTo + "</td>\n");
497 }
498 if (openedTo != null) {
499 sb.append("<td class=\"col-second\">" + openedTo + "</td>\n");
500 }
501 sb.append("<td class=\"col-last\">" + desc + "</td>");
502
503 checkOutput(moduleName + "/module-summary.html", true, sb.toString());
504 }
505
506 }
507