aefimov@3315: /* aefimov@3315: * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. aefimov@3315: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aefimov@3315: * aefimov@3315: * This code is free software; you can redistribute it and/or modify it aefimov@3315: * under the terms of the GNU General Public License version 2 only, as aefimov@3315: * published by the Free Software Foundation. Oracle designates this aefimov@3315: * particular file as subject to the "Classpath" exception as provided aefimov@3315: * by Oracle in the LICENSE file that accompanied this code. aefimov@3315: * aefimov@3315: * This code is distributed in the hope that it will be useful, but WITHOUT aefimov@3315: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aefimov@3315: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aefimov@3315: * version 2 for more details (a copy is included in the LICENSE file that aefimov@3315: * accompanied this code). aefimov@3315: * aefimov@3315: * You should have received a copy of the GNU General Public License version aefimov@3315: * 2 along with this work; if not, write to the Free Software Foundation, aefimov@3315: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aefimov@3315: * aefimov@3315: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aefimov@3315: * or visit www.oracle.com if you need additional information or have any aefimov@3315: * questions. aefimov@3315: */ aefimov@3315: aefimov@3315: /** aefimov@3315: * @test aefimov@3315: * @bug 8138725 aefimov@3315: * @summary test --allow-script-in-comments aefimov@3315: * @run main TestScriptInComment aefimov@3315: */ aefimov@3315: aefimov@3315: import java.io.File; aefimov@3315: import java.io.FileWriter; aefimov@3315: import java.io.IOException; aefimov@3315: import java.io.PrintStream; aefimov@3315: import java.io.PrintWriter; aefimov@3315: import java.io.StringWriter; aefimov@3315: import java.util.ArrayList; aefimov@3315: import java.util.Arrays; aefimov@3315: import java.util.Collections; aefimov@3315: import java.util.List; aefimov@3315: import java.util.regex.Matcher; aefimov@3315: import java.util.regex.Pattern; aefimov@3315: aefimov@3315: /** aefimov@3315: * Combo-style test, exercising combinations of different HTML fragments that may contain aefimov@3315: * JavaScript, different places to place those fragments, and whether or not to allow the use aefimov@3315: * of JavaScript. aefimov@3315: */ aefimov@3315: public class TestScriptInComment { aefimov@3315: public static void main(String... args) throws Exception { aefimov@3315: new TestScriptInComment().run(); aefimov@3315: } aefimov@3315: aefimov@3315: /** aefimov@3315: * Representative samples of different fragments of HTML that may contain JavaScript. aefimov@3315: * To facilitate checking the output manually in a browser, the text "#ALERT" will be aefimov@3315: * replaced by a JavaScript call of "alert(msg)", using a message string that is specific aefimov@3315: * to the test case. aefimov@3315: */ aefimov@3315: enum Comment { aefimov@3315: LC("", true), // script tag in Lower Case aefimov@3315: UC("", true), // script tag in Upper Case aefimov@3315: WS("< script >#ALERT", false, "-Xdoclint:none"), // script tag with invalid white space aefimov@3315: SA("", true), // script tag with an attribute aefimov@3315: ON("x", true), // event handler attribute aefimov@3315: URI("x", true); // javadcript URI aefimov@3315: aefimov@3315: /** aefimov@3315: * Creates an HTML fragment to be injected into a template. aefimov@3315: * @param text the HTML fragment to put into a doc comment or option. aefimov@3315: * @param hasScript whether or not this fragment does contain legal JavaScript aefimov@3315: * @param opts any additional options to be specified when javadoc is run aefimov@3315: */ aefimov@3315: Comment(String text, boolean hasScript, String... opts) { aefimov@3315: this.text = text; aefimov@3315: this.hasScript = hasScript; aefimov@3315: this.opts = Arrays.asList(opts); aefimov@3315: } aefimov@3315: aefimov@3315: final String text; aefimov@3315: final boolean hasScript; aefimov@3315: final List opts; aefimov@3315: }; aefimov@3315: aefimov@3315: /** aefimov@3315: * Representative samples of positions in which javadoc may find JavaScript. aefimov@3315: * Each template contains a series of strings, which are written to files or inferred as options. aefimov@3315: * The first source file implies a corresponding output file which should not be written aefimov@3315: * if the comment contains JavaScript and JavaScript is not allowed. aefimov@3315: */ aefimov@3315: enum Template { aefimov@3315: OVR(" overview #COMMENT ", "package p; public class C { }"), aefimov@3315: PKGINFO("#COMMENT package p;", "package p; public class C { }"), aefimov@3315: PKGHTML("#COMMENT package p;", "package p; public class C { }"), aefimov@3315: CLS("package p; #COMMENT public class C { }"), aefimov@3315: CON("package p; public class C { #COMMENT public C() { } }"), aefimov@3315: FLD("package p; public class C { #COMMENT public int f; }"), aefimov@3315: MTH("package p; public class C { #COMMENT public void m() { } }"), aefimov@3315: TOP("-top", "lorem #COMMENT ipsum", "package p; public class C { }"), aefimov@3315: HDR("-header", "lorem #COMMENT ipsum", "package p; public class C { }"), aefimov@3315: FTR("-footer", "lorem #COMMENT ipsum", "package p; public class C { }"), aefimov@3315: BTM("-bottom", "lorem #COMMENT ipsum", "package p; public class C { }"), aefimov@3315: DTTL("-doctitle", "lorem #COMMENT ipsum", "package p; public class C { }"), aefimov@3315: PHDR("-packagesheader", "lorem #COMMENT ipsum", "package p; public class C { }"); aefimov@3315: aefimov@3315: Template(String... args) { aefimov@3315: opts = new ArrayList(); aefimov@3315: sources = new ArrayList(); aefimov@3315: int i = 0; aefimov@3315: while (args[i].startsWith("-")) { aefimov@3315: // all options being tested have a single argument that follow the option aefimov@3315: opts.add(args[i++]); aefimov@3315: opts.add(args[i++]); aefimov@3315: } aefimov@3315: while(i < args.length) { aefimov@3315: sources.add(args[i++]); aefimov@3315: } aefimov@3315: } aefimov@3315: aefimov@3315: // groups: 1 or not; 2: package name; 3: class name aefimov@3315: private final Pattern pat = aefimov@3315: Pattern.compile("(?i)()?.*?(?:package ([a-z]+);.*?(?:class ([a-z]+).*)?)?"); aefimov@3315: aefimov@3315: /** aefimov@3315: * Infer the file in which to write the given source. aefimov@3315: * @param dir the base source directory aefimov@3315: * @param src the source text aefimov@3315: * @return the file in which the source should be written aefimov@3315: */ aefimov@3315: File getSrcFile(File srcDir, String src) { aefimov@3315: String f; aefimov@3315: Matcher m = pat.matcher(src); aefimov@3315: if (!m.matches()) aefimov@3315: throw new Error("match failed"); aefimov@3315: if (m.group(3) != null) { aefimov@3315: f = m.group(2) + "/" + m.group(3) + ".java"; aefimov@3315: } else if (m.group(2) != null) { aefimov@3315: f = m.group(2) + "/" + (m.group(1) == null ? "package-info.java" : "package.html"); aefimov@3315: } else { aefimov@3315: f = "overview.html"; aefimov@3315: } aefimov@3315: return new File(srcDir, f); aefimov@3315: } aefimov@3315: aefimov@3315: /** aefimov@3315: * Get the options to give to javadoc. aefimov@3315: * @param srcDir the srcDir to use -overview is needed aefimov@3315: * @return aefimov@3315: */ aefimov@3315: List getOpts(File srcDir) { aefimov@3315: if (!opts.isEmpty()) { aefimov@3315: return opts; aefimov@3315: } else if (sources.get(0).contains("overview")) { aefimov@3315: return Arrays.asList("-overview", getSrcFile(srcDir, sources.get(0)).getPath()); aefimov@3315: } else { aefimov@3315: return Collections.emptyList(); aefimov@3315: } aefimov@3315: } aefimov@3315: aefimov@3315: /** aefimov@3315: * Gets the output file corresponding to the first source file. aefimov@3315: * This file should not be written if the comment contains JavaScript and JavaScripot is aefimov@3315: * not allowed. aefimov@3315: * @param dir the base output directory aefimov@3315: * @return the output file aefimov@3315: */ aefimov@3315: File getOutFile(File outDir) { aefimov@3315: String f; aefimov@3315: Matcher m = pat.matcher(sources.get(0)); aefimov@3315: if (!m.matches()) aefimov@3315: throw new Error("match failed"); aefimov@3315: if (m.group(3) != null) { aefimov@3315: f = m.group(2) + "/" + m.group(3) + ".html"; aefimov@3315: } else if (m.group(2) != null) { aefimov@3315: f = m.group(2) + "/package-summary.html"; aefimov@3315: } else { aefimov@3315: f = "overview-summary.html"; aefimov@3315: } aefimov@3315: return new File(outDir, f); aefimov@3315: } aefimov@3315: aefimov@3315: final List opts; aefimov@3315: final List sources; aefimov@3315: }; aefimov@3315: aefimov@3315: enum Option { aefimov@3315: OFF(null), aefimov@3315: ON("--allow-script-in-comments"); aefimov@3315: aefimov@3315: Option(String text) { aefimov@3315: this.text = text; aefimov@3315: } aefimov@3315: aefimov@3315: final String text; aefimov@3315: }; aefimov@3315: aefimov@3315: private PrintStream out = System.err; aefimov@3315: aefimov@3315: public void run() throws Exception { aefimov@3315: int count = 0; aefimov@3315: for (Template template: Template.values()) { aefimov@3315: for (Comment comment: Comment.values()) { aefimov@3315: for (Option option: Option.values()) { aefimov@3315: if (test(template, comment, option)) { aefimov@3315: count++; aefimov@3315: } aefimov@3315: } aefimov@3315: } aefimov@3315: } aefimov@3315: aefimov@3315: out.println(count + " test cases run"); aefimov@3315: if (errors > 0) { aefimov@3315: throw new Exception(errors + " errors occurred"); aefimov@3315: } aefimov@3315: } aefimov@3315: aefimov@3315: boolean test(Template template, Comment comment, Option option) throws IOException { aefimov@3315: if (option == Option.ON && !comment.hasScript) { aefimov@3315: // skip --allowScriptInComments if comment does not contain JavaScript aefimov@3315: return false; aefimov@3315: } aefimov@3315: aefimov@3315: String test = template + "-" + comment + "-" + option; aefimov@3315: out.println("Test: " + test); aefimov@3315: aefimov@3315: File dir = new File(test); aefimov@3315: dir.mkdirs(); aefimov@3315: File srcDir = new File(dir, "src"); aefimov@3315: File outDir = new File(dir, "out"); aefimov@3315: aefimov@3315: String alert = "alert(\"" + test + "\");"; aefimov@3315: for (String src: template.sources) { aefimov@3315: writeFile(template.getSrcFile(srcDir, src), aefimov@3315: src.replace("#COMMENT", aefimov@3315: "/** " + comment.text.replace("#ALERT", alert) + " **/")); aefimov@3315: } aefimov@3315: aefimov@3315: List opts = new ArrayList(); aefimov@3315: opts.add("-sourcepath"); aefimov@3315: opts.add(srcDir.getPath()); aefimov@3315: opts.add("-d"); aefimov@3315: opts.add(outDir.getPath()); aefimov@3315: if (option.text != null) aefimov@3315: opts.add(option.text); aefimov@3315: for (String opt: template.getOpts(srcDir)) { aefimov@3315: opts.add(opt.replace("#COMMENT", comment.text.replace("#ALERT", alert))); aefimov@3315: } aefimov@3315: opts.addAll(comment.opts); aefimov@3315: opts.add("-noindex"); // index not required; save time/space writing files aefimov@3315: opts.add("p"); aefimov@3315: aefimov@3315: StringWriter sw = new StringWriter(); aefimov@3315: PrintWriter pw = new PrintWriter(sw); aefimov@3315: int rc = javadoc(opts, pw); aefimov@3315: pw.close(); aefimov@3315: String log = sw.toString(); aefimov@3315: writeFile(new File(dir, "log.txt"), log); aefimov@3315: aefimov@3315: out.println("opts: " + opts); aefimov@3315: out.println(" rc: " + rc); aefimov@3315: out.println(" log:"); aefimov@3315: out.println(log); aefimov@3315: aefimov@3315: String ERROR = "Use --allow-script-in-comment"; aefimov@3315: File outFile = template.getOutFile(outDir); aefimov@3315: aefimov@3315: boolean expectErrors = comment.hasScript && (option == Option.OFF); aefimov@3315: aefimov@3315: if (expectErrors) { aefimov@3315: check(rc != 0, "unexpected exit code: " + rc); aefimov@3315: check(log.contains(ERROR), "expected error message not found"); aefimov@3315: check(!outFile.exists(), "output file found unexpectedly"); aefimov@3315: } else { aefimov@3315: check(rc == 0, "unexpected exit code: " + rc); aefimov@3315: check(!log.contains(ERROR), "error message found"); aefimov@3315: check(outFile.exists(), "output file not found"); aefimov@3315: } aefimov@3315: aefimov@3315: out.println(); aefimov@3315: return true; aefimov@3315: } aefimov@3315: aefimov@3315: int javadoc(List opts, PrintWriter pw) { aefimov@3315: return com.sun.tools.javadoc.Main.execute("javadoc", pw, pw, pw, aefimov@3315: "com.sun.tools.doclets.standard.Standard", opts.toArray(new String[opts.size()])); aefimov@3315: } aefimov@3315: aefimov@3315: File writeFile(File f, String text) throws IOException { aefimov@3315: f.getParentFile().mkdirs(); aefimov@3315: FileWriter fw = new FileWriter(f); aefimov@3315: try { aefimov@3315: fw.write(text); aefimov@3315: } finally { aefimov@3315: fw.close(); aefimov@3315: } aefimov@3315: return f; aefimov@3315: } aefimov@3315: aefimov@3315: void check(boolean cond, String errMessage) { aefimov@3315: if (!cond) { aefimov@3315: error(errMessage); aefimov@3315: } aefimov@3315: } aefimov@3315: aefimov@3315: void error(String message) { aefimov@3315: out.println("Error: " + message); aefimov@3315: errors++; aefimov@3315: } aefimov@3315: aefimov@3315: int errors = 0; aefimov@3315: } aefimov@3315: