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 ModuleModule 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         }
 390 
 391         finder = ModuleFinder.of(jar);
 392         try {
 393             finder.findAll();
 394             assertTrue(false);
 395         } catch (FindException e) {
 396             assertTrue(e.getCause() instanceof InvalidModuleDescriptorException);
 397         }
 398     }
 399 
 400     /**
 401      * Test ModuleModule with a JAR file containing a .class file in the top
 402      * level directory.
 403      */
 404     public void testOfOneExplodedModuleWithTopLevelClass() throws Exception {
 405         Path dir = Files.createTempDirectory(USER_DIR, "mods");
 406         Path m_dir = createExplodedModule(dir.resolve("m"), "m", "Mojo.class");
 407 
 408         ModuleFinder finder = ModuleFinder.of(m_dir);
 409         try {
 410             finder.find("m");
 411             assertTrue(false);
 412         } catch (FindException e) {
 413             assertTrue(e.getCause() instanceof InvalidModuleDescriptorException);
 414         }
 415 
 416         finder = ModuleFinder.of(m_dir);
 417         try {
 418             finder.findAll();
 419             assertTrue(false);
 420         } catch (FindException e) {
 421             assertTrue(e.getCause() instanceof InvalidModuleDescriptorException);
 422         }
 423     }
 424 
 425 
 426     /**
 427      * Test ModuleFinder.of with a path to a file that does not exist.
 428      */
 429     public void testOfWithDoesNotExistEntry() throws Exception {
 430         Path dir1 = Files.createTempDirectory(USER_DIR, "mods1");
 431 
 432         Path dir2 = Files.createTempDirectory(USER_DIR, "mods2");
 433         createModularJar(dir2.resolve("m2.jar"), "m2@1.0");
 434 
 435         Files.delete(dir1);
 436 
 437         ModuleFinder finder = ModuleFinder.of(dir1, dir2);
 438 
 439         assertTrue(finder.find("m2").isPresent());
 440         assertTrue(finder.findAll().size() == 1);
 441         assertFalse(finder.find("java.rhubarb").isPresent());
 442     }
 443 
 444 
 445     /**
 446      * Test ModuleFinder.of with a file path to an unrecognized file type.
 447      */
 448     public void testOfWithUnrecognizedEntry() throws Exception {
 449         Path dir = Files.createTempDirectory(USER_DIR, "mods");
 450         Path mod = Files.createTempFile(dir, "m", ".junk");
 451 
 452         ModuleFinder finder = ModuleFinder.of(mod);
 453         try {
 454             finder.find("java.rhubarb");
 455             assertTrue(false);
 456         } catch (FindException e) {
 457             // expected
 458         }
 459 
 460         finder = ModuleFinder.of(mod);
 461         try {
 462             finder.findAll();
 463             assertTrue(false);
 464         } catch (FindException e) {
 465             // expected
 466         }
 467     }
 468 
 469 
 470     /**
 471      * Test ModuleFinder.of with a file path to a directory containing a file
 472      * that will not be recognized as a module.
 473      */
 474     public void testOfWithUnrecognizedEntryInDirectory1() throws Exception {
 475         Path dir = Files.createTempDirectory(USER_DIR, "mods");
 476         Files.createTempFile(dir, "m", ".junk");
 477 
 478         ModuleFinder finder = ModuleFinder.of(dir);
 479         assertFalse(finder.find("java.rhubarb").isPresent());
 480 
 481         finder = ModuleFinder.of(dir);
 482         assertTrue(finder.findAll().isEmpty());
 483     }
 484 
 485 
 486     /**
 487      * Test ModuleFinder.of with a file path to a directory containing a file
 488      * that will not be recognized as a module.
 489      */
 490     public void testOfWithUnrecognizedEntryInDirectory2() throws Exception {
 491         Path dir = Files.createTempDirectory(USER_DIR, "mods");
 492         createModularJar(dir.resolve("m1.jar"), "m1");
 493         Files.createTempFile(dir, "m2", ".junk");
 494 
 495         ModuleFinder finder = ModuleFinder.of(dir);
 496         assertTrue(finder.find("m1").isPresent());
 497         assertFalse(finder.find("m2").isPresent());
 498 
 499         finder = ModuleFinder.of(dir);
 500         assertTrue(finder.findAll().size() == 1);
 501     }
 502 
 503 
 504     /**
 505      * Test ModuleFinder.of with a directory that contains two
 506      * versions of the same module
 507      */
 508     public void testOfDuplicateModulesInDirectory() throws Exception {
 509         Path dir = Files.createTempDirectory(USER_DIR, "mods");
 510         createModularJar(dir.resolve("m1@1.0.jar"), "m1");
 511         createModularJar(dir.resolve("m1@2.0.jar"), "m1");
 512 
 513         ModuleFinder finder = ModuleFinder.of(dir);
 514         try {
 515             finder.find("m1");
 516             assertTrue(false);
 517         } catch (FindException expected) { }
 518 
 519         finder = ModuleFinder.of(dir);
 520         try {
 521             finder.findAll();
 522             assertTrue(false);
 523         } catch (FindException expected) { }
 524     }
 525 
 526 
 527     /**
 528      * Test ModuleFinder.of with a truncated module-info.class
 529      */
 530     public void testOfWithTruncatedModuleInfo() throws Exception {
 531         Path dir = Files.createTempDirectory(USER_DIR, "mods");
 532 
 533         // create an empty <dir>/rhubarb/module-info.class
 534         Path subdir = Files.createDirectory(dir.resolve("rhubarb"));
 535         Files.createFile(subdir.resolve("module-info.class"));
 536 
 537         ModuleFinder finder = ModuleFinder.of(dir);
 538         try {
 539             finder.find("rhubarb");
 540             assertTrue(false);
 541         } catch (FindException e) {
 542             assertTrue(e.getCause() instanceof InvalidModuleDescriptorException);
 543         }
 544 
 545         finder = ModuleFinder.of(dir);
 546         try {
 547             finder.findAll();
 548             assertTrue(false);
 549         } catch (FindException e) {
 550             assertTrue(e.getCause() instanceof InvalidModuleDescriptorException);
 551         }
 552     }
 553 
 554 
 555     /**
 556      * Test ModuleFinder.compose with no module finders
 557      */
 558     public void testComposeOfNone() throws Exception {
 559         ModuleFinder finder = ModuleFinder.of();
 560         assertTrue(finder.findAll().isEmpty());
 561         assertFalse(finder.find("java.rhubarb").isPresent());
 562     }
 563 
 564 
 565     /**
 566      * Test ModuleFinder.compose with one module finder
 567      */
 568     public void testComposeOfOne() throws Exception {
 569         Path dir = Files.createTempDirectory(USER_DIR, "mods");
 570         createModularJar(dir.resolve("m1.jar"), "m1");
 571         createModularJar(dir.resolve("m2.jar"), "m2");
 572 
 573         ModuleFinder finder1 = ModuleFinder.of(dir);
 574 
 575         ModuleFinder finder = ModuleFinder.compose(finder1);
 576         assertTrue(finder.findAll().size() == 2);
 577         assertTrue(finder.find("m1").isPresent());
 578         assertTrue(finder.find("m2").isPresent());
 579         assertFalse(finder.find("java.rhubarb").isPresent());
 580     }
 581 
 582 
 583     /**
 584      * Test ModuleFinder.compose with two module finders
 585      */
 586     public void testComposeOfTwo() throws Exception {
 587         Path dir1 = Files.createTempDirectory(USER_DIR, "mods1");
 588         createModularJar(dir1.resolve("m1.jar"), "m1@1.0");
 589         createModularJar(dir1.resolve("m2.jar"), "m2@1.0");
 590 
 591         Path dir2 = Files.createTempDirectory(USER_DIR, "mods2");
 592         createModularJar(dir2.resolve("m1.jar"), "m1@2.0");
 593         createModularJar(dir2.resolve("m2.jar"), "m2@2.0");
 594         createModularJar(dir2.resolve("m3.jar"), "m3");
 595         createModularJar(dir2.resolve("m4.jar"), "m4");
 596 
 597         ModuleFinder finder1 = ModuleFinder.of(dir1);
 598         ModuleFinder finder2 = ModuleFinder.of(dir2);
 599 
 600         ModuleFinder finder = ModuleFinder.compose(finder1, finder2);
 601         assertTrue(finder.findAll().size() == 4);
 602         assertTrue(finder.find("m1").isPresent());
 603         assertTrue(finder.find("m2").isPresent());
 604         assertTrue(finder.find("m3").isPresent());
 605         assertTrue(finder.find("m4").isPresent());
 606         assertFalse(finder.find("java.rhubarb").isPresent());
 607 
 608         // check that m1@1.0 is found
 609         ModuleDescriptor m1 = finder.find("m1").get().descriptor();
 610         assertEquals(m1.version().get().toString(), "1.0");
 611 
 612         // check that m2@1.0 is found
 613         ModuleDescriptor m2 = finder.find("m2").get().descriptor();
 614         assertEquals(m2.version().get().toString(), "1.0");
 615     }
 616 
 617 
 618     /**
 619      * Test ModuleFinder.compose with three module finders
 620      */
 621     public void testComposeOfThree() throws Exception {
 622         Path dir1 = Files.createTempDirectory(USER_DIR, "mods1");
 623         createModularJar(dir1.resolve("m1.jar"), "m1@1.0");
 624         createModularJar(dir1.resolve("m2.jar"), "m2@1.0");
 625 
 626         Path dir2 = Files.createTempDirectory(USER_DIR, "mods2");
 627         createModularJar(dir2.resolve("m1.jar"), "m1@2.0");
 628         createModularJar(dir2.resolve("m2.jar"), "m2@2.0");
 629         createModularJar(dir2.resolve("m3.jar"), "m3@2.0");
 630         createModularJar(dir2.resolve("m4.jar"), "m4@2.0");
 631 
 632         Path dir3 = Files.createTempDirectory(USER_DIR, "mods3");
 633         createModularJar(dir3.resolve("m3.jar"), "m3@3.0");
 634         createModularJar(dir3.resolve("m4.jar"), "m4@3.0");
 635         createModularJar(dir3.resolve("m5.jar"), "m5");
 636         createModularJar(dir3.resolve("m6.jar"), "m6");
 637 
 638         ModuleFinder finder1 = ModuleFinder.of(dir1);
 639         ModuleFinder finder2 = ModuleFinder.of(dir2);
 640         ModuleFinder finder3 = ModuleFinder.of(dir3);
 641 
 642         ModuleFinder finder = ModuleFinder.compose(finder1, finder2, finder3);
 643         assertTrue(finder.findAll().size() == 6);
 644         assertTrue(finder.find("m1").isPresent());
 645         assertTrue(finder.find("m2").isPresent());
 646         assertTrue(finder.find("m3").isPresent());
 647         assertTrue(finder.find("m4").isPresent());
 648         assertTrue(finder.find("m5").isPresent());
 649         assertTrue(finder.find("m6").isPresent());
 650         assertFalse(finder.find("java.rhubarb").isPresent());
 651 
 652         // check that m1@1.0 is found
 653         ModuleDescriptor m1 = finder.find("m1").get().descriptor();
 654         assertEquals(m1.version().get().toString(), "1.0");
 655 
 656         // check that m2@1.0 is found
 657         ModuleDescriptor m2 = finder.find("m2").get().descriptor();
 658         assertEquals(m2.version().get().toString(), "1.0");
 659 
 660         // check that m3@2.0 is found
 661         ModuleDescriptor m3 = finder.find("m3").get().descriptor();
 662         assertEquals(m3.version().get().toString(), "2.0");
 663 
 664         // check that m4@2.0 is found
 665         ModuleDescriptor m4 = finder.find("m4").get().descriptor();
 666         assertEquals(m4.version().get().toString(), "2.0");
 667     }
 668 
 669 
 670     /**
 671      * Test null handling
 672      */
 673     public void testNulls() {
 674 
 675         // ofSystem
 676         try {
 677             ModuleFinder.ofSystem().find(null);
 678             assertTrue(false);
 679         } catch (NullPointerException expected) { }
 680 
 681         // of
 682         Path dir = Paths.get("d");
 683         try {
 684             ModuleFinder.of().find(null);
 685             assertTrue(false);
 686         } catch (NullPointerException expected) { }
 687         try {
 688             ModuleFinder.of((Path)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(dir, null);
 697             assertTrue(false);
 698         } catch (NullPointerException expected) { }
 699         try {
 700             ModuleFinder.of(null, dir);
 701             assertTrue(false);
 702         } catch (NullPointerException expected) { }
 703 
 704         // compose
 705         ModuleFinder finder = ModuleFinder.of();
 706         try {
 707             ModuleFinder.compose((ModuleFinder)null);
 708             assertTrue(false);
 709         } catch (NullPointerException expected) { }
 710         try {
 711             ModuleFinder.compose((ModuleFinder[])null);
 712             assertTrue(false);
 713         } catch (NullPointerException expected) { }
 714         try {
 715             ModuleFinder.compose(finder, null);
 716             assertTrue(false);
 717         } catch (NullPointerException expected) { }
 718         try {
 719             ModuleFinder.compose(null, finder);
 720             assertTrue(false);
 721         } catch (NullPointerException expected) { }
 722 
 723     }
 724 
 725 
 726     /**
 727      * Parses a string of the form {@code name[@version]} and returns a
 728      * ModuleDescriptor with that name and version. The ModuleDescriptor
 729      * will have a requires on java.base.
 730      */
 731     static ModuleDescriptor newModuleDescriptor(String mid) {
 732         String mn;
 733         String vs;
 734         int i = mid.indexOf("@");
 735         if (i == -1) {
 736             mn = mid;
 737             vs = null;
 738         } else {
 739             mn = mid.substring(0, i);
 740             vs = mid.substring(i+1);
 741         }
 742         ModuleDescriptor.Builder builder
 743             = ModuleDescriptor.newModule(mn).requires("java.base");
 744         if (vs != null)
 745             builder.version(vs);
 746         return builder.build();
 747     }
 748 
 749     /**
 750      * Creates an exploded module in the given directory and containing a
 751      * module descriptor with the given module name/version.
 752      */
 753     static Path createExplodedModule(Path dir, String mid, String... entries)
 754         throws Exception
 755     {
 756         ModuleDescriptor descriptor = newModuleDescriptor(mid);
 757         Files.createDirectories(dir);
 758         Path mi = dir.resolve("module-info.class");
 759         try (OutputStream out = Files.newOutputStream(mi)) {
 760             ModuleInfoWriter.write(descriptor, out);
 761         }
 762 
 763         for (String entry : entries) {
 764             Path file = dir.resolve(entry.replace('/', File.separatorChar));
 765             Files.createDirectories(file.getParent());
 766             Files.createFile(file);
 767         }
 768 
 769         return dir;
 770     }
 771 
 772     /**
 773      * Creates a JAR file with the given file path and containing a module
 774      * descriptor with the given module name/version.
 775      */
 776     static Path createModularJar(Path file, String mid, String ... entries)
 777         throws Exception
 778     {
 779         ModuleDescriptor descriptor = newModuleDescriptor(mid);
 780         try (OutputStream out = Files.newOutputStream(file)) {
 781             try (JarOutputStream jos = new JarOutputStream(out)) {
 782 
 783                 JarEntry je = new JarEntry("module-info.class");
 784                 jos.putNextEntry(je);
 785                 ModuleInfoWriter.write(descriptor, jos);
 786                 jos.closeEntry();
 787 
 788                 for (String entry : entries) {
 789                     je = new JarEntry(entry);
 790                     jos.putNextEntry(je);
 791                     jos.closeEntry();
 792                 }
 793             }
 794 
 795         }
 796         return file;
 797     }
 798 
 799 }
 800