TestExtra.java revision 12745:f068a4ffddd2
1/* 2 * Copyright (c) 2006, 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/** 25 * @test 26 * @bug 6480504 6303183 27 * @summary Test that client-provided data in the extra field is written and 28 * read correctly, taking into account the JAR_MAGIC written into the extra 29 * field of the first entry of JAR files. 30 * @author Dave Bristor 31 */ 32 33import java.io.*; 34import java.nio.charset.Charset; 35import java.util.Arrays; 36import java.util.jar.*; 37import java.util.zip.*; 38 39/** 40 * Tests that the get/set operations on extra data in zip and jar files work 41 * as advertised. The base class tests ZIP files, the member class 42 * TestJarExtra checks JAR files. 43 */ 44public class TestExtra { 45 static final int JAR_MAGIC = 0xcafe; // private IN JarOutputStream.java 46 static final int TEST_HEADER = 0xbabe; 47 48 static final Charset ascii = Charset.forName("ASCII"); 49 50 // ZipEntry extra data 51 static final byte[][] extra = new byte[][] { 52 ascii.encode("hello, world").array(), 53 ascii.encode("foo bar").array() 54 }; 55 56 // For naming entries in JAR/ZIP streams 57 int count = 1; 58 59 // Use byte arrays instead of files 60 ByteArrayOutputStream baos; 61 62 // JAR/ZIP content written here. 63 ZipOutputStream zos; 64 65 public static void realMain(String[] args) throws Throwable{ 66 new TestExtra().testHeaderPlusData(); 67 68 new TestJarExtra().testHeaderPlusData(); 69 new TestJarExtra().testHeaderOnly(); 70 new TestJarExtra().testClientJarMagic(); 71 } 72 73 TestExtra() { 74 try { 75 baos = new ByteArrayOutputStream(); 76 zos = getOutputStream(baos); 77 } catch (Throwable t) { 78 unexpected(t); 79 } 80 } 81 82 /** Test that a header + data set by client works. */ 83 void testHeaderPlusData() throws IOException { 84 for (byte[] b : extra) { 85 ZipEntry ze = getEntry(); 86 byte[] data = new byte[b.length + 4]; 87 set16(data, 0, TEST_HEADER); 88 set16(data, 2, b.length); 89 for (int i = 0; i < b.length; i++) { 90 data[i + 4] = b[i]; 91 } 92 ze.setExtra(data); 93 zos.putNextEntry(ze); 94 } 95 zos.close(); 96 97 ZipInputStream zis = getInputStream(); 98 99 ZipEntry ze = zis.getNextEntry(); 100 checkEntry(ze, 0, extra[0].length); 101 102 ze = zis.getNextEntry(); 103 checkEntry(ze, 1, extra[1].length); 104 } 105 106 /** Test that a header only (i.e., no extra "data") set by client works. */ 107 void testHeaderOnly() throws IOException { 108 ZipEntry ze = getEntry(); 109 byte[] data = new byte[4]; 110 set16(data, 0, TEST_HEADER); 111 set16(data, 2, 0); // Length of data is 0. 112 ze.setExtra(data); 113 zos.putNextEntry(ze); 114 115 zos.close(); 116 117 ZipInputStream zis = getInputStream(); 118 119 ze = zis.getNextEntry(); 120 checkExtra(data, ze.getExtra()); 121 checkEntry(ze, 0, 0); 122 } 123 124 /** Tests the client providing extra data which uses JAR_MAGIC header. */ 125 void testClientJarMagic() throws IOException { 126 ZipEntry ze = getEntry(); 127 byte[] data = new byte[8]; 128 129 set16(data, 0, TEST_HEADER); 130 set16(data, 2, 0); // Length of data is 0. 131 set16(data, 4, JAR_MAGIC); 132 set16(data, 6, 0); // Length of data is 0. 133 134 ze.setExtra(data); 135 zos.putNextEntry(ze); 136 137 zos.close(); 138 139 ZipInputStream zis = getInputStream(); 140 ze = zis.getNextEntry(); 141 byte[] e = ze.getExtra(); 142 checkExtra(data, ze.getExtra()); 143 checkEntry(ze, 0, 0); 144 } 145 146 // check if all "expected" extra fields equal to their 147 // corresponding fields in "extra". The "extra" might have 148 // timestamp fields added by ZOS. 149 static void checkExtra(byte[] expected, byte[] extra) { 150 if (expected == null) 151 return; 152 int off = 0; 153 int len = expected.length; 154 while (off + 4 < len) { 155 int tag = get16(expected, off); 156 int sz = get16(expected, off + 2); 157 int off0 = 0; 158 int len0 = extra.length; 159 boolean matched = false; 160 while (off0 + 4 < len0) { 161 int tag0 = get16(extra, off0); 162 int sz0 = get16(extra, off0 + 2); 163 if (tag == tag0 && sz == sz0) { 164 matched = true; 165 for (int i = 0; i < sz; i++) { 166 if (expected[off + i] != extra[off0 +i]) 167 matched = false; 168 } 169 break; 170 } 171 off0 += (4 + sz0); 172 } 173 if (!matched) { 174 fail("Expected extra data [tag=" + tag + "sz=" + sz + "] check failed"); 175 } 176 off += (4 + sz); 177 } 178 } 179 180 /** Check that the entry's extra data is correct. */ 181 void checkEntry(ZipEntry ze, int count, int dataLength) { 182 byte[] extraData = ze.getExtra(); 183 byte[] data = getField(TEST_HEADER, extraData); 184 if (!check(data != null, "unexpected null data for TEST_HEADER")) { 185 return; 186 } 187 188 if (dataLength == 0) { 189 check(data.length == 0, "unexpected non-zero data length for TEST_HEADER"); 190 } else { 191 check(Arrays.equals(extra[count], data), 192 "failed to get entry " + ze.getName() 193 + ", expected " + new String(extra[count]) + ", got '" + new String(data) + "'"); 194 } 195 } 196 197 /** Look up descriptor in data, returning corresponding byte[]. */ 198 static byte[] getField(int descriptor, byte[] data) { 199 byte[] rc = null; 200 try { 201 int i = 0; 202 while (i < data.length) { 203 if (get16(data, i) == descriptor) { 204 int length = get16(data, i + 2); 205 rc = new byte[length]; 206 for (int j = 0; j < length; j++) { 207 rc[j] = data[i + 4 + j]; 208 } 209 return rc; 210 } 211 i += get16(data, i + 2) + 4; 212 } 213 } catch (ArrayIndexOutOfBoundsException e) { 214 // descriptor not found 215 } 216 return rc; 217 } 218 219 ZipInputStream getInputStream() { 220 return new ZipInputStream( 221 new ByteArrayInputStream(baos.toByteArray())); 222 } 223 224 ZipOutputStream getOutputStream(ByteArrayOutputStream baos) throws IOException { 225 return new ZipOutputStream(baos); 226 } 227 228 ZipInputStream getInputStream(ByteArrayInputStream bais) throws IOException { 229 return new ZipInputStream(bais); 230 } 231 232 ZipEntry getEntry() { return new ZipEntry("zip" + count++ + ".txt"); } 233 234 235 private static int get16(byte[] b, int off) { 236 return (b[off] & 0xff) | ((b[off+1] & 0xff) << 8); 237 } 238 239 private static void set16(byte[] b, int off, int value) { 240 b[off+0] = (byte)value; 241 b[off+1] = (byte)(value >> 8); 242 } 243 244 /** Test extra field of a JAR file. */ 245 static class TestJarExtra extends TestExtra { 246 ZipOutputStream getOutputStream(ByteArrayOutputStream baos) throws IOException { 247 return new JarOutputStream(baos); 248 } 249 250 ZipInputStream getInputStream(ByteArrayInputStream bais) throws IOException { 251 return new JarInputStream(bais); 252 } 253 254 ZipEntry getEntry() { return new ZipEntry("jar" + count++ + ".txt"); } 255 256 void checkEntry(ZipEntry ze, int count, int dataLength) { 257 // zeroth entry should have JAR_MAGIC 258 if (count == 0) { 259 byte[] extraData = ze.getExtra(); 260 byte[] data = getField(JAR_MAGIC, extraData); 261 if (!check(data != null, "unexpected null data for JAR_MAGIC")) { 262 check(data.length != 0, "unexpected non-zero data length for JAR_MAGIC"); 263 } 264 } 265 // In a jar file, the first ZipEntry should have both JAR_MAGIC 266 // and the TEST_HEADER, so check that also. 267 super.checkEntry(ze, count, dataLength); 268 } 269 } 270 271 //--------------------- Infrastructure --------------------------- 272 static volatile int passed = 0, failed = 0; 273 static void pass() {passed++;} 274 static void fail() {failed++; Thread.dumpStack();} 275 static void fail(String msg) {System.out.println(msg); fail();} 276 static void unexpected(Throwable t) {failed++; t.printStackTrace();} 277 static void check(boolean cond) {if (cond) pass(); else fail();} 278 static boolean check(boolean cond, String msg) {if (cond) pass(); else fail(msg); return cond; } 279 static void equal(Object x, Object y) { 280 if (x == null ? y == null : x.equals(y)) pass(); 281 else fail(x + " not equal to " + y);} 282 public static void main(String[] args) throws Throwable { 283 try {realMain(args);} catch (Throwable t) {unexpected(t);} 284 System.out.println("\nPassed = " + passed + " failed = " + failed); 285 if (failed > 0) throw new Error("Some tests failed");} 286} 287