Assortment.java revision 9330:8b1f1c2a400f
1/*
2 * Copyright (c) 2005, 2013, 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/* @test
25 * @bug 4770745 6234507 6303183
26 * @summary test a variety of zip file entries
27 * @author Martin Buchholz
28 */
29
30import java.util.*;
31import java.util.zip.*;
32import java.util.jar.*;
33import java.io.*;
34
35public class Assortment {
36    static int passed = 0, failed = 0;
37
38    static void fail(String msg) {
39        failed++;
40        new Exception(msg).printStackTrace();
41    }
42
43    static void unexpected(Throwable t) {
44        failed++;
45        t.printStackTrace();
46    }
47
48    static void check(boolean condition, String msg) {
49        if (! condition)
50            fail(msg);
51    }
52
53    static void check(boolean condition) {
54        check(condition, "Something's wrong");
55    }
56
57    static final int get16(byte b[], int off) {
58        return Byte.toUnsignedInt(b[off]) | (Byte.toUnsignedInt(b[off+1]) << 8);
59    }
60
61    // check if all "expected" extra fields equal to their
62    // corresponding fields in "extra". The "extra" might have
63    // timestamp fields added by ZOS.
64    static boolean equalsExtraData(byte[] expected, byte[] extra) {
65        if (expected == null)
66            return true;
67        int off = 0;
68        int len = expected.length;
69        while (off + 4 < len) {
70            int tag = get16(expected, off);
71            int sz = get16(expected, off + 2);
72            int off0 = 0;
73            int len0 = extra.length;
74            boolean matched = false;
75            while (off0 + 4 < len0) {
76                int tag0 = get16(extra, off0);
77                int sz0 = get16(extra, off0 + 2);
78                if (tag == tag0 && sz == sz0) {
79                    matched = true;
80                    for (int i = 0; i < sz; i++) {
81                        if (expected[off + i] != extra[off0 +i])
82                            matched = false;
83                    }
84                    break;
85                }
86                off0 += (4 + sz0);
87            }
88            if (!matched)
89                return false;
90            off += (4 + sz);
91        }
92        return true;
93    }
94
95    private static class Entry {
96        private String name;
97        private int    method;
98        private byte[] data;
99        private byte[] extra;
100        private String comment;
101
102        Entry(String name,
103              int    method,
104              byte[] data,
105              byte[] extra,
106              String comment) {
107            this.name    = name;
108            this.method  = method;
109            this.data    = data;
110            this.extra   = extra;
111            this.comment = comment;
112        }
113
114        void write(ZipOutputStream s) throws Exception {
115            ZipEntry e = new ZipEntry(name);
116            CRC32 crc32 = new CRC32();
117            e.setMethod(method);
118            if (method == ZipEntry.STORED) {
119                e.setSize(data == null ? 0 : data.length);
120                crc32.reset();
121                if (data != null) crc32.update(data);
122                e.setCrc(crc32.getValue());
123            } else {
124                e.setSize(0);
125                e.setCrc(0);
126            }
127            if (comment != null) e.setComment(comment);
128            if (extra   != null) e.setExtra(extra);
129            s.putNextEntry(e);
130            if (data != null) s.write(data);
131        }
132
133        byte[] getData(ZipFile f, ZipEntry e) throws Exception {
134            byte[] fdata = new byte[(int)e.getSize()];
135            InputStream is = f.getInputStream(e);
136            is.read(fdata);
137            return fdata;
138        }
139
140        void verify(ZipFile f) throws Exception {
141            ZipEntry e = f.getEntry(name);
142            byte[] data  = (this.data == null) ? new byte[]{} : this.data;
143            byte[] extra = (this.extra != null && this.extra.length == 0) ?
144                null : this.extra;
145            check(name.equals(e.getName()));
146            check(method == e.getMethod());
147            check((((comment == null) || comment.equals(""))
148                   && (e.getComment() == null))
149                  || comment.equals(e.getComment()));
150            check(equalsExtraData(extra, e.getExtra()));
151            check(Arrays.equals(data, getData(f, e)));
152            check(e.getSize() == data.length);
153            check((method == ZipEntry.DEFLATED) ||
154                  (e.getCompressedSize() == data.length));
155        }
156
157        void verify(JarInputStream jis) throws Exception {
158            // JarInputStream "automatically" reads the manifest
159            if (name.equals("meta-iNf/ManIfEst.Mf"))
160                return;
161            ZipEntry e = jis.getNextEntry();
162
163            byte[] data = (this.data == null) ? new byte[]{} : this.data;
164            byte[] otherData = new byte[data.length];
165            jis.read(otherData);
166            check(Arrays.equals(data, otherData));
167
168            byte[] extra = (this.extra != null && this.extra.length == 0) ?
169                null : this.extra;
170            check(equalsExtraData(extra, e.getExtra()));
171            check(name.equals(e.getName()));
172            check(method == e.getMethod());
173            check(e.getSize() == -1 || e.getSize() == data.length);
174            check((method == ZipEntry.DEFLATED) ||
175                  (e.getCompressedSize() == data.length));
176
177        }
178    }
179
180    private static int uniquifier = 86;
181    private static String uniquify(String name) {
182        return name + (uniquifier++);
183    }
184
185    private static byte[] toBytes(String s) throws Exception {
186        return s.getBytes("UTF-8");
187    }
188
189    private static byte[] toExtra(byte[] bytes) throws Exception {
190        if (bytes == null) return null;
191        // Construct a fake extra field with valid header length
192        byte[] v = new byte[bytes.length + 4];
193        v[0] = (byte) 0x47;
194        v[1] = (byte) 0xff;
195        v[2] = (byte) bytes.length;
196        v[3] = (byte) (bytes.length << 8);
197        System.arraycopy(bytes, 0, v, 4, bytes.length);
198        return v;
199    }
200
201    private static Random random = new Random();
202
203    private static String makeName(int length) {
204        StringBuilder sb = new StringBuilder(length);
205        for (int i = 0; i < length; i++)
206            sb.append((char)(random.nextInt(10000)+1));
207        return sb.toString();
208    }
209
210    public static void main(String[] args) throws Exception {
211        File zipName = new File("x.zip");
212        int[]    methods  = {ZipEntry.STORED, ZipEntry.DEFLATED};
213        String[] names    = {makeName(1), makeName(160), makeName(9000)};
214        byte[][] datas    = {null, new byte[]{}, new byte[]{'d'}};
215        byte[][] extras   = {null, new byte[]{}, new byte[]{'e'}};
216        String[] comments = {null, "", "c"};
217
218        List<Entry> entries = new ArrayList<Entry>();
219
220        // Highly unusual manifest
221        entries.add(new Entry("meta-iNf/ManIfEst.Mf",
222                              ZipEntry.STORED,
223                              toBytes("maNiFest-VeRsIon: 1.0\n"),
224                              toExtra(toBytes("Can manifests have extra??")),
225                              "Can manifests have comments??"));
226
227        // The emptiest possible entry
228        entries.add(new Entry("", ZipEntry.STORED,   null, null, ""));
229
230        for (String name : names)
231            for (int method : methods)
232                for (byte[] data : datas) // datae??
233                    for (byte[] extra : extras)
234                        for (String comment : comments)
235                            entries.add(new Entry(uniquify(name), method, data,
236                                                  toExtra(extra), comment));
237
238        //----------------------------------------------------------------
239        // Write zip file using ZipOutputStream
240        //----------------------------------------------------------------
241        try (FileOutputStream fos = new FileOutputStream(zipName);
242             ZipOutputStream zos = new ZipOutputStream(fos))
243        {
244            for (Entry e : entries)
245                e.write(zos);
246        }
247
248        //----------------------------------------------------------------
249        // Verify zip file contents using JarFile class
250        //----------------------------------------------------------------
251        JarFile f = new JarFile(zipName);
252
253        check(f.getManifest() != null);
254
255        for (Entry e : entries)
256            e.verify(f);
257
258        f.close();
259
260        //----------------------------------------------------------------
261        // Verify zip file contents using JarInputStream class
262        //----------------------------------------------------------------
263        JarInputStream jis = new JarInputStream(
264            new FileInputStream(zipName));
265
266        // JarInputStream "automatically" reads the manifest
267        check(jis.getManifest() != null);
268
269        for (Entry e : entries)
270            e.verify(jis);
271
272        jis.close();
273
274//      String cmd = "unzip -t " + zipName.getPath() + " >/dev/tty";
275//      new ProcessBuilder(new String[]{"/bin/sh", "-c", cmd}).start().waitFor();
276
277        zipName.deleteOnExit();
278
279        System.out.printf("passed = %d, failed = %d%n", passed, failed);
280        if (failed > 0) throw new Exception("Some tests failed");
281    }
282}
283