1 /* 2 * Copyright (c) 2016, 2017, 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 8173707 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 38 import java.io.*; 39 import java.lang.annotation.Annotation; 40 import java.lang.reflect.InvocationTargetException; 41 import java.lang.reflect.Method; 42 import java.nio.file.*; 43 import java.util.*; 44 import java.util.stream.Collectors; 45 46 import toolbox.ModuleBuilder; 47 import toolbox.ToolBox; 48 49 public 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>/module-frame.html and <module>/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 + "/module-frame.html", 310 m + "/module-type-frame.html").stream()) 311 .collect(Collectors.toSet())); 312 313 // <package>/package-frame.html files 314 checkFiles(frames, classes.stream() 315 .map(c -> (isInModule(c) ? (modulePart(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 and Javascript to default to no-frames view, 327 // in frames mode 328 checkOutput("index.html", frames, 329 "<iframe ", 330 "</iframe>", 331 "<body onload=\"loadFrames()\">\n" 332 + "<script type=\"text/javascript\">\n" 333 + "if (targetPage == \"\" || targetPage == \"undefined\")"); 334 335 // the index.html contains the overview if one 336 // has been given, and not in frames mode 337 checkOutput("index.html", !frames && oKind == OverviewKind.OVERVIEW, 338 "This is the overview file"); 339 340 // the index.html file contains a summary table 341 // if an overview was generated and not in frames mode 342 checkOutput("index.html", !frames && overview, 343 "<table class=\"overviewSummary\""); 344 345 // the index.html file contains a redirect if 346 // no frames and no overview 347 checkOutput("index.html", !frames && !overview, 348 "<meta http-equiv=\"Refresh\" content=\"0;", 349 "<script type=\"text/javascript\">window.location.replace("); 350 351 // the index.html file <meta> refresh should only use <noscript> in HTML 5 352 if (!frames && !overview) { 353 checkOutput("index.html", hKind == HtmlKind.HTML5, 354 "<noscript>\n<meta http-equiv=\"Refresh\" content=\"0;"); 355 } 356 } 357 358 private void checkNavBar() { 359 // the files containing a navigation bar should only 360 // contain FRAMES/NO-FRAMES links in frames mode 361 List<String> navbarFiles = new ArrayList<>(); 362 navbarFiles.addAll(classes.stream() 363 .map(c -> (isInModule(c) ? (modulePart(c) + "/") : "") + toHtml(packageClassPart(c))) 364 .collect(Collectors.toSet())); 365 for (String f : navbarFiles) { 366 checkOutput(f, frames, 367 "target=\"_top\">Frames</a>", 368 "target=\"_top\">No Frames</a>"); 369 } 370 } 371 372 private void checkOverviewSummary() { 373 // the overview-summary.html file only appears if 374 // in frames mode and (overview requested or multiple packages) 375 checkFiles(frames && overview, 376 "overview-summary.html"); 377 } 378 379 private long getPackageCount() { 380 return this.classes.stream() 381 .filter(name -> name.contains(".")) 382 .map(name -> name.substring(0, name.lastIndexOf("."))) 383 .distinct() 384 .count(); 385 } 386 387 private String classPart(String className) { 388 int lastDot = className.lastIndexOf("."); 389 return className.substring(lastDot + 1); 390 } 391 392 private String packagePart(String className) { 393 int slash = className.indexOf("/"); 394 int lastDot = className.lastIndexOf("."); 395 return className.substring(slash + 1, lastDot); 396 } 397 398 private String packageClassPart(String className) { 399 int slash = className.indexOf("/"); 400 return className.substring(slash + 1); 401 } 402 403 private boolean isInModule(String className) { 404 return className.contains("/"); 405 } 406 407 private String modulePart(String className) { 408 int slash = className.indexOf("/"); 409 return className.substring(0, slash); 410 } 411 412 private String toHtml(String className) { 413 return className.replace(".", "/") + ".html"; 414 } 415 } 416 }