9e334e5e92f4c5bf8e77f2a263cf503a761d1e9e
[tools/dynpart-tools.git] /
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // http://code.google.com/p/protobuf/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 //     * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 //     * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31 package com.google.protobuf.test;
32 import com.google.protobuf.*;
33
34 import com.google.protobuf.ByteString.Output;
35
36 import junit.framework.TestCase;
37
38 import java.io.ByteArrayInputStream;
39 import java.io.IOException;
40 import java.io.InputStream;
41 import java.io.OutputStream;
42 import java.io.UnsupportedEncodingException;
43 import java.nio.ByteBuffer;
44 import java.util.ArrayList;
45 import java.util.Arrays;
46 import java.util.Iterator;
47 import java.util.List;
48 import java.util.NoSuchElementException;
49 import java.util.Random;
50
51 /**
52  * Test methods with implementations in {@link ByteString}, plus do some top-level "integration"
53  * tests.
54  *
55  * @author carlanton@google.com (Carl Haverl)
56  */
57 public class ByteStringTest extends TestCase {
58
59   private static final String UTF_16 = "UTF-16";
60
61   static byte[] getTestBytes(int size, long seed) {
62     Random random = new Random(seed);
63     byte[] result = new byte[size];
64     random.nextBytes(result);
65     return result;
66   }
67
68   private byte[] getTestBytes(int size) {
69     return getTestBytes(size, 445566L);
70   }
71
72   private byte[] getTestBytes() {
73     return getTestBytes(1000);
74   }
75
76   // Compare the entire left array with a subset of the right array.
77   private boolean isArrayRange(byte[] left, byte[] right, int rightOffset, int length) {
78     boolean stillEqual = (left.length == length);
79     for (int i = 0; (stillEqual && i < length); ++i) {
80       stillEqual = (left[i] == right[rightOffset + i]);
81     }
82     return stillEqual;
83   }
84
85   // Returns true only if the given two arrays have identical contents.
86   private boolean isArray(byte[] left, byte[] right) {
87     return left.length == right.length && isArrayRange(left, right, 0, left.length);
88   }
89
90   public void testSubstring_BeginIndex() {
91     byte[] bytes = getTestBytes();
92     ByteString substring = ByteString.copyFrom(bytes).substring(500);
93     assertTrue("substring must contain the tail of the string",
94         isArrayRange(substring.toByteArray(), bytes, 500, bytes.length - 500));
95   }
96
97   public void testCopyFrom_BytesOffsetSize() {
98     byte[] bytes = getTestBytes();
99     ByteString byteString = ByteString.copyFrom(bytes, 500, 200);
100     assertTrue("copyFrom sub-range must contain the expected bytes",
101         isArrayRange(byteString.toByteArray(), bytes, 500, 200));
102   }
103
104   public void testCopyFrom_Bytes() {
105     byte[] bytes = getTestBytes();
106     ByteString byteString = ByteString.copyFrom(bytes);
107     assertTrue("copyFrom must contain the expected bytes",
108         isArray(byteString.toByteArray(), bytes));
109   }
110
111   public void testCopyFrom_ByteBufferSize() {
112     byte[] bytes = getTestBytes();
113     ByteBuffer byteBuffer = ByteBuffer.allocate(bytes.length);
114     byteBuffer.put(bytes);
115     byteBuffer.position(500);
116     ByteString byteString = ByteString.copyFrom(byteBuffer, 200);
117     assertTrue("copyFrom byteBuffer sub-range must contain the expected bytes",
118         isArrayRange(byteString.toByteArray(), bytes, 500, 200));
119   }
120
121   public void testCopyFrom_ByteBuffer() {
122     byte[] bytes = getTestBytes();
123     ByteBuffer byteBuffer = ByteBuffer.allocate(bytes.length);
124     byteBuffer.put(bytes);
125     byteBuffer.position(500);
126     ByteString byteString = ByteString.copyFrom(byteBuffer);
127     assertTrue("copyFrom byteBuffer sub-range must contain the expected bytes",
128         isArrayRange(byteString.toByteArray(), bytes, 500, bytes.length - 500));
129   }
130
131   public void testCopyFrom_StringEncoding() throws UnsupportedEncodingException {
132     String testString = "I love unicode \u1234\u5678 characters";
133     ByteString byteString = ByteString.copyFrom(testString, UTF_16);
134     byte[] testBytes = testString.getBytes(UTF_16);
135     assertTrue("copyFrom string must respect the charset",
136         isArrayRange(byteString.toByteArray(), testBytes, 0, testBytes.length));
137   }
138
139   public void testCopyFrom_Utf8() throws UnsupportedEncodingException {
140     String testString = "I love unicode \u1234\u5678 characters";
141     ByteString byteString = ByteString.copyFromUtf8(testString);
142     byte[] testBytes = testString.getBytes("UTF-8");
143     assertTrue("copyFromUtf8 string must respect the charset",
144         isArrayRange(byteString.toByteArray(), testBytes, 0, testBytes.length));
145   }
146
147   public void testCopyFrom_Iterable() {
148     byte[] testBytes = getTestBytes(77777, 113344L);
149     final List<ByteString> pieces = makeConcretePieces(testBytes);
150     // Call copyFrom() on a Collection
151     ByteString byteString = ByteString.copyFrom(pieces);
152     assertTrue("copyFrom a List must contain the expected bytes",
153         isArrayRange(byteString.toByteArray(), testBytes, 0, testBytes.length));
154     // Call copyFrom on an iteration that's not a collection
155     ByteString byteStringAlt = ByteString.copyFrom(new Iterable<ByteString>() {
156       public Iterator<ByteString> iterator() {
157         return pieces.iterator();
158       }
159     });
160     assertEquals("copyFrom from an Iteration must contain the expected bytes",
161         byteString, byteStringAlt);
162   }
163
164   public void testCopyTo_TargetOffset() {
165     byte[] bytes = getTestBytes();
166     ByteString byteString = ByteString.copyFrom(bytes);
167     byte[] target = new byte[bytes.length + 1000];
168     byteString.copyTo(target, 400);
169     assertTrue("copyFrom byteBuffer sub-range must contain the expected bytes",
170         isArrayRange(bytes, target, 400, bytes.length));
171   }
172
173   public void testReadFrom_emptyStream() throws IOException {
174     ByteString byteString =
175         ByteString.readFrom(new ByteArrayInputStream(new byte[0]));
176     assertSame("reading an empty stream must result in the EMPTY constant "
177         + "byte string", ByteString.EMPTY, byteString);
178   }
179
180   public void testReadFrom_smallStream() throws IOException {
181     assertReadFrom(getTestBytes(10));
182   }
183
184   public void testReadFrom_mutating() throws IOException {
185     byte[] capturedArray = null;
186     EvilInputStream eis = new EvilInputStream();
187     ByteString byteString = ByteString.readFrom(eis);
188
189     capturedArray = eis.capturedArray;
190     byte[] originalValue = byteString.toByteArray();
191     for (int x = 0; x < capturedArray.length; ++x) {
192       capturedArray[x] = (byte) 0;
193     }
194
195     byte[] newValue = byteString.toByteArray();
196     assertTrue("copyFrom byteBuffer must not grant access to underlying array",
197         Arrays.equals(originalValue, newValue));
198   }
199
200   // Tests sizes that are over multi-segment rope threshold.
201   public void testReadFrom_largeStream() throws IOException {
202     assertReadFrom(getTestBytes(0x100));
203     assertReadFrom(getTestBytes(0x101));
204     assertReadFrom(getTestBytes(0x110));
205     assertReadFrom(getTestBytes(0x1000));
206     assertReadFrom(getTestBytes(0x1001));
207     assertReadFrom(getTestBytes(0x1010));
208     assertReadFrom(getTestBytes(0x10000));
209     assertReadFrom(getTestBytes(0x10001));
210     assertReadFrom(getTestBytes(0x10010));
211   }
212
213   // Tests that IOExceptions propagate through ByteString.readFrom().
214   public void testReadFrom_IOExceptions() {
215     try {
216       ByteString.readFrom(new FailStream());
217       fail("readFrom must throw the underlying IOException");
218
219     } catch (IOException e) {
220       assertEquals("readFrom must throw the expected exception",
221                    "synthetic failure", e.getMessage());
222     }
223   }
224
225   // Tests that ByteString.readFrom works with streams that don't
226   // always fill their buffers.
227   public void testReadFrom_reluctantStream() throws IOException {
228     final byte[] data = getTestBytes(0x1000);
229
230     ByteString byteString = ByteString.readFrom(new ReluctantStream(data));
231     assertTrue("readFrom byte stream must contain the expected bytes",
232         isArray(byteString.toByteArray(), data));
233
234     // Same test as above, but with some specific chunk sizes.
235     assertReadFromReluctantStream(data, 100);
236     assertReadFromReluctantStream(data, 248);
237     assertReadFromReluctantStream(data, 249);
238     assertReadFromReluctantStream(data, 250);
239     assertReadFromReluctantStream(data, 251);
240     assertReadFromReluctantStream(data, 0x1000);
241     assertReadFromReluctantStream(data, 0x1001);
242   }
243
244   // Fails unless ByteString.readFrom reads the bytes correctly from a
245   // reluctant stream with the given chunkSize parameter.
246   private void assertReadFromReluctantStream(byte[] bytes, int chunkSize)
247       throws IOException {
248     ByteString b = ByteString.readFrom(new ReluctantStream(bytes), chunkSize);
249     assertTrue("readFrom byte stream must contain the expected bytes",
250         isArray(b.toByteArray(), bytes));
251   }
252
253   // Tests that ByteString.readFrom works with streams that implement
254   // available().
255   public void testReadFrom_available() throws IOException {
256     final byte[] data = getTestBytes(0x1001);
257
258     ByteString byteString = ByteString.readFrom(new AvailableStream(data));
259     assertTrue("readFrom byte stream must contain the expected bytes",
260         isArray(byteString.toByteArray(), data));
261   }
262
263   // Fails unless ByteString.readFrom reads the bytes correctly.
264   private void assertReadFrom(byte[] bytes) throws IOException {
265     ByteString byteString =
266         ByteString.readFrom(new ByteArrayInputStream(bytes));
267     assertTrue("readFrom byte stream must contain the expected bytes",
268         isArray(byteString.toByteArray(), bytes));
269   }
270
271   // A stream that fails when read.
272   private static final class FailStream extends InputStream {
273     @Override public int read() throws IOException {
274       throw new IOException("synthetic failure");
275     }
276   }
277
278   // A stream that simulates blocking by only producing 250 characters
279   // per call to read(byte[]).
280   private static class ReluctantStream extends InputStream {
281     protected final byte[] data;
282     protected int pos = 0;
283
284     public ReluctantStream(byte[] data) {
285       this.data = data;
286     }
287
288     @Override public int read() {
289       if (pos == data.length) {
290         return -1;
291       } else {
292         return data[pos++];
293       }
294     }
295
296     @Override public int read(byte[] buf) {
297       return read(buf, 0, buf.length);
298     }
299
300     @Override public int read(byte[] buf, int offset, int size) {
301       if (pos == data.length) {
302         return -1;
303       }
304       int count = Math.min(Math.min(size, data.length - pos), 250);
305       System.arraycopy(data, pos, buf, offset, count);
306       pos += count;
307       return count;
308     }
309   }
310
311   // Same as above, but also implements available().
312   private static final class AvailableStream extends ReluctantStream {
313     public AvailableStream(byte[] data) {
314       super(data);
315     }
316
317     @Override public int available() {
318       return Math.min(250, data.length - pos);
319     }
320   }
321
322   // A stream which exposes the byte array passed into read(byte[], int, int).
323   private static class EvilInputStream extends InputStream {
324     public byte[] capturedArray = null;
325
326     @Override
327     public int read(byte[] buf, int off, int len) {
328       if (capturedArray != null) {
329         return -1;
330       } else {
331         capturedArray = buf;
332         for (int x = 0; x < len; ++x) {
333           buf[x] = (byte) x;
334         }
335         return len;
336       }
337     }
338
339     @Override
340     public int read() {
341       // Purposefully do nothing.
342       return -1;
343     }
344   }
345
346   // A stream which exposes the byte array passed into write(byte[], int, int).
347   private static class EvilOutputStream extends OutputStream {
348     public byte[] capturedArray = null;
349
350     @Override
351     public void write(byte[] buf, int off, int len) {
352       if (capturedArray == null) {
353         capturedArray = buf;
354       }
355     }
356
357     @Override
358     public void write(int ignored) {
359       // Purposefully do nothing.
360     }
361   }
362
363   public void testToStringUtf8() throws UnsupportedEncodingException {
364     String testString = "I love unicode \u1234\u5678 characters";
365     byte[] testBytes = testString.getBytes("UTF-8");
366     ByteString byteString = ByteString.copyFrom(testBytes);
367     assertEquals("copyToStringUtf8 must respect the charset",
368         testString, byteString.toStringUtf8());
369   }
370
371   public void testNewOutput_InitialCapacity() throws IOException {
372     byte[] bytes = getTestBytes();
373     ByteString.Output output = ByteString.newOutput(bytes.length + 100);
374     output.write(bytes);
375     ByteString byteString = output.toByteString();
376     assertTrue(
377         "String built from newOutput(int) must contain the expected bytes",
378         isArrayRange(bytes, byteString.toByteArray(), 0, bytes.length));
379   }
380
381   // Test newOutput() using a variety of buffer sizes and a variety of (fixed)
382   // write sizes
383   public void testNewOutput_ArrayWrite() throws IOException {
384     byte[] bytes = getTestBytes();
385     int length = bytes.length;
386     int[] bufferSizes = {128, 256, length / 2, length - 1, length, length + 1,
387                          2 * length, 3 * length};
388     int[] writeSizes = {1, 4, 5, 7, 23, bytes.length};
389
390     for (int bufferSize : bufferSizes) {
391       for (int writeSize : writeSizes) {
392         // Test writing the entire output writeSize bytes at a time.
393         ByteString.Output output = ByteString.newOutput(bufferSize);
394         for (int i = 0; i < length; i += writeSize) {
395           output.write(bytes, i, Math.min(writeSize, length - i));
396         }
397         ByteString byteString = output.toByteString();
398         assertTrue("String built from newOutput() must contain the expected bytes",
399             isArrayRange(bytes, byteString.toByteArray(), 0, bytes.length));
400       }
401     }
402   }
403
404   // Test newOutput() using a variety of buffer sizes, but writing all the
405   // characters using write(byte);
406   public void testNewOutput_WriteChar() throws IOException {
407     byte[] bytes = getTestBytes();
408     int length = bytes.length;
409     int[] bufferSizes = {0, 1, 128, 256, length / 2,
410                          length - 1, length, length + 1,
411                          2 * length, 3 * length};
412     for (int bufferSize : bufferSizes) {
413       ByteString.Output output = ByteString.newOutput(bufferSize);
414       for (byte byteValue : bytes) {
415         output.write(byteValue);
416       }
417       ByteString byteString = output.toByteString();
418       assertTrue("String built from newOutput() must contain the expected bytes",
419           isArrayRange(bytes, byteString.toByteArray(), 0, bytes.length));
420     }
421   }
422
423   // Test newOutput() in which we write the bytes using a variety of methods
424   // and sizes, and in which we repeatedly call toByteString() in the middle.
425   public void testNewOutput_Mixed() throws IOException {
426     Random rng = new Random(1);
427     byte[] bytes = getTestBytes();
428     int length = bytes.length;
429     int[] bufferSizes = {0, 1, 128, 256, length / 2,
430                          length - 1, length, length + 1,
431                          2 * length, 3 * length};
432
433     for (int bufferSize : bufferSizes) {
434       // Test writing the entire output using a mixture of write sizes and
435       // methods;
436       ByteString.Output output = ByteString.newOutput(bufferSize);
437       int position = 0;
438       while (position < bytes.length) {
439         if (rng.nextBoolean()) {
440           int count = 1 + rng.nextInt(bytes.length - position);
441           output.write(bytes, position, count);
442           position += count;
443         } else {
444           output.write(bytes[position]);
445           position++;
446         }
447         assertEquals("size() returns the right value", position, output.size());
448         assertTrue("newOutput() substring must have correct bytes",
449             isArrayRange(output.toByteString().toByteArray(),
450                 bytes, 0, position));
451       }
452       ByteString byteString = output.toByteString();
453       assertTrue("String built from newOutput() must contain the expected bytes",
454           isArrayRange(bytes, byteString.toByteArray(), 0, bytes.length));
455     }
456   }
457
458   public void testNewOutputEmpty() throws IOException {
459     // Make sure newOutput() correctly builds empty byte strings
460     ByteString byteString = ByteString.newOutput().toByteString();
461     assertEquals(ByteString.EMPTY, byteString);
462   }
463
464   public void testNewOutput_Mutating() throws IOException {
465     Output os = ByteString.newOutput(5);
466     os.write(new byte[] {1, 2, 3, 4, 5});
467     EvilOutputStream eos = new EvilOutputStream();
468     os.writeTo(eos);
469     byte[] capturedArray = eos.capturedArray;
470     ByteString byteString = os.toByteString();
471     byte[] oldValue = byteString.toByteArray();
472     Arrays.fill(capturedArray, (byte) 0);
473     byte[] newValue = byteString.toByteArray();
474     assertTrue("Output must not provide access to the underlying byte array",
475         Arrays.equals(oldValue, newValue));
476   }
477
478   public void testSubstringParity() {
479     byte[] bigBytes = getTestBytes(2048 * 1024, 113344L);
480     int start = 512 * 1024 - 3333;
481     int end   = 512 * 1024 + 7777;
482     ByteString concreteSubstring = ByteString.copyFrom(bigBytes).substring(start, end);
483     boolean ok = true;
484     for (int i = start; ok && i < end; ++i) {
485       ok = (bigBytes[i] == concreteSubstring.byteAt(i - start));
486     }
487     assertTrue("Concrete substring didn't capture the right bytes", ok);
488
489     ByteString literalString = ByteString.copyFrom(bigBytes, start, end - start);
490     assertTrue("Substring must be equal to literal string",
491         concreteSubstring.equals(literalString));
492     assertEquals("Substring must have same hashcode as literal string",
493         literalString.hashCode(), concreteSubstring.hashCode());
494   }
495
496   public void testCompositeSubstring() {
497     byte[] referenceBytes = getTestBytes(77748, 113344L);
498
499     List<ByteString> pieces = makeConcretePieces(referenceBytes);
500     ByteString listString = ByteString.copyFrom(pieces);
501
502     int from = 1000;
503     int to = 40000;
504     ByteString compositeSubstring = listString.substring(from, to);
505     byte[] substringBytes = compositeSubstring.toByteArray();
506     boolean stillEqual = true;
507     for (int i = 0; stillEqual && i < to - from; ++i) {
508       stillEqual = referenceBytes[from + i] == substringBytes[i];
509     }
510     assertTrue("Substring must return correct bytes", stillEqual);
511
512     stillEqual = true;
513     for (int i = 0; stillEqual && i < to - from; ++i) {
514       stillEqual = referenceBytes[from + i] == compositeSubstring.byteAt(i);
515     }
516     assertTrue("Substring must support byteAt() correctly", stillEqual);
517
518     ByteString literalSubstring = ByteString.copyFrom(referenceBytes, from, to - from);
519     assertTrue("Composite substring must equal a literal substring over the same bytes",
520         compositeSubstring.equals(literalSubstring));
521     assertTrue("Literal substring must equal a composite substring over the same bytes",
522         literalSubstring.equals(compositeSubstring));
523
524     assertEquals("We must get the same hashcodes for composite and literal substrings",
525         literalSubstring.hashCode(), compositeSubstring.hashCode());
526
527     assertFalse("We can't be equal to a proper substring",
528         compositeSubstring.equals(literalSubstring.substring(0, literalSubstring.size() - 1)));
529   }
530
531   public void testCopyFromList() {
532     byte[] referenceBytes = getTestBytes(77748, 113344L);
533     ByteString literalString = ByteString.copyFrom(referenceBytes);
534
535     List<ByteString> pieces = makeConcretePieces(referenceBytes);
536     ByteString listString = ByteString.copyFrom(pieces);
537
538     assertTrue("Composite string must be equal to literal string",
539         listString.equals(literalString));
540     assertEquals("Composite string must have same hashcode as literal string",
541         literalString.hashCode(), listString.hashCode());
542   }
543
544   public void testConcat() {
545     byte[] referenceBytes = getTestBytes(77748, 113344L);
546     ByteString literalString = ByteString.copyFrom(referenceBytes);
547
548     List<ByteString> pieces = makeConcretePieces(referenceBytes);
549
550     Iterator<ByteString> iter = pieces.iterator();
551     ByteString concatenatedString = iter.next();
552     while (iter.hasNext()) {
553       concatenatedString = concatenatedString.concat(iter.next());
554     }
555
556     assertTrue("Concatenated string must be equal to literal string",
557         concatenatedString.equals(literalString));
558     assertEquals("Concatenated string must have same hashcode as literal string",
559         literalString.hashCode(), concatenatedString.hashCode());
560   }
561
562   public void testStartsWith() {
563     byte[] bytes = getTestBytes(1000, 1234L);
564     ByteString string = ByteString.copyFrom(bytes);
565     ByteString prefix = ByteString.copyFrom(bytes, 0, 500);
566     ByteString suffix = ByteString.copyFrom(bytes, 400, 600);
567     assertTrue(string.startsWith(ByteString.EMPTY));
568     assertTrue(string.startsWith(string));
569     assertTrue(string.startsWith(prefix));
570     assertFalse(string.startsWith(suffix));
571     assertFalse(prefix.startsWith(suffix));
572     assertFalse(suffix.startsWith(prefix));
573     assertFalse(ByteString.EMPTY.startsWith(prefix));
574     assertTrue(ByteString.EMPTY.startsWith(ByteString.EMPTY));
575   }
576
577   static List<ByteString> makeConcretePieces(byte[] referenceBytes) {
578     List<ByteString> pieces = new ArrayList<ByteString>();
579     // Starting length should be small enough that we'll do some concatenating by
580     // copying if we just concatenate all these pieces together.
581     for (int start = 0, length = 16; start < referenceBytes.length; start += length) {
582       length = (length << 1) - 1;
583       if (start + length > referenceBytes.length) {
584         length = referenceBytes.length - start;
585       }
586       pieces.add(ByteString.copyFrom(referenceBytes, start, length));
587     }
588     return pieces;
589   }
590 }