1/*
2 * Copyright (c) 2016, 2017, 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/*
25 * @test
26 * @bug 8165944
27 * @summary test several jar tool input file scenarios with variations on -C
28 *          options with/without a --release option.  Some input files are
29 *          duplicates that sometimes cause exceptions and other times do not,
30 *          demonstrating identical behavior to JDK 8 jar tool.
31 * @library /test/lib
32 * @modules jdk.jartool
33 * @build jdk.test.lib.Platform
34 *        jdk.test.lib.util.FileUtils
35 * @run testng InputFilesTest
36 */
37
38import org.testng.Assert;
39import org.testng.annotations.AfterMethod;
40import org.testng.annotations.BeforeMethod;
41import org.testng.annotations.Test;
42
43import java.io.ByteArrayOutputStream;
44import java.io.IOException;
45import java.io.PrintStream;
46import java.io.UncheckedIOException;
47import java.nio.file.Files;
48import java.nio.file.Path;
49import java.nio.file.Paths;
50import java.util.Arrays;
51import java.util.spi.ToolProvider;
52import java.util.stream.Stream;
53import java.util.zip.ZipException;
54
55import jdk.test.lib.util.FileUtils;
56
57public class InputFilesTest {
58    private static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar")
59        .orElseThrow(() ->
60            new RuntimeException("jar tool not found")
61        );
62
63    private final String nl = System.lineSeparator();
64    private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
65    private final PrintStream out = new PrintStream(baos);
66    private Runnable onCompletion;
67
68    @BeforeMethod
69    public void reset() {
70        onCompletion = null;
71    }
72
73    @AfterMethod
74    public void run() {
75        if (onCompletion != null) {
76            onCompletion.run();
77        }
78    }
79
80    @Test
81    public void test1() throws IOException {
82        mkdir("test1 test2");
83        touch("test1/testfile1 test2/testfile2");
84        jar("cf test.jar -C test1 . -C test2 .");
85        jar("tf test.jar");
86        println();
87        String output = "META-INF/" + nl +
88                "META-INF/MANIFEST.MF" + nl +
89                "testfile1" + nl +
90                "testfile2" + nl;
91        rm("test.jar test1 test2");
92        Assert.assertEquals(baos.toByteArray(), output.getBytes());
93    }
94
95    @Test
96    public void test2() throws IOException {
97        mkdir("test1 test2 test3 test4");
98        touch("test1/testfile1 test2/testfile2 test3/testfile3 test4/testfile4");
99        jar("cf test.jar -C test1 . -C test2 . --release 9 -C test3 . -C test4 .");
100        jar("tf test.jar");
101        println();
102        String output = "META-INF/" + nl +
103                "META-INF/MANIFEST.MF" + nl +
104                "testfile1" + nl +
105                "testfile2" + nl +
106                "META-INF/versions/9/" + nl +
107                "META-INF/versions/9/testfile3" + nl +
108                "META-INF/versions/9/testfile4" + nl;
109        rm("test.jar test1 test2 test3 test4");
110        Assert.assertEquals(baos.toByteArray(), output.getBytes());
111    }
112
113    @Test
114    public void test3() throws IOException {
115        touch("test");
116        jar("cf test.jar test test");
117        jar("tf test.jar");
118        println();
119        String output = "META-INF/" + nl +
120                "META-INF/MANIFEST.MF" + nl +
121                "test" + nl;
122        rm("test.jar test");
123        Assert.assertEquals(baos.toByteArray(), output.getBytes());
124    }
125
126    @Test
127    public void test4() throws IOException {
128        mkdir("a");
129        touch("a/test");
130        jar("cf test.jar -C a test -C a test");
131        jar("tf test.jar");
132        println();
133        String output = "META-INF/" + nl +
134                "META-INF/MANIFEST.MF" + nl +
135                "test" + nl;
136        rm("test.jar a");
137        Assert.assertEquals(baos.toByteArray(), output.getBytes());
138    }
139
140    @Test(expectedExceptions = {ZipException.class})
141    public void test5() throws IOException {
142        mkdir("a");
143        touch("test a/test");
144        onCompletion = () -> rm("test a");
145        jar("cf test.jar -C a test test");
146    }
147
148    @Test(expectedExceptions = {ZipException.class})
149    public void test6() throws IOException {
150        mkdir("test1 test2");
151        touch("test1/a test2/a");
152        onCompletion = () -> rm("test1 test2");
153        jar("cf test.jar --release 9 -C test1 a -C test2 a");
154    }
155
156    private Stream<Path> mkpath(String... args) {
157        return Arrays.stream(args).map(d -> Paths.get(".", d.split("/")));
158    }
159
160    private void mkdir(String cmdline) {
161        System.out.println("mkdir -p " + cmdline);
162        mkpath(cmdline.split(" +")).forEach(p -> {
163            try {
164                Files.createDirectories(p);
165            } catch (IOException x) {
166                throw new UncheckedIOException(x);
167            }
168        });
169    }
170
171    private void touch(String cmdline) {
172        System.out.println("touch " + cmdline);
173        mkpath(cmdline.split(" +")).forEach(p -> {
174            try {
175                Files.createFile(p);
176            } catch (IOException x) {
177                throw new UncheckedIOException(x);
178            }
179        });
180    }
181
182    private void rm(String cmdline) {
183        System.out.println("rm -rf " + cmdline);
184        mkpath(cmdline.split(" +")).forEach(p -> {
185            try {
186                if (Files.isDirectory(p)) {
187                    FileUtils.deleteFileTreeWithRetry(p);
188                } else {
189                    FileUtils.deleteFileIfExistsWithRetry(p);
190                }
191            } catch (IOException x) {
192                throw new UncheckedIOException(x);
193            }
194        });
195    }
196
197    private void jar(String cmdline) throws IOException {
198        System.out.println("jar " + cmdline);
199        baos.reset();
200
201        // the run method catches IOExceptions, we need to expose them
202        ByteArrayOutputStream baes = new ByteArrayOutputStream();
203        PrintStream err = new PrintStream(baes);
204        PrintStream saveErr = System.err;
205        System.setErr(err);
206        int rc = JAR_TOOL.run(out, err, cmdline.split(" +"));
207        System.setErr(saveErr);
208        if (rc != 0) {
209            String s = baes.toString();
210            if (s.startsWith("java.util.zip.ZipException: duplicate entry: ")) {
211                throw new ZipException(s);
212            }
213            throw new IOException(s);
214        }
215    }
216
217    private void println() throws IOException {
218        System.out.println(new String(baos.toByteArray()));
219    }
220}
221