• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 //     * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 //     * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 package com.google.protobuf;
32 
33 import static com.google.protobuf.Internal.UTF_8;
34 
35 import junit.framework.TestCase;
36 
37 import java.io.ByteArrayInputStream;
38 import java.io.ByteArrayOutputStream;
39 import java.io.EOFException;
40 import java.io.IOException;
41 import java.io.InputStream;
42 import java.io.ObjectInputStream;
43 import java.io.ObjectOutputStream;
44 import java.io.OutputStream;
45 import java.io.UnsupportedEncodingException;
46 import java.nio.BufferOverflowException;
47 import java.nio.ByteBuffer;
48 import java.util.Arrays;
49 import java.util.List;
50 import java.util.NoSuchElementException;
51 
52 /**
53  * Tests for {@link NioByteString}.
54  */
55 public class NioByteStringTest extends TestCase {
56   private static final ByteString EMPTY = new NioByteString(ByteBuffer.wrap(new byte[0]));
57   private static final String CLASSNAME = NioByteString.class.getSimpleName();
58   private static final byte[] BYTES = ByteStringTest.getTestBytes(1234, 11337766L);
59   private static final int EXPECTED_HASH = ByteString.wrap(BYTES).hashCode();
60 
61   private final ByteBuffer backingBuffer = ByteBuffer.wrap(BYTES.clone());
62   private final ByteString testString = new NioByteString(backingBuffer);
63 
testExpectedType()64   public void testExpectedType() {
65     String actualClassName = getActualClassName(testString);
66     assertEquals(CLASSNAME + " should match type exactly", CLASSNAME, actualClassName);
67   }
68 
getActualClassName(Object object)69   protected String getActualClassName(Object object) {
70     String actualClassName = object.getClass().getName();
71     actualClassName = actualClassName.substring(actualClassName.lastIndexOf('.') + 1);
72     return actualClassName;
73   }
74 
testByteAt()75   public void testByteAt() {
76     boolean stillEqual = true;
77     for (int i = 0; stillEqual && i < BYTES.length; ++i) {
78       stillEqual = (BYTES[i] == testString.byteAt(i));
79     }
80     assertTrue(CLASSNAME + " must capture the right bytes", stillEqual);
81   }
82 
testByteIterator()83   public void testByteIterator() {
84     boolean stillEqual = true;
85     ByteString.ByteIterator iter = testString.iterator();
86     for (int i = 0; stillEqual && i < BYTES.length; ++i) {
87       stillEqual = (iter.hasNext() && BYTES[i] == iter.nextByte());
88     }
89     assertTrue(CLASSNAME + " must capture the right bytes", stillEqual);
90     assertFalse(CLASSNAME + " must have exhausted the itertor", iter.hasNext());
91 
92     try {
93       iter.nextByte();
94       fail("Should have thrown an exception.");
95     } catch (NoSuchElementException e) {
96       // This is success
97     }
98   }
99 
testByteIterable()100   public void testByteIterable() {
101     boolean stillEqual = true;
102     int j = 0;
103     for (byte quantum : testString) {
104       stillEqual = (BYTES[j] == quantum);
105       ++j;
106     }
107     assertTrue(CLASSNAME + " must capture the right bytes as Bytes", stillEqual);
108     assertEquals(CLASSNAME + " iterable character count", BYTES.length, j);
109   }
110 
testSize()111   public void testSize() {
112     assertEquals(CLASSNAME + " must have the expected size", BYTES.length,
113         testString.size());
114   }
115 
testGetTreeDepth()116   public void testGetTreeDepth() {
117     assertEquals(CLASSNAME + " must have depth 0", 0, testString.getTreeDepth());
118   }
119 
testIsBalanced()120   public void testIsBalanced() {
121     assertTrue(CLASSNAME + " is technically balanced", testString.isBalanced());
122   }
123 
testCopyTo_ByteArrayOffsetLength()124   public void testCopyTo_ByteArrayOffsetLength() {
125     int destinationOffset = 50;
126     int length = 100;
127     byte[] destination = new byte[destinationOffset + length];
128     int sourceOffset = 213;
129     testString.copyTo(destination, sourceOffset, destinationOffset, length);
130     boolean stillEqual = true;
131     for (int i = 0; stillEqual && i < length; ++i) {
132       stillEqual = BYTES[i + sourceOffset] == destination[i + destinationOffset];
133     }
134     assertTrue(CLASSNAME + ".copyTo(4 arg) must give the expected bytes", stillEqual);
135   }
136 
testCopyTo_ByteArrayOffsetLengthErrors()137   public void testCopyTo_ByteArrayOffsetLengthErrors() {
138     int destinationOffset = 50;
139     int length = 100;
140     byte[] destination = new byte[destinationOffset + length];
141 
142     try {
143       // Copy one too many bytes
144       testString.copyTo(destination, testString.size() + 1 - length,
145           destinationOffset, length);
146       fail("Should have thrown an exception when copying too many bytes of a "
147           + CLASSNAME);
148     } catch (IndexOutOfBoundsException expected) {
149       // This is success
150     }
151 
152     try {
153       // Copy with illegal negative sourceOffset
154       testString.copyTo(destination, -1, destinationOffset, length);
155       fail("Should have thrown an exception when given a negative sourceOffset in "
156           + CLASSNAME);
157     } catch (IndexOutOfBoundsException expected) {
158       // This is success
159     }
160 
161     try {
162       // Copy with illegal negative destinationOffset
163       testString.copyTo(destination, 0, -1, length);
164       fail("Should have thrown an exception when given a negative destinationOffset in "
165           + CLASSNAME);
166     } catch (IndexOutOfBoundsException expected) {
167       // This is success
168     }
169 
170     try {
171       // Copy with illegal negative size
172       testString.copyTo(destination, 0, 0, -1);
173       fail("Should have thrown an exception when given a negative size in "
174           + CLASSNAME);
175     } catch (IndexOutOfBoundsException expected) {
176       // This is success
177     }
178 
179     try {
180       // Copy with illegal too-large sourceOffset
181       testString.copyTo(destination, 2 * testString.size(), 0, length);
182       fail("Should have thrown an exception when the destinationOffset is too large in "
183           + CLASSNAME);
184     } catch (IndexOutOfBoundsException expected) {
185       // This is success
186     }
187 
188     try {
189       // Copy with illegal too-large destinationOffset
190       testString.copyTo(destination, 0, 2 * destination.length, length);
191       fail("Should have thrown an exception when the destinationOffset is too large in "
192           + CLASSNAME);
193     } catch (IndexOutOfBoundsException expected) {
194       // This is success
195     }
196   }
197 
testCopyTo_ByteBuffer()198   public void testCopyTo_ByteBuffer() {
199     // Same length.
200     ByteBuffer myBuffer = ByteBuffer.allocate(BYTES.length);
201     testString.copyTo(myBuffer);
202     myBuffer.flip();
203     assertEquals(CLASSNAME + ".copyTo(ByteBuffer) must give back the same bytes",
204         backingBuffer, myBuffer);
205 
206     // Target buffer bigger than required.
207     myBuffer = ByteBuffer.allocate(testString.size() + 1);
208     testString.copyTo(myBuffer);
209     myBuffer.flip();
210     assertEquals(backingBuffer, myBuffer);
211 
212     // Target buffer has no space.
213     myBuffer = ByteBuffer.allocate(0);
214     try {
215       testString.copyTo(myBuffer);
216       fail("Should have thrown an exception when target ByteBuffer has insufficient capacity");
217     } catch (BufferOverflowException e) {
218       // Expected.
219     }
220 
221     // Target buffer too small.
222     myBuffer = ByteBuffer.allocate(1);
223     try {
224       testString.copyTo(myBuffer);
225       fail("Should have thrown an exception when target ByteBuffer has insufficient capacity");
226     } catch (BufferOverflowException e) {
227       // Expected.
228     }
229   }
230 
testMarkSupported()231   public void testMarkSupported() {
232     InputStream stream = testString.newInput();
233     assertTrue(CLASSNAME + ".newInput() must support marking", stream.markSupported());
234   }
235 
testMarkAndReset()236   public void testMarkAndReset() throws IOException {
237     int fraction = testString.size() / 3;
238 
239     InputStream stream = testString.newInput();
240     stream.mark(testString.size()); // First, mark() the end.
241 
242     skipFully(stream, fraction); // Skip a large fraction, but not all.
243     assertEquals(
244         CLASSNAME + ": after skipping to the 'middle', half the bytes are available",
245         (testString.size() - fraction), stream.available());
246     stream.reset();
247     assertEquals(
248         CLASSNAME + ": after resetting, all bytes are available",
249         testString.size(), stream.available());
250 
251     skipFully(stream, testString.size()); // Skip to the end.
252     assertEquals(
253         CLASSNAME + ": after skipping to the end, no more bytes are available",
254         0, stream.available());
255   }
256 
257   /**
258    * Discards {@code n} bytes of data from the input stream. This method
259    * will block until the full amount has been skipped. Does not close the
260    * stream.
261    * <p>Copied from com.google.common.io.ByteStreams to avoid adding dependency.
262    *
263    * @param in the input stream to read from
264    * @param n the number of bytes to skip
265    * @throws EOFException if this stream reaches the end before skipping all
266    *     the bytes
267    * @throws IOException if an I/O error occurs, or the stream does not
268    *     support skipping
269    */
skipFully(InputStream in, long n)270   static void skipFully(InputStream in, long n) throws IOException {
271     long toSkip = n;
272     while (n > 0) {
273       long amt = in.skip(n);
274       if (amt == 0) {
275         // Force a blocking read to avoid infinite loop
276         if (in.read() == -1) {
277           long skipped = toSkip - n;
278           throw new EOFException("reached end of stream after skipping "
279               + skipped + " bytes; " + toSkip + " bytes expected");
280         }
281         n--;
282       } else {
283         n -= amt;
284       }
285     }
286   }
287 
testAsReadOnlyByteBuffer()288   public void testAsReadOnlyByteBuffer() {
289     ByteBuffer byteBuffer = testString.asReadOnlyByteBuffer();
290     byte[] roundTripBytes = new byte[BYTES.length];
291     assertTrue(byteBuffer.remaining() == BYTES.length);
292     assertTrue(byteBuffer.isReadOnly());
293     byteBuffer.get(roundTripBytes);
294     assertTrue(CLASSNAME + ".asReadOnlyByteBuffer() must give back the same bytes",
295         Arrays.equals(BYTES, roundTripBytes));
296   }
297 
testAsReadOnlyByteBufferList()298   public void testAsReadOnlyByteBufferList() {
299     List<ByteBuffer> byteBuffers = testString.asReadOnlyByteBufferList();
300     int bytesSeen = 0;
301     byte[] roundTripBytes = new byte[BYTES.length];
302     for (ByteBuffer byteBuffer : byteBuffers) {
303       int thisLength = byteBuffer.remaining();
304       assertTrue(byteBuffer.isReadOnly());
305       assertTrue(bytesSeen + thisLength <= BYTES.length);
306       byteBuffer.get(roundTripBytes, bytesSeen, thisLength);
307       bytesSeen += thisLength;
308     }
309     assertTrue(bytesSeen == BYTES.length);
310     assertTrue(CLASSNAME + ".asReadOnlyByteBufferTest() must give back the same bytes",
311         Arrays.equals(BYTES, roundTripBytes));
312   }
313 
testToByteArray()314   public void testToByteArray() {
315     byte[] roundTripBytes = testString.toByteArray();
316     assertTrue(CLASSNAME + ".toByteArray() must give back the same bytes",
317         Arrays.equals(BYTES, roundTripBytes));
318   }
319 
testWriteTo()320   public void testWriteTo() throws IOException {
321     ByteArrayOutputStream bos = new ByteArrayOutputStream();
322     testString.writeTo(bos);
323     byte[] roundTripBytes = bos.toByteArray();
324     assertTrue(CLASSNAME + ".writeTo() must give back the same bytes",
325         Arrays.equals(BYTES, roundTripBytes));
326   }
327 
testWriteToShouldNotExposeInternalBufferToOutputStream()328   public void testWriteToShouldNotExposeInternalBufferToOutputStream() throws IOException {
329     OutputStream os = new OutputStream() {
330       @Override
331       public void write(byte[] b, int off, int len) {
332         Arrays.fill(b, off, off + len, (byte) 0);
333       }
334 
335       @Override
336       public void write(int b) {
337         throw new UnsupportedOperationException();
338       }
339     };
340 
341     byte[] original = Arrays.copyOf(BYTES, BYTES.length);
342     testString.writeTo(os);
343     assertTrue(CLASSNAME + ".writeTo() must NOT grant access to underlying buffer",
344         Arrays.equals(original, BYTES));
345   }
346 
testWriteToInternalShouldExposeInternalBufferToOutputStream()347   public void testWriteToInternalShouldExposeInternalBufferToOutputStream() throws IOException {
348     OutputStream os = new OutputStream() {
349       @Override
350       public void write(byte[] b, int off, int len) {
351         Arrays.fill(b, off, off + len, (byte) 0);
352       }
353 
354       @Override
355       public void write(int b) {
356         throw new UnsupportedOperationException();
357       }
358     };
359 
360     testString.writeToInternal(os, 0, testString.size());
361     byte[] allZeros = new byte[testString.size()];
362     assertTrue(CLASSNAME + ".writeToInternal() must grant access to underlying buffer",
363         Arrays.equals(allZeros, backingBuffer.array()));
364   }
365 
testWriteToShouldExposeInternalBufferToByteOutput()366   public void testWriteToShouldExposeInternalBufferToByteOutput() throws IOException {
367     ByteOutput out = new ByteOutput() {
368       @Override
369       public void write(byte value) throws IOException {
370         throw new UnsupportedOperationException();
371       }
372 
373       @Override
374       public void write(byte[] value, int offset, int length) throws IOException {
375         throw new UnsupportedOperationException();
376       }
377 
378       @Override
379       public void writeLazy(byte[] value, int offset, int length) throws IOException {
380         throw new UnsupportedOperationException();
381       }
382 
383       @Override
384       public void write(ByteBuffer value) throws IOException {
385         throw new UnsupportedOperationException();
386       }
387 
388       @Override
389       public void writeLazy(ByteBuffer value) throws IOException {
390         Arrays.fill(value.array(), value.arrayOffset(), value.arrayOffset() + value.limit(),
391             (byte) 0);
392       }
393     };
394 
395     testString.writeTo(out);
396     byte[] allZeros = new byte[testString.size()];
397     assertTrue(CLASSNAME + ".writeTo() must grant access to underlying buffer",
398         Arrays.equals(allZeros, backingBuffer.array()));
399   }
400 
testNewOutput()401   public void testNewOutput() throws IOException {
402     ByteArrayOutputStream bos = new ByteArrayOutputStream();
403     ByteString.Output output = ByteString.newOutput();
404     testString.writeTo(output);
405     assertEquals("Output Size returns correct result",
406         output.size(), testString.size());
407     output.writeTo(bos);
408     assertTrue("Output.writeTo() must give back the same bytes",
409         Arrays.equals(BYTES, bos.toByteArray()));
410 
411     // write the output stream to itself! This should cause it to double
412     output.writeTo(output);
413     assertEquals("Writing an output stream to itself is successful",
414         testString.concat(testString), output.toByteString());
415 
416     output.reset();
417     assertEquals("Output.reset() resets the output", 0, output.size());
418     assertEquals("Output.reset() resets the output",
419         EMPTY, output.toByteString());
420   }
421 
testToString()422   public void testToString() {
423     String testString = "I love unicode \u1234\u5678 characters";
424     ByteString unicode = forString(testString);
425     String roundTripString = unicode.toString(UTF_8);
426     assertEquals(CLASSNAME + " unicode must match", testString, roundTripString);
427   }
428 
testCharsetToString()429   public void testCharsetToString() {
430     String testString = "I love unicode \u1234\u5678 characters";
431     ByteString unicode = forString(testString);
432     String roundTripString = unicode.toString(UTF_8);
433     assertEquals(CLASSNAME + " unicode must match", testString, roundTripString);
434   }
435 
testToString_returnsCanonicalEmptyString()436   public void testToString_returnsCanonicalEmptyString() {
437     assertSame(CLASSNAME + " must be the same string references",
438         EMPTY.toString(UTF_8),
439         new NioByteString(ByteBuffer.wrap(new byte[0])).toString(UTF_8));
440   }
441 
testToString_raisesException()442   public void testToString_raisesException() {
443     try {
444       EMPTY.toString("invalid");
445       fail("Should have thrown an exception.");
446     } catch (UnsupportedEncodingException expected) {
447       // This is success
448     }
449 
450     try {
451       testString.toString("invalid");
452       fail("Should have thrown an exception.");
453     } catch (UnsupportedEncodingException expected) {
454       // This is success
455     }
456   }
457 
testEquals()458   public void testEquals() {
459     assertEquals(CLASSNAME + " must not equal null", false, testString.equals(null));
460     assertEquals(CLASSNAME + " must equal self", testString, testString);
461     assertFalse(CLASSNAME + " must not equal the empty string",
462         testString.equals(EMPTY));
463     assertEquals(CLASSNAME + " empty strings must be equal",
464         EMPTY, testString.substring(55, 55));
465     assertEquals(CLASSNAME + " must equal another string with the same value",
466         testString, new NioByteString(backingBuffer));
467 
468     byte[] mungedBytes = mungedBytes();
469     assertFalse(CLASSNAME + " must not equal every string with the same length",
470         testString.equals(new NioByteString(ByteBuffer.wrap(mungedBytes))));
471   }
472 
testEqualsLiteralByteString()473   public void testEqualsLiteralByteString() {
474     ByteString literal = ByteString.copyFrom(BYTES);
475     assertEquals(CLASSNAME + " must equal LiteralByteString with same value", literal,
476         testString);
477     assertEquals(CLASSNAME + " must equal LiteralByteString with same value", testString,
478         literal);
479     assertFalse(CLASSNAME + " must not equal the empty string",
480         testString.equals(ByteString.EMPTY));
481     assertEquals(CLASSNAME + " empty strings must be equal",
482         ByteString.EMPTY, testString.substring(55, 55));
483 
484     literal = ByteString.copyFrom(mungedBytes());
485     assertFalse(CLASSNAME + " must not equal every LiteralByteString with the same length",
486         testString.equals(literal));
487     assertFalse(CLASSNAME + " must not equal every LiteralByteString with the same length",
488         literal.equals(testString));
489   }
490 
testEqualsRopeByteString()491   public void testEqualsRopeByteString() {
492     ByteString p1 = ByteString.copyFrom(BYTES, 0, 5);
493     ByteString p2 = ByteString.copyFrom(BYTES, 5, BYTES.length - 5);
494     ByteString rope = p1.concat(p2);
495 
496     assertEquals(CLASSNAME + " must equal RopeByteString with same value", rope,
497         testString);
498     assertEquals(CLASSNAME + " must equal RopeByteString with same value", testString,
499         rope);
500     assertFalse(CLASSNAME + " must not equal the empty string",
501         testString.equals(ByteString.EMPTY.concat(ByteString.EMPTY)));
502     assertEquals(CLASSNAME + " empty strings must be equal",
503         ByteString.EMPTY.concat(ByteString.EMPTY), testString.substring(55, 55));
504 
505     byte[] mungedBytes = mungedBytes();
506     p1 = ByteString.copyFrom(mungedBytes, 0, 5);
507     p2 = ByteString.copyFrom(mungedBytes, 5, mungedBytes.length - 5);
508     rope = p1.concat(p2);
509     assertFalse(CLASSNAME + " must not equal every RopeByteString with the same length",
510         testString.equals(rope));
511     assertFalse(CLASSNAME + " must not equal every RopeByteString with the same length",
512         rope.equals(testString));
513   }
514 
mungedBytes()515   private byte[] mungedBytes() {
516     byte[] mungedBytes = new byte[BYTES.length];
517     System.arraycopy(BYTES, 0, mungedBytes, 0, BYTES.length);
518     mungedBytes[mungedBytes.length - 5] = (byte) (mungedBytes[mungedBytes.length - 5] ^ 0xFF);
519     return mungedBytes;
520   }
521 
testHashCode()522   public void testHashCode() {
523     int hash = testString.hashCode();
524     assertEquals(CLASSNAME + " must have expected hashCode", EXPECTED_HASH, hash);
525   }
526 
testPeekCachedHashCode()527   public void testPeekCachedHashCode() {
528     ByteString newString = new NioByteString(backingBuffer);
529     assertEquals(CLASSNAME + ".peekCachedHashCode() should return zero at first", 0,
530         newString.peekCachedHashCode());
531     newString.hashCode();
532     assertEquals(CLASSNAME + ".peekCachedHashCode should return zero at first",
533         EXPECTED_HASH, newString.peekCachedHashCode());
534   }
535 
testPartialHash()536   public void testPartialHash() {
537     // partialHash() is more strenuously tested elsewhere by testing hashes of substrings.
538     // This test would fail if the expected hash were 1.  It's not.
539     int hash = testString.partialHash(testString.size(), 0, testString.size());
540     assertEquals(CLASSNAME + ".partialHash() must yield expected hashCode",
541         EXPECTED_HASH, hash);
542   }
543 
testNewInput()544   public void testNewInput() throws IOException {
545     InputStream input = testString.newInput();
546     assertEquals("InputStream.available() returns correct value",
547         testString.size(), input.available());
548     boolean stillEqual = true;
549     for (byte referenceByte : BYTES) {
550       int expectedInt = (referenceByte & 0xFF);
551       stillEqual = (expectedInt == input.read());
552     }
553     assertEquals("InputStream.available() returns correct value",
554         0, input.available());
555     assertTrue(CLASSNAME + " must give the same bytes from the InputStream", stillEqual);
556     assertEquals(CLASSNAME + " InputStream must now be exhausted", -1, input.read());
557   }
558 
testNewInput_skip()559   public void testNewInput_skip() throws IOException {
560     InputStream input = testString.newInput();
561     int stringSize = testString.size();
562     int nearEndIndex = stringSize * 2 / 3;
563     long skipped1 = input.skip(nearEndIndex);
564     assertEquals("InputStream.skip()", skipped1, nearEndIndex);
565     assertEquals("InputStream.available()",
566         stringSize - skipped1, input.available());
567     assertTrue("InputStream.mark() is available", input.markSupported());
568     input.mark(0);
569     assertEquals("InputStream.skip(), read()",
570         testString.byteAt(nearEndIndex) & 0xFF, input.read());
571     assertEquals("InputStream.available()",
572         stringSize - skipped1 - 1, input.available());
573     long skipped2 = input.skip(stringSize);
574     assertEquals("InputStream.skip() incomplete",
575         skipped2, stringSize - skipped1 - 1);
576     assertEquals("InputStream.skip(), no more input", 0, input.available());
577     assertEquals("InputStream.skip(), no more input", -1, input.read());
578     input.reset();
579     assertEquals("InputStream.reset() succeded",
580         stringSize - skipped1, input.available());
581     assertEquals("InputStream.reset(), read()",
582         testString.byteAt(nearEndIndex) & 0xFF, input.read());
583   }
584 
testNewCodedInput()585   public void testNewCodedInput() throws IOException {
586     CodedInputStream cis = testString.newCodedInput();
587     byte[] roundTripBytes = cis.readRawBytes(BYTES.length);
588     assertTrue(CLASSNAME + " must give the same bytes back from the CodedInputStream",
589         Arrays.equals(BYTES, roundTripBytes));
590     assertTrue(CLASSNAME + " CodedInputStream must now be exhausted", cis.isAtEnd());
591   }
592 
593   /**
594    * Make sure we keep things simple when concatenating with empty. See also
595    * {@link ByteStringTest#testConcat_empty()}.
596    */
testConcat_empty()597   public void testConcat_empty() {
598     assertSame(CLASSNAME + " concatenated with empty must give " + CLASSNAME,
599         testString.concat(EMPTY), testString);
600     assertSame("empty concatenated with " + CLASSNAME + " must give " + CLASSNAME,
601         EMPTY.concat(testString), testString);
602   }
603 
testJavaSerialization()604   public void testJavaSerialization() throws Exception {
605     ByteArrayOutputStream out = new ByteArrayOutputStream();
606     ObjectOutputStream oos = new ObjectOutputStream(out);
607     oos.writeObject(testString);
608     oos.close();
609     byte[] pickled = out.toByteArray();
610     InputStream in = new ByteArrayInputStream(pickled);
611     ObjectInputStream ois = new ObjectInputStream(in);
612     Object o = ois.readObject();
613     assertTrue("Didn't get a ByteString back", o instanceof ByteString);
614     assertEquals("Should get an equal ByteString back", testString, o);
615   }
616 
forString(String str)617   private static ByteString forString(String str) {
618     return new NioByteString(ByteBuffer.wrap(str.getBytes(UTF_8)));
619   }
620 }
621