1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 // http://code.google.com/p/protobuf/
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
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
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.
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.
31 package com.google.protobuf;
33 import com.google.protobuf.ByteString.Output;
35 import junit.framework.TestCase;
37 import java.io.ByteArrayInputStream;
38 import java.io.IOException;
39 import java.io.InputStream;
40 import java.io.OutputStream;
41 import java.io.UnsupportedEncodingException;
42 import java.nio.ByteBuffer;
43 import java.util.ArrayList;
44 import java.util.Arrays;
45 import java.util.Iterator;
46 import java.util.List;
47 import java.util.NoSuchElementException;
48 import java.util.Random;
51 * Test methods with implementations in {@link ByteString}, plus do some top-level "integration"
54 * @author carlanton@google.com (Carl Haverl)
56 public class ByteStringTest extends TestCase {
58 private static final String UTF_16 = "UTF-16";
60 static byte[] getTestBytes(int size, long seed) {
61 Random random = new Random(seed);
62 byte[] result = new byte[size];
63 random.nextBytes(result);
67 private byte[] getTestBytes(int size) {
68 return getTestBytes(size, 445566L);
71 private byte[] getTestBytes() {
72 return getTestBytes(1000);
75 // Compare the entire left array with a subset of the right array.
76 private boolean isArrayRange(byte[] left, byte[] right, int rightOffset, int length) {
77 boolean stillEqual = (left.length == length);
78 for (int i = 0; (stillEqual && i < length); ++i) {
79 stillEqual = (left[i] == right[rightOffset + i]);
84 // Returns true only if the given two arrays have identical contents.
85 private boolean isArray(byte[] left, byte[] right) {
86 return left.length == right.length && isArrayRange(left, right, 0, left.length);
89 public void testSubstring_BeginIndex() {
90 byte[] bytes = getTestBytes();
91 ByteString substring = ByteString.copyFrom(bytes).substring(500);
92 assertTrue("substring must contain the tail of the string",
93 isArrayRange(substring.toByteArray(), bytes, 500, bytes.length - 500));
96 public void testCopyFrom_BytesOffsetSize() {
97 byte[] bytes = getTestBytes();
98 ByteString byteString = ByteString.copyFrom(bytes, 500, 200);
99 assertTrue("copyFrom sub-range must contain the expected bytes",
100 isArrayRange(byteString.toByteArray(), bytes, 500, 200));
103 public void testCopyFrom_Bytes() {
104 byte[] bytes = getTestBytes();
105 ByteString byteString = ByteString.copyFrom(bytes);
106 assertTrue("copyFrom must contain the expected bytes",
107 isArray(byteString.toByteArray(), bytes));
110 public void testCopyFrom_ByteBufferSize() {
111 byte[] bytes = getTestBytes();
112 ByteBuffer byteBuffer = ByteBuffer.allocate(bytes.length);
113 byteBuffer.put(bytes);
114 byteBuffer.position(500);
115 ByteString byteString = ByteString.copyFrom(byteBuffer, 200);
116 assertTrue("copyFrom byteBuffer sub-range must contain the expected bytes",
117 isArrayRange(byteString.toByteArray(), bytes, 500, 200));
120 public void testCopyFrom_ByteBuffer() {
121 byte[] bytes = getTestBytes();
122 ByteBuffer byteBuffer = ByteBuffer.allocate(bytes.length);
123 byteBuffer.put(bytes);
124 byteBuffer.position(500);
125 ByteString byteString = ByteString.copyFrom(byteBuffer);
126 assertTrue("copyFrom byteBuffer sub-range must contain the expected bytes",
127 isArrayRange(byteString.toByteArray(), bytes, 500, bytes.length - 500));
130 public void testCopyFrom_StringEncoding() throws UnsupportedEncodingException {
131 String testString = "I love unicode \u1234\u5678 characters";
132 ByteString byteString = ByteString.copyFrom(testString, UTF_16);
133 byte[] testBytes = testString.getBytes(UTF_16);
134 assertTrue("copyFrom string must respect the charset",
135 isArrayRange(byteString.toByteArray(), testBytes, 0, testBytes.length));
138 public void testCopyFrom_Utf8() throws UnsupportedEncodingException {
139 String testString = "I love unicode \u1234\u5678 characters";
140 ByteString byteString = ByteString.copyFromUtf8(testString);
141 byte[] testBytes = testString.getBytes("UTF-8");
142 assertTrue("copyFromUtf8 string must respect the charset",
143 isArrayRange(byteString.toByteArray(), testBytes, 0, testBytes.length));
146 public void testCopyFrom_Iterable() {
147 byte[] testBytes = getTestBytes(77777, 113344L);
148 final List<ByteString> pieces = makeConcretePieces(testBytes);
149 // Call copyFrom() on a Collection
150 ByteString byteString = ByteString.copyFrom(pieces);
151 assertTrue("copyFrom a List must contain the expected bytes",
152 isArrayRange(byteString.toByteArray(), testBytes, 0, testBytes.length));
153 // Call copyFrom on an iteration that's not a collection
154 ByteString byteStringAlt = ByteString.copyFrom(new Iterable<ByteString>() {
155 public Iterator<ByteString> iterator() {
156 return pieces.iterator();
159 assertEquals("copyFrom from an Iteration must contain the expected bytes",
160 byteString, byteStringAlt);
163 public void testCopyTo_TargetOffset() {
164 byte[] bytes = getTestBytes();
165 ByteString byteString = ByteString.copyFrom(bytes);
166 byte[] target = new byte[bytes.length + 1000];
167 byteString.copyTo(target, 400);
168 assertTrue("copyFrom byteBuffer sub-range must contain the expected bytes",
169 isArrayRange(bytes, target, 400, bytes.length));
172 public void testReadFrom_emptyStream() throws IOException {
173 ByteString byteString =
174 ByteString.readFrom(new ByteArrayInputStream(new byte[0]));
175 assertSame("reading an empty stream must result in the EMPTY constant "
176 + "byte string", ByteString.EMPTY, byteString);
179 public void testReadFrom_smallStream() throws IOException {
180 assertReadFrom(getTestBytes(10));
183 public void testReadFrom_mutating() throws IOException {
184 byte[] capturedArray = null;
185 EvilInputStream eis = new EvilInputStream();
186 ByteString byteString = ByteString.readFrom(eis);
188 capturedArray = eis.capturedArray;
189 byte[] originalValue = byteString.toByteArray();
190 for (int x = 0; x < capturedArray.length; ++x) {
191 capturedArray[x] = (byte) 0;
194 byte[] newValue = byteString.toByteArray();
195 assertTrue("copyFrom byteBuffer must not grant access to underlying array",
196 Arrays.equals(originalValue, newValue));
199 // Tests sizes that are near the rope copy-out threshold.
200 public void testReadFrom_mediumStream() throws IOException {
201 assertReadFrom(getTestBytes(ByteString.CONCATENATE_BY_COPY_SIZE - 1));
202 assertReadFrom(getTestBytes(ByteString.CONCATENATE_BY_COPY_SIZE));
203 assertReadFrom(getTestBytes(ByteString.CONCATENATE_BY_COPY_SIZE + 1));
204 assertReadFrom(getTestBytes(200));
207 // Tests sizes that are over multi-segment rope threshold.
208 public void testReadFrom_largeStream() throws IOException {
209 assertReadFrom(getTestBytes(0x100));
210 assertReadFrom(getTestBytes(0x101));
211 assertReadFrom(getTestBytes(0x110));
212 assertReadFrom(getTestBytes(0x1000));
213 assertReadFrom(getTestBytes(0x1001));
214 assertReadFrom(getTestBytes(0x1010));
215 assertReadFrom(getTestBytes(0x10000));
216 assertReadFrom(getTestBytes(0x10001));
217 assertReadFrom(getTestBytes(0x10010));
220 // Tests sizes that are near the read buffer size.
221 public void testReadFrom_byteBoundaries() throws IOException {
222 final int min = ByteString.MIN_READ_FROM_CHUNK_SIZE;
223 final int max = ByteString.MAX_READ_FROM_CHUNK_SIZE;
225 assertReadFrom(getTestBytes(min - 1));
226 assertReadFrom(getTestBytes(min));
227 assertReadFrom(getTestBytes(min + 1));
229 assertReadFrom(getTestBytes(min * 2 - 1));
230 assertReadFrom(getTestBytes(min * 2));
231 assertReadFrom(getTestBytes(min * 2 + 1));
233 assertReadFrom(getTestBytes(min * 4 - 1));
234 assertReadFrom(getTestBytes(min * 4));
235 assertReadFrom(getTestBytes(min * 4 + 1));
237 assertReadFrom(getTestBytes(min * 8 - 1));
238 assertReadFrom(getTestBytes(min * 8));
239 assertReadFrom(getTestBytes(min * 8 + 1));
241 assertReadFrom(getTestBytes(max - 1));
242 assertReadFrom(getTestBytes(max));
243 assertReadFrom(getTestBytes(max + 1));
245 assertReadFrom(getTestBytes(max * 2 - 1));
246 assertReadFrom(getTestBytes(max * 2));
247 assertReadFrom(getTestBytes(max * 2 + 1));
250 // Tests that IOExceptions propagate through ByteString.readFrom().
251 public void testReadFrom_IOExceptions() {
253 ByteString.readFrom(new FailStream());
254 fail("readFrom must throw the underlying IOException");
256 } catch (IOException e) {
257 assertEquals("readFrom must throw the expected exception",
258 "synthetic failure", e.getMessage());
262 // Tests that ByteString.readFrom works with streams that don't
263 // always fill their buffers.
264 public void testReadFrom_reluctantStream() throws IOException {
265 final byte[] data = getTestBytes(0x1000);
267 ByteString byteString = ByteString.readFrom(new ReluctantStream(data));
268 assertTrue("readFrom byte stream must contain the expected bytes",
269 isArray(byteString.toByteArray(), data));
271 // Same test as above, but with some specific chunk sizes.
272 assertReadFromReluctantStream(data, 100);
273 assertReadFromReluctantStream(data, 248);
274 assertReadFromReluctantStream(data, 249);
275 assertReadFromReluctantStream(data, 250);
276 assertReadFromReluctantStream(data, 251);
277 assertReadFromReluctantStream(data, 0x1000);
278 assertReadFromReluctantStream(data, 0x1001);
281 // Fails unless ByteString.readFrom reads the bytes correctly from a
282 // reluctant stream with the given chunkSize parameter.
283 private void assertReadFromReluctantStream(byte[] bytes, int chunkSize)
285 ByteString b = ByteString.readFrom(new ReluctantStream(bytes), chunkSize);
286 assertTrue("readFrom byte stream must contain the expected bytes",
287 isArray(b.toByteArray(), bytes));
290 // Tests that ByteString.readFrom works with streams that implement
292 public void testReadFrom_available() throws IOException {
293 final byte[] data = getTestBytes(0x1001);
295 ByteString byteString = ByteString.readFrom(new AvailableStream(data));
296 assertTrue("readFrom byte stream must contain the expected bytes",
297 isArray(byteString.toByteArray(), data));
300 // Fails unless ByteString.readFrom reads the bytes correctly.
301 private void assertReadFrom(byte[] bytes) throws IOException {
302 ByteString byteString =
303 ByteString.readFrom(new ByteArrayInputStream(bytes));
304 assertTrue("readFrom byte stream must contain the expected bytes",
305 isArray(byteString.toByteArray(), bytes));
308 // A stream that fails when read.
309 private static final class FailStream extends InputStream {
310 @Override public int read() throws IOException {
311 throw new IOException("synthetic failure");
315 // A stream that simulates blocking by only producing 250 characters
316 // per call to read(byte[]).
317 private static class ReluctantStream extends InputStream {
318 protected final byte[] data;
319 protected int pos = 0;
321 public ReluctantStream(byte[] data) {
325 @Override public int read() {
326 if (pos == data.length) {
333 @Override public int read(byte[] buf) {
334 return read(buf, 0, buf.length);
337 @Override public int read(byte[] buf, int offset, int size) {
338 if (pos == data.length) {
341 int count = Math.min(Math.min(size, data.length - pos), 250);
342 System.arraycopy(data, pos, buf, offset, count);
348 // Same as above, but also implements available().
349 private static final class AvailableStream extends ReluctantStream {
350 public AvailableStream(byte[] data) {
354 @Override public int available() {
355 return Math.min(250, data.length - pos);
359 // A stream which exposes the byte array passed into read(byte[], int, int).
360 private static class EvilInputStream extends InputStream {
361 public byte[] capturedArray = null;
364 public int read(byte[] buf, int off, int len) {
365 if (capturedArray != null) {
369 for (int x = 0; x < len; ++x) {
378 // Purposefully do nothing.
383 // A stream which exposes the byte array passed into write(byte[], int, int).
384 private static class EvilOutputStream extends OutputStream {
385 public byte[] capturedArray = null;
388 public void write(byte[] buf, int off, int len) {
389 if (capturedArray == null) {
395 public void write(int ignored) {
396 // Purposefully do nothing.
400 public void testToStringUtf8() throws UnsupportedEncodingException {
401 String testString = "I love unicode \u1234\u5678 characters";
402 byte[] testBytes = testString.getBytes("UTF-8");
403 ByteString byteString = ByteString.copyFrom(testBytes);
404 assertEquals("copyToStringUtf8 must respect the charset",
405 testString, byteString.toStringUtf8());
408 public void testNewOutput_InitialCapacity() throws IOException {
409 byte[] bytes = getTestBytes();
410 ByteString.Output output = ByteString.newOutput(bytes.length + 100);
412 ByteString byteString = output.toByteString();
414 "String built from newOutput(int) must contain the expected bytes",
415 isArrayRange(bytes, byteString.toByteArray(), 0, bytes.length));
418 // Test newOutput() using a variety of buffer sizes and a variety of (fixed)
420 public void testNewOutput_ArrayWrite() throws IOException {
421 byte[] bytes = getTestBytes();
422 int length = bytes.length;
423 int[] bufferSizes = {128, 256, length / 2, length - 1, length, length + 1,
424 2 * length, 3 * length};
425 int[] writeSizes = {1, 4, 5, 7, 23, bytes.length};
427 for (int bufferSize : bufferSizes) {
428 for (int writeSize : writeSizes) {
429 // Test writing the entire output writeSize bytes at a time.
430 ByteString.Output output = ByteString.newOutput(bufferSize);
431 for (int i = 0; i < length; i += writeSize) {
432 output.write(bytes, i, Math.min(writeSize, length - i));
434 ByteString byteString = output.toByteString();
435 assertTrue("String built from newOutput() must contain the expected bytes",
436 isArrayRange(bytes, byteString.toByteArray(), 0, bytes.length));
441 // Test newOutput() using a variety of buffer sizes, but writing all the
442 // characters using write(byte);
443 public void testNewOutput_WriteChar() throws IOException {
444 byte[] bytes = getTestBytes();
445 int length = bytes.length;
446 int[] bufferSizes = {0, 1, 128, 256, length / 2,
447 length - 1, length, length + 1,
448 2 * length, 3 * length};
449 for (int bufferSize : bufferSizes) {
450 ByteString.Output output = ByteString.newOutput(bufferSize);
451 for (byte byteValue : bytes) {
452 output.write(byteValue);
454 ByteString byteString = output.toByteString();
455 assertTrue("String built from newOutput() must contain the expected bytes",
456 isArrayRange(bytes, byteString.toByteArray(), 0, bytes.length));
460 // Test newOutput() in which we write the bytes using a variety of methods
461 // and sizes, and in which we repeatedly call toByteString() in the middle.
462 public void testNewOutput_Mixed() throws IOException {
463 Random rng = new Random(1);
464 byte[] bytes = getTestBytes();
465 int length = bytes.length;
466 int[] bufferSizes = {0, 1, 128, 256, length / 2,
467 length - 1, length, length + 1,
468 2 * length, 3 * length};
470 for (int bufferSize : bufferSizes) {
471 // Test writing the entire output using a mixture of write sizes and
473 ByteString.Output output = ByteString.newOutput(bufferSize);
475 while (position < bytes.length) {
476 if (rng.nextBoolean()) {
477 int count = 1 + rng.nextInt(bytes.length - position);
478 output.write(bytes, position, count);
481 output.write(bytes[position]);
484 assertEquals("size() returns the right value", position, output.size());
485 assertTrue("newOutput() substring must have correct bytes",
486 isArrayRange(output.toByteString().toByteArray(),
487 bytes, 0, position));
489 ByteString byteString = output.toByteString();
490 assertTrue("String built from newOutput() must contain the expected bytes",
491 isArrayRange(bytes, byteString.toByteArray(), 0, bytes.length));
495 public void testNewOutputEmpty() throws IOException {
496 // Make sure newOutput() correctly builds empty byte strings
497 ByteString byteString = ByteString.newOutput().toByteString();
498 assertEquals(ByteString.EMPTY, byteString);
501 public void testNewOutput_Mutating() throws IOException {
502 Output os = ByteString.newOutput(5);
503 os.write(new byte[] {1, 2, 3, 4, 5});
504 EvilOutputStream eos = new EvilOutputStream();
506 byte[] capturedArray = eos.capturedArray;
507 ByteString byteString = os.toByteString();
508 byte[] oldValue = byteString.toByteArray();
509 Arrays.fill(capturedArray, (byte) 0);
510 byte[] newValue = byteString.toByteArray();
511 assertTrue("Output must not provide access to the underlying byte array",
512 Arrays.equals(oldValue, newValue));
515 public void testNewCodedBuilder() throws IOException {
516 byte[] bytes = getTestBytes();
517 ByteString.CodedBuilder builder = ByteString.newCodedBuilder(bytes.length);
518 builder.getCodedOutput().writeRawBytes(bytes);
519 ByteString byteString = builder.build();
520 assertTrue("String built from newCodedBuilder() must contain the expected bytes",
521 isArrayRange(bytes, byteString.toByteArray(), 0, bytes.length));
524 public void testSubstringParity() {
525 byte[] bigBytes = getTestBytes(2048 * 1024, 113344L);
526 int start = 512 * 1024 - 3333;
527 int end = 512 * 1024 + 7777;
528 ByteString concreteSubstring = ByteString.copyFrom(bigBytes).substring(start, end);
530 for (int i = start; ok && i < end; ++i) {
531 ok = (bigBytes[i] == concreteSubstring.byteAt(i - start));
533 assertTrue("Concrete substring didn't capture the right bytes", ok);
535 ByteString literalString = ByteString.copyFrom(bigBytes, start, end - start);
536 assertTrue("Substring must be equal to literal string",
537 concreteSubstring.equals(literalString));
538 assertEquals("Substring must have same hashcode as literal string",
539 literalString.hashCode(), concreteSubstring.hashCode());
542 public void testCompositeSubstring() {
543 byte[] referenceBytes = getTestBytes(77748, 113344L);
545 List<ByteString> pieces = makeConcretePieces(referenceBytes);
546 ByteString listString = ByteString.copyFrom(pieces);
550 ByteString compositeSubstring = listString.substring(from, to);
551 byte[] substringBytes = compositeSubstring.toByteArray();
552 boolean stillEqual = true;
553 for (int i = 0; stillEqual && i < to - from; ++i) {
554 stillEqual = referenceBytes[from + i] == substringBytes[i];
556 assertTrue("Substring must return correct bytes", stillEqual);
559 for (int i = 0; stillEqual && i < to - from; ++i) {
560 stillEqual = referenceBytes[from + i] == compositeSubstring.byteAt(i);
562 assertTrue("Substring must support byteAt() correctly", stillEqual);
564 ByteString literalSubstring = ByteString.copyFrom(referenceBytes, from, to - from);
565 assertTrue("Composite substring must equal a literal substring over the same bytes",
566 compositeSubstring.equals(literalSubstring));
567 assertTrue("Literal substring must equal a composite substring over the same bytes",
568 literalSubstring.equals(compositeSubstring));
570 assertEquals("We must get the same hashcodes for composite and literal substrings",
571 literalSubstring.hashCode(), compositeSubstring.hashCode());
573 assertFalse("We can't be equal to a proper substring",
574 compositeSubstring.equals(literalSubstring.substring(0, literalSubstring.size() - 1)));
577 public void testCopyFromList() {
578 byte[] referenceBytes = getTestBytes(77748, 113344L);
579 ByteString literalString = ByteString.copyFrom(referenceBytes);
581 List<ByteString> pieces = makeConcretePieces(referenceBytes);
582 ByteString listString = ByteString.copyFrom(pieces);
584 assertTrue("Composite string must be equal to literal string",
585 listString.equals(literalString));
586 assertEquals("Composite string must have same hashcode as literal string",
587 literalString.hashCode(), listString.hashCode());
590 public void testConcat() {
591 byte[] referenceBytes = getTestBytes(77748, 113344L);
592 ByteString literalString = ByteString.copyFrom(referenceBytes);
594 List<ByteString> pieces = makeConcretePieces(referenceBytes);
596 Iterator<ByteString> iter = pieces.iterator();
597 ByteString concatenatedString = iter.next();
598 while (iter.hasNext()) {
599 concatenatedString = concatenatedString.concat(iter.next());
602 assertTrue("Concatenated string must be equal to literal string",
603 concatenatedString.equals(literalString));
604 assertEquals("Concatenated string must have same hashcode as literal string",
605 literalString.hashCode(), concatenatedString.hashCode());
609 * Test the Rope implementation can deal with Empty nodes, even though we
610 * guard against them. See also {@link LiteralByteStringTest#testConcat_empty()}.
612 public void testConcat_empty() {
613 byte[] referenceBytes = getTestBytes(7748, 113344L);
614 ByteString literalString = ByteString.copyFrom(referenceBytes);
616 ByteString duo = RopeByteString.newInstanceForTest(literalString, literalString);
617 ByteString temp = RopeByteString.newInstanceForTest(
618 RopeByteString.newInstanceForTest(literalString, ByteString.EMPTY),
619 RopeByteString.newInstanceForTest(ByteString.EMPTY, literalString));
620 ByteString quintet = RopeByteString.newInstanceForTest(temp, ByteString.EMPTY);
622 assertTrue("String with concatenated nulls must equal simple concatenate",
623 duo.equals(quintet));
624 assertEquals("String with concatenated nulls have same hashcode as simple concatenate",
625 duo.hashCode(), quintet.hashCode());
627 ByteString.ByteIterator duoIter = duo.iterator();
628 ByteString.ByteIterator quintetIter = quintet.iterator();
629 boolean stillEqual = true;
630 while (stillEqual && quintetIter.hasNext()) {
631 stillEqual = (duoIter.nextByte() == quintetIter.nextByte());
633 assertTrue("We must get the same characters by iterating", stillEqual);
634 assertFalse("Iterator must be exhausted", duoIter.hasNext());
637 fail("Should have thrown an exception.");
638 } catch (NoSuchElementException e) {
642 quintetIter.nextByte();
643 fail("Should have thrown an exception.");
644 } catch (NoSuchElementException e) {
648 // Test that even if we force empty strings in as rope leaves in this
649 // configuration, we always get a (possibly Bounded) LiteralByteString
650 // for a length 1 substring.
652 // It is possible, using the testing factory method to create deeply nested
653 // trees of empty leaves, to make a string that will fail this test.
654 for (int i = 1; i < duo.size(); ++i) {
655 assertTrue("Substrings of size() < 2 must not be RopeByteStrings",
656 duo.substring(i - 1, i) instanceof LiteralByteString);
658 for (int i = 1; i < quintet.size(); ++i) {
659 assertTrue("Substrings of size() < 2 must not be RopeByteStrings",
660 quintet.substring(i - 1, i) instanceof LiteralByteString);
664 public void testStartsWith() {
665 byte[] bytes = getTestBytes(1000, 1234L);
666 ByteString string = ByteString.copyFrom(bytes);
667 ByteString prefix = ByteString.copyFrom(bytes, 0, 500);
668 ByteString suffix = ByteString.copyFrom(bytes, 400, 600);
669 assertTrue(string.startsWith(ByteString.EMPTY));
670 assertTrue(string.startsWith(string));
671 assertTrue(string.startsWith(prefix));
672 assertFalse(string.startsWith(suffix));
673 assertFalse(prefix.startsWith(suffix));
674 assertFalse(suffix.startsWith(prefix));
675 assertFalse(ByteString.EMPTY.startsWith(prefix));
676 assertTrue(ByteString.EMPTY.startsWith(ByteString.EMPTY));
679 static List<ByteString> makeConcretePieces(byte[] referenceBytes) {
680 List<ByteString> pieces = new ArrayList<ByteString>();
681 // Starting length should be small enough that we'll do some concatenating by
682 // copying if we just concatenate all these pieces together.
683 for (int start = 0, length = 16; start < referenceBytes.length; start += length) {
684 length = (length << 1) - 1;
685 if (start + length > referenceBytes.length) {
686 length = referenceBytes.length - start;
688 pieces.add(ByteString.copyFrom(referenceBytes, start, length));