--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/jar/multiRelease/ApiValidatorTest.java Wed Jan 18 20:39:08 2017 +0300
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2017, 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
+ * @summary Tests for API validator.
+ * @library /test/lib /lib/testlibrary
+ * @modules java.base/jdk.internal.misc
+ * jdk.compiler
+ * jdk.jartool
+ * @build jdk.test.lib.JDKToolFinder jdk.test.lib.Utils jdk.test.lib.process.*
+ * @build jdk.testlibrary.FileUtils
+ * @build MRTestBase
+ * @run testng ApiValidatorTest
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.testlibrary.FileUtils;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.lang.reflect.Method;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public class ApiValidatorTest extends MRTestBase {
+
+ @Test(dataProvider = "signatureChange")
+ public void changeMethodSignature(String sigBase, String sigV10,
+ boolean isAcceptable,
+ Method method) throws Throwable {
+ Path root = Paths.get(method.getName());
+ Path classes = root.resolve("classes");
+
+ String METHOD_SIG = "#SIG";
+ String classTemplate =
+ "public class C { \n" +
+ " " + METHOD_SIG + "{ throw new RuntimeException(); };\n" +
+ "}\n";
+ String base = classTemplate.replace(METHOD_SIG, sigBase);
+ String v10 = classTemplate.replace(METHOD_SIG, sigV10);
+
+ compileTemplate(classes.resolve("base"), base);
+ compileTemplate(classes.resolve("v10"), v10);
+
+ String jarfile = root.resolve("test.jar").toString();
+ OutputAnalyzer result = jar("cf", jarfile,
+ "-C", classes.resolve("base").toString(), ".",
+ "--release", "10", "-C", classes.resolve("v10").toString(),
+ ".");
+ if (isAcceptable) {
+ result.shouldHaveExitValue(SUCCESS)
+ .shouldBeEmpty();
+ } else {
+ result.shouldNotHaveExitValue(SUCCESS)
+ .shouldContain("contains a class with different api from earlier version");
+ }
+
+ FileUtils.deleteFileTreeWithRetry(root);
+ }
+
+ @DataProvider
+ Object[][] signatureChange() {
+ return new Object[][]{
+ {"public int m()", "protected int m()", false},
+ {"protected int m()", "public int m()", false},
+ {"public int m()", "int m()", false},
+ {"protected int m()", "private int m()", false},
+ {"private int m()", "int m()", true},
+ {"int m()", "private int m()", true},
+ {"int m()", "private int m(boolean b)", true},
+ {"public int m()", "public int m(int i)", false},
+ {"public int m()", "public int k()", false},
+ {"public int m()", "private int k()", false},
+// @ignore JDK-8172147 {"public int m()", "public boolean m()", false},
+// @ignore JDK-8172147 {"public boolean", "public Boolean", false},
+// @ignore JDK-8172147 {"public <T> T", "public <T extends String> T", false},
+ };
+ }
+
+ @Test(dataProvider = "publicAPI")
+ public void introducingPublicMembers(String publicAPI,
+ Method method) throws Throwable {
+ Path root = Paths.get(method.getName());
+ Path classes = root.resolve("classes");
+
+ String API = "#API";
+ String classTemplate =
+ "public class C { \n" +
+ " " + API + "\n" +
+ " public void method(){ };\n" +
+ "}\n";
+ String base = classTemplate.replace(API, "");
+ String v10 = classTemplate.replace(API, publicAPI);
+
+ compileTemplate(classes.resolve("base"), base);
+ compileTemplate(classes.resolve("v10"), v10);
+
+ String jarfile = root.resolve("test.jar").toString();
+ jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
+ "--release", "10", "-C", classes.resolve("v10").toString(), ".")
+ .shouldNotHaveExitValue(SUCCESS)
+ .shouldContain("contains a class with different api from earlier version");
+
+ FileUtils.deleteFileTreeWithRetry(root);
+ }
+
+ @DataProvider
+ Object[][] publicAPI() {
+ return new Object[][]{
+// @ignore JDK-8172148 {"protected class Inner { public void m(){ } } "}, // protected inner class
+// @ignore JDK-8172148 {"public class Inner { public void m(){ } }"}, // public inner class
+// @ignore JDK-8172148 {"public enum E { A; }"}, // public enum
+ {"public void m(){ }"}, // public method
+ {"protected void m(){ }"}, // protected method
+ };
+ }
+
+ @Test(dataProvider = "privateAPI")
+ public void introducingPrivateMembers(String privateAPI,
+ Method method) throws Throwable {
+ Path root = Paths.get(method.getName());
+ Path classes = root.resolve("classes");
+
+ String API = "#API";
+ String classTemplate =
+ "public class C { \n" +
+ " " + API + "\n" +
+ " public void method(){ };\n" +
+ "}\n";
+ String base = classTemplate.replace(API, "");
+ String v10 = classTemplate.replace(API, privateAPI);
+
+ compileTemplate(classes.resolve("base"), base);
+ compileTemplate(classes.resolve("v10"), v10);
+
+ String jarfile = root.resolve("test.jar").toString();
+ jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
+ "--release", "10", "-C", classes.resolve("v10").toString(), ".")
+ .shouldHaveExitValue(SUCCESS);
+ // add release
+ jar("uf", jarfile,
+ "--release", "11", "-C", classes.resolve("v10").toString(), ".")
+ .shouldHaveExitValue(SUCCESS);
+ // replace release
+ jar("uf", jarfile,
+ "--release", "11", "-C", classes.resolve("v10").toString(), ".")
+ .shouldHaveExitValue(SUCCESS);
+
+ FileUtils.deleteFileTreeWithRetry(root);
+ }
+
+ @DataProvider
+ Object[][] privateAPI() {
+ return new Object[][]{
+ {"private class Inner { public void m(){ } } "}, // private inner class
+ {"class Inner { public void m(){ } }"}, // package private inner class
+ {"enum E { A; }"}, // package private enum
+ // Local class and private method
+ {"private void m(){ class Inner { public void m(){} } Inner i = null; }"},
+ {"void m(){ }"}, // package private method
+ };
+ }
+
+ private void compileTemplate(Path classes, String template) throws Throwable {
+ Path classSourceFile = Files.createDirectories(
+ classes.getParent().resolve("src").resolve(classes.getFileName()))
+ .resolve("C.java");
+ Files.write(classSourceFile, template.getBytes());
+ javac(classes, classSourceFile);
+ }
+}
\ No newline at end of file
--- a/jdk/test/tools/jar/multiRelease/Basic.java Wed Jan 18 08:03:04 2017 -0800
+++ b/jdk/test/tools/jar/multiRelease/Basic.java Wed Jan 18 20:39:08 2017 +0300
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
@@ -23,69 +23,59 @@
/*
* @test
- * @library /test/lib
+ * @library /test/lib /lib/testlibrary
* @modules java.base/jdk.internal.misc
* jdk.compiler
* jdk.jartool
- * @build jdk.test.lib.JDKToolFinder jdk.test.lib.Utils
+ * @build jdk.test.lib.JDKToolFinder jdk.test.lib.Utils jdk.test.lib.process.*
+ * @build jdk.testlibrary.FileUtils
+ * @build MRTestBase
* @run testng Basic
*/
import static org.testng.Assert.*;
+import jdk.testlibrary.FileUtils;
import org.testng.annotations.*;
-import java.io.*;
+import java.io.File;
import java.nio.file.*;
-import java.nio.file.attribute.*;
import java.util.*;
-import java.util.function.Consumer;
-import java.util.jar.*;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import java.util.zip.*;
+import java.util.jar.JarFile;
+import java.util.zip.ZipFile;
-import jdk.test.lib.JDKToolFinder;
-import jdk.test.lib.Utils;
-
-
-import static java.lang.String.format;
-import static java.lang.System.out;
-
-public class Basic {
- private final String src = System.getProperty("test.src", ".");
- private final String usr = System.getProperty("user.dir", ".");
+public class Basic extends MRTestBase {
@Test
// create a regular, non-multi-release jar
- public void test00() throws IOException {
+ public void test00() throws Throwable {
String jarfile = "test.jar";
compile("test01"); //use same data as test01
Path classes = Paths.get("classes");
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".")
- .assertSuccess();
+ .shouldHaveExitValue(SUCCESS);
checkMultiRelease(jarfile, false);
- Map<String,String[]> names = Map.of(
+ Map<String, String[]> names = Map.of(
"version/Main.class",
- new String[] {"base", "version", "Main.class"},
+ new String[]{"base", "version", "Main.class"},
"version/Version.class",
- new String[] {"base", "version", "Version.class"}
+ new String[]{"base", "version", "Version.class"}
);
compare(jarfile, names);
- delete(jarfile);
- deleteDir(Paths.get(usr, "classes"));
+ FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
+ FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
}
@Test
// create a multi-release jar
- public void test01() throws IOException {
+ public void test01() throws Throwable {
String jarfile = "test.jar";
compile("test01");
@@ -94,68 +84,96 @@
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
"--release", "9", "-C", classes.resolve("v9").toString(), ".",
"--release", "10", "-C", classes.resolve("v10").toString(), ".")
- .assertSuccess();
+ .shouldHaveExitValue(SUCCESS);
checkMultiRelease(jarfile, true);
- Map<String,String[]> names = Map.of(
+ Map<String, String[]> names = Map.of(
"version/Main.class",
- new String[] {"base", "version", "Main.class"},
+ new String[]{"base", "version", "Main.class"},
"version/Version.class",
- new String[] {"base", "version", "Version.class"},
+ new String[]{"base", "version", "Version.class"},
"META-INF/versions/9/version/Version.class",
- new String[] {"v9", "version", "Version.class"},
+ new String[]{"v9", "version", "Version.class"},
"META-INF/versions/10/version/Version.class",
- new String[] {"v10", "version", "Version.class"}
+ new String[]{"v10", "version", "Version.class"}
);
compare(jarfile, names);
- delete(jarfile);
- deleteDir(Paths.get(usr, "classes"));
+ FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
+ FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
+ }
+
+ @Test
+ public void versionFormat() throws Throwable {
+ String jarfile = "test.jar";
+
+ compile("test01");
+
+ Path classes = Paths.get("classes");
+
+ // valid
+ for (String release : List.of("10000", "09", "00010", "10")) {
+ jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
+ "--release", release, "-C", classes.resolve("v10").toString(), ".")
+ .shouldHaveExitValue(SUCCESS)
+ .shouldBeEmpty();
+ }
+ // invalid
+ for (String release : List.of("9.0", "8", "v9",
+ "9v", "0", "-10")) {
+ jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
+ "--release", release, "-C", classes.resolve("v10").toString(), ".")
+ .shouldNotHaveExitValue(SUCCESS)
+ .shouldContain("release " + release + " not valid");
+ }
+ FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
+ FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
}
@Test
// update a regular jar to a multi-release jar
- public void test02() throws IOException {
+ public void test02() throws Throwable {
String jarfile = "test.jar";
compile("test01"); //use same data as test01
Path classes = Paths.get("classes");
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".")
- .assertSuccess();
+ .shouldHaveExitValue(SUCCESS);
checkMultiRelease(jarfile, false);
- jar("uf", jarfile, "--release", "9", "-C", classes.resolve("v9").toString(), ".")
- .assertSuccess();
+ jar("uf", jarfile,
+ "--release", "9", "-C", classes.resolve("v9").toString(), ".")
+ .shouldHaveExitValue(SUCCESS);
checkMultiRelease(jarfile, true);
- Map<String,String[]> names = Map.of(
+ Map<String, String[]> names = Map.of(
"version/Main.class",
- new String[] {"base", "version", "Main.class"},
+ new String[]{"base", "version", "Main.class"},
"version/Version.class",
- new String[] {"base", "version", "Version.class"},
+ new String[]{"base", "version", "Version.class"},
"META-INF/versions/9/version/Version.class",
- new String[] {"v9", "version", "Version.class"}
+ new String[]{"v9", "version", "Version.class"}
);
compare(jarfile, names);
- delete(jarfile);
- deleteDir(Paths.get(usr, "classes"));
+ FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
+ FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
}
@Test
// replace a base entry and a versioned entry
- public void test03() throws IOException {
+ public void test03() throws Throwable {
String jarfile = "test.jar";
compile("test01"); //use same data as test01
@@ -163,19 +181,19 @@
Path classes = Paths.get("classes");
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
"--release", "9", "-C", classes.resolve("v9").toString(), ".")
- .assertSuccess();
+ .shouldHaveExitValue(SUCCESS);
checkMultiRelease(jarfile, true);
- Map<String,String[]> names = Map.of(
+ Map<String, String[]> names = Map.of(
"version/Main.class",
- new String[] {"base", "version", "Main.class"},
+ new String[]{"base", "version", "Main.class"},
"version/Version.class",
- new String[] {"base", "version", "Version.class"},
+ new String[]{"base", "version", "Version.class"},
"META-INF/versions/9/version/Version.class",
- new String[] {"v9", "version", "Version.class"}
+ new String[]{"v9", "version", "Version.class"}
);
compare(jarfile, names);
@@ -184,25 +202,25 @@
// version/Version.class entry in versions/9 section
jar("uf", jarfile, "-C", classes.resolve("v9").toString(), "version",
"--release", "9", "-C", classes.resolve("v10").toString(), ".")
- .assertSuccess();
+ .shouldHaveExitValue(SUCCESS);
checkMultiRelease(jarfile, true);
names = Map.of(
"version/Main.class",
- new String[] {"base", "version", "Main.class"},
+ new String[]{"base", "version", "Main.class"},
"version/Version.class",
- new String[] {"v9", "version", "Version.class"},
+ new String[]{"v9", "version", "Version.class"},
"META-INF/versions/9/version/Version.class",
- new String[] {"v10", "version", "Version.class"}
+ new String[]{"v10", "version", "Version.class"}
);
compare(jarfile, names);
- delete(jarfile);
- deleteDir(Paths.get(usr, "classes"));
+ FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
+ FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
}
/*
@@ -211,7 +229,7 @@
@Test
// META-INF/versions/9 class has different api than base class
- public void test04() throws IOException {
+ public void test04() throws Throwable {
String jarfile = "test.jar";
compile("test01"); //use same data as test01
@@ -224,18 +242,16 @@
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
"--release", "9", "-C", classes.resolve("v9").toString(), ".")
- .assertFailure()
- .resultChecker(r ->
- assertTrue(r.output.contains("different api from earlier"), r.output)
- );
+ .shouldNotHaveExitValue(SUCCESS)
+ .shouldContain("different api from earlier");
- delete(jarfile);
- deleteDir(Paths.get(usr, "classes"));
+ FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
+ FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
}
@Test
// META-INF/versions/9 contains an extra public class
- public void test05() throws IOException {
+ public void test05() throws Throwable {
String jarfile = "test.jar";
compile("test01"); //use same data as test01
@@ -248,18 +264,16 @@
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
"--release", "9", "-C", classes.resolve("v9").toString(), ".")
- .assertFailure()
- .resultChecker(r ->
- assertTrue(r.output.contains("contains a new public class"), r.output)
- );
+ .shouldNotHaveExitValue(SUCCESS)
+ .shouldContain("contains a new public class");
- delete(jarfile);
- deleteDir(Paths.get(usr, "classes"));
+ FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
+ FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
}
@Test
// META-INF/versions/9 contains an extra package private class -- this is okay
- public void test06() throws IOException {
+ public void test06() throws Throwable {
String jarfile = "test.jar";
compile("test01"); //use same data as test01
@@ -272,16 +286,16 @@
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
"--release", "9", "-C", classes.resolve("v9").toString(), ".")
- .assertSuccess();
+ .shouldHaveExitValue(SUCCESS);
- delete(jarfile);
- deleteDir(Paths.get(usr, "classes"));
+ FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
+ FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
}
@Test
// META-INF/versions/9 contains an identical class to base entry class
// this is okay but produces warning
- public void test07() throws IOException {
+ public void test07() throws Throwable {
String jarfile = "test.jar";
compile("test01"); //use same data as test01
@@ -294,19 +308,42 @@
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
"--release", "9", "-C", classes.resolve("v9").toString(), ".")
- .assertSuccess()
- .resultChecker(r ->
- assertTrue(r.outputContains("contains a class that is identical"), r.output)
- );
+ .shouldHaveExitValue(SUCCESS)
+ .shouldContain("contains a class that")
+ .shouldContain("is identical");
+
+ FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
+ FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
+ }
+
+ @Test
+ // META-INF/versions/9 contains an identical class to previous version entry class
+ // this is okay but produces warning
+ public void identicalClassToPreviousVersion() throws Throwable {
+ String jarfile = "test.jar";
- delete(jarfile);
- deleteDir(Paths.get(usr, "classes"));
+ compile("test01"); //use same data as test01
+
+ Path classes = Paths.get("classes");
+
+ jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
+ "--release", "9", "-C", classes.resolve("v9").toString(), ".")
+ .shouldHaveExitValue(SUCCESS)
+ .shouldBeEmpty();
+ jar("uf", jarfile,
+ "--release", "10", "-C", classes.resolve("v9").toString(), ".")
+ .shouldHaveExitValue(SUCCESS)
+ .shouldContain("contains a class that")
+ .shouldContain("is identical");
+
+ FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
+ FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
}
@Test
// resources with same name in different versions
// this is okay but produces warning
- public void test08() throws IOException {
+ public void test08() throws Throwable {
String jarfile = "test.jar";
compile("test01"); //use same data as test01
@@ -320,10 +357,8 @@
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
"--release", "9", "-C", classes.resolve("v9").toString(), ".")
- .assertSuccess()
- .resultChecker(r ->
- assertTrue(r.output.isEmpty(), r.output)
- );
+ .shouldHaveExitValue(SUCCESS)
+ .shouldBeEmpty();
// now add a different resource with same name to META-INF/version/9
Files.copy(source.resolve("Main.java"), classes.resolve("v9")
@@ -331,18 +366,16 @@
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
"--release", "9", "-C", classes.resolve("v9").toString(), ".")
- .assertSuccess()
- .resultChecker(r ->
- assertTrue(r.output.contains("multiple resources with same name"), r.output)
- );
+ .shouldHaveExitValue(SUCCESS)
+ .shouldContain("multiple resources with same name");
- delete(jarfile);
- deleteDir(Paths.get(usr, "classes"));
+ FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
+ FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
}
@Test
// a class with an internal name different from the external name
- public void test09() throws IOException {
+ public void test09() throws Throwable {
String jarfile = "test.jar";
compile("test01"); //use same data as test01
@@ -355,18 +388,16 @@
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
"--release", "9", "-C", classes.resolve("v9").toString(), ".")
- .assertFailure()
- .resultChecker(r ->
- assertTrue(r.output.contains("names do not match"), r.output)
- );
+ .shouldNotHaveExitValue(SUCCESS)
+ .shouldContain("names do not match");
- delete(jarfile);
- deleteDir(Paths.get(usr, "classes"));
+ FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
+ FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
}
@Test
// assure that basic nested classes are acceptable
- public void test10() throws IOException {
+ public void test10() throws Throwable {
String jarfile = "test.jar";
compile("test01"); //use same data as test01
@@ -383,15 +414,15 @@
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
"--release", "9", "-C", classes.resolve("v9").toString(), ".")
- .assertSuccess();
+ .shouldHaveExitValue(SUCCESS);
- delete(jarfile);
- deleteDir(Paths.get(usr, "classes"));
+ FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
+ FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
}
@Test
// a base entry contains a nested class that doesn't have a matching top level class
- public void test11() throws IOException {
+ public void test11() throws Throwable {
String jarfile = "test.jar";
compile("test01"); //use same data as test01
@@ -409,30 +440,29 @@
source = Paths.get(src, "data", "test10", "v9", "version");
javac(classes.resolve("v9"), source.resolve("Nested.java"));
- jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
+ List<String> output = jar("cf", jarfile,
+ "-C", classes.resolve("base").toString(), ".",
"--release", "9", "-C", classes.resolve("v9").toString(), ".")
- .assertFailure()
- .resultChecker(r -> {
- String[] msg = r.output.split("\\R");
- // There should be 3 error messages, cascading from the first. Once we
- // remove the base top level class, the base nested class becomes isolated,
- // also the versioned top level class becomes a new public class, thus ignored
- // for subsequent checks, leading to the associated versioned nested class
- // becoming an isolated nested class
- assertTrue(msg.length == 4);
- assertTrue(msg[0].contains("an isolated nested class"), msg[0]);
- assertTrue(msg[1].contains("contains a new public class"), msg[1]);
- assertTrue(msg[2].contains("an isolated nested class"), msg[2]);
- assertTrue(msg[3].contains("invalid multi-release jar file"), msg[3]);
- });
+ .shouldNotHaveExitValue(SUCCESS)
+ .asLines();
- delete(jarfile);
- deleteDir(Paths.get(usr, "classes"));
+ assertTrue(output.size() == 4);
+ assertTrue(output.get(0).contains("an isolated nested class"),
+ output.get(0));
+ assertTrue(output.get(1).contains("contains a new public class"),
+ output.get(1));
+ assertTrue(output.get(2).contains("an isolated nested class"),
+ output.get(2));
+ assertTrue(output.get(3).contains("invalid multi-release jar file"),
+ output.get(3));
+
+ FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
+ FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
}
@Test
// a versioned entry contains a nested class that doesn't have a matching top level class
- public void test12() throws IOException {
+ public void test12() throws Throwable {
String jarfile = "test.jar";
compile("test01"); //use same data as test01
@@ -452,178 +482,59 @@
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
"--release", "9", "-C", classes.resolve("v9").toString(), ".")
- .assertFailure()
- .resultChecker(r ->
- assertTrue(r.outputContains("an isolated nested class"), r.output)
- );
-
- delete(jarfile);
- deleteDir(Paths.get(usr, "classes"));
- }
-
- /*
- * Test Infrastructure
- */
- private void compile(String test) throws IOException {
- Path classes = Paths.get(usr, "classes", "base");
- Files.createDirectories(classes);
- Path source = Paths.get(src, "data", test, "base", "version");
- javac(classes, source.resolve("Main.java"), source.resolve("Version.java"));
-
- classes = Paths.get(usr, "classes", "v9");
- Files.createDirectories(classes);
- source = Paths.get(src, "data", test, "v9", "version");
- javac(classes, source.resolve("Version.java"));
-
- classes = Paths.get(usr, "classes", "v10");
- Files.createDirectories(classes);
- source = Paths.get(src, "data", test, "v10", "version");
- javac(classes, source.resolve("Version.java"));
- }
-
- private void checkMultiRelease(String jarFile, boolean expected) throws IOException {
- try (JarFile jf = new JarFile(new File(jarFile), true, ZipFile.OPEN_READ,
- JarFile.runtimeVersion())) {
- assertEquals(jf.isMultiRelease(), expected);
- }
- }
+ .shouldNotHaveExitValue(SUCCESS)
+ .shouldContain("an isolated nested class");
- // compares the bytes found in the jar entries with the bytes found in the
- // corresponding data files used to create the entries
- private void compare(String jarfile, Map<String,String[]> names) throws IOException {
- try (JarFile jf = new JarFile(jarfile)) {
- for (String name : names.keySet()) {
- Path path = Paths.get("classes", names.get(name));
- byte[] b1 = Files.readAllBytes(path);
- byte[] b2;
- JarEntry je = jf.getJarEntry(name);
- try (InputStream is = jf.getInputStream(je)) {
- b2 = is.readAllBytes();
- }
- assertEquals(b1,b2);
- }
- }
- }
-
- private void delete(String name) throws IOException {
- Files.deleteIfExists(Paths.get(usr, name));
- }
-
- private void deleteDir(Path dir) throws IOException {
- Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
- @Override
- public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
- Files.delete(file);
- return FileVisitResult.CONTINUE;
- }
-
- @Override
- public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
- Files.delete(dir);
- return FileVisitResult.CONTINUE;
- }
- });
+ FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
+ FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
}
- /*
- * The following methods were taken from modular jar and other jar tests
- */
+ @Test
+ public void testCustomManifest() throws Throwable {
+ String jarfile = "test.jar";
- void javac(Path dest, Path... sourceFiles) throws IOException {
- String javac = JDKToolFinder.getJDKTool("javac");
+ compile("test01");
- List<String> commands = new ArrayList<>();
- commands.add(javac);
- String opts = System.getProperty("test.compiler.opts");
- if (!opts.isEmpty()) {
- commands.addAll(Arrays.asList(opts.split(" +")));
- }
- commands.add("-d");
- commands.add(dest.toString());
- Stream.of(sourceFiles).map(Object::toString).forEach(x -> commands.add(x));
+ Path classes = Paths.get("classes");
+ Path manifest = Paths.get("Manifest.txt");
- quickFail(run(new ProcessBuilder(commands)));
- }
+ // create
+ Files.write(manifest, "Class-Path: MyUtils.jar\n".getBytes());
- Result jarWithStdin(File stdinSource, String... args) {
- String jar = JDKToolFinder.getJDKTool("jar");
- List<String> commands = new ArrayList<>();
- commands.add(jar);
- commands.addAll(Utils.getForwardVmOptions());
- Stream.of(args).forEach(x -> commands.add(x));
- ProcessBuilder p = new ProcessBuilder(commands);
- if (stdinSource != null)
- p.redirectInput(stdinSource);
- return run(p);
- }
+ jar("cfm", jarfile, manifest.toString(),
+ "-C", classes.resolve("base").toString(), ".",
+ "--release", "10", "-C", classes.resolve("v10").toString(), ".")
+ .shouldHaveExitValue(SUCCESS)
+ .shouldBeEmpty();
- Result jar(String... args) {
- return jarWithStdin(null, args);
- }
-
- void quickFail(Result r) {
- if (r.ec != 0)
- throw new RuntimeException(r.output);
- }
-
- Result run(ProcessBuilder pb) {
- Process p;
- out.printf("Running: %s%n", pb.command());
- try {
- p = pb.start();
- } catch (IOException e) {
- throw new RuntimeException(
- format("Couldn't start process '%s'", pb.command()), e);
+ try (JarFile jf = new JarFile(new File(jarfile), true,
+ ZipFile.OPEN_READ, JarFile.runtimeVersion())) {
+ assertTrue(jf.isMultiRelease(), "Not multi-release jar");
+ assertEquals(jf.getManifest()
+ .getMainAttributes()
+ .getValue("Class-Path"),
+ "MyUtils.jar");
}
- String output;
- try {
- output = toString(p.getInputStream(), p.getErrorStream());
- } catch (IOException e) {
- throw new RuntimeException(
- format("Couldn't read process output '%s'", pb.command()), e);
+ // update
+ Files.write(manifest, "Multi-release: false\n".getBytes());
+
+ jar("ufm", jarfile, manifest.toString(),
+ "-C", classes.resolve("base").toString(), ".",
+ "--release", "9", "-C", classes.resolve("v10").toString(), ".")
+ .shouldHaveExitValue(SUCCESS)
+ .shouldContain("WARNING: Duplicate name in Manifest: Multi-release.");
+
+ try (JarFile jf = new JarFile(new File(jarfile), true,
+ ZipFile.OPEN_READ, JarFile.runtimeVersion())) {
+ assertTrue(jf.isMultiRelease(), "Not multi-release jar");
+ assertEquals(jf.getManifest()
+ .getMainAttributes()
+ .getValue("Class-Path"),
+ "MyUtils.jar");
}
- try {
- p.waitFor();
- } catch (InterruptedException e) {
- throw new RuntimeException(
- format("Process hasn't finished '%s'", pb.command()), e);
- }
- return new Result(p.exitValue(), output);
- }
-
- String toString(InputStream in1, InputStream in2) throws IOException {
- try (ByteArrayOutputStream dst = new ByteArrayOutputStream();
- InputStream concatenated = new SequenceInputStream(in1, in2)) {
- concatenated.transferTo(dst);
- return new String(dst.toByteArray(), "UTF-8");
- }
- }
-
- static class Result {
- final int ec;
- final String output;
-
- private Result(int ec, String output) {
- this.ec = ec;
- this.output = output;
- }
-
- boolean outputContains(String msg) {
- return Arrays.stream(output.split("\\R"))
- .collect(Collectors.joining(" "))
- .contains(msg);
- }
-
- Result assertSuccess() {
- assertTrue(ec == 0, format("ec: %d, output: %s", ec, output));
- return this;
- }
- Result assertFailure() {
- assertTrue(ec != 0, format("ec: %d, output: %s", ec, output));
- return this;
- }
- Result resultChecker(Consumer<Result> r) { r.accept(this); return this; }
+ FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
+ FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
}
}
--- a/jdk/test/tools/jar/multiRelease/Basic1.java Wed Jan 18 08:03:04 2017 -0800
+++ b/jdk/test/tools/jar/multiRelease/Basic1.java Wed Jan 18 20:39:08 2017 +0300
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
@@ -28,76 +28,65 @@
* jdk.compiler
* jdk.jartool
* @build jdk.test.lib.JDKToolFinder jdk.test.lib.Utils
+ * @build MRTestBase
* @run testng Basic1
*/
-import static org.testng.Assert.*;
-
import org.testng.annotations.*;
-import java.io.*;
import java.nio.file.*;
import java.util.*;
-import java.util.function.Consumer;
-import java.util.jar.*;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import java.util.zip.*;
-import jdk.test.lib.JDKToolFinder;
-import jdk.test.lib.Utils;
-
-
-import static java.lang.String.format;
-import static java.lang.System.out;
-
-public class Basic1 {
- private final String src = System.getProperty("test.src", ".");
+public class Basic1 extends MRTestBase {
@BeforeTest
- public void setup() throws IOException {
+ public void setup() throws Throwable {
String test = "test01";
- Path classes = Paths.get("classes", "base");
- Files.createDirectories(classes);
+ Path classes = Paths.get("classes");
+
+ Path base = classes.resolve("base");
+ Files.createDirectories(base);
Path source = Paths.get(src, "data", test, "base", "version");
- javac(classes, source.resolve("Main.java"), source.resolve("Version.java"));
+ javac(base, source.resolve("Main.java"), source.resolve("Version.java"));
- Path v9 = Paths.get("v9");
+ Path v9 = classes.resolve("v9");
Files.createDirectories(v9);
source = Paths.get(src, "data", test, "v9", "version");
javac(v9, source.resolve("Version.java"));
- Path v10 = Paths.get("v10");
+ Path v10 = classes.resolve("v10");
Files.createDirectories(v10);
source = Paths.get(src, "data", test, "v10", "version");
javac(v10, source.resolve("Version.java"));
- Path v10_1 = Paths.get("v10_1").resolve("META-INF").resolve("versions").resolve("v10");
+ Path v10_1 = classes.resolve("v10_1").resolve("META-INF").resolve("versions").resolve("v10");
Files.createDirectories(v10_1);
source = Paths.get(src, "data", test, "v10", "version");
javac(v10_1, source.resolve("Version.java"));
}
@Test
- public void test() throws IOException {
+ public void test() throws Throwable {
String jarfile = "test.jar";
Path classes = Paths.get("classes");
- Path v9 = Paths.get("v9");
- Path v10 = Paths.get("v10");
+
+ Path base = classes.resolve("base");
+ Path v9 = classes.resolve("v9");
+ Path v10 = classes.resolve("v10");
- jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
- "--release", "9", "-C", v9.toString(), ".",
- "--release", "10", "-C", v10.toString(), ".")
- .assertSuccess();
+ jar("cf", jarfile, "-C", base.toString(), ".",
+ "--release", "9", "-C", v9.toString(), ".",
+ "--release", "10", "-C", v10.toString(), ".")
+ .shouldHaveExitValue(SUCCESS);
checkMultiRelease(jarfile, true);
- Map<String,String[]> names = Map.of(
- "version/Main.class",
- new String[] {"classes", "base", "version", "Main.class"},
+ Map<String, String[]> names = Map.of(
+ "version/Main.class",
+ new String[]{"base", "version", "Main.class"},
- "version/Version.class",
- new String[] {"classes", "base", "version", "Version.class"},
+ "version/Version.class",
+ new String[]{"base", "version", "Version.class"},
"META-INF/versions/9/version/Version.class",
new String[] {"v9", "version", "Version.class"},
@@ -109,144 +98,16 @@
compare(jarfile, names);
}
-
@Test
- public void testFail() throws IOException {
+ public void testFail() throws Throwable {
String jarfile = "test.jar";
Path classes = Paths.get("classes");
- Path v10 = Paths.get("v10_1");
-
- jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
- "--release", "10", "-C", v10.toString(), ".")
- .assertFailure()
- .outputContains("unexpected versioned entry META-INF/versions/");
- }
-
-
-
- private void checkMultiRelease(String jarFile, boolean expected) throws IOException {
- try (JarFile jf = new JarFile(new File(jarFile), true, ZipFile.OPEN_READ,
- JarFile.runtimeVersion())) {
- assertEquals(jf.isMultiRelease(), expected);
- }
- }
-
- // compares the bytes found in the jar entries with the bytes found in the
- // corresponding data files used to create the entries
- private void compare(String jarfile, Map<String,String[]> names) throws IOException {
- try (JarFile jf = new JarFile(jarfile)) {
- for (String name : names.keySet()) {
- Path path = Paths.get("", names.get(name));
- byte[] b1 = Files.readAllBytes(path);
- byte[] b2;
- JarEntry je = jf.getJarEntry(name);
- try (InputStream is = jf.getInputStream(je)) {
- b2 = is.readAllBytes();
- }
- assertEquals(b1,b2);
- }
- }
- }
-
- /*
- * The following methods were taken from modular jar and other jar tests
- */
-
- void javac(Path dest, Path... sourceFiles) throws IOException {
- String javac = JDKToolFinder.getJDKTool("javac");
-
- List<String> commands = new ArrayList<>();
- commands.add(javac);
- String opts = System.getProperty("test.compiler.opts");
- if (!opts.isEmpty()) {
- commands.addAll(Arrays.asList(opts.split(" +")));
- }
- commands.add("-d");
- commands.add(dest.toString());
- Stream.of(sourceFiles).map(Object::toString).forEach(x -> commands.add(x));
-
- quickFail(run(new ProcessBuilder(commands)));
- }
-
- Result jarWithStdin(File stdinSource, String... args) {
- String jar = JDKToolFinder.getJDKTool("jar");
- List<String> commands = new ArrayList<>();
- commands.add(jar);
- commands.addAll(Utils.getForwardVmOptions());
- Stream.of(args).forEach(x -> commands.add(x));
- ProcessBuilder p = new ProcessBuilder(commands);
- if (stdinSource != null)
- p.redirectInput(stdinSource);
- return run(p);
- }
+ Path base = classes.resolve("base");
+ Path v10 = classes.resolve("v10_1");
- Result jar(String... args) {
- return jarWithStdin(null, args);
- }
-
- void quickFail(Result r) {
- if (r.ec != 0)
- throw new RuntimeException(r.output);
- }
-
- Result run(ProcessBuilder pb) {
- Process p;
- out.printf("Running: %s%n", pb.command());
- try {
- p = pb.start();
- } catch (IOException e) {
- throw new RuntimeException(
- format("Couldn't start process '%s'", pb.command()), e);
- }
-
- String output;
- try {
- output = toString(p.getInputStream(), p.getErrorStream());
- } catch (IOException e) {
- throw new RuntimeException(
- format("Couldn't read process output '%s'", pb.command()), e);
- }
-
- try {
- p.waitFor();
- } catch (InterruptedException e) {
- throw new RuntimeException(
- format("Process hasn't finished '%s'", pb.command()), e);
- }
- return new Result(p.exitValue(), output);
- }
-
- String toString(InputStream in1, InputStream in2) throws IOException {
- try (ByteArrayOutputStream dst = new ByteArrayOutputStream();
- InputStream concatenated = new SequenceInputStream(in1, in2)) {
- concatenated.transferTo(dst);
- return new String(dst.toByteArray(), "UTF-8");
- }
- }
-
- static class Result {
- final int ec;
- final String output;
-
- private Result(int ec, String output) {
- this.ec = ec;
- this.output = output;
- }
-
- boolean outputContains(String msg) {
- return Arrays.stream(output.split("\\R"))
- .collect(Collectors.joining(" "))
- .contains(msg);
- }
-
- Result assertSuccess() {
- assertTrue(ec == 0, format("ec: %d, output: %s", ec, output));
- return this;
- }
- Result assertFailure() {
- assertTrue(ec != 0, format("ec: %d, output: %s", ec, output));
- return this;
- }
- Result resultChecker(Consumer<Result> r) { r.accept(this); return this; }
+ jar("cf", jarfile, "-C", base.toString(), ".",
+ "--release", "10", "-C", v10.toString(), ".")
+ .shouldNotHaveExitValue(SUCCESS)
+ .shouldContain("unexpected versioned entry META-INF/versions/");
}
}