Path: blob/master/test/jdk/tools/jar/compat/CLICompatibility.java
41152 views
/*1* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223import java.io.*;24import java.lang.reflect.Method;25import java.nio.file.Files;26import java.nio.file.Path;27import java.nio.file.Paths;28import java.util.ArrayList;29import java.util.Arrays;30import java.util.List;31import java.util.function.Consumer;32import java.util.jar.JarEntry;33import java.util.jar.JarInputStream;34import java.util.jar.JarOutputStream;35import java.util.stream.Stream;3637import jdk.test.lib.util.FileUtils;38import jdk.test.lib.JDKToolFinder;39import org.testng.annotations.BeforeTest;40import org.testng.annotations.Test;4142import static java.lang.String.format;43import static java.lang.System.out;44import static java.nio.charset.StandardCharsets.UTF_8;45import static org.testng.Assert.assertFalse;46import static org.testng.Assert.assertTrue;4748/*49* @test50* @bug 817095251* @library /lib/testlibrary /test/lib52* @build jdk.test.lib.Platform53* jdk.test.lib.util.FileUtils54* jdk.test.lib.JDKToolFinder55* @run testng CLICompatibility56* @summary Basic test for compatibility of CLI options57*/5859public class CLICompatibility {60static final Path TEST_CLASSES = Paths.get(System.getProperty("test.classes", "."));61static final Path USER_DIR = Paths.get(System.getProperty("user.dir"));6263static final String TOOL_VM_OPTIONS = System.getProperty("test.tool.vm.opts", "");6465final boolean legacyOnly; // for running on older JDK's ( test validation )6667// Resources we know to exist, that can be used for creating jar files.68static final String RES1 = "CLICompatibility.class";69static final String RES2 = "CLICompatibility$Result.class";7071@BeforeTest72public void setupResourcesForJar() throws Exception {73// Copy the files that we are going to use for creating/updating test74// jar files, so that they can be referred to without '-C dir'75Files.copy(TEST_CLASSES.resolve(RES1), USER_DIR.resolve(RES1));76Files.copy(TEST_CLASSES.resolve(RES2), USER_DIR.resolve(RES2));77}7879static final IOConsumer<InputStream> ASSERT_CONTAINS_RES1 = in -> {80try (JarInputStream jin = new JarInputStream(in)) {81assertTrue(jarContains(jin, RES1), "Failed to find " + RES1);82}83};84static final IOConsumer<InputStream> ASSERT_CONTAINS_RES2 = in -> {85try (JarInputStream jin = new JarInputStream(in)) {86assertTrue(jarContains(jin, RES2), "Failed to find " + RES2);87}88};89static final IOConsumer<InputStream> ASSERT_CONTAINS_MAINFEST = in -> {90try (JarInputStream jin = new JarInputStream(in)) {91assertTrue(jin.getManifest() != null, "No META-INF/MANIFEST.MF");92}93};94static final IOConsumer<InputStream> ASSERT_DOES_NOT_CONTAIN_MAINFEST = in -> {95try (JarInputStream jin = new JarInputStream(in)) {96assertTrue(jin.getManifest() == null, "Found unexpected META-INF/MANIFEST.MF");97}98};99100static final FailCheckerWithMessage FAIL_TOO_MANY_MAIN_OPS =101new FailCheckerWithMessage("You may not specify more than one '-cuxtid' options",102/* legacy */ "{ctxui}[vfmn0Me] [jar-file] [manifest-file] [entry-point] [-C dir] files");103104// Create105106@Test107public void createBadArgs() {108final FailCheckerWithMessage FAIL_CREATE_NO_ARGS = new FailCheckerWithMessage(109"'c' flag requires manifest or input files to be specified!");110111jar("c")112.assertFailure()113.resultChecker(FAIL_CREATE_NO_ARGS);114115jar("-c")116.assertFailure()117.resultChecker(FAIL_CREATE_NO_ARGS);118119if (!legacyOnly)120jar("--create")121.assertFailure()122.resultChecker(FAIL_CREATE_NO_ARGS);123124jar("ct")125.assertFailure()126.resultChecker(FAIL_TOO_MANY_MAIN_OPS);127128jar("-ct")129.assertFailure()130.resultChecker(FAIL_TOO_MANY_MAIN_OPS);131132if (!legacyOnly)133jar("--create --list")134.assertFailure()135.resultChecker(FAIL_TOO_MANY_MAIN_OPS);136}137138@Test139public void createWriteToFile() throws IOException {140Path path = Paths.get("createJarFile.jar"); // for creating141String jn = path.toString();142for (String opts : new String[]{"cf " + jn, "-cf " + jn, "--create --file=" + jn}) {143if (legacyOnly && opts.startsWith("--"))144continue;145146jar(opts, RES1)147.assertSuccess()148.resultChecker(r -> {149ASSERT_CONTAINS_RES1.accept(Files.newInputStream(path));150ASSERT_CONTAINS_MAINFEST.accept(Files.newInputStream(path));151});152}153FileUtils.deleteFileIfExistsWithRetry(path);154}155156@Test157public void createWriteToStdout() throws IOException {158for (String opts : new String[]{"c", "-c", "--create"}) {159if (legacyOnly && opts.startsWith("--"))160continue;161162jar(opts, RES1)163.assertSuccess()164.resultChecker(r -> {165ASSERT_CONTAINS_RES1.accept(r.stdoutAsStream());166ASSERT_CONTAINS_MAINFEST.accept(r.stdoutAsStream());167});168}169}170171@Test172public void createWriteToStdoutNoManifest() throws IOException {173for (String opts : new String[]{"cM", "-cM", "--create --no-manifest"} ){174if (legacyOnly && opts.startsWith("--"))175continue;176177jar(opts, RES1)178.assertSuccess()179.resultChecker(r -> {180ASSERT_CONTAINS_RES1.accept(r.stdoutAsStream());181ASSERT_DOES_NOT_CONTAIN_MAINFEST.accept(r.stdoutAsStream());182});183}184}185186// Update187188@Test189public void updateBadArgs() {190final FailCheckerWithMessage FAIL_UPDATE_NO_ARGS = new FailCheckerWithMessage(191"'u' flag requires manifest, 'e' flag or input files to be specified!");192193jar("u")194.assertFailure()195.resultChecker(FAIL_UPDATE_NO_ARGS);196197jar("-u")198.assertFailure()199.resultChecker(FAIL_UPDATE_NO_ARGS);200201if (!legacyOnly)202jar("--update")203.assertFailure()204.resultChecker(FAIL_UPDATE_NO_ARGS);205206jar("ut")207.assertFailure()208.resultChecker(FAIL_TOO_MANY_MAIN_OPS);209210jar("-ut")211.assertFailure()212.resultChecker(FAIL_TOO_MANY_MAIN_OPS);213214if (!legacyOnly)215jar("--update --list")216.assertFailure()217.resultChecker(FAIL_TOO_MANY_MAIN_OPS);218}219220@Test221public void updateReadFileWriteFile() throws IOException {222Path path = Paths.get("updateReadWriteStdout.jar"); // for updating223String jn = path.toString();224225for (String opts : new String[]{"uf " + jn, "-uf " + jn, "--update --file=" + jn}) {226if (legacyOnly && opts.startsWith("--"))227continue;228229createJar(path, RES1);230jar(opts, RES2)231.assertSuccess()232.resultChecker(r -> {233ASSERT_CONTAINS_RES1.accept(Files.newInputStream(path));234ASSERT_CONTAINS_RES2.accept(Files.newInputStream(path));235ASSERT_CONTAINS_MAINFEST.accept(Files.newInputStream(path));236});237}238FileUtils.deleteFileIfExistsWithRetry(path);239}240241@Test242public void updateReadStdinWriteStdout() throws IOException {243Path path = Paths.get("updateReadStdinWriteStdout.jar");244245for (String opts : new String[]{"u", "-u", "--update"}) {246if (legacyOnly && opts.startsWith("--"))247continue;248249createJar(path, RES1);250jarWithStdin(path.toFile(), opts, RES2)251.assertSuccess()252.resultChecker(r -> {253ASSERT_CONTAINS_RES1.accept(r.stdoutAsStream());254ASSERT_CONTAINS_RES2.accept(r.stdoutAsStream());255ASSERT_CONTAINS_MAINFEST.accept(r.stdoutAsStream());256});257}258FileUtils.deleteFileIfExistsWithRetry(path);259}260261@Test262public void updateReadStdinWriteStdoutNoManifest() throws IOException {263Path path = Paths.get("updateReadStdinWriteStdoutNoManifest.jar");264265for (String opts : new String[]{"uM", "-uM", "--update --no-manifest"} ){266if (legacyOnly && opts.startsWith("--"))267continue;268269createJar(path, RES1);270jarWithStdin(path.toFile(), opts, RES2)271.assertSuccess()272.resultChecker(r -> {273ASSERT_CONTAINS_RES1.accept(r.stdoutAsStream());274ASSERT_CONTAINS_RES2.accept(r.stdoutAsStream());275ASSERT_DOES_NOT_CONTAIN_MAINFEST.accept(r.stdoutAsStream());276});277}278FileUtils.deleteFileIfExistsWithRetry(path);279}280281// List282283@Test284public void listBadArgs() {285jar("tx")286.assertFailure()287.resultChecker(FAIL_TOO_MANY_MAIN_OPS);288289jar("-tx")290.assertFailure()291.resultChecker(FAIL_TOO_MANY_MAIN_OPS);292293if (!legacyOnly)294jar("--list --extract")295.assertFailure()296.resultChecker(FAIL_TOO_MANY_MAIN_OPS);297}298299@Test300public void listReadFromFileWriteToStdout() throws IOException {301Path path = Paths.get("listReadFromFileWriteToStdout.jar"); // for listing302createJar(path, RES1);303String jn = path.toString();304305for (String opts : new String[]{"tf " + jn, "-tf " + jn, "--list --file " + jn}) {306if (legacyOnly && opts.startsWith("--"))307continue;308309jar(opts)310.assertSuccess()311.resultChecker(r ->312assertTrue(r.output.contains("META-INF/MANIFEST.MF") && r.output.contains(RES1),313"Failed, got [" + r.output + "]")314);315}316FileUtils.deleteFileIfExistsWithRetry(path);317}318319@Test320public void listReadFromStdinWriteToStdout() throws IOException {321Path path = Paths.get("listReadFromStdinWriteToStdout.jar");322createJar(path, RES1);323324for (String opts : new String[]{"t", "-t", "--list"} ){325if (legacyOnly && opts.startsWith("--"))326continue;327328jarWithStdin(path.toFile(), opts)329.assertSuccess()330.resultChecker(r ->331assertTrue(r.output.contains("META-INF/MANIFEST.MF") && r.output.contains(RES1),332"Failed, got [" + r.output + "]")333);334}335FileUtils.deleteFileIfExistsWithRetry(path);336}337338// Extract339340@Test341public void extractBadArgs() {342jar("xi")343.assertFailure()344.resultChecker(FAIL_TOO_MANY_MAIN_OPS);345346jar("-xi")347.assertFailure()348.resultChecker(FAIL_TOO_MANY_MAIN_OPS);349350if (!legacyOnly) {351jar("--extract --generate-index")352.assertFailure()353.resultChecker(new FailCheckerWithMessage(354"option --generate-index requires an argument"));355356jar("--extract --generate-index=foo")357.assertFailure()358.resultChecker(FAIL_TOO_MANY_MAIN_OPS);359}360}361362@Test363public void extractReadFromStdin() throws IOException {364Path path = Paths.get("extract");365Path jarPath = path.resolve("extractReadFromStdin.jar"); // for extracting366createJar(jarPath, RES1);367368for (String opts : new String[]{"x" ,"-x", "--extract"}) {369if (legacyOnly && opts.startsWith("--"))370continue;371372jarWithStdinAndWorkingDir(jarPath.toFile(), path.toFile(), opts)373.assertSuccess()374.resultChecker(r ->375assertTrue(Files.exists(path.resolve(RES1)),376"Expected to find:" + path.resolve(RES1))377);378FileUtils.deleteFileIfExistsWithRetry(path.resolve(RES1));379}380FileUtils.deleteFileTreeWithRetry(path);381}382383@Test384public void extractReadFromFile() throws IOException {385Path path = Paths.get("extract");386String jn = "extractReadFromFile.jar";387Path jarPath = path.resolve(jn);388createJar(jarPath, RES1);389390for (String opts : new String[]{"xf "+jn ,"-xf "+jn, "--extract --file "+jn}) {391if (legacyOnly && opts.startsWith("--"))392continue;393394jarWithStdinAndWorkingDir(null, path.toFile(), opts)395.assertSuccess()396.resultChecker(r ->397assertTrue(Files.exists(path.resolve(RES1)),398"Expected to find:" + path.resolve(RES1))399);400FileUtils.deleteFileIfExistsWithRetry(path.resolve(RES1));401}402FileUtils.deleteFileTreeWithRetry(path);403}404405// Basic help406407@Test408public void helpBadOptionalArg() {409if (legacyOnly)410return;411412jar("--help:")413.assertFailure();414415jar("--help:blah")416.assertFailure();417}418419@Test420public void help() {421if (legacyOnly)422return;423424jar("-h")425.assertSuccess()426.resultChecker(r ->427assertTrue(r.output.startsWith("Usage: jar [OPTION...] [ [--release VERSION] [-C dir] files]"),428"Failed, got [" + r.output + "]")429);430431jar("--help")432.assertSuccess()433.resultChecker(r -> {434assertTrue(r.output.startsWith("Usage: jar [OPTION...] [ [--release VERSION] [-C dir] files]"),435"Failed, got [" + r.output + "]");436assertFalse(r.output.contains("--do-not-resolve-by-default"));437assertFalse(r.output.contains("--warn-if-resolved"));438});439440jar("--help:compat")441.assertSuccess()442.resultChecker(r ->443assertTrue(r.output.startsWith("Compatibility Interface:"),444"Failed, got [" + r.output + "]")445);446447jar("--help-extra")448.assertSuccess()449.resultChecker(r -> {450assertTrue(r.output.startsWith("Usage: jar [OPTION...] [ [--release VERSION] [-C dir] files]"),451"Failed, got [" + r.output + "]");452assertTrue(r.output.contains("--do-not-resolve-by-default"));453assertTrue(r.output.contains("--warn-if-resolved"));454});455}456457// -- Infrastructure458459static boolean jarContains(JarInputStream jis, String entryName)460throws IOException461{462JarEntry e;463boolean found = false;464while((e = jis.getNextJarEntry()) != null) {465if (e.getName().equals(entryName))466return true;467}468return false;469}470471/* Creates a simple jar with entries of size 0, good enough for testing */472static void createJar(Path path, String... entries) throws IOException {473FileUtils.deleteFileIfExistsWithRetry(path);474Path parent = path.getParent();475if (parent != null)476Files.createDirectories(parent);477try (OutputStream out = Files.newOutputStream(path);478JarOutputStream jos = new JarOutputStream(out)) {479JarEntry je = new JarEntry("META-INF/MANIFEST.MF");480jos.putNextEntry(je);481jos.closeEntry();482483for (String entry : entries) {484je = new JarEntry(entry);485jos.putNextEntry(je);486jos.closeEntry();487}488}489}490491static class FailCheckerWithMessage implements Consumer<Result> {492final String[] messages;493FailCheckerWithMessage(String... m) {494messages = m;495}496@Override497public void accept(Result r) {498//out.printf("%s%n", r.output);499boolean found = false;500for (String m : messages) {501if (r.output.contains(m)) {502found = true;503break;504}505}506assertTrue(found,507"Excepted out to contain one of: " + Arrays.asList(messages)508+ " but got: " + r.output);509}510}511512static Result jar(String... args) {513return jarWithStdinAndWorkingDir(null, null, args);514}515516static Result jarWithStdin(File stdinSource, String... args) {517return jarWithStdinAndWorkingDir(stdinSource, null, args);518}519520static Result jarWithStdinAndWorkingDir(File stdinFrom,521File workingDir,522String... args) {523String jar = getJDKTool("jar");524List<String> commands = new ArrayList<>();525commands.add(jar);526if (!TOOL_VM_OPTIONS.isEmpty()) {527commands.addAll(Arrays.asList(TOOL_VM_OPTIONS.split("\\s+", -1)));528}529Stream.of(args).map(s -> s.split(" "))530.flatMap(Arrays::stream)531.forEach(x -> commands.add(x));532ProcessBuilder p = new ProcessBuilder(commands);533if (stdinFrom != null)534p.redirectInput(stdinFrom);535if (workingDir != null)536p.directory(workingDir);537return run(p);538}539540static Result run(ProcessBuilder pb) {541Process p;542byte[] stdout, stderr;543out.printf("Running: %s%n", pb.command());544try {545p = pb.start();546} catch (IOException e) {547throw new RuntimeException(548format("Couldn't start process '%s'", pb.command()), e);549}550551String output;552try {553stdout = readAllBytes(p.getInputStream());554stderr = readAllBytes(p.getErrorStream());555556output = toString(stdout, stderr);557} catch (IOException e) {558throw new RuntimeException(559format("Couldn't read process output '%s'", pb.command()), e);560}561562try {563p.waitFor();564} catch (InterruptedException e) {565throw new RuntimeException(566format("Process hasn't finished '%s'", pb.command()), e);567}568return new Result(p.exitValue(), stdout, stderr, output);569}570571static final Path JAVA_HOME = Paths.get(System.getProperty("java.home"));572573static String getJDKTool(String name) {574try {575return JDKToolFinder.getJDKTool(name);576} catch (Exception x) {577Path j = JAVA_HOME.resolve("bin").resolve(name);578if (Files.exists(j))579return j.toString();580j = JAVA_HOME.resolve("..").resolve("bin").resolve(name);581if (Files.exists(j))582return j.toString();583throw new RuntimeException(x);584}585}586587static String toString(byte[] ba1, byte[] ba2) {588return (new String(ba1, UTF_8)).concat(new String(ba2, UTF_8));589}590591static class Result {592final int exitValue;593final byte[] stdout;594final byte[] stderr;595final String output;596597private Result(int exitValue, byte[] stdout, byte[] stderr, String output) {598this.exitValue = exitValue;599this.stdout = stdout;600this.stderr = stderr;601this.output = output;602}603604InputStream stdoutAsStream() { return new ByteArrayInputStream(stdout); }605606Result assertSuccess() { assertTrue(exitValue == 0, output); return this; }607Result assertFailure() { assertTrue(exitValue != 0, output); return this; }608609Result resultChecker(IOConsumer<Result> r) {610try { r.accept(this); return this; }611catch (IOException x) { throw new UncheckedIOException(x); }612}613614Result resultChecker(FailCheckerWithMessage c) { c.accept(this); return this; }615}616617interface IOConsumer<T> { void accept(T t) throws IOException ; }618619// readAllBytes implementation so the test can be run pre 1.9 ( legacyOnly )620static byte[] readAllBytes(InputStream is) throws IOException {621byte[] buf = new byte[8192];622int capacity = buf.length;623int nread = 0;624int n;625for (;;) {626// read to EOF which may read more or less than initial buffer size627while ((n = is.read(buf, nread, capacity - nread)) > 0)628nread += n;629630// if the last call to read returned -1, then we're done631if (n < 0)632break;633634// need to allocate a larger buffer635capacity = capacity << 1;636637buf = Arrays.copyOf(buf, capacity);638}639return (capacity == nread) ? buf : Arrays.copyOf(buf, nread);640}641642// Standalone entry point for running with, possibly older, JDKs.643public static void main(String[] args) throws Throwable {644boolean legacyOnly = false;645if (args.length != 0 && args[0].equals("legacyOnly"))646legacyOnly = true;647648CLICompatibility test = new CLICompatibility(legacyOnly);649for (Method m : CLICompatibility.class.getDeclaredMethods()) {650if (m.getAnnotation(Test.class) != null) {651System.out.println("Invoking " + m.getName());652m.invoke(test);653}654}655}656CLICompatibility(boolean legacyOnly) { this.legacyOnly = legacyOnly; }657CLICompatibility() { this.legacyOnly = false; }658}659660661