| /* |
| * Copyright (C) 2019 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 okio.ByteString.Companion.decodeHex |
| import okio.ByteString.Companion.encodeUtf8 |
| import kotlin.math.pow |
| import kotlin.test.Test |
| import kotlin.test.assertEquals |
| import kotlin.test.assertFailsWith |
| |
| class BufferSinkTest : AbstractBufferedSinkTest(BufferedSinkFactory.BUFFER) |
| class RealBufferedSinkTest : AbstractBufferedSinkTest(BufferedSinkFactory.REAL_BUFFERED_SINK) |
| |
| abstract class AbstractBufferedSinkTest internal constructor( |
| factory: BufferedSinkFactory |
| ) { |
| private val data: Buffer = Buffer() |
| private val sink: BufferedSink = factory.create(data) |
| |
| @Test fun writeNothing() { |
| sink.writeUtf8("") |
| sink.flush() |
| assertEquals(0, data.size) |
| } |
| |
| @Test fun writeBytes() { |
| sink.writeByte(0xab) |
| sink.writeByte(0xcd) |
| sink.flush() |
| assertEquals("[hex=abcd]", data.toString()) |
| } |
| |
| @Test fun writeLastByteInSegment() { |
| sink.writeUtf8("a".repeat(Segment.SIZE - 1)) |
| sink.writeByte(0x20) |
| sink.writeByte(0x21) |
| sink.flush() |
| assertEquals(listOf(Segment.SIZE, 1), segmentSizes(data)) |
| assertEquals("a".repeat(Segment.SIZE - 1), data.readUtf8(Segment.SIZE - 1L)) |
| assertEquals("[text= !]", data.toString()) |
| } |
| |
| @Test fun writeShort() { |
| sink.writeShort(0xabcd) |
| sink.writeShort(0x4321) |
| sink.flush() |
| assertEquals("[hex=abcd4321]", data.toString()) |
| } |
| |
| @Test fun writeShortLe() { |
| sink.writeShortLe(0xcdab) |
| sink.writeShortLe(0x2143) |
| sink.flush() |
| assertEquals("[hex=abcd4321]", data.toString()) |
| } |
| |
| @Test fun writeInt() { |
| sink.writeInt(-0x543210ff) |
| sink.writeInt(-0x789abcdf) |
| sink.flush() |
| assertEquals("[hex=abcdef0187654321]", data.toString()) |
| } |
| |
| @Test fun writeLastIntegerInSegment() { |
| sink.writeUtf8("a".repeat(Segment.SIZE - 4)) |
| sink.writeInt(-0x543210ff) |
| sink.writeInt(-0x789abcdf) |
| sink.flush() |
| assertEquals(listOf(Segment.SIZE, 4), segmentSizes(data)) |
| assertEquals("a".repeat(Segment.SIZE - 4), data.readUtf8(Segment.SIZE - 4L)) |
| assertEquals("[hex=abcdef0187654321]", data.toString()) |
| } |
| |
| @Test fun writeIntegerDoesNotQuiteFitInSegment() { |
| sink.writeUtf8("a".repeat(Segment.SIZE - 3)) |
| sink.writeInt(-0x543210ff) |
| sink.writeInt(-0x789abcdf) |
| sink.flush() |
| assertEquals(listOf(Segment.SIZE - 3, 8), segmentSizes(data)) |
| assertEquals("a".repeat(Segment.SIZE - 3), data.readUtf8(Segment.SIZE - 3L)) |
| assertEquals("[hex=abcdef0187654321]", data.toString()) |
| } |
| |
| @Test fun writeIntLe() { |
| sink.writeIntLe(-0x543210ff) |
| sink.writeIntLe(-0x789abcdf) |
| sink.flush() |
| assertEquals("[hex=01efcdab21436587]", data.toString()) |
| } |
| |
| @Test fun writeLong() { |
| sink.writeLong(-0x543210fe789abcdfL) |
| sink.writeLong(-0x350145414f4ea400L) |
| sink.flush() |
| assertEquals("[hex=abcdef0187654321cafebabeb0b15c00]", data.toString()) |
| } |
| |
| @Test fun writeLongLe() { |
| sink.writeLongLe(-0x543210fe789abcdfL) |
| sink.writeLongLe(-0x350145414f4ea400L) |
| sink.flush() |
| assertEquals("[hex=2143658701efcdab005cb1b0bebafeca]", data.toString()) |
| } |
| |
| @Test fun writeByteString() { |
| sink.write("təˈranəˌsôr".encodeUtf8()) |
| sink.flush() |
| assertEquals("74c999cb8872616ec999cb8c73c3b472".decodeHex(), data.readByteString()) |
| } |
| |
| @Test fun writeByteStringOffset() { |
| sink.write("təˈranəˌsôr".encodeUtf8(), 5, 5) |
| sink.flush() |
| assertEquals("72616ec999".decodeHex(), data.readByteString()) |
| } |
| |
| @Test fun writeSegmentedByteString() { |
| sink.write(Buffer().write("təˈranəˌsôr".encodeUtf8()).snapshot()) |
| sink.flush() |
| assertEquals("74c999cb8872616ec999cb8c73c3b472".decodeHex(), data.readByteString()) |
| } |
| |
| @Test fun writeSegmentedByteStringOffset() { |
| sink.write(Buffer().write("təˈranəˌsôr".encodeUtf8()).snapshot(), 5, 5) |
| sink.flush() |
| assertEquals("72616ec999".decodeHex(), data.readByteString()) |
| } |
| |
| @Test fun writeStringUtf8() { |
| sink.writeUtf8("təˈranəˌsôr") |
| sink.flush() |
| assertEquals("74c999cb8872616ec999cb8c73c3b472".decodeHex(), data.readByteString()) |
| } |
| |
| @Test fun writeSubstringUtf8() { |
| sink.writeUtf8("təˈranəˌsôr", 3, 7) |
| sink.flush() |
| assertEquals("72616ec999".decodeHex(), data.readByteString()) |
| } |
| |
| @Test fun writeAll() { |
| val source = Buffer().writeUtf8("abcdef") |
| |
| assertEquals(6, sink.writeAll(source)) |
| assertEquals(0, source.size) |
| sink.flush() |
| assertEquals("abcdef", data.readUtf8()) |
| } |
| |
| @Test fun writeSource() { |
| val source = Buffer().writeUtf8("abcdef") |
| |
| // Force resolution of the Source method overload. |
| sink.write(source as Source, 4) |
| sink.flush() |
| assertEquals("abcd", data.readUtf8()) |
| assertEquals("ef", source.readUtf8()) |
| } |
| |
| @Test fun writeSourceReadsFully() { |
| val source = object : Source by Buffer() { |
| override fun read(sink: Buffer, byteCount: Long): Long { |
| sink.writeUtf8("abcd") |
| return 4 |
| } |
| } |
| |
| sink.write(source, 8) |
| sink.flush() |
| assertEquals("abcdabcd", data.readUtf8()) |
| } |
| |
| @Test fun writeSourcePropagatesEof() { |
| val source: Source = Buffer().writeUtf8("abcd") |
| |
| assertFailsWith<EOFException> { |
| sink.write(source, 8) |
| } |
| |
| // Ensure that whatever was available was correctly written. |
| sink.flush() |
| assertEquals("abcd", data.readUtf8()) |
| } |
| |
| @Test fun writeSourceWithZeroIsNoOp() { |
| // 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. |
| val source = object : Source by Buffer() { |
| override fun read(sink: Buffer, byteCount: Long): Long { |
| throw AssertionError() |
| } |
| } |
| sink.write(source, 0) |
| assertEquals(0, data.size) |
| } |
| |
| @Test fun writeAllExhausted() { |
| val source = Buffer() |
| assertEquals(0, sink.writeAll(source)) |
| assertEquals(0, source.size) |
| } |
| |
| @Test fun closeEmitsBufferedBytes() { |
| sink.writeByte('a'.toInt()) |
| sink.close() |
| assertEquals('a', data.readByte().toChar()) |
| } |
| |
| @Test fun longDecimalString() { |
| assertLongDecimalString(0) |
| assertLongDecimalString(Long.MIN_VALUE) |
| assertLongDecimalString(Long.MAX_VALUE) |
| |
| for (i in 1..19) { |
| val value = 10.0.pow(i).toLong() |
| assertLongDecimalString(value - 1) |
| assertLongDecimalString(value) |
| } |
| } |
| |
| private fun assertLongDecimalString(value: Long) { |
| sink.writeDecimalLong(value).writeUtf8("zzz").flush() |
| val expected = "${value}zzz" |
| val actual = data.readUtf8() |
| assertEquals(expected, actual, "$value expected $expected but was $actual") |
| } |
| |
| @Test fun longHexString() { |
| assertLongHexString(0) |
| assertLongHexString(Long.MIN_VALUE) |
| assertLongHexString(Long.MAX_VALUE) |
| |
| for (i in 0..62) { |
| assertLongHexString((1L shl i) - 1) |
| assertLongHexString(1L shl i) |
| } |
| } |
| |
| private fun assertLongHexString(value: Long) { |
| sink.writeHexadecimalUnsignedLong(value).writeUtf8("zzz").flush() |
| val expected = "${value.toHexString()}zzz" |
| val actual = data.readUtf8() |
| assertEquals(expected, actual, "$value expected $expected but was $actual") |
| } |
| } |