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.EOFException;
19import java.io.IOException;
20import java.io.InputStream;
21import org.junit.Test;
22
23import static okio.TestUtil.repeat;
24import static okio.Util.UTF_8;
25import static org.junit.Assert.assertEquals;
26import static org.junit.Assert.fail;
27
28/**
29 * Tests solely for the behavior of RealBufferedSource's implementation. For generic
30 * BufferedSource behavior use BufferedSourceTest.
31 */
32public final class RealBufferedSourceTest {
33  @Test public void inputStreamTracksSegments() throws Exception {
34    Buffer source = new Buffer();
35    source.writeUtf8("a");
36    source.writeUtf8(repeat('b', Segment.SIZE));
37    source.writeUtf8("c");
38
39    InputStream in = new RealBufferedSource(source).inputStream();
40    assertEquals(0, in.available());
41    assertEquals(Segment.SIZE + 2, source.size());
42
43    // Reading one byte buffers a full segment.
44    assertEquals('a', in.read());
45    assertEquals(Segment.SIZE - 1, in.available());
46    assertEquals(2, source.size());
47
48    // Reading as much as possible reads the rest of that buffered segment.
49    byte[] data = new byte[Segment.SIZE * 2];
50    assertEquals(Segment.SIZE - 1, in.read(data, 0, data.length));
51    assertEquals(repeat('b', Segment.SIZE - 1), new String(data, 0, Segment.SIZE - 1, UTF_8));
52    assertEquals(2, source.size());
53
54    // Continuing to read buffers the next segment.
55    assertEquals('b', in.read());
56    assertEquals(1, in.available());
57    assertEquals(0, source.size());
58
59    // Continuing to read reads from the buffer.
60    assertEquals('c', in.read());
61    assertEquals(0, in.available());
62    assertEquals(0, source.size());
63
64    // Once we've exhausted the source, we're done.
65    assertEquals(-1, in.read());
66    assertEquals(0, source.size());
67  }
68
69  @Test public void inputStreamCloses() throws Exception {
70    RealBufferedSource source = new RealBufferedSource(new Buffer());
71    InputStream in = source.inputStream();
72    in.close();
73    try {
74      source.require(1);
75      fail();
76    } catch (IllegalStateException e) {
77      assertEquals("closed", e.getMessage());
78    }
79  }
80
81  @Test public void requireTracksBufferFirst() throws Exception {
82    Buffer source = new Buffer();
83    source.writeUtf8("bb");
84
85    BufferedSource bufferedSource = new RealBufferedSource(source);
86    bufferedSource.buffer().writeUtf8("aa");
87
88    bufferedSource.require(2);
89    assertEquals(2, bufferedSource.buffer().size());
90    assertEquals(2, source.size());
91  }
92
93  @Test public void requireIncludesBufferBytes() throws Exception {
94    Buffer source = new Buffer();
95    source.writeUtf8("b");
96
97    BufferedSource bufferedSource = new RealBufferedSource(source);
98    bufferedSource.buffer().writeUtf8("a");
99
100    bufferedSource.require(2);
101    assertEquals("ab", bufferedSource.buffer().readUtf8(2));
102  }
103
104  @Test public void requireInsufficientData() throws Exception {
105    Buffer source = new Buffer();
106    source.writeUtf8("a");
107
108    BufferedSource bufferedSource = new RealBufferedSource(source);
109
110    try {
111      bufferedSource.require(2);
112      fail();
113    } catch (EOFException expected) {
114    }
115  }
116
117  @Test public void requireReadsOneSegmentAtATime() throws Exception {
118    Buffer source = new Buffer();
119    source.writeUtf8(repeat('a', Segment.SIZE));
120    source.writeUtf8(repeat('b', Segment.SIZE));
121
122    BufferedSource bufferedSource = new RealBufferedSource(source);
123
124    bufferedSource.require(2);
125    assertEquals(Segment.SIZE, source.size());
126    assertEquals(Segment.SIZE, bufferedSource.buffer().size());
127  }
128
129  @Test public void skipReadsOneSegmentAtATime() throws Exception {
130    Buffer source = new Buffer();
131    source.writeUtf8(repeat('a', Segment.SIZE));
132    source.writeUtf8(repeat('b', Segment.SIZE));
133    BufferedSource bufferedSource = new RealBufferedSource(source);
134    bufferedSource.skip(2);
135    assertEquals(Segment.SIZE, source.size());
136    assertEquals(Segment.SIZE - 2, bufferedSource.buffer().size());
137  }
138
139  @Test public void skipTracksBufferFirst() throws Exception {
140    Buffer source = new Buffer();
141    source.writeUtf8("bb");
142
143    BufferedSource bufferedSource = new RealBufferedSource(source);
144    bufferedSource.buffer().writeUtf8("aa");
145
146    bufferedSource.skip(2);
147    assertEquals(0, bufferedSource.buffer().size());
148    assertEquals(2, source.size());
149  }
150
151  @Test public void operationsAfterClose() throws IOException {
152    Buffer source = new Buffer();
153    BufferedSource bufferedSource = new RealBufferedSource(source);
154    bufferedSource.close();
155
156    // Test a sample set of methods.
157    try {
158      bufferedSource.indexOf((byte) 1);
159      fail();
160    } catch (IllegalStateException expected) {
161    }
162
163    try {
164      bufferedSource.skip(1);
165      fail();
166    } catch (IllegalStateException expected) {
167    }
168
169    try {
170      bufferedSource.readByte();
171      fail();
172    } catch (IllegalStateException expected) {
173    }
174
175    try {
176      bufferedSource.readByteString(10);
177      fail();
178    } catch (IllegalStateException expected) {
179    }
180
181    // Test a sample set of methods on the InputStream.
182    InputStream is = bufferedSource.inputStream();
183    try {
184      is.read();
185      fail();
186    } catch (IOException expected) {
187    }
188
189    try {
190      is.read(new byte[10]);
191      fail();
192    } catch (IOException expected) {
193    }
194  }
195
196  /**
197   * We don't want readAll to buffer an unbounded amount of data. Instead it
198   * should buffer a segment, write it, and repeat.
199   */
200  @Test public void readAllReadsOneSegmentAtATime() throws IOException {
201    Buffer write1 = new Buffer().writeUtf8(TestUtil.repeat('a', Segment.SIZE));
202    Buffer write2 = new Buffer().writeUtf8(TestUtil.repeat('b', Segment.SIZE));
203    Buffer write3 = new Buffer().writeUtf8(TestUtil.repeat('c', Segment.SIZE));
204
205    Buffer source = new Buffer().writeUtf8(""
206        + TestUtil.repeat('a', Segment.SIZE)
207        + TestUtil.repeat('b', Segment.SIZE)
208        + TestUtil.repeat('c', Segment.SIZE));
209
210    MockSink mockSink = new MockSink();
211    BufferedSource bufferedSource = Okio.buffer((Source) source);
212    assertEquals(Segment.SIZE * 3, bufferedSource.readAll(mockSink));
213    mockSink.assertLog(
214        "write(" + write1 + ", " + write1.size() + ")",
215        "write(" + write2 + ", " + write2.size() + ")",
216        "write(" + write3 + ", " + write3.size() + ")");
217  }
218}
219