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 8171322 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 36 import java.io.File; 37 import java.nio.file.Files; 38 import java.nio.file.Path; 39 import java.util.Arrays; 40 import java.util.HashSet; 41 import java.util.List; 42 import java.util.Objects; 43 import java.util.Set; 44 import java.util.stream.Collectors; 45 46 import javax.annotation.processing.AbstractProcessor; 47 import javax.annotation.processing.RoundEnvironment; 48 import javax.annotation.processing.SupportedAnnotationTypes; 49 import javax.annotation.processing.SupportedOptions; 50 import javax.lang.model.element.AnnotationMirror; 51 import javax.lang.model.element.ModuleElement; 52 import javax.lang.model.element.TypeElement; 53 54 import com.sun.tools.classfile.Attribute; 55 import com.sun.tools.classfile.ClassFile; 56 import com.sun.tools.classfile.RuntimeInvisibleAnnotations_attribute; 57 import com.sun.tools.classfile.RuntimeVisibleAnnotations_attribute; 58 import toolbox.JavacTask; 59 import toolbox.Task; 60 import toolbox.Task.OutputKind; 61 62 public 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 AAA { }", 377 "package p2; import java.lang.annotation.*; @Target(ElementType.MODULE) public @interface AAA { }"); 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.*; @AAA 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: AAA, kindname.class, p2.AAA, p2, kindname.class, p1.AAA, p1", 406 "1 error"); 407 if (!log.containsAll(expected)) { 408 throw new AssertionError("Expected output not found. Expected: " + expected); 409 } 410 411 } 412 413 @Test 414 public void testModuleInfoAnnotationsInAPI(Path base) throws Exception { 415 Path moduleSrc = base.resolve("module-src"); 416 Path m1 = moduleSrc.resolve("m1x"); 417 418 tb.writeJavaFiles(m1, 419 "import m1x.*; @A @Deprecated @E @E module m1x { }", 420 "package m1x; import java.lang.annotation.*; @Target(ElementType.MODULE) public @interface A {}", 421 "package m1x; import java.lang.annotation.*; @Target(ElementType.MODULE) @Repeatable(C.class) public @interface E {}", 422 "package m1x; import java.lang.annotation.*; @Target(ElementType.MODULE) public @interface C { public E[] value(); }"); 423 424 Path modulePath = base.resolve("module-path"); 425 426 Files.createDirectories(modulePath); 427 428 new JavacTask(tb) 429 .options("--module-source-path", moduleSrc.toString(), 430 "-processor", AP.class.getName()) 431 .outdir(modulePath) 432 .files(findJavaFiles(m1)) 433 .run() 434 .writeAll(); 435 436 Path src = base.resolve("src"); 437 438 tb.writeJavaFiles(src, 439 "class T {}"); 440 441 Path out = base.resolve("out"); 442 443 Files.createDirectories(out); 444 445 new JavacTask(tb) 446 .options("--module-path", modulePath.toString(), 447 "--add-modules", "m1x", 448 "-processor", AP.class.getName()) 449 .outdir(out) 450 .files(findJavaFiles(src)) 451 .run() 452 .writeAll(); 453 454 new JavacTask(tb) 455 .options("--module-path", modulePath.toString() + File.pathSeparator + out.toString(), 456 "--add-modules", "m1x", 457 "-processor", AP.class.getName(), 458 "-proc:only") 459 .classes("m1x/m1x.A") 460 .files(findJavaFiles(src)) 461 .run() 462 .writeAll(); 463 } 464 465 @SupportedAnnotationTypes("*") 466 public static final class AP extends AbstractProcessor { 467 468 @Override 469 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 470 ModuleElement m1 = processingEnv.getElementUtils().getModuleElement("m1x"); 471 Set<String> actualAnnotations = new HashSet<>(); 472 Set<String> expectedAnnotations = 473 new HashSet<>(Arrays.asList("@m1x.A", "@java.lang.Deprecated", "@m1x.C({@m1x.E, @m1x.E})")); 474 475 for (AnnotationMirror am : m1.getAnnotationMirrors()) { 476 actualAnnotations.add(am.toString()); 477 } 478 479 if (!expectedAnnotations.equals(actualAnnotations)) { 480 throw new AssertionError("Incorrect annotations: " + actualAnnotations); 481 } 482 483 return false; 484 } 485 486 } 487 488 @Test 489 public void testModuleDeprecation(Path base) throws Exception { 490 Path moduleSrc = base.resolve("module-src"); 491 Path m1 = moduleSrc.resolve("m1x"); 492 493 tb.writeJavaFiles(m1, 494 "@Deprecated module m1x { }"); 495 496 Path m2 = moduleSrc.resolve("m2x"); 497 498 tb.writeJavaFiles(m2, 499 "@Deprecated module m2x { }"); 500 501 Path m3 = moduleSrc.resolve("m3x"); 502 503 Path modulePath = base.resolve("module-path"); 504 505 Files.createDirectories(modulePath); 506 507 List<String> actual; 508 List<String> expected; 509 510 String DEPRECATED_JAVADOC = "/** @deprecated */"; 511 for (String suppress : new String[] {"", DEPRECATED_JAVADOC, "@Deprecated ", "@SuppressWarnings(\"deprecation\") "}) { 512 tb.writeJavaFiles(m3, 513 suppress + "module m3x {\n" + 514 " requires m1x;\n" + 515 " exports api to m1x, m2x;\n" + 516 "}", 517 "package api; public class Api { }"); 518 System.err.println("compile m3x"); 519 actual = new JavacTask(tb) 520 .options("--module-source-path", moduleSrc.toString(), 521 "-XDrawDiagnostics") 522 .outdir(modulePath) 523 .files(findJavaFiles(moduleSrc)) 524 .run() 525 .writeAll() 526 .getOutputLines(OutputKind.DIRECT); 527 528 if (suppress.isEmpty()) { 529 expected = Arrays.asList( 530 "- compiler.note.deprecated.filename: module-info.java", 531 "- compiler.note.deprecated.recompile"); 532 } else if (suppress.equals(DEPRECATED_JAVADOC)) { 533 expected = Arrays.asList( 534 "module-info.java:1:19: compiler.warn.missing.deprecated.annotation", 535 "- compiler.note.deprecated.filename: module-info.java", 536 "- compiler.note.deprecated.recompile", 537 "1 warning"); 538 } else { 539 expected = Arrays.asList(""); 540 } 541 542 if (!expected.equals(actual)) { 543 throw new AssertionError("Unexpected output: " + actual + "; suppress: " + suppress); 544 } 545 546 System.err.println("compile m3x with -Xlint:-deprecation"); 547 actual = new JavacTask(tb) 548 .options("--module-source-path", moduleSrc.toString(), 549 "-XDrawDiagnostics", 550 "-Xlint:deprecation") 551 .outdir(modulePath) 552 .files(findJavaFiles(moduleSrc)) 553 .run() 554 .writeAll() 555 .getOutputLines(OutputKind.DIRECT); 556 557 if (suppress.isEmpty()) { 558 expected = Arrays.asList( 559 "module-info.java:2:14: compiler.warn.has.been.deprecated.module: m1x", 560 "1 warning"); 561 } else if (suppress.equals(DEPRECATED_JAVADOC)) { 562 expected = Arrays.asList( 563 "module-info.java:1:19: compiler.warn.missing.deprecated.annotation", 564 "module-info.java:2:14: compiler.warn.has.been.deprecated.module: m1x", 565 "2 warnings"); 566 } else { 567 expected = Arrays.asList(""); 568 } 569 570 if (!expected.equals(actual)) { 571 throw new AssertionError("Unexpected output: " + actual + "; suppress: " + suppress); 572 } 573 574 //load the deprecated module-infos from classfile: 575 System.err.println("compile m3x with -Xlint:-deprecation, loading deprecated modules from classes"); 576 actual = new JavacTask(tb) 577 .options("--module-path", modulePath.toString(), 578 "-XDrawDiagnostics", 579 "-Xlint:deprecation") 580 .outdir(modulePath.resolve("m3x")) 581 .files(findJavaFiles(moduleSrc.resolve("m3x"))) 582 .run() 583 .writeAll() 584 .getOutputLines(OutputKind.DIRECT); 585 586 if (!expected.equals(actual)) { 587 throw new AssertionError("Unexpected output: " + actual + "; suppress: " + suppress); 588 } 589 } 590 } 591 592 @Test 593 public void testAttributeValues(Path base) throws Exception { 594 class TestCase { 595 public final String extraDecl; 596 public final String decl; 597 public final String use; 598 public final String expectedAnnotations; 599 600 public TestCase(String extraDecl, String decl, String use, String expectedAnnotations) { 601 this.extraDecl = extraDecl; 602 this.decl = decl; 603 this.use = use; 604 this.expectedAnnotations = expectedAnnotations; 605 } 606 } 607 608 TestCase[] testCases = new TestCase[] { 609 new TestCase("package test; public enum E {A, B;}", 610 "public E value();", 611 "test.E.A", 612 "@test.A(A)"), 613 new TestCase("package test; public enum E {A, B;}", 614 "public E[] value();", 615 "{test.E.A, test.E.B}", 616 "@test.A({A, B})"), 617 new TestCase("package test; public class Extra {}", 618 "public Class value();", 619 "test.Extra.class", 620 "@test.A(test.Extra.class)"), 621 new TestCase("package test; public class Extra {}", 622 "public Class[] value();", 623 "{test.Extra.class, String.class}", 624 "@test.A({test.Extra.class, java.lang.String.class})"), 625 new TestCase("package test; public @interface Extra { public Class value(); }", 626 "public test.Extra value();", 627 "@test.Extra(String.class)", 628 "@test.A(@test.Extra(java.lang.String.class))"), 629 new TestCase("package test; public @interface Extra { public Class value(); }", 630 "public test.Extra[] value();", 631 "{@test.Extra(String.class), @test.Extra(Integer.class)}", 632 "@test.A({@test.Extra(java.lang.String.class), @test.Extra(java.lang.Integer.class)})"), 633 new TestCase("package test; public class Any { }", 634 "public int value();", 635 "1", 636 "@test.A(1)"), 637 new TestCase("package test; public class Any { }", 638 "public int[] value();", 639 "{1, 2}", 640 "@test.A({1, 2})"), 641 new TestCase("package test; public enum E {A;}", 642 "int integer(); boolean flag(); double value(); String string(); E enumeration(); ", 643 "enumeration = test.E.A, integer = 42, flag = true, value = 3.5, string = \"Text\"", 644 "@test.A(enumeration=A, integer=42, flag=true, value=3.5, string=\"Text\")"), 645 }; 646 647 Path extraSrc = base.resolve("extra-src"); 648 tb.writeJavaFiles(extraSrc, 649 "class Any {}"); 650 651 int count = 0; 652 653 for (TestCase tc : testCases) { 654 Path testBase = base.resolve(String.valueOf(count)); 655 Path moduleSrc = testBase.resolve("module-src"); 656 Path m = moduleSrc.resolve("m"); 657 658 tb.writeJavaFiles(m, 659 "@test.A(" + tc.use + ") module m { }", 660 "package test; @java.lang.annotation.Target(java.lang.annotation.ElementType.MODULE) public @interface A { " + tc.decl + "}", 661 tc.extraDecl); 662 663 Path modulePath = testBase.resolve("module-path"); 664 665 Files.createDirectories(modulePath); 666 667 new JavacTask(tb) 668 .options("--module-source-path", moduleSrc.toString()) 669 .outdir(modulePath) 670 .files(findJavaFiles(moduleSrc)) 671 .run() 672 .writeAll(); 673 674 Path classes = testBase.resolve("classes"); 675 676 Files.createDirectories(classes); 677 678 new JavacTask(tb) 679 .options("--module-path", modulePath.toString(), 680 "--add-modules", "m", 681 "-processorpath", System.getProperty("test.classes"), 682 "-processor", ProxyTypeValidator.class.getName(), 683 "-A" + OPT_EXPECTED_ANNOTATIONS + "=" + tc.expectedAnnotations) 684 .outdir(classes) 685 .files(findJavaFiles(extraSrc)) 686 .run() 687 .writeAll(); 688 } 689 } 690 691 private static final String OPT_EXPECTED_ANNOTATIONS = "expectedAnnotations"; 692 693 @SupportedAnnotationTypes("*") 694 @SupportedOptions(OPT_EXPECTED_ANNOTATIONS) 695 public static final class ProxyTypeValidator extends AbstractProcessor { 696 697 @Override 698 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 699 ModuleElement m = processingEnv.getElementUtils().getModuleElement("m"); 700 String actualTypes = m.getAnnotationMirrors() 701 .stream() 702 .map(am -> am.toString()) 703 .collect(Collectors.joining(", ")); 704 if (!Objects.equals(actualTypes, processingEnv.getOptions().get(OPT_EXPECTED_ANNOTATIONS))) { 705 throw new IllegalStateException("Expected annotations not found, actual: " + actualTypes); 706 } 707 return false; 708 } 709 710 } 711 712 }