1 /*
   2  * Copyright (c) 2015, 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  * @summary simple tests of module provides
  27  * @bug 8168854
  28  * @library /tools/lib
  29  * @modules
  30  *      jdk.compiler/com.sun.tools.javac.api
  31  *      jdk.compiler/com.sun.tools.javac.main
  32  * @build toolbox.ToolBox toolbox.JavacTask ModuleTestBase
  33  * @run main ProvidesTest
  34  */
  35 
  36 import java.nio.file.Files;
  37 import java.nio.file.Path;
  38 import java.util.Arrays;
  39 import java.util.List;
  40 
  41 import toolbox.JavacTask;
  42 import toolbox.Task;
  43 import toolbox.Task.Expect;
  44 
  45 public class ProvidesTest extends ModuleTestBase {
  46     public static void main(String... args) throws Exception {
  47         ProvidesTest t = new ProvidesTest();
  48         t.runTests();
  49     }
  50 
  51     @Test
  52     public void testSimple(Path base) throws Exception {
  53         Path src = base.resolve("src");
  54         tb.writeJavaFiles(src,
  55                 "module m { provides p1.C1 with p2.C2; }",
  56                 "package p1; public class C1 { }",
  57                 "package p2; public class C2 extends p1.C1 { }");
  58         Path classes = base.resolve("classes");
  59         Files.createDirectories(classes);
  60 
  61         new JavacTask(tb)
  62                 .outdir(classes)
  63                 .files(findJavaFiles(src))
  64                 .run(Task.Expect.SUCCESS)
  65                 .writeAll();
  66     }
  67 
  68     @Test
  69     public void testMulti(Path base) throws Exception {
  70         Path src = base.resolve("src");
  71         tb.writeJavaFiles(src.resolve("m1"),
  72                 "module m1 { exports p1; }",
  73                 "package p1; public class C1 { }");
  74         tb.writeJavaFiles(src.resolve("m2"),
  75                 "module m2 { requires m1; provides p1.C1 with p2.C2; }",
  76                 "package p2; public class C2 extends p1.C1 { }");
  77         Path modules = base.resolve("modules");
  78         Files.createDirectories(modules);
  79 
  80         new JavacTask(tb)
  81                 .options("--module-source-path", src.toString())
  82                 .outdir(modules)
  83                 .files(findJavaFiles(src))
  84                 .run(Task.Expect.SUCCESS)
  85                 .writeAll();
  86 
  87     }
  88 
  89     @Test
  90     public void testMissingWith(Path base) throws Exception {
  91         Path src = base.resolve("src");
  92         tb.writeJavaFiles(src,
  93                 "module m { provides p.C; }",
  94                 "package p; public class C { }");
  95         Path classes = base.resolve("classes");
  96         Files.createDirectories(classes);
  97 
  98         String log = new JavacTask(tb)
  99                 .options("-XDrawDiagnostics")
 100                 .outdir(classes)
 101                 .files(findJavaFiles(src))
 102                 .run(Task.Expect.FAIL)
 103                 .writeAll()
 104                 .getOutput(Task.OutputKind.DIRECT);
 105 
 106         if (!log.contains("module-info.java:1:24: compiler.err.expected: 'with'"))
 107             throw new Exception("expected output not found");
 108 
 109     }
 110 
 111     @Test
 112     public void testDuplicateProvides(Path base) throws Exception {
 113         Path src = base.resolve("src");
 114         tb.writeJavaFiles(src,
 115                 "module m { provides p1.C1 with p2.C2; provides p1.C1 with p2.C2; }",
 116                 "package p1; public class C1 { }",
 117                 "package p2; public class C2 extends p1.C1 { }");
 118         Path classes = base.resolve("classes");
 119         Files.createDirectories(classes);
 120 
 121         new JavacTask(tb)
 122                 .options("-XDrawDiagnostic")
 123                 .outdir(classes)
 124                 .files(findJavaFiles(src))
 125                 .run(Task.Expect.FAIL)
 126                 .writeAll();
 127     }
 128 
 129     @Test
 130     public void testMissingService(Path base) throws Exception {
 131         Path src = base.resolve("src");
 132         tb.writeJavaFiles(src,
 133                 "module m { provides p.Missing with p.C; }",
 134                 "package p; public class C extends p.Missing { }");
 135 
 136         List<String> output = new JavacTask(tb)
 137                 .options("-XDrawDiagnostics")
 138                 .outdir(Files.createDirectories(base.resolve("classes")))
 139                 .files(findJavaFiles(src))
 140                 .run(Task.Expect.FAIL)
 141                 .writeAll()
 142                 .getOutputLines(Task.OutputKind.DIRECT);
 143 
 144         List<String> expected = Arrays.asList(
 145                 "C.java:1:36: compiler.err.cant.resolve.location: kindname.class, Missing, , , (compiler.misc.location: kindname.package, p, null)",
 146                 "module-info.java:1:22: compiler.err.cant.resolve.location: kindname.class, Missing, , , (compiler.misc.location: kindname.package, p, null)",
 147                 "2 errors");
 148         if (!output.containsAll(expected)) {
 149             throw new Exception("Expected output not found");
 150         }
 151     }
 152 
 153     @Test
 154     public void testProvidesFromAnotherModule(Path base) throws Exception {
 155         Path modules = base.resolve("modules");
 156         tb.writeJavaFiles(modules.resolve("M"),
 157                 "module M { exports p; }",
 158                 "package p; public class Service { }");
 159         tb.writeJavaFiles(modules.resolve("L"),
 160                 "module L { requires M; provides p.Service with p.Service; }");
 161 
 162         List<String> output = new JavacTask(tb)
 163                 .options("-XDrawDiagnostics",
 164                         "--module-source-path", modules.toString())
 165                 .outdir(Files.createDirectories(base.resolve("classes")))
 166                 .files(findJavaFiles(modules))
 167                 .run(Task.Expect.FAIL)
 168                 .writeAll()
 169                 .getOutputLines(Task.OutputKind.DIRECT);
 170         List<String> expected = Arrays.asList(
 171                 "module-info.java:1:24: compiler.err.service.implementation.not.in.right.module: M",
 172                 "1 error");
 173         if (!output.containsAll(expected)) {
 174             throw new Exception("Expected output not found");
 175         }
 176 
 177     }
 178 
 179     @Test
 180     public void testServiceIsNotImplemented(Path base) throws Exception {
 181         Path src = base.resolve("src");
 182         tb.writeJavaFiles(src,
 183                 "module m { provides p.A with p.B; }",
 184                 "package p; public class A { }",
 185                 "package p; public class B { }");
 186 
 187         List<String> output = new JavacTask(tb)
 188                 .options("-XDrawDiagnostics")
 189                 .outdir(Files.createDirectories(base.resolve("classes")))
 190                 .files(findJavaFiles(src))
 191                 .run(Task.Expect.FAIL)
 192                 .writeAll()
 193                 .getOutputLines(Task.OutputKind.DIRECT);
 194 
 195         List<String> expected = Arrays.asList(
 196                 "module-info.java:1:31: compiler.err.service.implementation.must.be.subtype.of.service.interface",
 197                 "module-info.java:1:12: compiler.warn.service.provided.but.not.exported.or.used: p.A",
 198                 "1 error",
 199                 "1 warning");
 200         if (!output.containsAll(expected)) {
 201             throw new Exception("Expected output not found");
 202         }
 203     }
 204 
 205     @Test
 206     public void testMissingImplementation(Path base) throws Exception {
 207         Path src = base.resolve("src");
 208         tb.writeJavaFiles(src,
 209                 "module m { provides p.C with p.Impl; }",
 210                 "package p; public class C { }");
 211 
 212         List<String> output = new JavacTask(tb)
 213                 .options("-XDrawDiagnostics")
 214                 .outdir(Files.createDirectories(base.resolve("classes")))
 215                 .files(findJavaFiles(src))
 216                 .run(Task.Expect.FAIL)
 217                 .writeAll()
 218                 .getOutputLines(Task.OutputKind.DIRECT);
 219 
 220         List<String> expected = Arrays.asList("module-info.java:1:31: compiler.err.cant.resolve.location: kindname.class, Impl, , , (compiler.misc.location: kindname.package, p, null)",
 221                 "1 error");
 222         if (!output.containsAll(expected)) {
 223             throw new Exception("Expected output not found");
 224         }
 225     }
 226 
 227     @Test
 228     public void testSeveralImplementations(Path base) throws Exception {
 229         Path src = base.resolve("src");
 230         tb.writeJavaFiles(src,
 231                 "module m { provides p.C with p.Impl1; provides p.C with p.Impl2; }",
 232                 "package p; public class C { }",
 233                 "package p; public class Impl1 extends p.C { }",
 234                 "package p; public class Impl2 extends p.C { }");
 235 
 236         new JavacTask(tb)
 237                 .outdir(Files.createDirectories(base.resolve("classes")))
 238                 .files(findJavaFiles(src))
 239                 .run(Task.Expect.SUCCESS)
 240                 .writeAll();
 241     }
 242 
 243     @Test
 244     public void testOneImplementationsForServices(Path base) throws Exception {
 245         Path src = base.resolve("src");
 246         tb.writeJavaFiles(src,
 247                 "module m { provides p.Service1 with p.Impl; provides p.Service2 with p.Impl; }",
 248                 "package p; public interface Service1 { }",
 249                 "package p; public abstract class Service2 { }",
 250                 "package p; public class Impl extends p.Service2 implements p.Service1 { }");
 251 
 252         new JavacTask(tb)
 253                 .outdir(Files.createDirectories(base.resolve("classes")))
 254                 .files(findJavaFiles(src))
 255                 .run(Task.Expect.SUCCESS)
 256                 .writeAll();
 257     }
 258 
 259     @Test
 260     public void testAbstractImplementation(Path base) throws Exception {
 261         Path src = base.resolve("src");
 262         tb.writeJavaFiles(src,
 263                 "module m { provides p1.C1 with p2.C2; }",
 264                 "package p1; public class C1 { }",
 265                 "package p2; public abstract class C2 extends p1.C1 { }");
 266 
 267         List<String> output = new JavacTask(tb)
 268                 .options("-XDrawDiagnostics")
 269                 .outdir(Files.createDirectories(base.resolve("classes")))
 270                 .files(findJavaFiles(src))
 271                 .run(Task.Expect.FAIL)
 272                 .writeAll()
 273                 .getOutputLines(Task.OutputKind.DIRECT);
 274 
 275         List<String> expected = Arrays.asList(
 276                 "module-info.java:1:34: compiler.err.service.implementation.is.abstract: p2.C2");
 277         if (!output.containsAll(expected)) {
 278             throw new Exception("Expected output not found");
 279         }
 280     }
 281 
 282     @Test
 283     public void testInterfaceImplementation(Path base) throws Exception {
 284         Path src = base.resolve("src");
 285         tb.writeJavaFiles(src,
 286                 "module m { provides p1.Service with p2.Impl; }",
 287                 "package p1; public interface Service { }",
 288                 "package p2; public interface Impl extends p1.Service { }");
 289 
 290         List<String> output = new JavacTask(tb)
 291                 .options("-XDrawDiagnostics")
 292                 .outdir(Files.createDirectories(base.resolve("classes")))
 293                 .files(findJavaFiles(src))
 294                 .run(Task.Expect.FAIL)
 295                 .writeAll()
 296                 .getOutputLines(Task.OutputKind.DIRECT);
 297 
 298         List<String> expected = Arrays.asList(
 299                 "module-info.java:1:39: compiler.err.service.implementation.is.abstract: p2.Impl");
 300         if (!output.containsAll(expected)) {
 301             throw new Exception("Expected output not found");
 302         }
 303     }
 304 
 305     @Test
 306     public void testProtectedImplementation(Path base) throws Exception {
 307         Path src = base.resolve("src");
 308         tb.writeJavaFiles(src,
 309                 "module m { provides p1.C1 with p2.C2; }",
 310                 "package p1; public class C1 { }",
 311                 "package p2; class C2 extends p1.C1 { }");
 312 
 313         List<String> output = new JavacTask(tb)
 314                 .options("-XDrawDiagnostics")
 315                 .outdir(Files.createDirectories(base.resolve("classes")))
 316                 .files(findJavaFiles(src))
 317                 .run(Task.Expect.FAIL)
 318                 .writeAll()
 319                 .getOutputLines(Task.OutputKind.DIRECT);
 320 
 321         List<String> expected = Arrays.asList("module-info.java:1:34: compiler.err.not.def.public.cant.access: p2.C2, p2",
 322                 "1 error");
 323         if (!output.containsAll(expected)) {
 324             throw new Exception("Expected output not found");
 325         }
 326     }
 327 
 328     @Test
 329     public void testNoNoArgConstructor(Path base) throws Exception {
 330         Path src = base.resolve("src");
 331         tb.writeJavaFiles(src,
 332                 "module m { uses p1.C1; provides p1.C1 with p2.C2; }",
 333                 "package p1; public class C1 { }",
 334                 "package p2; public class C2 extends p1.C1 { public C2(String str) { } }");
 335 
 336         List<String> output = new JavacTask(tb)
 337                 .options("-XDrawDiagnostics")
 338                 .outdir(Files.createDirectories(base.resolve("classes")))
 339                 .files(findJavaFiles(src))
 340                 .run(Task.Expect.FAIL)
 341                 .writeAll()
 342                 .getOutputLines(Task.OutputKind.DIRECT);
 343 
 344         List<String> expected = Arrays.asList(
 345                 "module-info.java:1:46: compiler.err.service.implementation.doesnt.have.a.no.args.constructor: p2.C2");
 346         if (!output.containsAll(expected)) {
 347             throw new Exception("Expected output not found");
 348         }
 349     }
 350 
 351     @Test
 352     public void testPrivateNoArgConstructor(Path base) throws Exception {
 353         Path src = base.resolve("src");
 354         tb.writeJavaFiles(src,
 355                 "module m { uses p1.C1; provides p1.C1 with p2.C2; }",
 356                 "package p1; public class C1 { }",
 357                 "package p2; public class C2 extends p1.C1 { private C2() { } }");
 358 
 359         List<String> output = new JavacTask(tb)
 360                 .options("-XDrawDiagnostics")
 361                 .outdir(Files.createDirectories(base.resolve("classes")))
 362                 .files(findJavaFiles(src))
 363                 .run(Task.Expect.FAIL)
 364                 .writeAll()
 365                 .getOutputLines(Task.OutputKind.DIRECT);
 366 
 367         List<String> expected = Arrays.asList(
 368                 "module-info.java:1:46: compiler.err.service.implementation.no.args.constructor.not.public: p2.C2");
 369         if (!output.containsAll(expected)) {
 370             throw new Exception("Expected output not found");
 371         }
 372     }
 373 
 374     @Test
 375     public void testServiceIndirectlyImplemented(Path base) throws Exception {
 376         Path src = base.resolve("src");
 377         tb.writeJavaFiles(src,
 378                 "module m { provides p1.C1 with p2.C3; }",
 379                 "package p1; public class C1 { }",
 380                 "package p2; public class C2 extends p1.C1 {  }",
 381                 "package p2; public class C3 extends p2.C2 {  }");
 382 
 383         new JavacTask(tb)
 384                 .outdir(Files.createDirectories(base.resolve("classes")))
 385                 .files(findJavaFiles(src))
 386                 .run(Task.Expect.SUCCESS)
 387                 .writeAll();
 388     }
 389 
 390     @Test
 391     public void testServiceImplementationInnerClass(Path base) throws Exception {
 392         Path src = base.resolve("src");
 393         tb.writeJavaFiles(src,
 394                 "module m { provides p1.C1 with p2.C2.Inner; }",
 395                 "package p1; public class C1 { }",
 396                 "package p2; public class C2  { public class Inner extends p1.C1 { } }");
 397 
 398         List<String> output = new JavacTask(tb)
 399                 .options("-XDrawDiagnostics")
 400                 .outdir(Files.createDirectories(base.resolve("classes")))
 401                 .files(findJavaFiles(src))
 402                 .run(Task.Expect.FAIL)
 403                 .writeAll()
 404                 .getOutputLines(Task.OutputKind.DIRECT);
 405 
 406         List<String> expected = Arrays.asList(
 407                 "module-info.java:1:37: compiler.err.service.implementation.is.inner: p2.C2.Inner");
 408         if (!output.containsAll(expected)) {
 409             throw new Exception("Expected output not found");
 410         }
 411     }
 412 
 413     @Test
 414     public void testServiceDefinitionInnerClass(Path base) throws Exception {
 415         Path src = base.resolve("src");
 416         tb.writeJavaFiles(src,
 417                 "module m { provides p1.C1.InnerDefinition with p2.C2; }",
 418                 "package p1; public class C1 { public class InnerDefinition { } }",
 419                 "package p2; public class C2 extends p1.C1.InnerDefinition { public C2() { new p1.C1().super(); } }");
 420 
 421         new JavacTask(tb)
 422                 .options("-XDrawDiagnostics")
 423                 .outdir(Files.createDirectories(base.resolve("classes")))
 424                 .files(findJavaFiles(src))
 425                 .run(Expect.SUCCESS)
 426                 .writeAll();
 427     }
 428 
 429     @Test
 430     public void testFactory(Path base) throws Exception {
 431         Path src = base.resolve("src");
 432         tb.writeJavaFiles(src,
 433                 "module m { exports p1; provides p1.C1 with p2.C2; }",
 434                 "package p1; public interface C1 { }",
 435                 "package p2; public class C2 { public static p1.C1 provider() { return null; } }");
 436 
 437         new JavacTask(tb)
 438                 .options("-XDrawDiagnostics")
 439                 .outdir(Files.createDirectories(base.resolve("classes")))
 440                 .files(findJavaFiles(src))
 441                 .run()
 442                 .writeAll()
 443                 .getOutput(Task.OutputKind.DIRECT);
 444 
 445         List<String> output;
 446         List<String> expected;
 447 
 448         tb.writeJavaFiles(src,
 449                 "package p2; public class C2 { public p1.C1 provider() { return null; } }");
 450 
 451         output = new JavacTask(tb)
 452                 .options("-XDrawDiagnostics")
 453                 .outdir(Files.createDirectories(base.resolve("classes")))
 454                 .files(findJavaFiles(src))
 455                 .run(Task.Expect.FAIL)
 456                 .writeAll()
 457                 .getOutputLines(Task.OutputKind.DIRECT);
 458 
 459         expected = Arrays.asList("module-info.java:1:46: compiler.err.service.implementation.must.be.subtype.of.service.interface",
 460                                  "1 error");
 461 
 462         if (!expected.equals(output)) {
 463             throw new Exception("Expected output not found. Output: " + output);
 464         }
 465 
 466         tb.writeJavaFiles(src,
 467                 "package p2; public class C2 { static p1.C1 provider() { return null; } }");
 468 
 469         output = new JavacTask(tb)
 470                 .options("-XDrawDiagnostics")
 471                 .outdir(Files.createDirectories(base.resolve("classes")))
 472                 .files(findJavaFiles(src))
 473                 .run(Task.Expect.FAIL)
 474                 .writeAll()
 475                 .getOutputLines(Task.OutputKind.DIRECT);
 476 
 477         expected = Arrays.asList("module-info.java:1:46: compiler.err.service.implementation.must.be.subtype.of.service.interface",
 478                                  "1 error");
 479 
 480         if (!expected.equals(output)) {
 481             throw new Exception("Expected output not found. Output: " + output);
 482         }
 483 
 484         tb.writeJavaFiles(src,
 485                 "package p2; public class C2 { public static Object provider() { return null; } }");
 486 
 487         output = new JavacTask(tb)
 488                 .options("-XDrawDiagnostics")
 489                 .outdir(Files.createDirectories(base.resolve("classes")))
 490                 .files(findJavaFiles(src))
 491                 .run(Task.Expect.FAIL)
 492                 .writeAll()
 493                 .getOutputLines(Task.OutputKind.DIRECT);
 494 
 495         expected = Arrays.asList("module-info.java:1:46: compiler.err.service.implementation.provider.return.must.be.subtype.of.service.interface",
 496                                  "1 error");
 497 
 498         if (!expected.equals(output)) {
 499             throw new Exception("Expected output not found. Output: " + output);
 500         }
 501 
 502         tb.writeJavaFiles(src,
 503                 "package p2; public class C2 { public static p1.C1 provider = new p1.C1() {}; }");
 504 
 505         output = new JavacTask(tb)
 506                 .options("-XDrawDiagnostics")
 507                 .outdir(Files.createDirectories(base.resolve("classes")))
 508                 .files(findJavaFiles(src))
 509                 .run(Task.Expect.FAIL)
 510                 .writeAll()
 511                 .getOutputLines(Task.OutputKind.DIRECT);
 512 
 513         expected = Arrays.asList("module-info.java:1:46: compiler.err.service.implementation.must.be.subtype.of.service.interface",
 514                                  "1 error");
 515 
 516         if (!expected.equals(output)) {
 517             throw new Exception("Expected output not found. Output: " + output);
 518         }
 519     }
 520 }