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 }