/*
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8142968 8166568 8166286 8170618 8168149 8240910 8276764 8276766
* @summary Basic test for jmod
* @library /test/lib
* @modules jdk.compiler
* jdk.jlink
* @build jdk.test.lib.compiler.CompilerUtils
* jdk.test.lib.util.FileUtils
* jdk.test.lib.Platform
* @run testng/othervm -Djava.io.tmpdir=. JmodTest
*/
import java.io.*;
import java.lang.module.ModuleDescriptor;
import java.lang.reflect.Method;
import java.nio.file.*;
import java.util.*;
import java.util.function .Consumer;
import java.util.regex.Pattern;
import java.util.spi.ToolProvider;
import java.util.stream.Stream;
import jdk.test.lib.compiler.CompilerUtils;
import jdk.test.lib.util.FileUtils;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import static java.io.File.pathSeparator;
import static java.lang.module.ModuleDescriptor.Version;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.toSet;
import static org.testng.Assert .*;
public class JmodTest {
static final ToolProvider JMOD_TOOL = ToolProvider.findFirst("jmod" )
.orElseThrow(() ->
new RuntimeException("jmod tool not found" )
);
static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar" )
.orElseThrow(() ->
new RuntimeException("jar tool not found" )
);
static final String TEST_SRC = System.getProperty("test.src" , "." );
static final Path SRC_DIR = Paths.get(TEST_SRC, "src" );
static final Path EXPLODED_DIR = Paths.get("build" );
static final Path MODS_DIR = Paths.get("jmods" );
static final String CLASSES_PREFIX = "classes/" ;
static final String CMDS_PREFIX = "bin/" ;
static final String LIBS_PREFIX = "lib/" ;
static final String CONFIGS_PREFIX = "conf/" ;
@BeforeTest
public void buildExplodedModules() throws IOException {
if (Files.exists(EXPLODED_DIR))
FileUtils.deleteFileTreeWithRetry(EXPLODED_DIR);
for (String name : new String[] { "foo" /*, "bar", "baz"*/ } ) {
Path dir = EXPLODED_DIR.resolve(name);
assertTrue(compileModule(name, dir.resolve("classes" )));
copyResource(SRC_DIR.resolve("foo" ),
dir.resolve("classes" ),
"jdk/test/foo/resources/foo.properties" );
createCmds(dir.resolve("bin" ));
createLibs(dir.resolve("lib" ));
createConfigs(dir.resolve("conf" ));
}
if (Files.exists(MODS_DIR))
FileUtils.deleteFileTreeWithRetry(MODS_DIR);
Files.createDirectories(MODS_DIR);
}
// JDK-8166286 - jmod fails on symlink to directory
@Test
public void testDirSymlinks() throws IOException {
Path apaDir = EXPLODED_DIR.resolve("apa" );
Path classesDir = EXPLODED_DIR.resolve("apa" ).resolve("classes" );
assertTrue(compileModule("apa" , classesDir));
Path libDir = apaDir.resolve("lib" );
createFiles(libDir, List.of("foo/bar/libfoo.so" ));
try {
Path link = Files.createSymbolicLink(
libDir.resolve("baz" ), libDir.resolve("foo" ).toAbsolutePath());
assertTrue(Files.exists(link));
} catch (IOException|UnsupportedOperationException uoe) {
// OS does not support symlinks. Nothing to test!
System.out.println("Creating symlink failed. Test passes vacuously." );
uoe.printStackTrace();
return ;
}
Path jmod = MODS_DIR.resolve("apa.jmod" );
jmod("create" ,
"--libs=" + libDir.toString(),
"--class-path" , classesDir.toString(),
jmod.toString())
.assertSuccess();
Files.delete (jmod);
}
// JDK-8267583 - jmod fails on symlink to class file
@Test
public void testFileSymlinks() throws IOException {
Path apaDir = EXPLODED_DIR.resolve("apa" );
Path classesDir = EXPLODED_DIR.resolve("apa" ).resolve("classes" );
assertTrue(compileModule("apa" , classesDir));
Files.move(classesDir.resolve("module-info.class" ),
classesDir.resolve("module-info.class1" ));
Files.move(classesDir.resolve(Paths.get("jdk" , "test" , "apa" , "Apa.class" )),
classesDir.resolve("Apa.class1" ));
try {
Path link = Files.createSymbolicLink(
classesDir.resolve("module-info.class" ),
classesDir.resolve("module-info.class1" ).toAbsolutePath());
assertTrue(Files.exists(link));
link = Files.createSymbolicLink(
classesDir.resolve(Paths.get("jdk" , "test" , "apa" , "Apa.class" )),
classesDir.resolve("Apa.class1" ).toAbsolutePath());
assertTrue(Files.exists(link));
} catch (IOException|UnsupportedOperationException uoe) {
// OS does not support symlinks. Nothing to test!
System.out.println("Creating symlinks failed. Test passes vacuously." );
uoe.printStackTrace();
return ;
}
Path jmod = MODS_DIR.resolve("apa.jmod" );
jmod("create" ,
"--class-path" , classesDir.toString(),
jmod.toString())
.assertSuccess();
Files.delete (jmod);
}
// JDK-8170618 - jmod should validate if any exported or open package is missing
@Test
public void testMissingPackages() throws IOException {
Path apaDir = EXPLODED_DIR.resolve("apa" );
Path classesDir = EXPLODED_DIR.resolve("apa" ).resolve("classes" );
if (Files.exists(classesDir))
FileUtils.deleteFileTreeWithRetry(classesDir);
assertTrue(compileModule("apa" , classesDir));
FileUtils.deleteFileTreeWithRetry(classesDir.resolve("jdk" ));
Path jmod = MODS_DIR.resolve("apa.jmod" );
jmod("create" ,
"--class-path" , classesDir.toString(),
jmod.toString())
.assertFailure()
.resultChecker(r -> {
assertContains(r.output, "Packages that are exported or open in apa are not present: [jdk.test.apa]" );
});
if (Files.exists(classesDir))
FileUtils.deleteFileTreeWithRetry(classesDir);
}
@Test
public void testList() throws IOException {
String cp = EXPLODED_DIR.resolve("foo" ).resolve("classes" ).toString();
Path jmod = MODS_DIR.resolve("foo.jmod" );
FileUtils.deleteFileIfExistsWithRetry(jmod);
jmod("create" ,
"--class-path" , cp,
jmod.toString())
.assertSuccess();
jmod("list" ,
jmod.toString())
.assertSuccess()
.resultChecker(r -> {
// asserts dependent on the exact contents of foo
assertContains(r.output, CLASSES_PREFIX + "module-info.class" );
assertContains(r.output, CLASSES_PREFIX + "jdk/test/foo/Foo.class" );
assertContains(r.output, CLASSES_PREFIX + "jdk/test/foo/internal/Message.class" );
assertContains(r.output, CLASSES_PREFIX + "jdk/test/foo/resources/foo.properties" );
// JDK-8276764: Ensure the order is sorted for reproducible jmod content
// module-info, followed by <sorted classes>
int mod_info_i = r.output.indexOf(CLASSES_PREFIX + "module-info.class" );
int foo_cls_i = r.output.indexOf(CLASSES_PREFIX + "jdk/test/foo/Foo.class" );
int msg_i = r.output.indexOf(CLASSES_PREFIX + "jdk/test/foo/internal/Message.class" );
int res_i = r.output.indexOf(CLASSES_PREFIX + "jdk/test/foo/resources/foo.properties" );
System.out.println("jmod classes sort order check:\n" +r.output);
assertTrue(mod_info_i < foo_cls_i);
assertTrue(foo_cls_i < msg_i);
assertTrue(msg_i < res_i);
});
}
@Test
public void testSourceDateReproducible() throws IOException {
String cp = EXPLODED_DIR.resolve("foo" ).resolve("classes" ).toString();
Path jmod1 = MODS_DIR.resolve("foo1.jmod" );
Path jmod2 = MODS_DIR.resolve("foo2.jmod" );
Path jmod3 = MODS_DIR.resolve("foo3.jmod" );
FileUtils.deleteFileIfExistsWithRetry(jmod1);
FileUtils.deleteFileIfExistsWithRetry(jmod2);
FileUtils.deleteFileIfExistsWithRetry(jmod3);
// Use source date of 15/03/2022
String sourceDate = "2022-03-15T00:00:00+00:00" ;
jmod("create" ,
"--class-path" , cp,
"--date" , sourceDate,
jmod1.toString())
.assertSuccess();
try {
// Sleep 5 seconds to ensure zip timestamps might be different if they could be
Thread .sleep(5000);
} catch (InterruptedException ex) {}
jmod("create" ,
"--class-path" , cp,
"--date" , sourceDate,
jmod2.toString())
.assertSuccess();
// Compare file byte content to see if they are identical
assertSameContent(jmod1, jmod2);
// Use a date before 1980 and assert failure error
sourceDate = "1976-03-15T00:00:00+00:00" ;
jmod("create" ,
"--class-path" , cp,
"--date" , sourceDate,
jmod3.toString())
.assertFailure()
.resultChecker(r -> {
assertContains(r.output, "is out of the valid range" );
});
// Use a date after 2099 and assert failure error
sourceDate = "2100-03-15T00:00:00+00:00" ;
jmod("create" ,
"--class-path" , cp,
"--date" , sourceDate,
jmod3.toString())
.assertFailure()
.resultChecker(r -> {
assertContains(r.output, "is out of the valid range" );
});
}
@Test
public void testExtractCWD() throws IOException {
Path cp = EXPLODED_DIR.resolve("foo" ).resolve("classes" );
jmod("create" ,
"--class-path" , cp.toString(),
MODS_DIR.resolve("fooExtractCWD.jmod" ).toString())
.assertSuccess();
jmod("extract" ,
MODS_DIR.resolve("fooExtractCWD.jmod" ).toString())
.assertSuccess()
.resultChecker(r -> {
// module-info should exist, but jmod will have added its Packages attr.
assertTrue(Files.exists(Paths.get("classes/module-info.class" )));
assertSameContent(cp.resolve("jdk/test/foo/Foo.class" ),
Paths.get("classes/jdk/test/foo/Foo.class" ));
assertSameContent(cp.resolve("jdk/test/foo/internal/Message.class" ),
Paths.get("classes/jdk/test/foo/internal/Message.class" ));
assertSameContent(cp.resolve("jdk/test/foo/resources/foo.properties" ),
Paths.get("classes/jdk/test/foo/resources/foo.properties" ));
});
}
@Test
public void testExtractDir() throws IOException {
if (Files.exists(Paths.get("extractTestDir" )))
FileUtils.deleteFileTreeWithRetry(Paths.get("extractTestDir" ));
Path cp = EXPLODED_DIR.resolve("foo" ).resolve("classes" );
Path bp = EXPLODED_DIR.resolve("foo" ).resolve("bin" );
Path lp = EXPLODED_DIR.resolve("foo" ).resolve("lib" );
Path cf = EXPLODED_DIR.resolve("foo" ).resolve("conf" );
jmod("create" ,
"--conf" , cf.toString(),
"--cmds" , bp.toString(),
"--libs" , lp.toString(),
"--class-path" , cp.toString(),
MODS_DIR.resolve("fooExtractDir.jmod" ).toString())
.assertSuccess();
jmod("extract" ,
"--dir" , "extractTestDir" ,
MODS_DIR.resolve("fooExtractDir.jmod" ).toString())
.assertSuccess();
jmod("extract" ,
"--dir" , "extractTestDir" ,
MODS_DIR.resolve("fooExtractDir.jmod" ).toString())
.assertSuccess()
.resultChecker(r -> {
// check a sample of the extracted files
Path p = Paths.get("extractTestDir" );
assertTrue(Files.exists(p.resolve("classes/module-info.class" )));
assertSameContent(cp.resolve("jdk/test/foo/Foo.class" ),
p.resolve("classes/jdk/test/foo/Foo.class" ));
assertSameContent(bp.resolve("first" ),
p.resolve(CMDS_PREFIX).resolve("first" ));
assertSameContent(lp.resolve("first.so" ),
p.resolve(LIBS_PREFIX).resolve("second.so" ));
assertSameContent(cf.resolve("second.cfg" ),
p.resolve(CONFIGS_PREFIX).resolve("second.cfg" ));
});
}
@Test
public void testMainClass() throws IOException {
Path jmod = MODS_DIR.resolve("fooMainClass.jmod" );
FileUtils.deleteFileIfExistsWithRetry(jmod);
String cp = EXPLODED_DIR.resolve("foo" ).resolve("classes" ).toString();
jmod("create" ,
"--class-path" , cp,
"--main-class" , "jdk.test.foo.Foo" ,
jmod.toString())
.assertSuccess()
.resultChecker(r -> {
Optional<String> omc = getModuleDescriptor(jmod).mainClass();
assertTrue(omc.isPresent());
assertEquals(omc.get(), "jdk.test.foo.Foo" );
});
}
@Test
public void testModuleVersion() throws IOException {
Path jmod = MODS_DIR.resolve("fooVersion.jmod" );
FileUtils.deleteFileIfExistsWithRetry(jmod);
String cp = EXPLODED_DIR.resolve("foo" ).resolve("classes" ).toString();
jmod("create" ,
"--class-path" , cp,
"--module-version" , "5.4.3" ,
jmod.toString())
.assertSuccess()
.resultChecker(r -> {
Optional<Version> ov = getModuleDescriptor(jmod).version();
assertTrue(ov.isPresent());
assertEquals(ov.get().toString(), "5.4.3" );
});
}
@Test
public void testConfig() throws IOException {
Path jmod = MODS_DIR.resolve("fooConfig.jmod" );
FileUtils.deleteFileIfExistsWithRetry(jmod);
Path cp = EXPLODED_DIR.resolve("foo" ).resolve("classes" );
Path cf = EXPLODED_DIR.resolve("foo" ).resolve("conf" );
jmod("create" ,
"--class-path" , cp.toString(),
"--config" , cf.toString(),
jmod.toString())
.assertSuccess()
.resultChecker(r -> {
try (Stream<String> s1 = findFiles(cf).map(p -> CONFIGS_PREFIX + p);
Stream<String> s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p)) {
Set<String> expectedFilenames = Stream.concat(s1, s2)
.collect(toSet());
assertJmodContent(jmod, expectedFilenames);
}
});
}
@Test
public void testCmds() throws IOException {
Path jmod = MODS_DIR.resolve("fooCmds.jmod" );
FileUtils.deleteFileIfExistsWithRetry(jmod);
Path cp = EXPLODED_DIR.resolve("foo" ).resolve("classes" );
Path bp = EXPLODED_DIR.resolve("foo" ).resolve("bin" );
jmod("create" ,
"--cmds" , bp.toString(),
"--class-path" , cp.toString(),
jmod.toString())
.assertSuccess()
.resultChecker(r -> {
try (Stream<String> s1 = findFiles(bp).map(p -> CMDS_PREFIX + p);
Stream<String> s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p)) {
Set<String> expectedFilenames = Stream.concat(s1,s2)
.collect(toSet());
assertJmodContent(jmod, expectedFilenames);
}
});
}
@Test
public void testLibs() throws IOException {
Path jmod = MODS_DIR.resolve("fooLibs.jmod" );
FileUtils.deleteFileIfExistsWithRetry(jmod);
Path cp = EXPLODED_DIR.resolve("foo" ).resolve("classes" );
Path lp = EXPLODED_DIR.resolve("foo" ).resolve("lib" );
jmod("create" ,
"--libs=" + lp.toString(),
"--class-path" , cp.toString(),
jmod.toString())
.assertSuccess()
.resultChecker(r -> {
try (Stream<String> s1 = findFiles(lp).map(p -> LIBS_PREFIX + p);
Stream<String> s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p)) {
Set<String> expectedFilenames = Stream.concat(s1,s2)
.collect(toSet());
assertJmodContent(jmod, expectedFilenames);
}
});
}
@Test
public void testAll() throws IOException {
Path jmod = MODS_DIR.resolve("fooAll.jmod" );
FileUtils.deleteFileIfExistsWithRetry(jmod);
Path cp = EXPLODED_DIR.resolve("foo" ).resolve("classes" );
Path bp = EXPLODED_DIR.resolve("foo" ).resolve("bin" );
Path lp = EXPLODED_DIR.resolve("foo" ).resolve("lib" );
Path cf = EXPLODED_DIR.resolve("foo" ).resolve("conf" );
jmod("create" ,
"--conf" , cf.toString(),
"--cmds=" + bp.toString(),
"--libs=" + lp.toString(),
"--class-path" , cp.toString(),
jmod.toString())
.assertSuccess()
.resultChecker(r -> {
try (Stream<String> s1 = findFiles(lp).map(p -> LIBS_PREFIX + p);
Stream<String> s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p);
Stream<String> s3 = findFiles(bp).map(p -> CMDS_PREFIX + p);
Stream<String> s4 = findFiles(cf).map(p -> CONFIGS_PREFIX + p)) {
Set<String> expectedFilenames = Stream.concat(Stream.concat(s1,s2),
Stream.concat(s3, s4))
.collect(toSet());
assertJmodContent(jmod, expectedFilenames);
}
});
}
@Test
public void testExcludes() throws IOException {
Path jmod = MODS_DIR.resolve("fooLibs.jmod" );
FileUtils.deleteFileIfExistsWithRetry(jmod);
Path cp = EXPLODED_DIR.resolve("foo" ).resolve("classes" );
Path lp = EXPLODED_DIR.resolve("foo" ).resolve("lib" );
jmod("create" ,
"--libs=" + lp.toString(),
"--class-path" , cp.toString(),
"--exclude" , "**internal**" ,
"--exclude" , "first.so" ,
jmod.toString())
.assertSuccess()
.resultChecker(r -> {
Set<String> expectedFilenames = new HashSet<>();
expectedFilenames.add(CLASSES_PREFIX + "module-info.class" );
expectedFilenames.add(CLASSES_PREFIX + "jdk/test/foo/Foo.class" );
expectedFilenames.add(CLASSES_PREFIX + "jdk/test/foo/resources/foo.properties" );
expectedFilenames.add(LIBS_PREFIX + "second.so" );
expectedFilenames.add(LIBS_PREFIX + "third/third.so" );
assertJmodContent(jmod, expectedFilenames);
Set<String> unexpectedFilenames = new HashSet<>();
unexpectedFilenames.add(CLASSES_PREFIX + "jdk/test/foo/internal/Message.class" );
unexpectedFilenames.add(LIBS_PREFIX + "first.so" );
assertJmodDoesNotContain(jmod, unexpectedFilenames);
});
}
@Test
public void describe() throws IOException {
String cp = EXPLODED_DIR.resolve("foo" ).resolve("classes" ).toString();
jmod("create" ,
"--class-path" , cp,
MODS_DIR.resolve("describeFoo.jmod" ).toString())
.assertSuccess();
jmod("describe" ,
MODS_DIR.resolve("describeFoo.jmod" ).toString())
.assertSuccess()
.resultChecker(r -> {
// Expect similar output: "foo... exports jdk.test.foo ...
// ... requires java.base mandated... contains jdk.test.foo.internal"
Pattern p = Pattern.compile("foo\\s+exports\\s+jdk.test.foo" );
assertTrue(p.matcher(r.output).find(),
"Expecting to find \" foo... exports jdk.test.foo\"" +
"in output, but did not: [" + r.output + "]" );
p = Pattern.compile(
"requires\\s+java.base\\s+mandated\\s+contains\\s+jdk.test.foo.internal" );
assertTrue(p.matcher(r.output).find(),
"Expecting to find \" requires java.base mandated..., " +
"contains jdk.test.foo.internal ...\" " +
"in output, but did not: [" + r.output + "]" );
});
}
@Test
public void testDuplicateEntries() throws IOException {
Path jmod = MODS_DIR.resolve("testDuplicates.jmod" );
FileUtils.deleteFileIfExistsWithRetry(jmod);
String cp = EXPLODED_DIR.resolve("foo" ).resolve("classes" ).toString();
Path lp = EXPLODED_DIR.resolve("foo" ).resolve("lib" );
jmod("create" ,
"--class-path" , cp + pathSeparator + cp,
jmod.toString())
.assertSuccess()
.resultChecker(r ->
assertContains(r.output, "Warning: ignoring duplicate entry" )
);
FileUtils.deleteFileIfExistsWithRetry(jmod);
jmod("create" ,
"--class-path" , cp,
"--libs" , lp.toString() + pathSeparator + lp.toString(),
jmod.toString())
.assertSuccess()
.resultChecker(r ->
assertContains(r.output, "Warning: ignoring duplicate entry" )
);
}
@Test
public void testDuplicateEntriesFromJarFile() throws IOException {
String cp = EXPLODED_DIR.resolve("foo" ).resolve("classes" ).toString();
Path jar = Paths.get("foo.jar" );
Path jmod = MODS_DIR.resolve("testDuplicates.jmod" );
FileUtils.deleteFileIfExistsWithRetry(jar);
FileUtils.deleteFileIfExistsWithRetry(jmod);
// create JAR file
assertTrue(JAR_TOOL.run(System.out, System.err, "cf" , jar.toString(), "-C" , cp, "." ) == 0);
jmod("create" ,
"--class-path" , jar.toString() + pathSeparator + jar.toString(),
jmod.toString())
.assertSuccess()
.resultChecker(r ->
assertContains(r.output, "Warning: ignoring duplicate entry" )
);
}
@Test
public void testIgnoreModuleInfoInOtherSections() throws IOException {
Path jmod = MODS_DIR.resolve("testIgnoreModuleInfoInOtherSections.jmod" );
FileUtils.deleteFileIfExistsWithRetry(jmod);
String cp = EXPLODED_DIR.resolve("foo" ).resolve("classes" ).toString();
jmod("create" ,
"--class-path" , cp,
"--libs" , cp,
jmod.toString())
.assertSuccess()
.resultChecker(r ->
assertContains(r.output, "Warning: ignoring entry" )
);
FileUtils.deleteFileIfExistsWithRetry(jmod);
jmod("create" ,
"--class-path" , cp,
"--cmds" , cp,
jmod.toString())
.assertSuccess()
.resultChecker(r ->
assertContains(r.output, "Warning: ignoring entry" )
);
}
@Test
public void testLastOneWins() throws IOException {
Path workDir = Paths.get("lastOneWins" );
if (Files.exists(workDir))
FileUtils.deleteFileTreeWithRetry(workDir);
Files.createDirectory(workDir);
Path jmod = MODS_DIR.resolve("lastOneWins.jmod" );
FileUtils.deleteFileIfExistsWithRetry(jmod);
Path cp = EXPLODED_DIR.resolve("foo" ).resolve("classes" );
Path bp = EXPLODED_DIR.resolve("foo" ).resolve("bin" );
Path lp = EXPLODED_DIR.resolve("foo" ).resolve("lib" );
Path cf = EXPLODED_DIR.resolve("foo" ).resolve("conf" );
Path shouldNotBeAdded = workDir.resolve("shouldNotBeAdded" );
Files.createDirectory(shouldNotBeAdded);
Files.write(shouldNotBeAdded.resolve("aFile" ), "hello" .getBytes(UTF_8));
// Pairs of options. For options with required arguments the last one
// should win ( first should be effectively ignored, but may still be
// validated ).
jmod("create" ,
"--conf" , shouldNotBeAdded.toString(),
"--conf" , cf.toString(),
"--cmds" , shouldNotBeAdded.toString(),
"--cmds" , bp.toString(),
"--libs" , shouldNotBeAdded.toString(),
"--libs" , lp.toString(),
"--class-path" , shouldNotBeAdded.toString(),
"--class-path" , cp.toString(),
"--main-class" , "does.NotExist" ,
"--main-class" , "jdk.test.foo.Foo" ,
"--module-version" , "00001" ,
"--module-version" , "5.4.3" ,
"--do-not-resolve-by-default" ,
"--do-not-resolve-by-default" ,
"--warn-if-resolved=incubating" ,
"--warn-if-resolved=deprecated" ,
MODS_DIR.resolve("lastOneWins.jmod" ).toString())
.assertSuccess()
.resultChecker(r -> {
ModuleDescriptor md = getModuleDescriptor(jmod);
Optional<String> omc = md.mainClass();
assertTrue(omc.isPresent());
assertEquals(omc.get(), "jdk.test.foo.Foo" );
Optional<Version> ov = md.version();
assertTrue(ov.isPresent());
assertEquals(ov.get().toString(), "5.4.3" );
try (Stream<String> s1 = findFiles(lp).map(p -> LIBS_PREFIX + p);
Stream<String> s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p);
Stream<String> s3 = findFiles(bp).map(p -> CMDS_PREFIX + p);
Stream<String> s4 = findFiles(cf).map(p -> CONFIGS_PREFIX + p)) {
Set<String> expectedFilenames = Stream.concat(Stream.concat(s1,s2),
Stream.concat(s3, s4))
.collect(toSet());
assertJmodContent(jmod, expectedFilenames);
}
});
jmod("extract" ,
"--dir" , "blah" ,
"--dir" , "lastOneWinsExtractDir" ,
jmod.toString())
.assertSuccess()
.resultChecker(r -> {
assertTrue(Files.exists(Paths.get("lastOneWinsExtractDir" )));
assertTrue(Files.notExists(Paths.get("blah" )));
});
}
@Test
public void testPackagesAttribute() throws IOException {
Path jmod = MODS_DIR.resolve("foo.jmod" );
FileUtils.deleteFileIfExistsWithRetry(jmod);
String cp = EXPLODED_DIR.resolve("foo" ).resolve("classes" ).toString();
Set<String> expectedPackages = Set.of("jdk.test.foo" ,
"jdk.test.foo.internal" ,
"jdk.test.foo.resources" );
jmod("create" ,
"--class-path" , cp,
jmod.toString())
.assertSuccess()
.resultChecker(r -> {
Set<String> pkgs = getModuleDescriptor(jmod).packages();
assertEquals(pkgs, expectedPackages);
});
}
@Test
public void testVersion() {
jmod("--version" )
.assertSuccess()
.resultChecker(r -> {
assertContains(r.output, System.getProperty("java.version" ));
});
}
@Test
public void testHelp() {
jmod("--help" )
.assertSuccess()
.resultChecker(r -> {
assertTrue(r.output.startsWith("Usage: jmod" ), "Help not printed" );
assertFalse(r.output.contains("--do-not-resolve-by-default" ));
assertFalse(r.output.contains("--warn-if-resolved" ));
});
}
@Test
public void testHelpExtra() {
jmod("--help-extra" )
.assertSuccess()
.resultChecker(r -> {
assertTrue(r.output.startsWith("Usage: jmod" ), "Extra help not printed" );
assertContains(r.output, "--do-not-resolve-by-default" );
assertContains(r.output, "--warn-if-resolved" );
});
}
@Test
public void testTmpFileRemoved() throws IOException {
// Implementation detail: jmod tool creates <jmod-file>.tmp
// Ensure that it is removed in the event of a failure.
// The failure in this case is a class in the unnamed package.
Path jmod = MODS_DIR.resolve("testTmpFileRemoved.jmod" );
Path tmp = MODS_DIR.resolve(".testTmpFileRemoved.jmod.tmp" );
FileUtils.deleteFileIfExistsWithRetry(jmod);
FileUtils.deleteFileIfExistsWithRetry(tmp);
String cp = EXPLODED_DIR.resolve("foo" ).resolve("classes" ) + File.pathSeparator +
EXPLODED_DIR.resolve("foo" ).resolve("classes" )
.resolve("jdk" ).resolve("test" ).resolve("foo" ).toString();
jmod("create" ,
"--class-path" , cp,
jmod.toString())
.assertFailure()
.resultChecker(r -> {
assertContains(r.output, "unnamed package" );
assertTrue(Files.notExists(tmp), "Unexpected tmp file:" + tmp);
});
}
@Test
public void testCompressionLevel() throws IOException {
String cp = EXPLODED_DIR.resolve("foo" ).resolve("classes" ).toString();
Path jmod = MODS_DIR.resolve("foo.jmod" );
FileUtils.deleteFileIfExistsWithRetry(jmod);
jmod("create" ,
"--class-path" , cp,
"--compress" , "zip-0" ,
jmod.toString())
.assertSuccess();
jmod("list" ,
"--compress" , "zip-0" ,
jmod.toString())
.assertFailure()
.resultChecker(r -> {
assertTrue(r.output.contains("--compress is only accepted with create mode" ), "Error message printed" );
});
FileUtils.deleteFileIfExistsWithRetry(jmod);
jmod("create" ,
"--class-path" , cp,
"--compress" , "zip-9" ,
jmod.toString())
.assertSuccess();
FileUtils.deleteFileIfExistsWithRetry(jmod);
jmod("create" ,
"--class-path" , cp,
"--compress" , "zip--1" ,
jmod.toString())
.assertFailure()
.resultChecker(r -> {
assertTrue(r.output.contains("--compress value is invalid" ), "Error message printed" );
});
FileUtils.deleteFileIfExistsWithRetry(jmod);
jmod("create" ,
"--class-path" , cp,
"--compress" , "zip-1-something" ,
jmod.toString())
.assertFailure()
.resultChecker(r -> {
assertTrue(r.output.contains("--compress value is invalid" ), "Error message printed" );
});
FileUtils.deleteFileIfExistsWithRetry(jmod);
jmod("create" ,
"--class-path" , cp,
"--compress" , "zip-10" ,
jmod.toString())
.assertFailure()
.resultChecker(r -> {
assertTrue(r.output.contains("--compress value is invalid" ), "Error message printed" );
});
FileUtils.deleteFileIfExistsWithRetry(jmod);
jmod("create" ,
"--class-path" , cp,
"--compress" , "zip-" ,
jmod.toString())
.assertFailure()
.resultChecker(r -> {
assertTrue(r.output.contains("--compress value is invalid" ), "Error message printed" );
});
FileUtils.deleteFileIfExistsWithRetry(jmod);
jmod("create" ,
"--class-path" , cp,
"--compress" , "test" ,
jmod.toString())
.assertFailure()
.resultChecker(r -> {
assertTrue(r.output.contains("--compress value is invalid" ), "Error message printed" );
});
FileUtils.deleteFileIfExistsWithRetry(jmod);
jmod("create" ,
"--class-path" , cp,
"--compress" , "test-0" ,
jmod.toString())
.assertFailure()
.resultChecker(r -> {
assertTrue(r.output.contains("--compress value is invalid" ), "Error message printed" );
});
}
// ---
static boolean compileModule(String name, Path dest) throws IOException {
return CompilerUtils.compile(SRC_DIR.resolve(name), dest);
}
static void assertContains(String output, String subString) {
if (output.contains(subString))
assertTrue(true );
else
assertTrue(false ,"Expected to find [" + subString + "], in output ["
+ output + "]" + "\n" );
}
static ModuleDescriptor getModuleDescriptor(Path jmod) {
ClassLoader cl = ClassLoader.getSystemClassLoader();
try (FileSystem fs = FileSystems.newFileSystem(jmod, cl)) {
String p = "/classes/module-info.class" ;
try (InputStream is = Files.newInputStream(fs.getPath(p))) {
return ModuleDescriptor.read(is);
}
} catch (IOException ioe) {
throw new UncheckedIOException(ioe);
}
}
static Stream<String> findFiles(Path dir) {
try {
return Files.find(dir, Integer.MAX_VALUE, (p, a) -> a.isRegularFile())
.map(dir::relativize)
.map(Path::toString)
.map(p -> p.replace(File.separator, "/" ));
} catch (IOException x) {
throw new UncheckedIOException(x);
}
}
static Set<String> getJmodContent(Path jmod) {
JmodResult r = jmod("list" , jmod.toString()).assertSuccess();
return Stream.of(r.output.split("\r?\n" )).collect(toSet());
}
static void assertJmodContent(Path jmod, Set<String> expected) {
Set<String> actual = getJmodContent(jmod);
if (!Objects.equals(actual, expected)) {
Set<String> unexpected = new HashSet<>(actual);
unexpected.removeAll(expected);
Set<String> notFound = new HashSet<>(expected);
notFound.removeAll(actual);
StringBuilder sb = new StringBuilder();
sb.append("Unexpected but found:\n" );
unexpected.forEach(s -> sb.append("\t" + s + "\n" ));
sb.append("Expected but not found:\n" );
notFound.forEach(s -> sb.append("\t" + s + "\n" ));
assertTrue(false , "Jmod content check failed.\n" + sb.toString());
}
}
static void assertJmodDoesNotContain(Path jmod, Set<String> unexpectedNames) {
Set<String> actual = getJmodContent(jmod);
Set<String> unexpected = new HashSet<>();
for (String name : unexpectedNames) {
if (actual.contains(name))
unexpected.add(name);
}
if (!unexpected.isEmpty()) {
StringBuilder sb = new StringBuilder();
for (String s : unexpected)
sb.append("Unexpected but found: " + s + "\n" );
sb.append("In :" );
for (String s : actual)
sb.append("\t" + s + "\n" );
assertTrue(false , "Jmod content check failed.\n" + sb.toString());
}
}
static void assertSameContent(Path p1, Path p2) {
try {
byte [] ba1 = Files.readAllBytes(p1);
byte [] ba2 = Files.readAllBytes(p2);
assertEquals(ba1, ba2);
} catch (IOException x) {
throw new UncheckedIOException(x);
}
}
static JmodResult jmod(String... args) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos);
System.out.println("jmod " + Arrays.asList(args));
int ec = JMOD_TOOL.run(ps, ps, args);
return new JmodResult(ec, new String(baos.toByteArray(), UTF_8));
}
static class JmodResult {
final int exitCode;
final String output;
JmodResult(int exitValue, String output) {
this .exitCode = exitValue;
this .output = output;
}
JmodResult assertSuccess() { assertTrue(exitCode == 0, output); return this ; }
JmodResult assertFailure() { assertTrue(exitCode != 0, output); return this ; }
JmodResult resultChecker(Consumer<JmodResult> r) { r.accept(this ); return this ; }
}
static void createCmds(Path dir) throws IOException {
List<String> files = Arrays.asList(
"first" , "second" , "third" + File.separator + "third" );
createFiles(dir, files);
}
static void createLibs(Path dir) throws IOException {
List<String> files = Arrays.asList(
"first.so" , "second.so" , "third" + File.separator + "third.so" );
createFiles(dir, files);
}
static void createConfigs(Path dir) throws IOException {
List<String> files = Arrays.asList(
"first.cfg" , "second.cfg" , "third" + File.separator + "third.cfg" );
createFiles(dir, files);
}
static void createFiles(Path dir, List<String> filenames) throws IOException {
for (String name : filenames) {
Path file = dir.resolve(name);
Files.createDirectories(file.getParent());
Files.createFile(file);
try (OutputStream os = Files.newOutputStream(file)) {
os.write("blahblahblah" .getBytes(UTF_8));
}
}
}
static void copyResource(Path srcDir, Path dir, String resource) throws IOException {
Path dest = dir.resolve(resource);
Files.deleteIfExists(dest);
Files.createDirectories(dest.getParent());
Files.copy(srcDir.resolve(resource), dest);
}
// Standalone entry point.
public static void main(String[] args) throws Throwable {
JmodTest test = new JmodTest();
test.buildExplodedModules();
for (Method m : JmodTest.class .getDeclaredMethods()) {
if (m.getAnnotation(Test.class ) != null ) {
System.out.println("Invoking " + m.getName());
m.invoke(test);
}
}
}
}
quality 71%
¤ Dauer der Verarbeitung: 0.4 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland