1 /*
   2  * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /**
  25  * @test
  26  * @modules java.base/jdk.internal.module
  27  * @build ModuleFinderTest
  28  * @run testng ModuleFinderTest
  29  * @summary Basic tests for java.lang.module.ModuleFinder
  30  */
  31 
  32 import java.io.File;
  33 import java.io.OutputStream;
  34 import java.lang.module.FindException;
  35 import java.lang.module.InvalidModuleDescriptorException;
  36 import java.lang.module.ModuleDescriptor;
  37 import java.lang.module.ModuleFinder;
  38 import java.lang.module.ModuleReference;
  39 import java.nio.file.Files;
  40 import java.nio.file.Path;
  41 import java.nio.file.Paths;
  42 import java.util.Optional;
  43 import java.util.Set;
  44 import java.util.jar.JarEntry;
  45 import java.util.jar.JarOutputStream;
  46 import java.util.stream.Collectors;
  47 
  48 import jdk.internal.module.ModuleInfoWriter;
  49 
  50 import org.testng.annotations.Test;
  51 import static org.testng.Assert.*;
  52 
  53 @Test
  54 public class ModuleFinderTest {
  55 
  56     private static final Path USER_DIR
  57         = Paths.get(System.getProperty("user.dir"));
  58 
  59 
  60     /**
  61      * Test ModuleFinder.ofSystem
  62      */
  63     public void testOfSystem() {
  64         ModuleFinder finder = ModuleFinder.ofSystem();
  65 
  66         assertTrue(finder.find("java.se").isPresent());
  67         assertTrue(finder.find("java.base").isPresent());
  68         assertFalse(finder.find("java.rhubarb").isPresent());
  69 
  70         Set<String> names = finder.findAll().stream()
  71             .map(ModuleReference::descriptor)
  72             .map(ModuleDescriptor::name)
  73             .collect(Collectors.toSet());
  74         assertTrue(names.contains("java.se"));
  75         assertTrue(names.contains("java.base"));
  76         assertFalse(names.contains("java.rhubarb"));
  77     }
  78 
  79 
  80     /**
  81      * Test ModuleFinder.of with no entries
  82      */
  83     public void testOfNoEntries() {
  84         ModuleFinder finder = ModuleFinder.of();
  85         assertTrue(finder.findAll().isEmpty());
  86         assertFalse(finder.find("java.rhubarb").isPresent());
  87     }
  88 
  89 
  90     /**
  91      * Test ModuleFinder.of with one directory of modules
  92      */
  93     public void testOfOneDirectory() throws Exception {
  94         Path dir = Files.createTempDirectory(USER_DIR, "mods");
  95         createExplodedModule(dir.resolve("m1"), "m1");
  96         createModularJar(dir.resolve("m2.jar"), "m2");
  97 
  98         ModuleFinder finder = ModuleFinder.of(dir);
  99         assertTrue(finder.findAll().size() == 2);
 100         assertTrue(finder.find("m1").isPresent());
 101         assertTrue(finder.find("m2").isPresent());
 102         assertFalse(finder.find("java.rhubarb").isPresent());
 103     }
 104 
 105 
 106     /**
 107      * Test ModuleFinder.of with two directories
 108      */
 109     public void testOfTwoDirectories() throws Exception {
 110         Path dir1 = Files.createTempDirectory(USER_DIR, "mods1");
 111         createExplodedModule(dir1.resolve("m1"), "m1@1.0");
 112         createModularJar(dir1.resolve("m2.jar"), "m2@1.0");
 113 
 114         Path dir2 = Files.createTempDirectory(USER_DIR, "mods2");
 115         createExplodedModule(dir2.resolve("m1"), "m1@2.0");
 116         createModularJar(dir2.resolve("m2.jar"), "m2@2.0");
 117         createExplodedModule(dir2.resolve("m3"), "m3");
 118         createModularJar(dir2.resolve("m4.jar"), "m4");
 119 
 120         ModuleFinder finder = ModuleFinder.of(dir1, dir2);
 121         assertTrue(finder.findAll().size() == 4);
 122         assertTrue(finder.find("m1").isPresent());
 123         assertTrue(finder.find("m2").isPresent());
 124         assertTrue(finder.find("m3").isPresent());
 125         assertTrue(finder.find("m4").isPresent());
 126         assertFalse(finder.find("java.rhubarb").isPresent());
 127 
 128         // check that m1@1.0 (and not m1@2.0) is found
 129         ModuleDescriptor m1 = finder.find("m1").get().descriptor();
 130         assertEquals(m1.version().get().toString(), "1.0");
 131 
 132         // check that m2@1.0 (and not m2@2.0) is found
 133         ModuleDescriptor m2 = finder.find("m2").get().descriptor();
 134         assertEquals(m2.version().get().toString(), "1.0");
 135     }
 136 
 137 
 138     /**
 139      * Test ModuleFinder.of with one JAR file
 140      */
 141     public void testOfOneJarFile() throws Exception {
 142         Path dir = Files.createTempDirectory(USER_DIR, "mods");
 143         Path jar1 = createModularJar(dir.resolve("m1.jar"), "m1");
 144 
 145         ModuleFinder finder = ModuleFinder.of(jar1);
 146         assertTrue(finder.findAll().size() == 1);
 147         assertTrue(finder.find("m1").isPresent());
 148         assertFalse(finder.find("java.rhubarb").isPresent());
 149     }
 150 
 151 
 152     /**
 153      * Test ModuleFinder.of with two JAR files
 154      */
 155     public void testOfTwoJarFiles() throws Exception {
 156         Path dir = Files.createTempDirectory(USER_DIR, "mods");
 157 
 158         Path jar1 = createModularJar(dir.resolve("m1.jar"), "m1");
 159         Path jar2 = createModularJar(dir.resolve("m2.jar"), "m2");
 160 
 161         ModuleFinder finder = ModuleFinder.of(jar1, jar2);
 162         assertTrue(finder.findAll().size() == 2);
 163         assertTrue(finder.find("m1").isPresent());
 164         assertTrue(finder.find("m2").isPresent());
 165         assertFalse(finder.find("java.rhubarb").isPresent());
 166     }
 167 
 168 
 169     /**
 170      * Test ModuleFinder.of with many JAR files
 171      */
 172     public void testOfManyJarFiles() throws Exception {
 173         Path dir = Files.createTempDirectory(USER_DIR, "mods");
 174 
 175         Path jar1 = createModularJar(dir.resolve("m1@1.0.jar"), "m1@1.0");
 176         Path jar2 = createModularJar(dir.resolve("m2@1.0.jar"), "m2");
 177         Path jar3 = createModularJar(dir.resolve("m1@2.0.jar"), "m1@2.0"); // shadowed
 178         Path jar4 = createModularJar(dir.resolve("m3@1.0.jar"), "m3");
 179 
 180         ModuleFinder finder = ModuleFinder.of(jar1, jar2, jar3, jar4);
 181         assertTrue(finder.findAll().size() == 3);
 182         assertTrue(finder.find("m1").isPresent());
 183         assertTrue(finder.find("m2").isPresent());
 184         assertTrue(finder.find("m3").isPresent());
 185         assertFalse(finder.find("java.rhubarb").isPresent());
 186 
 187         // check that m1@1.0 (and not m1@2.0) is found
 188         ModuleDescriptor m1 = finder.find("m1").get().descriptor();
 189         assertEquals(m1.version().get().toString(), "1.0");
 190     }
 191 
 192 
 193     /**
 194      * Test ModuleFinder.of with one exploded module.
 195      */
 196     public void testOfOneExplodedModule() throws Exception {
 197         Path dir = Files.createTempDirectory(USER_DIR, "mods");
 198         Path m1_dir = createExplodedModule(dir.resolve("m1"), "m1");
 199 
 200         ModuleFinder finder = ModuleFinder.of(m1_dir);
 201         assertTrue(finder.findAll().size() == 1);
 202         assertTrue(finder.find("m1").isPresent());
 203         assertFalse(finder.find("java.rhubarb").isPresent());
 204     }
 205 
 206 
 207     /**
 208      * Test ModuleFinder.of with two exploded modules.
 209      */
 210     public void testOfTwoExplodedModules() throws Exception {
 211         Path dir = Files.createTempDirectory(USER_DIR, "mods");
 212         Path m1_dir = createExplodedModule(dir.resolve("m1"), "m1");
 213         Path m2_dir = createExplodedModule(dir.resolve("m2"), "m2");
 214 
 215         ModuleFinder finder = ModuleFinder.of(m1_dir, m2_dir);
 216         assertTrue(finder.findAll().size() == 2);
 217         assertTrue(finder.find("m1").isPresent());
 218         assertTrue(finder.find("m2").isPresent());
 219         assertFalse(finder.find("java.rhubarb").isPresent());
 220     }
 221 
 222 
 223     /**
 224      * Test ModuleFinder.of with a mix of module directories and JAR files.
 225      */
 226     public void testOfMixDirectoriesAndJars() throws Exception {
 227 
 228         // directory with m1@1.0 and m2@1.0
 229         Path dir1 = Files.createTempDirectory(USER_DIR, "mods1");
 230         createExplodedModule(dir1.resolve("m1"), "m1@1.0");
 231         createModularJar(dir1.resolve("m2.jar"), "m2@1.0");
 232 
 233         // JAR files: m1@2.0, m2@2.0, m3@2.0, m4@2.0
 234         Path dir2 = Files.createTempDirectory(USER_DIR, "mods2");
 235         Path jar1 = createModularJar(dir2.resolve("m1.jar"), "m1@2.0");
 236         Path jar2 = createModularJar(dir2.resolve("m2.jar"), "m2@2.0");
 237         Path jar3 = createModularJar(dir2.resolve("m3.jar"), "m3@2.0");
 238         Path jar4 = createModularJar(dir2.resolve("m4.jar"), "m4@2.0");
 239 
 240         // directory with m3@3.0 and m4@3.0
 241         Path dir3 = Files.createTempDirectory(USER_DIR, "mods3");
 242         createExplodedModule(dir3.resolve("m3"), "m3@3.0");
 243         createModularJar(dir3.resolve("m4.jar"), "m4@3.0");
 244 
 245         // JAR files: m5 and m6
 246         Path dir4 = Files.createTempDirectory(USER_DIR, "mods4");
 247         Path jar5 = createModularJar(dir4.resolve("m5.jar"), "m5@4.0");
 248         Path jar6 = createModularJar(dir4.resolve("m6.jar"), "m6@4.0");
 249 
 250 
 251         ModuleFinder finder
 252             = ModuleFinder.of(dir1, jar1, jar2, jar3, jar4, dir3, jar5, jar6);
 253         assertTrue(finder.findAll().size() == 6);
 254         assertTrue(finder.find("m1").isPresent());
 255         assertTrue(finder.find("m2").isPresent());
 256         assertTrue(finder.find("m3").isPresent());
 257         assertTrue(finder.find("m4").isPresent());
 258         assertTrue(finder.find("m5").isPresent());
 259         assertTrue(finder.find("m6").isPresent());
 260         assertFalse(finder.find("java.rhubarb").isPresent());
 261 
 262         // m1 and m2 should be located in dir1
 263         ModuleDescriptor m1 = finder.find("m1").get().descriptor();
 264         assertEquals(m1.version().get().toString(), "1.0");
 265         ModuleDescriptor m2 = finder.find("m2").get().descriptor();
 266         assertEquals(m2.version().get().toString(), "1.0");
 267 
 268         // m3 and m4 should be located in JAR files
 269         ModuleDescriptor m3 = finder.find("m3").get().descriptor();
 270         assertEquals(m3.version().get().toString(), "2.0");
 271         ModuleDescriptor m4 = finder.find("m4").get().descriptor();
 272         assertEquals(m4.version().get().toString(), "2.0");
 273 
 274         // m5 and m6 should be located in JAR files
 275         ModuleDescriptor m5 = finder.find("m5").get().descriptor();
 276         assertEquals(m5.version().get().toString(), "4.0");
 277         ModuleDescriptor m6 = finder.find("m6").get().descriptor();
 278         assertEquals(m6.version().get().toString(), "4.0");
 279     }
 280 
 281 
 282     /**
 283      * Test ModuleFinder.of with a mix of module directories and exploded
 284      * modules.
 285      */
 286     public void testOfMixDirectoriesAndExplodedModules() throws Exception {
 287         // directory with m1@1.0 and m2@1.0
 288         Path dir1 = Files.createTempDirectory(USER_DIR, "mods1");
 289         createExplodedModule(dir1.resolve("m1"), "m1@1.0");
 290         createModularJar(dir1.resolve("m2.jar"), "m2@1.0");
 291 
 292         // exploded modules: m1@2.0, m2@2.0, m3@2.0, m4@2.0
 293         Path dir2 = Files.createTempDirectory(USER_DIR, "mods2");
 294         Path m1_dir = createExplodedModule(dir2.resolve("m1"), "m1@2.0");
 295         Path m2_dir = createExplodedModule(dir2.resolve("m2"), "m2@2.0");
 296         Path m3_dir = createExplodedModule(dir2.resolve("m3"), "m3@2.0");
 297         Path m4_dir = createExplodedModule(dir2.resolve("m4"), "m4@2.0");
 298 
 299         ModuleFinder finder = ModuleFinder.of(dir1, m1_dir, m2_dir, m3_dir, m4_dir);
 300         assertTrue(finder.findAll().size() == 4);
 301         assertTrue(finder.find("m1").isPresent());
 302         assertTrue(finder.find("m2").isPresent());
 303         assertTrue(finder.find("m3").isPresent());
 304         assertTrue(finder.find("m4").isPresent());
 305         assertFalse(finder.find("java.rhubarb").isPresent());
 306 
 307         // m1 and m2 should be located in dir1
 308         ModuleDescriptor m1 = finder.find("m1").get().descriptor();
 309         assertEquals(m1.version().get().toString(), "1.0");
 310         ModuleDescriptor m2 = finder.find("m2").get().descriptor();
 311         assertEquals(m2.version().get().toString(), "1.0");
 312 
 313         // m3 and m4 should be located in dir2
 314         ModuleDescriptor m3 = finder.find("m3").get().descriptor();
 315         assertEquals(m3.version().get().toString(), "2.0");
 316         ModuleDescriptor m4 = finder.find("m4").get().descriptor();
 317         assertEquals(m4.version().get().toString(), "2.0");
 318     }
 319 
 320 
 321     /**
 322      * Test ModuleFinder with a JAR file containing a mix of class and
 323      * non-class resources.
 324      */
 325     public void testOfOneJarFileWithResources() throws Exception {
 326         Path dir = Files.createTempDirectory(USER_DIR, "mods");
 327         Path jar = createModularJar(dir.resolve("m.jar"), "m",
 328                 "LICENSE",
 329                 "README",
 330                 "WEB-INF/tags",
 331                 "p/Type.class",
 332                 "p/resources/m.properties",
 333                 "q-/Type.class",                // not a legal package name
 334                 "q-/resources/m/properties");
 335 
 336         ModuleFinder finder = ModuleFinder.of(jar);
 337         Optional<ModuleReference> mref = finder.find("m");
 338         assertTrue(mref.isPresent(), "m1 not found");
 339 
 340         ModuleDescriptor descriptor = mref.get().descriptor();
 341 
 342         assertTrue(descriptor.packages().size() == 2);
 343         assertTrue(descriptor.packages().contains("p"));
 344         assertTrue(descriptor.packages().contains("p.resources"));
 345     }
 346 
 347 
 348     /**
 349      * Test ModuleFinder with an exploded module containing a mix of class
 350      * and non-class resources
 351      */
 352     public void testOfOneExplodedModuleWithResources() throws Exception {
 353         Path dir = Files.createTempDirectory(USER_DIR, "mods");
 354         Path m_dir = createExplodedModule(dir.resolve("m"), "m",
 355                 "LICENSE",
 356                 "README",
 357                 "WEB-INF/tags",
 358                 "p/Type.class",
 359                 "p/resources/m.properties",
 360                 "q-/Type.class",                 // not a legal package name
 361                 "q-/resources/m/properties");
 362 
 363         ModuleFinder finder = ModuleFinder.of(m_dir);
 364         Optional<ModuleReference> mref = finder.find("m");
 365         assertTrue(mref.isPresent(), "m not found");
 366 
 367         ModuleDescriptor descriptor = mref.get().descriptor();
 368 
 369         assertTrue(descriptor.packages().size() == 2);
 370         assertTrue(descriptor.packages().contains("p"));
 371         assertTrue(descriptor.packages().contains("p.resources"));
 372     }
 373 
 374 
 375     /**
 376      * Test ModuleFinder with a JAR file containing a .class file in the top
 377      * level directory.
 378      */
 379     public void testOfOneJarFileWithTopLevelClass() throws Exception {
 380         Path dir = Files.createTempDirectory(USER_DIR, "mods");
 381         Path jar = createModularJar(dir.resolve("m.jar"), "m", "Mojo.class");
 382 
 383         ModuleFinder finder = ModuleFinder.of(jar);
 384         try {
 385             finder.find("m");
 386             assertTrue(false);
 387         } catch (FindException e) {
 388             assertTrue(e.getCause() instanceof InvalidModuleDescriptorException);
 389             assertTrue(e.getCause().getMessage().contains("Mojo.class"));
 390         }
 391 
 392         finder = ModuleFinder.of(jar);
 393         try {
 394             finder.findAll();
 395             assertTrue(false);
 396         } catch (FindException e) {
 397             assertTrue(e.getCause() instanceof InvalidModuleDescriptorException);
 398             assertTrue(e.getCause().getMessage().contains("Mojo.class"));
 399         }
 400     }
 401 
 402     /**
 403      * Test ModuleFinder with a JAR file containing a .class file in the top
 404      * level directory.
 405      */
 406     public void testOfOneExplodedModuleWithTopLevelClass() throws Exception {
 407         Path dir = Files.createTempDirectory(USER_DIR, "mods");
 408         Path m_dir = createExplodedModule(dir.resolve("m"), "m", "Mojo.class");
 409 
 410         ModuleFinder finder = ModuleFinder.of(m_dir);
 411         try {
 412             finder.find("m");
 413             assertTrue(false);
 414         } catch (FindException e) {
 415             assertTrue(e.getCause() instanceof InvalidModuleDescriptorException);
 416             assertTrue(e.getCause().getMessage().contains("Mojo.class"));
 417         }
 418 
 419         finder = ModuleFinder.of(m_dir);
 420         try {
 421             finder.findAll();
 422             assertTrue(false);
 423         } catch (FindException e) {
 424             assertTrue(e.getCause() instanceof InvalidModuleDescriptorException);
 425             assertTrue(e.getCause().getMessage().contains("Mojo.class"));
 426         }
 427     }
 428 
 429 
 430     /**
 431      * Test ModuleFinder.of with a path to a file that does not exist.
 432      */
 433     public void testOfWithDoesNotExistEntry() throws Exception {
 434         Path dir1 = Files.createTempDirectory(USER_DIR, "mods1");
 435 
 436         Path dir2 = Files.createTempDirectory(USER_DIR, "mods2");
 437         createModularJar(dir2.resolve("m2.jar"), "m2@1.0");
 438 
 439         Files.delete(dir1);
 440 
 441         ModuleFinder finder = ModuleFinder.of(dir1, dir2);
 442 
 443         assertTrue(finder.find("m2").isPresent());
 444         assertTrue(finder.findAll().size() == 1);
 445         assertFalse(finder.find("java.rhubarb").isPresent());
 446     }
 447 
 448 
 449     /**
 450      * Test ModuleFinder.of with a file path to an unrecognized file type.
 451      */
 452     public void testOfWithUnrecognizedEntry() throws Exception {
 453         Path dir = Files.createTempDirectory(USER_DIR, "mods");
 454         Path mod = Files.createTempFile(dir, "m", ".junk");
 455 
 456         ModuleFinder finder = ModuleFinder.of(mod);
 457         try {
 458             finder.find("java.rhubarb");
 459             assertTrue(false);
 460         } catch (FindException e) {
 461             // expected
 462         }
 463 
 464         finder = ModuleFinder.of(mod);
 465         try {
 466             finder.findAll();
 467             assertTrue(false);
 468         } catch (FindException e) {
 469             // expected
 470         }
 471     }
 472 
 473 
 474     /**
 475      * Test ModuleFinder.of with a file path to a directory containing a file
 476      * that will not be recognized as a module.
 477      */
 478     public void testOfWithUnrecognizedEntryInDirectory1() throws Exception {
 479         Path dir = Files.createTempDirectory(USER_DIR, "mods");
 480         Files.createTempFile(dir, "m", ".junk");
 481 
 482         ModuleFinder finder = ModuleFinder.of(dir);
 483         assertFalse(finder.find("java.rhubarb").isPresent());
 484 
 485         finder = ModuleFinder.of(dir);
 486         assertTrue(finder.findAll().isEmpty());
 487     }
 488 
 489 
 490     /**
 491      * Test ModuleFinder.of with a file path to a directory containing a file
 492      * that will not be recognized as a module.
 493      */
 494     public void testOfWithUnrecognizedEntryInDirectory2() throws Exception {
 495         Path dir = Files.createTempDirectory(USER_DIR, "mods");
 496         createModularJar(dir.resolve("m1.jar"), "m1");
 497         Files.createTempFile(dir, "m2", ".junk");
 498 
 499         ModuleFinder finder = ModuleFinder.of(dir);
 500         assertTrue(finder.find("m1").isPresent());
 501         assertFalse(finder.find("m2").isPresent());
 502 
 503         finder = ModuleFinder.of(dir);
 504         assertTrue(finder.findAll().size() == 1);
 505     }
 506 
 507 
 508     /**
 509      * Test ModuleFinder.of with a directory that contains two
 510      * versions of the same module
 511      */
 512     public void testOfDuplicateModulesInDirectory() throws Exception {
 513         Path dir = Files.createTempDirectory(USER_DIR, "mods");
 514         createModularJar(dir.resolve("m1@1.0.jar"), "m1");
 515         createModularJar(dir.resolve("m1@2.0.jar"), "m1");
 516 
 517         ModuleFinder finder = ModuleFinder.of(dir);
 518         try {
 519             finder.find("m1");
 520             assertTrue(false);
 521         } catch (FindException expected) { }
 522 
 523         finder = ModuleFinder.of(dir);
 524         try {
 525             finder.findAll();
 526             assertTrue(false);
 527         } catch (FindException expected) { }
 528     }
 529 
 530 
 531     /**
 532      * Test ModuleFinder.of with a truncated module-info.class
 533      */
 534     public void testOfWithTruncatedModuleInfo() throws Exception {
 535         Path dir = Files.createTempDirectory(USER_DIR, "mods");
 536 
 537         // create an empty <dir>/rhubarb/module-info.class
 538         Path subdir = Files.createDirectory(dir.resolve("rhubarb"));
 539         Files.createFile(subdir.resolve("module-info.class"));
 540 
 541         ModuleFinder finder = ModuleFinder.of(dir);
 542         try {
 543             finder.find("rhubarb");
 544             assertTrue(false);
 545         } catch (FindException e) {
 546             assertTrue(e.getCause() instanceof InvalidModuleDescriptorException);
 547         }
 548 
 549         finder = ModuleFinder.of(dir);
 550         try {
 551             finder.findAll();
 552             assertTrue(false);
 553         } catch (FindException e) {
 554             assertTrue(e.getCause() instanceof InvalidModuleDescriptorException);
 555         }
 556     }
 557 
 558 
 559     /**
 560      * Test ModuleFinder.compose with no module finders
 561      */
 562     public void testComposeOfNone() throws Exception {
 563         ModuleFinder finder = ModuleFinder.of();
 564         assertTrue(finder.findAll().isEmpty());
 565         assertFalse(finder.find("java.rhubarb").isPresent());
 566     }
 567 
 568 
 569     /**
 570      * Test ModuleFinder.compose with one module finder
 571      */
 572     public void testComposeOfOne() throws Exception {
 573         Path dir = Files.createTempDirectory(USER_DIR, "mods");
 574         createModularJar(dir.resolve("m1.jar"), "m1");
 575         createModularJar(dir.resolve("m2.jar"), "m2");
 576 
 577         ModuleFinder finder1 = ModuleFinder.of(dir);
 578 
 579         ModuleFinder finder = ModuleFinder.compose(finder1);
 580         assertTrue(finder.findAll().size() == 2);
 581         assertTrue(finder.find("m1").isPresent());
 582         assertTrue(finder.find("m2").isPresent());
 583         assertFalse(finder.find("java.rhubarb").isPresent());
 584     }
 585 
 586 
 587     /**
 588      * Test ModuleFinder.compose with two module finders
 589      */
 590     public void testComposeOfTwo() throws Exception {
 591         Path dir1 = Files.createTempDirectory(USER_DIR, "mods1");
 592         createModularJar(dir1.resolve("m1.jar"), "m1@1.0");
 593         createModularJar(dir1.resolve("m2.jar"), "m2@1.0");
 594 
 595         Path dir2 = Files.createTempDirectory(USER_DIR, "mods2");
 596         createModularJar(dir2.resolve("m1.jar"), "m1@2.0");
 597         createModularJar(dir2.resolve("m2.jar"), "m2@2.0");
 598         createModularJar(dir2.resolve("m3.jar"), "m3");
 599         createModularJar(dir2.resolve("m4.jar"), "m4");
 600 
 601         ModuleFinder finder1 = ModuleFinder.of(dir1);
 602         ModuleFinder finder2 = ModuleFinder.of(dir2);
 603 
 604         ModuleFinder finder = ModuleFinder.compose(finder1, finder2);
 605         assertTrue(finder.findAll().size() == 4);
 606         assertTrue(finder.find("m1").isPresent());
 607         assertTrue(finder.find("m2").isPresent());
 608         assertTrue(finder.find("m3").isPresent());
 609         assertTrue(finder.find("m4").isPresent());
 610         assertFalse(finder.find("java.rhubarb").isPresent());
 611 
 612         // check that m1@1.0 is found
 613         ModuleDescriptor m1 = finder.find("m1").get().descriptor();
 614         assertEquals(m1.version().get().toString(), "1.0");
 615 
 616         // check that m2@1.0 is found
 617         ModuleDescriptor m2 = finder.find("m2").get().descriptor();
 618         assertEquals(m2.version().get().toString(), "1.0");
 619     }
 620 
 621 
 622     /**
 623      * Test ModuleFinder.compose with three module finders
 624      */
 625     public void testComposeOfThree() throws Exception {
 626         Path dir1 = Files.createTempDirectory(USER_DIR, "mods1");
 627         createModularJar(dir1.resolve("m1.jar"), "m1@1.0");
 628         createModularJar(dir1.resolve("m2.jar"), "m2@1.0");
 629 
 630         Path dir2 = Files.createTempDirectory(USER_DIR, "mods2");
 631         createModularJar(dir2.resolve("m1.jar"), "m1@2.0");
 632         createModularJar(dir2.resolve("m2.jar"), "m2@2.0");
 633         createModularJar(dir2.resolve("m3.jar"), "m3@2.0");
 634         createModularJar(dir2.resolve("m4.jar"), "m4@2.0");
 635 
 636         Path dir3 = Files.createTempDirectory(USER_DIR, "mods3");
 637         createModularJar(dir3.resolve("m3.jar"), "m3@3.0");
 638         createModularJar(dir3.resolve("m4.jar"), "m4@3.0");
 639         createModularJar(dir3.resolve("m5.jar"), "m5");
 640         createModularJar(dir3.resolve("m6.jar"), "m6");
 641 
 642         ModuleFinder finder1 = ModuleFinder.of(dir1);
 643         ModuleFinder finder2 = ModuleFinder.of(dir2);
 644         ModuleFinder finder3 = ModuleFinder.of(dir3);
 645 
 646         ModuleFinder finder = ModuleFinder.compose(finder1, finder2, finder3);
 647         assertTrue(finder.findAll().size() == 6);
 648         assertTrue(finder.find("m1").isPresent());
 649         assertTrue(finder.find("m2").isPresent());
 650         assertTrue(finder.find("m3").isPresent());
 651         assertTrue(finder.find("m4").isPresent());
 652         assertTrue(finder.find("m5").isPresent());
 653         assertTrue(finder.find("m6").isPresent());
 654         assertFalse(finder.find("java.rhubarb").isPresent());
 655 
 656         // check that m1@1.0 is found
 657         ModuleDescriptor m1 = finder.find("m1").get().descriptor();
 658         assertEquals(m1.version().get().toString(), "1.0");
 659 
 660         // check that m2@1.0 is found
 661         ModuleDescriptor m2 = finder.find("m2").get().descriptor();
 662         assertEquals(m2.version().get().toString(), "1.0");
 663 
 664         // check that m3@2.0 is found
 665         ModuleDescriptor m3 = finder.find("m3").get().descriptor();
 666         assertEquals(m3.version().get().toString(), "2.0");
 667 
 668         // check that m4@2.0 is found
 669         ModuleDescriptor m4 = finder.find("m4").get().descriptor();
 670         assertEquals(m4.version().get().toString(), "2.0");
 671     }
 672 
 673 
 674     /**
 675      * Test null handling
 676      */
 677     public void testNulls() {
 678 
 679         // ofSystem
 680         try {
 681             ModuleFinder.ofSystem().find(null);
 682             assertTrue(false);
 683         } catch (NullPointerException expected) { }
 684 
 685         // of
 686         Path dir = Paths.get("d");
 687         try {
 688             ModuleFinder.of().find(null);
 689             assertTrue(false);
 690         } catch (NullPointerException expected) { }
 691         try {
 692             ModuleFinder.of((Path)null);
 693             assertTrue(false);
 694         } catch (NullPointerException expected) { }
 695         try {
 696             ModuleFinder.of((Path[])null);
 697             assertTrue(false);
 698         } catch (NullPointerException expected) { }
 699         try {
 700             ModuleFinder.of(dir, null);
 701             assertTrue(false);
 702         } catch (NullPointerException expected) { }
 703         try {
 704             ModuleFinder.of(null, dir);
 705             assertTrue(false);
 706         } catch (NullPointerException expected) { }
 707 
 708         // compose
 709         ModuleFinder finder = ModuleFinder.of();
 710         try {
 711             ModuleFinder.compose((ModuleFinder)null);
 712             assertTrue(false);
 713         } catch (NullPointerException expected) { }
 714         try {
 715             ModuleFinder.compose((ModuleFinder[])null);
 716             assertTrue(false);
 717         } catch (NullPointerException expected) { }
 718         try {
 719             ModuleFinder.compose(finder, null);
 720             assertTrue(false);
 721         } catch (NullPointerException expected) { }
 722         try {
 723             ModuleFinder.compose(null, finder);
 724             assertTrue(false);
 725         } catch (NullPointerException expected) { }
 726 
 727     }
 728 
 729 
 730     /**
 731      * Parses a string of the form {@code name[@version]} and returns a
 732      * ModuleDescriptor with that name and version. The ModuleDescriptor
 733      * will have a requires on java.base.
 734      */
 735     static ModuleDescriptor newModuleDescriptor(String mid) {
 736         String mn;
 737         String vs;
 738         int i = mid.indexOf("@");
 739         if (i == -1) {
 740             mn = mid;
 741             vs = null;
 742         } else {
 743             mn = mid.substring(0, i);
 744             vs = mid.substring(i+1);
 745         }
 746         ModuleDescriptor.Builder builder
 747             = ModuleDescriptor.newModule(mn).requires("java.base");
 748         if (vs != null)
 749             builder.version(vs);
 750         return builder.build();
 751     }
 752 
 753     /**
 754      * Creates an exploded module in the given directory and containing a
 755      * module descriptor with the given module name/version.
 756      */
 757     static Path createExplodedModule(Path dir, String mid, String... entries)
 758         throws Exception
 759     {
 760         ModuleDescriptor descriptor = newModuleDescriptor(mid);
 761         Files.createDirectories(dir);
 762         Path mi = dir.resolve("module-info.class");
 763         try (OutputStream out = Files.newOutputStream(mi)) {
 764             ModuleInfoWriter.write(descriptor, out);
 765         }
 766 
 767         for (String entry : entries) {
 768             Path file = dir.resolve(entry.replace('/', File.separatorChar));
 769             Files.createDirectories(file.getParent());
 770             Files.createFile(file);
 771         }
 772 
 773         return dir;
 774     }
 775 
 776     /**
 777      * Creates a JAR file with the given file path and containing a module
 778      * descriptor with the given module name/version.
 779      */
 780     static Path createModularJar(Path file, String mid, String ... entries)
 781         throws Exception
 782     {
 783         ModuleDescriptor descriptor = newModuleDescriptor(mid);
 784         try (OutputStream out = Files.newOutputStream(file)) {
 785             try (JarOutputStream jos = new JarOutputStream(out)) {
 786 
 787                 JarEntry je = new JarEntry("module-info.class");
 788                 jos.putNextEntry(je);
 789                 ModuleInfoWriter.write(descriptor, jos);
 790                 jos.closeEntry();
 791 
 792                 for (String entry : entries) {
 793                     je = new JarEntry(entry);
 794                     jos.putNextEntry(je);
 795                     jos.closeEntry();
 796                 }
 797             }
 798 
 799         }
 800         return file;
 801     }
 802 
 803 }
 804