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 exportSameName(Path base) throws Exception {
127 Path src = base.resolve("src");
128 new ModuleBuilder(tb, "m")
129 .comment("exports same qualified package and types as module o")
130 .exports("p")
131 .classes("package p; public class C { }")
132 .write(src);
133 new ModuleBuilder(tb, "o")
134 .comment("exports same qualified package and types as module m")
135 .exports("p")
136 .classes("package p; public class C { }")
137 .write(src);
138
139 javadoc("-d", base.resolve("out").toString(),
140 "-quiet",
141 "--module-source-path", src.toString(),
142 "--module", "m,o");
143
144 checkExit(Exit.ERROR);
145 checkOutput(Output.OUT, true, "error: the unnamed module reads package p from both o and m");
146 checkCaption("m", TabKind.EXPORTS);
147 checkCaption("o", TabKind.EXPORTS);
148 checkTableHead("m");
149 checkTableHead("o");
150 checkPackageRow("m", "p", "i0", null, null, " ");
151 checkPackageRow("o", "p", "i0", null, null, " ");
152 checkOutput("m/p/package-summary.html", true,
153 """
154 <div class="sub-title"><span class="module-label-in-package">Module</span> <a href="../module-summary.html">m</a></div>
155 """);
156 checkOutput("o/p/package-summary.html", true,
157 """
158 <div class="sub-title"><span class="module-label-in-package">Module</span> <a href="../module-summary.html">o</a></div>
159 """);
160 checkOutput("m/p/C.html", true,
161 """
162 <div class="sub-title"><span class="module-label-in-type">Module</span> <a href="../module-summary.html">m</a></div>
163 <div class="sub-title"><span class="package-label-in-type">Package</span> <a href="package-summary.html">p</a></div>
164 """);
165 checkOutput("o/p/C.html", true,
166 """
167 <div class="sub-title"><span class="module-label-in-type">Module</span> <a href="../module-summary.html">o</a></div>
168 <div class="sub-title"><span class="package-label-in-type">Package</span> <a href="package-summary.html">p</a></div>
169 """);
170 }
171
172 @Test
173 public void exportSomeQualified(Path base) throws Exception {
174 Path src = base.resolve("src");
175 new ModuleBuilder(tb, "m")
176 .comment("exports multiple packages, some qualified")
177 .exports("p")
178 .exportsTo("q", "other")
179 .classes("package p; public class C { }")
180 .classes("package q; public class D { }")
181 .write(src);
182
183 new ModuleBuilder(tb, "other")
184 .comment("dummy module for target of export")
185 .write(src);
186
187 javadoc("-d", base.resolve("out-api").toString(),
188 "-quiet",
189 "-noindex",
190 "--module-source-path", src.toString(),
191 "--module", "m,other");
192
193 checkExit(Exit.OK);
194 checkCaption("m", TabKind.EXPORTS);
195 checkTableHead("m");
196 checkPackageRow("m", "p", "i0", null, null, " ");
197
198 javadoc("-d", base.resolve("out-all").toString(),
199 "-quiet",
200 "-noindex",
201 "--show-module-contents", "all",
202 "--module-source-path", src.toString(),
203 "--module", "m,other");
204
205 checkExit(Exit.OK);
206 checkCaption("m", TabKind.EXPORTS);
207 checkTableHead("m", ColKind.EXPORTED_TO);
208 checkPackageRow("m", "p", "i0", "All Modules", null, " ");
209 checkPackageRow("m", "q", "i1",
210 """
211 <a href="../other/module-summary.html">other</a>""", null, " ");
212 }
213
214 @Test
215 public void exportWithConcealed(Path base) throws Exception {
216 Path src = base.resolve("src");
217 new ModuleBuilder(tb, "m")
218 .comment("exports package, has concealed package")
219 .exports("p")
220 .classes("package p; public class C { }")
221 .classes("package q; public class D { }")
222 .write(src);
223
224 javadoc("-d", base.resolve("out-api").toString(),
225 "-quiet",
226 "-noindex",
227 "--module-source-path", src.toString(),
228 "--module", "m");
229
230 checkExit(Exit.OK);
231 checkCaption("m", TabKind.EXPORTS);
232 checkTableHead("m");
233 checkPackageRow("m", "p", "i0", null, null, " ");
234
235 javadoc("-d", base.resolve("out-all").toString(),
236 "-quiet",
237 "-noindex",
238 "--show-module-contents", "all",
239 "--show-packages", "all",
240 "--module-source-path", src.toString(),
241 "--module", "m");
242
243 checkExit(Exit.OK);
244 checkCaption("m", TabKind.EXPORTS, TabKind.CONCEALED);
245 checkTableHead("m", ColKind.EXPORTED_TO);
246 checkPackageRow("m", "p", "i0", "All Modules", null, " ");
247 checkPackageRow("m", "q", "i1", "None", null, " ");
248 }
249
250 @Test
251 public void exportOpenWithConcealed(Path base) throws Exception {
252 Path src = base.resolve("src");
253 new ModuleBuilder(tb, "m")
254 .comment("exports and opens qual and unqual, with concealed")
255 .exports("e.all")
256 .exportsTo("e.other", "other")
257 .opens("o.all")
258 .opensTo("o.other", "other")
259 .exports("eo")
260 .opens("eo")
261 .classes("package e.all; public class CEAll { }")
262 .classes("package e.other; public class CEOther { }")
263 .classes("package o.all; public class COAll { }")
264 .classes("package o.other; public class COOther { }")
265 .classes("package eo; public class CEO { }")
266 .classes("package c; public class C { }")
267 .write(src);
268
269 new ModuleBuilder(tb, "other")
270 .comment("dummy module for target of export and open")
271 .write(src);
272
273 javadoc("-d", base.resolve("out-api").toString(),
274 "-quiet",
275 "-noindex",
276 "--module-source-path", src.toString(),
277 "--module", "m,other");
278
279 checkExit(Exit.OK);
280 checkCaption("m", TabKind.EXPORTS, TabKind.OPENS);
281 checkTableHead("m", ColKind.EXPORTED_TO, ColKind.OPENED_TO);
282 checkPackageRow("m", "e.all", "i0", "All Modules", "None", " ");
283 checkPackageRow("m", "eo", "i1", "All Modules", "All Modules", " ");
284
285 javadoc("-d", base.resolve("out-all").toString(),
286 "-quiet",
287 "-noindex",
288 "--show-module-contents", "all",
289 "--show-packages", "all",
290 "--module-source-path", src.toString(),
291 "--module", "m,other");
292
293 checkExit(Exit.OK);
294 checkCaption("m", TabKind.EXPORTS, TabKind.OPENS, TabKind.CONCEALED);
295 checkTableHead("m", ColKind.EXPORTED_TO, ColKind.OPENED_TO);
296 checkPackageRow("m", "c", "i0", "None", "None", " ");
297 checkPackageRow("m", "e.all", "i1", "All Modules", "None", " ");
298 checkPackageRow("m", "e.other", "i2",
299 """
300 <a href="../other/module-summary.html">other</a>""", "None", " ");
301 checkPackageRow("m", "eo", "i3", "All Modules", "All Modules", " ");
302 checkPackageRow("m", "o.all", "i4", "None", "All Modules", " ");
303 checkPackageRow("m", "o.other", "i5", "None",
304 """
305 <a href="../other/module-summary.html">other</a>""", " ");
306 }
307
308 @Test
309 public void openModule(Path base) throws Exception {
310 Path src = base.resolve("src");
311 new ModuleBuilder(tb, true, "m")
312 .comment("open module")
313 .classes("/** implicitly open package */ package p;")
314 .classes("package p; public class C { } ")
315 .classes("/** implicitly open package */ package q;")
316 .classes("package q; public class D { }")
317 .write(src);
318
319 javadoc("-d", base.resolve("out").toString(),
320 "-quiet",
321 "-noindex",
322 "--show-packages", "all", // required, to show open packages; see JDK-8193107
323 "--module-source-path", src.toString(),
324 "--module", "m");
325
326 checkExit(Exit.OK);
327 checkCaption("m", TabKind.OPENS);
328 checkTableHead("m");
329 checkPackageRow("m", "p", "i0", null, null,
330 """
331
332 <div class="block">implicitly open package</div>
333 """);
334 checkPackageRow("m", "q", "i1", null, null,
335 """
336
337 <div class="block">implicitly open package</div>
338 """);
339 }
340 @Test
341 public void openSingle(Path base) throws Exception {
342 Path src = base.resolve("src");
343 new ModuleBuilder(tb, "m")
344 .comment("opens single package to all")
345 .opens("p")
346 .classes("package p; public class C { }")
347 .write(src);
348
349 javadoc("-d", base.resolve("out").toString(),
350 "-quiet",
351 "-noindex",
352 "--show-packages", "all", // required, to show open packages; see JDK-8193107
353 "--module-source-path", src.toString(),
354 "--module", "m");
355
356 checkExit(Exit.OK);
357 checkCaption("m", TabKind.OPENS);
358 checkTableHead("m");
359 checkPackageRow("m", "p", "i0", null, null, " ");
360 }
361
362 @Test
363 public void openMultiple(Path base) throws Exception {
364 Path src = base.resolve("src");
365 new ModuleBuilder(tb, "m")
366 .comment("opens multiple packages to all")
367 .opens("p")
368 .opens("q")
369 .classes("package p; public class C { }")
370 .classes("package q; public class D { }")
371 .write(src);
372
373 javadoc("-d", base.resolve("out").toString(),
374 "-quiet",
375 "-noindex",
376 "--show-packages", "all", // required, to show open packages; see JDK-8193107
377 "--module-source-path", src.toString(),
378 "--module", "m");
379
380 checkExit(Exit.OK);
381 checkCaption("m", TabKind.OPENS);
382 checkTableHead("m");
383 checkPackageRow("m", "p", "i0", null, null, " ");
384 checkPackageRow("m", "q", "i1", null, null, " ");
385 }
386
387 @Test
388 public void openSomeQualified(Path base) throws Exception {
389 Path src = base.resolve("src");
390 new ModuleBuilder(tb, "m")
391 .comment("opens multiple packages, some qualified")
392 .opens("p")
393 .opensTo("q", "other")
394 .classes("package p; public class C { }")
395 .classes("package q; public class D { }")
396 .write(src);
397
398 new ModuleBuilder(tb, "other")
399 .comment("dummy module for target of export")
400 .write(src);
401
402 javadoc("-d", base.resolve("out-api").toString(),
403 "-quiet",
404 "-noindex",
405 "--show-packages", "all", // required, to show open packages; see JDK-8193107
406 "--module-source-path", src.toString(),
407 "--module", "m,other");
408
409 checkExit(Exit.OK);
410 checkCaption("m", TabKind.OPENS);
411 checkTableHead("m");
412 checkPackageRow("m", "p", "i0", null, null, " ");
413
414 javadoc("-d", base.resolve("out-all").toString(),
415 "-quiet",
416 "-noindex",
417 "--show-packages", "all", // required, to show open packages; see JDK-8193107
418 "--show-module-contents", "all",
419 "--module-source-path", src.toString(),
420 "--module", "m,other");
421
422 checkExit(Exit.OK);
423 checkCaption("m", TabKind.OPENS);
424 checkTableHead("m", ColKind.OPENED_TO);
425 checkPackageRow("m", "p", "i0", null, "All Modules", " ");
426 checkPackageRow("m", "q", "i1", null,
427 """
428 <a href="../other/module-summary.html">other</a>""", " ");
429 }
430
431 @Test
432 public void openWithConcealed(Path base) throws Exception {
433 Path src = base.resolve("src");
434 new ModuleBuilder(tb, "m")
435 .comment("opens package, has concealed package")
436 .opens("p")
437 .classes("package p; public class C { }")
438 .classes("package q; public class D { }")
439 .write(src);
440
441 javadoc("-d", base.resolve("out-api").toString(),
442 "-quiet",
443 "-noindex",
444 "--show-packages", "all", // required, to show open packages; see JDK-8193107
445 "--module-source-path", src.toString(),
446 "--module", "m");
447
448 checkExit(Exit.OK);
449 checkCaption("m", TabKind.OPENS);
450 checkTableHead("m");
451 checkPackageRow("m", "p", "i0", null, null, " ");
452
453 javadoc("-d", base.resolve("out-all").toString(),
454 "-quiet",
455 "-noindex",
456 "--show-module-contents", "all",
457 "--show-packages", "all",
458 "--module-source-path", src.toString(),
459 "--module", "m");
460
461 checkExit(Exit.OK);
462 checkCaption("m", TabKind.OPENS, TabKind.CONCEALED);
463 checkTableHead("m", ColKind.OPENED_TO);
464 checkPackageRow("m", "p", "i0", null, "All Modules", " ");
465 checkPackageRow("m", "q", "i1", null, "None", " ");
466 }
467
468
469 private void checkCaption(String moduleName, TabKind... kinds) {
470 String expect;
471 if (kinds.length > 1) {
472 Set<TabKind> kindSet = Set.of(kinds);
473 StringBuilder sb = new StringBuilder();
474 sb.append("""
475 <div class="table-tabs" role="tablist" aria-orientation="horizontal"><button rol\
476 e="tab" aria-selected="true" aria-controls="package-summary-table.tabpanel" tabi\
477 ndex="0" onkeydown="switchTab(event)" id="t0" class="active-table-tab">All Packa\
478 ges</button>""");
479 if (kindSet.contains(TabKind.EXPORTS)) {
480 sb.append("""
481 <button role="tab" aria-selected="false" aria-controls="package-summary-table.ta\
482 bpanel" tabindex="-1" onkeydown="switchTab(event)" id="t1" class="table-tab" onc\
483 lick="show(1);">Exports</button>""");
484 }
485 if (kindSet.contains(TabKind.OPENS)) {
486 sb.append("""
487 <button role="tab" aria-selected="false" aria-controls="package-summary-table.ta\
488 bpanel" tabindex="-1" onkeydown="switchTab(event)" id="t2" class="table-tab" onc\
489 lick="show(2);">Opens</button>""");
490 }
491 if (kindSet.contains(TabKind.CONCEALED)) {
492 sb.append("""
493 <button role="tab" aria-selected="false" aria-controls="package-summary-table.ta\
494 bpanel" tabindex="-1" onkeydown="switchTab(event)" id="t3" class="table-tab" onc\
495 lick="show(4);">Concealed</button>""");
496 }
497 sb.append("</div>");
498 expect = sb.toString();
499 } else {
500 TabKind k = kinds[0];
501 String name = k.toString().charAt(0) + k.toString().substring(1).toLowerCase();
502 expect = "<caption><span>" + name + "</span></caption>";
503 }
504
505 checkOutput(moduleName + "/module-summary.html", true, expect);
506 }
507
508
509 private void checkTableHead(String moduleName, ColKind... kinds) {
510 Set<ColKind> kindSet = Set.of(kinds);
511 StringBuilder sb = new StringBuilder();
512 sb.append("""
513 <tr>
514 <th class="col-first" scope="col">Package</th>
515 """);
516 if (kindSet.contains(ColKind.EXPORTED_TO)) {
517 sb.append("""
518 <th class="col-second" scope="col">Exported To Modules</th>
519 """);
520 }
521 if (kindSet.contains(ColKind.OPENED_TO)) {
522 sb.append("""
523 <th class="col-second" scope="col">Opened To Modules</th>
524 """);
525 }
526 sb.append("""
527 <th class="col-last" scope="col">Description</th>
528 </tr>""");
529
530 checkOutput(moduleName + "/module-summary.html", true, sb.toString());
531 }
532
533 private void checkPackageRow(String moduleName, String packageName,
534 String id, String exportedTo, String openedTo, String desc) {
535 StringBuilder sb = new StringBuilder();
536 int idNum = Integer.parseInt(id.substring(1));
537 String color = (idNum % 2 == 1 ? "row-color" : "alt-color");
538 sb.append("<tr class=\"" + color + "\" id=\"" + id + """
539 ">
540 <th class="col-first" scope="row"><a href=\"""" + packageName.replace('.', '/') + "/package-summary.html\">"
541 + packageName + "</a></th>\n");
542 if (exportedTo != null) {
543 sb.append("<td class=\"col-second\">" + exportedTo + "</td>\n");
544 }
545 if (openedTo != null) {
546 sb.append("<td class=\"col-second\">" + openedTo + "</td>\n");
547 }
548 sb.append("<td class=\"col-last\">" + desc + "</td>");
549
550 checkOutput(moduleName + "/module-summary.html", true, sb.toString());
551 }
552
553 }
554
--- EOF ---