< prev index next >
test/tools/jar/modularJar/Basic.java
Print this page
rev 14541 : 8156497: Add jar tool support for Multi-Release Modular JARs
Reviewed-by:
@@ -31,10 +31,11 @@
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import java.util.function.Consumer;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
+import java.util.jar.Manifest;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
@@ -50,58 +51,78 @@
import static java.lang.System.out;
/*
* @test
* @library /lib/testlibrary
+ * @modules jdk.compiler
+ * jdk.jartool
* @build jdk.testlibrary.FileUtils jdk.testlibrary.JDKToolFinder
* @compile Basic.java
* @run testng Basic
- * @summary Basic test for Modular jars
+ * @summary Tests for plain Modular jars & Multi-Release Modular jars
*/
public class Basic {
static final Path TEST_SRC = Paths.get(System.getProperty("test.src", "."));
static final Path TEST_CLASSES = Paths.get(System.getProperty("test.classes", "."));
static final Path MODULE_CLASSES = TEST_CLASSES.resolve("build");
+ static final Path MRJAR_DIR = MODULE_CLASSES.resolve("mrjar");
// Details based on the checked in module source
static TestModuleData FOO = new TestModuleData("foo",
"1.123",
"jdk.test.foo.Foo",
- "Hello World!!!", null,
- "jdk.test.foo.internal");
+ "Hello World!!!",
+ null, // no hashes
+ Set.of("java.base"),
+ Set.of("jdk.test.foo"),
+ null, // no uses
+ null, // no provides
+ Set.of("jdk.test.foo.internal"));
static TestModuleData BAR = new TestModuleData("bar",
"4.5.6.7",
"jdk.test.bar.Bar",
- "Hello from Bar!", null,
- "jdk.test.bar",
- "jdk.test.bar.internal");
+ "Hello from Bar!",
+ null, // no hashes
+ Set.of("java.base", "foo"),
+ null, // no exports
+ null, // no uses
+ null, // no provides
+ Set.of("jdk.test.bar",
+ "jdk.test.bar.internal"));
static class TestModuleData {
final String moduleName;
+ final Set<String> requires;
+ final Set<String> exports;
+ final Set<String> uses;
+ final Set<String> provides;
final String mainClass;
final String version;
final String message;
final String hashes;
final Set<String> conceals;
- TestModuleData(String mn, String v, String mc, String m, String h, String... pkgs) {
- moduleName = mn; mainClass = mc; version = v; message = m; hashes = h;
- conceals = new HashSet<>();
- Stream.of(pkgs).forEach(conceals::add);
- }
- TestModuleData(String mn, String v, String mc, String m, String h, Set<String> pkgs) {
+
+ TestModuleData(String mn, String v, String mc, String m, String h,
+ Set<String> requires, Set<String> exports, Set<String> uses,
+ Set<String> provides, Set<String> conceals) {
moduleName = mn; mainClass = mc; version = v; message = m; hashes = h;
- conceals = pkgs;
+ this.requires = requires;
+ this.exports = exports;
+ this.uses = uses;
+ this.provides = provides;
+ this.conceals = conceals;
}
static TestModuleData from(String s) {
try {
BufferedReader reader = new BufferedReader(new StringReader(s));
String line;
String message = null;
String name = null, version = null, mainClass = null;
String hashes = null;
- Set<String> conceals = null;
+ Set<String> requires, exports, uses, provides, conceals;
+ requires = exports = uses = provides = conceals = null;
while ((line = reader.readLine()) != null) {
if (line.startsWith("message:")) {
message = line.substring("message:".length());
} else if (line.startsWith("nameAndVersion:")) {
line = line.substring("nameAndVersion:".length());
@@ -112,32 +133,50 @@
} else {
name = line;
}
} else if (line.startsWith("mainClass:")) {
mainClass = line.substring("mainClass:".length());
+ } else if (line.startsWith("requires:")) {
+ line = line.substring("requires:".length());
+ requires = stringToSet(line);
+ } else if (line.startsWith("exports:")) {
+ line = line.substring("exports:".length());
+ exports = stringToSet(line);
+ } else if (line.startsWith("uses:")) {
+ line = line.substring("uses:".length());
+ uses = stringToSet(line);
+ } else if (line.startsWith("provides:")) {
+ line = line.substring("provides:".length());
+ provides = stringToSet(line);
} else if (line.startsWith("hashes:")) {
hashes = line.substring("hashes:".length());
} else if (line.startsWith("conceals:")) {
line = line.substring("conceals:".length());
- conceals = new HashSet<>();
- int i = line.indexOf(',');
- if (i != -1) {
- String[] p = line.split(",");
- Stream.of(p).forEach(conceals::add);
- } else {
- conceals.add(line);
- }
+ conceals = stringToSet(line);
} else {
throw new AssertionError("Unknown value " + line);
}
}
- return new TestModuleData(name, version, mainClass, message, hashes, conceals);
+ return new TestModuleData(name, version, mainClass, message,
+ hashes, requires, exports, uses,
+ provides, conceals);
} catch (IOException x) {
throw new UncheckedIOException(x);
}
}
+ static Set<String> stringToSet(String commaList) {
+ Set<String> s = new HashSet<>();
+ int i = commaList.indexOf(',');
+ if (i != -1) {
+ String[] p = commaList.split(",");
+ Stream.of(p).forEach(s::add);
+ } else {
+ s.add(commaList);
+ }
+ return s;
+ }
}
static void assertModuleData(Result r, TestModuleData expected) {
//out.printf("%s%n", r.output);
TestModuleData received = TestModuleData.from(r.output);
@@ -148,21 +187,34 @@
"Expected moduleName: ", expected.moduleName, ", got:", received.moduleName);
assertTrue(expected.version.equals(received.version),
"Expected version: ", expected.version, ", got:", received.version);
assertTrue(expected.mainClass.equals(received.mainClass),
"Expected mainClass: ", expected.mainClass, ", got:", received.mainClass);
- expected.conceals.forEach(p -> assertTrue(received.conceals.contains(p),
- "Expected ", p, ", in ", received.conceals));
- received.conceals.forEach(p -> assertTrue(expected.conceals.contains(p),
- "Expected ", p, ", in ", expected.conceals));
+ assertSetsEqual(expected.requires, received.requires);
+ assertSetsEqual(expected.exports, received.exports);
+ assertSetsEqual(expected.uses, received.uses);
+ assertSetsEqual(expected.provides, received.provides);
+ assertSetsEqual(expected.conceals, received.conceals);
+ }
+
+ static void assertSetsEqual(Set<String> s1, Set<String> s2) {
+ if (s1 == null && s2 == null) // none expected, or received
+ return;
+ assertTrue(s1.size() == s2.size(),
+ "Unexpected set size difference: ", s1.size(), ", ", s2.size());
+ s1.forEach(p -> assertTrue(s2.contains(p), "Expected ", p, ", in ", s2));
}
@BeforeTest
public void compileModules() throws Exception {
compileModule(FOO.moduleName);
compileModule(BAR.moduleName, MODULE_CLASSES);
compileModule("baz"); // for service provider consistency checking
+
+ setupMRJARModuleInfo(FOO.moduleName);
+ setupMRJARModuleInfo(BAR.moduleName);
+ setupMRJARModuleInfo("baz");
}
@Test
public void createFoo() throws IOException {
Path mp = Paths.get("createFoo");
@@ -178,18 +230,41 @@
"-C", modClasses.toString(), ".")
.assertSuccess();
java(mp, FOO.moduleName + "/" + FOO.mainClass)
.assertSuccess()
.resultChecker(r -> assertModuleData(r, FOO));
-
try (InputStream fis = Files.newInputStream(modularJar);
JarInputStream jis = new JarInputStream(fis)) {
assertTrue(!jarContains(jis, "./"),
"Unexpected ./ found in ", modularJar.toString());
}
}
+ /** Similar to createFoo, but with a Multi-Release Modular jar. */
+ @Test
+ public void createMRMJarFoo() throws IOException {
+ Path mp = Paths.get("createMRMJarFoo");
+ createTestDir(mp);
+ Path modClasses = MODULE_CLASSES.resolve(FOO.moduleName);
+ Path mrjarDir = MRJAR_DIR.resolve(FOO.moduleName);
+ Path modularJar = mp.resolve(FOO.moduleName + ".jar");
+
+ // Positive test, create
+ jar("--create",
+ "--file=" + modularJar.toString(),
+ "--main-class=" + FOO.mainClass,
+ "--module-version=" + FOO.version,
+ "-m", mrjarDir.resolve("META-INF/MANIFEST.MF").toRealPath().toString(),
+ "-C", mrjarDir.toString(), "META-INF/versions/9/module-info.class",
+ "-C", modClasses.toString(), ".")
+ .assertSuccess();
+ java(mp, FOO.moduleName + "/" + FOO.mainClass)
+ .assertSuccess()
+ .resultChecker(r -> assertModuleData(r, FOO));
+ }
+
+
@Test
public void updateFoo() throws IOException {
Path mp = Paths.get("updateFoo");
createTestDir(mp);
Path modClasses = MODULE_CLASSES.resolve(FOO.moduleName);
@@ -211,10 +286,36 @@
.assertSuccess()
.resultChecker(r -> assertModuleData(r, FOO));
}
@Test
+ public void updateMRMJarFoo() throws IOException {
+ Path mp = Paths.get("updateMRMJarFoo");
+ createTestDir(mp);
+ Path modClasses = MODULE_CLASSES.resolve(FOO.moduleName);
+ Path mrjarDir = MRJAR_DIR.resolve(FOO.moduleName);
+ Path modularJar = mp.resolve(FOO.moduleName + ".jar");
+
+ jar("--create",
+ "--file=" + modularJar.toString(),
+ "--no-manifest",
+ "-C", modClasses.toString(), "jdk")
+ .assertSuccess();
+ jar("--update",
+ "--file=" + modularJar.toString(),
+ "--main-class=" + FOO.mainClass,
+ "--module-version=" + FOO.version,
+ "-m", mrjarDir.resolve("META-INF/MANIFEST.MF").toRealPath().toString(),
+ "-C", mrjarDir.toString(), "META-INF/versions/9/module-info.class",
+ "-C", modClasses.toString(), "module-info.class")
+ .assertSuccess();
+ java(mp, FOO.moduleName + "/" + FOO.mainClass)
+ .assertSuccess()
+ .resultChecker(r -> assertModuleData(r, FOO));
+ }
+
+ @Test
public void partialUpdateFooMainClass() throws IOException {
Path mp = Paths.get("partialUpdateFooMainClass");
createTestDir(mp);
Path modClasses = MODULE_CLASSES.resolve(FOO.moduleName);
Path modularJar = mp.resolve(FOO.moduleName + ".jar");
@@ -288,10 +389,34 @@
.assertSuccess()
.resultChecker(r -> assertModuleData(r, FOO));
}
@Test
+ public void partialUpdateMRMJarFooNotAllFiles() throws IOException {
+ Path mp = Paths.get("partialUpdateMRMJarFooNotAllFiles");
+ createTestDir(mp);
+ Path modClasses = MODULE_CLASSES.resolve(FOO.moduleName);
+ Path mrjarDir = MRJAR_DIR.resolve(FOO.moduleName);
+ Path modularJar = mp.resolve(FOO.moduleName + ".jar");
+
+ jar("--create",
+ "--file=" + modularJar.toString(),
+ "--module-version=" + FOO.version,
+ "-C", modClasses.toString(), ".")
+ .assertSuccess();
+ jar("--update",
+ "--file=" + modularJar.toString(),
+ "--main-class=" + FOO.mainClass,
+ "-m", mrjarDir.resolve("META-INF/MANIFEST.MF").toRealPath().toString(),
+ "-C", mrjarDir.toString(), "META-INF/versions/9/module-info.class")
+ .assertSuccess();
+ java(mp, FOO.moduleName + "/" + FOO.mainClass)
+ .assertSuccess()
+ .resultChecker(r -> assertModuleData(r, FOO));
+ }
+
+ @Test
public void partialUpdateFooAllFilesAndAttributes() throws IOException {
Path mp = Paths.get("partialUpdateFooAllFilesAndAttributes");
createTestDir(mp);
Path modClasses = MODULE_CLASSES.resolve(FOO.moduleName);
Path modularJar = mp.resolve(FOO.moduleName + ".jar");
@@ -526,10 +651,28 @@
"-C", modClasses.toString(), "module-info.class")
.assertFailure();
}
@Test
+ public void servicesCreateWithoutFailureMRMJAR() throws IOException {
+ Path mp = Paths.get("servicesCreateWithoutFailureMRMJAR");
+ createTestDir(mp);
+ Path modClasses = MODULE_CLASSES.resolve("baz");
+ Path mrjarDir = MRJAR_DIR.resolve("baz");
+ Path modularJar = mp.resolve("baz" + ".jar");
+
+ jar("--create",
+ "--file=" + modularJar.toString(),
+ "-m", mrjarDir.resolve("META-INF/MANIFEST.MF").toRealPath().toString(),
+ "-C", modClasses.toString(), "module-info.class",
+ "-C", mrjarDir.toString(), "META-INF/versions/9/module-info.class",
+ "-C", modClasses.toString(), "jdk/test/baz/BazService.class",
+ "-C", modClasses.toString(), "jdk/test/baz/internal/BazServiceImpl.class")
+ .assertSuccess();
+ }
+
+ @Test
public void printModuleDescriptorFoo() throws IOException {
Path mp = Paths.get("printModuleDescriptorFoo");
createTestDir(mp);
Path modClasses = MODULE_CLASSES.resolve(FOO.moduleName);
Path modularJar = mp.resolve(FOO.moduleName + ".jar");
@@ -609,10 +752,28 @@
Path build = Files.createDirectories(MODULE_CLASSES.resolve(mn));
javac(build, mp, fileList(fooSourcePath));
return build;
}
+ static void setupMRJARModuleInfo(String moduleName) throws IOException {
+ Path modClasses = MODULE_CLASSES.resolve(moduleName);
+ Path metaInfDir = MRJAR_DIR.resolve(moduleName).resolve("META-INF");
+ Path versionSection = metaInfDir.resolve("versions").resolve("9");
+ createTestDir(versionSection);
+
+ Path versionModuleInfo = versionSection.resolve("module-info.class");
+ System.out.println("copying " + modClasses.resolve("module-info.class") + " to " + versionModuleInfo);
+ Files.copy(modClasses.resolve("module-info.class"), versionModuleInfo);
+
+ Manifest manifest = new Manifest();
+ manifest.getMainAttributes().putValue("Manifest-Version", "1.0");
+ manifest.getMainAttributes().putValue("Multi-Release", "true");
+ try (OutputStream os = Files.newOutputStream(metaInfDir.resolve("MANIFEST.MF"))) {
+ manifest.write(os);
+ }
+ }
+
// Re-enable when there is support in javax.tools for module path
// static void javac(Path dest, Path... sourceFiles) throws IOException {
// out.printf("Compiling %d source files %s%n", sourceFiles.length,
// Arrays.asList(sourceFiles));
// JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
@@ -688,11 +849,11 @@
}
static void createTestDir(Path p) throws IOException{
if (Files.exists(p))
FileUtils.deleteFileTreeWithRetry(p);
- Files.createDirectory(p);
+ Files.createDirectories(p);
}
static boolean jarContains(JarInputStream jis, String entryName)
throws IOException
{
< prev index next >