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 import java.io.IOException; 25 import java.io.PrintWriter; 26 import java.io.StringWriter; 27 import java.nio.file.Path; 28 import java.nio.file.Paths; 29 import java.util.Arrays; 30 import java.util.HashSet; 31 import java.util.List; 32 import java.util.Locale; 33 import java.util.Set; 34 import java.util.TreeSet; 35 import java.util.stream.Collectors; 36 37 import javax.lang.model.SourceVersion; 38 import javax.lang.model.element.Element; 39 import javax.lang.model.element.ElementKind; 40 import javax.lang.model.element.ModuleElement; 41 import javax.lang.model.element.PackageElement; 42 import javax.lang.model.element.TypeElement; 43 import javax.lang.model.util.ElementFilter; 44 import javax.lang.model.util.SimpleElementVisitor9; 45 46 import jdk.javadoc.doclet.Doclet; 47 import jdk.javadoc.doclet.DocletEnvironment; 48 import jdk.javadoc.doclet.Reporter; 49 50 import toolbox.JavadocTask; 51 import toolbox.Task; 52 import toolbox.Task.Expect; 53 import toolbox.TestRunner; 54 import toolbox.ToolBox; 55 56 import static toolbox.Task.OutputKind.*; 57 58 /** 59 * Base class for module tests. 60 */ 61 public class ModuleTestBase extends TestRunner { 62 63 // Field Separator 64 private static final String FS = " "; 65 66 protected ToolBox tb; 67 private final Class<?> docletClass; 68 69 private Task.Result currentTask = null; 70 ModuleTestBase()71 ModuleTestBase() { 72 super(System.err); 73 tb = new ToolBox(); 74 ClassLoader cl = ModuleTestBase.class.getClassLoader(); 75 try { 76 docletClass = cl.loadClass("ModuleTestBase$ModulesTesterDoclet"); 77 } catch (ClassNotFoundException cfe) { 78 throw new Error(cfe); 79 } 80 } 81 82 /** 83 * Execute methods annotated with @Test, and throw an exception if any 84 * errors are reported.. 85 * 86 * @throws Exception if any errors occurred 87 */ runTests()88 protected void runTests() throws Exception { 89 runTests(m -> new Object[] { Paths.get(m.getName()) }); 90 } 91 execTask(String... args)92 Task.Result execTask(String... args) { 93 return execTask0(false, args); 94 } 95 execNegativeTask(String... args)96 Task.Result execNegativeTask(String... args) { 97 return execTask0(true, args); 98 } 99 execTask0(boolean isNegative, String... args)100 private Task.Result execTask0(boolean isNegative, String... args) { 101 JavadocTask et = new JavadocTask(tb, Task.Mode.API); 102 et.docletClass(docletClass); 103 //Arrays.asList(args).forEach((a -> System.err.println("arg: " + a))); 104 System.err.println(Arrays.asList(args)); 105 currentTask = isNegative 106 ? et.options(args).run(Expect.FAIL) 107 : et.options(args).run(); 108 return currentTask; 109 } 110 findHtmlFiles(Path... paths)111 Path[] findHtmlFiles(Path... paths) throws IOException { 112 return tb.findFiles(".html", paths); 113 } 114 grep(String regex, Path file)115 boolean grep(String regex, Path file) throws Exception { 116 List<String> lines = tb.readAllLines(file); 117 List<String> foundList = tb.grep(regex, lines); 118 return !foundList.isEmpty(); 119 } 120 normalize(String in)121 String normalize(String in) { 122 return in.replace('\\', '/'); 123 } 124 checkModulesSpecified(String... args)125 void checkModulesSpecified(String... args) throws Exception { 126 for (String arg : args) { 127 checkDocletOutputPresent("Specified", ElementKind.MODULE, arg); 128 } 129 } 130 checkPackagesSpecified(String... args)131 void checkPackagesSpecified(String... args) throws Exception { 132 for (String arg : args) { 133 checkDocletOutputPresent("Specified", ElementKind.PACKAGE, arg); 134 } 135 } 136 checkTypesSpecified(String... args)137 void checkTypesSpecified(String... args) throws Exception { 138 for (String arg : args) { 139 checkDocletOutputPresent("Specified", ElementKind.CLASS, arg); 140 } 141 } 142 checkModulesIncluded(String... args)143 void checkModulesIncluded(String... args) throws Exception { 144 for (String arg : args) { 145 checkDocletOutputPresent("Included", ElementKind.MODULE, arg); 146 } 147 } 148 checkPackagesIncluded(String... args)149 void checkPackagesIncluded(String... args) throws Exception { 150 for (String arg : args) { 151 checkDocletOutputPresent("Included", ElementKind.PACKAGE, arg); 152 } 153 } 154 checkTypesIncluded(String... args)155 void checkTypesIncluded(String... args) throws Exception { 156 for (String arg : args) { 157 checkDocletOutputPresent("Included", ElementKind.CLASS, arg); 158 } 159 } 160 checkTypesSelected(String... args)161 void checkTypesSelected(String... args) throws Exception { 162 for (String arg : args) { 163 checkDocletOutputPresent("Selected", ElementKind.CLASS, arg); 164 } 165 } 166 checkMembersSelected(String... args)167 void checkMembersSelected(String... args) throws Exception { 168 for (String arg : args) { 169 checkDocletOutputPresent("Selected", ElementKind.METHOD, arg); 170 } 171 } 172 checkModuleMode(String mode)173 void checkModuleMode(String mode) throws Exception { 174 assertPresent("^ModuleMode" + FS + mode); 175 } 176 checkStringPresent(String regex)177 void checkStringPresent(String regex) throws Exception { 178 assertPresent(regex); 179 } 180 checkDocletOutputPresent(String category, ElementKind kind, String regex)181 void checkDocletOutputPresent(String category, ElementKind kind, String regex) throws Exception { 182 assertPresent("^" + category + " " + kind.toString() + " " + regex); 183 } 184 assertPresent(String regex)185 void assertPresent(String regex) throws Exception { 186 assertPresent(regex, STDOUT); 187 } 188 assertMessagePresent(String regex)189 void assertMessagePresent(String regex) throws Exception { 190 assertPresent(regex, Task.OutputKind.DIRECT); 191 } 192 assertMessageNotPresent(String regex)193 void assertMessageNotPresent(String regex) throws Exception { 194 assertNotPresent(regex, Task.OutputKind.DIRECT); 195 } 196 assertPresent(String regex, Task.OutputKind kind)197 void assertPresent(String regex, Task.OutputKind kind) throws Exception { 198 List<String> foundList = tb.grep(regex, currentTask.getOutputLines(kind)); 199 if (foundList.isEmpty()) { 200 dumpDocletDiagnostics(); 201 throw new Exception(regex + " not found in: " + kind); 202 } 203 } 204 assertNotPresent(String regex, Task.OutputKind kind)205 void assertNotPresent(String regex, Task.OutputKind kind) throws Exception { 206 List<String> foundList = tb.grep(regex, currentTask.getOutputLines(kind)); 207 if (!foundList.isEmpty()) { 208 dumpDocletDiagnostics(); 209 throw new Exception(regex + " found in: " + kind); 210 } 211 } 212 dumpDocletDiagnostics()213 void dumpDocletDiagnostics() { 214 for (Task.OutputKind kind : Task.OutputKind.values()) { 215 String output = currentTask.getOutput(kind); 216 if (output != null && !output.isEmpty()) { 217 System.err.println("<" + kind + ">"); 218 System.err.println(output); 219 } 220 } 221 } 222 checkModulesNotSpecified(String... args)223 void checkModulesNotSpecified(String... args) throws Exception { 224 for (String arg : args) { 225 checkDocletOutputAbsent("Specified", ElementKind.MODULE, arg); 226 } 227 } 228 checkPackagesNotSpecified(String... args)229 void checkPackagesNotSpecified(String... args) throws Exception { 230 for (String arg : args) { 231 checkDocletOutputAbsent("Specified", ElementKind.PACKAGE, arg); 232 } 233 } 234 checkTypesNotSpecified(String... args)235 void checkTypesNotSpecified(String... args) throws Exception { 236 for (String arg : args) { 237 checkDocletOutputAbsent("Specified", ElementKind.CLASS, arg); 238 } 239 } 240 checkModulesNotIncluded(String... args)241 void checkModulesNotIncluded(String... args) throws Exception { 242 for (String arg : args) { 243 checkDocletOutputAbsent("Included", ElementKind.MODULE, arg); 244 } 245 } 246 checkPackagesNotIncluded(String... args)247 void checkPackagesNotIncluded(String... args) throws Exception { 248 for (String arg : args) { 249 checkDocletOutputAbsent("Included", ElementKind.PACKAGE, arg); 250 } 251 } 252 checkTypesNotIncluded(String... args)253 void checkTypesNotIncluded(String... args) throws Exception { 254 for (String arg : args) { 255 checkDocletOutputAbsent("Included", ElementKind.CLASS, arg); 256 } 257 } 258 checkMembersNotSelected(String... args)259 void checkMembersNotSelected(String... args) throws Exception { 260 for (String arg : args) { 261 checkDocletOutputAbsent("Selected", ElementKind.METHOD, arg); 262 } 263 } 264 checkStringAbsent(String regex)265 void checkStringAbsent(String regex) throws Exception { 266 assertAbsent(regex); 267 } 268 checkDocletOutputAbsent(String category, ElementKind kind, String regex)269 void checkDocletOutputAbsent(String category, ElementKind kind, String regex) throws Exception { 270 assertAbsent("^" + category + FS + kind.toString() + FS + regex); 271 } 272 assertAbsent(String regex)273 void assertAbsent(String regex) throws Exception { 274 assertAbsent(regex, STDOUT); 275 } 276 assertAbsent(String regex, Task.OutputKind kind)277 void assertAbsent(String regex, Task.OutputKind kind) throws Exception { 278 List<String> foundList = tb.grep(regex, currentTask.getOutputLines(kind)); 279 if (!foundList.isEmpty()) { 280 dumpDocletDiagnostics(); 281 throw new Exception(regex + " found in: " + kind); 282 } 283 } 284 285 public static class ModulesTesterDoclet implements Doclet { 286 StringWriter sw = new StringWriter(); 287 PrintWriter ps = new PrintWriter(sw); 288 289 DocletEnvironment docEnv = null; 290 291 boolean hasDocComments = false; 292 hasDocComments(Element e)293 String hasDocComments(Element e) { 294 String comment = docEnv.getElementUtils().getDocComment(e); 295 return comment != null && !comment.isEmpty() 296 ? "hasDocComments" 297 : "noDocComments"; 298 } 299 300 // csv style output, for simple regex verification printDataSet(String header, Set<? extends Element> set)301 void printDataSet(String header, Set<? extends Element> set) { 302 for (Element e : set) { 303 ps.print(header); 304 new SimpleElementVisitor9<Void, Void>() { 305 @Override 306 public Void visitModule(ModuleElement e, Void p) { 307 ps.print(FS); 308 ps.print(e.getKind()); 309 ps.print(FS); 310 ps.print(e.getQualifiedName()); 311 if (hasDocComments) { 312 ps.print(FS); 313 ps.print(hasDocComments(e)); 314 } 315 ps.println(); 316 return null; 317 } 318 319 @Override 320 public Void visitPackage(PackageElement e, Void p) { 321 ps.print(FS); 322 ps.print(e.getKind()); 323 ps.print(FS); 324 ps.print(e.getQualifiedName()); 325 if (hasDocComments) { 326 ps.print(FS); 327 ps.print(hasDocComments(e)); 328 } 329 ps.println(); 330 return null; 331 } 332 333 @Override 334 public Void visitType(TypeElement e, Void p) { 335 ps.print(FS); 336 ps.print(ElementKind.CLASS); 337 ps.print(FS); 338 ps.print(e.getQualifiedName()); 339 if (hasDocComments) { 340 ps.print(FS); 341 ps.print(hasDocComments(e)); 342 } 343 ps.println(); 344 return null; 345 } 346 347 @Override 348 protected Void defaultAction(Element e, Void p) { 349 Element encl = e.getEnclosingElement(); 350 CharSequence fqn = new SimpleElementVisitor9<CharSequence, Void>() { 351 @Override 352 public CharSequence visitModule(ModuleElement e, Void p) { 353 return e.getQualifiedName(); 354 } 355 356 @Override 357 public CharSequence visitType(TypeElement e, Void p) { 358 return e.getQualifiedName(); 359 } 360 361 @Override 362 public CharSequence visitPackage(PackageElement e, Void p) { 363 return e.getQualifiedName(); 364 } 365 366 }.visit(encl); 367 368 ps.print(FS); 369 ps.print(ElementKind.METHOD); // always METHOD 370 ps.print(FS); 371 ps.print(fqn); 372 ps.print("."); 373 ps.print(e.getSimpleName()); 374 if (hasDocComments) { 375 ps.print(FS); 376 ps.print(hasDocComments(e)); 377 } 378 ps.println(); 379 return null; 380 } 381 }.visit(e); 382 } 383 } 384 385 @Override run(DocletEnvironment docenv)386 public boolean run(DocletEnvironment docenv) { 387 this.docEnv = docenv; 388 ps.println("ModuleMode" + FS + docenv.getModuleMode()); 389 printDataSet("Specified", docenv.getSpecifiedElements()); 390 printDataSet("Included", docenv.getIncludedElements()); 391 printDataSet("Selected", getAllSelectedElements(docenv)); 392 System.out.println(sw); 393 return true; 394 } 395 getAllSelectedElements(DocletEnvironment docenv)396 Set<Element> getAllSelectedElements(DocletEnvironment docenv) { 397 Set<Element> result = new TreeSet<Element>((Element e1, Element e2) -> { 398 // some grouping by kind preferred 399 int rc = e1.getKind().compareTo(e2.getKind()); 400 if (rc != 0) return rc; 401 rc = e1.toString().compareTo(e2.toString()); 402 if (rc != 0) return rc; 403 return Integer.compare(e1.hashCode(), e2.hashCode()); 404 }); 405 Set<? extends Element> elements = docenv.getIncludedElements(); 406 for (ModuleElement me : ElementFilter.modulesIn(elements)) { 407 addEnclosedElements(docenv, result, me); 408 } 409 for (PackageElement pe : ElementFilter.packagesIn(elements)) { 410 ModuleElement mdle = docenv.getElementUtils().getModuleOf(pe); 411 if (mdle != null) 412 addEnclosedElements(docenv, result, mdle); 413 addEnclosedElements(docenv, result, pe); 414 } 415 for (TypeElement te : ElementFilter.typesIn(elements)) { 416 addEnclosedElements(docenv, result, te); 417 } 418 return result; 419 } 420 addEnclosedElements(DocletEnvironment docenv, Set<Element> result, Element e)421 void addEnclosedElements(DocletEnvironment docenv, Set<Element> result, Element e) { 422 List<Element> elems = e.getEnclosedElements().stream() 423 .filter(el -> docenv.isIncluded(el)) 424 .collect(Collectors.toList()); 425 result.addAll(elems); 426 for (TypeElement t : ElementFilter.typesIn(elems)) { 427 addEnclosedElements(docenv, result, t); 428 } 429 } 430 431 @Override getSupportedOptions()432 public Set<Doclet.Option> getSupportedOptions() { 433 Option[] options = { 434 new Option() { 435 private final List<String> someOption = Arrays.asList( 436 "-hasDocComments" 437 ); 438 439 @Override 440 public int getArgumentCount() { 441 return 0; 442 } 443 444 @Override 445 public String getDescription() { 446 return "print disposition of doc comments on an element"; 447 } 448 449 @Override 450 public Option.Kind getKind() { 451 return Option.Kind.STANDARD; 452 } 453 454 @Override 455 public List<String> getNames() { 456 return someOption; 457 } 458 459 @Override 460 public String getParameters() { 461 return "flag"; 462 } 463 464 @Override 465 public boolean process(String opt, List<String> arguments) { 466 hasDocComments = true; 467 return true; 468 } 469 } 470 }; 471 return new HashSet<>(Arrays.asList(options)); 472 } 473 474 @Override init(Locale locale, Reporter reporter)475 public void init(Locale locale, Reporter reporter) {} 476 477 @Override getName()478 public String getName() { 479 return "ModulesTesterDoclet"; 480 } 481 482 @Override getSupportedSourceVersion()483 public SourceVersion getSupportedSourceVersion() { 484 return SourceVersion.latest(); 485 } 486 } 487 } 488