TestScriptInComment.java revision 3896:8e4dbcb99277
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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26/** 27 * @test 28 * @bug 8138725 29 * @summary test --allow-script-in-comments 30 * @modules jdk.javadoc/jdk.javadoc.internal.tool 31 */ 32 33import java.io.File; 34import java.io.FileWriter; 35import java.io.IOException; 36import java.io.PrintStream; 37import java.io.PrintWriter; 38import java.io.StringWriter; 39import java.util.ArrayList; 40import java.util.Arrays; 41import java.util.Collections; 42import java.util.List; 43import java.util.regex.Matcher; 44import java.util.regex.Pattern; 45 46/** 47 * Combo-style test, exercising combinations of different HTML fragments that may contain 48 * JavaScript, different places to place those fragments, and whether or not to allow the use 49 * of JavaScript. 50 */ 51public class TestScriptInComment { 52 public static void main(String... args) throws Exception { 53 new TestScriptInComment().run(); 54 } 55 56 /** 57 * Representative samples of different fragments of HTML that may contain JavaScript. 58 * To facilitate checking the output manually in a browser, the text "#ALERT" will be 59 * replaced by a JavaScript call of "alert(msg)", using a message string that is specific 60 * to the test case. 61 */ 62 enum Comment { 63 LC("<script>#ALERT</script>", true), // script tag in Lower Case 64 UC("<SCRIPT>#ALERT</script>", true), // script tag in Upper Case 65 WS("< script >#ALERT</script>", false, "-Xdoclint:none"), // script tag with invalid white space 66 SP("<script src=\"file\"> #ALERT </script>", true), // script tag with an attribute 67 ON("<a onclick='#ALERT'>x</a>", true), // event handler attribute 68 URI("<a href='javascript:#ALERT'>x</a>", true); // javascript URI 69 70 /** 71 * Creates an HTML fragment to be injected into a template. 72 * @param text the HTML fragment to put into a doc comment or option. 73 * @param hasScript whether or not this fragment does contain legal JavaScript 74 * @param opts any additional options to be specified when javadoc is run 75 */ 76 Comment(String text, boolean hasScript, String... opts) { 77 this.text = text; 78 this.hasScript = hasScript; 79 this.opts = Arrays.asList(opts); 80 } 81 82 final String text; 83 final boolean hasScript; 84 final List<String> opts; 85 }; 86 87 /** 88 * Representative samples of positions in which javadoc may find JavaScript. 89 * Each template contains a series of strings, which are written to files or inferred as options. 90 * The first source file implies a corresponding output file which should not be written 91 * if the comment contains JavaScript and JavaScript is not allowed. 92 */ 93 enum Template { 94 OVR("<html><body> overview #COMMENT </body></html>", "package p; public class C { }"), 95 PKGINFO("#COMMENT package p;", "package p; public class C { }"), 96 PKGHTML("<html><body>#COMMENT package p;</body></html>", "package p; public class C { }"), 97 CLS("package p; #COMMENT public class C { }"), 98 CON("package p; public class C { #COMMENT public C() { } }"), 99 FLD("package p; public class C { #COMMENT public int f; }"), 100 MTH("package p; public class C { #COMMENT public void m() { } }"), 101 TOP("-top", "lorem #COMMENT ipsum", "package p; public class C { }"), 102 HDR("-header", "lorem #COMMENT ipsum", "package p; public class C { }"), 103 FTR("-footer", "lorem #COMMENT ipsum", "package p; public class C { }"), 104 BTM("-bottom", "lorem #COMMENT ipsum", "package p; public class C { }"), 105 DTTL("-doctitle", "lorem #COMMENT ipsum", "package p; public class C { }"), 106 PHDR("-packagesheader", "lorem #COMMENT ipsum", "package p; public class C { }"); 107 108 Template(String... args) { 109 opts = new ArrayList<String>(); 110 sources = new ArrayList<String>(); 111 int i = 0; 112 while (args[i].startsWith("-")) { 113 // all options being tested have a single argument that follow the option 114 opts.add(args[i++]); 115 opts.add(args[i++]); 116 } 117 while(i < args.length) { 118 sources.add(args[i++]); 119 } 120 } 121 122 // groups: 1 <html> or not; 2: package name; 3: class name 123 private final Pattern pat = 124 Pattern.compile("(?i)(<html>)?.*?(?:package ([a-z]+);.*?(?:class ([a-z]+).*)?)?"); 125 126 /** 127 * Infer the file in which to write the given source. 128 * @param dir the base source directory 129 * @param src the source text 130 * @return the file in which the source should be written 131 */ 132 File getSrcFile(File srcDir, String src) { 133 String f; 134 Matcher m = pat.matcher(src); 135 if (!m.matches()) 136 throw new Error("match failed"); 137 if (m.group(3) != null) { 138 f = m.group(2) + "/" + m.group(3) + ".java"; 139 } else if (m.group(2) != null) { 140 f = m.group(2) + "/" + (m.group(1) == null ? "package-info.java" : "package.html"); 141 } else { 142 f = "overview.html"; 143 } 144 return new File(srcDir, f); 145 } 146 147 /** 148 * Get the options to give to javadoc. 149 * @param srcDir the srcDir to use -overview is needed 150 * @return 151 */ 152 List<String> getOpts(File srcDir) { 153 if (!opts.isEmpty()) { 154 return opts; 155 } else if (sources.get(0).contains("overview")) { 156 return Arrays.asList("-overview", getSrcFile(srcDir, sources.get(0)).getPath()); 157 } else { 158 return Collections.emptyList(); 159 } 160 } 161 162 /** 163 * Gets the output file corresponding to the first source file. 164 * This file should not be written if the comment contains JavaScript and JavaScripot is 165 * not allowed. 166 * @param dir the base output directory 167 * @return the output file 168 */ 169 File getOutFile(File outDir) { 170 String f; 171 Matcher m = pat.matcher(sources.get(0)); 172 if (!m.matches()) 173 throw new Error("match failed"); 174 if (m.group(3) != null) { 175 f = m.group(2) + "/" + m.group(3) + ".html"; 176 } else if (m.group(2) != null) { 177 f = m.group(2) + "/package-summary.html"; 178 } else { 179 f = "overview-summary.html"; 180 } 181 return new File(outDir, f); 182 } 183 184 final List<String> opts; 185 final List<String> sources; 186 }; 187 188 enum Option { 189 OFF(null), 190 ON("--allow-script-in-comments"); 191 192 Option(String text) { 193 this.text = text; 194 } 195 196 final String text; 197 }; 198 199 private PrintStream out = System.err; 200 201 public void run() throws Exception { 202 int count = 0; 203 for (Template template: Template.values()) { 204 for (Comment comment: Comment.values()) { 205 for (Option option: Option.values()) { 206 if (test(template, comment, option)) { 207 count++; 208 } 209 } 210 } 211 } 212 213 out.println(count + " test cases run"); 214 if (errors > 0) { 215 throw new Exception(errors + " errors occurred"); 216 } 217 } 218 219 boolean test(Template template, Comment comment, Option option) throws IOException { 220 if (option == Option.ON && !comment.hasScript) { 221 // skip --allowScriptInComments if comment does not contain JavaScript 222 return false; 223 } 224 225 String test = template + "-" + comment + "-" + option; 226 out.println("Test: " + test); 227 228 File dir = new File(test); 229 dir.mkdirs(); 230 File srcDir = new File(dir, "src"); 231 File outDir = new File(dir, "out"); 232 233 String alert = "alert(\"" + test + "\");"; 234 for (String src: template.sources) { 235 writeFile(template.getSrcFile(srcDir, src), 236 src.replace("#COMMENT", 237 "/** " + comment.text.replace("#ALERT", alert) + " **/")); 238 } 239 240 List<String> opts = new ArrayList<String>(); 241 opts.add("-sourcepath"); 242 opts.add(srcDir.getPath()); 243 opts.add("-d"); 244 opts.add(outDir.getPath()); 245 if (option.text != null) 246 opts.add(option.text); 247 for (String opt: template.getOpts(srcDir)) { 248 opts.add(opt.replace("#COMMENT", comment.text.replace("#ALERT", alert))); 249 } 250 opts.addAll(comment.opts); 251 opts.add("-noindex"); // index not required; save time/space writing files 252 opts.add("p"); 253 254 StringWriter sw = new StringWriter(); 255 PrintWriter pw = new PrintWriter(sw); 256 int rc = javadoc(opts, pw); 257 pw.close(); 258 String log = sw.toString(); 259 writeFile(new File(dir, "log.txt"), log); 260 261 out.println("opts: " + opts); 262 out.println(" rc: " + rc); 263 out.println(" log:"); 264 out.println(log); 265 266 String ERROR = "Use --allow-script-in-comment"; 267 File outFile = template.getOutFile(outDir); 268 269 boolean expectErrors = comment.hasScript && (option == Option.OFF); 270 271 if (expectErrors) { 272 check(rc != 0, "unexpected exit code: " + rc); 273 check(log.contains(ERROR), "expected error message not found"); 274 check(!outFile.exists(), "output file found unexpectedly"); 275 } else { 276 check(rc == 0, "unexpected exit code: " + rc); 277 check(!log.contains(ERROR), "error message found"); 278 check(outFile.exists(), "output file not found"); 279 } 280 281 out.println(); 282 return true; 283 } 284 285 int javadoc(List<String> opts, PrintWriter pw) { 286 return jdk.javadoc.internal.tool.Main.execute(opts.toArray(new String[opts.size()]), pw); 287 } 288 289 File writeFile(File f, String text) throws IOException { 290 f.getParentFile().mkdirs(); 291 FileWriter fw = new FileWriter(f); 292 try { 293 fw.write(text); 294 } finally { 295 fw.close(); 296 } 297 return f; 298 } 299 300 void check(boolean cond, String errMessage) { 301 if (!cond) { 302 error(errMessage); 303 } 304 } 305 306 void error(String message) { 307 out.println("Error: " + message); 308 errors++; 309 } 310 311 int errors = 0; 312} 313 314