1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 // https://developers.google.com/protocol-buffers/
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.ByteArrayOutputStream;
39 import java.io.IOException;
40 import java.io.InputStream;
41 import java.io.OutputStream;
42 import java.io.UnsupportedEncodingException;
43 import java.nio.ByteBuffer;
44 import java.util.ArrayList;
45 import java.util.Arrays;
46 import java.util.Iterator;
47 import java.util.List;
48 import java.util.NoSuchElementException;
49 import java.util.Random;
52 * Test methods with implementations in {@link ByteString}, plus do some top-level "integration"
55 * @author carlanton@google.com (Carl Haverl)
57 public class ByteStringTest extends TestCase {
59 private static final String UTF_16 = "UTF-16";
61 static byte[] getTestBytes(int size, long seed) {
62 Random random = new Random(seed);
63 byte[] result = new byte[size];
64 random.nextBytes(result);
68 private byte[] getTestBytes(int size) {
69 return getTestBytes(size, 445566L);
72 private byte[] getTestBytes() {
73 return getTestBytes(1000);
76 // Compare the entire left array with a subset of the right array.
77 private boolean isArrayRange(byte[] left, byte[] right, int rightOffset, int length) {
78 boolean stillEqual = (left.length == length);
79 for (int i = 0; (stillEqual && i < length); ++i) {
80 stillEqual = (left[i] == right[rightOffset + i]);
85 // Returns true only if the given two arrays have identical contents.
86 private boolean isArray(byte[] left, byte[] right) {
87 return left.length == right.length && isArrayRange(left, right, 0, left.length);
90 public void testSubstring_BeginIndex() {
91 byte[] bytes = getTestBytes();
92 ByteString substring = ByteString.copyFrom(bytes).substring(500);
93 assertTrue("substring must contain the tail of the string",
94 isArrayRange(substring.toByteArray(), bytes, 500, bytes.length - 500));
97 public void testCopyFrom_BytesOffsetSize() {
98 byte[] bytes = getTestBytes();
99 ByteString byteString = ByteString.copyFrom(bytes, 500, 200);
100 assertTrue("copyFrom sub-range must contain the expected bytes",
101 isArrayRange(byteString.toByteArray(), bytes, 500, 200));
104 public void testCopyFrom_Bytes() {
105 byte[] bytes = getTestBytes();
106 ByteString byteString = ByteString.copyFrom(bytes);
107 assertTrue("copyFrom must contain the expected bytes",
108 isArray(byteString.toByteArray(), bytes));
111 public void testCopyFrom_ByteBufferSize() {
112 byte[] bytes = getTestBytes();
113 ByteBuffer byteBuffer = ByteBuffer.allocate(bytes.length);
114 byteBuffer.put(bytes);
115 byteBuffer.position(500);
116 ByteString byteString = ByteString.copyFrom(byteBuffer, 200);
117 assertTrue("copyFrom byteBuffer sub-range must contain the expected bytes",
118 isArrayRange(byteString.toByteArray(), bytes, 500, 200));
121 public void testCopyFrom_ByteBuffer() {
122 byte[] bytes = getTestBytes();
123 ByteBuffer byteBuffer = ByteBuffer.allocate(bytes.length);
124 byteBuffer.put(bytes);
125 byteBuffer.position(500);
126 ByteString byteString = ByteString.copyFrom(byteBuffer);
127 assertTrue("copyFrom byteBuffer sub-range must contain the expected bytes",
128 isArrayRange(byteString.toByteArray(), bytes, 500, bytes.length - 500));
131 public void testCopyFrom_StringEncoding() throws UnsupportedEncodingException {
132 String testString = "I love unicode \u1234\u5678 characters";
133 ByteString byteString = ByteString.copyFrom(testString, UTF_16);
134 byte[] testBytes = testString.getBytes(UTF_16);
135 assertTrue("copyFrom string must respect the charset",
136 isArrayRange(byteString.toByteArray(), testBytes, 0, testBytes.length));
139 public void testCopyFrom_Utf8() throws UnsupportedEncodingException {
140 String testString = "I love unicode \u1234\u5678 characters";
141 ByteString byteString = ByteString.copyFromUtf8(testString);
142 byte[] testBytes = testString.getBytes("UTF-8");
143 assertTrue("copyFromUtf8 string must respect the charset",
144 isArrayRange(byteString.toByteArray(), testBytes, 0, testBytes.length));
147 public void testCopyFrom_Iterable() {
148 byte[] testBytes = getTestBytes(77777, 113344L);
149 final List<ByteString> pieces = makeConcretePieces(testBytes);
150 // Call copyFrom() on a Collection
151 ByteString byteString = ByteString.copyFrom(pieces);
152 assertTrue("copyFrom a List must contain the expected bytes",
153 isArrayRange(byteString.toByteArray(), testBytes, 0, testBytes.length));
154 // Call copyFrom on an iteration that's not a collection
155 ByteString byteStringAlt = ByteString.copyFrom(new Iterable<ByteString>() {
156 public Iterator<ByteString> iterator() {
157 return pieces.iterator();
160 assertEquals("copyFrom from an Iteration must contain the expected bytes",
161 byteString, byteStringAlt);
164 public void testCopyTo_TargetOffset() {
165 byte[] bytes = getTestBytes();
166 ByteString byteString = ByteString.copyFrom(bytes);
167 byte[] target = new byte[bytes.length + 1000];
168 byteString.copyTo(target, 400);
169 assertTrue("copyFrom byteBuffer sub-range must contain the expected bytes",
170 isArrayRange(bytes, target, 400, bytes.length));
173 public void testReadFrom_emptyStream() throws IOException {
174 ByteString byteString =
175 ByteString.readFrom(new ByteArrayInputStream(new byte[0]));
176 assertSame("reading an empty stream must result in the EMPTY constant "
177 + "byte string", ByteString.EMPTY, byteString);
180 public void testReadFrom_smallStream() throws IOException {
181 assertReadFrom(getTestBytes(10));
184 public void testReadFrom_mutating() throws IOException {
185 byte[] capturedArray = null;
186 EvilInputStream eis = new EvilInputStream();
187 ByteString byteString = ByteString.readFrom(eis);
189 capturedArray = eis.capturedArray;
190 byte[] originalValue = byteString.toByteArray();
191 for (int x = 0; x < capturedArray.length; ++x) {
192 capturedArray[x] = (byte) 0;
195 byte[] newValue = byteString.toByteArray();
196 assertTrue("copyFrom byteBuffer must not grant access to underlying array",
197 Arrays.equals(originalValue, newValue));
200 // Tests sizes that are near the rope copy-out threshold.
201 public void testReadFrom_mediumStream() throws IOException {
202 assertReadFrom(getTestBytes(ByteString.CONCATENATE_BY_COPY_SIZE - 1));
203 assertReadFrom(getTestBytes(ByteString.CONCATENATE_BY_COPY_SIZE));
204 assertReadFrom(getTestBytes(ByteString.CONCATENATE_BY_COPY_SIZE + 1));
205 assertReadFrom(getTestBytes(200));
208 // Tests sizes that are over multi-segment rope threshold.
209 public void testReadFrom_largeStream() throws IOException {
210 assertReadFrom(getTestBytes(0x100));
211 assertReadFrom(getTestBytes(0x101));
212 assertReadFrom(getTestBytes(0x110));
213 assertReadFrom(getTestBytes(0x1000));
214 assertReadFrom(getTestBytes(0x1001));
215 assertReadFrom(getTestBytes(0x1010));
216 assertReadFrom(getTestBytes(0x10000));
217 assertReadFrom(getTestBytes(0x10001));
218 assertReadFrom(getTestBytes(0x10010));
221 // Tests sizes that are near the read buffer size.
222 public void testReadFrom_byteBoundaries() throws IOException {
223 final int min = ByteString.MIN_READ_FROM_CHUNK_SIZE;
224 final int max = ByteString.MAX_READ_FROM_CHUNK_SIZE;
226 assertReadFrom(getTestBytes(min - 1));
227 assertReadFrom(getTestBytes(min));
228 assertReadFrom(getTestBytes(min + 1));
230 assertReadFrom(getTestBytes(min * 2 - 1));
231 assertReadFrom(getTestBytes(min * 2));
232 assertReadFrom(getTestBytes(min * 2 + 1));
234 assertReadFrom(getTestBytes(min * 4 - 1));
235 assertReadFrom(getTestBytes(min * 4));
236 assertReadFrom(getTestBytes(min * 4 + 1));
238 assertReadFrom(getTestBytes(min * 8 - 1));
239 assertReadFrom(getTestBytes(min * 8));
240 assertReadFrom(getTestBytes(min * 8 + 1));
242 assertReadFrom(getTestBytes(max - 1));
243 assertReadFrom(getTestBytes(max));
244 assertReadFrom(getTestBytes(max + 1));
246 assertReadFrom(getTestBytes(max * 2 - 1));
247 assertReadFrom(getTestBytes(max * 2));
248 assertReadFrom(getTestBytes(max * 2 + 1));
251 // Tests that IOExceptions propagate through ByteString.readFrom().
252 public void testReadFrom_IOExceptions() {
254 ByteString.readFrom(new FailStream());
255 fail("readFrom must throw the underlying IOException");
257 } catch (IOException e) {
258 assertEquals("readFrom must throw the expected exception",
259 "synthetic failure", e.getMessage());
263 // Tests that ByteString.readFrom works with streams that don't
264 // always fill their buffers.
265 public void testReadFrom_reluctantStream() throws IOException {
266 final byte[] data = getTestBytes(0x1000);
268 ByteString byteString = ByteString.readFrom(new ReluctantStream(data));
269 assertTrue("readFrom byte stream must contain the expected bytes",
270 isArray(byteString.toByteArray(), data));
272 // Same test as above, but with some specific chunk sizes.
273 assertReadFromReluctantStream(data, 100);
274 assertReadFromReluctantStream(data, 248);
275 assertReadFromReluctantStream(data, 249);
276 assertReadFromReluctantStream(data, 250);
277 assertReadFromReluctantStream(data, 251);
278 assertReadFromReluctantStream(data, 0x1000);
279 assertReadFromReluctantStream(data, 0x1001);
282 // Fails unless ByteString.readFrom reads the bytes correctly from a
283 // reluctant stream with the given chunkSize parameter.
284 private void assertReadFromReluctantStream(byte[] bytes, int chunkSize)
286 ByteString b = ByteString.readFrom(new ReluctantStream(bytes), chunkSize);
287 assertTrue("readFrom byte stream must contain the expected bytes",
288 isArray(b.toByteArray(), bytes));
291 // Tests that ByteString.readFrom works with streams that implement
293 public void testReadFrom_available() throws IOException {
294 final byte[] data = getTestBytes(0x1001);
296 ByteString byteString = ByteString.readFrom(new AvailableStream(data));
297 assertTrue("readFrom byte stream must contain the expected bytes",
298 isArray(byteString.toByteArray(), data));
301 // Fails unless ByteString.readFrom reads the bytes correctly.
302 private void assertReadFrom(byte[] bytes) throws IOException {
303 ByteString byteString =
304 ByteString.readFrom(new ByteArrayInputStream(bytes));
305 assertTrue("readFrom byte stream must contain the expected bytes",
306 isArray(byteString.toByteArray(), bytes));
309 // A stream that fails when read.
310 private static final class FailStream extends InputStream {
311 @Override public int read() throws IOException {
312 throw new IOException("synthetic failure");
316 // A stream that simulates blocking by only producing 250 characters
317 // per call to read(byte[]).
318 private static class ReluctantStream extends InputStream {
319 protected final byte[] data;
320 protected int pos = 0;
322 public ReluctantStream(byte[] data) {
326 @Override public int read() {
327 if (pos == data.length) {
334 @Override public int read(byte[] buf) {
335 return read(buf, 0, buf.length);
338 @Override public int read(byte[] buf, int offset, int size) {
339 if (pos == data.length) {
342 int count = Math.min(Math.min(size, data.length - pos), 250);
343 System.arraycopy(data, pos, buf, offset, count);
349 // Same as above, but also implements available().
350 private static final class AvailableStream extends ReluctantStream {
351 public AvailableStream(byte[] data) {
355 @Override public int available() {
356 return Math.min(250, data.length - pos);
360 // A stream which exposes the byte array passed into read(byte[], int, int).
361 private static class EvilInputStream extends InputStream {
362 public byte[] capturedArray = null;
365 public int read(byte[] buf, int off, int len) {
366 if (capturedArray != null) {
370 for (int x = 0; x < len; ++x) {
379 // Purposefully do nothing.
384 // A stream which exposes the byte array passed into write(byte[], int, int).
385 private static class EvilOutputStream extends OutputStream {
386 public byte[] capturedArray = null;
389 public void write(byte[] buf, int off, int len) {
390 if (capturedArray == null) {
396 public void write(int ignored) {
397 // Purposefully do nothing.
401 public void testToStringUtf8() throws UnsupportedEncodingException {
402 String testString = "I love unicode \u1234\u5678 characters";
403 byte[] testBytes = testString.getBytes("UTF-8");
404 ByteString byteString = ByteString.copyFrom(testBytes);
405 assertEquals("copyToStringUtf8 must respect the charset",
406 testString, byteString.toStringUtf8());
409 public void testNewOutput_InitialCapacity() throws IOException {
410 byte[] bytes = getTestBytes();
411 ByteString.Output output = ByteString.newOutput(bytes.length + 100);
413 ByteString byteString = output.toByteString();
415 "String built from newOutput(int) must contain the expected bytes",
416 isArrayRange(bytes, byteString.toByteArray(), 0, bytes.length));
419 // Test newOutput() using a variety of buffer sizes and a variety of (fixed)
421 public void testNewOutput_ArrayWrite() throws IOException {
422 byte[] bytes = getTestBytes();
423 int length = bytes.length;
424 int[] bufferSizes = {128, 256, length / 2, length - 1, length, length + 1,
425 2 * length, 3 * length};
426 int[] writeSizes = {1, 4, 5, 7, 23, bytes.length};
428 for (int bufferSize : bufferSizes) {
429 for (int writeSize : writeSizes) {
430 // Test writing the entire output writeSize bytes at a time.
431 ByteString.Output output = ByteString.newOutput(bufferSize);
432 for (int i = 0; i < length; i += writeSize) {
433 output.write(bytes, i, Math.min(writeSize, length - i));
435 ByteString byteString = output.toByteString();
436 assertTrue("String built from newOutput() must contain the expected bytes",
437 isArrayRange(bytes, byteString.toByteArray(), 0, bytes.length));
442 // Test newOutput() using a variety of buffer sizes, but writing all the
443 // characters using write(byte);
444 public void testNewOutput_WriteChar() throws IOException {
445 byte[] bytes = getTestBytes();
446 int length = bytes.length;
447 int[] bufferSizes = {0, 1, 128, 256, length / 2,
448 length - 1, length, length + 1,
449 2 * length, 3 * length};
450 for (int bufferSize : bufferSizes) {
451 ByteString.Output output = ByteString.newOutput(bufferSize);
452 for (byte byteValue : bytes) {
453 output.write(byteValue);
455 ByteString byteString = output.toByteString();
456 assertTrue("String built from newOutput() must contain the expected bytes",
457 isArrayRange(bytes, byteString.toByteArray(), 0, bytes.length));
461 // Test newOutput() in which we write the bytes using a variety of methods
462 // and sizes, and in which we repeatedly call toByteString() in the middle.
463 public void testNewOutput_Mixed() throws IOException {
464 Random rng = new Random(1);
465 byte[] bytes = getTestBytes();
466 int length = bytes.length;
467 int[] bufferSizes = {0, 1, 128, 256, length / 2,
468 length - 1, length, length + 1,
469 2 * length, 3 * length};
471 for (int bufferSize : bufferSizes) {
472 // Test writing the entire output using a mixture of write sizes and
474 ByteString.Output output = ByteString.newOutput(bufferSize);
476 while (position < bytes.length) {
477 if (rng.nextBoolean()) {
478 int count = 1 + rng.nextInt(bytes.length - position);
479 output.write(bytes, position, count);
482 output.write(bytes[position]);
485 assertEquals("size() returns the right value", position, output.size());
486 assertTrue("newOutput() substring must have correct bytes",
487 isArrayRange(output.toByteString().toByteArray(),
488 bytes, 0, position));
490 ByteString byteString = output.toByteString();
491 assertTrue("String built from newOutput() must contain the expected bytes",
492 isArrayRange(bytes, byteString.toByteArray(), 0, bytes.length));
496 public void testNewOutputEmpty() throws IOException {
497 // Make sure newOutput() correctly builds empty byte strings
498 ByteString byteString = ByteString.newOutput().toByteString();
499 assertEquals(ByteString.EMPTY, byteString);
502 public void testNewOutput_Mutating() throws IOException {
503 Output os = ByteString.newOutput(5);
504 os.write(new byte[] {1, 2, 3, 4, 5});
505 EvilOutputStream eos = new EvilOutputStream();
507 byte[] capturedArray = eos.capturedArray;
508 ByteString byteString = os.toByteString();
509 byte[] oldValue = byteString.toByteArray();
510 Arrays.fill(capturedArray, (byte) 0);
511 byte[] newValue = byteString.toByteArray();
512 assertTrue("Output must not provide access to the underlying byte array",
513 Arrays.equals(oldValue, newValue));
516 public void testNewCodedBuilder() throws IOException {
517 byte[] bytes = getTestBytes();
518 ByteString.CodedBuilder builder = ByteString.newCodedBuilder(bytes.length);
519 builder.getCodedOutput().writeRawBytes(bytes);
520 ByteString byteString = builder.build();
521 assertTrue("String built from newCodedBuilder() must contain the expected bytes",
522 isArrayRange(bytes, byteString.toByteArray(), 0, bytes.length));
525 public void testSubstringParity() {
526 byte[] bigBytes = getTestBytes(2048 * 1024, 113344L);
527 int start = 512 * 1024 - 3333;
528 int end = 512 * 1024 + 7777;
529 ByteString concreteSubstring = ByteString.copyFrom(bigBytes).substring(start, end);
531 for (int i = start; ok && i < end; ++i) {
532 ok = (bigBytes[i] == concreteSubstring.byteAt(i - start));
534 assertTrue("Concrete substring didn't capture the right bytes", ok);
536 ByteString literalString = ByteString.copyFrom(bigBytes, start, end - start);
537 assertTrue("Substring must be equal to literal string",
538 concreteSubstring.equals(literalString));
539 assertEquals("Substring must have same hashcode as literal string",
540 literalString.hashCode(), concreteSubstring.hashCode());
543 public void testCompositeSubstring() {
544 byte[] referenceBytes = getTestBytes(77748, 113344L);
546 List<ByteString> pieces = makeConcretePieces(referenceBytes);
547 ByteString listString = ByteString.copyFrom(pieces);
551 ByteString compositeSubstring = listString.substring(from, to);
552 byte[] substringBytes = compositeSubstring.toByteArray();
553 boolean stillEqual = true;
554 for (int i = 0; stillEqual && i < to - from; ++i) {
555 stillEqual = referenceBytes[from + i] == substringBytes[i];
557 assertTrue("Substring must return correct bytes", stillEqual);
560 for (int i = 0; stillEqual && i < to - from; ++i) {
561 stillEqual = referenceBytes[from + i] == compositeSubstring.byteAt(i);
563 assertTrue("Substring must support byteAt() correctly", stillEqual);
565 ByteString literalSubstring = ByteString.copyFrom(referenceBytes, from, to - from);
566 assertTrue("Composite substring must equal a literal substring over the same bytes",
567 compositeSubstring.equals(literalSubstring));
568 assertTrue("Literal substring must equal a composite substring over the same bytes",
569 literalSubstring.equals(compositeSubstring));
571 assertEquals("We must get the same hashcodes for composite and literal substrings",
572 literalSubstring.hashCode(), compositeSubstring.hashCode());
574 assertFalse("We can't be equal to a proper substring",
575 compositeSubstring.equals(literalSubstring.substring(0, literalSubstring.size() - 1)));
578 public void testCopyFromList() {
579 byte[] referenceBytes = getTestBytes(77748, 113344L);
580 ByteString literalString = ByteString.copyFrom(referenceBytes);
582 List<ByteString> pieces = makeConcretePieces(referenceBytes);
583 ByteString listString = ByteString.copyFrom(pieces);
585 assertTrue("Composite string must be equal to literal string",
586 listString.equals(literalString));
587 assertEquals("Composite string must have same hashcode as literal string",
588 literalString.hashCode(), listString.hashCode());
591 public void testConcat() {
592 byte[] referenceBytes = getTestBytes(77748, 113344L);
593 ByteString literalString = ByteString.copyFrom(referenceBytes);
595 List<ByteString> pieces = makeConcretePieces(referenceBytes);
597 Iterator<ByteString> iter = pieces.iterator();
598 ByteString concatenatedString = iter.next();
599 while (iter.hasNext()) {
600 concatenatedString = concatenatedString.concat(iter.next());
603 assertTrue("Concatenated string must be equal to literal string",
604 concatenatedString.equals(literalString));
605 assertEquals("Concatenated string must have same hashcode as literal string",
606 literalString.hashCode(), concatenatedString.hashCode());
610 * Test the Rope implementation can deal with Empty nodes, even though we
611 * guard against them. See also {@link LiteralByteStringTest#testConcat_empty()}.
613 public void testConcat_empty() {
614 byte[] referenceBytes = getTestBytes(7748, 113344L);
615 ByteString literalString = ByteString.copyFrom(referenceBytes);
617 ByteString duo = RopeByteString.newInstanceForTest(literalString, literalString);
618 ByteString temp = RopeByteString.newInstanceForTest(
619 RopeByteString.newInstanceForTest(literalString, ByteString.EMPTY),
620 RopeByteString.newInstanceForTest(ByteString.EMPTY, literalString));
621 ByteString quintet = RopeByteString.newInstanceForTest(temp, ByteString.EMPTY);
623 assertTrue("String with concatenated nulls must equal simple concatenate",
624 duo.equals(quintet));
625 assertEquals("String with concatenated nulls have same hashcode as simple concatenate",
626 duo.hashCode(), quintet.hashCode());
628 ByteString.ByteIterator duoIter = duo.iterator();
629 ByteString.ByteIterator quintetIter = quintet.iterator();
630 boolean stillEqual = true;
631 while (stillEqual && quintetIter.hasNext()) {
632 stillEqual = (duoIter.nextByte() == quintetIter.nextByte());
634 assertTrue("We must get the same characters by iterating", stillEqual);
635 assertFalse("Iterator must be exhausted", duoIter.hasNext());
638 fail("Should have thrown an exception.");
639 } catch (NoSuchElementException e) {
643 quintetIter.nextByte();
644 fail("Should have thrown an exception.");
645 } catch (NoSuchElementException e) {
649 // Test that even if we force empty strings in as rope leaves in this
650 // configuration, we always get a (possibly Bounded) LiteralByteString
651 // for a length 1 substring.
653 // It is possible, using the testing factory method to create deeply nested
654 // trees of empty leaves, to make a string that will fail this test.
655 for (int i = 1; i < duo.size(); ++i) {
656 assertTrue("Substrings of size() < 2 must not be RopeByteStrings",
657 duo.substring(i - 1, i) instanceof LiteralByteString);
659 for (int i = 1; i < quintet.size(); ++i) {
660 assertTrue("Substrings of size() < 2 must not be RopeByteStrings",
661 quintet.substring(i - 1, i) instanceof LiteralByteString);
665 public void testStartsWith() {
666 byte[] bytes = getTestBytes(1000, 1234L);
667 ByteString string = ByteString.copyFrom(bytes);
668 ByteString prefix = ByteString.copyFrom(bytes, 0, 500);
669 ByteString suffix = ByteString.copyFrom(bytes, 400, 600);
670 assertTrue(string.startsWith(ByteString.EMPTY));
671 assertTrue(string.startsWith(string));
672 assertTrue(string.startsWith(prefix));
673 assertFalse(string.startsWith(suffix));
674 assertFalse(prefix.startsWith(suffix));
675 assertFalse(suffix.startsWith(prefix));
676 assertFalse(ByteString.EMPTY.startsWith(prefix));
677 assertTrue(ByteString.EMPTY.startsWith(ByteString.EMPTY));
680 public void testEndsWith() {
681 byte[] bytes = getTestBytes(1000, 1234L);
682 ByteString string = ByteString.copyFrom(bytes);
683 ByteString prefix = ByteString.copyFrom(bytes, 0, 500);
684 ByteString suffix = ByteString.copyFrom(bytes, 400, 600);
685 assertTrue(string.endsWith(ByteString.EMPTY));
686 assertTrue(string.endsWith(string));
687 assertTrue(string.endsWith(suffix));
688 assertFalse(string.endsWith(prefix));
689 assertFalse(suffix.endsWith(prefix));
690 assertFalse(prefix.endsWith(suffix));
691 assertFalse(ByteString.EMPTY.endsWith(suffix));
692 assertTrue(ByteString.EMPTY.endsWith(ByteString.EMPTY));
695 static List<ByteString> makeConcretePieces(byte[] referenceBytes) {
696 List<ByteString> pieces = new ArrayList<ByteString>();
697 // Starting length should be small enough that we'll do some concatenating by
698 // copying if we just concatenate all these pieces together.
699 for (int start = 0, length = 16; start < referenceBytes.length; start += length) {
700 length = (length << 1) - 1;
701 if (start + length > referenceBytes.length) {
702 length = referenceBytes.length - start;
704 pieces.add(ByteString.copyFrom(referenceBytes, start, length));
709 private byte[] substringUsingWriteTo(
710 ByteString data, int offset, int length) throws IOException {
711 ByteArrayOutputStream output = new ByteArrayOutputStream();
712 data.writeTo(output, offset, length);
713 return output.toByteArray();
716 public void testWriteToOutputStream() throws Exception {
717 // Choose a size large enough so when two ByteStrings are concatenated they
718 // won't be merged into one byte array due to some optimizations.
719 final int dataSize = ByteString.CONCATENATE_BY_COPY_SIZE + 1;
720 byte[] data1 = new byte[dataSize];
721 for (int i = 0; i < data1.length; i++) {
724 data1[1] = (byte) 11;
725 // Test LiteralByteString.writeTo(OutputStream,int,int)
726 LiteralByteString left = new LiteralByteString(data1);
727 byte[] result = substringUsingWriteTo(left, 1, 1);
728 assertEquals(1, result.length);
729 assertEquals((byte) 11, result[0]);
731 byte[] data2 = new byte[dataSize];
732 for (int i = 0; i < data1.length; i++) {
735 LiteralByteString right = new LiteralByteString(data2);
736 // Concatenate two ByteStrings to create a RopeByteString.
737 ByteString root = left.concat(right);
738 // Make sure we are actually testing a RopeByteString with a simple tree
740 assertEquals(1, root.getTreeDepth());
741 // Write parts of the left node.
742 result = substringUsingWriteTo(root, 0, dataSize);
743 assertEquals(dataSize, result.length);
744 assertEquals((byte) 1, result[0]);
745 assertEquals((byte) 1, result[dataSize - 1]);
746 // Write parts of the right node.
747 result = substringUsingWriteTo(root, dataSize, dataSize);
748 assertEquals(dataSize, result.length);
749 assertEquals((byte) 2, result[0]);
750 assertEquals((byte) 2, result[dataSize - 1]);
751 // Write a segment of bytes that runs across both nodes.
752 result = substringUsingWriteTo(root, dataSize / 2, dataSize);
753 assertEquals(dataSize, result.length);
754 assertEquals((byte) 1, result[0]);
755 assertEquals((byte) 1, result[dataSize - dataSize / 2 - 1]);
756 assertEquals((byte) 2, result[dataSize - dataSize / 2]);
757 assertEquals((byte) 2, result[dataSize - 1]);