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