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