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.OutputStream; 21import java.math.BigInteger; 22import java.nio.charset.Charset; 23import java.util.Arrays; 24import java.util.List; 25import org.junit.Before; 26import org.junit.Test; 27import org.junit.runner.RunWith; 28import org.junit.runners.Parameterized; 29 30import static java.util.Arrays.asList; 31import static okio.TestUtil.repeat; 32import static okio.Util.UTF_8; 33import static org.junit.Assert.assertEquals; 34import static org.junit.Assert.fail; 35 36@RunWith(Parameterized.class) 37public class BufferedSinkTest { 38 private interface Factory { 39 BufferedSink create(Buffer data); 40 } 41 42 // ANDROID-BEGIN 43 // @Parameterized.Parameters(name = "{0}") 44 @Parameterized.Parameters 45 // ANDROID-END 46 public static List<Object[]> parameters() { 47 return Arrays.asList(new Object[] { 48 new Factory() { 49 @Override public BufferedSink create(Buffer data) { 50 return data; 51 } 52 53 @Override public String toString() { 54 return "Buffer"; 55 } 56 } 57 }, new Object[] { 58 new Factory() { 59 @Override public BufferedSink create(Buffer data) { 60 return new RealBufferedSink(data); 61 } 62 63 @Override public String toString() { 64 return "RealBufferedSink"; 65 } 66 } 67 }); 68 } 69 70 // ANDROID-BEGIN 71 // @Parameterized.Parameter 72 private final Factory factory; 73 public BufferedSinkTest(Factory factory) { 74 this.factory = factory; 75 } 76 // ANDROID-END 77 78 private Buffer data; 79 private BufferedSink sink; 80 81 @Before public void setUp() { 82 data = new Buffer(); 83 sink = factory.create(data); 84 } 85 86 @Test public void writeNothing() throws IOException { 87 sink.writeUtf8(""); 88 sink.flush(); 89 assertEquals(0, data.size()); 90 } 91 92 @Test public void writeBytes() throws Exception { 93 sink.writeByte(0xab); 94 sink.writeByte(0xcd); 95 sink.flush(); 96 assertEquals("Buffer[size=2 data=abcd]", data.toString()); 97 } 98 99 @Test public void writeLastByteInSegment() throws Exception { 100 sink.writeUtf8(repeat('a', Segment.SIZE - 1)); 101 sink.writeByte(0x20); 102 sink.writeByte(0x21); 103 sink.flush(); 104 assertEquals(asList(Segment.SIZE, 1), data.segmentSizes()); 105 assertEquals(repeat('a', Segment.SIZE - 1), data.readUtf8(Segment.SIZE - 1)); 106 assertEquals("Buffer[size=2 data=2021]", data.toString()); 107 } 108 109 @Test public void writeShort() throws Exception { 110 sink.writeShort(0xabcd); 111 sink.writeShort(0x4321); 112 sink.flush(); 113 assertEquals("Buffer[size=4 data=abcd4321]", data.toString()); 114 } 115 116 @Test public void writeShortLe() throws Exception { 117 sink.writeShortLe(0xabcd); 118 sink.writeShortLe(0x4321); 119 sink.flush(); 120 assertEquals("Buffer[size=4 data=cdab2143]", data.toString()); 121 } 122 123 @Test public void writeInt() throws Exception { 124 sink.writeInt(0xabcdef01); 125 sink.writeInt(0x87654321); 126 sink.flush(); 127 assertEquals("Buffer[size=8 data=abcdef0187654321]", data.toString()); 128 } 129 130 @Test public void writeLastIntegerInSegment() throws Exception { 131 sink.writeUtf8(repeat('a', Segment.SIZE - 4)); 132 sink.writeInt(0xabcdef01); 133 sink.writeInt(0x87654321); 134 sink.flush(); 135 assertEquals(asList(Segment.SIZE, 4), data.segmentSizes()); 136 assertEquals(repeat('a', Segment.SIZE - 4), data.readUtf8(Segment.SIZE - 4)); 137 assertEquals("Buffer[size=8 data=abcdef0187654321]", data.toString()); 138 } 139 140 @Test public void writeIntegerDoesNotQuiteFitInSegment() throws Exception { 141 sink.writeUtf8(repeat('a', Segment.SIZE - 3)); 142 sink.writeInt(0xabcdef01); 143 sink.writeInt(0x87654321); 144 sink.flush(); 145 assertEquals(asList(Segment.SIZE - 3, 8), data.segmentSizes()); 146 assertEquals(repeat('a', Segment.SIZE - 3), data.readUtf8(Segment.SIZE - 3)); 147 assertEquals("Buffer[size=8 data=abcdef0187654321]", data.toString()); 148 } 149 150 @Test public void writeIntLe() throws Exception { 151 sink.writeIntLe(0xabcdef01); 152 sink.writeIntLe(0x87654321); 153 sink.flush(); 154 assertEquals("Buffer[size=8 data=01efcdab21436587]", data.toString()); 155 } 156 157 @Test public void writeLong() throws Exception { 158 sink.writeLong(0xabcdef0187654321L); 159 sink.writeLong(0xcafebabeb0b15c00L); 160 sink.flush(); 161 assertEquals("Buffer[size=16 data=abcdef0187654321cafebabeb0b15c00]", data.toString()); 162 } 163 164 @Test public void writeLongLe() throws Exception { 165 sink.writeLongLe(0xabcdef0187654321L); 166 sink.writeLongLe(0xcafebabeb0b15c00L); 167 sink.flush(); 168 assertEquals("Buffer[size=16 data=2143658701efcdab005cb1b0bebafeca]", data.toString()); 169 } 170 171 @Test public void writeStringUtf8() throws IOException { 172 sink.writeUtf8("təˈranəˌsôr"); 173 sink.flush(); 174 assertEquals(ByteString.decodeHex("74c999cb8872616ec999cb8c73c3b472"), data.readByteString()); 175 } 176 177 @Test public void writeSubstringUtf8() throws IOException { 178 sink.writeUtf8("təˈranəˌsôr", 3, 7); 179 sink.flush(); 180 assertEquals(ByteString.decodeHex("72616ec999"), data.readByteString()); 181 } 182 183 @Test public void writeStringWithCharset() throws IOException { 184 sink.writeString("təˈranəˌsôr", Charset.forName("utf-32be")); 185 sink.flush(); 186 assertEquals(ByteString.decodeHex("0000007400000259000002c800000072000000610000006e00000259" 187 + "000002cc00000073000000f400000072"), data.readByteString()); 188 } 189 190 @Test public void writeSubstringWithCharset() throws IOException { 191 sink.writeString("təˈranəˌsôr", 3, 7, Charset.forName("utf-32be")); 192 sink.flush(); 193 assertEquals(ByteString.decodeHex("00000072000000610000006e00000259"), data.readByteString()); 194 } 195 196 @Test public void writeAll() throws Exception { 197 Buffer source = new Buffer().writeUtf8("abcdef"); 198 199 assertEquals(6, sink.writeAll(source)); 200 assertEquals(0, source.size()); 201 sink.flush(); 202 assertEquals("abcdef", data.readUtf8()); 203 } 204 205 @Test public void writeSource() throws Exception { 206 Buffer source = new Buffer().writeUtf8("abcdef"); 207 208 // Force resolution of the Source method overload. 209 sink.write((Source) source, 4); 210 sink.flush(); 211 assertEquals("abcd", data.readUtf8()); 212 assertEquals("ef", source.readUtf8()); 213 } 214 215 @Test public void writeSourceReadsFully() throws Exception { 216 Source source = new ForwardingSource(new Buffer()) { 217 @Override public long read(Buffer sink, long byteCount) throws IOException { 218 sink.writeUtf8("abcd"); 219 return 4; 220 } 221 }; 222 223 sink.write(source, 8); 224 sink.flush(); 225 assertEquals("abcdabcd", data.readUtf8()); 226 } 227 228 @Test public void writeSourcePropagatesEof() throws IOException { 229 Source source = new Buffer().writeUtf8("abcd"); 230 231 try { 232 sink.write(source, 8); 233 fail(); 234 } catch (EOFException expected) { 235 } 236 237 // Ensure that whatever was available was correctly written. 238 sink.flush(); 239 assertEquals("abcd", data.readUtf8()); 240 } 241 242 @Test public void writeSourceWithZeroIsNoOp() throws IOException { 243 // This test ensures that a zero byte count never calls through to read the source. It may be 244 // tied to something like a socket which will potentially block trying to read a segment when 245 // ultimately we don't want any data. 246 Source source = new ForwardingSource(new Buffer()) { 247 @Override public long read(Buffer sink, long byteCount) throws IOException { 248 throw new AssertionError(); 249 } 250 }; 251 sink.write(source, 0); 252 assertEquals(0, data.size()); 253 } 254 255 @Test public void writeAllExhausted() throws Exception { 256 Buffer source = new Buffer(); 257 assertEquals(0, sink.writeAll(source)); 258 assertEquals(0, source.size()); 259 } 260 261 @Test public void closeEmitsBufferedBytes() throws IOException { 262 sink.writeByte('a'); 263 sink.close(); 264 assertEquals('a', data.readByte()); 265 } 266 267 @Test public void outputStream() throws Exception { 268 OutputStream out = sink.outputStream(); 269 out.write('a'); 270 out.write(repeat('b', 9998).getBytes(UTF_8)); 271 out.write('c'); 272 out.flush(); 273 assertEquals("a" + repeat('b', 9998) + "c", data.readUtf8()); 274 } 275 276 @Test public void outputStreamBounds() throws Exception { 277 OutputStream out = sink.outputStream(); 278 try { 279 out.write(new byte[100], 50, 51); 280 fail(); 281 } catch (ArrayIndexOutOfBoundsException expected) { 282 } 283 } 284 285 @Test public void longDecimalString() throws IOException { 286 assertLongDecimalString(0); 287 assertLongDecimalString(Long.MIN_VALUE); 288 assertLongDecimalString(Long.MAX_VALUE); 289 290 for (int i = 1; i < 20; i++) { 291 long value = BigInteger.valueOf(10L).pow(i).longValue(); 292 assertLongDecimalString(value - 1); 293 assertLongDecimalString(value); 294 } 295 } 296 297 private void assertLongDecimalString(long value) throws IOException { 298 sink.writeDecimalLong(value).writeUtf8("zzz").flush(); 299 String expected = Long.toString(value) + "zzz"; 300 String actual = data.readUtf8(); 301 assertEquals(value + " expected " + expected + " but was " + actual, actual, expected); 302 } 303 304 @Test public void longHexString() throws IOException { 305 assertLongHexString(0); 306 assertLongHexString(Long.MIN_VALUE); 307 assertLongHexString(Long.MAX_VALUE); 308 309 for (int i = 0; i < 16; i++) { 310 assertLongHexString((1 << i) - 1); 311 assertLongHexString(1 << i); 312 } 313 } 314 315 private void assertLongHexString(long value) throws IOException { 316 sink.writeHexadecimalUnsignedLong(value).writeUtf8("zzz").flush(); 317 String expected = String.format("%x", value) + "zzz"; 318 String actual = data.readUtf8(); 319 assertEquals(value + " expected " + expected + " but was " + actual, actual, expected); 320 } 321} 322