Home | History | Annotate | Download | only in okio
      1 /*
      2  * Copyright (C) 2014 Square, Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package okio;
     17 
     18 import java.io.EOFException;
     19 import java.io.IOException;
     20 import java.io.InputStream;
     21 import java.nio.charset.Charset;
     22 import java.util.Arrays;
     23 import java.util.List;
     24 import org.junit.Before;
     25 import org.junit.Test;
     26 import org.junit.runner.RunWith;
     27 import org.junit.runners.Parameterized;
     28 
     29 import static okio.TestUtil.assertByteArrayEquals;
     30 import static okio.TestUtil.assertByteArraysEquals;
     31 import static okio.TestUtil.repeat;
     32 import static okio.Util.UTF_8;
     33 import static org.junit.Assert.assertEquals;
     34 import static org.junit.Assert.assertFalse;
     35 import static org.junit.Assert.assertTrue;
     36 import static org.junit.Assert.fail;
     37 
     38 @RunWith(Parameterized.class)
     39 public class BufferedSourceTest {
     40   private static final Factory BUFFER_FACTORY = new Factory() {
     41     @Override public Pipe pipe() {
     42       Buffer buffer = new Buffer();
     43       Pipe result = new Pipe();
     44       result.sink = buffer;
     45       result.source = buffer;
     46       return result;
     47     }
     48 
     49     @Override public String toString() {
     50       return "Buffer";
     51     }
     52   };
     53 
     54   private static final Factory REAL_BUFFERED_SOURCE_FACTORY = new Factory() {
     55     @Override public Pipe pipe() {
     56       Buffer buffer = new Buffer();
     57       Pipe result = new Pipe();
     58       result.sink = buffer;
     59       result.source = new RealBufferedSource(buffer);
     60       return result;
     61     }
     62 
     63     @Override public String toString() {
     64       return "RealBufferedSource";
     65     }
     66   };
     67 
     68   private static final Factory ONE_BYTE_AT_A_TIME_FACTORY = new Factory() {
     69     @Override public Pipe pipe() {
     70       Buffer buffer = new Buffer();
     71       Pipe result = new Pipe();
     72       result.sink = buffer;
     73       result.source = new RealBufferedSource(new ForwardingSource(buffer) {
     74         @Override public long read(Buffer sink, long byteCount) throws IOException {
     75           return super.read(sink, Math.min(byteCount, 1L));
     76         }
     77       });
     78       return result;
     79     }
     80 
     81     @Override public String toString() {
     82       return "OneByteAtATime";
     83     }
     84   };
     85 
     86   private interface Factory {
     87     Pipe pipe();
     88   }
     89 
     90   private static class Pipe {
     91     BufferedSink sink;
     92     BufferedSource source;
     93   }
     94 
     95   // ANDROID-BEGIN
     96   //  @Parameterized.Parameters(name = "{0}")
     97   @Parameterized.Parameters
     98   // ANDROID-END
     99   public static List<Object[]> parameters() {
    100     return Arrays.asList(
    101         new Object[] { BUFFER_FACTORY },
    102         new Object[] { REAL_BUFFERED_SOURCE_FACTORY },
    103         new Object[] { ONE_BYTE_AT_A_TIME_FACTORY });
    104   }
    105 
    106   // ANDROID-BEGIN
    107   // @Parameterized.Parameter
    108   private final Factory factory;
    109   public BufferedSourceTest(Factory factory) {
    110     this.factory = factory;
    111   }
    112   // ANDROID-END
    113   private BufferedSink sink;
    114   private BufferedSource source;
    115 
    116   @Before public void setUp() {
    117     Pipe pipe = factory.pipe();
    118     sink = pipe.sink;
    119     source = pipe.source;
    120   }
    121 
    122   @Test public void readBytes() throws Exception {
    123     sink.write(new byte[] { (byte) 0xab, (byte) 0xcd });
    124     assertEquals(0xab, source.readByte() & 0xff);
    125     assertEquals(0xcd, source.readByte() & 0xff);
    126     assertTrue(source.exhausted());
    127   }
    128 
    129   @Test public void readShort() throws Exception {
    130     sink.write(new byte[] {
    131         (byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x01
    132     });
    133     assertEquals((short) 0xabcd, source.readShort());
    134     assertEquals((short) 0xef01, source.readShort());
    135     assertTrue(source.exhausted());
    136   }
    137 
    138   @Test public void readShortLe() throws Exception {
    139     sink.write(new byte[] {
    140         (byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x10
    141     });
    142     assertEquals((short) 0xcdab, source.readShortLe());
    143     assertEquals((short) 0x10ef, source.readShortLe());
    144     assertTrue(source.exhausted());
    145   }
    146 
    147   @Test public void readShortSplitAcrossMultipleSegments() throws Exception {
    148     sink.writeUtf8(repeat('a', Segment.SIZE - 1));
    149     sink.write(new byte[] { (byte) 0xab, (byte) 0xcd });
    150     source.skip(Segment.SIZE - 1);
    151     assertEquals((short) 0xabcd, source.readShort());
    152     assertTrue(source.exhausted());
    153   }
    154 
    155   @Test public void readInt() throws Exception {
    156     sink.write(new byte[] {
    157         (byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x01, (byte) 0x87, (byte) 0x65, (byte) 0x43,
    158         (byte) 0x21
    159     });
    160     assertEquals(0xabcdef01, source.readInt());
    161     assertEquals(0x87654321, source.readInt());
    162     assertTrue(source.exhausted());
    163   }
    164 
    165   @Test public void readIntLe() throws Exception {
    166     sink.write(new byte[] {
    167         (byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x10, (byte) 0x87, (byte) 0x65, (byte) 0x43,
    168         (byte) 0x21
    169     });
    170     assertEquals(0x10efcdab, source.readIntLe());
    171     assertEquals(0x21436587, source.readIntLe());
    172     assertTrue(source.exhausted());
    173   }
    174 
    175   @Test public void readIntSplitAcrossMultipleSegments() throws Exception {
    176     sink.writeUtf8(repeat('a', Segment.SIZE - 3));
    177     sink.write(new byte[] {
    178         (byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x01
    179     });
    180     source.skip(Segment.SIZE - 3);
    181     assertEquals(0xabcdef01, source.readInt());
    182     assertTrue(source.exhausted());
    183   }
    184 
    185   @Test public void readLong() throws Exception {
    186     sink.write(new byte[] {
    187         (byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x10, (byte) 0x87, (byte) 0x65, (byte) 0x43,
    188         (byte) 0x21, (byte) 0x36, (byte) 0x47, (byte) 0x58, (byte) 0x69, (byte) 0x12, (byte) 0x23,
    189         (byte) 0x34, (byte) 0x45
    190     });
    191     assertEquals(0xabcdef1087654321L, source.readLong());
    192     assertEquals(0x3647586912233445L, source.readLong());
    193     assertTrue(source.exhausted());
    194   }
    195 
    196   @Test public void readLongLe() throws Exception {
    197     sink.write(new byte[] {
    198         (byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x10, (byte) 0x87, (byte) 0x65, (byte) 0x43,
    199         (byte) 0x21, (byte) 0x36, (byte) 0x47, (byte) 0x58, (byte) 0x69, (byte) 0x12, (byte) 0x23,
    200         (byte) 0x34, (byte) 0x45
    201     });
    202     assertEquals(0x2143658710efcdabL, source.readLongLe());
    203     assertEquals(0x4534231269584736L, source.readLongLe());
    204     assertTrue(source.exhausted());
    205   }
    206 
    207   @Test public void readLongSplitAcrossMultipleSegments() throws Exception {
    208     sink.writeUtf8(repeat('a', Segment.SIZE - 7));
    209     sink.write(new byte[] {
    210         (byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x01, (byte) 0x87, (byte) 0x65, (byte) 0x43,
    211         (byte) 0x21,
    212     });
    213     source.skip(Segment.SIZE - 7);
    214     assertEquals(0xabcdef0187654321L, source.readLong());
    215     assertTrue(source.exhausted());
    216   }
    217 
    218   @Test public void readAll() throws IOException {
    219     source.buffer().writeUtf8("abc");
    220     sink.writeUtf8("def");
    221 
    222     Buffer sink = new Buffer();
    223     assertEquals(6, source.readAll(sink));
    224     assertEquals("abcdef", sink.readUtf8());
    225     assertTrue(source.exhausted());
    226   }
    227 
    228   @Test public void readAllExhausted() throws IOException {
    229     MockSink mockSink = new MockSink();
    230     assertEquals(0, source.readAll(mockSink));
    231     assertTrue(source.exhausted());
    232     mockSink.assertLog();
    233   }
    234 
    235   @Test public void readExhaustedSource() throws Exception {
    236     Buffer sink = new Buffer();
    237     sink.writeUtf8(repeat('a', 10));
    238     assertEquals(-1, source.read(sink, 10));
    239     assertEquals(10, sink.size());
    240     assertTrue(source.exhausted());
    241   }
    242 
    243   @Test public void readZeroBytesFromSource() throws Exception {
    244     Buffer sink = new Buffer();
    245     sink.writeUtf8(repeat('a', 10));
    246 
    247     // Either 0 or -1 is reasonable here. For consistency with Android's
    248     // ByteArrayInputStream we return 0.
    249     assertEquals(-1, source.read(sink, 0));
    250     assertEquals(10, sink.size());
    251     assertTrue(source.exhausted());
    252   }
    253 
    254   @Test public void readFully() throws Exception {
    255     sink.writeUtf8(repeat('a', 10000));
    256     Buffer sink = new Buffer();
    257     source.readFully(sink, 9999);
    258     assertEquals(repeat('a', 9999), sink.readUtf8());
    259     assertEquals("a", source.readUtf8());
    260   }
    261 
    262   @Test public void readFullyTooShortThrows() throws IOException {
    263     sink.writeUtf8("Hi");
    264     Buffer sink = new Buffer();
    265     try {
    266       source.readFully(sink, 5);
    267       fail();
    268     } catch (EOFException ignored) {
    269     }
    270 
    271     // Verify we read all that we could from the source.
    272     assertEquals("Hi", sink.readUtf8());
    273   }
    274 
    275   @Test public void readFullyByteArray() throws IOException {
    276     Buffer data = new Buffer();
    277     data.writeUtf8("Hello").writeUtf8(repeat('e', Segment.SIZE));
    278 
    279     byte[] expected = data.clone().readByteArray();
    280     sink.write(data, data.size());
    281 
    282     byte[] sink = new byte[Segment.SIZE + 5];
    283     source.readFully(sink);
    284     assertByteArraysEquals(expected, sink);
    285   }
    286 
    287   @Test public void readFullyByteArrayTooShortThrows() throws IOException {
    288     sink.writeUtf8("Hello");
    289 
    290     byte[] sink = new byte[6];
    291     try {
    292       source.readFully(sink);
    293       fail();
    294     } catch (EOFException ignored) {
    295     }
    296 
    297     // Verify we read all that we could from the source.
    298     assertByteArraysEquals(new byte[] { 'H', 'e', 'l', 'l', 'o', 0 }, sink);
    299   }
    300 
    301   @Test public void readIntoByteArray() throws IOException {
    302     sink.writeUtf8("abcd");
    303 
    304     byte[] sink = new byte[3];
    305     int read = source.read(sink);
    306     if (factory == ONE_BYTE_AT_A_TIME_FACTORY) {
    307       assertEquals(1, read);
    308       byte[] expected = { 'a', 0, 0 };
    309       assertByteArraysEquals(expected, sink);
    310     } else {
    311       assertEquals(3, read);
    312       byte[] expected = { 'a', 'b', 'c' };
    313       assertByteArraysEquals(expected, sink);
    314     }
    315   }
    316 
    317   @Test public void readIntoByteArrayNotEnough() throws IOException {
    318     sink.writeUtf8("abcd");
    319 
    320     byte[] sink = new byte[5];
    321     int read = source.read(sink);
    322     if (factory == ONE_BYTE_AT_A_TIME_FACTORY) {
    323       assertEquals(1, read);
    324       byte[] expected = { 'a', 0, 0, 0, 0 };
    325       assertByteArraysEquals(expected, sink);
    326     } else {
    327       assertEquals(4, read);
    328       byte[] expected = { 'a', 'b', 'c', 'd', 0 };
    329       assertByteArraysEquals(expected, sink);
    330     }
    331   }
    332 
    333   @Test public void readIntoByteArrayOffsetAndCount() throws IOException {
    334     sink.writeUtf8("abcd");
    335 
    336     byte[] sink = new byte[7];
    337     int read = source.read(sink, 2, 3);
    338     if (factory == ONE_BYTE_AT_A_TIME_FACTORY) {
    339       assertEquals(1, read);
    340       byte[] expected = { 0, 0, 'a', 0, 0, 0, 0 };
    341       assertByteArraysEquals(expected, sink);
    342     } else {
    343       assertEquals(3, read);
    344       byte[] expected = { 0, 0, 'a', 'b', 'c', 0, 0 };
    345       assertByteArraysEquals(expected, sink);
    346     }
    347   }
    348 
    349   @Test public void readByteArray() throws IOException {
    350     String string = "abcd" + repeat('e', Segment.SIZE);
    351     sink.writeUtf8(string);
    352     assertByteArraysEquals(string.getBytes(UTF_8), source.readByteArray());
    353   }
    354 
    355   @Test public void readByteArrayPartial() throws IOException {
    356     sink.writeUtf8("abcd");
    357     assertEquals("[97, 98, 99]", Arrays.toString(source.readByteArray(3)));
    358     assertEquals("d", source.readUtf8(1));
    359   }
    360 
    361   @Test public void readByteString() throws IOException {
    362     sink.writeUtf8("abcd").writeUtf8(repeat('e', Segment.SIZE));
    363     assertEquals("abcd" + repeat('e', Segment.SIZE), source.readByteString().utf8());
    364   }
    365 
    366   @Test public void readByteStringPartial() throws IOException {
    367     sink.writeUtf8("abcd").writeUtf8(repeat('e', Segment.SIZE));
    368     assertEquals("abc", source.readByteString(3).utf8());
    369     assertEquals("d", source.readUtf8(1));
    370   }
    371 
    372   @Test public void readSpecificCharsetPartial() throws Exception {
    373     sink.write(
    374         ByteString.decodeHex("0000007600000259000002c80000006c000000e40000007300000259"
    375             + "000002cc000000720000006100000070000000740000025900000072"));
    376     assertEquals("vls", source.readString(7 * 4, Charset.forName("utf-32")));
    377   }
    378 
    379   @Test public void readSpecificCharset() throws Exception {
    380     sink.write(
    381         ByteString.decodeHex("0000007600000259000002c80000006c000000e40000007300000259"
    382             + "000002cc000000720000006100000070000000740000025900000072"));
    383     assertEquals("vlsraptr", source.readString(Charset.forName("utf-32")));
    384   }
    385 
    386   @Test public void readUtf8SpansSegments() throws Exception {
    387     sink.writeUtf8(repeat('a', Segment.SIZE * 2));
    388     source.skip(Segment.SIZE - 1);
    389     assertEquals("aa", source.readUtf8(2));
    390   }
    391 
    392   @Test public void readUtf8Segment() throws Exception {
    393     sink.writeUtf8(repeat('a', Segment.SIZE));
    394     assertEquals(repeat('a', Segment.SIZE), source.readUtf8(Segment.SIZE));
    395   }
    396 
    397   @Test public void readUtf8PartialBuffer() throws Exception {
    398     sink.writeUtf8(repeat('a', Segment.SIZE + 20));
    399     assertEquals(repeat('a', Segment.SIZE + 10), source.readUtf8(Segment.SIZE + 10));
    400   }
    401 
    402   @Test public void readUtf8EntireBuffer() throws Exception {
    403     sink.writeUtf8(repeat('a', Segment.SIZE * 2));
    404     assertEquals(repeat('a', Segment.SIZE * 2), source.readUtf8());
    405   }
    406 
    407   @Test public void skip() throws Exception {
    408     sink.writeUtf8("a");
    409     sink.writeUtf8(repeat('b', Segment.SIZE));
    410     sink.writeUtf8("c");
    411     source.skip(1);
    412     assertEquals('b', source.readByte() & 0xff);
    413     source.skip(Segment.SIZE - 2);
    414     assertEquals('b', source.readByte() & 0xff);
    415     source.skip(1);
    416     assertTrue(source.exhausted());
    417   }
    418 
    419   @Test public void skipInsufficientData() throws Exception {
    420     sink.writeUtf8("a");
    421 
    422     try {
    423       source.skip(2);
    424       fail();
    425     } catch (EOFException ignored) {
    426     }
    427   }
    428 
    429   @Test public void indexOf() throws Exception {
    430     // The segment is empty.
    431     assertEquals(-1, source.indexOf((byte) 'a'));
    432 
    433     // The segment has one value.
    434     sink.writeUtf8("a"); // a
    435     assertEquals(0, source.indexOf((byte) 'a'));
    436     assertEquals(-1, source.indexOf((byte) 'b'));
    437 
    438     // The segment has lots of data.
    439     sink.writeUtf8(repeat('b', Segment.SIZE - 2)); // ab...b
    440     assertEquals(0, source.indexOf((byte) 'a'));
    441     assertEquals(1, source.indexOf((byte) 'b'));
    442     assertEquals(-1, source.indexOf((byte) 'c'));
    443 
    444     // The segment doesn't start at 0, it starts at 2.
    445     source.skip(2); // b...b
    446     assertEquals(-1, source.indexOf((byte) 'a'));
    447     assertEquals(0, source.indexOf((byte) 'b'));
    448     assertEquals(-1, source.indexOf((byte) 'c'));
    449 
    450     // The segment is full.
    451     sink.writeUtf8("c"); // b...bc
    452     assertEquals(-1, source.indexOf((byte) 'a'));
    453     assertEquals(0, source.indexOf((byte) 'b'));
    454     assertEquals(Segment.SIZE - 3, source.indexOf((byte) 'c'));
    455 
    456     // The segment doesn't start at 2, it starts at 4.
    457     source.skip(2); // b...bc
    458     assertEquals(-1, source.indexOf((byte) 'a'));
    459     assertEquals(0, source.indexOf((byte) 'b'));
    460     assertEquals(Segment.SIZE - 5, source.indexOf((byte) 'c'));
    461 
    462     // Two segments.
    463     sink.writeUtf8("d"); // b...bcd, d is in the 2nd segment.
    464     assertEquals(Segment.SIZE - 4, source.indexOf((byte) 'd'));
    465     assertEquals(-1, source.indexOf((byte) 'e'));
    466   }
    467 
    468   @Test public void indexOfWithOffset() throws IOException {
    469     sink.writeUtf8("a").writeUtf8(repeat('b', Segment.SIZE)).writeUtf8("c");
    470     assertEquals(-1, source.indexOf((byte) 'a', 1));
    471     assertEquals(15, source.indexOf((byte) 'b', 15));
    472   }
    473 
    474   @Test public void indexOfByteString() throws IOException {
    475     assertEquals(-1, source.indexOf(ByteString.encodeUtf8("flop")));
    476 
    477     sink.writeUtf8("flip flop");
    478     assertEquals(5, source.indexOf(ByteString.encodeUtf8("flop")));
    479     source.readUtf8(); // Clear stream.
    480 
    481     // Make sure we backtrack and resume searching after partial match.
    482     sink.writeUtf8("hi hi hi hey");
    483     assertEquals(3, source.indexOf(ByteString.encodeUtf8("hi hi hey")));
    484   }
    485 
    486   @Test public void indexOfByteStringWithOffset() throws IOException {
    487     assertEquals(-1, source.indexOf(ByteString.encodeUtf8("flop"), 1));
    488 
    489     sink.writeUtf8("flop flip flop");
    490     assertEquals(10, source.indexOf(ByteString.encodeUtf8("flop"), 1));
    491     source.readUtf8(); // Clear stream
    492 
    493     // Make sure we backtrack and resume searching after partial match.
    494     sink.writeUtf8("hi hi hi hi hey");
    495     assertEquals(6, source.indexOf(ByteString.encodeUtf8("hi hi hey"), 1));
    496   }
    497 
    498   @Test public void indexOfByteStringInvalidArgumentsThrows() throws IOException {
    499     try {
    500       source.indexOf(ByteString.of());
    501       fail();
    502     } catch (IllegalArgumentException e) {
    503       assertEquals("bytes is empty", e.getMessage());
    504     }
    505     try {
    506       source.indexOf(ByteString.encodeUtf8("hi"), -1);
    507       fail();
    508     } catch (IllegalArgumentException e) {
    509       assertEquals("fromIndex < 0", e.getMessage());
    510     }
    511   }
    512 
    513   @Test public void indexOfElement() throws IOException {
    514     sink.writeUtf8("a").writeUtf8(repeat('b', Segment.SIZE)).writeUtf8("c");
    515     assertEquals(0, source.indexOfElement(ByteString.encodeUtf8("DEFGaHIJK")));
    516     assertEquals(1, source.indexOfElement(ByteString.encodeUtf8("DEFGHIJKb")));
    517     assertEquals(Segment.SIZE + 1, source.indexOfElement(ByteString.encodeUtf8("cDEFGHIJK")));
    518     assertEquals(1, source.indexOfElement(ByteString.encodeUtf8("DEFbGHIc")));
    519     assertEquals(-1L, source.indexOfElement(ByteString.encodeUtf8("DEFGHIJK")));
    520     assertEquals(-1L, source.indexOfElement(ByteString.encodeUtf8("")));
    521   }
    522 
    523   @Test public void indexOfElementWithOffset() throws IOException {
    524     sink.writeUtf8("a").writeUtf8(repeat('b', Segment.SIZE)).writeUtf8("c");
    525     assertEquals(-1, source.indexOfElement(ByteString.encodeUtf8("DEFGaHIJK"), 1));
    526     assertEquals(15, source.indexOfElement(ByteString.encodeUtf8("DEFGHIJKb"), 15));
    527   }
    528 
    529   @Test public void request() throws IOException {
    530     sink.writeUtf8("a").writeUtf8(repeat('b', Segment.SIZE)).writeUtf8("c");
    531     assertTrue(source.request(Segment.SIZE + 2));
    532     assertFalse(source.request(Segment.SIZE + 3));
    533   }
    534 
    535   @Test public void require() throws IOException {
    536     sink.writeUtf8("a").writeUtf8(repeat('b', Segment.SIZE)).writeUtf8("c");
    537     source.require(Segment.SIZE + 2);
    538     try {
    539       source.require(Segment.SIZE + 3);
    540       fail();
    541     } catch (EOFException expected) {
    542     }
    543   }
    544 
    545   @Test public void inputStream() throws Exception {
    546     sink.writeUtf8("abc");
    547     InputStream in = source.inputStream();
    548     byte[] bytes = { 'z', 'z', 'z' };
    549     int read = in.read(bytes);
    550     if (factory == ONE_BYTE_AT_A_TIME_FACTORY) {
    551       assertEquals(1, read);
    552       assertByteArrayEquals("azz", bytes);
    553 
    554       read = in.read(bytes);
    555       assertEquals(1, read);
    556       assertByteArrayEquals("bzz", bytes);
    557 
    558       read = in.read(bytes);
    559       assertEquals(1, read);
    560       assertByteArrayEquals("czz", bytes);
    561     } else {
    562       assertEquals(3, read);
    563       assertByteArrayEquals("abc", bytes);
    564     }
    565 
    566     assertEquals(-1, in.read());
    567   }
    568 
    569   @Test public void inputStreamOffsetCount() throws Exception {
    570     sink.writeUtf8("abcde");
    571     InputStream in = source.inputStream();
    572     byte[] bytes = { 'z', 'z', 'z', 'z', 'z' };
    573     int read = in.read(bytes, 1, 3);
    574     if (factory == ONE_BYTE_AT_A_TIME_FACTORY) {
    575       assertEquals(1, read);
    576       assertByteArrayEquals("zazzz", bytes);
    577     } else {
    578       assertEquals(3, read);
    579       assertByteArrayEquals("zabcz", bytes);
    580     }
    581   }
    582 
    583   @Test public void inputStreamSkip() throws Exception {
    584     sink.writeUtf8("abcde");
    585     InputStream in = source.inputStream();
    586     assertEquals(4, in.skip(4));
    587     assertEquals('e', in.read());
    588   }
    589 
    590   @Test public void inputStreamCharByChar() throws Exception {
    591     sink.writeUtf8("abc");
    592     InputStream in = source.inputStream();
    593     assertEquals('a', in.read());
    594     assertEquals('b', in.read());
    595     assertEquals('c', in.read());
    596     assertEquals(-1, in.read());
    597   }
    598 
    599   @Test public void inputStreamBounds() throws IOException {
    600     sink.writeUtf8(repeat('a', 100));
    601     InputStream in = source.inputStream();
    602     try {
    603       in.read(new byte[100], 50, 51);
    604       fail();
    605     } catch (ArrayIndexOutOfBoundsException expected) {
    606     }
    607   }
    608 
    609   @Test public void longHexString() throws IOException {
    610     assertLongHexString("8000000000000000", 0x8000000000000000L);
    611     assertLongHexString("fffffffffffffffe", 0xFFFFFFFFFFFFFFFEL);
    612     assertLongHexString("FFFFFFFFFFFFFFFe", 0xFFFFFFFFFFFFFFFEL);
    613     assertLongHexString("ffffffffffffffff", 0xffffffffffffffffL);
    614     assertLongHexString("FFFFFFFFFFFFFFFF", 0xFFFFFFFFFFFFFFFFL);
    615     assertLongHexString("0000000000000000", 0x0);
    616     assertLongHexString("0000000000000001", 0x1);
    617     assertLongHexString("7999999999999999", 0x7999999999999999L);
    618 
    619     assertLongHexString("FF", 0xFF);
    620     assertLongHexString("0000000000000001", 0x1);
    621   }
    622 
    623   @Test public void hexStringWithManyLeadingZeros() throws IOException {
    624     assertLongHexString("00000000000000001", 0x1);
    625     assertLongHexString("0000000000000000ffffffffffffffff", 0xffffffffffffffffL);
    626     assertLongHexString("00000000000000007fffffffffffffff", 0x7fffffffffffffffL);
    627     assertLongHexString(TestUtil.repeat('0', Segment.SIZE + 1) + "1", 0x1);
    628   }
    629 
    630   private void assertLongHexString(String s, long expected) throws IOException {
    631     sink.writeUtf8(s);
    632     long actual = source.readHexadecimalUnsignedLong();
    633     assertEquals(s + " --> " + expected, expected, actual);
    634   }
    635 
    636   @Test public void longHexStringAcrossSegment() throws IOException {
    637     sink.writeUtf8(repeat('a', Segment.SIZE - 8)).writeUtf8("FFFFFFFFFFFFFFFF");
    638     source.skip(Segment.SIZE - 8);
    639     assertEquals(-1, source.readHexadecimalUnsignedLong());
    640   }
    641 
    642   @Test public void longHexStringTooLongThrows() throws IOException {
    643     try {
    644       sink.writeUtf8("fffffffffffffffff");
    645       source.readHexadecimalUnsignedLong();
    646       fail();
    647     } catch (NumberFormatException e) {
    648       assertEquals("Number too large: fffffffffffffffff", e.getMessage());
    649     }
    650   }
    651 
    652   @Test public void longHexStringTooShortThrows() throws IOException {
    653     try {
    654       sink.writeUtf8(" ");
    655       source.readHexadecimalUnsignedLong();
    656       fail();
    657     } catch (NumberFormatException e) {
    658       assertEquals("Expected leading [0-9a-fA-F] character but was 0x20", e.getMessage());
    659     }
    660   }
    661 
    662   @Test public void longHexEmptySourceThrows() throws IOException {
    663     try {
    664       sink.writeUtf8("");
    665       source.readHexadecimalUnsignedLong();
    666       fail();
    667     } catch (IllegalStateException | EOFException expected) {
    668     }
    669   }
    670 
    671   @Test public void longDecimalString() throws IOException {
    672     assertLongDecimalString("-9223372036854775808", -9223372036854775808L);
    673     assertLongDecimalString("-1", -1L);
    674     assertLongDecimalString("0", 0L);
    675     assertLongDecimalString("1", 1L);
    676     assertLongDecimalString("9223372036854775807", 9223372036854775807L);
    677 
    678     assertLongDecimalString("00000001", 1L);
    679     assertLongDecimalString("-000001", -1L);
    680   }
    681 
    682   private void assertLongDecimalString(String s, long expected) throws IOException {
    683     sink.writeUtf8(s);
    684     sink.writeUtf8("zzz");
    685     long actual = source.readDecimalLong();
    686     assertEquals(s + " --> " + expected, expected, actual);
    687     assertEquals("zzz", source.readUtf8());
    688   }
    689 
    690   @Test public void longDecimalStringAcrossSegment() throws IOException {
    691     sink.writeUtf8(repeat('a', Segment.SIZE - 8)).writeUtf8("1234567890123456");
    692     sink.writeUtf8("zzz");
    693     source.skip(Segment.SIZE - 8);
    694     assertEquals(1234567890123456L, source.readDecimalLong());
    695     assertEquals("zzz", source.readUtf8());
    696   }
    697 
    698   @Test public void longDecimalStringTooLongThrows() throws IOException {
    699     try {
    700       sink.writeUtf8("12345678901234567890"); // Too many digits.
    701       source.readDecimalLong();
    702       fail();
    703     } catch (NumberFormatException e) {
    704       assertEquals("Number too large: 12345678901234567890", e.getMessage());
    705     }
    706   }
    707 
    708   @Test public void longDecimalStringTooHighThrows() throws IOException {
    709     try {
    710       sink.writeUtf8("9223372036854775808"); // Right size but cannot fit.
    711       source.readDecimalLong();
    712       fail();
    713     } catch (NumberFormatException e) {
    714       assertEquals("Number too large: 9223372036854775808", e.getMessage());
    715     }
    716   }
    717 
    718   @Test public void longDecimalStringTooLowThrows() throws IOException {
    719     try {
    720       sink.writeUtf8("-9223372036854775809"); // Right size but cannot fit.
    721       source.readDecimalLong();
    722       fail();
    723     } catch (NumberFormatException e) {
    724       assertEquals("Number too large: -9223372036854775809", e.getMessage());
    725     }
    726   }
    727 
    728   @Test public void longDecimalStringTooShortThrows() throws IOException {
    729     try {
    730       sink.writeUtf8(" ");
    731       source.readDecimalLong();
    732       fail();
    733     } catch (NumberFormatException e) {
    734       assertEquals("Expected leading [0-9] or '-' character but was 0x20", e.getMessage());
    735     }
    736   }
    737 
    738   @Test public void longDecimalEmptyThrows() throws IOException {
    739     try {
    740       sink.writeUtf8("");
    741       source.readDecimalLong();
    742       fail();
    743     } catch (IllegalStateException | EOFException expected) {
    744     }
    745   }
    746 
    747   @Test public void codePoints() throws IOException {
    748     sink.write(ByteString.decodeHex("7f"));
    749     assertEquals(0x7f, source.readUtf8CodePoint());
    750 
    751     sink.write(ByteString.decodeHex("dfbf"));
    752     assertEquals(0x07ff, source.readUtf8CodePoint());
    753 
    754     sink.write(ByteString.decodeHex("efbfbf"));
    755     assertEquals(0xffff, source.readUtf8CodePoint());
    756 
    757     sink.write(ByteString.decodeHex("f48fbfbf"));
    758     assertEquals(0x10ffff, source.readUtf8CodePoint());
    759   }
    760 
    761   @Test public void decimalStringWithManyLeadingZeros() throws IOException {
    762     assertLongDecimalString("00000000000000001", 1);
    763     assertLongDecimalString("00000000000000009223372036854775807", 9223372036854775807L);
    764     assertLongDecimalString("-00000000000000009223372036854775808", -9223372036854775808L);
    765     assertLongDecimalString(TestUtil.repeat('0', Segment.SIZE + 1) + "1", 1);
    766   }
    767 }
    768