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  * @library /lib/testlibrary
  27  * @modules jdk.jlink/jdk.tools.jmod
  28  *          jdk.compiler
  29  * @build jdk.testlibrary.FileUtils CompilerUtils
  30  * @run testng JmodNegativeTest
  31  * @summary Negative tests for jmod
  32  */
  33 
  34 import java.io.*;
  35 import java.nio.file.Files;
  36 import java.nio.file.Path;
  37 import java.nio.file.Paths;
  38 import java.util.Arrays;
  39 import java.util.List;
  40 import java.util.function.Consumer;
  41 import java.util.function.Supplier;
  42 import java.util.zip.ZipOutputStream;
  43 import jdk.testlibrary.FileUtils;
  44 import org.testng.annotations.BeforeTest;
  45 import org.testng.annotations.DataProvider;
  46 import org.testng.annotations.Test;
  47 
  48 import static java.io.File.pathSeparator;
  49 import static java.nio.charset.StandardCharsets.UTF_8;
  50 import static org.testng.Assert.assertTrue;
  51 
  52 public class JmodNegativeTest {
  53 
  54     static final String TEST_SRC = System.getProperty("test.src", ".");
  55     static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
  56     static final Path EXPLODED_DIR = Paths.get("build");
  57     static final Path MODS_DIR = Paths.get("jmods");
  58 
  59     @BeforeTest
  60     public void buildExplodedModules() throws IOException {
  61         if (Files.exists(EXPLODED_DIR))
  62             FileUtils.deleteFileTreeWithRetry(EXPLODED_DIR);
  63 
  64         for (String name : new String[] { "foo"/*, "bar", "baz"*/ } ) {
  65             Path dir = EXPLODED_DIR.resolve(name);
  66             assertTrue(compileModule(name, dir.resolve("classes")));
  67         }
  68 
  69         if (Files.exists(MODS_DIR))
  70             FileUtils.deleteFileTreeWithRetry(MODS_DIR);
  71         Files.createDirectories(MODS_DIR);
  72     }
  73 
  74     @Test
  75     public void testNoArgs() {
  76         jmod()
  77             .assertFailure()
  78             .resultChecker(r ->
  79                 assertContains(r.output, "Error: one of create, list, describe, or hash must be specified")
  80             );
  81     }
  82 
  83     @Test
  84     public void testBadAction() {
  85         jmod("badAction")
  86             .assertFailure()
  87             .resultChecker(r ->
  88                 assertContains(r.output, "Error: mode must be one of create, list, describe, or hash")
  89             );
  90 
  91         jmod("--badOption")
  92             .assertFailure()
  93             .resultChecker(r ->
  94                 assertContains(r.output, "Error: 'badOption' is not a recognized option")
  95             );
  96     }
  97 
  98     @Test
  99     public void testTooManyArgs() throws IOException {
 100         Path jmod = MODS_DIR.resolve("doesNotExist.jmod");
 101         FileUtils.deleteFileIfExistsWithRetry(jmod);
 102 
 103         jmod("create",
 104              jmod.toString(),
 105              "AAA")
 106             .assertFailure()
 107             .resultChecker(r ->
 108                 assertContains(r.output, "Error: unknown option(s): [AAA]")
 109             );
 110     }
 111 
 112     @Test
 113     public void testCreateNoArgs() {
 114         jmod("create")
 115             .assertFailure()
 116             .resultChecker(r ->
 117                 assertContains(r.output, "Error: jmod-file must be specified")
 118             );
 119     }
 120 
 121     @Test
 122     public void testListNoArgs() {
 123         jmod("list")
 124             .assertFailure()
 125             .resultChecker(r ->
 126                 assertContains(r.output, "Error: jmod-file must be specified")
 127             );
 128     }
 129 
 130     @Test
 131     public void testListFileDoesNotExist() throws IOException {
 132         Path jmod = MODS_DIR.resolve("doesNotExist.jmod");
 133         FileUtils.deleteFileIfExistsWithRetry(jmod);
 134 
 135         jmod("list",
 136              jmod.toString())
 137             .assertFailure()
 138             .resultChecker(r ->
 139                 assertContains(r.output, "Error: no jmod file found: "
 140                         + jmod.toString())
 141             );
 142     }
 143 
 144     @Test
 145     public void testListJmodIsDir() throws IOException {
 146         Path jmod = MODS_DIR.resolve("testListJmodIsDir.jmod");
 147         if (Files.notExists(jmod))
 148             Files.createDirectory(jmod);
 149 
 150         jmod("list",
 151              jmod.toString())
 152             .assertFailure()
 153             .resultChecker(r ->
 154                 assertContains(r.output, "Error: error opening jmod file")
 155             );
 156     }
 157 
 158     @Test
 159     public void testlistJmodMalformed() throws IOException {
 160         Path jmod = MODS_DIR.resolve("testlistJmodMalformed.jmod");
 161         if (Files.notExists(jmod))
 162             Files.createFile(jmod);
 163 
 164         jmod("list",
 165              jmod.toString())
 166             .assertFailure()
 167             .resultChecker(r ->
 168                 assertContains(r.output, "Error: error opening jmod file")
 169             );
 170     }
 171 
 172     @Test
 173     public void testHashModulesModulePathNotSpecified() {
 174         jmod("create",
 175              "--hash-modules", "anyPattern.*",
 176              "output.jmod")
 177             .assertFailure()
 178             .resultChecker(r ->
 179                 assertContains(r.output, "Error: --module-path must be "
 180                         +"specified when hashing modules")
 181             );
 182     }
 183 
 184     @Test
 185     public void testCreateJmodAlreadyExists() throws IOException {
 186         Path jmod = MODS_DIR.resolve("testCreateJmodAlreadyExists.jmod");
 187         if (Files.notExists(jmod))
 188             Files.createFile(jmod);
 189 
 190         jmod("create",
 191              "--class-path", Paths.get(".").toString(), // anything that exists
 192              jmod.toString())
 193             .assertFailure()
 194             .resultChecker(r ->
 195                 assertContains(r.output, "Error: file already exists: " + jmod.toString())
 196             );
 197     }
 198 
 199     @Test
 200     public void testCreateJmodIsDir() throws IOException {
 201         Path jmod = MODS_DIR.resolve("testCreateJmodAlreadyExists");
 202         if (Files.notExists(jmod))
 203             Files.createDirectory(jmod);
 204 
 205         jmod("create",
 206              "--class-path", Paths.get(".").toString(), // anything that exists
 207              jmod.toString())
 208             .assertFailure()
 209             .resultChecker(r ->
 210                 assertContains(r.output, "Error: file already exists: " + jmod.toString())
 211             );
 212     }
 213 
 214     @Test
 215     public void testInvalidModuleVersion() throws IOException {
 216         Path jmod = MODS_DIR.resolve("testEmptyModuleVersion.jmod");
 217         FileUtils.deleteFileIfExistsWithRetry(jmod);
 218         String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();
 219 
 220         for (String version : new String[] { "", "NOT_A_VALID_VERSION" }) {
 221             jmod("create",
 222                  "--class-path", cp,
 223                  "--module-version", version,
 224                  jmod.toString())
 225                 .assertFailure()
 226                 .resultChecker(r ->
 227                     assertContains(r.output, "Error: invalid module version")
 228                 );
 229         }
 230     }
 231 
 232     @Test
 233     public void testEmptyFileInClasspath() throws IOException {
 234         Path jmod = MODS_DIR.resolve("testEmptyFileInClasspath.jmod");
 235         FileUtils.deleteFileIfExistsWithRetry(jmod);
 236         Path jar = MODS_DIR.resolve("NotARealJar_Empty.jar");
 237         FileUtils.deleteFileIfExistsWithRetry(jar);
 238         Files.createFile(jar);
 239 
 240         jmod("create",
 241              "--class-path", jar.toString(),
 242              jmod.toString())
 243             .assertFailure()
 244             .resultChecker(r ->
 245                 assertContains(r.output, "Error: module-info.class not found")
 246             );
 247     }
 248 
 249     @Test
 250     public void testEmptyJarInClasspath() throws IOException {
 251         Path jmod = MODS_DIR.resolve("testEmptyJarInClasspath.jmod");
 252         FileUtils.deleteFileIfExistsWithRetry(jmod);
 253         Path jar = MODS_DIR.resolve("empty.jar");
 254         FileUtils.deleteFileIfExistsWithRetry(jar);
 255         try (FileOutputStream fos = new FileOutputStream(jar.toFile());
 256              ZipOutputStream zos = new ZipOutputStream(fos)) {
 257             // empty
 258         }
 259 
 260         jmod("create",
 261              "--class-path", jar.toString(),
 262              jmod.toString())
 263             .assertFailure()
 264             .resultChecker(r ->
 265                 assertContains(r.output, "Error: module-info.class not found")
 266             );
 267     }
 268 
 269     @Test
 270     public void testModuleInfoNotFound() throws IOException {
 271         Path jmod = MODS_DIR.resolve("output.jmod");
 272         FileUtils.deleteFileIfExistsWithRetry(jmod);
 273         Path jar = MODS_DIR.resolve("empty");
 274         FileUtils.deleteFileIfExistsWithRetry(jar);
 275         Files.createDirectory(jar);
 276 
 277         jmod("create",
 278              "--class-path", jar.toString(),
 279              jmod.toString())
 280             .assertFailure()
 281             .resultChecker(r ->
 282                 assertContains(r.output, "Error: module-info.class not found")
 283             );
 284     }
 285 
 286     @Test
 287     public void testModuleInfoIsDir() throws IOException {
 288         Path jmod = MODS_DIR.resolve("output.jmod");
 289         FileUtils.deleteFileIfExistsWithRetry(jmod);
 290         Path cp = MODS_DIR.resolve("module-info.class");
 291         FileUtils.deleteFileIfExistsWithRetry(cp);
 292         Files.createDirectory(cp);
 293         Files.createFile(cp.resolve("nada.txt"));
 294 
 295         jmod("create",
 296              "--class-path", cp.toString(),
 297              jmod.toString())
 298             .assertFailure()
 299             .resultChecker(r ->
 300                 assertContains(r.output, "Error: module-info.class not found")
 301             );
 302     }
 303 
 304     @Test
 305     public void testNoModuleHash() throws IOException {
 306         Path jmod = MODS_DIR.resolve("output.jmod");
 307         FileUtils.deleteFileIfExistsWithRetry(jmod);
 308         Path emptyDir = Paths.get("empty");
 309         if (Files.exists(emptyDir))
 310             FileUtils.deleteFileTreeWithRetry(emptyDir);
 311         Files.createDirectory(emptyDir);
 312         String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();
 313 
 314         jmod("create",
 315              "--class-path", cp,
 316              "--hash-modules", ".*",
 317              "--modulepath", emptyDir.toString(),
 318             jmod.toString())
 319             .resultChecker(r ->
 320                 assertContains(r.output, "No hashes recorded: " +
 321                     "no module specified for hashing depends on foo")
 322             );
 323     }
 324 
 325     @Test
 326     public void testEmptyFileInModulePath() throws IOException {
 327         Path jmod = MODS_DIR.resolve("output.jmod");
 328         FileUtils.deleteFileIfExistsWithRetry(jmod);
 329         Path empty = MODS_DIR.resolve("emptyFile.jmod");
 330         FileUtils.deleteFileIfExistsWithRetry(empty);
 331         Files.createFile(empty);
 332         try {
 333             String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();
 334 
 335             jmod("create",
 336                  "--class-path", cp,
 337                  "--hash-modules", ".*",
 338                  "--modulepath", MODS_DIR.toString(),
 339                  jmod.toString())
 340                 .assertFailure();
 341         } finally {
 342             FileUtils.deleteFileWithRetry(empty);
 343         }
 344     }
 345 
 346     @Test
 347     public void testFileInModulePath() throws IOException {
 348         Path jmod = MODS_DIR.resolve("output.jmod");
 349         FileUtils.deleteFileIfExistsWithRetry(jmod);
 350         Path file = MODS_DIR.resolve("testFileInModulePath.txt");
 351         FileUtils.deleteFileIfExistsWithRetry(file);
 352         Files.createFile(file);
 353 
 354         jmod("create",
 355              "--hash-modules", ".*",
 356              "--modulepath", file.toString(),
 357              jmod.toString())
 358             .assertFailure()
 359             .resultChecker(r ->
 360                 assertContains(r.output, "Error: path must be a directory")
 361             );
 362     }
 363 
 364     @DataProvider(name = "pathDoesNotExist")
 365     public Object[][] pathDoesNotExist() throws IOException {
 366         Path jmod = MODS_DIR.resolve("output.jmod");
 367         FileUtils.deleteFileIfExistsWithRetry(jmod);
 368         FileUtils.deleteFileIfExistsWithRetry(Paths.get("doesNotExist"));
 369 
 370         List<Supplier<JmodResult>> tasks = Arrays.asList(
 371                 () -> jmod("create",
 372                            "--hash-modules", "anyPattern",
 373                            "--modulepath", "doesNotExist",
 374                            "output.jmod"),
 375                 () -> jmod("create",
 376                            "--class-path", "doesNotExist",
 377                            "output.jmod"),
 378                 () -> jmod("create",
 379                            "--class-path", "doesNotExist.jar",
 380                            "output.jmod"),
 381                 () -> jmod("create",
 382                            "--cmds", "doesNotExist",
 383                            "output.jmod"),
 384                 () -> jmod("create",
 385                            "--config", "doesNotExist",
 386                            "output.jmod"),
 387                 () -> jmod("create",
 388                            "--libs", "doesNotExist",
 389                            "output.jmod") );
 390 
 391         String errMsg = "Error: path not found: doesNotExist";
 392         return tasks.stream().map(t -> new Object[] {t, errMsg} )
 393                              .toArray(Object[][]::new);
 394     }
 395 
 396     @Test(dataProvider = "pathDoesNotExist")
 397     public void testPathDoesNotExist(Supplier<JmodResult> supplier,
 398                                      String errMsg)
 399     {
 400         supplier.get()
 401                 .assertFailure()
 402                 .resultChecker(r -> {
 403                     assertContains(r.output, errMsg);
 404                 });
 405     }
 406 
 407     @DataProvider(name = "partOfPathDoesNotExist")
 408     public Object[][] partOfPathDoesNotExist() throws IOException {
 409         Path jmod = MODS_DIR.resolve("output.jmod");
 410         FileUtils.deleteFileIfExistsWithRetry(jmod);
 411         FileUtils.deleteFileIfExistsWithRetry(Paths.get("doesNotExist"));
 412 
 413         Path emptyDir = Paths.get("empty");
 414         if (Files.exists(emptyDir))
 415             FileUtils.deleteFileTreeWithRetry(emptyDir);
 416         Files.createDirectory(emptyDir);
 417 
 418         List<Supplier<JmodResult>> tasks = Arrays.asList(
 419             () -> jmod("create",
 420                        "--hash-modules", "anyPattern",
 421                        "--modulepath","empty" + pathSeparator + "doesNotExist",
 422                        "output.jmod"),
 423             () -> jmod("create",
 424                        "--class-path", "empty" + pathSeparator + "doesNotExist",
 425                        "output.jmod"),
 426             () -> jmod("create",
 427                        "--class-path", "empty" + pathSeparator + "doesNotExist.jar",
 428                        "output.jmod"),
 429             () -> jmod("create",
 430                        "--cmds", "empty" + pathSeparator + "doesNotExist",
 431                        "output.jmod"),
 432             () -> jmod("create",
 433                        "--config", "empty" + pathSeparator + "doesNotExist",
 434                        "output.jmod"),
 435             () -> jmod("create",
 436                        "--libs", "empty" + pathSeparator + "doesNotExist",
 437                        "output.jmod") );
 438 
 439         String errMsg = "Error: path not found: doesNotExist";
 440         return tasks.stream().map(t -> new Object[] {t, errMsg} )
 441                              .toArray(Object[][]::new);
 442     }
 443 
 444     @Test(dataProvider = "partOfPathDoesNotExist")
 445     public void testPartOfPathNotExist(Supplier<JmodResult> supplier,
 446                                        String errMsg)
 447     {
 448         supplier.get()
 449                 .assertFailure()
 450                 .resultChecker(r -> {
 451                     assertContains(r.output, errMsg);
 452                 });
 453     }
 454 
 455     @DataProvider(name = "pathIsFile")
 456     public Object[][] pathIsFile() throws IOException {
 457         Path jmod = MODS_DIR.resolve("output.jmod");
 458         FileUtils.deleteFileIfExistsWithRetry(jmod);
 459         Path aFile = Paths.get("aFile.txt");
 460         if (Files.exists(aFile) && !Files.isRegularFile(aFile))
 461             throw new InternalError("Unexpected file:" + aFile);
 462         else
 463             Files.createFile(aFile);
 464 
 465         List<Supplier<JmodResult>> tasks = Arrays.asList(
 466                 () -> jmod("create",
 467                            "--class-path", "aFile.txt",
 468                            "output.jmod"),
 469                 () -> jmod("create",
 470                            "--modulepath", "aFile.txt",
 471                            "output.jmod"),
 472                 () -> jmod("create",
 473                            "--cmds", "aFile.txt",
 474                            "output.jmod"),
 475                 () -> jmod("create",
 476                            "--config", "aFile.txt",
 477                            "output.jmod"),
 478                 () -> jmod("create",
 479                            "--libs", "aFile.txt",
 480                            "output.jmod") );
 481 
 482         String errMsg = "Error: path must be a directory: aFile.txt";
 483         Object[][] a = tasks.stream().map(t -> new Object[] {t, errMsg} )
 484                                      .toArray(Object[][]::new);
 485         a[0][1] = "invalid class path entry: aFile.txt";  // class path err msg
 486         return a;
 487     }
 488 
 489     @Test(dataProvider = "pathIsFile")
 490     public void testPathIsFile(Supplier<JmodResult> supplier,
 491                                String errMsg)
 492     {
 493         supplier.get()
 494                 .assertFailure()
 495                 .resultChecker(r -> {
 496                     assertContains(r.output, errMsg);
 497                 });
 498     }
 499 
 500     // ---
 501 
 502     static boolean compileModule(String name, Path dest) throws IOException {
 503         return CompilerUtils.compile(SRC_DIR.resolve(name), dest);
 504     }
 505 
 506     static void assertContains(String output, String subString) {
 507         if (output.contains(subString))
 508             assertTrue(true);
 509         else
 510             assertTrue(false,"Expected to find [" + subString + "], in output ["
 511                              + output + "]");
 512     }
 513 
 514     static JmodResult jmod(String... args) {
 515         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 516         PrintStream ps = new PrintStream(baos);
 517         System.out.println("jmod " + Arrays.asList(args));
 518         int ec = jdk.tools.jmod.Main.run(args, ps);
 519         return new JmodResult(ec, new String(baos.toByteArray(), UTF_8));
 520     }
 521 
 522     static class JmodResult {
 523         final int exitCode;
 524         final String output;
 525 
 526         JmodResult(int exitValue, String output) {
 527             this.exitCode = exitValue;
 528             this.output = output;
 529         }
 530         JmodResult assertFailure() { assertTrue(exitCode != 0, output); return this; }
 531         JmodResult resultChecker(Consumer<JmodResult> r) { r.accept(this); return this; }
 532     }
 533 }