diff options
Diffstat (limited to 'okio/src/jvmTest/kotlin/okio/SegmentSharingTest.kt')
-rw-r--r-- | okio/src/jvmTest/kotlin/okio/SegmentSharingTest.kt | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/okio/src/jvmTest/kotlin/okio/SegmentSharingTest.kt b/okio/src/jvmTest/kotlin/okio/SegmentSharingTest.kt new file mode 100644 index 00000000..73e74d99 --- /dev/null +++ b/okio/src/jvmTest/kotlin/okio/SegmentSharingTest.kt @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2015 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.encodeUtf8 +import okio.TestUtil.assertEquivalent +import okio.TestUtil.bufferWithSegments +import okio.TestUtil.takeAllPoolSegments +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertTrue + +/** Tests behavior optimized by sharing segments between buffers and byte strings. */ +class SegmentSharingTest { + @Test fun snapshotOfEmptyBuffer() { + val snapshot = Buffer().snapshot() + assertEquivalent(snapshot, ByteString.EMPTY) + } + + @Test fun snapshotsAreEquivalent() { + val byteString = bufferWithSegments(xs, ys, zs).snapshot() + assertEquivalent(byteString, bufferWithSegments(xs, ys + zs).snapshot()) + assertEquivalent(byteString, bufferWithSegments(xs + ys + zs).snapshot()) + assertEquivalent(byteString, (xs + ys + zs).encodeUtf8()) + } + + @Test fun snapshotGetByte() { + val byteString = bufferWithSegments(xs, ys, zs).snapshot() + assertEquals('x', byteString[0].toChar()) + assertEquals('x', byteString[xs.length - 1].toChar()) + assertEquals('y', byteString[xs.length].toChar()) + assertEquals('y', byteString[xs.length + ys.length - 1].toChar()) + assertEquals('z', byteString[xs.length + ys.length].toChar()) + assertEquals('z', byteString[xs.length + ys.length + zs.length - 1].toChar()) + assertFailsWith<IndexOutOfBoundsException> { + byteString[-1] + } + + assertFailsWith<IndexOutOfBoundsException> { + byteString[xs.length + ys.length + zs.length] + } + } + + @Test fun snapshotWriteToOutputStream() { + val byteString = bufferWithSegments(xs, ys, zs).snapshot() + val out = Buffer() + byteString.write(out.outputStream()) + assertEquals(xs + ys + zs, out.readUtf8()) + } + + /** + * Snapshots share their backing byte arrays with the source buffers. Those byte arrays must not + * be recycled, otherwise the new writer could corrupt the segment. + */ + @Test fun snapshotSegmentsAreNotRecycled() { + val buffer = bufferWithSegments(xs, ys, zs) + val snapshot = buffer.snapshot() + assertEquals(xs + ys + zs, snapshot.utf8()) + + // Confirm that clearing the buffer doesn't release its segments. + val bufferHead = buffer.head + takeAllPoolSegments() // Make room for new segments. + buffer.clear() + assertTrue(bufferHead !in takeAllPoolSegments()) + } + + /** + * Clones share their backing byte arrays with the source buffers. Those byte arrays must not + * be recycled, otherwise the new writer could corrupt the segment. + */ + @Test fun cloneSegmentsAreNotRecycled() { + val buffer = bufferWithSegments(xs, ys, zs) + val clone = buffer.clone() + + // While locking the pool, confirm that clearing the buffer doesn't release its segments. + val bufferHead = buffer.head!! + takeAllPoolSegments() // Make room for new segments. + buffer.clear() + assertTrue(bufferHead !in takeAllPoolSegments()) + + val cloneHead = clone.head!! + takeAllPoolSegments() // Make room for new segments. + clone.clear() + assertTrue(cloneHead !in takeAllPoolSegments()) + } + + @Test fun snapshotJavaSerialization() { + val byteString = bufferWithSegments(xs, ys, zs).snapshot() + assertEquivalent(byteString, TestUtil.reserialize(byteString)) + } + + @Test fun clonesAreEquivalent() { + val bufferA = bufferWithSegments(xs, ys, zs) + val bufferB = bufferA.clone() + assertEquivalent(bufferA, bufferB) + assertEquivalent(bufferA, bufferWithSegments(xs + ys, zs)) + } + + /** Even though some segments are shared, clones can be mutated independently. */ + @Test fun mutateAfterClone() { + val bufferA = Buffer() + bufferA.writeUtf8("abc") + val bufferB = bufferA.clone() + bufferA.writeUtf8("def") + bufferB.writeUtf8("DEF") + assertEquals("abcdef", bufferA.readUtf8()) + assertEquals("abcDEF", bufferB.readUtf8()) + } + + @Test fun concatenateSegmentsCanCombine() { + val bufferA = Buffer().writeUtf8(ys).writeUtf8(us) + assertEquals(ys, bufferA.readUtf8(ys.length.toLong())) + val bufferB = Buffer().writeUtf8(vs).writeUtf8(ws) + val bufferC = bufferA.clone() + bufferA.write(bufferB, vs.length.toLong()) + bufferC.writeUtf8(xs) + + assertEquals(us + vs, bufferA.readUtf8()) + assertEquals(ws, bufferB.readUtf8()) + assertEquals(us + xs, bufferC.readUtf8()) + } + + @Test fun shareAndSplit() { + val bufferA = Buffer().writeUtf8("xxxx") + val snapshot = bufferA.snapshot() // Share the segment. + val bufferB = Buffer() + bufferB.write(bufferA, 2) // Split the shared segment in two. + bufferB.writeUtf8("yy") // Append to the first half of the shared segment. + assertEquals("xxxx", snapshot.utf8()) + } + + @Test fun appendSnapshotToEmptyBuffer() { + val bufferA = bufferWithSegments(xs, ys) + val snapshot = bufferA.snapshot() + val bufferB = Buffer() + bufferB.write(snapshot) + assertEquivalent(bufferB, bufferA) + } + + @Test fun appendSnapshotToNonEmptyBuffer() { + val bufferA = bufferWithSegments(xs, ys) + val snapshot = bufferA.snapshot() + val bufferB = Buffer().writeUtf8(us) + bufferB.write(snapshot) + assertEquivalent(bufferB, Buffer().writeUtf8(us + xs + ys)) + } + + @Test fun copyToSegmentSharing() { + val bufferA = bufferWithSegments(ws, xs + "aaaa", ys, "bbbb$zs") + val bufferB = bufferWithSegments(us) + bufferA.copyTo(bufferB, (ws.length + xs.length).toLong(), (4 + ys.length + 4).toLong()) + assertEquivalent(bufferB, Buffer().writeUtf8(us + "aaaa" + ys + "bbbb")) + } +} + +private val us = "u".repeat(Segment.SIZE / 2 - 2) +private val vs = "v".repeat(Segment.SIZE / 2 - 1) +private val ws = "w".repeat(Segment.SIZE / 2) +private val xs = "x".repeat(Segment.SIZE / 2 + 1) +private val ys = "y".repeat(Segment.SIZE / 2 + 2) +private val zs = "z".repeat(Segment.SIZE / 2 + 3) |