diff --git a/src/java.base/share/classes/java/lang/module/Configuration.java b/src/java.base/share/classes/java/lang/module/Configuration.java index 7959df3ca00..a76a32cfb28 100644 --- a/src/java.base/share/classes/java/lang/module/Configuration.java +++ b/src/java.base/share/classes/java/lang/module/Configuration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -75,6 +75,61 @@ import jdk.internal.vm.annotation.Stable; * ModuleLayer.boot().configuration()}. The configuration for the boot layer * will often be the parent when creating new configurations.

* + *

Optional Services

+ * + * Resolution requires that if a module {@code M} '{@code uses}' a service or + * '{@code provides}' an implementation of a service, then the service must be available + * to {@code M} at run time, either because {@code M} itself contains the service's + * package or because {@code M} reads another module that exports the service's package. + * However, it is sometimes desirable for the service's package to come from a module + * that is optional at run time, as indicated by the use of 'requires static' in this + * example: + * + * {@snippet : + * module M { + * requires static Y; + * uses p.S; + * } + * + * module Y { + * exports p; + * } + * } + * + * Resolution is resilient when a service's package comes from a module that is optional + * at run time. That is, if a module {@code M} has an optional dependency on some module + * {@code Y}, but {@code Y} is not needed at run time ({@code Y} might be observable but + * no-one reads it), then resolution at run time assumes that {@code Y} exported + * the service's package at compile time. Resolution at run time does not attempt to + * check whether {@code Y} is observable or (if it is observable) whether {@code Y} + * exports the service's package. + * + *

The module that '{@code uses}' the service, or '{@code provides}' an implementation + * of it, may depend directly on the optional module, as {@code M} does above, or may + * depend indirectly on the optional module, as shown here: + * + * {@snippet : + * module M { + * requires X; + * uses p.S; + * } + * + * module X { + * requires static transitive Y; + * } + * + * module Y { + * exports p; + * } + * } + * + * In effect, the service that {@code M} '{@code uses}', or '{@code provides}' an + * implementation of, is optional if it comes from an optional dependency. In this case, + * code in {@code M} must be prepared to deal with the class or interface that denotes + * the service being unavailable at run time. This is distinct from the more regular + * case where the service is available but no implementations of the service are + * available. + * *

Example

* *

The following example uses the {@link @@ -153,7 +208,6 @@ public final class Configuration { this.graph = g; this.modules = Set.of(moduleArray); this.nameToModule = Map.ofEntries(nameEntries); - this.targetPlatform = resolver.targetPlatform(); } @@ -358,10 +412,17 @@ public final class Configuration { * module {@code M} containing package {@code p} reads another module * that exports {@code p} to {@code M}.

* - *
  • A module {@code M} declares that it "{@code uses p.S}" or - * "{@code provides p.S with ...}" but package {@code p} is neither in - * module {@code M} nor exported to {@code M} by any module that - * {@code M} reads.

  • + *
  • A module {@code M} declares that it '{@code uses p.S}' or + * '{@code provides p.S with ...}', but the package {@code p} is neither in + * module {@code M} nor exported to {@code M} by any module that {@code M} + * reads. Additionally, neither of the following is {@code true}: + *

  • * * * diff --git a/src/java.base/share/classes/java/lang/module/Resolver.java b/src/java.base/share/classes/java/lang/module/Resolver.java index bff087fb674..e09060fd797 100644 --- a/src/java.base/share/classes/java/lang/module/Resolver.java +++ b/src/java.base/share/classes/java/lang/module/Resolver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -689,7 +689,6 @@ final class Resolver { * */ private void checkExportSuppliers(Map> graph) { - for (Map.Entry> e : graph.entrySet()) { ModuleDescriptor descriptor1 = e.getKey().descriptor(); String name1 = descriptor1.name(); @@ -754,7 +753,6 @@ final class Resolver { failTwoSuppliers(descriptor1, source, descriptor2, supplier); } } - } } @@ -764,18 +762,21 @@ final class Resolver { // uses S for (String service : descriptor1.uses()) { String pn = packageName(service); - if (!packageToExporter.containsKey(pn)) { - resolveFail("Module %s does not read a module that exports %s", - descriptor1.name(), pn); + if (!packageToExporter.containsKey(pn) + && !requiresStaticMissingModule(descriptor1, reads)) { + resolveFail("Module %s uses %s but does not read a module that exports %s to %s", + descriptor1.name(), service, pn, descriptor1.name()); + } } // provides S for (ModuleDescriptor.Provides provides : descriptor1.provides()) { String pn = packageName(provides.service()); - if (!packageToExporter.containsKey(pn)) { - resolveFail("Module %s does not read a module that exports %s", - descriptor1.name(), pn); + if (!packageToExporter.containsKey(pn) + && !requiresStaticMissingModule(descriptor1, reads)) { + resolveFail("Module %s provides %s but does not read a module that exports %s to %s", + descriptor1.name(), provides.service(), pn, descriptor1.name()); } } @@ -785,6 +786,34 @@ final class Resolver { } + /** + * Returns true if a module 'requires static' a module that is not in the + * readability graph, or reads a module that 'requires static transitive' + * a module that is not in the readability graph. + */ + private boolean requiresStaticMissingModule(ModuleDescriptor descriptor, + Set reads) { + Set moduleNames = reads.stream() + .map(ResolvedModule::name) + .collect(Collectors.toSet()); + for (ModuleDescriptor.Requires r : descriptor.requires()) { + if (r.modifiers().contains(Modifier.STATIC) + && !moduleNames.contains(r.name())) { + return true; + } + } + for (ResolvedModule rm : reads) { + for (ModuleDescriptor.Requires r : rm.descriptor().requires()) { + if (r.modifiers().contains(Modifier.STATIC) + && r.modifiers().contains(Modifier.TRANSITIVE) + && !moduleNames.contains(r.name())) { + return true; + } + } + } + return false; + } + /** * Fail because a module in the configuration exports the same package to * a module that reads both. This includes the case where a module M diff --git a/src/java.base/share/classes/java/lang/module/package-info.java b/src/java.base/share/classes/java/lang/module/package-info.java index 45dc40d5218..561a8ab635a 100644 --- a/src/java.base/share/classes/java/lang/module/package-info.java +++ b/src/java.base/share/classes/java/lang/module/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -191,6 +191,10 @@ * However, if M is recursively enumerated at step 1 then all modules that are * enumerated and `requires static M` will read M.

    * + *

    The {@linkplain java.lang.module.Configuration##optional-services Optional + * Services} section of {@link java.lang.module.Configuration} shows how resolution + * can be resilient when a service comes from a module that is optional at run time. + * *

    Completeness

    * *

    Resolution may be partial at compile-time in that the complete transitive diff --git a/test/jdk/java/lang/module/ConfigurationTest.java b/test/jdk/java/lang/module/ConfigurationTest.java index da64f955fd6..b32d49ffb06 100644 --- a/test/jdk/java/lang/module/ConfigurationTest.java +++ b/test/jdk/java/lang/module/ConfigurationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,13 +23,14 @@ /** * @test + * @bug 8142968 8299504 * @modules java.base/jdk.internal.access * java.base/jdk.internal.module * @library /test/lib * @build ConfigurationTest * jdk.test.lib.util.ModuleInfoWriter * jdk.test.lib.util.ModuleUtils - * @run testng ConfigurationTest + * @run junit ConfigurationTest * @summary Basic tests for java.lang.module.Configuration */ @@ -47,18 +48,20 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; import java.util.Set; +import java.util.stream.Stream; +import jdk.internal.access.SharedSecrets; +import jdk.internal.module.ModuleTarget; import jdk.test.lib.util.ModuleInfoWriter; import jdk.test.lib.util.ModuleUtils; -import jdk.internal.access.SharedSecrets; -import jdk.internal.module.ModuleTarget; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.CsvSource; +import static org.junit.jupiter.api.Assertions.*; -@Test -public class ConfigurationTest { +class ConfigurationTest { /** * Creates a "non-strict" builder for building a module. This allows the @@ -73,7 +76,8 @@ public class ConfigurationTest { * Basic test of resolver * m1 requires m2, m2 requires m3 */ - public void testBasic() { + @Test + void testBasic() { ModuleDescriptor descriptor1 = newBuilder("m1") .requires("m2") .build(); @@ -125,7 +129,8 @@ public class ConfigurationTest { * Basic test of "requires transitive": * m1 requires m2, m2 requires transitive m3 */ - public void testRequiresTransitive1() { + @Test + void testRequiresTransitive1() { // m1 requires m2, m2 requires transitive m3 ModuleDescriptor descriptor1 = newBuilder("m1") .requires("m2") @@ -177,7 +182,8 @@ public class ConfigurationTest { * - Configuration cf1: m1, m2 requires transitive m1 * - Configuration cf2: m3 requires m2 */ - public void testRequiresTransitive2() { + @Test + void testRequiresTransitive2() { // cf1: m1 and m2, m2 requires transitive m1 @@ -238,7 +244,8 @@ public class ConfigurationTest { * - Configuration cf1: m1 * - Configuration cf2: m2 requires transitive m1, m3 requires m2 */ - public void testRequiresTransitive3() { + @Test + void testRequiresTransitive3() { // cf1: m1 @@ -300,7 +307,8 @@ public class ConfigurationTest { * - Configuration cf2: m2 requires transitive m1 * - Configuraiton cf3: m3 requires m2 */ - public void testRequiresTransitive4() { + @Test + void testRequiresTransitive4() { // cf1: m1 @@ -375,7 +383,8 @@ public class ConfigurationTest { * - Configuration cf1: m1, m2 requires transitive m1 * - Configuration cf2: m3 requires transitive m2, m4 requires m3 */ - public void testRequiresTransitive5() { + @Test + void testRequiresTransitive5() { // cf1: m1, m2 requires transitive m1 @@ -452,7 +461,8 @@ public class ConfigurationTest { * m2 is not observable * resolve m1 */ - public void testRequiresStatic1() { + @Test + void testRequiresStatic1() { ModuleDescriptor descriptor1 = newBuilder("m1") .requires(Set.of(Requires.Modifier.STATIC), "m2") .build(); @@ -474,7 +484,8 @@ public class ConfigurationTest { * m2 * resolve m1 */ - public void testRequiresStatic2() { + @Test + void testRequiresStatic2() { ModuleDescriptor descriptor1 = newBuilder("m1") .requires(Set.of(Requires.Modifier.STATIC), "m2") .build(); @@ -499,7 +510,8 @@ public class ConfigurationTest { * m2 * resolve m1, m2 */ - public void testRequiresStatic3() { + @Test + void testRequiresStatic3() { ModuleDescriptor descriptor1 = newBuilder("m1") .requires(Set.of(Requires.Modifier.STATIC), "m2") .build(); @@ -529,7 +541,8 @@ public class ConfigurationTest { * m2 requires static m2 * m3 */ - public void testRequiresStatic4() { + @Test + void testRequiresStatic4() { ModuleDescriptor descriptor1 = newBuilder("m1") .requires("m2") .requires("m3") @@ -570,7 +583,8 @@ public class ConfigurationTest { * - Configuration cf1: m1, m2 * - Configuration cf2: m3 requires m1, requires static m2 */ - public void testRequiresStatic5() { + @Test + void testRequiresStatic5() { ModuleDescriptor descriptor1 = newBuilder("m1") .build(); @@ -613,7 +627,8 @@ public class ConfigurationTest { * - Configuration cf1: m1 * - Configuration cf2: m3 requires m1, requires static m2 */ - public void testRequiresStatic6() { + @Test + void testRequiresStatic6() { ModuleDescriptor descriptor1 = newBuilder("m1") .build(); @@ -650,7 +665,8 @@ public class ConfigurationTest { * m2 requires transitive static m1 * m3 requires m2 */ - public void testRequiresStatic7() { + @Test + void testRequiresStatic7() { ModuleDescriptor descriptor1 = null; // not observable ModuleDescriptor descriptor2 = newBuilder("m2") @@ -683,7 +699,8 @@ public class ConfigurationTest { * - Configuration cf1: m2 requires transitive static m1 * - Configuration cf2: m3 requires m2 */ - public void testRequiresStatic8() { + @Test + void testRequiresStatic8() { ModuleDescriptor descriptor1 = null; // not observable ModuleDescriptor descriptor2 = newBuilder("m2") @@ -722,8 +739,8 @@ public class ConfigurationTest { * m1 uses p.S * m2 provides p.S */ - public void testServiceBinding1() { - + @Test + void testServiceBinding1() { ModuleDescriptor descriptor1 = newBuilder("m1") .exports("p") .uses("p.S") @@ -762,8 +779,8 @@ public class ConfigurationTest { * m2 provides p.S1, m2 uses p.S2 * m3 provides p.S2 */ - public void testServiceBinding2() { - + @Test + void testServiceBinding2() { ModuleDescriptor descriptor1 = newBuilder("m1") .exports("p") .uses("p.S1") @@ -816,8 +833,8 @@ public class ConfigurationTest { * - Configuration cf1: m1 uses p.S * - Configuration cf2: m2 provides p.S */ - public void testServiceBindingWithConfigurations1() { - + @Test + void testServiceBindingWithConfigurations1() { ModuleDescriptor descriptor1 = newBuilder("m1") .exports("p") .uses("p.S") @@ -862,8 +879,8 @@ public class ConfigurationTest { * - Configuration cf2: m3 provides p.S * m4 provides p.S */ - public void testServiceBindingWithConfigurations2() { - + @Test + void testServiceBindingWithConfigurations2() { ModuleDescriptor descriptor1 = newBuilder("m1") .exports("p") .uses("p.S") @@ -930,8 +947,8 @@ public class ConfigurationTest { * Test configuration cf2: m1 uses p.S, p@2.0 provides p.S * Test configuration cf2: m1 uses p.S */ - public void testServiceBindingWithConfigurations3() { - + @Test + void testServiceBindingWithConfigurations3() { ModuleDescriptor service = newBuilder("s") .exports("p") .build(); @@ -952,7 +969,7 @@ public class ConfigurationTest { // p@1.0 in cf1 ResolvedModule p = cf1.findModule("p").get(); - assertEquals(p.reference().descriptor(), provider_v1); + assertEquals(provider_v1, p.reference().descriptor()); ModuleDescriptor descriptor1 = newBuilder("m1") @@ -980,7 +997,7 @@ public class ConfigurationTest { // p should be found in cf2 p = cf2.findModule("p").get(); assertTrue(p.configuration() == cf2); - assertEquals(p.reference().descriptor(), provider_v2); + assertEquals(provider_v2, p.reference().descriptor()); // finder2 is the after ModuleFinder and so p@2.0 should not be located @@ -995,7 +1012,7 @@ public class ConfigurationTest { // p should be found in cf1 p = cf2.findModule("p").get(); assertTrue(p.configuration() == cf1); - assertEquals(p.reference().descriptor(), provider_v1); + assertEquals(provider_v1, p.reference().descriptor()); } @@ -1004,8 +1021,8 @@ public class ConfigurationTest { * * Module m2 can be found by both the before and after finders. */ - public void testWithTwoFinders1() { - + @Test + void testWithTwoFinders1() { ModuleDescriptor descriptor1 = newBuilder("m1") .requires("m2") .build(); @@ -1030,8 +1047,8 @@ public class ConfigurationTest { ResolvedModule m1 = cf.findModule("m1").get(); ResolvedModule m2 = cf.findModule("m2").get(); - assertEquals(m1.reference().descriptor(), descriptor1); - assertEquals(m2.reference().descriptor(), descriptor2_v1); + assertEquals(descriptor1, m1.reference().descriptor()); + assertEquals(descriptor2_v1, m2.reference().descriptor()); } @@ -1041,8 +1058,8 @@ public class ConfigurationTest { * The before and after ModuleFinders both locate a service provider module * named "m2" that provide implementations of the same service type. */ - public void testWithTwoFinders2() { - + @Test + void testWithTwoFinders2() { ModuleDescriptor descriptor1 = newBuilder("m1") .exports("p") .uses("p.S") @@ -1070,8 +1087,8 @@ public class ConfigurationTest { ResolvedModule m1 = cf.findModule("m1").get(); ResolvedModule m2 = cf.findModule("m2").get(); - assertEquals(m1.reference().descriptor(), descriptor1); - assertEquals(m2.reference().descriptor(), descriptor2_v1); + assertEquals(descriptor1, m1.reference().descriptor()); + assertEquals(descriptor2_v1, m2.reference().descriptor()); } @@ -1079,8 +1096,8 @@ public class ConfigurationTest { * Basic test for resolving a module that is located in the parent * configuration. */ - public void testResolvedInParent1() { - + @Test + void testResolvedInParent1() { ModuleDescriptor descriptor1 = newBuilder("m1") .build(); @@ -1101,8 +1118,8 @@ public class ConfigurationTest { * Basic test for resolving a module that has a dependency on a module * in the parent configuration. */ - public void testResolvedInParent2() { - + @Test + void testResolvedInParent2() { ModuleDescriptor descriptor1 = newBuilder("m1").build(); ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1); @@ -1142,12 +1159,12 @@ public class ConfigurationTest { * - Configuration cf2: m2 * - Configuration cf3(cf1,cf2): m3 requires m1, m2 */ - public void testResolvedInMultipleParents1() { - + @Test + void testResolvedInMultipleParents1() { // Configuration cf1: m1 ModuleDescriptor descriptor1 = newBuilder("m1").build(); Configuration cf1 = resolve(ModuleUtils.finderOf(descriptor1), "m1"); - assertEquals(cf1.parents(), List.of(Configuration.empty())); + assertEquals(List.of(Configuration.empty()), cf1.parents()); assertTrue(cf1.findModule("m1").isPresent()); ResolvedModule m1 = cf1.findModule("m1").get(); assertTrue(m1.configuration() == cf1); @@ -1155,7 +1172,7 @@ public class ConfigurationTest { // Configuration cf2: m2 ModuleDescriptor descriptor2 = newBuilder("m2").build(); Configuration cf2 = resolve(ModuleUtils.finderOf(descriptor2), "m2"); - assertEquals(cf2.parents(), List.of(Configuration.empty())); + assertEquals(List.of(Configuration.empty()), cf2.parents()); assertTrue(cf2.findModule("m2").isPresent()); ResolvedModule m2 = cf2.findModule("m2").get(); assertTrue(m2.configuration() == cf2); @@ -1171,7 +1188,7 @@ public class ConfigurationTest { List.of(cf1, cf2), // parents ModuleFinder.of(), Set.of("m3")); - assertEquals(cf3.parents(), List.of(cf1, cf2)); + assertEquals(List.of(cf1, cf2), cf3.parents()); assertTrue(cf3.findModule("m3").isPresent()); ResolvedModule m3 = cf3.findModule("m3").get(); assertTrue(m3.configuration() == cf3); @@ -1179,7 +1196,7 @@ public class ConfigurationTest { // check readability assertTrue(m1.reads().isEmpty()); assertTrue(m2.reads().isEmpty()); - assertEquals(m3.reads(), Set.of(m1, m2)); + assertEquals(Set.of(m1, m2), m3.reads()); } @@ -1193,11 +1210,12 @@ public class ConfigurationTest { * - Configuration cf3(cf3): m3 requires m1 * - Configuration cf4(cf2,cf3): m4 requires m1,m2,m3 */ - public void testResolvedInMultipleParents2() { + @Test + void testResolvedInMultipleParents2() { // Configuration cf1: m1 ModuleDescriptor descriptor1 = newBuilder("m1").build(); Configuration cf1 = resolve(ModuleUtils.finderOf(descriptor1), "m1"); - assertEquals(cf1.parents(), List.of(Configuration.empty())); + assertEquals( List.of(Configuration.empty()), cf1.parents()); assertTrue(cf1.findModule("m1").isPresent()); ResolvedModule m1 = cf1.findModule("m1").get(); assertTrue(m1.configuration() == cf1); @@ -1211,7 +1229,7 @@ public class ConfigurationTest { List.of(cf1), // parents ModuleFinder.of(), Set.of("m2")); - assertEquals(cf2.parents(), List.of(cf1)); + assertEquals(List.of(cf1), cf2.parents()); assertTrue(cf2.findModule("m2").isPresent()); ResolvedModule m2 = cf2.findModule("m2").get(); assertTrue(m2.configuration() == cf2); @@ -1225,7 +1243,7 @@ public class ConfigurationTest { List.of(cf1), // parents ModuleFinder.of(), Set.of("m3")); - assertEquals(cf3.parents(), List.of(cf1)); + assertEquals(List.of(cf1), cf3.parents()); assertTrue(cf3.findModule("m3").isPresent()); ResolvedModule m3 = cf3.findModule("m3").get(); assertTrue(m3.configuration() == cf3); @@ -1241,16 +1259,16 @@ public class ConfigurationTest { List.of(cf2, cf3), // parents ModuleFinder.of(), Set.of("m4")); - assertEquals(cf4.parents(), List.of(cf2, cf3)); + assertEquals(List.of(cf2, cf3), cf4.parents()); assertTrue(cf4.findModule("m4").isPresent()); ResolvedModule m4 = cf4.findModule("m4").get(); assertTrue(m4.configuration() == cf4); // check readability assertTrue(m1.reads().isEmpty()); - assertEquals(m2.reads(), Set.of(m1)); - assertEquals(m3.reads(), Set.of(m1)); - assertEquals(m4.reads(), Set.of(m1, m2, m3)); + assertEquals(Set.of(m1), m2.reads()); + assertEquals(Set.of(m1), m3.reads()); + assertEquals(Set.of(m1, m2, m3), m4.reads()); } @@ -1264,13 +1282,14 @@ public class ConfigurationTest { * - Configuration cf3: m1@3, m2@3, m3@3 * - Configuration cf4(cf1,cf2,cf3): m4 requires m1,m2,m3 */ - public void testResolvedInMultipleParents3() { + @Test + void testResolvedInMultipleParents3() { ModuleDescriptor descriptor1, descriptor2, descriptor3; // Configuration cf1: m1@1 descriptor1 = newBuilder("m1").version("1").build(); Configuration cf1 = resolve(ModuleUtils.finderOf(descriptor1), "m1"); - assertEquals(cf1.parents(), List.of(Configuration.empty())); + assertEquals(List.of(Configuration.empty()), cf1.parents()); // Configuration cf2: m1@2, m2@2 descriptor1 = newBuilder("m1").version("2").build(); @@ -1278,7 +1297,7 @@ public class ConfigurationTest { Configuration cf2 = resolve( ModuleUtils.finderOf(descriptor1, descriptor2), "m1", "m2"); - assertEquals(cf2.parents(), List.of(Configuration.empty())); + assertEquals(List.of(Configuration.empty()), cf2.parents()); // Configuration cf3: m1@3, m2@3, m3@3 descriptor1 = newBuilder("m1").version("3").build(); @@ -1287,7 +1306,7 @@ public class ConfigurationTest { Configuration cf3 = resolve( ModuleUtils.finderOf(descriptor1, descriptor2, descriptor3), "m1", "m2", "m3"); - assertEquals(cf3.parents(), List.of(Configuration.empty())); + assertEquals(List.of(Configuration.empty()), cf3.parents()); // Configuration cf4(cf1,cf2,cf3): m4 requires m1,m2,m3 ModuleDescriptor descriptor4 = newBuilder("m4") @@ -1300,7 +1319,7 @@ public class ConfigurationTest { List.of(cf1, cf2, cf3), // parents ModuleFinder.of(), Set.of("m4")); - assertEquals(cf4.parents(), List.of(cf1, cf2, cf3)); + assertEquals(List.of(cf1, cf2, cf3), cf4.parents()); assertTrue(cf1.findModule("m1").isPresent()); assertTrue(cf2.findModule("m1").isPresent()); @@ -1333,7 +1352,7 @@ public class ConfigurationTest { assertTrue(m1_3.reads().isEmpty()); assertTrue(m2_3.reads().isEmpty()); assertTrue(m3_3.reads().isEmpty()); - assertEquals(m4.reads(), Set.of(m1_1, m2_2, m3_3)); + assertEquals(Set.of(m1_1, m2_2, m3_3), m4.reads()); } @@ -1341,7 +1360,8 @@ public class ConfigurationTest { * Basic test of using the beforeFinder to override a module in a parent * configuration. */ - public void testOverriding1() { + @Test + void testOverriding1() { ModuleDescriptor descriptor1 = newBuilder("m1").build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1); @@ -1359,7 +1379,8 @@ public class ConfigurationTest { * Basic test of using the beforeFinder to override a module in a parent * configuration. */ - public void testOverriding2() { + @Test + void testOverriding2() { ModuleDescriptor descriptor1 = newBuilder("m1").build(); Configuration cf1 = resolve(ModuleUtils.finderOf(descriptor1), "m1"); assertTrue(cf1.modules().size() == 1); @@ -1398,8 +1419,8 @@ public class ConfigurationTest { * - Configuration cf1: m1, m2 requires transitive m1 * - Configuration cf2: m1, m3 requires m2 */ - public void testOverriding3() { - + @Test + void testOverriding3() { ModuleDescriptor descriptor1 = newBuilder("m1") .build(); @@ -1452,40 +1473,40 @@ public class ConfigurationTest { /** * Root module not found */ - @Test(expectedExceptions = { FindException.class }) - public void testRootNotFound() { - resolve(ModuleFinder.of(), "m1"); + @Test + void testRootNotFound() { + assertThrows(FindException.class, () -> resolve(ModuleFinder.of(), "m1")); } /** * Direct dependency not found */ - @Test(expectedExceptions = { FindException.class }) - public void testDirectDependencyNotFound() { + @Test + void testDirectDependencyNotFound() { ModuleDescriptor descriptor1 = newBuilder("m1").requires("m2").build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1); - resolve(finder, "m1"); + assertThrows(FindException.class, () -> resolve(finder, "m1")); } /** * Transitive dependency not found */ - @Test(expectedExceptions = { FindException.class }) - public void testTransitiveDependencyNotFound() { + @Test + void testTransitiveDependencyNotFound() { ModuleDescriptor descriptor1 = newBuilder("m1").requires("m2").build(); ModuleDescriptor descriptor2 = newBuilder("m2").requires("m3").build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2); - resolve(finder, "m1"); + assertThrows(FindException.class, () -> resolve(finder, "m1")); } /** * Service provider dependency not found */ - @Test(expectedExceptions = { FindException.class }) - public void testServiceProviderDependencyNotFound() { + @Test + void testServiceProviderDependencyNotFound() { // service provider dependency (on m3) not found @@ -1503,29 +1524,27 @@ public class ConfigurationTest { ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2); // should throw ResolutionException because m3 is not found - Configuration cf = resolveAndBind(finder, "m1"); + assertThrows(FindException.class, () -> resolveAndBind(finder, "m1")); } /** * Simple cycle. */ - @Test(expectedExceptions = { ResolutionException.class }) - public void testSimpleCycle() { + @Test + void testSimpleCycle() { ModuleDescriptor descriptor1 = newBuilder("m1").requires("m2").build(); ModuleDescriptor descriptor2 = newBuilder("m2").requires("m3").build(); ModuleDescriptor descriptor3 = newBuilder("m3").requires("m1").build(); - ModuleFinder finder - = ModuleUtils.finderOf(descriptor1, descriptor2, descriptor3); - resolve(finder, "m1"); + ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2, descriptor3); + assertThrows(ResolutionException.class, () -> resolve(finder, "m1")); } /** * Basic test for detecting cycles involving a service provider module */ - @Test(expectedExceptions = { ResolutionException.class }) - public void testCycleInProvider() { - + @Test + void testCycleInProvider() { ModuleDescriptor descriptor1 = newBuilder("m1") .exports("p") .uses("p.S") @@ -1543,7 +1562,7 @@ public class ConfigurationTest { = ModuleUtils.finderOf(descriptor1, descriptor2, descriptor3); // should throw ResolutionException because of the m2 <--> m3 cycle - resolveAndBind(finder, "m1"); + assertThrows(ResolutionException.class, () -> resolveAndBind(finder, "m1")); } @@ -1554,8 +1573,8 @@ public class ConfigurationTest { * - Configuration cf1: m1, m2 requires transitive m1 * - Configuration cf2: m1 requires m2 */ - @Test(expectedExceptions = { ResolutionException.class }) - public void testReadModuleWithSameNameAsSelf() { + @Test + void testReadModuleWithSameNameAsSelf() { ModuleDescriptor descriptor1_v1 = newBuilder("m1") .build(); @@ -1573,7 +1592,7 @@ public class ConfigurationTest { // resolve should throw ResolutionException ModuleFinder finder2 = ModuleUtils.finderOf(descriptor1_v2); - resolve(cf1, finder2, "m1"); + assertThrows(ResolutionException.class, () -> resolve(cf1, finder2, "m1")); } @@ -1585,8 +1604,8 @@ public class ConfigurationTest { * - Configuration cf2: m1, m3 requires transitive m1 * - Configuration cf3(cf1,cf2): m4 requires m2, m3 */ - @Test(expectedExceptions = { ResolutionException.class }) - public void testReadTwoModuleWithSameName() { + @Test + void testReadTwoModuleWithSameName() { ModuleDescriptor descriptor1 = newBuilder("m1") .build(); @@ -1613,16 +1632,19 @@ public class ConfigurationTest { // should throw ResolutionException as m4 will read modules named "m1". ModuleFinder finder3 = ModuleUtils.finderOf(descriptor4); - Configuration.resolve(finder3, List.of(cf1, cf2), ModuleFinder.of(), Set.of("m4")); + assertThrows(ResolutionException.class, + () -> Configuration.resolve(finder3, + List.of(cf1, cf2), + ModuleFinder.of(), + Set.of("m4"))); } /** * Test two modules exporting package p to a module that reads both. */ - @Test(expectedExceptions = { ResolutionException.class }) - public void testPackageSuppliedByTwoOthers() { - + @Test + void testPackageSuppliedByTwoOthers() { ModuleDescriptor descriptor1 = newBuilder("m1") .requires("m2") .requires("m3") @@ -1640,7 +1662,7 @@ public class ConfigurationTest { = ModuleUtils.finderOf(descriptor1, descriptor2, descriptor3); // m2 and m3 export package p to module m1 - resolve(finder, "m1"); + assertThrows(ResolutionException.class, () -> resolve(finder, "m1")); } @@ -1648,9 +1670,8 @@ public class ConfigurationTest { * Test the scenario where a module contains a package p and reads * a module that exports package p. */ - @Test(expectedExceptions = { ResolutionException.class }) - public void testPackageSuppliedBySelfAndOther() { - + @Test + void testPackageSuppliedBySelfAndOther() { ModuleDescriptor descriptor1 = newBuilder("m1") .requires("m2") .packages(Set.of("p")) @@ -1663,7 +1684,7 @@ public class ConfigurationTest { ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2); // m1 contains package p, module m2 exports package p to m1 - resolve(finder, "m1"); + assertThrows(ResolutionException.class, () -> resolve(finder, "m1")); } @@ -1671,7 +1692,8 @@ public class ConfigurationTest { * Test the scenario where a module contains a package p and reads * a module that also contains a package p. */ - public void testContainsPackageInSelfAndOther() { + @Test + void testContainsPackageInSelfAndOther() { ModuleDescriptor descriptor1 = newBuilder("m1") .requires("m2") .packages(Set.of("p")) @@ -1702,8 +1724,8 @@ public class ConfigurationTest { * Test the scenario where a module that exports a package that is also * exported by a module that it reads in a parent layer. */ - @Test(expectedExceptions = { ResolutionException.class }) - public void testExportSamePackageAsBootLayer() { + @Test + void testExportSamePackageAsBootLayer() { ModuleDescriptor descriptor = newBuilder("m1") .requires("java.base") .exports("java.lang") @@ -1714,14 +1736,15 @@ public class ConfigurationTest { Configuration bootConfiguration = ModuleLayer.boot().configuration(); // m1 contains package java.lang, java.base exports package java.lang to m1 - resolve(bootConfiguration, finder, "m1"); + assertThrows(ResolutionException.class, () -> resolve(bootConfiguration, finder, "m1")); } /** * Test "uses p.S" where p is contained in the same module. */ - public void testContainsService1() { + @Test + void testContainsService1() { ModuleDescriptor descriptor1 = newBuilder("m1") .packages(Set.of("p")) .uses("p.S") @@ -1739,8 +1762,8 @@ public class ConfigurationTest { /** * Test "uses p.S" where p is contained in a different module. */ - @Test(expectedExceptions = { ResolutionException.class }) - public void testContainsService2() { + @Test + void testContainsService2() { ModuleDescriptor descriptor1 = newBuilder("m1") .packages(Set.of("p")) .build(); @@ -1753,14 +1776,15 @@ public class ConfigurationTest { ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2); // m2 does not read a module that exports p - resolve(finder, "m2"); + assertThrows(ResolutionException.class, () -> resolve(finder, "m2")); } /** * Test "provides p.S" where p is contained in the same module. */ - public void testContainsService3() { + @Test + void testContainsService3() { ModuleDescriptor descriptor1 = newBuilder("m1") .packages(Set.of("p", "q")) .provides("p.S", List.of("q.S1")) @@ -1778,8 +1802,8 @@ public class ConfigurationTest { /** * Test "provides p.S" where p is contained in a different module. */ - @Test(expectedExceptions = { ResolutionException.class }) - public void testContainsService4() { + @Test + void testContainsService4() { ModuleDescriptor descriptor1 = newBuilder("m1") .packages(Set.of("p")) .build(); @@ -1792,46 +1816,182 @@ public class ConfigurationTest { ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2); // m2 does not read a module that exports p - resolve(finder, "m2"); + assertThrows(ResolutionException.class, () -> resolve(finder, "m2")); } + /** + * Test uses optional service. + * + * module m1 { requires static m2; uses p.S; } + */ + @Test + void testUsesOptionalService1() { + ModuleDescriptor descriptor1 = newBuilder("m1") + .requires(Set.of(Requires.Modifier.STATIC), "m2") + .uses("p.S") + .build(); + ModuleFinder finder = ModuleUtils.finderOf(descriptor1); + Configuration cf = resolve(finder, "m1"); + assertTrue(cf.modules().size() == 1); + assertTrue(cf.findModule("m1").isPresent()); + } + + /** + * Test uses optional service. + * + * module m1 { requires m2; uses p.S; } + * module m2 { requires static transitive m3; } + */ + @Test + void testUsesOptionalService2() { + ModuleDescriptor descriptor1 = newBuilder("m1") + .requires("m2") + .uses("p.S") + .build(); + ModuleDescriptor descriptor2 = newBuilder("m2") + .requires(Set.of(Requires.Modifier.STATIC, Requires.Modifier.TRANSITIVE), "m3") + .build(); + ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2); + Configuration cf = resolve(finder, "m1"); + assertTrue(cf.modules().size() == 2); + assertTrue(cf.findModule("m1").isPresent()); + assertTrue(cf.findModule("m2").isPresent()); + } + + /** + * Test uses optional service. + * + * module m1 { requires m2; uses p.S; } + * module m2 { requires transitive m3; } + * module m3 { requires static transitive m4; } + */ + @Test + void testUsesOptionalService3() { + ModuleDescriptor descriptor1 = newBuilder("m1") + .requires("m2") + .uses("p.S") + .build(); + ModuleDescriptor descriptor2 = newBuilder("m2") + .requires(Set.of(Requires.Modifier.TRANSITIVE), "m3") + .build(); + ModuleDescriptor descriptor3 = newBuilder("m3") + .requires(Set.of(Requires.Modifier.STATIC, Requires.Modifier.TRANSITIVE), "m4") + .build(); + ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2, descriptor3); + Configuration cf = resolve(finder, "m1"); + assertTrue(cf.modules().size() == 3); + assertTrue(cf.findModule("m1").isPresent()); + assertTrue(cf.findModule("m2").isPresent()); + assertTrue(cf.findModule("m3").isPresent()); + } + + /** + * Test provides optional service. + * + * module m1 { requires static m2; provides p.S with q.P; } + */ + @Test + void testProvidesOptionalService1() { + ModuleDescriptor descriptor1 = newBuilder("m1") + .requires(Set.of(Requires.Modifier.STATIC), "m2") + .provides("p.S", List.of("q.P")) + .build(); + ModuleFinder finder = ModuleUtils.finderOf(descriptor1); + Configuration cf = resolve(finder, "m1"); + assertTrue(cf.modules().size() == 1); + assertTrue(cf.findModule("m1").isPresent()); + } + + /** + * Test provides optional service. + * + * module m1 { requires m2; provides p.S with q.P; } + * module m2 { requires static transitive m3; } + */ + @Test + void testProvidesOptionalService2() { + ModuleDescriptor descriptor1 = newBuilder("m1") + .requires("m2") + .provides("p.S", List.of("q.P")) + .build(); + ModuleDescriptor descriptor2 = newBuilder("m2") + .requires(Set.of(Requires.Modifier.STATIC, Requires.Modifier.TRANSITIVE), "m3") + .build(); + ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2); + Configuration cf = resolve(finder, "m1"); + assertTrue(cf.modules().size() == 2); + assertTrue(cf.findModule("m1").isPresent()); + assertTrue(cf.findModule("m2").isPresent()); + } + + /** + * Test provides optional service. + * + * module m1 { requires m2; provides p.S with q.P; } + * module m2 { requires transitive m3; } + * module m3 { requires static transitive m4; } + */ + @Test + void testProvidesOptionalService3() { + ModuleDescriptor descriptor1 = newBuilder("m1") + .requires("m2") + .provides("p.S", List.of("q.P")) + .build(); + ModuleDescriptor descriptor2 = newBuilder("m2") + .requires(Set.of(Requires.Modifier.TRANSITIVE), "m3") + .build(); + ModuleDescriptor descriptor3 = newBuilder("m3") + .requires(Set.of(Requires.Modifier.STATIC, Requires.Modifier.TRANSITIVE), "m4") + .build(); + ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2, descriptor3); + Configuration cf = resolve(finder, "m1"); + assertTrue(cf.modules().size() == 3); + assertTrue(cf.findModule("m1").isPresent()); + assertTrue(cf.findModule("m2").isPresent()); + assertTrue(cf.findModule("m3").isPresent()); + } /** * Test "uses p.S" where p is not exported to the module. */ - @Test(expectedExceptions = { ResolutionException.class }) - public void testServiceTypePackageNotExported1() { + @Test + void testServiceTypePackageNotExported1() { ModuleDescriptor descriptor1 = newBuilder("m1") .uses("p.S") .build(); - ModuleFinder finder = ModuleUtils.finderOf(descriptor1); - // m1 does not read a module that exports p - resolve(finder, "m1"); + // m1 does not read a module that exports p to m1 + assertThrows(ResolutionException.class, () -> resolve(finder, "m1")); } - /** - * Test "provides p.S" where p is not exported to the module. + * Test "uses p.S" where p is not exported to the module. + * + * module m1 { requires m2; uses p.S; } + * module m2 { contains p; } */ - @Test(expectedExceptions = { ResolutionException.class }) - public void testServiceTypePackageNotExported2() { + @Test + void testServiceTypePackageNotExported2() { ModuleDescriptor descriptor1 = newBuilder("m1") - .provides("p.S", List.of("q.T")) + .requires("m2") + .uses("p.S") .build(); + ModuleDescriptor descriptor2 = newBuilder("m2") + .packages(Set.of("p")) + .build(); + ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2); - ModuleFinder finder = ModuleUtils.finderOf(descriptor1); - - // m1 does not read a module that exports p - resolve(finder, "m1"); + // m1 does not read a module that exports p to m1 + assertThrows(ResolutionException.class, () -> resolve(finder, "m1")); } /** * Test the empty configuration. */ - public void testEmptyConfiguration() { + @Test + void testEmptyConfiguration() { Configuration cf = Configuration.empty(); assertTrue(cf.parents().isEmpty()); @@ -1840,47 +2000,22 @@ public class ConfigurationTest { assertFalse(cf.findModule("java.base").isPresent()); } - - // platform specific modules - - @DataProvider(name = "platformmatch") - public Object[][] createPlatformMatches() { - return new Object[][]{ - - { "", "" }, - { "linux-arm", "" }, - { "linux-arm", "linux-arm" }, - - }; - - }; - - @DataProvider(name = "platformmismatch") - public Object[][] createBad() { - return new Object[][] { - - { "linux-x64", "linux-arm" }, - { "linux-x64", "windows-x64" }, - - }; - } - /** * Test creating a configuration containing platform specific modules. */ - @Test(dataProvider = "platformmatch") - public void testPlatformMatch(String s1, String s2) throws IOException { - + @ParameterizedTest + @CsvSource({",", "linux-aarch64,", "linux-aarch64,linux-aarch64"}) + void testPlatformMatch(String targetPlatform1, String targetPlatform2) throws IOException { ModuleDescriptor base = ModuleDescriptor.newModule("java.base").build(); Path system = writeModule(base, null); ModuleDescriptor descriptor1 = ModuleDescriptor.newModule("m1") .requires("m2") .build(); - Path dir1 = writeModule(descriptor1, s1); + Path dir1 = writeModule(descriptor1, targetPlatform1); ModuleDescriptor descriptor2 = ModuleDescriptor.newModule("m2").build(); - Path dir2 = writeModule(descriptor2, s2); + Path dir2 = writeModule(descriptor2, targetPlatform2); ModuleFinder finder = ModuleFinder.of(system, dir1, dir2); @@ -1893,43 +2028,45 @@ public class ConfigurationTest { } /** - * Test attempting to create a configuration with modules for different - * platforms. + * Test attempting to create a configuration with modules for different platforms. */ - @Test(dataProvider = "platformmismatch", - expectedExceptions = FindException.class ) - public void testPlatformMisMatch(String s1, String s2) throws IOException { - testPlatformMatch(s1, s2); + @ParameterizedTest + @CsvSource({"linux-x64,linux-aarch64", "linux-x64,windows-x64"}) + void testPlatformMisMatch(String targetPlatform1, String targetPlatform2) throws IOException { + assertThrows(FindException.class, + () -> testPlatformMatch(targetPlatform1, targetPlatform2)); } // no parents - @Test(expectedExceptions = { IllegalArgumentException.class }) - public void testResolveRequiresWithNoParents() { + @Test + void testResolveRequiresWithNoParents() { ModuleFinder empty = ModuleFinder.of(); - Configuration.resolve(empty, List.of(), empty, Set.of()); + assertThrows(IllegalArgumentException.class, + () -> Configuration.resolve(empty, List.of(), empty, Set.of())); } - @Test(expectedExceptions = { IllegalArgumentException.class }) - public void testResolveRequiresAndUsesWithNoParents() { + @Test + void testResolveRequiresAndUsesWithNoParents() { ModuleFinder empty = ModuleFinder.of(); - Configuration.resolveAndBind(empty, List.of(), empty, Set.of()); + assertThrows(IllegalArgumentException.class, + () -> Configuration.resolveAndBind(empty, List.of(), empty, Set.of())); } // parents with modules for specific platforms - @Test(dataProvider = "platformmatch") - public void testResolveRequiresWithCompatibleParents(String s1, String s2) - throws IOException - { - ModuleDescriptor base = ModuleDescriptor.newModule("java.base").build(); + @ParameterizedTest + @CsvSource({",", "linux-aarch64,", "linux-aarch64,linux-aarch64"}) + void testResolveRequiresWithCompatibleParents(String targetPlatform1, + String targetPlatform2) throws IOException { + ModuleDescriptor base = ModuleDescriptor.newModule("java.base").build(); Path system = writeModule(base, null); ModuleDescriptor descriptor1 = ModuleDescriptor.newModule("m1").build(); - Path dir1 = writeModule(descriptor1, s1); + Path dir1 = writeModule(descriptor1, targetPlatform1); ModuleDescriptor descriptor2 = ModuleDescriptor.newModule("m2").build(); - Path dir2 = writeModule(descriptor2, s2); + Path dir2 = writeModule(descriptor2, targetPlatform2); ModuleFinder finder1 = ModuleFinder.of(system, dir1); Configuration cf1 = resolve(finder1, "m1"); @@ -1945,163 +2082,160 @@ public class ConfigurationTest { } - @Test(dataProvider = "platformmismatch", - expectedExceptions = IllegalArgumentException.class ) - public void testResolveRequiresWithConflictingParents(String s1, String s2) - throws IOException - { - testResolveRequiresWithCompatibleParents(s1, s2); + @ParameterizedTest + @CsvSource({"linux-x64,linux-aarch64", "linux-x64,windows-x64"}) + void testResolveRequiresWithConflictingParents(String targetPlatform1, + String targetPlatform2) throws IOException { + assertThrows(IllegalArgumentException.class, + () -> testResolveRequiresWithCompatibleParents(targetPlatform1, targetPlatform2)); } // null handling - // finder1, finder2, roots - - - @Test(expectedExceptions = { NullPointerException.class }) - public void testResolveRequiresWithNull1() { - resolve((ModuleFinder)null, ModuleFinder.of()); + @Test + void testResolveRequiresWithNull1() { + assertThrows(NullPointerException.class, + () -> resolve((ModuleFinder) null, ModuleFinder.of())); } - @Test(expectedExceptions = { NullPointerException.class }) - public void testResolveRequiresWithNull2() { - resolve(ModuleFinder.of(), (ModuleFinder)null); + @Test + void testResolveRequiresWithNull2() { + assertThrows(NullPointerException.class, + () -> resolve(ModuleFinder.of(), (ModuleFinder) null)); } - @Test(expectedExceptions = { NullPointerException.class }) - public void testResolveRequiresWithNull3() { + @Test + void testResolveRequiresWithNull3() { Configuration empty = Configuration.empty(); - Configuration.resolve(null, List.of(empty), ModuleFinder.of(), Set.of()); + assertThrows(NullPointerException.class, + () -> Configuration.resolve(null, List.of(empty), ModuleFinder.of(), Set.of())); } - @Test(expectedExceptions = { NullPointerException.class }) - public void testResolveRequiresWithNull4() { + @Test + void testResolveRequiresWithNull4() { ModuleFinder empty = ModuleFinder.of(); - Configuration.resolve(empty, null, empty, Set.of()); + assertThrows(NullPointerException.class, + () -> Configuration.resolve(empty, null, empty, Set.of())); } - @Test(expectedExceptions = { NullPointerException.class }) - public void testResolveRequiresWithNull5() { + @Test + void testResolveRequiresWithNull5() { Configuration cf = ModuleLayer.boot().configuration(); - Configuration.resolve(ModuleFinder.of(), List.of(cf), null, Set.of()); + assertThrows(NullPointerException.class, + () -> Configuration.resolve(ModuleFinder.of(), List.of(cf), null, Set.of())); } - @Test(expectedExceptions = { NullPointerException.class }) - public void testResolveRequiresWithNull6() { + @Test + void testResolveRequiresWithNull6() { ModuleFinder empty = ModuleFinder.of(); Configuration cf = ModuleLayer.boot().configuration(); - Configuration.resolve(empty, List.of(cf), empty, null); + assertThrows(NullPointerException.class, + () -> Configuration.resolve(empty, List.of(cf), empty, null)); } - @Test(expectedExceptions = { NullPointerException.class }) - public void testResolveRequiresAndUsesWithNull1() { - resolveAndBind((ModuleFinder) null, ModuleFinder.of()); + @Test + void testResolveRequiresAndUsesWithNull1() { + assertThrows(NullPointerException.class, + () -> resolveAndBind((ModuleFinder) null, ModuleFinder.of())); } - @Test(expectedExceptions = { NullPointerException.class }) - public void testResolveRequiresAndUsesWithNull2() { - resolveAndBind(ModuleFinder.of(), (ModuleFinder) null); + @Test + void testResolveRequiresAndUsesWithNull2() { + assertThrows(NullPointerException.class, + () -> resolveAndBind(ModuleFinder.of(), (ModuleFinder) null)); } - @Test(expectedExceptions = { NullPointerException.class }) - public void testResolveRequiresAndUsesWithNull3() { + @Test + void testResolveRequiresAndUsesWithNull3() { Configuration empty = Configuration.empty(); - Configuration.resolveAndBind(null, List.of(empty), ModuleFinder.of(), Set.of()); + assertThrows(NullPointerException.class, + () -> Configuration.resolveAndBind(null, List.of(empty), ModuleFinder.of(), Set.of())); } - @Test(expectedExceptions = { NullPointerException.class }) - public void testResolveRequiresAndUsesWithNull4() { + @Test + void testResolveRequiresAndUsesWithNull4() { ModuleFinder empty = ModuleFinder.of(); - Configuration.resolveAndBind(empty, null, empty, Set.of()); + assertThrows(NullPointerException.class, + () -> Configuration.resolveAndBind(empty, null, empty, Set.of())); } - @Test(expectedExceptions = { NullPointerException.class }) - public void testResolveRequiresAndUsesWithNull5() { + @Test + void testResolveRequiresAndUsesWithNull5() { Configuration cf = ModuleLayer.boot().configuration(); - Configuration.resolveAndBind(ModuleFinder.of(), List.of(cf), null, Set.of()); + assertThrows(NullPointerException.class, + () -> Configuration.resolveAndBind(ModuleFinder.of(), List.of(cf), null, Set.of())); } - @Test(expectedExceptions = { NullPointerException.class }) - public void testResolveRequiresAndUsesWithNull6() { + @Test + void testResolveRequiresAndUsesWithNull6() { ModuleFinder empty = ModuleFinder.of(); Configuration cf = ModuleLayer.boot().configuration(); - Configuration.resolveAndBind(empty, List.of(cf), empty, null); + assertThrows(NullPointerException.class, + () -> Configuration.resolveAndBind(empty, List.of(cf), empty, null)); } - @Test(expectedExceptions = { NullPointerException.class }) - public void testFindModuleWithNull() { - Configuration.empty().findModule(null); + @Test + void testFindModuleWithNull() { + assertThrows(NullPointerException.class, () -> Configuration.empty().findModule(null)); } // unmodifiable collections - @DataProvider(name = "configurations") - public Object[][] configurations() { - // empty, boot, and custom configurations - return new Object[][] { - { Configuration.empty(), null }, - { ModuleLayer.boot().configuration(), null }, - { resolve(ModuleFinder.of()), null }, - }; + static Stream configurations() { + return Stream.of( + Configuration.empty(), + ModuleLayer.boot().configuration(), + resolve(ModuleFinder.of()) + ); } - @Test(dataProvider = "configurations", - expectedExceptions = { UnsupportedOperationException.class }) - public void testUnmodifiableParents1(Configuration cf, Object ignore) { - cf.parents().add(Configuration.empty()); + @ParameterizedTest + @MethodSource("configurations") + void testUnmodifiableParents(Configuration cf) { + assertThrows(UnsupportedOperationException.class, + () -> cf.parents().add(Configuration.empty())); + assertThrows(UnsupportedOperationException.class, + () -> cf.parents().remove(Configuration.empty())); } - @Test(dataProvider = "configurations", - expectedExceptions = { UnsupportedOperationException.class }) - public void testUnmodifiableParents2(Configuration cf, Object ignore) { - cf.parents().remove(Configuration.empty()); - } - - @Test(dataProvider = "configurations", - expectedExceptions = { UnsupportedOperationException.class }) - public void testUnmodifiableModules1(Configuration cf, Object ignore) { + @ParameterizedTest + @MethodSource("configurations") + void testUnmodifiableModules(Configuration cf) { ResolvedModule module = ModuleLayer.boot() .configuration() .findModule("java.base") .orElseThrow(); - cf.modules().add(module); - } - - @Test(dataProvider = "configurations", - expectedExceptions = { UnsupportedOperationException.class }) - public void testUnmodifiableModules2(Configuration cf, Object ignore) { - ResolvedModule module = ModuleLayer.boot() - .configuration() - .findModule("java.base") - .orElseThrow(); - cf.modules().remove(module); + assertThrows(UnsupportedOperationException.class, + () -> cf.modules().add(module)); + assertThrows(UnsupportedOperationException.class, + () -> cf.modules().remove(module)); } /** * Invokes parent.resolve(...) */ - private Configuration resolve(Configuration parent, - ModuleFinder before, - ModuleFinder after, - String... roots) { + private static Configuration resolve(Configuration parent, + ModuleFinder before, + ModuleFinder after, + String... roots) { return parent.resolve(before, after, Set.of(roots)); } - private Configuration resolve(Configuration parent, - ModuleFinder before, - String... roots) { + private static Configuration resolve(Configuration parent, + ModuleFinder before, + String... roots) { return resolve(parent, before, ModuleFinder.of(), roots); } - private Configuration resolve(ModuleFinder before, - ModuleFinder after, - String... roots) { + private static Configuration resolve(ModuleFinder before, + ModuleFinder after, + String... roots) { return resolve(Configuration.empty(), before, after, roots); } - private Configuration resolve(ModuleFinder before, - String... roots) { + private static Configuration resolve(ModuleFinder before, + String... roots) { return resolve(Configuration.empty(), before, roots); } @@ -2109,27 +2243,27 @@ public class ConfigurationTest { /** * Invokes parent.resolveAndBind(...) */ - private Configuration resolveAndBind(Configuration parent, - ModuleFinder before, - ModuleFinder after, - String... roots) { + private static Configuration resolveAndBind(Configuration parent, + ModuleFinder before, + ModuleFinder after, + String... roots) { return parent.resolveAndBind(before, after, Set.of(roots)); } - private Configuration resolveAndBind(Configuration parent, - ModuleFinder before, - String... roots) { + private static Configuration resolveAndBind(Configuration parent, + ModuleFinder before, + String... roots) { return resolveAndBind(parent, before, ModuleFinder.of(), roots); } - private Configuration resolveAndBind(ModuleFinder before, - ModuleFinder after, - String... roots) { + private static Configuration resolveAndBind(ModuleFinder before, + ModuleFinder after, + String... roots) { return resolveAndBind(Configuration.empty(), before, after, roots); } - private Configuration resolveAndBind(ModuleFinder before, - String... roots) { + private static Configuration resolveAndBind(ModuleFinder before, + String... roots) { return resolveAndBind(Configuration.empty(), before, roots); } @@ -2138,7 +2272,7 @@ public class ConfigurationTest { * Writes a module-info.class. If {@code targetPlatform} is not null then * it includes the ModuleTarget class file attribute with the target platform. */ - static Path writeModule(ModuleDescriptor descriptor, String targetPlatform) + private static Path writeModule(ModuleDescriptor descriptor, String targetPlatform) throws IOException { ModuleTarget target;