AnnotationsOnModules.java revision 3839:03c2338ea473
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. 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/* 25 * @test 26 * @bug 8159602 8170549 8171255 27 * @summary Test annotations on module declaration. 28 * @library /tools/lib 29 * @modules jdk.compiler/com.sun.tools.javac.api 30 * jdk.compiler/com.sun.tools.javac.main 31 * jdk.jdeps/com.sun.tools.classfile 32 * @build toolbox.ToolBox toolbox.JavacTask ModuleTestBase 33 * @run main AnnotationsOnModules 34 */ 35 36import java.io.File; 37import java.nio.file.Files; 38import java.nio.file.Path; 39import java.util.Arrays; 40import java.util.HashSet; 41import java.util.List; 42import java.util.Objects; 43import java.util.Set; 44import java.util.stream.Collectors; 45 46import javax.annotation.processing.AbstractProcessor; 47import javax.annotation.processing.RoundEnvironment; 48import javax.annotation.processing.SupportedAnnotationTypes; 49import javax.annotation.processing.SupportedOptions; 50import javax.lang.model.element.AnnotationMirror; 51import javax.lang.model.element.ModuleElement; 52import javax.lang.model.element.TypeElement; 53 54import com.sun.tools.classfile.Attribute; 55import com.sun.tools.classfile.ClassFile; 56import com.sun.tools.classfile.RuntimeInvisibleAnnotations_attribute; 57import com.sun.tools.classfile.RuntimeVisibleAnnotations_attribute; 58import toolbox.JavacTask; 59import toolbox.Task; 60import toolbox.Task.OutputKind; 61 62public class AnnotationsOnModules extends ModuleTestBase { 63 64 public static void main(String... args) throws Exception { 65 AnnotationsOnModules t = new AnnotationsOnModules(); 66 t.runTests(); 67 } 68 69 @Test 70 public void testSimpleAnnotation(Path base) throws Exception { 71 Path moduleSrc = base.resolve("module-src"); 72 Path m1 = moduleSrc.resolve("m1x"); 73 74 tb.writeJavaFiles(m1, 75 "@Deprecated module m1x { }"); 76 77 Path modulePath = base.resolve("module-path"); 78 79 Files.createDirectories(modulePath); 80 81 new JavacTask(tb) 82 .options("--module-source-path", moduleSrc.toString()) 83 .outdir(modulePath) 84 .files(findJavaFiles(m1)) 85 .run() 86 .writeAll(); 87 88 ClassFile cf = ClassFile.read(modulePath.resolve("m1x").resolve("module-info.class")); 89 RuntimeVisibleAnnotations_attribute annotations = (RuntimeVisibleAnnotations_attribute) cf.attributes.map.get(Attribute.RuntimeVisibleAnnotations); 90 91 if (annotations == null || annotations.annotations.length != 1) { 92 throw new AssertionError("Annotations not correct!"); 93 } 94 } 95 96 @Test 97 public void testSimpleJavadocDeprecationTag(Path base) throws Exception { 98 Path moduleSrc = base.resolve("module-src"); 99 Path m1 = moduleSrc.resolve("src1/A"); 100 101 tb.writeJavaFiles(m1, 102 "/** @deprecated */ module A { }"); 103 104 Path modulePath = base.resolve("module-path"); 105 106 Files.createDirectories(modulePath); 107 108 List<String> warning = new JavacTask(tb) 109 .options("--module-source-path", m1.getParent().toString(), 110 "-XDrawDiagnostics") 111 .outdir(modulePath) 112 .files(findJavaFiles(m1)) 113 .run() 114 .writeAll() 115 .getOutputLines(OutputKind.DIRECT); 116 117 List<String> expected = List.of( 118 "module-info.java:1:20: compiler.warn.missing.deprecated.annotation", 119 "1 warning"); 120 if (!warning.containsAll(expected)) { 121 throw new AssertionError("Expected output not found. Expected: " + expected); 122 } 123 124 Path m2 = base.resolve("src2/B"); 125 126 tb.writeJavaFiles(m2, 127 "module B { requires A; }"); 128 String log = new JavacTask(tb) 129 .options("--module-source-path", m2.getParent().toString(), 130 "--module-path", modulePath.toString(), 131 "-XDrawDiagnostics") 132 .outdir(modulePath) 133 .files(findJavaFiles(m2)) 134 .run() 135 .writeAll() 136 .getOutput(OutputKind.DIRECT); 137 138 if (!log.isEmpty()) { 139 throw new AssertionError("Output is not empty. Expected no output and no warnings."); 140 } 141 142 ClassFile cf = ClassFile.read(modulePath.resolve("A").resolve("module-info.class")); 143 RuntimeVisibleAnnotations_attribute annotations = (RuntimeVisibleAnnotations_attribute) cf.attributes.map.get(Attribute.RuntimeVisibleAnnotations); 144 145 if (annotations != null && annotations.annotations.length > 0) { 146 throw new AssertionError("Found annotation attributes. Expected no annotations for javadoc @deprecated tag."); 147 } 148 149 if (cf.attributes.map.get(Attribute.Deprecated) != null) { 150 throw new AssertionError("Found Deprecated attribute. Expected no Deprecated attribute for javadoc @deprecated tag."); 151 } 152 } 153 154 @Test 155 public void testEnhancedDeprecatedAnnotation(Path base) throws Exception { 156 Path moduleSrc = base.resolve("module-src"); 157 Path m1 = moduleSrc.resolve("src1/A"); 158 159 tb.writeJavaFiles(m1, 160 "@Deprecated(since=\"10.X\", forRemoval=true) module A { }"); 161 162 Path modulePath = base.resolve("module-path"); 163 164 Files.createDirectories(modulePath); 165 166 new JavacTask(tb) 167 .options("--module-source-path", m1.getParent().toString()) 168 .outdir(modulePath) 169 .files(findJavaFiles(m1)) 170 .run() 171 .writeAll(); 172 173 Path m2 = base.resolve("src2/B"); 174 175 tb.writeJavaFiles(m2, 176 "module B { requires A; }"); 177 List<String> log = new JavacTask(tb) 178 .options("--module-source-path", m2.getParent().toString(), 179 "--module-path", modulePath.toString(), 180 "-XDrawDiagnostics") 181 .outdir(modulePath) 182 .files(findJavaFiles(m2)) 183 .run() 184 .writeAll() 185 .getOutputLines(OutputKind.DIRECT); 186 187 List<String> expected = List.of("module-info.java:1:21: compiler.warn.has.been.deprecated.for.removal.module: A", 188 "1 warning"); 189 if (!log.containsAll(expected)) { 190 throw new AssertionError("Expected output not found. Expected: " + expected); 191 } 192 193 ClassFile cf = ClassFile.read(modulePath.resolve("A").resolve("module-info.class")); 194 RuntimeVisibleAnnotations_attribute annotations = (RuntimeVisibleAnnotations_attribute) cf.attributes.map.get(Attribute.RuntimeVisibleAnnotations); 195 196 if (annotations == null ) { 197 throw new AssertionError("Annotations not found!"); 198 } 199 int length = annotations.annotations.length; 200 if (length != 1 ) { 201 throw new AssertionError("Incorrect number of annotations: " + length); 202 } 203 int pairsCount = annotations.annotations[0].num_element_value_pairs; 204 if (pairsCount != 2) { 205 throw new AssertionError("Incorrect number of key-value pairs in annotation: " + pairsCount + " Expected two: forRemoval and since."); 206 } 207 } 208 209 @Test 210 public void testDeprecatedModuleRequiresDeprecatedForRemovalModule(Path base) throws Exception { 211 Path moduleSrc = base.resolve("module-src"); 212 Path m1 = moduleSrc.resolve("src1/A"); 213 214 tb.writeJavaFiles(m1, 215 "@Deprecated(forRemoval=true) module A { }"); 216 217 Path modulePath = base.resolve("module-path"); 218 219 Files.createDirectories(modulePath); 220 221 new JavacTask(tb) 222 .options("--module-source-path", m1.getParent().toString()) 223 .outdir(modulePath) 224 .files(findJavaFiles(m1)) 225 .run() 226 .writeAll(); 227 228 Path m2 = base.resolve("src2/B"); 229 230 tb.writeJavaFiles(m2, 231 "@Deprecated(forRemoval=false) module B { requires A; }"); 232 List<String> log = new JavacTask(tb) 233 .options("--module-source-path", m2.getParent().toString(), 234 "--module-path", modulePath.toString(), 235 "-XDrawDiagnostics") 236 .outdir(modulePath) 237 .files(findJavaFiles(m2)) 238 .run() 239 .writeAll() 240 .getOutputLines(OutputKind.DIRECT); 241 242 List<String> expected = List.of("module-info.java:1:51: compiler.warn.has.been.deprecated.for.removal.module: A", 243 "1 warning"); 244 if (!log.containsAll(expected)) { 245 throw new AssertionError("Expected output not found. Expected: " + expected); 246 } 247 } 248 249 @Test 250 public void testExportsAndOpensToDeprecatedModule(Path base) throws Exception { 251 Path moduleSrc = base.resolve("module-src"); 252 253 254 tb.writeJavaFiles(moduleSrc.resolve("B"), 255 "@Deprecated module B { }"); 256 tb.writeJavaFiles(moduleSrc.resolve("C"), 257 "@Deprecated(forRemoval=true) module C { }"); 258 259 Path modulePath = base.resolve("module-path"); 260 Files.createDirectories(modulePath); 261 262 new JavacTask(tb) 263 .options("--module-source-path", moduleSrc.toString()) 264 .outdir(modulePath) 265 .files(findJavaFiles(moduleSrc)) 266 .run() 267 .writeAll(); 268 269 Path m1 = base.resolve("src1/A"); 270 271 tb.writeJavaFiles(m1, 272 "module A { " + 273 "exports p1 to B; opens p1 to B;" + 274 "exports p2 to C; opens p2 to C;" + 275 "exports p3 to B,C; opens p3 to B,C;" + 276 "}", 277 "package p1; public class A { }", 278 "package p2; public class A { }", 279 "package p3; public class A { }"); 280 String log = new JavacTask(tb) 281 .options("--module-source-path", m1.getParent().toString(), 282 "--module-path", modulePath.toString(), 283 "-XDrawDiagnostics") 284 .outdir(modulePath) 285 .files(findJavaFiles(m1)) 286 .run() 287 .writeAll() 288 .getOutput(OutputKind.DIRECT); 289 290 if (!log.isEmpty()) { 291 throw new AssertionError("Output is not empty! " + log); 292 } 293 } 294 295 @Test 296 public void testAnnotationWithImport(Path base) throws Exception { 297 Path moduleSrc = base.resolve("module-src"); 298 Path m1 = moduleSrc.resolve("m1x"); 299 300 tb.writeJavaFiles(m1, 301 "import m1x.A; @A module m1x { }", 302 "package m1x; import java.lang.annotation.*; @Target(ElementType.MODULE) public @interface A {}"); 303 304 Path modulePath = base.resolve("module-path"); 305 306 Files.createDirectories(modulePath); 307 308 new JavacTask(tb) 309 .options("--module-source-path", moduleSrc.toString()) 310 .outdir(modulePath) 311 .files(findJavaFiles(m1)) 312 .run() 313 .writeAll(); 314 315 ClassFile cf = ClassFile.read(modulePath.resolve("m1x").resolve("module-info.class")); 316 RuntimeInvisibleAnnotations_attribute annotations = (RuntimeInvisibleAnnotations_attribute) cf.attributes.map.get(Attribute.RuntimeInvisibleAnnotations); 317 318 if (annotations == null || annotations.annotations.length != 1) { 319 throw new AssertionError("Annotations not correct!"); 320 } 321 } 322 323 @Test 324 public void testAnnotationWithImportFromAnotherModule(Path base) throws Exception { 325 Path moduleSrc = base.resolve("module-src"); 326 Path m1 = moduleSrc.resolve("src1/A"); 327 328 tb.writeJavaFiles(m1, 329 "module A { exports p1; exports p2; }", 330 "package p1; import java.lang.annotation.*; @Target(ElementType.MODULE) public @interface A { }", 331 "package p2; import java.lang.annotation.*; @Target(ElementType.MODULE) public @interface B { }"); 332 333 Path modulePath = base.resolve("module-path"); 334 335 Files.createDirectories(modulePath); 336 337 new JavacTask(tb) 338 .options("--module-source-path", m1.getParent().toString()) 339 .outdir(modulePath) 340 .files(findJavaFiles(m1)) 341 .run() 342 .writeAll(); 343 344 Path m2 = base.resolve("src2/B"); 345 346 tb.writeJavaFiles(m2, 347 "import p1.A; @A @p2.B module B { requires A; }"); 348 new JavacTask(tb) 349 .options("--module-source-path", m2.getParent().toString(), 350 "--module-path", modulePath.toString() 351 ) 352 .outdir(modulePath) 353 .files(findJavaFiles(m2)) 354 .run() 355 .writeAll(); 356 357 ClassFile cf = ClassFile.read(modulePath.resolve("B").resolve("module-info.class")); 358 RuntimeInvisibleAnnotations_attribute annotations = (RuntimeInvisibleAnnotations_attribute) cf.attributes.map.get(Attribute.RuntimeInvisibleAnnotations); 359 360 if (annotations == null ) { 361 throw new AssertionError("Annotations not found!"); 362 } 363 int length = annotations.annotations.length; 364 if (length != 2 ) { 365 throw new AssertionError("Incorrect number of annotations: " + length); 366 } 367 } 368 369 @Test 370 public void testAnnotationWithImportAmbiguity(Path base) throws Exception { 371 Path moduleSrc = base.resolve("module-src"); 372 Path m1 = moduleSrc.resolve("src1/A"); 373 374 tb.writeJavaFiles(m1, 375 "module A { exports p1; exports p2; }", 376 "package p1; import java.lang.annotation.*; @Target(ElementType.MODULE) public @interface A { }", 377 "package p2; import java.lang.annotation.*; @Target(ElementType.MODULE) public @interface A { }"); 378 379 Path modulePath = base.resolve("module-path"); 380 381 Files.createDirectories(modulePath); 382 383 new JavacTask(tb) 384 .options("--module-source-path", m1.getParent().toString()) 385 .outdir(modulePath) 386 .files(findJavaFiles(m1)) 387 .run() 388 .writeAll(); 389 390 Path m2 = base.resolve("src2/B"); 391 392 tb.writeJavaFiles(m2, 393 "import p1.*; import p2.*; @A module B { requires A; }"); 394 List<String> log = new JavacTask(tb) 395 .options("--module-source-path", m2.getParent().toString(), 396 "--module-path", modulePath.toString(), 397 "-XDrawDiagnostics" 398 ) 399 .outdir(modulePath) 400 .files(findJavaFiles(m2)) 401 .run(Task.Expect.FAIL) 402 .writeAll() 403 .getOutputLines(OutputKind.DIRECT); 404 405 List<String> expected = List.of("module-info.java:1:28: compiler.err.ref.ambiguous: A, kindname.class, p2.A, p2, kindname.class, p1.A, p1", 406 "module-info.java:1:27: compiler.err.annotation.type.not.applicable", 407 "2 errors"); 408 if (!log.containsAll(expected)) { 409 throw new AssertionError("Expected output not found. Expected: " + expected); 410 } 411 412 } 413 414 @Test 415 public void testModuleInfoAnnotationsInAPI(Path base) throws Exception { 416 Path moduleSrc = base.resolve("module-src"); 417 Path m1 = moduleSrc.resolve("m1x"); 418 419 tb.writeJavaFiles(m1, 420 "import m1x.*; @A @Deprecated @E @E module m1x { }", 421 "package m1x; import java.lang.annotation.*; @Target(ElementType.MODULE) public @interface A {}", 422 "package m1x; import java.lang.annotation.*; @Target(ElementType.MODULE) @Repeatable(C.class) public @interface E {}", 423 "package m1x; import java.lang.annotation.*; @Target(ElementType.MODULE) public @interface C { public E[] value(); }"); 424 425 Path modulePath = base.resolve("module-path"); 426 427 Files.createDirectories(modulePath); 428 429 new JavacTask(tb) 430 .options("--module-source-path", moduleSrc.toString(), 431 "-processor", AP.class.getName()) 432 .outdir(modulePath) 433 .files(findJavaFiles(m1)) 434 .run() 435 .writeAll(); 436 437 Path src = base.resolve("src"); 438 439 tb.writeJavaFiles(src, 440 "class T {}"); 441 442 Path out = base.resolve("out"); 443 444 Files.createDirectories(out); 445 446 new JavacTask(tb) 447 .options("--module-path", modulePath.toString(), 448 "--add-modules", "m1x", 449 "-processor", AP.class.getName()) 450 .outdir(out) 451 .files(findJavaFiles(src)) 452 .run() 453 .writeAll(); 454 455 new JavacTask(tb) 456 .options("--module-path", modulePath.toString() + File.pathSeparator + out.toString(), 457 "--add-modules", "m1x", 458 "-processor", AP.class.getName(), 459 "-proc:only") 460 .classes("m1x/m1x.A") 461 .files(findJavaFiles(src)) 462 .run() 463 .writeAll(); 464 } 465 466 @SupportedAnnotationTypes("*") 467 public static final class AP extends AbstractProcessor { 468 469 @Override 470 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 471 ModuleElement m1 = processingEnv.getElementUtils().getModuleElement("m1x"); 472 Set<String> actualAnnotations = new HashSet<>(); 473 Set<String> expectedAnnotations = 474 new HashSet<>(Arrays.asList("@m1x.A", "@java.lang.Deprecated", "@m1x.C({@m1x.E, @m1x.E})")); 475 476 for (AnnotationMirror am : m1.getAnnotationMirrors()) { 477 actualAnnotations.add(am.toString()); 478 } 479 480 if (!expectedAnnotations.equals(actualAnnotations)) { 481 throw new AssertionError("Incorrect annotations: " + actualAnnotations); 482 } 483 484 return false; 485 } 486 487 } 488 489 @Test 490 public void testModuleDeprecation(Path base) throws Exception { 491 Path moduleSrc = base.resolve("module-src"); 492 Path m1 = moduleSrc.resolve("m1x"); 493 494 tb.writeJavaFiles(m1, 495 "@Deprecated module m1x { }"); 496 497 Path m2 = moduleSrc.resolve("m2x"); 498 499 tb.writeJavaFiles(m2, 500 "@Deprecated module m2x { }"); 501 502 Path m3 = moduleSrc.resolve("m3x"); 503 504 Path modulePath = base.resolve("module-path"); 505 506 Files.createDirectories(modulePath); 507 508 List<String> actual; 509 List<String> expected; 510 511 String DEPRECATED_JAVADOC = "/** @deprecated */"; 512 for (String suppress : new String[] {"", DEPRECATED_JAVADOC, "@Deprecated ", "@SuppressWarnings(\"deprecation\") "}) { 513 tb.writeJavaFiles(m3, 514 suppress + "module m3x {\n" + 515 " requires m1x;\n" + 516 " exports api to m1x, m2x;\n" + 517 "}", 518 "package api; public class Api { }"); 519 System.err.println("compile m3x"); 520 actual = new JavacTask(tb) 521 .options("--module-source-path", moduleSrc.toString(), 522 "-XDrawDiagnostics") 523 .outdir(modulePath) 524 .files(findJavaFiles(moduleSrc)) 525 .run() 526 .writeAll() 527 .getOutputLines(OutputKind.DIRECT); 528 529 if (suppress.isEmpty()) { 530 expected = Arrays.asList( 531 "- compiler.note.deprecated.filename: module-info.java", 532 "- compiler.note.deprecated.recompile"); 533 } else if (suppress.equals(DEPRECATED_JAVADOC)) { 534 expected = Arrays.asList( 535 "module-info.java:1:19: compiler.warn.missing.deprecated.annotation", 536 "- compiler.note.deprecated.filename: module-info.java", 537 "- compiler.note.deprecated.recompile", 538 "1 warning"); 539 } else { 540 expected = Arrays.asList(""); 541 } 542 543 if (!expected.equals(actual)) { 544 throw new AssertionError("Unexpected output: " + actual + "; suppress: " + suppress); 545 } 546 547 System.err.println("compile m3x with -Xlint:-deprecation"); 548 actual = new JavacTask(tb) 549 .options("--module-source-path", moduleSrc.toString(), 550 "-XDrawDiagnostics", 551 "-Xlint:deprecation") 552 .outdir(modulePath) 553 .files(findJavaFiles(moduleSrc)) 554 .run() 555 .writeAll() 556 .getOutputLines(OutputKind.DIRECT); 557 558 if (suppress.isEmpty()) { 559 expected = Arrays.asList( 560 "module-info.java:2:14: compiler.warn.has.been.deprecated.module: m1x", 561 "1 warning"); 562 } else if (suppress.equals(DEPRECATED_JAVADOC)) { 563 expected = Arrays.asList( 564 "module-info.java:1:19: compiler.warn.missing.deprecated.annotation", 565 "module-info.java:2:14: compiler.warn.has.been.deprecated.module: m1x", 566 "2 warnings"); 567 } else { 568 expected = Arrays.asList(""); 569 } 570 571 if (!expected.equals(actual)) { 572 throw new AssertionError("Unexpected output: " + actual + "; suppress: " + suppress); 573 } 574 575 //load the deprecated module-infos from classfile: 576 System.err.println("compile m3x with -Xlint:-deprecation, loading deprecated modules from classes"); 577 actual = new JavacTask(tb) 578 .options("--module-path", modulePath.toString(), 579 "-XDrawDiagnostics", 580 "-Xlint:deprecation") 581 .outdir(modulePath.resolve("m3x")) 582 .files(findJavaFiles(moduleSrc.resolve("m3x"))) 583 .run() 584 .writeAll() 585 .getOutputLines(OutputKind.DIRECT); 586 587 if (!expected.equals(actual)) { 588 throw new AssertionError("Unexpected output: " + actual + "; suppress: " + suppress); 589 } 590 } 591 } 592 593 @Test 594 public void testAttributeValues(Path base) throws Exception { 595 class TestCase { 596 public final String extraDecl; 597 public final String decl; 598 public final String use; 599 public final String expectedAnnotations; 600 601 public TestCase(String extraDecl, String decl, String use, String expectedAnnotations) { 602 this.extraDecl = extraDecl; 603 this.decl = decl; 604 this.use = use; 605 this.expectedAnnotations = expectedAnnotations; 606 } 607 } 608 609 TestCase[] testCases = new TestCase[] { 610 new TestCase("package test; public enum E {A, B;}", 611 "public E value();", 612 "test.E.A", 613 "@test.A(test.E.A)"), 614 new TestCase("package test; public enum E {A, B;}", 615 "public E[] value();", 616 "{test.E.A, test.E.B}", 617 "@test.A({test.E.A, test.E.B})"), 618 new TestCase("package test; public class Extra {}", 619 "public Class value();", 620 "test.Extra.class", 621 "@test.A(test.Extra.class)"), 622 new TestCase("package test; public class Extra {}", 623 "public Class[] value();", 624 "{test.Extra.class, String.class}", 625 "@test.A({test.Extra.class, java.lang.String.class})"), 626 new TestCase("package test; public @interface Extra { public Class value(); }", 627 "public test.Extra value();", 628 "@test.Extra(String.class)", 629 "@test.A(@test.Extra(java.lang.String.class))"), 630 new TestCase("package test; public @interface Extra { public Class value(); }", 631 "public test.Extra[] value();", 632 "{@test.Extra(String.class), @test.Extra(Integer.class)}", 633 "@test.A({@test.Extra(java.lang.String.class), @test.Extra(java.lang.Integer.class)})"), 634 new TestCase("package test; public class Any { }", 635 "public int value();", 636 "1", 637 "@test.A(1)"), 638 new TestCase("package test; public class Any { }", 639 "public int[] value();", 640 "{1, 2}", 641 "@test.A({1, 2})"), 642 new TestCase("package test; public enum E {A;}", 643 "int integer(); boolean flag(); double value(); String string(); E enumeration(); ", 644 "enumeration = test.E.A, integer = 42, flag = true, value = 3.5, string = \"Text\"", 645 "@test.A(enumeration=test.E.A, integer=42, flag=true, value=3.5, string=\"Text\")"), 646 }; 647 648 Path extraSrc = base.resolve("extra-src"); 649 tb.writeJavaFiles(extraSrc, 650 "class Any {}"); 651 652 int count = 0; 653 654 for (TestCase tc : testCases) { 655 Path testBase = base.resolve(String.valueOf(count)); 656 Path moduleSrc = testBase.resolve("module-src"); 657 Path m = moduleSrc.resolve("m"); 658 659 tb.writeJavaFiles(m, 660 "@test.A(" + tc.use + ") module m { }", 661 "package test; @java.lang.annotation.Target(java.lang.annotation.ElementType.MODULE) public @interface A { " + tc.decl + "}", 662 tc.extraDecl); 663 664 Path modulePath = testBase.resolve("module-path"); 665 666 Files.createDirectories(modulePath); 667 668 new JavacTask(tb) 669 .options("--module-source-path", moduleSrc.toString()) 670 .outdir(modulePath) 671 .files(findJavaFiles(moduleSrc)) 672 .run() 673 .writeAll(); 674 675 Path classes = testBase.resolve("classes"); 676 677 Files.createDirectories(classes); 678 679 new JavacTask(tb) 680 .options("--module-path", modulePath.toString(), 681 "--add-modules", "m", 682 "-processorpath", System.getProperty("test.classes"), 683 "-processor", ProxyTypeValidator.class.getName(), 684 "-A" + OPT_EXPECTED_ANNOTATIONS + "=" + tc.expectedAnnotations) 685 .outdir(classes) 686 .files(findJavaFiles(extraSrc)) 687 .run() 688 .writeAll(); 689 } 690 } 691 692 private static final String OPT_EXPECTED_ANNOTATIONS = "expectedAnnotations"; 693 694 @SupportedAnnotationTypes("*") 695 @SupportedOptions(OPT_EXPECTED_ANNOTATIONS) 696 public static final class ProxyTypeValidator extends AbstractProcessor { 697 698 @Override 699 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 700 ModuleElement m = processingEnv.getElementUtils().getModuleElement("m"); 701 String actualTypes = m.getAnnotationMirrors() 702 .stream() 703 .map(am -> am.toString()) 704 .collect(Collectors.joining(", ")); 705 if (!Objects.equals(actualTypes, processingEnv.getOptions().get(OPT_EXPECTED_ANNOTATIONS))) { 706 throw new IllegalStateException("Expected annotations not found, actual: " + actualTypes); 707 } 708 return false; 709 } 710 711 } 712 713} 714