• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
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;
32 
33 import com.google.protobuf.ByteString.Output;
34 import java.io.ByteArrayInputStream;
35 import java.io.ByteArrayOutputStream;
36 import java.io.IOException;
37 import java.io.InputStream;
38 import java.io.OutputStream;
39 import java.lang.reflect.Field;
40 import java.nio.ByteBuffer;
41 import java.nio.charset.Charset;
42 import java.util.ArrayList;
43 import java.util.Arrays;
44 import java.util.Comparator;
45 import java.util.Iterator;
46 import java.util.List;
47 import java.util.NoSuchElementException;
48 import java.util.Random;
49 import junit.framework.TestCase;
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 Charset UTF_16 = Charset.forName("UTF-16");
60 
getTestBytes(int size, long seed)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 
getTestBytes(int size)68   private byte[] getTestBytes(int size) {
69     return getTestBytes(size, 445566L);
70   }
71 
getTestBytes()72   private byte[] getTestBytes() {
73     return getTestBytes(1000);
74   }
75 
76   // Compare the entire left array with a subset of the right array.
isArrayRange(byte[] left, byte[] right, int rightOffset, int length)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.
isArray(byte[] left, byte[] right)86   private boolean isArray(byte[] left, byte[] right) {
87     return left.length == right.length && isArrayRange(left, right, 0, left.length);
88   }
89 
testCompare_equalByteStrings_compareEqual()90   public void testCompare_equalByteStrings_compareEqual() throws Exception {
91     byte[] referenceBytes = getTestBytes();
92     ByteString string1 = ByteString.copyFrom(referenceBytes);
93     ByteString string2 = ByteString.copyFrom(referenceBytes);
94 
95     assertEquals(
96         "ByteString instances containing the same data must compare equal.",
97         0,
98         ByteString.unsignedLexicographicalComparator().compare(string1, string2));
99   }
100 
testCompare_byteStringsSortLexicographically()101   public void testCompare_byteStringsSortLexicographically() throws Exception {
102     ByteString app = ByteString.copyFromUtf8("app");
103     ByteString apple = ByteString.copyFromUtf8("apple");
104     ByteString banana = ByteString.copyFromUtf8("banana");
105 
106     Comparator<ByteString> comparator = ByteString.unsignedLexicographicalComparator();
107 
108     assertTrue("ByteString(app) < ByteString(apple)", comparator.compare(app, apple) < 0);
109     assertTrue("ByteString(app) < ByteString(banana)", comparator.compare(app, banana) < 0);
110     assertTrue("ByteString(apple) < ByteString(banana)", comparator.compare(apple, banana) < 0);
111   }
112 
testCompare_interpretsByteValuesAsUnsigned()113   public void testCompare_interpretsByteValuesAsUnsigned() throws Exception {
114     // Two's compliment of `-1` == 0b11111111 == 255
115     ByteString twoHundredFiftyFive = ByteString.copyFrom(new byte[] {-1});
116     // 0b00000001 == 1
117     ByteString one = ByteString.copyFrom(new byte[] {1});
118 
119     assertTrue(
120         "ByteString comparison treats bytes as unsigned values",
121         ByteString.unsignedLexicographicalComparator().compare(one, twoHundredFiftyFive) < 0);
122   }
123 
testSubstring_BeginIndex()124   public void testSubstring_BeginIndex() {
125     byte[] bytes = getTestBytes();
126     ByteString substring = ByteString.copyFrom(bytes).substring(500);
127     assertTrue(
128         "substring must contain the tail of the string",
129         isArrayRange(substring.toByteArray(), bytes, 500, bytes.length - 500));
130   }
131 
testCopyFrom_BytesOffsetSize()132   public void testCopyFrom_BytesOffsetSize() {
133     byte[] bytes = getTestBytes();
134     ByteString byteString = ByteString.copyFrom(bytes, 500, 200);
135     assertTrue(
136         "copyFrom sub-range must contain the expected bytes",
137         isArrayRange(byteString.toByteArray(), bytes, 500, 200));
138   }
139 
testCopyFrom_Bytes()140   public void testCopyFrom_Bytes() {
141     byte[] bytes = getTestBytes();
142     ByteString byteString = ByteString.copyFrom(bytes);
143     assertTrue(
144         "copyFrom must contain the expected bytes", isArray(byteString.toByteArray(), bytes));
145   }
146 
testCopyFrom_ByteBufferSize()147   public void testCopyFrom_ByteBufferSize() {
148     byte[] bytes = getTestBytes();
149     ByteBuffer byteBuffer = ByteBuffer.allocate(bytes.length);
150     byteBuffer.put(bytes);
151     byteBuffer.position(500);
152     ByteString byteString = ByteString.copyFrom(byteBuffer, 200);
153     assertTrue(
154         "copyFrom byteBuffer sub-range must contain the expected bytes",
155         isArrayRange(byteString.toByteArray(), bytes, 500, 200));
156   }
157 
testCopyFrom_ByteBuffer()158   public void testCopyFrom_ByteBuffer() {
159     byte[] bytes = getTestBytes();
160     ByteBuffer byteBuffer = ByteBuffer.allocate(bytes.length);
161     byteBuffer.put(bytes);
162     byteBuffer.position(500);
163     ByteString byteString = ByteString.copyFrom(byteBuffer);
164     assertTrue(
165         "copyFrom byteBuffer sub-range must contain the expected bytes",
166         isArrayRange(byteString.toByteArray(), bytes, 500, bytes.length - 500));
167   }
168 
testCopyFrom_StringEncoding()169   public void testCopyFrom_StringEncoding() {
170     String testString = "I love unicode \u1234\u5678 characters";
171     ByteString byteString = ByteString.copyFrom(testString, UTF_16);
172     byte[] testBytes = testString.getBytes(UTF_16);
173     assertTrue(
174         "copyFrom string must respect the charset",
175         isArrayRange(byteString.toByteArray(), testBytes, 0, testBytes.length));
176   }
177 
testCopyFrom_Utf8()178   public void testCopyFrom_Utf8() {
179     String testString = "I love unicode \u1234\u5678 characters";
180     ByteString byteString = ByteString.copyFromUtf8(testString);
181     byte[] testBytes = testString.getBytes(Internal.UTF_8);
182     assertTrue(
183         "copyFromUtf8 string must respect the charset",
184         isArrayRange(byteString.toByteArray(), testBytes, 0, testBytes.length));
185   }
186 
testCopyFrom_Iterable()187   public void testCopyFrom_Iterable() {
188     byte[] testBytes = getTestBytes(77777, 113344L);
189     final List<ByteString> pieces = makeConcretePieces(testBytes);
190     // Call copyFrom() on a Collection
191     ByteString byteString = ByteString.copyFrom(pieces);
192     assertTrue(
193         "copyFrom a List must contain the expected bytes",
194         isArrayRange(byteString.toByteArray(), testBytes, 0, testBytes.length));
195     // Call copyFrom on an iteration that's not a collection
196     ByteString byteStringAlt =
197         ByteString.copyFrom(
198             new Iterable<ByteString>() {
199               @Override
200               public Iterator<ByteString> iterator() {
201                 return pieces.iterator();
202               }
203             });
204     assertEquals(
205         "copyFrom from an Iteration must contain the expected bytes", byteString, byteStringAlt);
206   }
207 
testCopyFrom_LengthTooBig()208   public void testCopyFrom_LengthTooBig() {
209     byte[] testBytes = getTestBytes(100);
210     try {
211       ByteString.copyFrom(testBytes, 0, 200);
212       fail("Should throw");
213     } catch (IndexOutOfBoundsException expected) {
214     }
215 
216     try {
217       ByteString.copyFrom(testBytes, 99, 2);
218       fail();
219     } catch (IndexOutOfBoundsException expected) {
220     }
221 
222     ByteBuffer buf = ByteBuffer.wrap(testBytes);
223     try {
224       ByteString.copyFrom(buf, 101);
225       fail();
226     } catch (IndexOutOfBoundsException expected) {
227     }
228 
229     try {
230       ByteString.copyFrom(testBytes, -1, 10);
231       fail("Should throw");
232     } catch (IndexOutOfBoundsException expected) {
233     }
234   }
235 
testCopyTo_TargetOffset()236   public void testCopyTo_TargetOffset() {
237     byte[] bytes = getTestBytes();
238     ByteString byteString = ByteString.copyFrom(bytes);
239     byte[] target = new byte[bytes.length + 1000];
240     byteString.copyTo(target, 400);
241     assertTrue(
242         "copyFrom byteBuffer sub-range must contain the expected bytes",
243         isArrayRange(bytes, target, 400, bytes.length));
244   }
245 
testReadFrom_emptyStream()246   public void testReadFrom_emptyStream() throws IOException {
247     ByteString byteString = ByteString.readFrom(new ByteArrayInputStream(new byte[0]));
248     assertSame(
249         "reading an empty stream must result in the EMPTY constant byte string",
250         ByteString.EMPTY,
251         byteString);
252   }
253 
testReadFrom_smallStream()254   public void testReadFrom_smallStream() throws IOException {
255     assertReadFrom(getTestBytes(10));
256   }
257 
testReadFrom_mutating()258   public void testReadFrom_mutating() throws IOException {
259     EvilInputStream eis = new EvilInputStream();
260     ByteString byteString = ByteString.readFrom(eis);
261     byte[] capturedArray = eis.capturedArray;
262 
263     byte[] originalValue = byteString.toByteArray();
264     for (int x = 0; x < capturedArray.length; ++x) {
265       capturedArray[x] = (byte) 0;
266     }
267 
268     byte[] newValue = byteString.toByteArray();
269     assertTrue(
270         "copyFrom byteBuffer must not grant access to underlying array",
271         Arrays.equals(originalValue, newValue));
272   }
273 
274   // Tests sizes that are near the rope copy-out threshold.
testReadFrom_mediumStream()275   public void testReadFrom_mediumStream() throws IOException {
276     assertReadFrom(getTestBytes(ByteString.CONCATENATE_BY_COPY_SIZE - 1));
277     assertReadFrom(getTestBytes(ByteString.CONCATENATE_BY_COPY_SIZE));
278     assertReadFrom(getTestBytes(ByteString.CONCATENATE_BY_COPY_SIZE + 1));
279     assertReadFrom(getTestBytes(200));
280   }
281 
282   // Tests sizes that are over multi-segment rope threshold.
testReadFrom_largeStream()283   public void testReadFrom_largeStream() throws IOException {
284     assertReadFrom(getTestBytes(0x100));
285     assertReadFrom(getTestBytes(0x101));
286     assertReadFrom(getTestBytes(0x110));
287     assertReadFrom(getTestBytes(0x1000));
288     assertReadFrom(getTestBytes(0x1001));
289     assertReadFrom(getTestBytes(0x1010));
290     assertReadFrom(getTestBytes(0x10000));
291     assertReadFrom(getTestBytes(0x10001));
292     assertReadFrom(getTestBytes(0x10010));
293   }
294 
295   // Tests sizes that are near the read buffer size.
testReadFrom_byteBoundaries()296   public void testReadFrom_byteBoundaries() throws IOException {
297     final int min = ByteString.MIN_READ_FROM_CHUNK_SIZE;
298     final int max = ByteString.MAX_READ_FROM_CHUNK_SIZE;
299 
300     assertReadFrom(getTestBytes(min - 1));
301     assertReadFrom(getTestBytes(min));
302     assertReadFrom(getTestBytes(min + 1));
303 
304     assertReadFrom(getTestBytes(min * 2 - 1));
305     assertReadFrom(getTestBytes(min * 2));
306     assertReadFrom(getTestBytes(min * 2 + 1));
307 
308     assertReadFrom(getTestBytes(min * 4 - 1));
309     assertReadFrom(getTestBytes(min * 4));
310     assertReadFrom(getTestBytes(min * 4 + 1));
311 
312     assertReadFrom(getTestBytes(min * 8 - 1));
313     assertReadFrom(getTestBytes(min * 8));
314     assertReadFrom(getTestBytes(min * 8 + 1));
315 
316     assertReadFrom(getTestBytes(max - 1));
317     assertReadFrom(getTestBytes(max));
318     assertReadFrom(getTestBytes(max + 1));
319 
320     assertReadFrom(getTestBytes(max * 2 - 1));
321     assertReadFrom(getTestBytes(max * 2));
322     assertReadFrom(getTestBytes(max * 2 + 1));
323   }
324 
325   // Tests that IOExceptions propagate through ByteString.readFrom().
testReadFrom_IOExceptions()326   public void testReadFrom_IOExceptions() {
327     try {
328       ByteString.readFrom(new FailStream());
329       fail("readFrom must throw the underlying IOException");
330 
331     } catch (IOException e) {
332       assertEquals(
333           "readFrom must throw the expected exception", "synthetic failure", e.getMessage());
334     }
335   }
336 
337   // Tests that ByteString.readFrom works with streams that don't
338   // always fill their buffers.
testReadFrom_reluctantStream()339   public void testReadFrom_reluctantStream() throws IOException {
340     final byte[] data = getTestBytes(0x1000);
341 
342     ByteString byteString = ByteString.readFrom(new ReluctantStream(data));
343     assertTrue(
344         "readFrom byte stream must contain the expected bytes",
345         isArray(byteString.toByteArray(), data));
346 
347     // Same test as above, but with some specific chunk sizes.
348     assertReadFromReluctantStream(data, 100);
349     assertReadFromReluctantStream(data, 248);
350     assertReadFromReluctantStream(data, 249);
351     assertReadFromReluctantStream(data, 250);
352     assertReadFromReluctantStream(data, 251);
353     assertReadFromReluctantStream(data, 0x1000);
354     assertReadFromReluctantStream(data, 0x1001);
355   }
356 
357   // Fails unless ByteString.readFrom reads the bytes correctly from a
358   // reluctant stream with the given chunkSize parameter.
assertReadFromReluctantStream(byte[] bytes, int chunkSize)359   private void assertReadFromReluctantStream(byte[] bytes, int chunkSize) throws IOException {
360     ByteString b = ByteString.readFrom(new ReluctantStream(bytes), chunkSize);
361     assertTrue(
362         "readFrom byte stream must contain the expected bytes", isArray(b.toByteArray(), bytes));
363   }
364 
365   // Tests that ByteString.readFrom works with streams that implement
366   // available().
testReadFrom_available()367   public void testReadFrom_available() throws IOException {
368     final byte[] data = getTestBytes(0x1001);
369 
370     ByteString byteString = ByteString.readFrom(new AvailableStream(data));
371     assertTrue(
372         "readFrom byte stream must contain the expected bytes",
373         isArray(byteString.toByteArray(), data));
374   }
375 
376   // Fails unless ByteString.readFrom reads the bytes correctly.
assertReadFrom(byte[] bytes)377   private void assertReadFrom(byte[] bytes) throws IOException {
378     ByteString byteString = ByteString.readFrom(new ByteArrayInputStream(bytes));
379     assertTrue(
380         "readFrom byte stream must contain the expected bytes",
381         isArray(byteString.toByteArray(), bytes));
382   }
383 
384   // A stream that fails when read.
385   private static final class FailStream extends InputStream {
386     @Override
read()387     public int read() throws IOException {
388       throw new IOException("synthetic failure");
389     }
390   }
391 
392   // A stream that simulates blocking by only producing 250 characters
393   // per call to read(byte[]).
394   private static class ReluctantStream extends InputStream {
395     protected final byte[] data;
396     protected int pos = 0;
397 
ReluctantStream(byte[] data)398     public ReluctantStream(byte[] data) {
399       this.data = data;
400     }
401 
402     @Override
read()403     public int read() {
404       if (pos == data.length) {
405         return -1;
406       } else {
407         return data[pos++];
408       }
409     }
410 
411     @Override
read(byte[] buf)412     public int read(byte[] buf) {
413       return read(buf, 0, buf.length);
414     }
415 
416     @Override
read(byte[] buf, int offset, int size)417     public int read(byte[] buf, int offset, int size) {
418       if (pos == data.length) {
419         return -1;
420       }
421       int count = Math.min(Math.min(size, data.length - pos), 250);
422       System.arraycopy(data, pos, buf, offset, count);
423       pos += count;
424       return count;
425     }
426   }
427 
428   // Same as above, but also implements available().
429   private static final class AvailableStream extends ReluctantStream {
AvailableStream(byte[] data)430     public AvailableStream(byte[] data) {
431       super(data);
432     }
433 
434     @Override
available()435     public int available() {
436       return Math.min(250, data.length - pos);
437     }
438   }
439 
440   // A stream which exposes the byte array passed into read(byte[], int, int).
441   private static class EvilInputStream extends InputStream {
442     public byte[] capturedArray = null;
443 
444     @Override
read(byte[] buf, int off, int len)445     public int read(byte[] buf, int off, int len) {
446       if (capturedArray != null) {
447         return -1;
448       } else {
449         capturedArray = buf;
450         for (int x = 0; x < len; ++x) {
451           buf[x] = (byte) x;
452         }
453         return len;
454       }
455     }
456 
457     @Override
read()458     public int read() {
459       // Purposefully do nothing.
460       return -1;
461     }
462   }
463 
464   // A stream which exposes the byte array passed into write(byte[], int, int).
465   private static class EvilOutputStream extends OutputStream {
466     public byte[] capturedArray = null;
467 
468     @Override
write(byte[] buf, int off, int len)469     public void write(byte[] buf, int off, int len) {
470       if (capturedArray == null) {
471         capturedArray = buf;
472       }
473     }
474 
475     @Override
write(int ignored)476     public void write(int ignored) {
477       // Purposefully do nothing.
478     }
479   }
480 
testToStringUtf8()481   public void testToStringUtf8() {
482     String testString = "I love unicode \u1234\u5678 characters";
483     byte[] testBytes = testString.getBytes(Internal.UTF_8);
484     ByteString byteString = ByteString.copyFrom(testBytes);
485     assertEquals(
486         "copyToStringUtf8 must respect the charset", testString, byteString.toStringUtf8());
487   }
488 
testToString()489   public void testToString() {
490     String toString =
491         ByteString.copyFrom("Here are some bytes: \t\u00a1".getBytes(Internal.UTF_8)).toString();
492     assertTrue(toString, toString.contains("size=24"));
493     assertTrue(toString, toString.contains("contents=\"Here are some bytes: \\t\\302\\241\""));
494   }
495 
testToString_long()496   public void testToString_long() {
497     String toString =
498         ByteString.copyFrom(
499                 "123456789012345678901234567890123456789012345678901234567890"
500                     .getBytes(Internal.UTF_8))
501             .toString();
502     assertTrue(toString, toString.contains("size=60"));
503     assertTrue(
504         toString,
505         toString.contains("contents=\"12345678901234567890123456789012345678901234567...\""));
506   }
507 
testNewOutput_InitialCapacity()508   public void testNewOutput_InitialCapacity() throws IOException {
509     byte[] bytes = getTestBytes();
510     ByteString.Output output = ByteString.newOutput(bytes.length + 100);
511     output.write(bytes);
512     ByteString byteString = output.toByteString();
513     assertTrue(
514         "String built from newOutput(int) must contain the expected bytes",
515         isArrayRange(bytes, byteString.toByteArray(), 0, bytes.length));
516   }
517 
518   // Test newOutput() using a variety of buffer sizes and a variety of (fixed)
519   // write sizes
testNewOutput_ArrayWrite()520   public void testNewOutput_ArrayWrite() {
521     byte[] bytes = getTestBytes();
522     int length = bytes.length;
523     int[] bufferSizes = {
524       128, 256, length / 2, length - 1, length, length + 1, 2 * length, 3 * length
525     };
526     int[] writeSizes = {1, 4, 5, 7, 23, bytes.length};
527 
528     for (int bufferSize : bufferSizes) {
529       for (int writeSize : writeSizes) {
530         // Test writing the entire output writeSize bytes at a time.
531         ByteString.Output output = ByteString.newOutput(bufferSize);
532         for (int i = 0; i < length; i += writeSize) {
533           output.write(bytes, i, Math.min(writeSize, length - i));
534         }
535         ByteString byteString = output.toByteString();
536         assertTrue(
537             "String built from newOutput() must contain the expected bytes",
538             isArrayRange(bytes, byteString.toByteArray(), 0, bytes.length));
539       }
540     }
541   }
542 
543   // Test newOutput() using a variety of buffer sizes, but writing all the
544   // characters using write(byte);
testNewOutput_WriteChar()545   public void testNewOutput_WriteChar() {
546     byte[] bytes = getTestBytes();
547     int length = bytes.length;
548     int[] bufferSizes = {
549       0, 1, 128, 256, length / 2, length - 1, length, length + 1, 2 * length, 3 * length
550     };
551     for (int bufferSize : bufferSizes) {
552       ByteString.Output output = ByteString.newOutput(bufferSize);
553       for (byte byteValue : bytes) {
554         output.write(byteValue);
555       }
556       ByteString byteString = output.toByteString();
557       assertTrue(
558           "String built from newOutput() must contain the expected bytes",
559           isArrayRange(bytes, byteString.toByteArray(), 0, bytes.length));
560     }
561   }
562 
563   // Test newOutput() in which we write the bytes using a variety of methods
564   // and sizes, and in which we repeatedly call toByteString() in the middle.
testNewOutput_Mixed()565   public void testNewOutput_Mixed() {
566     Random rng = new Random(1);
567     byte[] bytes = getTestBytes();
568     int length = bytes.length;
569     int[] bufferSizes = {
570       0, 1, 128, 256, length / 2, length - 1, length, length + 1, 2 * length, 3 * length
571     };
572 
573     for (int bufferSize : bufferSizes) {
574       // Test writing the entire output using a mixture of write sizes and
575       // methods;
576       ByteString.Output output = ByteString.newOutput(bufferSize);
577       int position = 0;
578       while (position < bytes.length) {
579         if (rng.nextBoolean()) {
580           int count = 1 + rng.nextInt(bytes.length - position);
581           output.write(bytes, position, count);
582           position += count;
583         } else {
584           output.write(bytes[position]);
585           position++;
586         }
587         assertEquals("size() returns the right value", position, output.size());
588         assertTrue(
589             "newOutput() substring must have correct bytes",
590             isArrayRange(output.toByteString().toByteArray(), bytes, 0, position));
591       }
592       ByteString byteString = output.toByteString();
593       assertTrue(
594           "String built from newOutput() must contain the expected bytes",
595           isArrayRange(bytes, byteString.toByteArray(), 0, bytes.length));
596     }
597   }
598 
testNewOutputEmpty()599   public void testNewOutputEmpty() {
600     // Make sure newOutput() correctly builds empty byte strings
601     ByteString byteString = ByteString.newOutput().toByteString();
602     assertEquals(ByteString.EMPTY, byteString);
603   }
604 
testNewOutput_Mutating()605   public void testNewOutput_Mutating() throws IOException {
606     Output os = ByteString.newOutput(5);
607     os.write(new byte[] {1, 2, 3, 4, 5});
608     EvilOutputStream eos = new EvilOutputStream();
609     os.writeTo(eos);
610     byte[] capturedArray = eos.capturedArray;
611     ByteString byteString = os.toByteString();
612     byte[] oldValue = byteString.toByteArray();
613     Arrays.fill(capturedArray, (byte) 0);
614     byte[] newValue = byteString.toByteArray();
615     assertTrue(
616         "Output must not provide access to the underlying byte array",
617         Arrays.equals(oldValue, newValue));
618   }
619 
testNewCodedBuilder()620   public void testNewCodedBuilder() throws IOException {
621     byte[] bytes = getTestBytes();
622     ByteString.CodedBuilder builder = ByteString.newCodedBuilder(bytes.length);
623     builder.getCodedOutput().writeRawBytes(bytes);
624     ByteString byteString = builder.build();
625     assertTrue(
626         "String built from newCodedBuilder() must contain the expected bytes",
627         isArrayRange(bytes, byteString.toByteArray(), 0, bytes.length));
628   }
629 
testSubstringParity()630   public void testSubstringParity() {
631     byte[] bigBytes = getTestBytes(2048 * 1024, 113344L);
632     int start = 512 * 1024 - 3333;
633     int end = 512 * 1024 + 7777;
634     ByteString concreteSubstring = ByteString.copyFrom(bigBytes).substring(start, end);
635     boolean ok = true;
636     for (int i = start; ok && i < end; ++i) {
637       ok = (bigBytes[i] == concreteSubstring.byteAt(i - start));
638     }
639     assertTrue("Concrete substring didn't capture the right bytes", ok);
640 
641     ByteString literalString = ByteString.copyFrom(bigBytes, start, end - start);
642     assertEquals("Substring must be equal to literal string", literalString, concreteSubstring);
643     assertEquals(
644         "Substring must have same hashcode as literal string",
645         literalString.hashCode(),
646         concreteSubstring.hashCode());
647   }
648 
testCompositeSubstring()649   public void testCompositeSubstring() {
650     byte[] referenceBytes = getTestBytes(77748, 113344L);
651 
652     List<ByteString> pieces = makeConcretePieces(referenceBytes);
653     ByteString listString = ByteString.copyFrom(pieces);
654 
655     int from = 1000;
656     int to = 40000;
657     ByteString compositeSubstring = listString.substring(from, to);
658     byte[] substringBytes = compositeSubstring.toByteArray();
659     boolean stillEqual = true;
660     for (int i = 0; stillEqual && i < to - from; ++i) {
661       stillEqual = referenceBytes[from + i] == substringBytes[i];
662     }
663     assertTrue("Substring must return correct bytes", stillEqual);
664 
665     stillEqual = true;
666     for (int i = 0; stillEqual && i < to - from; ++i) {
667       stillEqual = referenceBytes[from + i] == compositeSubstring.byteAt(i);
668     }
669     assertTrue("Substring must support byteAt() correctly", stillEqual);
670 
671     ByteString literalSubstring = ByteString.copyFrom(referenceBytes, from, to - from);
672     assertEquals(
673         "Composite substring must equal a literal substring over the same bytes",
674         literalSubstring,
675         compositeSubstring);
676     assertEquals(
677         "Literal substring must equal a composite substring over the same bytes",
678         compositeSubstring,
679         literalSubstring);
680 
681     assertEquals(
682         "We must get the same hashcodes for composite and literal substrings",
683         literalSubstring.hashCode(),
684         compositeSubstring.hashCode());
685 
686     assertFalse(
687         "We can't be equal to a proper substring",
688         compositeSubstring.equals(literalSubstring.substring(0, literalSubstring.size() - 1)));
689   }
690 
testCopyFromList()691   public void testCopyFromList() {
692     byte[] referenceBytes = getTestBytes(77748, 113344L);
693     ByteString literalString = ByteString.copyFrom(referenceBytes);
694 
695     List<ByteString> pieces = makeConcretePieces(referenceBytes);
696     ByteString listString = ByteString.copyFrom(pieces);
697 
698     assertEquals("Composite string must be equal to literal string", literalString, listString);
699     assertEquals(
700         "Composite string must have same hashcode as literal string",
701         literalString.hashCode(),
702         listString.hashCode());
703   }
704 
testConcat()705   public void testConcat() {
706     byte[] referenceBytes = getTestBytes(77748, 113344L);
707     ByteString literalString = ByteString.copyFrom(referenceBytes);
708 
709     List<ByteString> pieces = makeConcretePieces(referenceBytes);
710 
711     Iterator<ByteString> iter = pieces.iterator();
712     ByteString concatenatedString = iter.next();
713     while (iter.hasNext()) {
714       concatenatedString = concatenatedString.concat(iter.next());
715     }
716 
717     assertEquals(
718         "Concatenated string must be equal to literal string", literalString, concatenatedString);
719     assertEquals(
720         "Concatenated string must have same hashcode as literal string",
721         literalString.hashCode(),
722         concatenatedString.hashCode());
723   }
724 
725   /**
726    * Test the Rope implementation can deal with Empty nodes, even though we guard against them. See
727    * also {@link LiteralByteStringTest#testConcat_empty()}.
728    */
testConcat_empty()729   public void testConcat_empty() {
730     byte[] referenceBytes = getTestBytes(7748, 113344L);
731     ByteString literalString = ByteString.copyFrom(referenceBytes);
732 
733     ByteString duo = RopeByteString.newInstanceForTest(literalString, literalString);
734     ByteString temp =
735         RopeByteString.newInstanceForTest(
736             RopeByteString.newInstanceForTest(literalString, ByteString.EMPTY),
737             RopeByteString.newInstanceForTest(ByteString.EMPTY, literalString));
738     ByteString quintet = RopeByteString.newInstanceForTest(temp, ByteString.EMPTY);
739 
740     assertEquals("String with concatenated nulls must equal simple concatenate", quintet, duo);
741     assertEquals(
742         "String with concatenated nulls have same hashcode as simple concatenate",
743         duo.hashCode(),
744         quintet.hashCode());
745 
746     ByteString.ByteIterator duoIter = duo.iterator();
747     ByteString.ByteIterator quintetIter = quintet.iterator();
748     boolean stillEqual = true;
749     while (stillEqual && quintetIter.hasNext()) {
750       stillEqual = (duoIter.nextByte() == quintetIter.nextByte());
751     }
752     assertTrue("We must get the same characters by iterating", stillEqual);
753     assertFalse("Iterator must be exhausted", duoIter.hasNext());
754     try {
755       duoIter.nextByte();
756       fail("Should have thrown an exception.");
757     } catch (NoSuchElementException e) {
758       // This is success
759     }
760     try {
761       quintetIter.nextByte();
762       fail("Should have thrown an exception.");
763     } catch (NoSuchElementException e) {
764       // This is success
765     }
766 
767     // Test that even if we force empty strings in as rope leaves in this
768     // configuration, we always get a (possibly Bounded) LiteralByteString
769     // for a length 1 substring.
770     //
771     // It is possible, using the testing factory method to create deeply nested
772     // trees of empty leaves, to make a string that will fail this test.
773     for (int i = 1; i < duo.size(); ++i) {
774       assertTrue(
775           "Substrings of size() < 2 must not be RopeByteStrings",
776           duo.substring(i - 1, i) instanceof ByteString.LeafByteString);
777     }
778     for (int i = 1; i < quintet.size(); ++i) {
779       assertTrue(
780           "Substrings of size() < 2 must not be RopeByteStrings",
781           quintet.substring(i - 1, i) instanceof ByteString.LeafByteString);
782     }
783   }
784 
testStartsWith()785   public void testStartsWith() {
786     byte[] bytes = getTestBytes(1000, 1234L);
787     ByteString string = ByteString.copyFrom(bytes);
788     ByteString prefix = ByteString.copyFrom(bytes, 0, 500);
789     ByteString suffix = ByteString.copyFrom(bytes, 400, 600);
790     assertTrue(string.startsWith(ByteString.EMPTY));
791     assertTrue(string.startsWith(string));
792     assertTrue(string.startsWith(prefix));
793     assertFalse(string.startsWith(suffix));
794     assertFalse(prefix.startsWith(suffix));
795     assertFalse(suffix.startsWith(prefix));
796     assertFalse(ByteString.EMPTY.startsWith(prefix));
797     assertTrue(ByteString.EMPTY.startsWith(ByteString.EMPTY));
798   }
799 
testEndsWith()800   public void testEndsWith() {
801     byte[] bytes = getTestBytes(1000, 1234L);
802     ByteString string = ByteString.copyFrom(bytes);
803     ByteString prefix = ByteString.copyFrom(bytes, 0, 500);
804     ByteString suffix = ByteString.copyFrom(bytes, 400, 600);
805     assertTrue(string.endsWith(ByteString.EMPTY));
806     assertTrue(string.endsWith(string));
807     assertTrue(string.endsWith(suffix));
808     assertFalse(string.endsWith(prefix));
809     assertFalse(suffix.endsWith(prefix));
810     assertFalse(prefix.endsWith(suffix));
811     assertFalse(ByteString.EMPTY.endsWith(suffix));
812     assertTrue(ByteString.EMPTY.endsWith(ByteString.EMPTY));
813   }
814 
makeConcretePieces(byte[] referenceBytes)815   static List<ByteString> makeConcretePieces(byte[] referenceBytes) {
816     List<ByteString> pieces = new ArrayList<ByteString>();
817     // Starting length should be small enough that we'll do some concatenating by
818     // copying if we just concatenate all these pieces together.
819     for (int start = 0, length = 16; start < referenceBytes.length; start += length) {
820       length = (length << 1) - 1;
821       if (start + length > referenceBytes.length) {
822         length = referenceBytes.length - start;
823       }
824       pieces.add(ByteString.copyFrom(referenceBytes, start, length));
825     }
826     return pieces;
827   }
828 
substringUsingWriteTo(ByteString data, int offset, int length)829   private byte[] substringUsingWriteTo(ByteString data, int offset, int length) throws IOException {
830     ByteArrayOutputStream output = new ByteArrayOutputStream();
831     data.writeTo(output, offset, length);
832     return output.toByteArray();
833   }
834 
testWriteToOutputStream()835   public void testWriteToOutputStream() throws Exception {
836     // Choose a size large enough so when two ByteStrings are concatenated they
837     // won't be merged into one byte array due to some optimizations.
838     final int dataSize = ByteString.CONCATENATE_BY_COPY_SIZE + 1;
839     byte[] data1 = new byte[dataSize];
840     Arrays.fill(data1, (byte) 1);
841     data1[1] = (byte) 11;
842     // Test LiteralByteString.writeTo(OutputStream,int,int)
843     ByteString left = ByteString.wrap(data1);
844     byte[] result = substringUsingWriteTo(left, 1, 1);
845     assertEquals(1, result.length);
846     assertEquals((byte) 11, result[0]);
847 
848     byte[] data2 = new byte[dataSize];
849     Arrays.fill(data2, 0, data1.length, (byte) 2);
850     ByteString right = ByteString.wrap(data2);
851     // Concatenate two ByteStrings to create a RopeByteString.
852     ByteString root = left.concat(right);
853     // Make sure we are actually testing a RopeByteString with a simple tree
854     // structure.
855     assertEquals(1, root.getTreeDepth());
856     // Write parts of the left node.
857     result = substringUsingWriteTo(root, 0, dataSize);
858     assertEquals(dataSize, result.length);
859     assertEquals((byte) 1, result[0]);
860     assertEquals((byte) 1, result[dataSize - 1]);
861     // Write parts of the right node.
862     result = substringUsingWriteTo(root, dataSize, dataSize);
863     assertEquals(dataSize, result.length);
864     assertEquals((byte) 2, result[0]);
865     assertEquals((byte) 2, result[dataSize - 1]);
866     // Write a segment of bytes that runs across both nodes.
867     result = substringUsingWriteTo(root, dataSize / 2, dataSize);
868     assertEquals(dataSize, result.length);
869     assertEquals((byte) 1, result[0]);
870     assertEquals((byte) 1, result[dataSize - dataSize / 2 - 1]);
871     assertEquals((byte) 2, result[dataSize - dataSize / 2]);
872     assertEquals((byte) 2, result[dataSize - 1]);
873   }
874 
875   /** Tests ByteString uses Arrays based byte copier when running under Hotstop VM. */
testByteArrayCopier()876   public void testByteArrayCopier() throws Exception {
877     if (Android.isOnAndroidDevice()) {
878       return;
879     }
880     Field field = ByteString.class.getDeclaredField("byteArrayCopier");
881     field.setAccessible(true);
882     Object byteArrayCopier = field.get(null);
883     assertNotNull(byteArrayCopier);
884     assertTrue(
885         byteArrayCopier.toString(),
886         byteArrayCopier.getClass().getSimpleName().endsWith("ArraysByteArrayCopier"));
887   }
888 }
889