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, "&nbsp;");
  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, "&nbsp;");
 122         checkPackageRow("m", "q", "i1", null, null, "&nbsp;");
 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, "&nbsp;");
 151         checkPackageRow("o", "p", "i0", null, null, "&nbsp;");
 152         checkOutput("m/p/package-summary.html", true,
 153                 """
 154                     <div class="sub-title"><span class="module-label-in-package">Module</span>&nbsp;<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>&nbsp;<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>&nbsp;<a href="../module-summary.html">m</a></div>
 163                     <div class="sub-title"><span class="package-label-in-type">Package</span>&nbsp;<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>&nbsp;<a href="../module-summary.html">o</a></div>
 168                     <div class="sub-title"><span class="package-label-in-type">Package</span>&nbsp;<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, "&nbsp;");
 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, "&nbsp;");
 209         checkPackageRow("m", "q", "i1",
 210                 """
 211                     <a href="../other/module-summary.html">other</a>""", null, "&nbsp;");
 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, "&nbsp;");
 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, "&nbsp;");
 247         checkPackageRow("m", "q", "i1", "None", null, "&nbsp;");
 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", "&nbsp;");
 283         checkPackageRow("m", "eo", "i1", "All Modules", "All Modules", "&nbsp;");
 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", "&nbsp;");
 297         checkPackageRow("m", "e.all", "i1", "All Modules", "None", "&nbsp;");
 298         checkPackageRow("m", "e.other", "i2",
 299                 """
 300                     <a href="../other/module-summary.html">other</a>""", "None", "&nbsp;");
 301         checkPackageRow("m", "eo", "i3", "All Modules", "All Modules", "&nbsp;");
 302         checkPackageRow("m", "o.all", "i4", "None", "All Modules", "&nbsp;");
 303         checkPackageRow("m", "o.other", "i5", "None",
 304                 """
 305                     <a href="../other/module-summary.html">other</a>""", "&nbsp;");
 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, "&nbsp;");
 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, "&nbsp;");
 384         checkPackageRow("m", "q", "i1", null, null, "&nbsp;");
 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, "&nbsp;");
 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", "&nbsp;");
 426         checkPackageRow("m", "q", "i1", null,
 427                 """
 428                     <a href="../other/module-summary.html">other</a>""", "&nbsp;");
 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, "&nbsp;");
 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", "&nbsp;");
 465         checkPackageRow("m", "q", "i1", null, "None", "&nbsp;");
 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