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