jdk/test/tools/jar/compat/CLICompatibility.java
changeset 36511 9d0388c6b336
child 38762 2956ccc7cb77
equal deleted inserted replaced
36510:043f1af70518 36511:9d0388c6b336
       
     1 /*
       
     2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  */
       
    23 
       
    24 import java.io.*;
       
    25 import java.lang.reflect.Method;
       
    26 import java.nio.file.Files;
       
    27 import java.nio.file.Path;
       
    28 import java.nio.file.Paths;
       
    29 import java.util.ArrayList;
       
    30 import java.util.Arrays;
       
    31 import java.util.List;
       
    32 import java.util.function.Consumer;
       
    33 import java.util.jar.JarEntry;
       
    34 import java.util.jar.JarInputStream;
       
    35 import java.util.jar.JarOutputStream;
       
    36 import java.util.stream.Stream;
       
    37 
       
    38 import jdk.testlibrary.FileUtils;
       
    39 import jdk.testlibrary.JDKToolFinder;
       
    40 import org.testng.annotations.BeforeTest;
       
    41 import org.testng.annotations.Test;
       
    42 
       
    43 import static java.lang.String.format;
       
    44 import static java.lang.System.out;
       
    45 import static java.nio.charset.StandardCharsets.UTF_8;
       
    46 import static org.testng.Assert.assertTrue;
       
    47 
       
    48 /*
       
    49  * @test
       
    50  * @library /lib/testlibrary
       
    51  * @build jdk.testlibrary.FileUtils jdk.testlibrary.JDKToolFinder
       
    52  * @run testng CLICompatibility
       
    53  * @summary Basic test for compatibility of CLI options
       
    54  */
       
    55 
       
    56 public class CLICompatibility {
       
    57     static final Path TEST_CLASSES = Paths.get(System.getProperty("test.classes", "."));
       
    58     static final Path USER_DIR = Paths.get(System.getProperty("user.dir"));
       
    59 
       
    60     final boolean legacyOnly;  // for running on older JDK's ( test validation )
       
    61 
       
    62     // Resources we know to exist, that can be used for creating jar files.
       
    63     static final String RES1 = "CLICompatibility.class";
       
    64     static final String RES2 = "CLICompatibility$Result.class";
       
    65 
       
    66     @BeforeTest
       
    67     public void setupResourcesForJar() throws Exception {
       
    68         // Copy the files that we are going to use for creating/updating test
       
    69         // jar files, so that they can be referred to without '-C dir'
       
    70         Files.copy(TEST_CLASSES.resolve(RES1), USER_DIR.resolve(RES1));
       
    71         Files.copy(TEST_CLASSES.resolve(RES2), USER_DIR.resolve(RES2));
       
    72     }
       
    73 
       
    74     static final IOConsumer<InputStream> ASSERT_CONTAINS_RES1 = in -> {
       
    75         try (JarInputStream jin = new JarInputStream(in)) {
       
    76             assertTrue(jarContains(jin, RES1), "Failed to find " + RES1);
       
    77         }
       
    78     };
       
    79     static final IOConsumer<InputStream> ASSERT_CONTAINS_RES2 = in -> {
       
    80         try (JarInputStream jin = new JarInputStream(in)) {
       
    81             assertTrue(jarContains(jin, RES2), "Failed to find " + RES2);
       
    82         }
       
    83     };
       
    84     static final IOConsumer<InputStream> ASSERT_CONTAINS_MAINFEST = in -> {
       
    85         try (JarInputStream jin = new JarInputStream(in)) {
       
    86             assertTrue(jin.getManifest() != null, "No META-INF/MANIFEST.MF");
       
    87         }
       
    88     };
       
    89     static final IOConsumer<InputStream> ASSERT_DOES_NOT_CONTAIN_MAINFEST = in -> {
       
    90         try (JarInputStream jin = new JarInputStream(in)) {
       
    91             assertTrue(jin.getManifest() == null, "Found unexpected META-INF/MANIFEST.MF");
       
    92         }
       
    93     };
       
    94 
       
    95     static final FailCheckerWithMessage FAIL_TOO_MANY_MAIN_OPS =
       
    96         new FailCheckerWithMessage("You must specify one of -ctxui options",
       
    97         /* legacy */ "{ctxui}[vfmn0Me] [jar-file] [manifest-file] [entry-point] [-C dir] files");
       
    98 
       
    99     // Create
       
   100 
       
   101     @Test
       
   102     public void createBadArgs() {
       
   103         final FailCheckerWithMessage FAIL_CREATE_NO_ARGS = new FailCheckerWithMessage(
       
   104                 "'c' flag requires manifest or input files to be specified!");
       
   105 
       
   106         jar("c")
       
   107             .assertFailure()
       
   108             .resultChecker(FAIL_CREATE_NO_ARGS);
       
   109 
       
   110         jar("-c")
       
   111             .assertFailure()
       
   112             .resultChecker(FAIL_CREATE_NO_ARGS);
       
   113 
       
   114         if (!legacyOnly)
       
   115             jar("--create")
       
   116                 .assertFailure()
       
   117                 .resultChecker(FAIL_CREATE_NO_ARGS);
       
   118 
       
   119         jar("ct")
       
   120             .assertFailure()
       
   121             .resultChecker(FAIL_TOO_MANY_MAIN_OPS);
       
   122 
       
   123         jar("-ct")
       
   124             .assertFailure()
       
   125             .resultChecker(FAIL_TOO_MANY_MAIN_OPS);
       
   126 
       
   127         if (!legacyOnly)
       
   128             jar("--create --list")
       
   129                 .assertFailure()
       
   130                 .resultChecker(FAIL_TOO_MANY_MAIN_OPS);
       
   131     }
       
   132 
       
   133     @Test
       
   134     public void createWriteToFile() throws IOException {
       
   135         Path path = Paths.get("createJarFile.jar");  // for creating
       
   136         String jn = path.toString();
       
   137         for (String opts : new String[]{"cf " + jn, "-cf " + jn, "--create --file=" + jn}) {
       
   138             if (legacyOnly && opts.startsWith("--"))
       
   139                 continue;
       
   140 
       
   141             jar(opts, RES1)
       
   142                 .assertSuccess()
       
   143                 .resultChecker(r -> {
       
   144                     ASSERT_CONTAINS_RES1.accept(Files.newInputStream(path));
       
   145                     ASSERT_CONTAINS_MAINFEST.accept(Files.newInputStream(path));
       
   146                 });
       
   147         }
       
   148         FileUtils.deleteFileIfExistsWithRetry(path);
       
   149     }
       
   150 
       
   151     @Test
       
   152     public void createWriteToStdout() throws IOException {
       
   153         for (String opts : new String[]{"c", "-c", "--create"}) {
       
   154             if (legacyOnly && opts.startsWith("--"))
       
   155                 continue;
       
   156 
       
   157             jar(opts, RES1)
       
   158                 .assertSuccess()
       
   159                 .resultChecker(r -> {
       
   160                     ASSERT_CONTAINS_RES1.accept(r.stdoutAsStream());
       
   161                     ASSERT_CONTAINS_MAINFEST.accept(r.stdoutAsStream());
       
   162                 });
       
   163         }
       
   164     }
       
   165 
       
   166     @Test
       
   167     public void createWriteToStdoutNoManifest() throws IOException {
       
   168         for (String opts : new String[]{"cM", "-cM", "--create --no-manifest"} ){
       
   169             if (legacyOnly && opts.startsWith("--"))
       
   170                 continue;
       
   171 
       
   172             jar(opts, RES1)
       
   173                 .assertSuccess()
       
   174                 .resultChecker(r -> {
       
   175                     ASSERT_CONTAINS_RES1.accept(r.stdoutAsStream());
       
   176                     ASSERT_DOES_NOT_CONTAIN_MAINFEST.accept(r.stdoutAsStream());
       
   177                 });
       
   178         }
       
   179     }
       
   180 
       
   181     // Update
       
   182 
       
   183     @Test
       
   184     public void updateBadArgs() {
       
   185         final FailCheckerWithMessage FAIL_UPDATE_NO_ARGS = new FailCheckerWithMessage(
       
   186                 "'u' flag requires manifest, 'e' flag or input files to be specified!");
       
   187 
       
   188         jar("u")
       
   189             .assertFailure()
       
   190             .resultChecker(FAIL_UPDATE_NO_ARGS);
       
   191 
       
   192         jar("-u")
       
   193             .assertFailure()
       
   194             .resultChecker(FAIL_UPDATE_NO_ARGS);
       
   195 
       
   196         if (!legacyOnly)
       
   197             jar("--update")
       
   198                 .assertFailure()
       
   199                 .resultChecker(FAIL_UPDATE_NO_ARGS);
       
   200 
       
   201         jar("ut")
       
   202             .assertFailure()
       
   203             .resultChecker(FAIL_TOO_MANY_MAIN_OPS);
       
   204 
       
   205         jar("-ut")
       
   206             .assertFailure()
       
   207             .resultChecker(FAIL_TOO_MANY_MAIN_OPS);
       
   208 
       
   209         if (!legacyOnly)
       
   210             jar("--update --list")
       
   211                 .assertFailure()
       
   212                 .resultChecker(FAIL_TOO_MANY_MAIN_OPS);
       
   213     }
       
   214 
       
   215     @Test
       
   216     public void updateReadFileWriteFile() throws IOException {
       
   217         Path path = Paths.get("updateReadWriteStdout.jar");  // for updating
       
   218         String jn = path.toString();
       
   219 
       
   220         for (String opts : new String[]{"uf " + jn, "-uf " + jn, "--update --file=" + jn}) {
       
   221             if (legacyOnly && opts.startsWith("--"))
       
   222                 continue;
       
   223 
       
   224             createJar(path, RES1);
       
   225             jar(opts, RES2)
       
   226                 .assertSuccess()
       
   227                 .resultChecker(r -> {
       
   228                     ASSERT_CONTAINS_RES1.accept(Files.newInputStream(path));
       
   229                     ASSERT_CONTAINS_RES2.accept(Files.newInputStream(path));
       
   230                     ASSERT_CONTAINS_MAINFEST.accept(Files.newInputStream(path));
       
   231                 });
       
   232         }
       
   233         FileUtils.deleteFileIfExistsWithRetry(path);
       
   234     }
       
   235 
       
   236     @Test
       
   237     public void updateReadStdinWriteStdout() throws IOException {
       
   238         Path path = Paths.get("updateReadStdinWriteStdout.jar");
       
   239 
       
   240         for (String opts : new String[]{"u", "-u", "--update"}) {
       
   241             if (legacyOnly && opts.startsWith("--"))
       
   242                 continue;
       
   243 
       
   244             createJar(path, RES1);
       
   245             jarWithStdin(path.toFile(), opts, RES2)
       
   246                 .assertSuccess()
       
   247                 .resultChecker(r -> {
       
   248                     ASSERT_CONTAINS_RES1.accept(r.stdoutAsStream());
       
   249                     ASSERT_CONTAINS_RES2.accept(r.stdoutAsStream());
       
   250                     ASSERT_CONTAINS_MAINFEST.accept(r.stdoutAsStream());
       
   251                 });
       
   252         }
       
   253         FileUtils.deleteFileIfExistsWithRetry(path);
       
   254     }
       
   255 
       
   256     @Test
       
   257     public void updateReadStdinWriteStdoutNoManifest() throws IOException {
       
   258         Path path = Paths.get("updateReadStdinWriteStdoutNoManifest.jar");
       
   259 
       
   260         for (String opts : new String[]{"uM", "-uM", "--update --no-manifest"} ){
       
   261             if (legacyOnly && opts.startsWith("--"))
       
   262                 continue;
       
   263 
       
   264             createJar(path, RES1);
       
   265             jarWithStdin(path.toFile(), opts, RES2)
       
   266                 .assertSuccess()
       
   267                 .resultChecker(r -> {
       
   268                     ASSERT_CONTAINS_RES1.accept(r.stdoutAsStream());
       
   269                     ASSERT_CONTAINS_RES2.accept(r.stdoutAsStream());
       
   270                     ASSERT_DOES_NOT_CONTAIN_MAINFEST.accept(r.stdoutAsStream());
       
   271                 });
       
   272         }
       
   273         FileUtils.deleteFileIfExistsWithRetry(path);
       
   274     }
       
   275 
       
   276     // List
       
   277 
       
   278     @Test
       
   279     public void listBadArgs() {
       
   280         jar("te")
       
   281             .assertFailure()
       
   282             .resultChecker(FAIL_TOO_MANY_MAIN_OPS);
       
   283 
       
   284         jar("-te")
       
   285             .assertFailure()
       
   286             .resultChecker(FAIL_TOO_MANY_MAIN_OPS);
       
   287 
       
   288         if (!legacyOnly)
       
   289             jar("--list --extract")
       
   290                 .assertFailure()
       
   291                 .resultChecker(FAIL_TOO_MANY_MAIN_OPS);
       
   292     }
       
   293 
       
   294     @Test
       
   295     public void listReadFromFileWriteToStdout() throws IOException {
       
   296         Path path = Paths.get("listReadFromFileWriteToStdout.jar");  // for listing
       
   297         createJar(path, RES1);
       
   298         String jn = path.toString();
       
   299 
       
   300         for (String opts : new String[]{"tf " + jn, "-tf " + jn, "--list --file " + jn}) {
       
   301             if (legacyOnly && opts.startsWith("--"))
       
   302                 continue;
       
   303 
       
   304             jar(opts)
       
   305                 .assertSuccess()
       
   306                 .resultChecker(r ->
       
   307                     assertTrue(r.output.contains("META-INF/MANIFEST.MF") && r.output.contains(RES1),
       
   308                                "Failed, got [" + r.output + "]")
       
   309                 );
       
   310         }
       
   311         FileUtils.deleteFileIfExistsWithRetry(path);
       
   312     }
       
   313 
       
   314     @Test
       
   315     public void listReadFromStdinWriteToStdout() throws IOException {
       
   316         Path path = Paths.get("listReadFromStdinWriteToStdout.jar");
       
   317         createJar(path, RES1);
       
   318 
       
   319         for (String opts : new String[]{"t", "-t", "--list"} ){
       
   320             if (legacyOnly && opts.startsWith("--"))
       
   321                 continue;
       
   322 
       
   323             jarWithStdin(path.toFile(), opts)
       
   324                 .assertSuccess()
       
   325                 .resultChecker(r ->
       
   326                     assertTrue(r.output.contains("META-INF/MANIFEST.MF") && r.output.contains(RES1),
       
   327                                "Failed, got [" + r.output + "]")
       
   328                 );
       
   329         }
       
   330         FileUtils.deleteFileIfExistsWithRetry(path);
       
   331     }
       
   332 
       
   333     // Extract
       
   334 
       
   335     @Test
       
   336     public void extractBadArgs() {
       
   337         jar("xi")
       
   338             .assertFailure()
       
   339             .resultChecker(FAIL_TOO_MANY_MAIN_OPS);
       
   340 
       
   341         jar("-xi")
       
   342             .assertFailure()
       
   343             .resultChecker(FAIL_TOO_MANY_MAIN_OPS);
       
   344 
       
   345         if (!legacyOnly)
       
   346             jar("--extract --generate-index")
       
   347                 .assertFailure()
       
   348                 .resultChecker(FAIL_TOO_MANY_MAIN_OPS);
       
   349     }
       
   350 
       
   351     @Test
       
   352     public void extractReadFromStdin() throws IOException {
       
   353         Path path = Paths.get("extract");
       
   354         Path jarPath = path.resolve("extractReadFromStdin.jar"); // for extracting
       
   355         createJar(jarPath, RES1);
       
   356 
       
   357         for (String opts : new String[]{"x" ,"-x", "--extract"}) {
       
   358             if (legacyOnly && opts.startsWith("--"))
       
   359                 continue;
       
   360 
       
   361             jarWithStdinAndWorkingDir(jarPath.toFile(), path.toFile(), opts)
       
   362                 .assertSuccess()
       
   363                 .resultChecker(r ->
       
   364                     assertTrue(Files.exists(path.resolve(RES1)),
       
   365                                "Expected to find:" + path.resolve(RES1))
       
   366                 );
       
   367             FileUtils.deleteFileIfExistsWithRetry(path.resolve(RES1));
       
   368         }
       
   369         FileUtils.deleteFileTreeWithRetry(path);
       
   370     }
       
   371 
       
   372     @Test
       
   373     public void extractReadFromFile() throws IOException {
       
   374         Path path = Paths.get("extract");
       
   375         String jn = "extractReadFromFile.jar";
       
   376         Path jarPath = path.resolve(jn);
       
   377         createJar(jarPath, RES1);
       
   378 
       
   379         for (String opts : new String[]{"xf "+jn ,"-xf "+jn, "--extract --file "+jn}) {
       
   380             if (legacyOnly && opts.startsWith("--"))
       
   381                 continue;
       
   382 
       
   383             jarWithStdinAndWorkingDir(null, path.toFile(), opts)
       
   384                 .assertSuccess()
       
   385                 .resultChecker(r ->
       
   386                     assertTrue(Files.exists(path.resolve(RES1)),
       
   387                                "Expected to find:" + path.resolve(RES1))
       
   388                 );
       
   389             FileUtils.deleteFileIfExistsWithRetry(path.resolve(RES1));
       
   390         }
       
   391         FileUtils.deleteFileTreeWithRetry(path);
       
   392     }
       
   393 
       
   394     // Basic help
       
   395 
       
   396     @Test
       
   397     public void helpBadOptionalArg() {
       
   398         if (legacyOnly)
       
   399             return;
       
   400 
       
   401         jar("--help:")
       
   402             .assertFailure();
       
   403 
       
   404         jar("--help:blah")
       
   405             .assertFailure();
       
   406     }
       
   407 
       
   408     @Test
       
   409     public void help() {
       
   410         if (legacyOnly)
       
   411             return;
       
   412 
       
   413         jar("-h")
       
   414             .assertSuccess()
       
   415             .resultChecker(r ->
       
   416                 assertTrue(r.output.startsWith("Usage: jar [OPTION...] [-C dir] files"),
       
   417                            "Failed, got [" + r.output + "]")
       
   418             );
       
   419 
       
   420         jar("--help")
       
   421             .assertSuccess()
       
   422             .resultChecker(r ->
       
   423                 assertTrue(r.output.startsWith("Usage: jar [OPTION...] [-C dir] files"),
       
   424                            "Failed, got [" + r.output + "]")
       
   425             );
       
   426 
       
   427         jar("--help:compat")
       
   428             .assertSuccess()
       
   429             .resultChecker(r ->
       
   430                 assertTrue(r.output.startsWith("Compatibility Interface:"),
       
   431                            "Failed, got [" + r.output + "]")
       
   432             );
       
   433     }
       
   434 
       
   435     // -- Infrastructure
       
   436 
       
   437     static boolean jarContains(JarInputStream jis, String entryName)
       
   438         throws IOException
       
   439     {
       
   440         JarEntry e;
       
   441         boolean found = false;
       
   442         while((e = jis.getNextJarEntry()) != null) {
       
   443             if (e.getName().equals(entryName))
       
   444                 return true;
       
   445         }
       
   446         return false;
       
   447     }
       
   448 
       
   449     /* Creates a simple jar with entries of size 0, good enough for testing */
       
   450     static void createJar(Path path, String... entries) throws IOException {
       
   451         FileUtils.deleteFileIfExistsWithRetry(path);
       
   452         Path parent = path.getParent();
       
   453         if (parent != null)
       
   454             Files.createDirectories(parent);
       
   455         try (OutputStream out = Files.newOutputStream(path);
       
   456              JarOutputStream jos = new JarOutputStream(out)) {
       
   457             JarEntry je = new JarEntry("META-INF/MANIFEST.MF");
       
   458             jos.putNextEntry(je);
       
   459             jos.closeEntry();
       
   460 
       
   461             for (String entry : entries) {
       
   462                 je = new JarEntry(entry);
       
   463                 jos.putNextEntry(je);
       
   464                 jos.closeEntry();
       
   465             }
       
   466         }
       
   467     }
       
   468 
       
   469     static class FailCheckerWithMessage implements Consumer<Result> {
       
   470         final String[] messages;
       
   471         FailCheckerWithMessage(String... m) {
       
   472             messages = m;
       
   473         }
       
   474         @Override
       
   475         public void accept(Result r) {
       
   476             //out.printf("%s%n", r.output);
       
   477             boolean found = false;
       
   478             for (String m : messages) {
       
   479                 if (r.output.contains(m)) {
       
   480                     found = true;
       
   481                     break;
       
   482                 }
       
   483             }
       
   484             assertTrue(found,
       
   485                        "Excepted out to contain one of: " + Arrays.asList(messages)
       
   486                            + " but got: " + r.output);
       
   487         }
       
   488     }
       
   489 
       
   490     static Result jar(String... args) {
       
   491         return jarWithStdinAndWorkingDir(null, null, args);
       
   492     }
       
   493 
       
   494     static Result jarWithStdin(File stdinSource, String... args) {
       
   495         return jarWithStdinAndWorkingDir(stdinSource, null, args);
       
   496     }
       
   497 
       
   498     static Result jarWithStdinAndWorkingDir(File stdinFrom,
       
   499                                             File workingDir,
       
   500                                             String... args) {
       
   501         String jar = getJDKTool("jar");
       
   502         List<String> commands = new ArrayList<>();
       
   503         commands.add(jar);
       
   504         Stream.of(args).map(s -> s.split(" "))
       
   505                        .flatMap(Arrays::stream)
       
   506                        .forEach(x -> commands.add(x));
       
   507         ProcessBuilder p = new ProcessBuilder(commands);
       
   508         if (stdinFrom != null)
       
   509             p.redirectInput(stdinFrom);
       
   510         if (workingDir != null)
       
   511             p.directory(workingDir);
       
   512         return run(p);
       
   513     }
       
   514 
       
   515     static Result run(ProcessBuilder pb) {
       
   516         Process p;
       
   517         byte[] stdout, stderr;
       
   518         out.printf("Running: %s%n", pb.command());
       
   519         try {
       
   520             p = pb.start();
       
   521         } catch (IOException e) {
       
   522             throw new RuntimeException(
       
   523                     format("Couldn't start process '%s'", pb.command()), e);
       
   524         }
       
   525 
       
   526         String output;
       
   527         try {
       
   528             stdout = readAllBytes(p.getInputStream());
       
   529             stderr = readAllBytes(p.getErrorStream());
       
   530 
       
   531             output = toString(stdout, stderr);
       
   532         } catch (IOException e) {
       
   533             throw new RuntimeException(
       
   534                     format("Couldn't read process output '%s'", pb.command()), e);
       
   535         }
       
   536 
       
   537         try {
       
   538             p.waitFor();
       
   539         } catch (InterruptedException e) {
       
   540             throw new RuntimeException(
       
   541                     format("Process hasn't finished '%s'", pb.command()), e);
       
   542         }
       
   543         return new Result(p.exitValue(), stdout, stderr, output);
       
   544     }
       
   545 
       
   546     static final Path JAVA_HOME = Paths.get(System.getProperty("java.home"));
       
   547 
       
   548     static String getJDKTool(String name) {
       
   549         try {
       
   550             return JDKToolFinder.getJDKTool(name);
       
   551         } catch (Exception x) {
       
   552             Path j = JAVA_HOME.resolve("bin").resolve(name);
       
   553             if (Files.exists(j))
       
   554                 return j.toString();
       
   555             j = JAVA_HOME.resolve("..").resolve("bin").resolve(name);
       
   556             if (Files.exists(j))
       
   557                 return j.toString();
       
   558             throw new RuntimeException(x);
       
   559         }
       
   560     }
       
   561 
       
   562     static String toString(byte[] ba1, byte[] ba2) {
       
   563         return (new String(ba1, UTF_8)).concat(new String(ba2, UTF_8));
       
   564     }
       
   565 
       
   566     static class Result {
       
   567         final int exitValue;
       
   568         final byte[] stdout;
       
   569         final byte[] stderr;
       
   570         final String output;
       
   571 
       
   572         private Result(int exitValue, byte[] stdout, byte[] stderr, String output) {
       
   573             this.exitValue = exitValue;
       
   574             this.stdout = stdout;
       
   575             this.stderr = stderr;
       
   576             this.output = output;
       
   577         }
       
   578 
       
   579         InputStream stdoutAsStream() { return new ByteArrayInputStream(stdout); }
       
   580 
       
   581         Result assertSuccess() { assertTrue(exitValue == 0, output); return this; }
       
   582         Result assertFailure() { assertTrue(exitValue != 0, output); return this; }
       
   583 
       
   584         Result resultChecker(IOConsumer<Result> r) {
       
   585             try {  r.accept(this); return this; }
       
   586             catch (IOException x) { throw new UncheckedIOException(x); }
       
   587         }
       
   588 
       
   589         Result resultChecker(FailCheckerWithMessage c) { c.accept(this); return this; }
       
   590     }
       
   591 
       
   592     interface IOConsumer<T> { void accept(T t) throws IOException ;  }
       
   593 
       
   594     // readAllBytes implementation so the test can be run pre 1.9 ( legacyOnly )
       
   595     static byte[] readAllBytes(InputStream is) throws IOException {
       
   596         byte[] buf = new byte[8192];
       
   597         int capacity = buf.length;
       
   598         int nread = 0;
       
   599         int n;
       
   600         for (;;) {
       
   601             // read to EOF which may read more or less than initial buffer size
       
   602             while ((n = is.read(buf, nread, capacity - nread)) > 0)
       
   603                 nread += n;
       
   604 
       
   605             // if the last call to read returned -1, then we're done
       
   606             if (n < 0)
       
   607                 break;
       
   608 
       
   609             // need to allocate a larger buffer
       
   610             capacity = capacity << 1;
       
   611 
       
   612             buf = Arrays.copyOf(buf, capacity);
       
   613         }
       
   614         return (capacity == nread) ? buf : Arrays.copyOf(buf, nread);
       
   615     }
       
   616 
       
   617     // Standalone entry point for running with, possibly older, JDKs.
       
   618     public static void main(String[] args) throws Throwable {
       
   619         boolean legacyOnly = false;
       
   620         if (args.length != 0 && args[0].equals("legacyOnly"))
       
   621             legacyOnly = true;
       
   622 
       
   623         CLICompatibility test = new CLICompatibility(legacyOnly);
       
   624         for (Method m : CLICompatibility.class.getDeclaredMethods()) {
       
   625             if (m.getAnnotation(Test.class) != null) {
       
   626                 System.out.println("Invoking " + m.getName());
       
   627                 m.invoke(test);
       
   628             }
       
   629         }
       
   630     }
       
   631     CLICompatibility(boolean legacyOnly) { this.legacyOnly = legacyOnly; }
       
   632     CLICompatibility() { this.legacyOnly = false; }
       
   633 }