3c644dc586
Reviewed-by: prappo
452 lines
16 KiB
Java
452 lines
16 KiB
Java
/*
|
|
* Copyright (c) 2019, 2022, 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 8219060 8241190 8242056 8254627
|
|
* @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 {
|
|
var 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");
|
|
args.add("-linksource");
|
|
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 body 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-page",
|
|
"all-packages-index-page",
|
|
"class-declaration-page",
|
|
"class-use-page",
|
|
"constants-summary-page",
|
|
"deprecated-list-page",
|
|
"doc-file-page",
|
|
"external-specs-page",
|
|
"help-page",
|
|
"index-page",
|
|
"index-redirect-page",
|
|
"module-declaration-page",
|
|
"module-index-page",
|
|
"package-declaration-page",
|
|
"package-index-page",
|
|
"package-tree-page",
|
|
"package-use-page",
|
|
"search-page",
|
|
"serialized-form-page",
|
|
"source-page",
|
|
"system-properties-page",
|
|
"tree-page"
|
|
);
|
|
|
|
void checkBodyClasses() throws IOException {
|
|
for (Path p : tb.findFiles(".html", outputDir)) {
|
|
checkBodyClass(outputDir.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",
|
|
"ClassUseWriter",
|
|
"ClassWriter",
|
|
"ConstantsSummaryWriter",
|
|
"DeprecatedListWriter",
|
|
"DocFileWriter",
|
|
"ExternalSpecsWriter",
|
|
"HelpWriter",
|
|
"IndexRedirectWriter",
|
|
"IndexWriter",
|
|
"ModuleIndexWriter",
|
|
"ModuleWriter",
|
|
"PackageIndexWriter",
|
|
"PackageTreeWriter",
|
|
"PackageUseWriter",
|
|
"PackageWriter",
|
|
"SearchWriter",
|
|
"SerializedFormWriter",
|
|
"SourceToHTMLConverter",
|
|
"SystemPropertiesWriter",
|
|
"TreeWriter"
|
|
);
|
|
|
|
void checkMetadata() throws IOException {
|
|
for (Path p : tb.findFiles(".html", outputDir)) {
|
|
checkMetadata(outputDir.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 "AnnotationTypeWriter":
|
|
case "ClassWriter":
|
|
case "ModuleWriter":
|
|
case "PackageWriter":
|
|
check(generator, content, content.startsWith("declaration: "));
|
|
break;
|
|
|
|
case "ClassUseWriter":
|
|
case "PackageUseWriter":
|
|
check(generator, content, content.startsWith("use: "));
|
|
break;
|
|
|
|
case "ConstantsSummaryWriter":
|
|
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 "ExternalSpecsWriter":
|
|
check(generator, content, content.startsWith("external specifications"));
|
|
break;
|
|
|
|
case "HelpWriter":
|
|
check(generator, content, content.contains("help"));
|
|
break;
|
|
|
|
case "IndexRedirectWriter":
|
|
check(generator, content, content.contains("redirect"));
|
|
break;
|
|
|
|
case "IndexWriter":
|
|
check(generator, content, content.startsWith("index"));
|
|
break;
|
|
|
|
case "PackageTreeWriter":
|
|
case "TreeWriter":
|
|
check(generator, content, content.contains("tree"));
|
|
break;
|
|
|
|
case "SearchWriter":
|
|
check(generator, content, content.contains("search"));
|
|
break;
|
|
|
|
case "SerializedFormWriter":
|
|
check(generator, content, content.contains("serialized"));
|
|
break;
|
|
|
|
case "SourceToHTMLConverter":
|
|
check(generator, content, content.startsWith("source:"));
|
|
break;
|
|
|
|
case "SystemPropertiesWriter":
|
|
check(generator, content, content.contains("system properties"));
|
|
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. {@systemProperty exampleProperty} */ package pA;",
|
|
"""
|
|
/** Class pA.CA. */
|
|
package pA; public class CA {
|
|
/**
|
|
* First sentence.
|
|
* @spec http://example.com example reference
|
|
*/
|
|
@Deprecated public static final int ZERO = 0;
|
|
}
|
|
""",
|
|
"/** 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>
|
|
<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 {
|
|
/**
|
|
* First sentence.
|
|
* @spec http://example.com example reference
|
|
*/
|
|
@Deprecated public static int ZERO = 0;
|
|
}
|
|
""")
|
|
.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;
|
|
}
|
|
}
|