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