| /* |
| * Copyright (C) 2014 Square, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package okio; |
| |
| import java.io.EOFException; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.math.BigInteger; |
| import java.nio.ByteBuffer; |
| import java.nio.charset.Charset; |
| import java.util.Arrays; |
| import java.util.List; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.Parameterized; |
| import org.junit.runners.Parameterized.Parameter; |
| import org.junit.runners.Parameterized.Parameters; |
| |
| import static java.util.Arrays.asList; |
| import static kotlin.text.Charsets.UTF_8; |
| import static kotlin.text.StringsKt.repeat; |
| import static okio.TestUtil.SEGMENT_SIZE; |
| import static okio.TestUtil.segmentSizes; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.fail; |
| |
| @RunWith(Parameterized.class) |
| public final class BufferedSinkTest { |
| private interface Factory { |
| Factory BUFFER = new Factory() { |
| @Override public BufferedSink create(Buffer data) { |
| return data; |
| } |
| |
| @Override public String toString() { |
| return "Buffer"; |
| } |
| }; |
| |
| Factory REAL_BUFFERED_SINK = new Factory() { |
| @Override public BufferedSink create(Buffer data) { |
| return Okio.buffer((Sink) data); |
| } |
| |
| @Override public String toString() { |
| return "RealBufferedSink"; |
| } |
| }; |
| |
| BufferedSink create(Buffer data); |
| } |
| |
| @Parameters(name = "{0}") |
| public static List<Object[]> parameters() { |
| return Arrays.asList( |
| new Object[] {Factory.BUFFER}, |
| new Object[] {Factory.REAL_BUFFERED_SINK}); |
| } |
| |
| @Parameter public Factory factory; |
| private Buffer data; |
| private BufferedSink sink; |
| |
| @Before public void setUp() { |
| data = new Buffer(); |
| sink = factory.create(data); |
| } |
| |
| @Test public void writeNothing() throws IOException { |
| sink.writeUtf8(""); |
| sink.flush(); |
| assertEquals(0, data.size()); |
| } |
| |
| @Test public void writeBytes() throws Exception { |
| sink.writeByte(0xab); |
| sink.writeByte(0xcd); |
| sink.flush(); |
| assertEquals("[hex=abcd]", data.toString()); |
| } |
| |
| @Test public void writeLastByteInSegment() throws Exception { |
| sink.writeUtf8(repeat("a", SEGMENT_SIZE - 1)); |
| sink.writeByte(0x20); |
| sink.writeByte(0x21); |
| sink.flush(); |
| assertEquals(asList(SEGMENT_SIZE, 1), segmentSizes(data)); |
| assertEquals(repeat("a", SEGMENT_SIZE - 1), data.readUtf8(SEGMENT_SIZE - 1)); |
| assertEquals("[text= !]", data.toString()); |
| } |
| |
| @Test public void writeShort() throws Exception { |
| sink.writeShort(0xabcd); |
| sink.writeShort(0x4321); |
| sink.flush(); |
| assertEquals("[hex=abcd4321]", data.toString()); |
| } |
| |
| @Test public void writeShortLe() throws Exception { |
| sink.writeShortLe(0xcdab); |
| sink.writeShortLe(0x2143); |
| sink.flush(); |
| assertEquals("[hex=abcd4321]", data.toString()); |
| } |
| |
| @Test public void writeInt() throws Exception { |
| sink.writeInt(0xabcdef01); |
| sink.writeInt(0x87654321); |
| sink.flush(); |
| assertEquals("[hex=abcdef0187654321]", data.toString()); |
| } |
| |
| @Test public void writeLastIntegerInSegment() throws Exception { |
| sink.writeUtf8(repeat("a", SEGMENT_SIZE - 4)); |
| sink.writeInt(0xabcdef01); |
| sink.writeInt(0x87654321); |
| sink.flush(); |
| assertEquals(asList(SEGMENT_SIZE, 4), segmentSizes(data)); |
| assertEquals(repeat("a", SEGMENT_SIZE - 4), data.readUtf8(SEGMENT_SIZE - 4)); |
| assertEquals("[hex=abcdef0187654321]", data.toString()); |
| } |
| |
| @Test public void writeIntegerDoesNotQuiteFitInSegment() throws Exception { |
| sink.writeUtf8(repeat("a", SEGMENT_SIZE - 3)); |
| sink.writeInt(0xabcdef01); |
| sink.writeInt(0x87654321); |
| sink.flush(); |
| assertEquals(asList(SEGMENT_SIZE - 3, 8), segmentSizes(data)); |
| assertEquals(repeat("a", SEGMENT_SIZE - 3), data.readUtf8(SEGMENT_SIZE - 3)); |
| assertEquals("[hex=abcdef0187654321]", data.toString()); |
| } |
| |
| @Test public void writeIntLe() throws Exception { |
| sink.writeIntLe(0xabcdef01); |
| sink.writeIntLe(0x87654321); |
| sink.flush(); |
| assertEquals("[hex=01efcdab21436587]", data.toString()); |
| } |
| |
| @Test public void writeLong() throws Exception { |
| sink.writeLong(0xabcdef0187654321L); |
| sink.writeLong(0xcafebabeb0b15c00L); |
| sink.flush(); |
| assertEquals("[hex=abcdef0187654321cafebabeb0b15c00]", data.toString()); |
| } |
| |
| @Test public void writeLongLe() throws Exception { |
| sink.writeLongLe(0xabcdef0187654321L); |
| sink.writeLongLe(0xcafebabeb0b15c00L); |
| sink.flush(); |
| assertEquals("[hex=2143658701efcdab005cb1b0bebafeca]", data.toString()); |
| } |
| |
| @Test public void writeByteString() throws IOException { |
| sink.write(ByteString.encodeUtf8("təˈranəˌsôr")); |
| sink.flush(); |
| assertEquals(ByteString.decodeHex("74c999cb8872616ec999cb8c73c3b472"), data.readByteString()); |
| } |
| |
| @Test public void writeByteStringOffset() throws IOException { |
| sink.write(ByteString.encodeUtf8("təˈranəˌsôr"), 5, 5); |
| sink.flush(); |
| assertEquals(ByteString.decodeHex("72616ec999"), data.readByteString()); |
| } |
| |
| @Test public void writeSegmentedByteString() throws IOException { |
| sink.write(new Buffer().write(ByteString.encodeUtf8("təˈranəˌsôr")).snapshot()); |
| sink.flush(); |
| assertEquals(ByteString.decodeHex("74c999cb8872616ec999cb8c73c3b472"), data.readByteString()); |
| } |
| |
| @Test public void writeSegmentedByteStringOffset() throws IOException { |
| sink.write(new Buffer().write(ByteString.encodeUtf8("təˈranəˌsôr")).snapshot(), 5, 5); |
| sink.flush(); |
| assertEquals(ByteString.decodeHex("72616ec999"), data.readByteString()); |
| } |
| |
| @Test public void writeStringUtf8() throws IOException { |
| sink.writeUtf8("təˈranəˌsôr"); |
| sink.flush(); |
| assertEquals(ByteString.decodeHex("74c999cb8872616ec999cb8c73c3b472"), data.readByteString()); |
| } |
| |
| @Test public void writeSubstringUtf8() throws IOException { |
| sink.writeUtf8("təˈranəˌsôr", 3, 7); |
| sink.flush(); |
| assertEquals(ByteString.decodeHex("72616ec999"), data.readByteString()); |
| } |
| |
| @Test public void writeStringWithCharset() throws IOException { |
| sink.writeString("təˈranəˌsôr", Charset.forName("utf-32be")); |
| sink.flush(); |
| assertEquals(ByteString.decodeHex("0000007400000259000002c800000072000000610000006e00000259" |
| + "000002cc00000073000000f400000072"), data.readByteString()); |
| } |
| |
| @Test public void writeSubstringWithCharset() throws IOException { |
| sink.writeString("təˈranəˌsôr", 3, 7, Charset.forName("utf-32be")); |
| sink.flush(); |
| assertEquals(ByteString.decodeHex("00000072000000610000006e00000259"), data.readByteString()); |
| } |
| |
| @Test public void writeUtf8SubstringWithCharset() throws IOException { |
| sink.writeString("təˈranəˌsôr", 3, 7, Charset.forName("utf-8")); |
| sink.flush(); |
| assertEquals(ByteString.encodeUtf8("ranə"), data.readByteString()); |
| } |
| |
| @Test public void writeAll() throws Exception { |
| Buffer source = new Buffer().writeUtf8("abcdef"); |
| |
| assertEquals(6, sink.writeAll(source)); |
| assertEquals(0, source.size()); |
| sink.flush(); |
| assertEquals("abcdef", data.readUtf8()); |
| } |
| |
| @Test public void writeSource() throws Exception { |
| Buffer source = new Buffer().writeUtf8("abcdef"); |
| |
| // Force resolution of the Source method overload. |
| sink.write((Source) source, 4); |
| sink.flush(); |
| assertEquals("abcd", data.readUtf8()); |
| assertEquals("ef", source.readUtf8()); |
| } |
| |
| @Test public void writeSourceReadsFully() throws Exception { |
| Source source = new ForwardingSource(new Buffer()) { |
| @Override public long read(Buffer sink, long byteCount) throws IOException { |
| sink.writeUtf8("abcd"); |
| return 4; |
| } |
| }; |
| |
| sink.write(source, 8); |
| sink.flush(); |
| assertEquals("abcdabcd", data.readUtf8()); |
| } |
| |
| @Test public void writeSourcePropagatesEof() throws IOException { |
| Source source = new Buffer().writeUtf8("abcd"); |
| |
| try { |
| sink.write(source, 8); |
| fail(); |
| } catch (EOFException expected) { |
| } |
| |
| // Ensure that whatever was available was correctly written. |
| sink.flush(); |
| assertEquals("abcd", data.readUtf8()); |
| } |
| |
| @Test public void writeSourceWithZeroIsNoOp() throws IOException { |
| // This test ensures that a zero byte count never calls through to read the source. It may be |
| // tied to something like a socket which will potentially block trying to read a segment when |
| // ultimately we don't want any data. |
| Source source = new ForwardingSource(new Buffer()) { |
| @Override public long read(Buffer sink, long byteCount) throws IOException { |
| throw new AssertionError(); |
| } |
| }; |
| sink.write(source, 0); |
| assertEquals(0, data.size()); |
| } |
| |
| @Test public void writeAllExhausted() throws Exception { |
| Buffer source = new Buffer(); |
| assertEquals(0, sink.writeAll(source)); |
| assertEquals(0, source.size()); |
| } |
| |
| @Test public void closeEmitsBufferedBytes() throws IOException { |
| sink.writeByte('a'); |
| sink.close(); |
| assertEquals('a', data.readByte()); |
| } |
| |
| @Test public void outputStream() throws Exception { |
| OutputStream out = sink.outputStream(); |
| out.write('a'); |
| out.write(repeat("b", 9998).getBytes(UTF_8)); |
| out.write('c'); |
| out.flush(); |
| assertEquals("a" + repeat("b", 9998) + "c", data.readUtf8()); |
| } |
| |
| @Test public void outputStreamBounds() throws Exception { |
| OutputStream out = sink.outputStream(); |
| try { |
| out.write(new byte[100], 50, 51); |
| fail(); |
| } catch (ArrayIndexOutOfBoundsException expected) { |
| } |
| } |
| |
| @Test public void longDecimalString() throws IOException { |
| assertLongDecimalString(0); |
| assertLongDecimalString(Long.MIN_VALUE); |
| assertLongDecimalString(Long.MAX_VALUE); |
| |
| for (int i = 1; i < 20; i++) { |
| long value = BigInteger.valueOf(10L).pow(i).longValue(); |
| assertLongDecimalString(value - 1); |
| assertLongDecimalString(value); |
| } |
| } |
| |
| private void assertLongDecimalString(long value) throws IOException { |
| sink.writeDecimalLong(value).writeUtf8("zzz").flush(); |
| String expected = Long.toString(value) + "zzz"; |
| String actual = data.readUtf8(); |
| assertEquals(value + " expected " + expected + " but was " + actual, actual, expected); |
| } |
| |
| @Test public void longHexString() throws IOException { |
| assertLongHexString(0); |
| assertLongHexString(Long.MIN_VALUE); |
| assertLongHexString(Long.MAX_VALUE); |
| |
| for (int i = 0; i < 63; i++) { |
| assertLongHexString((1L << i) - 1); |
| assertLongHexString(1L << i); |
| } |
| } |
| |
| @Test public void writeNioBuffer() throws Exception { |
| String expected = "abcdefg"; |
| |
| ByteBuffer nioByteBuffer = ByteBuffer.allocate(1024); |
| nioByteBuffer.put("abcdefg".getBytes(UTF_8)); |
| nioByteBuffer.flip(); |
| |
| int byteCount = sink.write(nioByteBuffer); |
| assertEquals(expected.length(), byteCount); |
| assertEquals(expected.length(), nioByteBuffer.position()); |
| assertEquals(expected.length(), nioByteBuffer.limit()); |
| |
| sink.flush(); |
| assertEquals(expected, data.readUtf8()); |
| } |
| |
| @Test public void writeLargeNioBufferWritesAllData() throws Exception { |
| String expected = repeat("a", SEGMENT_SIZE * 3); |
| |
| ByteBuffer nioByteBuffer = ByteBuffer.allocate(SEGMENT_SIZE * 4); |
| nioByteBuffer.put(repeat("a", SEGMENT_SIZE * 3).getBytes(UTF_8)); |
| nioByteBuffer.flip(); |
| |
| int byteCount = sink.write(nioByteBuffer); |
| assertEquals(expected.length(), byteCount); |
| assertEquals(expected.length(), nioByteBuffer.position()); |
| assertEquals(expected.length(), nioByteBuffer.limit()); |
| |
| sink.flush(); |
| assertEquals(expected, data.readUtf8()); |
| } |
| |
| private void assertLongHexString(long value) throws IOException { |
| sink.writeHexadecimalUnsignedLong(value).writeUtf8("zzz").flush(); |
| String expected = String.format("%x", value) + "zzz"; |
| String actual = data.readUtf8(); |
| assertEquals(value + " expected " + expected + " but was " + actual, actual, expected); |
| } |
| } |