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 */
16package okio;
17
18import java.io.ByteArrayInputStream;
19import java.io.ByteArrayOutputStream;
20import java.io.IOException;
21import java.io.InputStream;
22import java.util.Arrays;
23import java.util.List;
24import java.util.Random;
25import org.junit.Test;
26
27import static java.util.Arrays.asList;
28import static okio.TestUtil.repeat;
29import static okio.Util.UTF_8;
30import static org.junit.Assert.assertEquals;
31import static org.junit.Assert.assertFalse;
32import static org.junit.Assert.assertTrue;
33import 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 */
39public 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