2ace3e69e6
Reviewed-by: jjg
413 lines
14 KiB
Java
413 lines
14 KiB
Java
/*
|
|
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
/*
|
|
* @test
|
|
* @bug 8218998 8219946
|
|
* @summary Add metadata to generated API documentation files
|
|
* @library /tools/lib ../../lib
|
|
* @modules jdk.javadoc/jdk.javadoc.internal.tool
|
|
* @modules jdk.compiler/com.sun.tools.javac.api
|
|
* jdk.compiler/com.sun.tools.javac.main
|
|
* jdk.javadoc/jdk.javadoc.internal.api
|
|
* jdk.javadoc/jdk.javadoc.internal.tool
|
|
* @build toolbox.ToolBox toolbox.JavacTask javadoc.tester.*
|
|
* @run main TestMetadata
|
|
*/
|
|
|
|
import java.io.IOException;
|
|
import java.nio.file.Path;
|
|
import java.util.ArrayList;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
import java.util.TreeSet;
|
|
import java.util.regex.Matcher;
|
|
import java.util.regex.Pattern;
|
|
import java.util.stream.Collectors;
|
|
|
|
import toolbox.ModuleBuilder;
|
|
import toolbox.ToolBox;
|
|
|
|
import javadoc.tester.JavadocTester;
|
|
|
|
public class TestMetadata extends JavadocTester {
|
|
public static void main(String... args) throws Exception {
|
|
TestMetadata tester = new TestMetadata();
|
|
tester.runTests();
|
|
}
|
|
|
|
enum Index { SINGLE, SPLIT };
|
|
enum Source { PACKAGES, MODULES };
|
|
|
|
final ToolBox tb = new ToolBox();
|
|
final Set<String> allBodyClassesFound = new HashSet<>();
|
|
final Set<String> allGeneratorsFound = new HashSet<>();
|
|
|
|
public void runTests() throws Exception {
|
|
for (Source s : Source.values()) {
|
|
Path src = genSource(s);
|
|
for (Index i : Index.values()) {
|
|
List<String> args = new ArrayList<>();
|
|
args.add("-d");
|
|
args.add(String.format("out-%s-%s", s, i));
|
|
args.add("-use");
|
|
if (s != Source.MODULES) {
|
|
args.add("-linksource"); // broken, with modules: JDK-8219060
|
|
}
|
|
if (i == Index.SPLIT) {
|
|
args.add("-splitIndex");
|
|
}
|
|
if (s == Source.PACKAGES) {
|
|
args.add("-sourcepath");
|
|
args.add(src.toString());
|
|
args.add("pA");
|
|
args.add("pB");
|
|
} else {
|
|
args.add("--module-source-path");
|
|
args.add(src.toString());
|
|
args.add("--module");
|
|
args.add("mA,mB");
|
|
}
|
|
javadoc(args.toArray(new String[args.size()]));
|
|
checkExit(Exit.OK);
|
|
checkBodyClasses();
|
|
checkMetadata();
|
|
|
|
// spot check the descriptions for declarations
|
|
switch (s) {
|
|
case PACKAGES:
|
|
checkOutput("pA/package-summary.html", true,
|
|
"<meta name=\"description\" content=\"declaration: package: pA\">");
|
|
checkOutput("pA/CA.html", true,
|
|
"<meta name=\"description\" content=\"declaration: package: pA, class: CA\">");
|
|
break;
|
|
|
|
case MODULES:
|
|
checkOutput("mA/module-summary.html", true,
|
|
"<meta name=\"description\" content=\"declaration: module: mA\">");
|
|
checkOutput("mA/pA/package-summary.html", true,
|
|
"<meta name=\"description\" content=\"declaration: module: mA, package: pA\">");
|
|
checkOutput("mA/pA/CA.html", true,
|
|
"<meta name=\"description\" content=\"declaration: module: mA, package: pA, class: CA\">");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
checking ("all generators");
|
|
if (allGeneratorsFound.equals(allGenerators)) {
|
|
passed("all generators found");
|
|
} else {
|
|
Set<String> notFound = new TreeSet<>(allGenerators);
|
|
notFound.removeAll(allGeneratorsFound);
|
|
failed("not found: " + notFound);
|
|
}
|
|
|
|
checking ("all body classes");
|
|
if (allBodyClassesFound.equals(allBodyClasses)) {
|
|
passed("all gbody classes found");
|
|
} else {
|
|
Set<String> notFound = new TreeSet<>(allBodyClasses);
|
|
notFound.removeAll(allBodyClassesFound);
|
|
failed("not found: " + notFound);
|
|
}
|
|
|
|
printSummary();
|
|
}
|
|
|
|
final Pattern nl = Pattern.compile("[\\r\\n]+");
|
|
final Pattern bodyPattern = Pattern.compile("<body [^>]*class=\"([^\"]+)\"");
|
|
final Set<String> allBodyClasses = Set.of(
|
|
"all-classes-index",
|
|
"all-packages-index",
|
|
"class-declaration",
|
|
"class-use",
|
|
"constants-summary",
|
|
"deprecated-list",
|
|
"doc-file",
|
|
"help",
|
|
"index-redirect",
|
|
"module-declaration",
|
|
"module-index",
|
|
"package-declaration",
|
|
"package-index",
|
|
"package-tree",
|
|
"package-use",
|
|
"serialized-form",
|
|
"single-index",
|
|
"source",
|
|
"split-index",
|
|
"tree"
|
|
);
|
|
|
|
void checkBodyClasses() throws IOException {
|
|
Path outputDirPath = outputDir.toPath();
|
|
for (Path p : tb.findFiles(".html", outputDirPath)) {
|
|
checkBodyClass(outputDirPath.relativize(p));
|
|
}
|
|
}
|
|
|
|
void checkBodyClass(Path p) {
|
|
checking("Check body: " + p);
|
|
|
|
List<String> bodyLines = nl.splitAsStream(readOutputFile(p.toString()))
|
|
.filter(s -> s.contains("<body class="))
|
|
.collect(Collectors.toList());
|
|
|
|
String bodyLine;
|
|
switch (bodyLines.size()) {
|
|
case 0:
|
|
failed("Not found: <body class=");
|
|
return;
|
|
case 1:
|
|
bodyLine = bodyLines.get(0);
|
|
break;
|
|
default:
|
|
failed("Multiple found: <body class=");
|
|
return;
|
|
}
|
|
|
|
Matcher m = bodyPattern.matcher(bodyLine);
|
|
if (m.find()) {
|
|
String bodyClass = m.group(1);
|
|
if (allBodyClasses.contains(bodyClass)) {
|
|
passed("found: " + bodyClass);
|
|
allBodyClassesFound.add(bodyClass);
|
|
} else {
|
|
failed("Unrecognized body class: " + bodyClass);
|
|
}
|
|
} else {
|
|
failed("Unrecognized line:\n" + bodyLine);
|
|
}
|
|
}
|
|
|
|
final Pattern contentPattern = Pattern.compile("content=\"([^\"]+)\">");
|
|
final Pattern generatorPattern = Pattern.compile("content=\"javadoc/([^\"]+)\">");
|
|
final Set<String> allGenerators = Set.of(
|
|
"AllClassesIndexWriter",
|
|
"AllPackagesIndexWriter",
|
|
"AnnotationTypeWriterImpl",
|
|
"ClassUseWriter",
|
|
"ClassWriterImpl",
|
|
"ConstantsSummaryWriterImpl",
|
|
"DeprecatedListWriter",
|
|
"DocFileWriter",
|
|
"HelpWriter",
|
|
"IndexRedirectWriter",
|
|
"ModuleIndexWriter",
|
|
"ModuleWriterImpl",
|
|
"PackageIndexWriter",
|
|
"PackageTreeWriter",
|
|
"PackageUseWriter",
|
|
"PackageWriterImpl",
|
|
"SerializedFormWriterImpl",
|
|
"SingleIndexWriter",
|
|
"SourceToHTMLConverter",
|
|
"SplitIndexWriter",
|
|
"TreeWriter"
|
|
);
|
|
|
|
void checkMetadata() throws IOException {
|
|
Path outputDirPath = outputDir.toPath();
|
|
for (Path p : tb.findFiles(".html", outputDirPath)) {
|
|
checkMetadata(outputDirPath.relativize(p));
|
|
}
|
|
}
|
|
|
|
void checkMetadata(Path p) {
|
|
checking("Check generator: " + p);
|
|
|
|
List<String> generators = nl.splitAsStream(readOutputFile(p.toString()))
|
|
.filter(s -> s.contains("<meta name=\"generator\""))
|
|
.collect(Collectors.toList());
|
|
|
|
String generator;
|
|
switch (generators.size()) {
|
|
case 0:
|
|
failed("Not found: <meta name=\"generator\"");
|
|
return;
|
|
case 1:
|
|
generator = generators.get(0);
|
|
break;
|
|
default:
|
|
failed("Multiple found: <meta name=\"generator\"");
|
|
return;
|
|
}
|
|
|
|
Matcher m = generatorPattern.matcher(generator);
|
|
if (m.find()) {
|
|
String content = m.group(1);
|
|
if (allGenerators.contains(content)) {
|
|
passed("found: " + content);
|
|
allGeneratorsFound.add(content);
|
|
checkDescription(p, content);
|
|
} else {
|
|
failed("Unrecognized content: " + content);
|
|
}
|
|
} else {
|
|
failed("Unrecognized line:\n" + generator);
|
|
}
|
|
|
|
}
|
|
|
|
void checkDescription(Path p, String generator) {
|
|
checking("Check description: " + p);
|
|
|
|
List<String> descriptions = nl.splitAsStream(readOutputFile(p.toString()))
|
|
.filter(s -> s.contains("<meta name=\"description\""))
|
|
.collect(Collectors.toList());
|
|
|
|
String description;
|
|
switch (descriptions.size()) {
|
|
case 0:
|
|
if (generator.equals("DocFileWriter")) {
|
|
passed("Not found, as expected");
|
|
} else {
|
|
failed("Not found: <meta name=\"description\"");
|
|
}
|
|
return;
|
|
case 1:
|
|
description = descriptions.get(0);
|
|
break;
|
|
default:
|
|
failed("Multiple found: <meta name=\"description\"");
|
|
return;
|
|
}
|
|
|
|
String content;
|
|
Matcher m = contentPattern.matcher(description);
|
|
if (m.find()) {
|
|
content = m.group(1);
|
|
} else {
|
|
failed("Unrecognized line:\n" + description);
|
|
return;
|
|
}
|
|
|
|
switch (generator) {
|
|
case "AllClassesIndexWriter":
|
|
case "AllPackagesIndexWriter":
|
|
case "ModuleIndexWriter":
|
|
case "PackageIndexWriter":
|
|
check(generator, content, content.contains("index"));
|
|
break;
|
|
|
|
|
|
case "AnnotationTypeWriterImpl":
|
|
case "ClassWriterImpl":
|
|
case "ModuleWriterImpl":
|
|
case "PackageWriterImpl":
|
|
check(generator, content, content.startsWith("declaration: "));
|
|
break;
|
|
|
|
case "ClassUseWriter":
|
|
case "PackageUseWriter":
|
|
check(generator, content, content.startsWith("use: "));
|
|
break;
|
|
|
|
case "ConstantsSummaryWriterImpl":
|
|
check(generator, content, content.contains("constants"));
|
|
break;
|
|
|
|
case "DeprecatedListWriter":
|
|
check(generator, content, content.contains("deprecated"));
|
|
break;
|
|
|
|
case "DocFileWriter":
|
|
passed("no constraint for user-provided doc-files");
|
|
break;
|
|
|
|
case "HelpWriter":
|
|
check(generator, content, content.contains("help"));
|
|
break;
|
|
|
|
case "IndexRedirectWriter":
|
|
check(generator, content, content.contains("redirect"));
|
|
break;
|
|
|
|
case "PackageTreeWriter":
|
|
case "TreeWriter":
|
|
check(generator, content, content.contains("tree"));
|
|
break;
|
|
|
|
case "SerializedFormWriterImpl":
|
|
check(generator, content, content.contains("serialized"));
|
|
break;
|
|
|
|
case "SingleIndexWriter":
|
|
case "SplitIndexWriter":
|
|
check(generator, content, content.startsWith("index"));
|
|
break;
|
|
|
|
case "SourceToHTMLConverter":
|
|
check(generator, content, content.startsWith("source:"));
|
|
break;
|
|
|
|
default:
|
|
failed("unexpected generator: " + generator);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void check(String generator, String content, boolean ok) {
|
|
if (ok) {
|
|
passed("OK: " + generator + " " + content);
|
|
} else {
|
|
failed("unexpected value for " + generator + ": " + content);
|
|
}
|
|
}
|
|
|
|
Path genSource(Source s) throws IOException {
|
|
Path src = Path.of("src-" + s);
|
|
switch (s) {
|
|
case PACKAGES:
|
|
tb.writeJavaFiles(src,
|
|
"/** Package pA. */ package pA;",
|
|
"/** Class pA.CA. */ package pA; public class CA { }",
|
|
"/** Anno pA.Anno, */ package pA; public @interface Anno { }",
|
|
"/** Serializable pA.Ser, */ package pA; public class Ser implements java.io.Serializable { }",
|
|
"/** Package pB. */ package pB;",
|
|
"/** Class pB.CB. */ package pB; public class CB { }");
|
|
tb.writeFile(src.resolve("pA").resolve("doc-files").resolve("extra.html"),
|
|
"<!doctype html>\n<html><head></head><body>Extra</body></html>");
|
|
break;
|
|
|
|
case MODULES:
|
|
new ModuleBuilder(tb, "mA")
|
|
.exports("pA")
|
|
.classes("/** Package mA/pA. */ package pA;")
|
|
.classes("/** Class mA/pA.CA. */ package pA; public class CA { }")
|
|
.write(src);
|
|
new ModuleBuilder(tb, "mB")
|
|
.exports("pB")
|
|
.classes("/** Package mB/pB. */ package pB;")
|
|
.classes("/** Class mB/pB.CB. */ package pB; public class CB { }")
|
|
.write(src);
|
|
break;
|
|
}
|
|
|
|
return src;
|
|
}
|
|
}
|
|
|