1 /*
   2  * Copyright (c) 2014, 2025, 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 8142968 8299504
  27  * @modules java.base/jdk.internal.access
  28  *          java.base/jdk.internal.module
  29  * @library /test/lib
  30  * @build ConfigurationTest
  31  *        jdk.test.lib.util.ModuleUtils
  32  * @run junit ConfigurationTest
  33  * @summary Basic tests for java.lang.module.Configuration
  34  */
  35 
  36 import java.io.IOException;
  37 import java.io.OutputStream;
  38 import java.lang.module.Configuration;
  39 import java.lang.module.FindException;
  40 import java.lang.module.ModuleDescriptor;
  41 import java.lang.module.ModuleDescriptor.Requires;
  42 import java.lang.module.ModuleFinder;
  43 import java.lang.module.ResolutionException;
  44 import java.lang.module.ResolvedModule;
  45 import java.nio.file.Files;
  46 import java.nio.file.Path;
  47 import java.nio.file.Paths;
  48 import java.util.List;
  49 import java.util.Set;
  50 import java.util.stream.Stream;
  51 import jdk.internal.access.SharedSecrets;
  52 import jdk.internal.module.ModuleTarget;
  53 
  54 import jdk.test.lib.util.ModuleInfoWriter;
  55 import jdk.test.lib.util.ModuleUtils;
  56 
  57 import org.junit.jupiter.api.Test;
  58 import org.junit.jupiter.params.ParameterizedTest;
  59 import org.junit.jupiter.params.provider.MethodSource;
  60 import org.junit.jupiter.params.provider.CsvSource;
  61 import static org.junit.jupiter.api.Assertions.*;
  62 
  63 class ConfigurationTest {
  64 
  65     /**
  66      * Creates a "non-strict" builder for building a module. This allows the
  67      * test the create ModuleDescriptor objects that do not require java.base.
  68      */
  69     private static ModuleDescriptor.Builder newBuilder(String mn) {
  70         return SharedSecrets.getJavaLangModuleAccess()
  71                 .newModuleBuilder(mn, false, Set.of());
  72     }
  73 
  74     /**
  75      * Basic test of resolver
  76      *     m1 requires m2, m2 requires m3
  77      */
  78     @Test
  79     void testBasic() {
  80         ModuleDescriptor descriptor1 = newBuilder("m1")
  81                 .requires("m2")
  82                 .build();
  83 
  84         ModuleDescriptor descriptor2 = newBuilder("m2")
  85                 .requires("m3")
  86                 .build();
  87 
  88         ModuleDescriptor descriptor3 = newBuilder("m3")
  89                 .build();
  90 
  91         ModuleFinder finder
  92             = ModuleUtils.finderOf(descriptor1, descriptor2, descriptor3);
  93 
  94         Configuration cf = resolve(finder, "m1");
  95 
  96         assertTrue(cf.modules().size() == 3);
  97 
  98         assertTrue(cf.findModule("m1").isPresent());
  99         assertTrue(cf.findModule("m2").isPresent());
 100         assertTrue(cf.findModule("m3").isPresent());
 101 
 102         assertTrue(cf.parents().size() == 1);
 103         assertTrue(cf.parents().get(0) == Configuration.empty());
 104 
 105         ResolvedModule m1 = cf.findModule("m1").get();
 106         ResolvedModule m2 = cf.findModule("m2").get();
 107         ResolvedModule m3 = cf.findModule("m3").get();
 108 
 109         // m1 reads m2
 110         assertTrue(m1.reads().size() == 1);
 111         assertTrue(m1.reads().contains(m2));
 112 
 113         // m2 reads m3
 114         assertTrue(m2.reads().size() == 1);
 115         assertTrue(m2.reads().contains(m3));
 116 
 117         // m3 reads nothing
 118         assertTrue(m3.reads().size() == 0);
 119 
 120         // toString
 121         assertTrue(cf.toString().contains("m1"));
 122         assertTrue(cf.toString().contains("m2"));
 123         assertTrue(cf.toString().contains("m3"));
 124     }
 125 
 126 
 127     /**
 128      * Basic test of "requires transitive":
 129      *     m1 requires m2, m2 requires transitive m3
 130      */
 131     @Test
 132     void testRequiresTransitive1() {
 133         // m1 requires m2, m2 requires transitive m3
 134         ModuleDescriptor descriptor1 = newBuilder("m1")
 135                 .requires("m2")
 136                 .build();
 137 
 138         ModuleDescriptor descriptor2 = newBuilder("m2")
 139                 .requires(Set.of(Requires.Modifier.TRANSITIVE), "m3")
 140                 .build();
 141 
 142         ModuleDescriptor descriptor3 = newBuilder("m3")
 143                 .build();
 144 
 145         ModuleFinder finder
 146             = ModuleUtils.finderOf(descriptor1, descriptor2, descriptor3);
 147 
 148         Configuration cf = resolve(finder, "m1");
 149 
 150         assertTrue(cf.modules().size() == 3);
 151 
 152         assertTrue(cf.findModule("m1").isPresent());
 153         assertTrue(cf.findModule("m2").isPresent());
 154         assertTrue(cf.findModule("m3").isPresent());
 155 
 156         assertTrue(cf.parents().size() == 1);
 157         assertTrue(cf.parents().get(0) == Configuration.empty());
 158 
 159         ResolvedModule m1 = cf.findModule("m1").get();
 160         ResolvedModule m2 = cf.findModule("m2").get();
 161         ResolvedModule m3 = cf.findModule("m3").get();
 162 
 163         // m1 reads m2 and m3
 164         assertTrue(m1.reads().size() == 2);
 165         assertTrue(m1.reads().contains(m2));
 166         assertTrue(m1.reads().contains(m3));
 167 
 168         // m2 reads m3
 169         assertTrue(m2.reads().size() == 1);
 170         assertTrue(m2.reads().contains(m3));
 171 
 172         // m3 reads nothing
 173         assertTrue(m3.reads().size() == 0);
 174     }
 175 
 176 
 177     /**
 178      * Basic test of "requires transitive" with configurations.
 179      *
 180      * The test consists of three configurations:
 181      * - Configuration cf1: m1, m2 requires transitive m1
 182      * - Configuration cf2: m3 requires m2
 183      */
 184     @Test
 185     void testRequiresTransitive2() {
 186 
 187         // cf1: m1 and m2, m2 requires transitive m1
 188 
 189         ModuleDescriptor descriptor1 = newBuilder("m1")
 190                 .build();
 191 
 192         ModuleDescriptor descriptor2 = newBuilder("m2")
 193                 .requires(Set.of(Requires.Modifier.TRANSITIVE), "m1")
 194                 .build();
 195 
 196         ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2);
 197 
 198         Configuration cf1 = resolve(finder1, "m2");
 199 
 200         assertTrue(cf1.modules().size() == 2);
 201         assertTrue(cf1.findModule("m1").isPresent());
 202         assertTrue(cf1.findModule("m2").isPresent());
 203         assertTrue(cf1.parents().size() == 1);
 204         assertTrue(cf1.parents().get(0) == Configuration.empty());
 205 
 206         ResolvedModule m1 = cf1.findModule("m1").get();
 207         ResolvedModule m2 = cf1.findModule("m2").get();
 208 
 209         assertTrue(m1.reads().size() == 0);
 210         assertTrue(m2.reads().size() == 1);
 211         assertTrue(m2.reads().contains(m1));
 212 
 213 
 214         // cf2: m3, m3 requires m2
 215 
 216         ModuleDescriptor descriptor3 = newBuilder("m3")
 217                 .requires("m2")
 218                 .build();
 219 
 220         ModuleFinder finder2 = ModuleUtils.finderOf(descriptor3);
 221 
 222         Configuration cf2 = resolve(cf1, finder2, "m3");
 223 
 224         assertTrue(cf2.modules().size() == 1);
 225         assertTrue(cf2.findModule("m1").isPresent());  // in parent
 226         assertTrue(cf2.findModule("m2").isPresent());  // in parent
 227         assertTrue(cf2.findModule("m3").isPresent());
 228         assertTrue(cf2.parents().size() == 1);
 229         assertTrue(cf2.parents().get(0) == cf1);
 230 
 231         ResolvedModule m3 = cf2.findModule("m3").get();
 232         assertTrue(m3.configuration() == cf2);
 233         assertTrue(m3.reads().size() == 2);
 234         assertTrue(m3.reads().contains(m1));
 235         assertTrue(m3.reads().contains(m2));
 236     }
 237 
 238 
 239     /**
 240      * Basic test of "requires transitive" with configurations.
 241      *
 242      * The test consists of three configurations:
 243      * - Configuration cf1: m1
 244      * - Configuration cf2: m2 requires transitive m1, m3 requires m2
 245      */
 246     @Test
 247     void testRequiresTransitive3() {
 248 
 249         // cf1: m1
 250 
 251         ModuleDescriptor descriptor1 = newBuilder("m1").build();
 252 
 253         ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1);
 254 
 255         Configuration cf1 = resolve(finder1, "m1");
 256 
 257         assertTrue(cf1.modules().size() == 1);
 258         assertTrue(cf1.findModule("m1").isPresent());
 259         assertTrue(cf1.parents().size() == 1);
 260         assertTrue(cf1.parents().get(0) == Configuration.empty());
 261 
 262         ResolvedModule m1 = cf1.findModule("m1").get();
 263         assertTrue(m1.reads().size() == 0);
 264 
 265 
 266         // cf2: m2, m3: m2 requires transitive m1, m3 requires m2
 267 
 268         ModuleDescriptor descriptor2 = newBuilder("m2")
 269                 .requires(Set.of(Requires.Modifier.TRANSITIVE), "m1")
 270                 .build();
 271 
 272         ModuleDescriptor descriptor3 = newBuilder("m3")
 273                 .requires("m2")
 274                 .build();
 275 
 276         ModuleFinder finder2 = ModuleUtils.finderOf(descriptor2, descriptor3);
 277 
 278         Configuration cf2 = resolve(cf1, finder2, "m3");
 279 
 280         assertTrue(cf2.modules().size() == 2);
 281         assertTrue(cf2.findModule("m1").isPresent());   // in parent
 282         assertTrue(cf2.findModule("m2").isPresent());
 283         assertTrue(cf2.findModule("m3").isPresent());
 284         assertTrue(cf2.parents().size() == 1);
 285         assertTrue(cf2.parents().get(0) == cf1);
 286 
 287         ResolvedModule m2 = cf2.findModule("m2").get();
 288         ResolvedModule m3 = cf2.findModule("m3").get();
 289 
 290         assertTrue(m2.configuration() == cf2);
 291         assertTrue(m2.reads().size() == 1);
 292         assertTrue(m2.reads().contains(m1));
 293 
 294         assertTrue(m3.configuration() == cf2);
 295         assertTrue(m3.reads().size() == 2);
 296         assertTrue(m3.reads().contains(m1));
 297         assertTrue(m3.reads().contains(m2));
 298     }
 299 
 300 
 301     /**
 302      * Basic test of "requires transitive" with configurations.
 303      *
 304      * The test consists of three configurations:
 305      * - Configuration cf1: m1
 306      * - Configuration cf2: m2 requires transitive m1
 307      * - Configuraiton cf3: m3 requires m2
 308      */
 309     @Test
 310     void testRequiresTransitive4() {
 311 
 312         // cf1: m1
 313 
 314         ModuleDescriptor descriptor1 = newBuilder("m1").build();
 315 
 316         ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1);
 317 
 318         Configuration cf1 = resolve(finder1, "m1");
 319 
 320         assertTrue(cf1.modules().size() == 1);
 321         assertTrue(cf1.findModule("m1").isPresent());
 322         assertTrue(cf1.parents().size() == 1);
 323         assertTrue(cf1.parents().get(0) == Configuration.empty());
 324 
 325         ResolvedModule m1 = cf1.findModule("m1").get();
 326         assertTrue(m1.reads().size() == 0);
 327 
 328 
 329         // cf2: m2 requires transitive m1
 330 
 331         ModuleDescriptor descriptor2 = newBuilder("m2")
 332                 .requires(Set.of(Requires.Modifier.TRANSITIVE), "m1")
 333                 .build();
 334 
 335         ModuleFinder finder2 = ModuleUtils.finderOf(descriptor2);
 336 
 337         Configuration cf2 = resolve(cf1, finder2, "m2");
 338 
 339         assertTrue(cf2.modules().size() == 1);
 340         assertTrue(cf2.findModule("m1").isPresent());  // in parent
 341         assertTrue(cf2.findModule("m2").isPresent());
 342         assertTrue(cf2.parents().size() == 1);
 343         assertTrue(cf2.parents().get(0) == cf1);
 344 
 345         ResolvedModule m2 = cf2.findModule("m2").get();
 346 
 347         assertTrue(m2.configuration() == cf2);
 348         assertTrue(m2.reads().size() == 1);
 349         assertTrue(m2.reads().contains(m1));
 350 
 351 
 352         // cf3: m3 requires m2
 353 
 354         ModuleDescriptor descriptor3 = newBuilder("m3")
 355                 .requires("m2")
 356                 .build();
 357 
 358         ModuleFinder finder3 = ModuleUtils.finderOf(descriptor3);
 359 
 360         Configuration cf3 = resolve(cf2, finder3, "m3");
 361 
 362         assertTrue(cf3.modules().size() == 1);
 363         assertTrue(cf3.findModule("m1").isPresent());  // in parent
 364         assertTrue(cf3.findModule("m2").isPresent());  // in parent
 365         assertTrue(cf3.findModule("m3").isPresent());
 366         assertTrue(cf3.parents().size() == 1);
 367         assertTrue(cf3.parents().get(0) == cf2);
 368 
 369         ResolvedModule m3 = cf3.findModule("m3").get();
 370 
 371         assertTrue(m3.configuration() == cf3);
 372         assertTrue(m3.reads().size() == 2);
 373         assertTrue(m3.reads().contains(m1));
 374         assertTrue(m3.reads().contains(m2));
 375     }
 376 
 377 
 378     /**
 379      * Basic test of "requires transitive" with configurations.
 380      *
 381      * The test consists of two configurations:
 382      * - Configuration cf1: m1, m2 requires transitive m1
 383      * - Configuration cf2: m3 requires transitive m2, m4 requires m3
 384      */
 385     @Test
 386     void testRequiresTransitive5() {
 387 
 388         // cf1: m1, m2 requires transitive m1
 389 
 390         ModuleDescriptor descriptor1 = newBuilder("m1")
 391                 .build();
 392 
 393         ModuleDescriptor descriptor2 = newBuilder("m2")
 394                 .requires(Set.of(Requires.Modifier.TRANSITIVE), "m1")
 395                 .build();
 396 
 397         ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2);
 398 
 399         Configuration cf1 = resolve(finder1, "m2");
 400 
 401         assertTrue(cf1.modules().size() == 2);
 402         assertTrue(cf1.findModule("m1").isPresent());
 403         assertTrue(cf1.findModule("m2").isPresent());
 404         assertTrue(cf1.parents().size() == 1);
 405         assertTrue(cf1.parents().get(0) == Configuration.empty());
 406 
 407         ResolvedModule m1 = cf1.findModule("m1").get();
 408         ResolvedModule m2 = cf1.findModule("m2").get();
 409 
 410         assertTrue(m1.configuration() == cf1);
 411         assertTrue(m1.reads().size() == 0);
 412 
 413         assertTrue(m2.configuration() == cf1);
 414         assertTrue(m2.reads().size() == 1);
 415         assertTrue(m2.reads().contains(m1));
 416 
 417 
 418         // cf2: m3 requires transitive m2, m4 requires m3
 419 
 420         ModuleDescriptor descriptor3 = newBuilder("m3")
 421                 .requires(Set.of(Requires.Modifier.TRANSITIVE), "m2")
 422                 .build();
 423 
 424         ModuleDescriptor descriptor4 = newBuilder("m4")
 425                 .requires("m3")
 426                 .build();
 427 
 428 
 429         ModuleFinder finder2 = ModuleUtils.finderOf(descriptor3, descriptor4);
 430 
 431         Configuration cf2 = resolve(cf1, finder2, "m3", "m4");
 432 
 433         assertTrue(cf2.modules().size() == 2);
 434         assertTrue(cf2.findModule("m1").isPresent());   // in parent
 435         assertTrue(cf2.findModule("m2").isPresent());   // in parent
 436         assertTrue(cf2.findModule("m3").isPresent());
 437         assertTrue(cf2.findModule("m4").isPresent());
 438         assertTrue(cf2.parents().size() == 1);
 439         assertTrue(cf2.parents().get(0) == cf1);
 440 
 441         ResolvedModule m3 = cf2.findModule("m3").get();
 442         ResolvedModule m4 = cf2.findModule("m4").get();
 443 
 444         assertTrue(m3.configuration() == cf2);
 445         assertTrue(m3.reads().size() == 2);
 446         assertTrue(m3.reads().contains(m1));
 447         assertTrue(m3.reads().contains(m2));
 448 
 449         assertTrue(m4.configuration() == cf2);
 450         assertTrue(m4.reads().size() == 3);
 451         assertTrue(m4.reads().contains(m1));
 452         assertTrue(m4.reads().contains(m2));
 453         assertTrue(m4.reads().contains(m3));
 454     }
 455 
 456 
 457     /**
 458      * Basic test of "requires static":
 459      *     m1 requires static m2
 460      *     m2 is not observable
 461      *     resolve m1
 462      */
 463     @Test
 464     void testRequiresStatic1() {
 465         ModuleDescriptor descriptor1 = newBuilder("m1")
 466                 .requires(Set.of(Requires.Modifier.STATIC), "m2")
 467                 .build();
 468 
 469         ModuleFinder finder = ModuleUtils.finderOf(descriptor1);
 470 
 471         Configuration cf = resolve(finder, "m1");
 472 
 473         assertTrue(cf.modules().size() == 1);
 474 
 475         ResolvedModule m1 = cf.findModule("m1").get();
 476         assertTrue(m1.reads().size() == 0);
 477     }
 478 
 479 
 480     /**
 481      * Basic test of "requires static":
 482      *     m1 requires static m2
 483      *     m2
 484      *     resolve m1
 485      */
 486     @Test
 487     void testRequiresStatic2() {
 488         ModuleDescriptor descriptor1 = newBuilder("m1")
 489                 .requires(Set.of(Requires.Modifier.STATIC), "m2")
 490                 .build();
 491 
 492         ModuleDescriptor descriptor2 = newBuilder("m2")
 493                 .build();
 494 
 495         ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2);
 496 
 497         Configuration cf = resolve(finder, "m1");
 498 
 499         assertTrue(cf.modules().size() == 1);
 500 
 501         ResolvedModule m1 = cf.findModule("m1").get();
 502         assertTrue(m1.reads().size() == 0);
 503     }
 504 
 505 
 506     /**
 507      * Basic test of "requires static":
 508      *     m1 requires static m2
 509      *     m2
 510      *     resolve m1, m2
 511      */
 512     @Test
 513     void testRequiresStatic3() {
 514         ModuleDescriptor descriptor1 = newBuilder("m1")
 515                 .requires(Set.of(Requires.Modifier.STATIC), "m2")
 516                 .build();
 517 
 518         ModuleDescriptor descriptor2 = newBuilder("m2")
 519                 .build();
 520 
 521         ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2);
 522 
 523         Configuration cf = resolve(finder, "m1", "m2");
 524 
 525         assertTrue(cf.modules().size() == 2);
 526 
 527         ResolvedModule m1 = cf.findModule("m1").get();
 528         ResolvedModule m2 = cf.findModule("m2").get();
 529 
 530         assertTrue(m1.reads().size() == 1);
 531         assertTrue(m1.reads().contains(m2));
 532 
 533         assertTrue(m2.reads().size() == 0);
 534     }
 535 
 536 
 537     /**
 538      * Basic test of "requires static":
 539      *     m1 requires m2, m3
 540      *     m2 requires static m2
 541      *     m3
 542      */
 543     @Test
 544     void testRequiresStatic4() {
 545         ModuleDescriptor descriptor1 = newBuilder("m1")
 546                 .requires("m2")
 547                 .requires("m3")
 548                 .build();
 549 
 550         ModuleDescriptor descriptor2 = newBuilder("m2")
 551                 .requires(Set.of(Requires.Modifier.STATIC), "m3")
 552                 .build();
 553 
 554         ModuleDescriptor descriptor3 = newBuilder("m3")
 555                 .build();
 556 
 557         ModuleFinder finder
 558             = ModuleUtils.finderOf(descriptor1, descriptor2, descriptor3);
 559 
 560         Configuration cf = resolve(finder, "m1");
 561 
 562         assertTrue(cf.modules().size() == 3);
 563 
 564         ResolvedModule m1 = cf.findModule("m1").get();
 565         ResolvedModule m2 = cf.findModule("m2").get();
 566         ResolvedModule m3 = cf.findModule("m3").get();
 567 
 568         assertTrue(m1.reads().size() == 2);
 569         assertTrue(m1.reads().contains(m2));
 570         assertTrue(m1.reads().contains(m3));
 571 
 572         assertTrue(m2.reads().size() == 1);
 573         assertTrue(m2.reads().contains(m3));
 574 
 575         assertTrue(m3.reads().size() == 0);
 576     }
 577 
 578 
 579     /**
 580      * Basic test of "requires static":
 581      * The test consists of three configurations:
 582      * - Configuration cf1: m1, m2
 583      * - Configuration cf2: m3 requires m1, requires static m2
 584      */
 585     @Test
 586     void testRequiresStatic5() {
 587         ModuleDescriptor descriptor1 = newBuilder("m1")
 588                 .build();
 589 
 590         ModuleDescriptor descriptor2 = newBuilder("m2")
 591                 .build();
 592 
 593         ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2);
 594 
 595         Configuration cf1 = resolve(finder1, "m1", "m2");
 596 
 597         assertTrue(cf1.modules().size() == 2);
 598         assertTrue(cf1.findModule("m1").isPresent());
 599         assertTrue(cf1.findModule("m2").isPresent());
 600 
 601         ModuleDescriptor descriptor3 = newBuilder("m3")
 602                 .requires("m1")
 603                 .requires(Set.of(Requires.Modifier.STATIC), "m2")
 604                 .build();
 605 
 606         ModuleFinder finder2 = ModuleUtils.finderOf(descriptor3);
 607 
 608         Configuration cf2 = resolve(cf1, finder2, "m3");
 609 
 610         assertTrue(cf2.modules().size() == 1);
 611         assertTrue(cf2.findModule("m3").isPresent());
 612 
 613         ResolvedModule m1 = cf1.findModule("m1").get();
 614         ResolvedModule m2 = cf1.findModule("m2").get();
 615         ResolvedModule m3 = cf2.findModule("m3").get();
 616 
 617         assertTrue(m3.reads().size() == 2);
 618         assertTrue(m3.reads().contains(m1));
 619         assertTrue(m3.reads().contains(m2));
 620     }
 621 
 622 
 623     /**
 624      * Basic test of "requires static":
 625      * The test consists of three configurations:
 626      * - Configuration cf1: m1
 627      * - Configuration cf2: m3 requires m1, requires static m2
 628      */
 629     @Test
 630     void testRequiresStatic6() {
 631         ModuleDescriptor descriptor1 = newBuilder("m1")
 632                 .build();
 633 
 634         ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1);
 635 
 636         Configuration cf1 = resolve(finder1, "m1");
 637 
 638         assertTrue(cf1.modules().size() == 1);
 639         assertTrue(cf1.findModule("m1").isPresent());
 640 
 641         ModuleDescriptor descriptor3 = newBuilder("m3")
 642                 .requires("m1")
 643                 .requires(Set.of(Requires.Modifier.STATIC), "m2")
 644                 .build();
 645 
 646         ModuleFinder finder2 = ModuleUtils.finderOf(descriptor3);
 647 
 648         Configuration cf2 = resolve(cf1, finder2, "m3");
 649 
 650         assertTrue(cf2.modules().size() == 1);
 651         assertTrue(cf2.findModule("m3").isPresent());
 652 
 653         ResolvedModule m1 = cf1.findModule("m1").get();
 654         ResolvedModule m3 = cf2.findModule("m3").get();
 655 
 656         assertTrue(m3.reads().size() == 1);
 657         assertTrue(m3.reads().contains(m1));
 658     }
 659 
 660 
 661     /**
 662      * Basic test of "requires static":
 663      *     (m1 not observable)
 664      *     m2 requires transitive static m1
 665      *     m3 requires m2
 666      */
 667     @Test
 668     void testRequiresStatic7() {
 669         ModuleDescriptor descriptor1 = null;  // not observable
 670 
 671         ModuleDescriptor descriptor2 = newBuilder("m2")
 672                 .requires(Set.of(Requires.Modifier.TRANSITIVE,
 673                                 Requires.Modifier.STATIC),
 674                          "m1")
 675                 .build();
 676 
 677         ModuleDescriptor descriptor3 = newBuilder("m3")
 678                 .requires("m2")
 679                 .build();
 680 
 681         ModuleFinder finder = ModuleUtils.finderOf(descriptor2, descriptor3);
 682 
 683         Configuration cf = resolve(finder, "m3");
 684 
 685         assertTrue(cf.modules().size() == 2);
 686         assertTrue(cf.findModule("m2").isPresent());
 687         assertTrue(cf.findModule("m3").isPresent());
 688         ResolvedModule m2 = cf.findModule("m2").get();
 689         ResolvedModule m3 = cf.findModule("m3").get();
 690         assertTrue(m2.reads().isEmpty());
 691         assertTrue(m3.reads().size() == 1);
 692         assertTrue(m3.reads().contains(m2));
 693     }
 694 
 695 
 696     /**
 697      * Basic test of "requires static":
 698      * - Configuration cf1: m2 requires transitive static m1
 699      * - Configuration cf2: m3 requires m2
 700      */
 701     @Test
 702     void testRequiresStatic8() {
 703         ModuleDescriptor descriptor1 = null;  // not observable
 704 
 705         ModuleDescriptor descriptor2 = newBuilder("m2")
 706                 .requires(Set.of(Requires.Modifier.TRANSITIVE,
 707                                 Requires.Modifier.STATIC),
 708                         "m1")
 709                 .build();
 710 
 711         ModuleFinder finder1 = ModuleUtils.finderOf(descriptor2);
 712 
 713         Configuration cf1 = resolve(finder1, "m2");
 714 
 715         assertTrue(cf1.modules().size() == 1);
 716         assertTrue(cf1.findModule("m2").isPresent());
 717         ResolvedModule m2 = cf1.findModule("m2").get();
 718         assertTrue(m2.reads().isEmpty());
 719 
 720         ModuleDescriptor descriptor3 = newBuilder("m3")
 721                 .requires("m2")
 722                 .build();
 723 
 724         ModuleFinder finder2 = ModuleUtils.finderOf(descriptor3);
 725 
 726         Configuration cf2 = resolve(cf1, finder2, "m3");
 727 
 728         assertTrue(cf2.modules().size() == 1);
 729         assertTrue(cf2.findModule("m3").isPresent());
 730         ResolvedModule m3 = cf2.findModule("m3").get();
 731         assertTrue(m3.reads().size() == 1);
 732         assertTrue(m3.reads().contains(m2));
 733     }
 734 
 735 
 736     /**
 737      * Basic test of binding services
 738      *     m1 uses p.S
 739      *     m2 provides p.S
 740      */
 741     @Test
 742     void testServiceBinding1() {
 743         ModuleDescriptor descriptor1 = newBuilder("m1")
 744                 .exports("p")
 745                 .uses("p.S")
 746                 .build();
 747 
 748         ModuleDescriptor descriptor2 = newBuilder("m2")
 749                 .requires("m1")
 750                 .provides("p.S", List.of("q.T"))
 751                 .build();
 752 
 753         ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2);
 754 
 755         Configuration cf = resolveAndBind(finder, "m1");
 756 
 757         assertTrue(cf.modules().size() == 2);
 758         assertTrue(cf.findModule("m1").isPresent());
 759         assertTrue(cf.findModule("m2").isPresent());
 760         assertTrue(cf.parents().size() == 1);
 761         assertTrue(cf.parents().get(0) == Configuration.empty());
 762 
 763         ResolvedModule m1 = cf.findModule("m1").get();
 764         ResolvedModule m2 = cf.findModule("m2").get();
 765 
 766         assertTrue(m1.configuration() == cf);
 767         assertTrue(m1.reads().size() == 0);
 768 
 769         assertTrue(m2.configuration() == cf);
 770         assertTrue(m2.reads().size() == 1);
 771         assertTrue(m2.reads().contains(m1));
 772     }
 773 
 774 
 775     /**
 776      * Basic test of binding services
 777      *     m1 uses p.S1
 778      *     m2 provides p.S1, m2 uses p.S2
 779      *     m3 provides p.S2
 780      */
 781     @Test
 782     void testServiceBinding2() {
 783         ModuleDescriptor descriptor1 = newBuilder("m1")
 784                 .exports("p")
 785                 .uses("p.S1")
 786                 .build();
 787 
 788         ModuleDescriptor descriptor2 = newBuilder("m2")
 789                 .requires("m1")
 790                 .uses("p.S2")
 791                 .provides("p.S1", List.of("q.Service1Impl"))
 792                 .build();
 793 
 794         ModuleDescriptor descriptor3 = newBuilder("m3")
 795                 .requires("m1")
 796                 .provides("p.S2", List.of("q.Service2Impl"))
 797                 .build();
 798 
 799         ModuleFinder finder
 800             = ModuleUtils.finderOf(descriptor1, descriptor2, descriptor3);
 801 
 802         Configuration cf = resolveAndBind(finder, "m1");
 803 
 804         assertTrue(cf.modules().size() == 3);
 805         assertTrue(cf.findModule("m1").isPresent());
 806         assertTrue(cf.findModule("m2").isPresent());
 807         assertTrue(cf.findModule("m3").isPresent());
 808         assertTrue(cf.parents().size() == 1);
 809         assertTrue(cf.parents().get(0) == Configuration.empty());
 810 
 811         ResolvedModule m1 = cf.findModule("m1").get();
 812         ResolvedModule m2 = cf.findModule("m2").get();
 813         ResolvedModule m3 = cf.findModule("m3").get();
 814 
 815         assertTrue(m1.configuration() == cf);
 816         assertTrue(m1.reads().size() == 0);
 817 
 818         assertTrue(m2.configuration() == cf);
 819         assertTrue(m2.reads().size() == 1);
 820         assertTrue(m2.reads().contains(m1));
 821 
 822         assertTrue(m3.configuration() == cf);
 823         assertTrue(m3.reads().size() == 1);
 824         assertTrue(m3.reads().contains(m1));
 825     }
 826 
 827 
 828     /**
 829      * Basic test of binding services with configurations.
 830      *
 831      * The test consists of two configurations:
 832      * - Configuration cf1: m1 uses p.S
 833      * - Configuration cf2: m2 provides p.S
 834      */
 835     @Test
 836     void testServiceBindingWithConfigurations1() {
 837         ModuleDescriptor descriptor1 = newBuilder("m1")
 838                 .exports("p")
 839                 .uses("p.S")
 840                 .build();
 841 
 842         ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1);
 843 
 844         Configuration cf1 = resolve(finder1, "m1");
 845 
 846         assertTrue(cf1.modules().size() == 1);
 847         assertTrue(cf1.findModule("m1").isPresent());
 848 
 849         ModuleDescriptor descriptor2 = newBuilder("m2")
 850                 .requires("m1")
 851                 .provides("p.S", List.of("q.T"))
 852                 .build();
 853 
 854         ModuleFinder finder2 = ModuleUtils.finderOf(descriptor2);
 855 
 856         Configuration cf2 = resolveAndBind(cf1, finder2); // no roots
 857 
 858         assertTrue(cf2.parents().size() == 1);
 859         assertTrue(cf2.parents().get(0) == cf1);
 860 
 861         assertTrue(cf2.modules().size() == 1);
 862         assertTrue(cf2.findModule("m2").isPresent());
 863 
 864         ResolvedModule m1 = cf1.findModule("m1").get();
 865         ResolvedModule m2 = cf2.findModule("m2").get();
 866 
 867         assertTrue(m2.reads().size() == 1);
 868         assertTrue(m2.reads().contains(m1));
 869     }
 870 
 871 
 872     /**
 873      * Basic test of binding services with configurations.
 874      *
 875      * The test consists of two configurations:
 876      * - Configuration cf1: m1 uses p.S && provides p.S,
 877      *                      m2 provides p.S
 878      * - Configuration cf2: m3 provides p.S
 879      *                      m4 provides p.S
 880      */
 881     @Test
 882     void testServiceBindingWithConfigurations2() {
 883         ModuleDescriptor descriptor1 = newBuilder("m1")
 884                 .exports("p")
 885                 .uses("p.S")
 886                 .provides("p.S", List.of("p1.ServiceImpl"))
 887                 .build();
 888 
 889         ModuleDescriptor descriptor2 = newBuilder("m2")
 890                 .requires("m1")
 891                 .provides("p.S", List.of("p2.ServiceImpl"))
 892                 .build();
 893 
 894         ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2);
 895 
 896         Configuration cf1 = resolveAndBind(finder1, "m1");
 897 
 898         assertTrue(cf1.modules().size() == 2);
 899         assertTrue(cf1.findModule("m1").isPresent());
 900         assertTrue(cf1.findModule("m2").isPresent());
 901 
 902 
 903         ModuleDescriptor descriptor3 = newBuilder("m3")
 904                 .requires("m1")
 905                 .provides("p.S", List.of("p3.ServiceImpl"))
 906                 .build();
 907 
 908         ModuleDescriptor descriptor4 = newBuilder("m4")
 909                 .requires("m1")
 910                 .provides("p.S", List.of("p4.ServiceImpl"))
 911                 .build();
 912 
 913         ModuleFinder finder2 = ModuleUtils.finderOf(descriptor3, descriptor4);
 914 
 915         Configuration cf2 = resolveAndBind(cf1, finder2); // no roots
 916 
 917         assertTrue(cf2.parents().size() == 1);
 918         assertTrue(cf2.parents().get(0) == cf1);
 919 
 920         assertTrue(cf2.modules().size() == 2);
 921         assertTrue(cf2.findModule("m3").isPresent());
 922         assertTrue(cf2.findModule("m4").isPresent());
 923 
 924         ResolvedModule m1 = cf2.findModule("m1").get();  // should find in parent
 925         ResolvedModule m2 = cf2.findModule("m2").get();
 926         ResolvedModule m3 = cf2.findModule("m3").get();
 927         ResolvedModule m4 = cf2.findModule("m4").get();
 928 
 929         assertTrue(m1.reads().size() == 0);
 930 
 931         assertTrue(m2.reads().size() == 1);
 932         assertTrue(m2.reads().contains(m1));
 933 
 934         assertTrue(m3.reads().size() == 1);
 935         assertTrue(m3.reads().contains(m1));
 936 
 937         assertTrue(m4.reads().size() == 1);
 938         assertTrue(m4.reads().contains(m1));
 939     }
 940 
 941 
 942     /**
 943      * Basic test of binding services with configurations.
 944      *
 945      * Configuration cf1: p@1.0 provides p.S
 946      * Test configuration cf2: m1 uses p.S, p@2.0 provides p.S
 947      * Test configuration cf2: m1 uses p.S
 948      */
 949     @Test
 950     void testServiceBindingWithConfigurations3() {
 951         ModuleDescriptor service = newBuilder("s")
 952                 .exports("p")
 953                 .build();
 954 
 955         ModuleDescriptor provider_v1 = newBuilder("p")
 956                 .version("1.0")
 957                 .requires("s")
 958                 .provides("p.S", List.of("q.T"))
 959                 .build();
 960 
 961         ModuleFinder finder1 = ModuleUtils.finderOf(service, provider_v1);
 962 
 963         Configuration cf1 = resolve(finder1, "p");
 964 
 965         assertTrue(cf1.modules().size() == 2);
 966         assertTrue(cf1.findModule("s").isPresent());
 967         assertTrue(cf1.findModule("p").isPresent());
 968 
 969         // p@1.0 in cf1
 970         ResolvedModule p = cf1.findModule("p").get();
 971         assertEquals(provider_v1, p.reference().descriptor());
 972 
 973 
 974         ModuleDescriptor descriptor1 = newBuilder("m1")
 975                 .requires("s")
 976                 .uses("p.S")
 977                 .build();
 978 
 979         ModuleDescriptor provider_v2 = newBuilder("p")
 980                 .version("2.0")
 981                 .requires("s")
 982                 .provides("p.S", List.of("q.T"))
 983                 .build();
 984 
 985         ModuleFinder finder2 = ModuleUtils.finderOf(descriptor1, provider_v2);
 986 
 987 
 988         // finder2 is the before ModuleFinder and so p@2.0 should be located
 989 
 990         Configuration cf2 = resolveAndBind(cf1, finder2, "m1");
 991 
 992         assertTrue(cf2.parents().size() == 1);
 993         assertTrue(cf2.parents().get(0) == cf1);
 994         assertTrue(cf2.modules().size() == 2);
 995 
 996         // p should be found in cf2
 997         p = cf2.findModule("p").get();
 998         assertTrue(p.configuration() == cf2);
 999         assertEquals(provider_v2, p.reference().descriptor());
1000 
1001 
1002         // finder2 is the after ModuleFinder and so p@2.0 should not be located
1003         // as module p is in parent configuration.
1004 
1005         cf2 = resolveAndBind(cf1, ModuleFinder.of(), finder2, "m1");
1006 
1007         assertTrue(cf2.parents().size() == 1);
1008         assertTrue(cf2.parents().get(0) == cf1);
1009         assertTrue(cf2.modules().size() == 1);
1010 
1011         // p should be found in cf1
1012         p = cf2.findModule("p").get();
1013         assertTrue(p.configuration() == cf1);
1014         assertEquals(provider_v1, p.reference().descriptor());
1015     }
1016 
1017 
1018     /**
1019      * Basic test with two module finders.
1020      *
1021      * Module m2 can be found by both the before and after finders.
1022      */
1023     @Test
1024     void testWithTwoFinders1() {
1025         ModuleDescriptor descriptor1 = newBuilder("m1")
1026                 .requires("m2")
1027                 .build();
1028 
1029         ModuleDescriptor descriptor2_v1 = newBuilder("m2")
1030                 .version("1.0")
1031                 .build();
1032 
1033         ModuleDescriptor descriptor2_v2 = newBuilder("m2")
1034                 .version("2.0")
1035                 .build();
1036 
1037         ModuleFinder finder1 = ModuleUtils.finderOf(descriptor2_v1);
1038         ModuleFinder finder2 = ModuleUtils.finderOf(descriptor1, descriptor2_v2);
1039 
1040         Configuration cf = resolve(finder1, finder2, "m1");
1041 
1042         assertTrue(cf.modules().size() == 2);
1043         assertTrue(cf.findModule("m1").isPresent());
1044         assertTrue(cf.findModule("m2").isPresent());
1045 
1046         ResolvedModule m1 = cf.findModule("m1").get();
1047         ResolvedModule m2 = cf.findModule("m2").get();
1048 
1049         assertEquals(descriptor1, m1.reference().descriptor());
1050         assertEquals(descriptor2_v1, m2.reference().descriptor());
1051     }
1052 
1053 
1054     /**
1055      * Basic test with two modules finders and service binding.
1056      *
1057      * The before and after ModuleFinders both locate a service provider module
1058      * named "m2" that provide implementations of the same service type.
1059      */
1060     @Test
1061     void testWithTwoFinders2() {
1062         ModuleDescriptor descriptor1 = newBuilder("m1")
1063                 .exports("p")
1064                 .uses("p.S")
1065                 .build();
1066 
1067         ModuleDescriptor descriptor2_v1 = newBuilder("m2")
1068                 .requires("m1")
1069                 .provides("p.S", List.of("q.T"))
1070                 .build();
1071 
1072         ModuleDescriptor descriptor2_v2 = newBuilder("m2")
1073                 .requires("m1")
1074                 .provides("p.S", List.of("q.T"))
1075                 .build();
1076 
1077         ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2_v1);
1078         ModuleFinder finder2 = ModuleUtils.finderOf(descriptor2_v2);
1079 
1080         Configuration cf = resolveAndBind(finder1, finder2, "m1");
1081 
1082         assertTrue(cf.modules().size() == 2);
1083         assertTrue(cf.findModule("m1").isPresent());
1084         assertTrue(cf.findModule("m2").isPresent());
1085 
1086         ResolvedModule m1 = cf.findModule("m1").get();
1087         ResolvedModule m2 = cf.findModule("m2").get();
1088 
1089         assertEquals(descriptor1, m1.reference().descriptor());
1090         assertEquals(descriptor2_v1, m2.reference().descriptor());
1091     }
1092 
1093 
1094     /**
1095      * Basic test for resolving a module that is located in the parent
1096      * configuration.
1097      */
1098     @Test
1099     void testResolvedInParent1() {
1100         ModuleDescriptor descriptor1 = newBuilder("m1")
1101                 .build();
1102 
1103         ModuleFinder finder = ModuleUtils.finderOf(descriptor1);
1104 
1105         Configuration cf1 = resolve(finder, "m1");
1106 
1107         assertTrue(cf1.modules().size() == 1);
1108         assertTrue(cf1.findModule("m1").isPresent());
1109 
1110         Configuration cf2 = resolve(cf1, finder, "m1");
1111 
1112         assertTrue(cf2.modules().size() == 1);
1113     }
1114 
1115 
1116     /**
1117      * Basic test for resolving a module that has a dependency on a module
1118      * in the parent configuration.
1119      */
1120     @Test
1121     void testResolvedInParent2() {
1122         ModuleDescriptor descriptor1 = newBuilder("m1").build();
1123 
1124         ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1);
1125 
1126         Configuration cf1 = resolve(finder1, "m1");
1127 
1128         assertTrue(cf1.modules().size() == 1);
1129         assertTrue(cf1.findModule("m1").isPresent());
1130 
1131 
1132         ModuleDescriptor descriptor2 = newBuilder("m2")
1133                 .requires("m1")
1134                 .build();
1135 
1136         ModuleFinder finder2 = ModuleUtils.finderOf(descriptor2);
1137 
1138         Configuration cf2 = resolve(cf1, ModuleFinder.of(), finder2, "m2");
1139 
1140         assertTrue(cf2.modules().size() == 1);
1141         assertTrue(cf2.findModule("m2").isPresent());
1142 
1143         ResolvedModule m1 = cf2.findModule("m1").get();   // find in parent
1144         ResolvedModule m2 = cf2.findModule("m2").get();
1145 
1146         assertTrue(m1.reads().size() == 0);
1147         assertTrue(m2.reads().size() == 1);
1148         assertTrue(m2.reads().contains(m1));
1149     }
1150 
1151 
1152     /**
1153      * Basic test of resolving a module that depends on modules in two parent
1154      * configurations.
1155      *
1156      * The test consists of three configurations:
1157      * - Configuration cf1: m1
1158      * - Configuration cf2: m2
1159      * - Configuration cf3(cf1,cf2): m3 requires m1, m2
1160      */
1161     @Test
1162     void testResolvedInMultipleParents1() {
1163         // Configuration cf1: m1
1164         ModuleDescriptor descriptor1 = newBuilder("m1").build();
1165         Configuration cf1 = resolve(ModuleUtils.finderOf(descriptor1), "m1");
1166         assertEquals(List.of(Configuration.empty()), cf1.parents());
1167         assertTrue(cf1.findModule("m1").isPresent());
1168         ResolvedModule m1 = cf1.findModule("m1").get();
1169         assertTrue(m1.configuration() == cf1);
1170 
1171         // Configuration cf2: m2
1172         ModuleDescriptor descriptor2 = newBuilder("m2").build();
1173         Configuration cf2 = resolve(ModuleUtils.finderOf(descriptor2), "m2");
1174         assertEquals(List.of(Configuration.empty()), cf2.parents());
1175         assertTrue(cf2.findModule("m2").isPresent());
1176         ResolvedModule m2 = cf2.findModule("m2").get();
1177         assertTrue(m2.configuration() == cf2);
1178 
1179         // Configuration cf3(cf1,cf2): m3 requires m1 and m2
1180         ModuleDescriptor descriptor3 = newBuilder("m3")
1181                 .requires("m1")
1182                 .requires("m2")
1183                 .build();
1184         ModuleFinder finder = ModuleUtils.finderOf(descriptor3);
1185         Configuration cf3 = Configuration.resolve(
1186                 finder,
1187                 List.of(cf1, cf2),  // parents
1188                 ModuleFinder.of(),
1189                 Set.of("m3"));
1190         assertEquals(List.of(cf1, cf2), cf3.parents());
1191         assertTrue(cf3.findModule("m3").isPresent());
1192         ResolvedModule m3 = cf3.findModule("m3").get();
1193         assertTrue(m3.configuration() == cf3);
1194 
1195         // check readability
1196         assertTrue(m1.reads().isEmpty());
1197         assertTrue(m2.reads().isEmpty());
1198         assertEquals(Set.of(m1, m2), m3.reads());
1199     }
1200 
1201 
1202     /**
1203      * Basic test of resolving a module that depends on modules in three parent
1204      * configurations arranged in a diamond (two direct parents).
1205      *
1206      * The test consists of four configurations:
1207      * - Configuration cf1: m1
1208      * - Configuration cf2(cf1): m2 requires m1
1209      * - Configuration cf3(cf3): m3 requires m1
1210      * - Configuration cf4(cf2,cf3): m4 requires m1,m2,m3
1211      */
1212     @Test
1213     void testResolvedInMultipleParents2() {
1214         // Configuration cf1: m1
1215         ModuleDescriptor descriptor1 = newBuilder("m1").build();
1216         Configuration cf1 = resolve(ModuleUtils.finderOf(descriptor1), "m1");
1217         assertEquals( List.of(Configuration.empty()), cf1.parents());
1218         assertTrue(cf1.findModule("m1").isPresent());
1219         ResolvedModule m1 = cf1.findModule("m1").get();
1220         assertTrue(m1.configuration() == cf1);
1221 
1222         // Configuration cf2(cf1): m2 requires m1
1223         ModuleDescriptor descriptor2 = newBuilder("m2")
1224                 .requires("m1")
1225                 .build();
1226         Configuration cf2 = Configuration.resolve(
1227                 ModuleUtils.finderOf(descriptor2),
1228                 List.of(cf1),  // parents
1229                 ModuleFinder.of(),
1230                 Set.of("m2"));
1231         assertEquals(List.of(cf1), cf2.parents());
1232         assertTrue(cf2.findModule("m2").isPresent());
1233         ResolvedModule m2 = cf2.findModule("m2").get();
1234         assertTrue(m2.configuration() == cf2);
1235 
1236         // Configuration cf3(cf1): m3 requires m1
1237         ModuleDescriptor descriptor3 = newBuilder("m3")
1238                 .requires("m1")
1239                 .build();
1240         Configuration cf3 = Configuration.resolve(
1241                 ModuleUtils.finderOf(descriptor3),
1242                 List.of(cf1),  // parents
1243                 ModuleFinder.of(),
1244                 Set.of("m3"));
1245         assertEquals(List.of(cf1), cf3.parents());
1246         assertTrue(cf3.findModule("m3").isPresent());
1247         ResolvedModule m3 = cf3.findModule("m3").get();
1248         assertTrue(m3.configuration() == cf3);
1249 
1250         // Configuration cf4(cf2,cf3): m4 requires m1,m2,m3
1251         ModuleDescriptor descriptor4 = newBuilder("m4")
1252                 .requires("m1")
1253                 .requires("m2")
1254                 .requires("m3")
1255                 .build();
1256         Configuration cf4 = Configuration.resolve(
1257                 ModuleUtils.finderOf(descriptor4),
1258                 List.of(cf2, cf3),  // parents
1259                 ModuleFinder.of(),
1260                 Set.of("m4"));
1261         assertEquals(List.of(cf2, cf3), cf4.parents());
1262         assertTrue(cf4.findModule("m4").isPresent());
1263         ResolvedModule m4 = cf4.findModule("m4").get();
1264         assertTrue(m4.configuration() == cf4);
1265 
1266         // check readability
1267         assertTrue(m1.reads().isEmpty());
1268         assertEquals(Set.of(m1), m2.reads());
1269         assertEquals(Set.of(m1), m3.reads());
1270         assertEquals(Set.of(m1, m2, m3), m4.reads());
1271     }
1272 
1273 
1274     /**
1275      * Basic test of resolving a module that depends on modules in three parent
1276      * configurations arranged in a diamond (two direct parents).
1277      *
1278      * The test consists of four configurations:
1279      * - Configuration cf1: m1@1
1280      * - Configuration cf2: m1@2, m2@2
1281      * - Configuration cf3: m1@3, m2@3, m3@3
1282      * - Configuration cf4(cf1,cf2,cf3): m4 requires m1,m2,m3
1283      */
1284     @Test
1285     void testResolvedInMultipleParents3() {
1286         ModuleDescriptor descriptor1, descriptor2, descriptor3;
1287 
1288         // Configuration cf1: m1@1
1289         descriptor1 = newBuilder("m1").version("1").build();
1290         Configuration cf1 = resolve(ModuleUtils.finderOf(descriptor1), "m1");
1291         assertEquals(List.of(Configuration.empty()), cf1.parents());
1292 
1293         // Configuration cf2: m1@2, m2@2
1294         descriptor1 = newBuilder("m1").version("2").build();
1295         descriptor2 = newBuilder("m2").version("2").build();
1296         Configuration cf2 = resolve(
1297                 ModuleUtils.finderOf(descriptor1, descriptor2),
1298                 "m1", "m2");
1299         assertEquals(List.of(Configuration.empty()), cf2.parents());
1300 
1301         // Configuration cf3: m1@3, m2@3, m3@3
1302         descriptor1 = newBuilder("m1").version("3").build();
1303         descriptor2 = newBuilder("m2").version("3").build();
1304         descriptor3 = newBuilder("m3").version("3").build();
1305         Configuration cf3 = resolve(
1306                 ModuleUtils.finderOf(descriptor1, descriptor2, descriptor3),
1307                 "m1", "m2", "m3");
1308         assertEquals(List.of(Configuration.empty()), cf3.parents());
1309 
1310         // Configuration cf4(cf1,cf2,cf3): m4 requires m1,m2,m3
1311         ModuleDescriptor descriptor4 = newBuilder("m4")
1312                 .requires("m1")
1313                 .requires("m2")
1314                 .requires("m3")
1315                 .build();
1316         Configuration cf4 = Configuration.resolve(
1317                 ModuleUtils.finderOf(descriptor4),
1318                 List.of(cf1, cf2, cf3),  // parents
1319                 ModuleFinder.of(),
1320                 Set.of("m4"));
1321         assertEquals(List.of(cf1, cf2, cf3), cf4.parents());
1322 
1323         assertTrue(cf1.findModule("m1").isPresent());
1324         assertTrue(cf2.findModule("m1").isPresent());
1325         assertTrue(cf2.findModule("m2").isPresent());
1326         assertTrue(cf3.findModule("m1").isPresent());
1327         assertTrue(cf3.findModule("m2").isPresent());
1328         assertTrue(cf3.findModule("m3").isPresent());
1329         assertTrue(cf4.findModule("m4").isPresent());
1330 
1331         ResolvedModule m1_1 = cf1.findModule("m1").get();
1332         ResolvedModule m1_2 = cf2.findModule("m1").get();
1333         ResolvedModule m2_2 = cf2.findModule("m2").get();
1334         ResolvedModule m1_3 = cf3.findModule("m1").get();
1335         ResolvedModule m2_3 = cf3.findModule("m2").get();
1336         ResolvedModule m3_3 = cf3.findModule("m3").get();
1337         ResolvedModule m4   = cf4.findModule("m4").get();
1338 
1339         assertTrue(m1_1.configuration() == cf1);
1340         assertTrue(m1_2.configuration() == cf2);
1341         assertTrue(m2_2.configuration() == cf2);
1342         assertTrue(m1_3.configuration() == cf3);
1343         assertTrue(m2_3.configuration() == cf3);
1344         assertTrue(m3_3.configuration() == cf3);
1345         assertTrue(m4.configuration() == cf4);
1346 
1347         // check readability
1348         assertTrue(m1_1.reads().isEmpty());
1349         assertTrue(m1_2.reads().isEmpty());
1350         assertTrue(m2_2.reads().isEmpty());
1351         assertTrue(m1_3.reads().isEmpty());
1352         assertTrue(m2_3.reads().isEmpty());
1353         assertTrue(m3_3.reads().isEmpty());
1354         assertEquals(Set.of(m1_1, m2_2, m3_3), m4.reads());
1355     }
1356 
1357 
1358     /**
1359      * Basic test of using the beforeFinder to override a module in a parent
1360      * configuration.
1361      */
1362     @Test
1363     void testOverriding1() {
1364         ModuleDescriptor descriptor1 = newBuilder("m1").build();
1365 
1366         ModuleFinder finder = ModuleUtils.finderOf(descriptor1);
1367 
1368         Configuration cf1 = resolve(finder, "m1");
1369         assertTrue(cf1.modules().size() == 1);
1370         assertTrue(cf1.findModule("m1").isPresent());
1371 
1372         Configuration cf2 = resolve(cf1, finder, "m1");
1373         assertTrue(cf2.modules().size() == 1);
1374         assertTrue(cf2.findModule("m1").isPresent());
1375     }
1376 
1377     /**
1378      * Basic test of using the beforeFinder to override a module in a parent
1379      * configuration.
1380      */
1381     @Test
1382     void testOverriding2() {
1383         ModuleDescriptor descriptor1 = newBuilder("m1").build();
1384         Configuration cf1 = resolve(ModuleUtils.finderOf(descriptor1), "m1");
1385         assertTrue(cf1.modules().size() == 1);
1386         assertTrue(cf1.findModule("m1").isPresent());
1387 
1388         ModuleDescriptor descriptor2 = newBuilder("m2").build();
1389         Configuration cf2 = resolve(ModuleUtils.finderOf(descriptor2), "m2");
1390         assertTrue(cf2.modules().size() == 1);
1391         assertTrue(cf2.findModule("m2").isPresent());
1392 
1393         ModuleDescriptor descriptor3 = newBuilder("m3").build();
1394         Configuration cf3 = resolve(ModuleUtils.finderOf(descriptor3), "m3");
1395         assertTrue(cf3.modules().size() == 1);
1396         assertTrue(cf3.findModule("m3").isPresent());
1397 
1398         // override m2, m1 and m3 should be found in parent configurations
1399         ModuleFinder finder = ModuleUtils.finderOf(descriptor2);
1400         Configuration cf4 = Configuration.resolve(
1401                 finder,
1402                 List.of(cf1, cf2, cf3),
1403                 ModuleFinder.of(),
1404                 Set.of("m1", "m2", "m3"));
1405         assertTrue(cf4.modules().size() == 1);
1406         assertTrue(cf4.findModule("m2").isPresent());
1407         ResolvedModule m2 = cf4.findModule("m2").get();
1408         assertTrue(m2.configuration() == cf4);
1409     }
1410 
1411 
1412     /**
1413      * Basic test of using the beforeFinder to override a module in the parent
1414      * configuration but where implied readability in the picture so that the
1415      * module in the parent is read.
1416      *
1417      * The test consists of two configurations:
1418      * - Configuration cf1: m1, m2 requires transitive m1
1419      * - Configuration cf2: m1, m3 requires m2
1420      */
1421     @Test
1422     void testOverriding3() {
1423         ModuleDescriptor descriptor1 = newBuilder("m1")
1424                 .build();
1425 
1426         ModuleDescriptor descriptor2 = newBuilder("m2")
1427                 .requires(Set.of(Requires.Modifier.TRANSITIVE), "m1")
1428                 .build();
1429 
1430         ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2);
1431 
1432         Configuration cf1 = resolve(finder1, "m2");
1433 
1434         assertTrue(cf1.modules().size() == 2);
1435         assertTrue(cf1.findModule("m1").isPresent());
1436         assertTrue(cf1.findModule("m2").isPresent());
1437 
1438         // cf2: m3 requires m2, m1
1439 
1440         ModuleDescriptor descriptor3 = newBuilder("m3")
1441                 .requires("m2")
1442                 .build();
1443 
1444         ModuleFinder finder2 = ModuleUtils.finderOf(descriptor1, descriptor3);
1445 
1446         Configuration cf2 = resolve(cf1, finder2, "m1", "m3");
1447 
1448         assertTrue(cf2.parents().size() == 1);
1449         assertTrue(cf2.parents().get(0) == cf1);
1450 
1451         assertTrue(cf2.modules().size() == 2);
1452         assertTrue(cf2.findModule("m1").isPresent());
1453         assertTrue(cf2.findModule("m3").isPresent());
1454 
1455         ResolvedModule m1_1 = cf1.findModule("m1").get();
1456         ResolvedModule m1_2 = cf2.findModule("m1").get();
1457         ResolvedModule m2 = cf1.findModule("m2").get();
1458         ResolvedModule m3 = cf2.findModule("m3").get();
1459 
1460         assertTrue(m1_1.configuration() == cf1);
1461         assertTrue(m1_2.configuration() == cf2);
1462         assertTrue(m3.configuration() == cf2);
1463 
1464 
1465         // check that m3 reads cf1/m1 and cf2/m2
1466         assertTrue(m3.reads().size() == 2);
1467         assertTrue(m3.reads().contains(m1_1));
1468         assertTrue(m3.reads().contains(m2));
1469     }
1470 
1471 
1472     /**
1473      * Root module not found
1474      */
1475     @Test
1476     void testRootNotFound() {
1477         assertThrows(FindException.class, () -> resolve(ModuleFinder.of(), "m1"));
1478     }
1479 
1480 
1481     /**
1482      * Direct dependency not found
1483      */
1484     @Test
1485     void testDirectDependencyNotFound() {
1486         ModuleDescriptor descriptor1 = newBuilder("m1").requires("m2").build();
1487         ModuleFinder finder = ModuleUtils.finderOf(descriptor1);
1488         assertThrows(FindException.class, () -> resolve(finder, "m1"));
1489     }
1490 
1491 
1492     /**
1493      * Transitive dependency not found
1494      */
1495     @Test
1496     void testTransitiveDependencyNotFound() {
1497         ModuleDescriptor descriptor1 = newBuilder("m1").requires("m2").build();
1498         ModuleDescriptor descriptor2 = newBuilder("m2").requires("m3").build();
1499         ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2);
1500         assertThrows(FindException.class, () -> resolve(finder, "m1"));
1501     }
1502 
1503 
1504     /**
1505      * Service provider dependency not found
1506      */
1507     @Test
1508     void testServiceProviderDependencyNotFound() {
1509 
1510         // service provider dependency (on m3) not found
1511 
1512         ModuleDescriptor descriptor1 = newBuilder("m1")
1513                 .exports("p")
1514                 .uses("p.S")
1515                 .build();
1516 
1517         ModuleDescriptor descriptor2 = newBuilder("m2")
1518                 .requires("m1")
1519                 .requires("m3")
1520                 .provides("p.S", List.of("q.T"))
1521                 .build();
1522 
1523         ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2);
1524 
1525         // should throw ResolutionException because m3 is not found
1526         assertThrows(FindException.class, () -> resolveAndBind(finder, "m1"));
1527     }
1528 
1529 
1530     /**
1531      * Simple cycle.
1532      */
1533     @Test
1534     void testSimpleCycle() {
1535         ModuleDescriptor descriptor1 = newBuilder("m1").requires("m2").build();
1536         ModuleDescriptor descriptor2 = newBuilder("m2").requires("m3").build();
1537         ModuleDescriptor descriptor3 = newBuilder("m3").requires("m1").build();
1538         ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2, descriptor3);
1539         assertThrows(ResolutionException.class, () -> resolve(finder, "m1"));
1540     }
1541 
1542     /**
1543      * Basic test for detecting cycles involving a service provider module
1544      */
1545     @Test
1546     void testCycleInProvider() {
1547         ModuleDescriptor descriptor1 = newBuilder("m1")
1548                 .exports("p")
1549                 .uses("p.S")
1550                 .build();
1551         ModuleDescriptor descriptor2 = newBuilder("m2")
1552                 .requires("m1")
1553                 .requires("m3")
1554                 .provides("p.S", List.of("q.T"))
1555                 .build();
1556         ModuleDescriptor descriptor3 = newBuilder("m3")
1557                 .requires("m2")
1558                 .build();
1559 
1560         ModuleFinder finder
1561             = ModuleUtils.finderOf(descriptor1, descriptor2, descriptor3);
1562 
1563         // should throw ResolutionException because of the m2 <--> m3 cycle
1564         assertThrows(ResolutionException.class, () -> resolveAndBind(finder, "m1"));
1565     }
1566 
1567 
1568     /**
1569      * Basic test to detect reading a module with the same name as itself
1570      *
1571      * The test consists of three configurations:
1572      * - Configuration cf1: m1, m2 requires transitive m1
1573      * - Configuration cf2: m1 requires m2
1574      */
1575     @Test
1576     void testReadModuleWithSameNameAsSelf() {
1577         ModuleDescriptor descriptor1_v1 = newBuilder("m1")
1578                 .build();
1579 
1580         ModuleDescriptor descriptor2 = newBuilder("m2")
1581                 .requires(Set.of(Requires.Modifier.TRANSITIVE), "m1")
1582                 .build();
1583 
1584         ModuleDescriptor descriptor1_v2 = newBuilder("m1")
1585                 .requires("m2")
1586                 .build();
1587 
1588         ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1_v1, descriptor2);
1589         Configuration cf1 = resolve(finder1, "m2");
1590         assertTrue(cf1.modules().size() == 2);
1591 
1592         // resolve should throw ResolutionException
1593         ModuleFinder finder2 = ModuleUtils.finderOf(descriptor1_v2);
1594         assertThrows(ResolutionException.class, () -> resolve(cf1, finder2, "m1"));
1595     }
1596 
1597 
1598     /**
1599      * Basic test to detect reading two modules with the same name
1600      *
1601      * The test consists of three configurations:
1602      * - Configuration cf1: m1, m2 requires transitive m1
1603      * - Configuration cf2: m1, m3 requires transitive m1
1604      * - Configuration cf3(cf1,cf2): m4 requires m2, m3
1605      */
1606     @Test
1607     void testReadTwoModuleWithSameName() {
1608         ModuleDescriptor descriptor1 = newBuilder("m1")
1609                 .build();
1610 
1611         ModuleDescriptor descriptor2 = newBuilder("m2")
1612                 .requires(Set.of(Requires.Modifier.TRANSITIVE), "m1")
1613                 .build();
1614 
1615         ModuleDescriptor descriptor3 = newBuilder("m3")
1616                 .requires(Set.of(Requires.Modifier.TRANSITIVE), "m1")
1617                 .build();
1618 
1619         ModuleDescriptor descriptor4 = newBuilder("m4")
1620                 .requires("m2")
1621                 .requires("m3")
1622                 .build();
1623 
1624         ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2);
1625         Configuration cf1 = resolve(finder1, "m2");
1626         assertTrue(cf1.modules().size() == 2);
1627 
1628         ModuleFinder finder2 = ModuleUtils.finderOf(descriptor1, descriptor3);
1629         Configuration cf2 = resolve(finder2, "m3");
1630         assertTrue(cf2.modules().size() == 2);
1631 
1632         // should throw ResolutionException as m4 will read modules named "m1".
1633         ModuleFinder finder3 = ModuleUtils.finderOf(descriptor4);
1634         assertThrows(ResolutionException.class,
1635                      () -> Configuration.resolve(finder3,
1636                                                  List.of(cf1, cf2),
1637                                                  ModuleFinder.of(),
1638                                                  Set.of("m4")));
1639     }
1640 
1641 
1642     /**
1643      * Test two modules exporting package p to a module that reads both.
1644      */
1645     @Test
1646     void testPackageSuppliedByTwoOthers() {
1647         ModuleDescriptor descriptor1 = newBuilder("m1")
1648                 .requires("m2")
1649                 .requires("m3")
1650                 .build();
1651 
1652         ModuleDescriptor descriptor2 = newBuilder("m2")
1653                 .exports("p")
1654                 .build();
1655 
1656         ModuleDescriptor descriptor3 = newBuilder("m3")
1657                 .exports("p", Set.of("m1"))
1658                 .build();
1659 
1660         ModuleFinder finder
1661             = ModuleUtils.finderOf(descriptor1, descriptor2, descriptor3);
1662 
1663         // m2 and m3 export package p to module m1
1664         assertThrows(ResolutionException.class, () -> resolve(finder, "m1"));
1665     }
1666 
1667 
1668     /**
1669      * Test the scenario where a module contains a package p and reads
1670      * a module that exports package p.
1671      */
1672     @Test
1673     void testPackageSuppliedBySelfAndOther() {
1674         ModuleDescriptor descriptor1 = newBuilder("m1")
1675                 .requires("m2")
1676                 .packages(Set.of("p"))
1677                 .build();
1678 
1679         ModuleDescriptor descriptor2 = newBuilder("m2")
1680                 .exports("p")
1681                 .build();
1682 
1683         ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2);
1684 
1685         // m1 contains package p, module m2 exports package p to m1
1686         assertThrows(ResolutionException.class, () -> resolve(finder, "m1"));
1687     }
1688 
1689 
1690     /**
1691      * Test the scenario where a module contains a package p and reads
1692      * a module that also contains a package p.
1693      */
1694     @Test
1695     void testContainsPackageInSelfAndOther() {
1696         ModuleDescriptor descriptor1 = newBuilder("m1")
1697                 .requires("m2")
1698                 .packages(Set.of("p"))
1699                 .build();
1700 
1701         ModuleDescriptor descriptor2 = newBuilder("m2")
1702                 .packages(Set.of("p"))
1703                 .build();
1704 
1705         ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2);
1706 
1707         Configuration cf = resolve(finder, "m1");
1708 
1709         assertTrue(cf.modules().size() == 2);
1710         assertTrue(cf.findModule("m1").isPresent());
1711         assertTrue(cf.findModule("m2").isPresent());
1712 
1713         // m1 reads m2, m2 reads nothing
1714         ResolvedModule m1 = cf.findModule("m1").get();
1715         ResolvedModule m2 = cf.findModule("m2").get();
1716         assertTrue(m1.reads().size() == 1);
1717         assertTrue(m1.reads().contains(m2));
1718         assertTrue(m2.reads().size() == 0);
1719     }
1720 
1721 
1722     /**
1723      * Test the scenario where a module that exports a package that is also
1724      * exported by a module that it reads in a parent layer.
1725      */
1726     @Test
1727     void testExportSamePackageAsBootLayer() {
1728         ModuleDescriptor descriptor = newBuilder("m1")
1729                 .requires("java.base")
1730                 .exports("java.lang")
1731                 .build();
1732 
1733         ModuleFinder finder = ModuleUtils.finderOf(descriptor);
1734 
1735         Configuration bootConfiguration = ModuleLayer.boot().configuration();
1736 
1737         // m1 contains package java.lang, java.base exports package java.lang to m1
1738         assertThrows(ResolutionException.class, () -> resolve(bootConfiguration, finder, "m1"));
1739     }
1740 
1741 
1742     /**
1743      * Test "uses p.S" where p is contained in the same module.
1744      */
1745     @Test
1746     void testContainsService1() {
1747         ModuleDescriptor descriptor1 = newBuilder("m1")
1748                 .packages(Set.of("p"))
1749                 .uses("p.S")
1750                 .build();
1751 
1752         ModuleFinder finder = ModuleUtils.finderOf(descriptor1);
1753 
1754         Configuration cf = resolve(finder, "m1");
1755 
1756         assertTrue(cf.modules().size() == 1);
1757         assertTrue(cf.findModule("m1").isPresent());
1758     }
1759 
1760 
1761     /**
1762      * Test "uses p.S" where p is contained in a different module.
1763      */
1764     @Test
1765     void testContainsService2() {
1766         ModuleDescriptor descriptor1 = newBuilder("m1")
1767                 .packages(Set.of("p"))
1768                 .build();
1769 
1770         ModuleDescriptor descriptor2 = newBuilder("m2")
1771                 .requires("m1")
1772                 .uses("p.S")
1773                 .build();
1774 
1775         ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2);
1776 
1777         // m2 does not read a module that exports p
1778         assertThrows(ResolutionException.class, () -> resolve(finder, "m2"));
1779     }
1780 
1781 
1782     /**
1783      * Test "provides p.S" where p is contained in the same module.
1784      */
1785     @Test
1786     void testContainsService3() {
1787         ModuleDescriptor descriptor1 = newBuilder("m1")
1788                 .packages(Set.of("p", "q"))
1789                 .provides("p.S", List.of("q.S1"))
1790                 .build();
1791 
1792         ModuleFinder finder = ModuleUtils.finderOf(descriptor1);
1793 
1794         Configuration cf = resolve(finder, "m1");
1795 
1796         assertTrue(cf.modules().size() == 1);
1797         assertTrue(cf.findModule("m1").isPresent());
1798     }
1799 
1800 
1801     /**
1802      * Test "provides p.S" where p is contained in a different module.
1803      */
1804     @Test
1805     void testContainsService4() {
1806         ModuleDescriptor descriptor1 = newBuilder("m1")
1807                 .packages(Set.of("p"))
1808                 .build();
1809 
1810         ModuleDescriptor descriptor2 = newBuilder("m2")
1811                 .requires("m1")
1812                 .provides("p.S", List.of("q.S1"))
1813                 .build();
1814 
1815         ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2);
1816 
1817         // m2 does not read a module that exports p
1818         assertThrows(ResolutionException.class, () -> resolve(finder, "m2"));
1819     }
1820 
1821     /**
1822      * Test uses optional service.
1823      *
1824      *     module m1 { requires static m2; uses p.S; }
1825      */
1826     @Test
1827     void testUsesOptionalService1() {
1828         ModuleDescriptor descriptor1 = newBuilder("m1")
1829                 .requires(Set.of(Requires.Modifier.STATIC), "m2")
1830                 .uses("p.S")
1831                 .build();
1832         ModuleFinder finder = ModuleUtils.finderOf(descriptor1);
1833         Configuration cf = resolve(finder, "m1");
1834         assertTrue(cf.modules().size() == 1);
1835         assertTrue(cf.findModule("m1").isPresent());
1836     }
1837 
1838     /**
1839      * Test uses optional service.
1840      *
1841      *     module m1 { requires m2; uses p.S; }
1842      *     module m2 { requires static transitive m3; }
1843      */
1844     @Test
1845     void testUsesOptionalService2() {
1846         ModuleDescriptor descriptor1 = newBuilder("m1")
1847                 .requires("m2")
1848                 .uses("p.S")
1849                 .build();
1850         ModuleDescriptor descriptor2 = newBuilder("m2")
1851                 .requires(Set.of(Requires.Modifier.STATIC, Requires.Modifier.TRANSITIVE), "m3")
1852                 .build();
1853         ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2);
1854         Configuration cf = resolve(finder, "m1");
1855         assertTrue(cf.modules().size() == 2);
1856         assertTrue(cf.findModule("m1").isPresent());
1857         assertTrue(cf.findModule("m2").isPresent());
1858     }
1859 
1860     /**
1861      * Test uses optional service.
1862      *
1863      *     module m1 { requires m2; uses p.S; }
1864      *     module m2 { requires transitive m3; }
1865      *     module m3 { requires static transitive m4; }
1866      */
1867     @Test
1868     void testUsesOptionalService3() {
1869         ModuleDescriptor descriptor1 = newBuilder("m1")
1870                 .requires("m2")
1871                 .uses("p.S")
1872                 .build();
1873         ModuleDescriptor descriptor2 = newBuilder("m2")
1874                 .requires(Set.of(Requires.Modifier.TRANSITIVE), "m3")
1875                 .build();
1876         ModuleDescriptor descriptor3 = newBuilder("m3")
1877                 .requires(Set.of(Requires.Modifier.STATIC, Requires.Modifier.TRANSITIVE), "m4")
1878                 .build();
1879         ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2, descriptor3);
1880         Configuration cf = resolve(finder, "m1");
1881         assertTrue(cf.modules().size() == 3);
1882         assertTrue(cf.findModule("m1").isPresent());
1883         assertTrue(cf.findModule("m2").isPresent());
1884         assertTrue(cf.findModule("m3").isPresent());
1885     }
1886 
1887     /**
1888      * Test provides optional service.
1889      *
1890      *     module m1 { requires static m2; provides p.S with q.P; }
1891      */
1892     @Test
1893     void testProvidesOptionalService1() {
1894         ModuleDescriptor descriptor1 = newBuilder("m1")
1895                 .requires(Set.of(Requires.Modifier.STATIC), "m2")
1896                 .provides("p.S", List.of("q.P"))
1897                 .build();
1898         ModuleFinder finder = ModuleUtils.finderOf(descriptor1);
1899         Configuration cf = resolve(finder, "m1");
1900         assertTrue(cf.modules().size() == 1);
1901         assertTrue(cf.findModule("m1").isPresent());
1902     }
1903 
1904     /**
1905      * Test provides optional service.
1906      *
1907      *     module m1 { requires m2; provides p.S with q.P; }
1908      *     module m2 { requires static transitive m3; }
1909      */
1910     @Test
1911     void testProvidesOptionalService2() {
1912         ModuleDescriptor descriptor1 = newBuilder("m1")
1913                 .requires("m2")
1914                 .provides("p.S", List.of("q.P"))
1915                 .build();
1916         ModuleDescriptor descriptor2 = newBuilder("m2")
1917                 .requires(Set.of(Requires.Modifier.STATIC, Requires.Modifier.TRANSITIVE), "m3")
1918                 .build();
1919         ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2);
1920         Configuration cf = resolve(finder, "m1");
1921         assertTrue(cf.modules().size() == 2);
1922         assertTrue(cf.findModule("m1").isPresent());
1923         assertTrue(cf.findModule("m2").isPresent());
1924     }
1925 
1926     /**
1927      * Test provides optional service.
1928      *
1929      *     module m1 { requires m2; provides p.S with q.P; }
1930      *     module m2 { requires transitive m3; }
1931      *     module m3 { requires static transitive m4; }
1932      */
1933     @Test
1934     void testProvidesOptionalService3() {
1935         ModuleDescriptor descriptor1 = newBuilder("m1")
1936                 .requires("m2")
1937                 .provides("p.S", List.of("q.P"))
1938                 .build();
1939         ModuleDescriptor descriptor2 = newBuilder("m2")
1940                 .requires(Set.of(Requires.Modifier.TRANSITIVE), "m3")
1941                 .build();
1942         ModuleDescriptor descriptor3 = newBuilder("m3")
1943                 .requires(Set.of(Requires.Modifier.STATIC, Requires.Modifier.TRANSITIVE), "m4")
1944                 .build();
1945         ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2, descriptor3);
1946         Configuration cf = resolve(finder, "m1");
1947         assertTrue(cf.modules().size() == 3);
1948         assertTrue(cf.findModule("m1").isPresent());
1949         assertTrue(cf.findModule("m2").isPresent());
1950         assertTrue(cf.findModule("m3").isPresent());
1951     }
1952 
1953     /**
1954      * Test "uses p.S" where p is not exported to the module.
1955      */
1956     @Test
1957     void testServiceTypePackageNotExported1() {
1958         ModuleDescriptor descriptor1 = newBuilder("m1")
1959                 .uses("p.S")
1960                 .build();
1961         ModuleFinder finder = ModuleUtils.finderOf(descriptor1);
1962 
1963         // m1 does not read a module that exports p to m1
1964         assertThrows(ResolutionException.class, () -> resolve(finder, "m1"));
1965     }
1966 
1967     /**
1968      * Test "uses p.S" where p is not exported to the module.
1969      *
1970      *     module m1 { requires m2; uses p.S; }
1971      *     module m2 { contains p; }
1972      */
1973     @Test
1974     void testServiceTypePackageNotExported2() {
1975         ModuleDescriptor descriptor1 = newBuilder("m1")
1976                 .requires("m2")
1977                 .uses("p.S")
1978                 .build();
1979         ModuleDescriptor descriptor2 = newBuilder("m2")
1980                 .packages(Set.of("p"))
1981                 .build();
1982         ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2);
1983 
1984         // m1 does not read a module that exports p to m1
1985         assertThrows(ResolutionException.class, () -> resolve(finder, "m1"));
1986     }
1987 
1988 
1989     /**
1990      * Test the empty configuration.
1991      */
1992     @Test
1993     void testEmptyConfiguration() {
1994         Configuration cf = Configuration.empty();
1995 
1996         assertTrue(cf.parents().isEmpty());
1997 
1998         assertTrue(cf.modules().isEmpty());
1999         assertFalse(cf.findModule("java.base").isPresent());
2000     }
2001 
2002     /**
2003      * Test creating a configuration containing platform specific modules.
2004      */
2005     @ParameterizedTest
2006     @CsvSource({",", "linux-aarch64,", "linux-aarch64,linux-aarch64"})
2007     void testPlatformMatch(String targetPlatform1, String targetPlatform2) throws IOException {
2008         ModuleDescriptor base =  ModuleDescriptor.newModule("java.base").build();
2009         Path system = writeModule(base, null);
2010 
2011         ModuleDescriptor descriptor1 = ModuleDescriptor.newModule("m1")
2012                 .requires("m2")
2013                 .build();
2014         Path dir1 = writeModule(descriptor1, targetPlatform1);
2015 
2016         ModuleDescriptor descriptor2 = ModuleDescriptor.newModule("m2").build();
2017         Path dir2 = writeModule(descriptor2, targetPlatform2);
2018 
2019         ModuleFinder finder = ModuleFinder.of(system, dir1, dir2);
2020 
2021         Configuration cf = resolve(finder, "m1");
2022 
2023         assertTrue(cf.modules().size() == 3);
2024         assertTrue(cf.findModule("java.base").isPresent());
2025         assertTrue(cf.findModule("m1").isPresent());
2026         assertTrue(cf.findModule("m2").isPresent());
2027     }
2028 
2029     /**
2030      * Test attempting to create a configuration with modules for different platforms.
2031      */
2032     @ParameterizedTest
2033     @CsvSource({"linux-x64,linux-aarch64", "linux-x64,windows-x64"})
2034     void testPlatformMisMatch(String targetPlatform1, String targetPlatform2) throws IOException {
2035         assertThrows(FindException.class,
2036                 () -> testPlatformMatch(targetPlatform1, targetPlatform2));
2037     }
2038 
2039     // no parents
2040 
2041     @Test
2042     void testResolveRequiresWithNoParents() {
2043         ModuleFinder empty = ModuleFinder.of();
2044         assertThrows(IllegalArgumentException.class,
2045                 () -> Configuration.resolve(empty, List.of(), empty, Set.of()));
2046     }
2047 
2048     @Test
2049     void testResolveRequiresAndUsesWithNoParents() {
2050         ModuleFinder empty = ModuleFinder.of();
2051         assertThrows(IllegalArgumentException.class,
2052                 () -> Configuration.resolveAndBind(empty, List.of(), empty, Set.of()));
2053     }
2054 
2055 
2056     // parents with modules for specific platforms
2057     @ParameterizedTest
2058     @CsvSource({",", "linux-aarch64,", "linux-aarch64,linux-aarch64"})
2059     void testResolveRequiresWithCompatibleParents(String targetPlatform1,
2060                                                   String targetPlatform2) throws IOException {
2061         ModuleDescriptor base = ModuleDescriptor.newModule("java.base").build();
2062         Path system = writeModule(base, null);
2063 
2064         ModuleDescriptor descriptor1 = ModuleDescriptor.newModule("m1").build();
2065         Path dir1 = writeModule(descriptor1, targetPlatform1);
2066 
2067         ModuleDescriptor descriptor2 = ModuleDescriptor.newModule("m2").build();
2068         Path dir2 = writeModule(descriptor2, targetPlatform2);
2069 
2070         ModuleFinder finder1 = ModuleFinder.of(system, dir1);
2071         Configuration cf1 = resolve(finder1, "m1");
2072 
2073         ModuleFinder finder2 = ModuleFinder.of(system, dir2);
2074         Configuration cf2 = resolve(finder2, "m2");
2075 
2076         Configuration cf3 = Configuration.resolve(ModuleFinder.of(),
2077                                                   List.of(cf1, cf2),
2078                                                   ModuleFinder.of(),
2079                                                   Set.of());
2080         assertTrue(cf3.parents().size() == 2);
2081     }
2082 
2083 
2084     @ParameterizedTest
2085     @CsvSource({"linux-x64,linux-aarch64", "linux-x64,windows-x64"})
2086     void testResolveRequiresWithConflictingParents(String targetPlatform1,
2087                                                    String targetPlatform2) throws IOException {
2088         assertThrows(IllegalArgumentException.class,
2089                 () -> testResolveRequiresWithCompatibleParents(targetPlatform1, targetPlatform2));
2090     }
2091 
2092 
2093     // null handling
2094 
2095     @Test
2096     void testResolveRequiresWithNull1() {
2097         assertThrows(NullPointerException.class,
2098                 () -> resolve((ModuleFinder) null, ModuleFinder.of()));
2099     }
2100 
2101     @Test
2102     void testResolveRequiresWithNull2() {
2103         assertThrows(NullPointerException.class,
2104                 () -> resolve(ModuleFinder.of(), (ModuleFinder) null));
2105     }
2106 
2107     @Test
2108     void testResolveRequiresWithNull3() {
2109         Configuration empty = Configuration.empty();
2110         assertThrows(NullPointerException.class,
2111                 () -> Configuration.resolve(null, List.of(empty),  ModuleFinder.of(), Set.of()));
2112     }
2113 
2114     @Test
2115     void testResolveRequiresWithNull4() {
2116         ModuleFinder empty = ModuleFinder.of();
2117         assertThrows(NullPointerException.class,
2118                 () -> Configuration.resolve(empty, null, empty, Set.of()));
2119     }
2120 
2121     @Test
2122     void testResolveRequiresWithNull5() {
2123         Configuration cf = ModuleLayer.boot().configuration();
2124         assertThrows(NullPointerException.class,
2125                 () -> Configuration.resolve(ModuleFinder.of(), List.of(cf), null, Set.of()));
2126     }
2127 
2128     @Test
2129     void testResolveRequiresWithNull6() {
2130         ModuleFinder empty = ModuleFinder.of();
2131         Configuration cf = ModuleLayer.boot().configuration();
2132         assertThrows(NullPointerException.class,
2133                 () -> Configuration.resolve(empty, List.of(cf), empty, null));
2134     }
2135 
2136     @Test
2137     void testResolveRequiresAndUsesWithNull1() {
2138         assertThrows(NullPointerException.class,
2139                 () -> resolveAndBind((ModuleFinder) null, ModuleFinder.of()));
2140     }
2141 
2142     @Test
2143     void testResolveRequiresAndUsesWithNull2() {
2144         assertThrows(NullPointerException.class,
2145                 () -> resolveAndBind(ModuleFinder.of(), (ModuleFinder) null));
2146     }
2147 
2148     @Test
2149     void testResolveRequiresAndUsesWithNull3() {
2150         Configuration empty = Configuration.empty();
2151         assertThrows(NullPointerException.class,
2152                 () -> Configuration.resolveAndBind(null, List.of(empty), ModuleFinder.of(), Set.of()));
2153     }
2154 
2155     @Test
2156     void testResolveRequiresAndUsesWithNull4() {
2157         ModuleFinder empty = ModuleFinder.of();
2158         assertThrows(NullPointerException.class,
2159                 () -> Configuration.resolveAndBind(empty, null, empty, Set.of()));
2160     }
2161 
2162     @Test
2163     void testResolveRequiresAndUsesWithNull5() {
2164         Configuration cf = ModuleLayer.boot().configuration();
2165         assertThrows(NullPointerException.class,
2166                 () -> Configuration.resolveAndBind(ModuleFinder.of(), List.of(cf), null, Set.of()));
2167     }
2168 
2169     @Test
2170     void testResolveRequiresAndUsesWithNull6() {
2171         ModuleFinder empty = ModuleFinder.of();
2172         Configuration cf = ModuleLayer.boot().configuration();
2173         assertThrows(NullPointerException.class,
2174                 () -> Configuration.resolveAndBind(empty, List.of(cf), empty, null));
2175     }
2176 
2177     @Test
2178     void testFindModuleWithNull() {
2179         assertThrows(NullPointerException.class, () -> Configuration.empty().findModule(null));
2180     }
2181 
2182     // unmodifiable collections
2183 
2184     static Stream<Configuration> configurations() {
2185         return Stream.of(
2186                 Configuration.empty(),
2187                 ModuleLayer.boot().configuration(),
2188                 resolve(ModuleFinder.of())
2189         );
2190     }
2191 
2192     @ParameterizedTest
2193     @MethodSource("configurations")
2194     void testUnmodifiableParents(Configuration cf) {
2195         assertThrows(UnsupportedOperationException.class,
2196                 () -> cf.parents().add(Configuration.empty()));
2197         assertThrows(UnsupportedOperationException.class,
2198                 () -> cf.parents().remove(Configuration.empty()));
2199     }
2200 
2201     @ParameterizedTest
2202     @MethodSource("configurations")
2203     void testUnmodifiableModules(Configuration cf) {
2204         ResolvedModule module = ModuleLayer.boot()
2205                 .configuration()
2206                 .findModule("java.base")
2207                 .orElseThrow();
2208         assertThrows(UnsupportedOperationException.class,
2209                 () -> cf.modules().add(module));
2210         assertThrows(UnsupportedOperationException.class,
2211                 () -> cf.modules().remove(module));
2212     }
2213 
2214     /**
2215      * Invokes parent.resolve(...)
2216      */
2217     private static Configuration resolve(Configuration parent,
2218                                          ModuleFinder before,
2219                                          ModuleFinder after,
2220                                          String... roots) {
2221         return parent.resolve(before, after, Set.of(roots));
2222     }
2223 
2224     private static Configuration resolve(Configuration parent,
2225                                          ModuleFinder before,
2226                                           String... roots) {
2227         return resolve(parent, before, ModuleFinder.of(), roots);
2228     }
2229 
2230     private static Configuration resolve(ModuleFinder before,
2231                                          ModuleFinder after,
2232                                          String... roots) {
2233         return resolve(Configuration.empty(), before, after, roots);
2234     }
2235 
2236     private static Configuration resolve(ModuleFinder before,
2237                                          String... roots) {
2238         return resolve(Configuration.empty(), before, roots);
2239     }
2240 
2241 
2242     /**
2243      * Invokes parent.resolveAndBind(...)
2244      */
2245     private static Configuration resolveAndBind(Configuration parent,
2246                                                 ModuleFinder before,
2247                                                 ModuleFinder after,
2248                                                 String... roots) {
2249         return parent.resolveAndBind(before, after, Set.of(roots));
2250     }
2251 
2252     private static Configuration resolveAndBind(Configuration parent,
2253                                                 ModuleFinder before,
2254                                                 String... roots) {
2255         return resolveAndBind(parent, before, ModuleFinder.of(), roots);
2256     }
2257 
2258     private static Configuration resolveAndBind(ModuleFinder before,
2259                                                 ModuleFinder after,
2260                                                 String... roots) {
2261         return resolveAndBind(Configuration.empty(), before, after, roots);
2262     }
2263 
2264     private static Configuration resolveAndBind(ModuleFinder before,
2265                                                 String... roots) {
2266         return resolveAndBind(Configuration.empty(), before, roots);
2267     }
2268 
2269 
2270     /**
2271      * Writes a module-info.class. If {@code targetPlatform} is not null then
2272      * it includes the ModuleTarget class file attribute with the target platform.
2273      */
2274     private static Path writeModule(ModuleDescriptor descriptor, String targetPlatform)
2275         throws IOException
2276     {
2277         ModuleTarget target;
2278         if (targetPlatform != null) {
2279             target = new ModuleTarget(targetPlatform);
2280         } else {
2281             target = null;
2282         }
2283         String name = descriptor.name();
2284         Path dir = Files.createTempDirectory(Paths.get(""), name);
2285         Path mi = dir.resolve("module-info.class");
2286         try (OutputStream out = Files.newOutputStream(mi)) {
2287             ModuleInfoWriter.write(descriptor, target, out);
2288         }
2289         return dir;
2290     }
2291 }