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