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.ByteArrayInputStream;
     19 import java.io.ByteArrayOutputStream;
     20 import java.io.IOException;
     21 import java.io.InputStream;
     22 import java.util.Arrays;
     23 import java.util.List;
     24 import java.util.Random;
     25 import org.junit.Test;
     26 
     27 import static java.util.Arrays.asList;
     28 import static okio.TestUtil.repeat;
     29 import static okio.Util.UTF_8;
     30 import static org.junit.Assert.assertEquals;
     31 import static org.junit.Assert.assertFalse;
     32 import static org.junit.Assert.assertTrue;
     33 import static org.junit.Assert.fail;
     34 
     35 /**
     36  * Tests solely for the behavior of Buffer's implementation. For generic BufferedSink or
     37  * BufferedSource behavior use BufferedSinkTest or BufferedSourceTest, respectively.
     38  */
     39 public final class BufferTest {
     40   @Test public void readAndWriteUtf8() throws Exception {
     41     Buffer buffer = new Buffer();
     42     buffer.writeUtf8("ab");
     43     assertEquals(2, buffer.size());
     44     buffer.writeUtf8("cdef");
     45     assertEquals(6, buffer.size());
     46     assertEquals("abcd", buffer.readUtf8(4));
     47     assertEquals(2, buffer.size());
     48     assertEquals("ef", buffer.readUtf8(2));
     49     assertEquals(0, buffer.size());
     50     try {
     51       buffer.readUtf8(1);
     52       fail();
     53     } catch (ArrayIndexOutOfBoundsException expected) {
     54     }
     55   }
     56 
     57   @Test public void completeSegmentByteCountOnEmptyBuffer() throws Exception {
     58     Buffer buffer = new Buffer();
     59     assertEquals(0, buffer.completeSegmentByteCount());
     60   }
     61 
     62   @Test public void completeSegmentByteCountOnBufferWithFullSegments() throws Exception {
     63     Buffer buffer = new Buffer();
     64     buffer.writeUtf8(repeat('a', Segment.SIZE * 4));
     65     assertEquals(Segment.SIZE * 4, buffer.completeSegmentByteCount());
     66   }
     67 
     68   @Test public void completeSegmentByteCountOnBufferWithIncompleteTailSegment() throws Exception {
     69     Buffer buffer = new Buffer();
     70     buffer.writeUtf8(repeat('a', Segment.SIZE * 4 - 10));
     71     assertEquals(Segment.SIZE * 3, buffer.completeSegmentByteCount());
     72   }
     73 
     74   @Test public void toStringOnEmptyBuffer() throws Exception {
     75     Buffer buffer = new Buffer();
     76     assertEquals("Buffer[size=0]", buffer.toString());
     77   }
     78 
     79   @Test public void toStringOnSmallBufferIncludesContents() throws Exception {
     80     Buffer buffer = new Buffer();
     81     buffer.write(ByteString.decodeHex("a1b2c3d4e5f61a2b3c4d5e6f10203040"));
     82     assertEquals("Buffer[size=16 data=a1b2c3d4e5f61a2b3c4d5e6f10203040]", buffer.toString());
     83   }
     84 
     85   @Test public void toStringOnLargeBufferIncludesMd5() throws Exception {
     86     Buffer buffer = new Buffer();
     87     buffer.write(ByteString.encodeUtf8("12345678901234567"));
     88     assertEquals("Buffer[size=17 md5=2c9728a2138b2f25e9f89f99bdccf8db]", buffer.toString());
     89   }
     90 
     91   @Test public void toStringOnMultipleSegmentBuffer() throws Exception {
     92     Buffer buffer = new Buffer();
     93     buffer.writeUtf8(repeat('a', 6144));
     94     assertEquals("Buffer[size=6144 md5=d890021f28522533c1cc1b9b1f83ce73]", buffer.toString());
     95   }
     96 
     97   @Test public void multipleSegmentBuffers() throws Exception {
     98     Buffer buffer = new Buffer();
     99     buffer.writeUtf8(repeat('a', 1000));
    100     buffer.writeUtf8(repeat('b', 2500));
    101     buffer.writeUtf8(repeat('c', 5000));
    102     buffer.writeUtf8(repeat('d', 10000));
    103     buffer.writeUtf8(repeat('e', 25000));
    104     buffer.writeUtf8(repeat('f', 50000));
    105 
    106     assertEquals(repeat('a', 999), buffer.readUtf8(999)); // a...a
    107     assertEquals("a" + repeat('b', 2500) + "c", buffer.readUtf8(2502)); // ab...bc
    108     assertEquals(repeat('c', 4998), buffer.readUtf8(4998)); // c...c
    109     assertEquals("c" + repeat('d', 10000) + "e", buffer.readUtf8(10002)); // cd...de
    110     assertEquals(repeat('e', 24998), buffer.readUtf8(24998)); // e...e
    111     assertEquals("e" + repeat('f', 50000), buffer.readUtf8(50001)); // ef...f
    112     assertEquals(0, buffer.size());
    113   }
    114 
    115   @Test public void fillAndDrainPool() throws Exception {
    116     Buffer buffer = new Buffer();
    117 
    118     // Take 2 * MAX_SIZE segments. This will drain the pool, even if other tests filled it.
    119     buffer.write(new byte[(int) SegmentPool.MAX_SIZE]);
    120     buffer.write(new byte[(int) SegmentPool.MAX_SIZE]);
    121     assertEquals(0, SegmentPool.byteCount);
    122 
    123     // Recycle MAX_SIZE segments. They're all in the pool.
    124     buffer.readByteString(SegmentPool.MAX_SIZE);
    125     assertEquals(SegmentPool.MAX_SIZE, SegmentPool.byteCount);
    126 
    127     // Recycle MAX_SIZE more segments. The pool is full so they get garbage collected.
    128     buffer.readByteString(SegmentPool.MAX_SIZE);
    129     assertEquals(SegmentPool.MAX_SIZE, SegmentPool.byteCount);
    130 
    131     // Take MAX_SIZE segments to drain the pool.
    132     buffer.write(new byte[(int) SegmentPool.MAX_SIZE]);
    133     assertEquals(0, SegmentPool.byteCount);
    134 
    135     // Take MAX_SIZE more segments. The pool is drained so these will need to be allocated.
    136     buffer.write(new byte[(int) SegmentPool.MAX_SIZE]);
    137     assertEquals(0, SegmentPool.byteCount);
    138   }
    139 
    140   @Test public void moveBytesBetweenBuffersShareSegment() throws Exception {
    141     int size = (Segment.SIZE / 2) - 1;
    142     List<Integer> segmentSizes = moveBytesBetweenBuffers(repeat('a', size), repeat('b', size));
    143     assertEquals(asList(size * 2), segmentSizes);
    144   }
    145 
    146   @Test public void moveBytesBetweenBuffersReassignSegment() throws Exception {
    147     int size = (Segment.SIZE / 2) + 1;
    148     List<Integer> segmentSizes = moveBytesBetweenBuffers(repeat('a', size), repeat('b', size));
    149     assertEquals(asList(size, size), segmentSizes);
    150   }
    151 
    152   @Test public void moveBytesBetweenBuffersMultipleSegments() throws Exception {
    153     int size = 3 * Segment.SIZE + 1;
    154     List<Integer> segmentSizes = moveBytesBetweenBuffers(repeat('a', size), repeat('b', size));
    155     assertEquals(asList(Segment.SIZE, Segment.SIZE, Segment.SIZE, 1,
    156         Segment.SIZE, Segment.SIZE, Segment.SIZE, 1), segmentSizes);
    157   }
    158 
    159   private List<Integer> moveBytesBetweenBuffers(String... contents) throws IOException {
    160     StringBuilder expected = new StringBuilder();
    161     Buffer buffer = new Buffer();
    162     for (String s : contents) {
    163       Buffer source = new Buffer();
    164       source.writeUtf8(s);
    165       buffer.writeAll(source);
    166       expected.append(s);
    167     }
    168     List<Integer> segmentSizes = buffer.segmentSizes();
    169     assertEquals(expected.toString(), buffer.readUtf8(expected.length()));
    170     return segmentSizes;
    171   }
    172 
    173   /** The big part of source's first segment is being moved. */
    174   @Test public void writeSplitSourceBufferLeft() throws Exception {
    175     int writeSize = Segment.SIZE / 2 + 1;
    176 
    177     Buffer sink = new Buffer();
    178     sink.writeUtf8(repeat('b', Segment.SIZE - 10));
    179 
    180     Buffer source = new Buffer();
    181     source.writeUtf8(repeat('a', Segment.SIZE * 2));
    182     sink.write(source, writeSize);
    183 
    184     assertEquals(asList(Segment.SIZE - 10, writeSize), sink.segmentSizes());
    185     assertEquals(asList(Segment.SIZE - writeSize, Segment.SIZE), source.segmentSizes());
    186   }
    187 
    188   /** The big part of source's first segment is staying put. */
    189   @Test public void writeSplitSourceBufferRight() throws Exception {
    190     int writeSize = Segment.SIZE / 2 - 1;
    191 
    192     Buffer sink = new Buffer();
    193     sink.writeUtf8(repeat('b', Segment.SIZE - 10));
    194 
    195     Buffer source = new Buffer();
    196     source.writeUtf8(repeat('a', Segment.SIZE * 2));
    197     sink.write(source, writeSize);
    198 
    199     assertEquals(asList(Segment.SIZE - 10, writeSize), sink.segmentSizes());
    200     assertEquals(asList(Segment.SIZE - writeSize, Segment.SIZE), source.segmentSizes());
    201   }
    202 
    203   @Test public void writePrefixDoesntSplit() throws Exception {
    204     Buffer sink = new Buffer();
    205     sink.writeUtf8(repeat('b', 10));
    206 
    207     Buffer source = new Buffer();
    208     source.writeUtf8(repeat('a', Segment.SIZE * 2));
    209     sink.write(source, 20);
    210 
    211     assertEquals(asList(30), sink.segmentSizes());
    212     assertEquals(asList(Segment.SIZE - 20, Segment.SIZE), source.segmentSizes());
    213     assertEquals(30, sink.size());
    214     assertEquals(Segment.SIZE * 2 - 20, source.size());
    215   }
    216 
    217   @Test public void writePrefixDoesntSplitButRequiresCompact() throws Exception {
    218     Buffer sink = new Buffer();
    219     sink.writeUtf8(repeat('b', Segment.SIZE - 10)); // limit = size - 10
    220     sink.readUtf8(Segment.SIZE - 20); // pos = size = 20
    221 
    222     Buffer source = new Buffer();
    223     source.writeUtf8(repeat('a', Segment.SIZE * 2));
    224     sink.write(source, 20);
    225 
    226     assertEquals(asList(30), sink.segmentSizes());
    227     assertEquals(asList(Segment.SIZE - 20, Segment.SIZE), source.segmentSizes());
    228     assertEquals(30, sink.size());
    229     assertEquals(Segment.SIZE * 2 - 20, source.size());
    230   }
    231 
    232   @Test public void copyToSpanningSegments() throws Exception {
    233     Buffer source = new Buffer();
    234     source.writeUtf8(repeat('a', Segment.SIZE * 2));
    235     source.writeUtf8(repeat('b', Segment.SIZE * 2));
    236 
    237     ByteArrayOutputStream out = new ByteArrayOutputStream();
    238     source.copyTo(out, 10, Segment.SIZE * 3);
    239 
    240     assertEquals(repeat('a', Segment.SIZE * 2 - 10) + repeat('b', Segment.SIZE + 10),
    241         out.toString());
    242     assertEquals(repeat('a', Segment.SIZE * 2) + repeat('b', Segment.SIZE * 2),
    243         source.readUtf8(Segment.SIZE * 4));
    244   }
    245 
    246   @Test public void copyToStream() throws Exception {
    247     Buffer buffer = new Buffer().writeUtf8("hello, world!");
    248     ByteArrayOutputStream out = new ByteArrayOutputStream();
    249     buffer.copyTo(out);
    250     String outString = new String(out.toByteArray(), UTF_8);
    251     assertEquals("hello, world!", outString);
    252     assertEquals("hello, world!", buffer.readUtf8());
    253   }
    254 
    255   @Test public void writeToSpanningSegments() throws Exception {
    256     Buffer buffer = new Buffer();
    257     buffer.writeUtf8(repeat('a', Segment.SIZE * 2));
    258     buffer.writeUtf8(repeat('b', Segment.SIZE * 2));
    259 
    260     ByteArrayOutputStream out = new ByteArrayOutputStream();
    261     buffer.skip(10);
    262     buffer.writeTo(out, Segment.SIZE * 3);
    263 
    264     assertEquals(repeat('a', Segment.SIZE * 2 - 10) + repeat('b', Segment.SIZE + 10),
    265         out.toString());
    266     assertEquals(repeat('b', Segment.SIZE - 10), buffer.readUtf8(buffer.size));
    267   }
    268 
    269   @Test public void writeToStream() throws Exception {
    270     Buffer buffer = new Buffer().writeUtf8("hello, world!");
    271     ByteArrayOutputStream out = new ByteArrayOutputStream();
    272     buffer.writeTo(out);
    273     String outString = new String(out.toByteArray(), UTF_8);
    274     assertEquals("hello, world!", outString);
    275     assertEquals(0, buffer.size());
    276   }
    277 
    278   @Test public void readFromStream() throws Exception {
    279     InputStream in = new ByteArrayInputStream("hello, world!".getBytes(UTF_8));
    280     Buffer buffer = new Buffer();
    281     buffer.readFrom(in);
    282     String out = buffer.readUtf8();
    283     assertEquals("hello, world!", out);
    284   }
    285 
    286   @Test public void readFromSpanningSegments() throws Exception {
    287     InputStream in = new ByteArrayInputStream("hello, world!".getBytes(UTF_8));
    288     Buffer buffer = new Buffer().writeUtf8(repeat('a', Segment.SIZE - 10));
    289     buffer.readFrom(in);
    290     String out = buffer.readUtf8();
    291     assertEquals(repeat('a', Segment.SIZE - 10) + "hello, world!", out);
    292   }
    293 
    294   @Test public void readFromStreamWithCount() throws Exception {
    295     InputStream in = new ByteArrayInputStream("hello, world!".getBytes(UTF_8));
    296     Buffer buffer = new Buffer();
    297     buffer.readFrom(in, 10);
    298     String out = buffer.readUtf8();
    299     assertEquals("hello, wor", out);
    300   }
    301 
    302   @Test public void moveAllRequestedBytesWithRead() throws Exception {
    303     Buffer sink = new Buffer();
    304     sink.writeUtf8(repeat('a', 10));
    305 
    306     Buffer source = new Buffer();
    307     source.writeUtf8(repeat('b', 15));
    308 
    309     assertEquals(10, source.read(sink, 10));
    310     assertEquals(20, sink.size());
    311     assertEquals(5, source.size());
    312     assertEquals(repeat('a', 10) + repeat('b', 10), sink.readUtf8(20));
    313   }
    314 
    315   @Test public void moveFewerThanRequestedBytesWithRead() throws Exception {
    316     Buffer sink = new Buffer();
    317     sink.writeUtf8(repeat('a', 10));
    318 
    319     Buffer source = new Buffer();
    320     source.writeUtf8(repeat('b', 20));
    321 
    322     assertEquals(20, source.read(sink, 25));
    323     assertEquals(30, sink.size());
    324     assertEquals(0, source.size());
    325     assertEquals(repeat('a', 10) + repeat('b', 20), sink.readUtf8(30));
    326   }
    327 
    328   @Test public void indexOfWithOffset() throws Exception {
    329     Buffer buffer = new Buffer();
    330     int halfSegment = Segment.SIZE / 2;
    331     buffer.writeUtf8(repeat('a', halfSegment));
    332     buffer.writeUtf8(repeat('b', halfSegment));
    333     buffer.writeUtf8(repeat('c', halfSegment));
    334     buffer.writeUtf8(repeat('d', halfSegment));
    335     assertEquals(0, buffer.indexOf((byte) 'a', 0));
    336     assertEquals(halfSegment - 1, buffer.indexOf((byte) 'a', halfSegment - 1));
    337     assertEquals(halfSegment, buffer.indexOf((byte) 'b', halfSegment - 1));
    338     assertEquals(halfSegment * 2, buffer.indexOf((byte) 'c', halfSegment - 1));
    339     assertEquals(halfSegment * 3, buffer.indexOf((byte) 'd', halfSegment - 1));
    340     assertEquals(halfSegment * 3, buffer.indexOf((byte) 'd', halfSegment * 2));
    341     assertEquals(halfSegment * 3, buffer.indexOf((byte) 'd', halfSegment * 3));
    342     assertEquals(halfSegment * 4 - 1, buffer.indexOf((byte) 'd', halfSegment * 4 - 1));
    343   }
    344 
    345   @Test public void byteAt() throws Exception {
    346     Buffer buffer = new Buffer();
    347     buffer.writeUtf8("a");
    348     buffer.writeUtf8(repeat('b', Segment.SIZE));
    349     buffer.writeUtf8("c");
    350     assertEquals('a', buffer.getByte(0));
    351     assertEquals('a', buffer.getByte(0)); // getByte doesn't mutate!
    352     assertEquals('c', buffer.getByte(buffer.size - 1));
    353     assertEquals('b', buffer.getByte(buffer.size - 2));
    354     assertEquals('b', buffer.getByte(buffer.size - 3));
    355   }
    356 
    357   @Test public void getByteOfEmptyBuffer() throws Exception {
    358     Buffer buffer = new Buffer();
    359     try {
    360       buffer.getByte(0);
    361       fail();
    362     } catch (IndexOutOfBoundsException expected) {
    363     }
    364   }
    365 
    366   @Test public void writePrefixToEmptyBuffer() throws IOException {
    367     Buffer sink = new Buffer();
    368     Buffer source = new Buffer();
    369     source.writeUtf8("abcd");
    370     sink.write(source, 2);
    371     assertEquals("ab", sink.readUtf8(2));
    372   }
    373 
    374   @Test public void cloneDoesNotObserveWritesToOriginal() throws Exception {
    375     Buffer original = new Buffer();
    376     Buffer clone = original.clone();
    377     original.writeUtf8("abc");
    378     assertEquals(0, clone.size());
    379   }
    380 
    381   @Test public void cloneDoesNotObserveReadsFromOriginal() throws Exception {
    382     Buffer original = new Buffer();
    383     original.writeUtf8("abc");
    384     Buffer clone = original.clone();
    385     assertEquals("abc", original.readUtf8(3));
    386     assertEquals(3, clone.size());
    387     assertEquals("ab", clone.readUtf8(2));
    388   }
    389 
    390   @Test public void originalDoesNotObserveWritesToClone() throws Exception {
    391     Buffer original = new Buffer();
    392     Buffer clone = original.clone();
    393     clone.writeUtf8("abc");
    394     assertEquals(0, original.size());
    395   }
    396 
    397   @Test public void originalDoesNotObserveReadsFromClone() throws Exception {
    398     Buffer original = new Buffer();
    399     original.writeUtf8("abc");
    400     Buffer clone = original.clone();
    401     assertEquals("abc", clone.readUtf8(3));
    402     assertEquals(3, original.size());
    403     assertEquals("ab", original.readUtf8(2));
    404   }
    405 
    406   @Test public void cloneMultipleSegments() throws Exception {
    407     Buffer original = new Buffer();
    408     original.writeUtf8(repeat('a', Segment.SIZE * 3));
    409     Buffer clone = original.clone();
    410     original.writeUtf8(repeat('b', Segment.SIZE * 3));
    411     clone.writeUtf8(repeat('c', Segment.SIZE * 3));
    412 
    413     assertEquals(repeat('a', Segment.SIZE * 3) + repeat('b', Segment.SIZE * 3),
    414         original.readUtf8(Segment.SIZE * 6));
    415     assertEquals(repeat('a', Segment.SIZE * 3) + repeat('c', Segment.SIZE * 3),
    416         clone.readUtf8(Segment.SIZE * 6));
    417   }
    418 
    419   @Test public void equalsAndHashCodeEmpty() throws Exception {
    420     Buffer a = new Buffer();
    421     Buffer b = new Buffer();
    422     assertTrue(a.equals(b));
    423     assertTrue(a.hashCode() == b.hashCode());
    424   }
    425 
    426   @Test public void equalsAndHashCode() throws Exception {
    427     Buffer a = new Buffer().writeUtf8("dog");
    428     Buffer b = new Buffer().writeUtf8("hotdog");
    429     assertFalse(a.equals(b));
    430     assertFalse(a.hashCode() == b.hashCode());
    431 
    432     b.readUtf8(3); // Leaves b containing 'dog'.
    433     assertTrue(a.equals(b));
    434     assertTrue(a.hashCode() == b.hashCode());
    435   }
    436 
    437   @Test public void equalsAndHashCodeSpanningSegments() throws Exception {
    438     byte[] data = new byte[1024 * 1024];
    439     Random dice = new Random(0);
    440     dice.nextBytes(data);
    441 
    442     Buffer a = bufferWithRandomSegmentLayout(dice, data);
    443     Buffer b = bufferWithRandomSegmentLayout(dice, data);
    444     assertTrue(a.equals(b));
    445     assertTrue(a.hashCode() == b.hashCode());
    446 
    447     data[data.length / 2]++; // Change a single byte.
    448     Buffer c = bufferWithRandomSegmentLayout(dice, data);
    449     assertFalse(a.equals(c));
    450     assertFalse(a.hashCode() == c.hashCode());
    451   }
    452 
    453   @Test public void bufferInputStreamByteByByte() throws Exception {
    454     Buffer source = new Buffer();
    455     source.writeUtf8("abc");
    456 
    457     InputStream in = source.inputStream();
    458     assertEquals(3, in.available());
    459     assertEquals('a', in.read());
    460     assertEquals('b', in.read());
    461     assertEquals('c', in.read());
    462     assertEquals(-1, in.read());
    463     assertEquals(0, in.available());
    464   }
    465 
    466   @Test public void bufferInputStreamBulkReads() throws Exception {
    467     Buffer source = new Buffer();
    468     source.writeUtf8("abc");
    469 
    470     byte[] byteArray = new byte[4];
    471 
    472     Arrays.fill(byteArray, (byte) -5);
    473     InputStream in = source.inputStream();
    474     assertEquals(3, in.read(byteArray));
    475     assertEquals("[97, 98, 99, -5]", Arrays.toString(byteArray));
    476 
    477     Arrays.fill(byteArray, (byte) -7);
    478     assertEquals(-1, in.read(byteArray));
    479     assertEquals("[-7, -7, -7, -7]", Arrays.toString(byteArray));
    480   }
    481 
    482   /**
    483    * When writing data that's already buffered, there's no reason to page the
    484    * data by segment.
    485    */
    486   @Test public void readAllWritesAllSegmentsAtOnce() throws Exception {
    487     Buffer write1 = new Buffer().writeUtf8(""
    488         + TestUtil.repeat('a', Segment.SIZE)
    489         + TestUtil.repeat('b', Segment.SIZE)
    490         + TestUtil.repeat('c', Segment.SIZE));
    491 
    492     Buffer source = new Buffer().writeUtf8(""
    493         + TestUtil.repeat('a', Segment.SIZE)
    494         + TestUtil.repeat('b', Segment.SIZE)
    495         + TestUtil.repeat('c', Segment.SIZE));
    496 
    497     MockSink mockSink = new MockSink();
    498 
    499     assertEquals(Segment.SIZE * 3, source.readAll(mockSink));
    500     assertEquals(0, source.size());
    501     mockSink.assertLog("write(" + write1 + ", " + write1.size() + ")");
    502   }
    503 
    504   @Test public void writeAllMultipleSegments() throws Exception {
    505     Buffer source = new Buffer().writeUtf8(TestUtil.repeat('a', Segment.SIZE * 3));
    506     Buffer sink = new Buffer();
    507 
    508     assertEquals(Segment.SIZE * 3, sink.writeAll(source));
    509     assertEquals(0, source.size());
    510     assertEquals(TestUtil.repeat('a', Segment.SIZE * 3), sink.readUtf8());
    511   }
    512 
    513   @Test public void copyTo() throws Exception {
    514     Buffer source = new Buffer();
    515     source.writeUtf8("party");
    516 
    517     Buffer target = new Buffer();
    518     source.copyTo(target, 1, 3);
    519 
    520     assertEquals("art", target.readUtf8());
    521     assertEquals("party", source.readUtf8());
    522   }
    523 
    524   @Test public void copyToOnSegmentBoundary() throws Exception {
    525     String as = repeat('a', Segment.SIZE);
    526     String bs = repeat('b', Segment.SIZE);
    527     String cs = repeat('c', Segment.SIZE);
    528     String ds = repeat('d', Segment.SIZE);
    529 
    530     Buffer source = new Buffer();
    531     source.writeUtf8(as);
    532     source.writeUtf8(bs);
    533     source.writeUtf8(cs);
    534 
    535     Buffer target = new Buffer();
    536     target.writeUtf8(ds);
    537 
    538     source.copyTo(target, as.length(), bs.length() + cs.length());
    539     assertEquals(ds + bs + cs, target.readUtf8());
    540   }
    541 
    542   @Test public void copyToOffSegmentBoundary() throws Exception {
    543     String as = repeat('a', Segment.SIZE - 1);
    544     String bs = repeat('b', Segment.SIZE + 2);
    545     String cs = repeat('c', Segment.SIZE - 4);
    546     String ds = repeat('d', Segment.SIZE + 8);
    547 
    548     Buffer source = new Buffer();
    549     source.writeUtf8(as);
    550     source.writeUtf8(bs);
    551     source.writeUtf8(cs);
    552 
    553     Buffer target = new Buffer();
    554     target.writeUtf8(ds);
    555 
    556     source.copyTo(target, as.length(), bs.length() + cs.length());
    557     assertEquals(ds + bs + cs, target.readUtf8());
    558   }
    559 
    560   @Test public void copyToSourceAndTargetCanBeTheSame() throws Exception {
    561     String as = repeat('a', Segment.SIZE);
    562     String bs = repeat('b', Segment.SIZE);
    563 
    564     Buffer source = new Buffer();
    565     source.writeUtf8(as);
    566     source.writeUtf8(bs);
    567 
    568     source.copyTo(source, 0, source.size());
    569     assertEquals(as + bs + as + bs, source.readUtf8());
    570   }
    571 
    572   @Test public void copyToEmptySource() throws Exception {
    573     Buffer source = new Buffer();
    574     Buffer target = new Buffer().writeUtf8("aaa");
    575     source.copyTo(target, 0L, 0L);
    576     assertEquals("", source.readUtf8());
    577     assertEquals("aaa", target.readUtf8());
    578   }
    579 
    580   @Test public void copyToEmptyTarget() throws Exception {
    581     Buffer source = new Buffer().writeUtf8("aaa");
    582     Buffer target = new Buffer();
    583     source.copyTo(target, 0L, 3L);
    584     assertEquals("aaa", source.readUtf8());
    585     assertEquals("aaa", target.readUtf8());
    586   }
    587 
    588   /**
    589    * Returns a new buffer containing the data in {@code data}, and a segment
    590    * layout determined by {@code dice}.
    591    */
    592   private Buffer bufferWithRandomSegmentLayout(Random dice, byte[] data) throws IOException {
    593     Buffer result = new Buffer();
    594 
    595     // Writing to result directly will yield packed segments. Instead, write to
    596     // other buffers, then write those buffers to result.
    597     for (int pos = 0, byteCount; pos < data.length; pos += byteCount) {
    598       byteCount = (Segment.SIZE / 2) + dice.nextInt(Segment.SIZE / 2);
    599       if (byteCount > data.length - pos) byteCount = data.length - pos;
    600       int offset = dice.nextInt(Segment.SIZE - byteCount);
    601 
    602       Buffer segment = new Buffer();
    603       segment.write(new byte[offset]);
    604       segment.write(data, pos, byteCount);
    605       segment.skip(offset);
    606 
    607       result.write(segment, byteCount);
    608     }
    609 
    610     return result;
    611   }
    612 }
    613