diff options
Diffstat (limited to 'okio/src/commonTest/kotlin/okio')
23 files changed, 1064 insertions, 1631 deletions
diff --git a/okio/src/commonTest/kotlin/okio/AbstractBufferedSinkTest.kt b/okio/src/commonTest/kotlin/okio/AbstractBufferedSinkTest.kt index 49bff8d9..e922d5f2 100644 --- a/okio/src/commonTest/kotlin/okio/AbstractBufferedSinkTest.kt +++ b/okio/src/commonTest/kotlin/okio/AbstractBufferedSinkTest.kt @@ -16,18 +16,17 @@ 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 +import okio.ByteString.Companion.decodeHex +import okio.ByteString.Companion.encodeUtf8 class BufferSinkTest : AbstractBufferedSinkTest(BufferedSinkFactory.BUFFER) class RealBufferedSinkTest : AbstractBufferedSinkTest(BufferedSinkFactory.REAL_BUFFERED_SINK) abstract class AbstractBufferedSinkTest internal constructor( - factory: BufferedSinkFactory + factory: BufferedSinkFactory, ) { private val data: Buffer = Buffer() private val sink: BufferedSink = factory.create(data) @@ -217,26 +216,60 @@ abstract class AbstractBufferedSinkTest internal constructor( } @Test fun closeEmitsBufferedBytes() { - sink.writeByte('a'.toInt()) + sink.writeByte('a'.code) sink.close() - assertEquals('a', data.readByte().toChar()) + assertEquals('a', data.readByte().toInt().toChar()) } + /** + * This test hard codes the results of Long.toString() because that function rounds large values + * when using Kotlin/JS IR. https://youtrack.jetbrains.com/issue/KT-39891 + */ @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) - } + assertLongDecimalString("0", 0) + assertLongDecimalString("-9223372036854775808", Long.MIN_VALUE) + assertLongDecimalString("9223372036854775807", Long.MAX_VALUE) + assertLongDecimalString("9", 9L) + assertLongDecimalString("99", 99L) + assertLongDecimalString("999", 999L) + assertLongDecimalString("9999", 9999L) + assertLongDecimalString("99999", 99999L) + assertLongDecimalString("999999", 999999L) + assertLongDecimalString("9999999", 9999999L) + assertLongDecimalString("99999999", 99999999L) + assertLongDecimalString("999999999", 999999999L) + assertLongDecimalString("9999999999", 9999999999L) + assertLongDecimalString("99999999999", 99999999999L) + assertLongDecimalString("999999999999", 999999999999L) + assertLongDecimalString("9999999999999", 9999999999999L) + assertLongDecimalString("99999999999999", 99999999999999L) + assertLongDecimalString("999999999999999", 999999999999999L) + assertLongDecimalString("9999999999999999", 9999999999999999L) + assertLongDecimalString("99999999999999999", 99999999999999999L) + assertLongDecimalString("999999999999999999", 999999999999999999L) + assertLongDecimalString("10", 10L) + assertLongDecimalString("100", 100L) + assertLongDecimalString("1000", 1000L) + assertLongDecimalString("10000", 10000L) + assertLongDecimalString("100000", 100000L) + assertLongDecimalString("1000000", 1000000L) + assertLongDecimalString("10000000", 10000000L) + assertLongDecimalString("100000000", 100000000L) + assertLongDecimalString("1000000000", 1000000000L) + assertLongDecimalString("10000000000", 10000000000L) + assertLongDecimalString("100000000000", 100000000000L) + assertLongDecimalString("1000000000000", 1000000000000L) + assertLongDecimalString("10000000000000", 10000000000000L) + assertLongDecimalString("100000000000000", 100000000000000L) + assertLongDecimalString("1000000000000000", 1000000000000000L) + assertLongDecimalString("10000000000000000", 10000000000000000L) + assertLongDecimalString("100000000000000000", 100000000000000000L) + assertLongDecimalString("1000000000000000000", 1000000000000000000L) } - private fun assertLongDecimalString(value: Long) { + private fun assertLongDecimalString(string: String, value: Long) { sink.writeDecimalLong(value).writeUtf8("zzz").flush() - val expected = "${value}zzz" + val expected = "${string}zzz" val actual = data.readUtf8() assertEquals(expected, actual, "$value expected $expected but was $actual") } diff --git a/okio/src/commonTest/kotlin/okio/AbstractBufferedSourceTest.kt b/okio/src/commonTest/kotlin/okio/AbstractBufferedSourceTest.kt index b15d369c..70d76cb6 100644 --- a/okio/src/commonTest/kotlin/okio/AbstractBufferedSourceTest.kt +++ b/okio/src/commonTest/kotlin/okio/AbstractBufferedSourceTest.kt @@ -16,14 +16,13 @@ package okio -import okio.ByteString.Companion.decodeHex -import okio.ByteString.Companion.encodeUtf8 import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith import kotlin.test.assertFalse import kotlin.test.assertTrue -import kotlin.test.fail +import okio.ByteString.Companion.decodeHex +import okio.ByteString.Companion.encodeUtf8 class BufferSourceTest : AbstractBufferedSourceTest(BufferedSourceFactory.BUFFER) class RealBufferedSourceTest : AbstractBufferedSourceTest(BufferedSourceFactory.REAL_BUFFERED_SOURCE) @@ -33,7 +32,7 @@ class PeekBufferTest : AbstractBufferedSourceTest(BufferedSourceFactory.PEEK_BUF class PeekBufferedSourceTest : AbstractBufferedSourceTest(BufferedSourceFactory.PEEK_BUFFERED_SOURCE) abstract class AbstractBufferedSourceTest internal constructor( - private val factory: BufferedSourceFactory + private val factory: BufferedSourceFactory, ) { private val sink: BufferedSink private val source: BufferedSource @@ -111,8 +110,8 @@ abstract class AbstractBufferedSourceTest internal constructor( 0x87.toByte(), 0x65.toByte(), 0x43.toByte(), - 0x21.toByte() - ) + 0x21.toByte(), + ), ) sink.emit() assertEquals(-0x543210ff, source.readInt().toLong()) @@ -130,8 +129,8 @@ abstract class AbstractBufferedSourceTest internal constructor( 0x87.toByte(), 0x65.toByte(), 0x43.toByte(), - 0x21.toByte() - ) + 0x21.toByte(), + ), ) sink.emit() assertEquals(0x10efcdab, source.readIntLe().toLong()) @@ -184,8 +183,8 @@ abstract class AbstractBufferedSourceTest internal constructor( 0x12.toByte(), 0x23.toByte(), 0x34.toByte(), - 0x45.toByte() - ) + 0x45.toByte(), + ), ) sink.emit() assertEquals(-0x543210ef789abcdfL, source.readLong()) @@ -211,8 +210,8 @@ abstract class AbstractBufferedSourceTest internal constructor( 0x12.toByte(), 0x23.toByte(), 0x34.toByte(), - 0x45.toByte() - ) + 0x45.toByte(), + ), ) sink.emit() assertEquals(0x2143658710efcdabL, source.readLongLe()) @@ -231,8 +230,8 @@ abstract class AbstractBufferedSourceTest internal constructor( 0x87.toByte(), 0x65.toByte(), 0x43.toByte(), - 0x21.toByte() - ) + 0x21.toByte(), + ), ) sink.emit() source.skip((Segment.SIZE - 7).toLong()) @@ -341,14 +340,14 @@ abstract class AbstractBufferedSourceTest internal constructor( // Verify we read all that we could from the source. assertArrayEquals( byteArrayOf( - 'H'.toByte(), - 'e'.toByte(), - 'l'.toByte(), - 'l'.toByte(), - 'o'.toByte(), - 0 + 'H'.code.toByte(), + 'e'.code.toByte(), + 'l'.code.toByte(), + 'l'.code.toByte(), + 'o'.code.toByte(), + 0, ), - array + array, ) } @@ -360,11 +359,11 @@ abstract class AbstractBufferedSourceTest internal constructor( val read = source.read(sink) if (factory.isOneByteAtATime) { assertEquals(1, read.toLong()) - val expected = byteArrayOf('a'.toByte(), 0, 0) + val expected = byteArrayOf('a'.code.toByte(), 0, 0) assertArrayEquals(expected, sink) } else { assertEquals(3, read.toLong()) - val expected = byteArrayOf('a'.toByte(), 'b'.toByte(), 'c'.toByte()) + val expected = byteArrayOf('a'.code.toByte(), 'b'.code.toByte(), 'c'.code.toByte()) assertArrayEquals(expected, sink) } } @@ -377,11 +376,12 @@ abstract class AbstractBufferedSourceTest internal constructor( val read = source.read(sink) if (factory.isOneByteAtATime) { assertEquals(1, read.toLong()) - val expected = byteArrayOf('a'.toByte(), 0, 0, 0, 0) + val expected = byteArrayOf('a'.code.toByte(), 0, 0, 0, 0) assertArrayEquals(expected, sink) } else { assertEquals(4, read.toLong()) - val expected = byteArrayOf('a'.toByte(), 'b'.toByte(), 'c'.toByte(), 'd'.toByte(), 0) + val expected = + byteArrayOf('a'.code.toByte(), 'b'.code.toByte(), 'c'.code.toByte(), 'd'.code.toByte(), 0) assertArrayEquals(expected, sink) } } @@ -394,11 +394,12 @@ abstract class AbstractBufferedSourceTest internal constructor( val read = source.read(sink, 2, 3) if (factory.isOneByteAtATime) { assertEquals(1, read.toLong()) - val expected = byteArrayOf(0, 0, 'a'.toByte(), 0, 0, 0, 0) + val expected = byteArrayOf(0, 0, 'a'.code.toByte(), 0, 0, 0, 0) assertArrayEquals(expected, sink) } else { assertEquals(3, read.toLong()) - val expected = byteArrayOf(0, 0, 'a'.toByte(), 'b'.toByte(), 'c'.toByte(), 0, 0) + val expected = + byteArrayOf(0, 0, 'a'.code.toByte(), 'b'.code.toByte(), 'c'.code.toByte(), 0, 0) assertArrayEquals(expected, sink) } } @@ -491,9 +492,9 @@ abstract class AbstractBufferedSourceTest internal constructor( sink.writeUtf8("c") sink.emit() source.skip(1) - assertEquals('b'.toLong(), (source.readByte() and 0xff).toLong()) + assertEquals('b'.code.toLong(), (source.readByte() and 0xff).toLong()) source.skip((Segment.SIZE - 2).toLong()) - assertEquals('b'.toLong(), (source.readByte() and 0xff).toLong()) + assertEquals('b'.code.toLong(), (source.readByte() and 0xff).toLong()) source.skip(1) assertTrue(source.exhausted()) } @@ -501,7 +502,6 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun skipInsufficientData() { sink.writeUtf8("a") sink.emit() - assertFailsWith<EOFException> { source.skip(2) } @@ -509,61 +509,61 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun indexOf() { // The segment is empty. - assertEquals(-1, source.indexOf('a'.toByte())) + assertEquals(-1, source.indexOf('a'.code.toByte())) // The segment has one value. sink.writeUtf8("a") // a sink.emit() - assertEquals(0, source.indexOf('a'.toByte())) - assertEquals(-1, source.indexOf('b'.toByte())) + assertEquals(0, source.indexOf('a'.code.toByte())) + assertEquals(-1, source.indexOf('b'.code.toByte())) // The segment has lots of data. sink.writeUtf8("b".repeat(Segment.SIZE - 2)) // ab...b sink.emit() - assertEquals(0, source.indexOf('a'.toByte())) - assertEquals(1, source.indexOf('b'.toByte())) - assertEquals(-1, source.indexOf('c'.toByte())) + assertEquals(0, source.indexOf('a'.code.toByte())) + assertEquals(1, source.indexOf('b'.code.toByte())) + assertEquals(-1, source.indexOf('c'.code.toByte())) // The segment doesn't start at 0, it starts at 2. source.skip(2) // b...b - assertEquals(-1, source.indexOf('a'.toByte())) - assertEquals(0, source.indexOf('b'.toByte())) - assertEquals(-1, source.indexOf('c'.toByte())) + assertEquals(-1, source.indexOf('a'.code.toByte())) + assertEquals(0, source.indexOf('b'.code.toByte())) + assertEquals(-1, source.indexOf('c'.code.toByte())) // The segment is full. sink.writeUtf8("c") // b...bc sink.emit() - assertEquals(-1, source.indexOf('a'.toByte())) - assertEquals(0, source.indexOf('b'.toByte())) - assertEquals((Segment.SIZE - 3).toLong(), source.indexOf('c'.toByte())) + assertEquals(-1, source.indexOf('a'.code.toByte())) + assertEquals(0, source.indexOf('b'.code.toByte())) + assertEquals((Segment.SIZE - 3).toLong(), source.indexOf('c'.code.toByte())) // The segment doesn't start at 2, it starts at 4. source.skip(2) // b...bc - assertEquals(-1, source.indexOf('a'.toByte())) - assertEquals(0, source.indexOf('b'.toByte())) - assertEquals((Segment.SIZE - 5).toLong(), source.indexOf('c'.toByte())) + assertEquals(-1, source.indexOf('a'.code.toByte())) + assertEquals(0, source.indexOf('b'.code.toByte())) + assertEquals((Segment.SIZE - 5).toLong(), source.indexOf('c'.code.toByte())) // Two segments. sink.writeUtf8("d") // b...bcd, d is in the 2nd segment. sink.emit() - assertEquals((Segment.SIZE - 4).toLong(), source.indexOf('d'.toByte())) - assertEquals(-1, source.indexOf('e'.toByte())) + assertEquals((Segment.SIZE - 4).toLong(), source.indexOf('d'.code.toByte())) + assertEquals(-1, source.indexOf('e'.code.toByte())) } @Test fun indexOfByteWithStartOffset() { sink.writeUtf8("a").writeUtf8("b".repeat(Segment.SIZE)).writeUtf8("c") sink.emit() - assertEquals(-1, source.indexOf('a'.toByte(), 1)) - assertEquals(15, source.indexOf('b'.toByte(), 15)) + assertEquals(-1, source.indexOf('a'.code.toByte(), 1)) + assertEquals(15, source.indexOf('b'.code.toByte(), 15)) } @Test fun indexOfByteWithBothOffsets() { if (factory.isOneByteAtATime) { - // When run on Travis this causes out-of-memory errors. + // When run on CI this causes out-of-memory errors. return } - val a = 'a'.toByte() - val c = 'c'.toByte() + val a = 'a'.code.toByte() + val c = 'c'.code.toByte() val size = Segment.SIZE * 5 val bytes = ByteArray(size) { a } @@ -585,7 +585,7 @@ abstract class AbstractBufferedSourceTest internal constructor( size - Segment.SIZE + 1, size - 3, size - 2, - size - 1 + size - 1, ) // In each iteration, we write c to the known point and then search for it using different @@ -615,17 +615,11 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun indexOfByteInvalidBoundsThrows() { sink.writeUtf8("abc") sink.emit() - - try { - source.indexOf('a'.toByte(), -1) - fail("Expected failure: fromIndex < 0") - } catch (expected: IllegalArgumentException) { + assertFailsWith<IllegalArgumentException>("Expected failure: fromIndex < 0") { + source.indexOf('a'.code.toByte(), -1) } - - try { - source.indexOf('a'.toByte(), 10, 0) - fail("Expected failure: fromIndex > toIndex") - } catch (expected: IllegalArgumentException) { + assertFailsWith<IllegalArgumentException>("Expected failure: fromIndex > toIndex") { + source.indexOf('a'.code.toByte(), 10, 0) } } @@ -649,55 +643,55 @@ abstract class AbstractBufferedSourceTest internal constructor( sink.emit() assertEquals( (Segment.SIZE - 3).toLong(), - source.indexOf("aabc".encodeUtf8(), (Segment.SIZE - 4).toLong()) + source.indexOf("aabc".encodeUtf8(), (Segment.SIZE - 4).toLong()), ) assertEquals( (Segment.SIZE - 3).toLong(), - source.indexOf("aabc".encodeUtf8(), (Segment.SIZE - 3).toLong()) + source.indexOf("aabc".encodeUtf8(), (Segment.SIZE - 3).toLong()), ) assertEquals( (Segment.SIZE - 2).toLong(), - source.indexOf("abcd".encodeUtf8(), (Segment.SIZE - 2).toLong()) + source.indexOf("abcd".encodeUtf8(), (Segment.SIZE - 2).toLong()), ) assertEquals( (Segment.SIZE - 2).toLong(), - source.indexOf("abc".encodeUtf8(), (Segment.SIZE - 2).toLong()) + source.indexOf("abc".encodeUtf8(), (Segment.SIZE - 2).toLong()), ) assertEquals( (Segment.SIZE - 2).toLong(), - source.indexOf("abc".encodeUtf8(), (Segment.SIZE - 2).toLong()) + source.indexOf("abc".encodeUtf8(), (Segment.SIZE - 2).toLong()), ) assertEquals( (Segment.SIZE - 2).toLong(), - source.indexOf("ab".encodeUtf8(), (Segment.SIZE - 2).toLong()) + source.indexOf("ab".encodeUtf8(), (Segment.SIZE - 2).toLong()), ) assertEquals( (Segment.SIZE - 2).toLong(), - source.indexOf("a".encodeUtf8(), (Segment.SIZE - 2).toLong()) + source.indexOf("a".encodeUtf8(), (Segment.SIZE - 2).toLong()), ) assertEquals( (Segment.SIZE - 1).toLong(), - source.indexOf("bc".encodeUtf8(), (Segment.SIZE - 2).toLong()) + source.indexOf("bc".encodeUtf8(), (Segment.SIZE - 2).toLong()), ) assertEquals( (Segment.SIZE - 1).toLong(), - source.indexOf("b".encodeUtf8(), (Segment.SIZE - 2).toLong()) + source.indexOf("b".encodeUtf8(), (Segment.SIZE - 2).toLong()), ) assertEquals( Segment.SIZE.toLong(), - source.indexOf("c".encodeUtf8(), (Segment.SIZE - 2).toLong()) + source.indexOf("c".encodeUtf8(), (Segment.SIZE - 2).toLong()), ) assertEquals( Segment.SIZE.toLong(), - source.indexOf("c".encodeUtf8(), Segment.SIZE.toLong()) + source.indexOf("c".encodeUtf8(), Segment.SIZE.toLong()), ) assertEquals( (Segment.SIZE + 1).toLong(), - source.indexOf("d".encodeUtf8(), (Segment.SIZE - 2).toLong()) + source.indexOf("d".encodeUtf8(), (Segment.SIZE - 2).toLong()), ) assertEquals( (Segment.SIZE + 1).toLong(), - source.indexOf("d".encodeUtf8(), (Segment.SIZE + 1).toLong()) + source.indexOf("d".encodeUtf8(), (Segment.SIZE + 1).toLong()), ) } @@ -723,19 +717,15 @@ abstract class AbstractBufferedSourceTest internal constructor( } @Test fun indexOfByteStringInvalidArgumentsThrows() { - try { + var e = assertFailsWith<IllegalArgumentException> { source.indexOf(ByteString.of()) - fail() - } catch (e: IllegalArgumentException) { - assertEquals("bytes is empty", e.message) } + assertEquals("bytes is empty", e.message) - try { + e = assertFailsWith<IllegalArgumentException> { source.indexOf("hi".encodeUtf8(), -1) - fail() - } catch (e: IllegalArgumentException) { - assertEquals("fromIndex < 0: -1", e.message) } + assertEquals("fromIndex < 0: -1", e.message) } /** @@ -781,10 +771,10 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun indexOfByteWithFromIndex() { sink.writeUtf8("aaa") sink.emit() - assertEquals(0, source.indexOf('a'.toByte())) - assertEquals(0, source.indexOf('a'.toByte(), 0)) - assertEquals(1, source.indexOf('a'.toByte(), 1)) - assertEquals(2, source.indexOf('a'.toByte(), 2)) + assertEquals(0, source.indexOf('a'.code.toByte())) + assertEquals(0, source.indexOf('a'.code.toByte(), 0)) + assertEquals(1, source.indexOf('a'.code.toByte(), 1)) + assertEquals(2, source.indexOf('a'.code.toByte(), 2)) } @Test fun indexOfByteStringWithFromIndex() { @@ -857,35 +847,29 @@ abstract class AbstractBufferedSourceTest internal constructor( } @Test fun longHexStringTooLongThrows() { - try { - sink.writeUtf8("fffffffffffffffff") - sink.emit() + sink.writeUtf8("fffffffffffffffff") + sink.emit() + + val e = assertFailsWith<NumberFormatException> { source.readHexadecimalUnsignedLong() - fail() - } catch (e: NumberFormatException) { - assertEquals("Number too large: fffffffffffffffff", e.message) } + assertEquals("Number too large: fffffffffffffffff", e.message) } @Test fun longHexStringTooShortThrows() { - try { - sink.writeUtf8(" ") - sink.emit() + sink.writeUtf8(" ") + sink.emit() + + val e = assertFailsWith<NumberFormatException> { source.readHexadecimalUnsignedLong() - fail() - } catch (e: NumberFormatException) { - assertEquals("Expected leading [0-9a-fA-F] character but was 0x20", e.message) } + assertEquals("Expected leading [0-9a-fA-F] character but was 0x20", e.message) } @Test fun longHexEmptySourceThrows() { - try { - sink.writeUtf8("") - sink.emit() - source.readHexadecimalUnsignedLong() - fail() - } catch (expected: EOFException) { - } + sink.writeUtf8("") + sink.emit() + assertFailsWith<EOFException> { source.readHexadecimalUnsignedLong() } } @Test fun longDecimalString() { @@ -918,60 +902,74 @@ abstract class AbstractBufferedSourceTest internal constructor( } @Test fun longDecimalStringTooLongThrows() { - try { - sink.writeUtf8("12345678901234567890") // Too many digits. - sink.emit() + sink.writeUtf8("12345678901234567890") // Too many digits. + sink.emit() + + val e = assertFailsWith<NumberFormatException> { source.readDecimalLong() - fail() - } catch (e: NumberFormatException) { - assertEquals("Number too large: 12345678901234567890", e.message) } + assertEquals("Number too large: 12345678901234567890", e.message) } @Test fun longDecimalStringTooHighThrows() { - try { - sink.writeUtf8("9223372036854775808") // Right size but cannot fit. - sink.emit() + sink.writeUtf8("9223372036854775808") // Right size but cannot fit. + sink.emit() + + val e = assertFailsWith<NumberFormatException> { source.readDecimalLong() - fail() - } catch (e: NumberFormatException) { - assertEquals("Number too large: 9223372036854775808", e.message) } + assertEquals("Number too large: 9223372036854775808", e.message) } @Test fun longDecimalStringTooLowThrows() { - try { - sink.writeUtf8("-9223372036854775809") // Right size but cannot fit. - sink.emit() + sink.writeUtf8("-9223372036854775809") // Right size but cannot fit. + sink.emit() + + val e = assertFailsWith<NumberFormatException> { source.readDecimalLong() - fail() - } catch (e: NumberFormatException) { - assertEquals("Number too large: -9223372036854775809", e.message) } + assertEquals("Number too large: -9223372036854775809", e.message) } @Test fun longDecimalStringTooShortThrows() { - try { - sink.writeUtf8(" ") - sink.emit() + sink.writeUtf8(" ") + sink.emit() + + val e = assertFailsWith<NumberFormatException> { source.readDecimalLong() - fail() - } catch (e: NumberFormatException) { - assertEquals("Expected leading [0-9] or '-' character but was 0x20", e.message) } + assertEquals("Expected a digit or '-' but was 0x20", e.message) } @Test fun longDecimalEmptyThrows() { - try { - sink.writeUtf8("") - sink.emit() + sink.writeUtf8("") + sink.emit() + assertFailsWith<EOFException> { + source.readDecimalLong() + } + } + + @Test fun longDecimalLoneDashThrows() { + sink.writeUtf8("-") + sink.emit() + assertFailsWith<EOFException> { + source.readDecimalLong() + } + } + + @Test fun longDecimalDashFollowedByNonDigitThrows() { + sink.writeUtf8("- ") + sink.emit() + assertFailsWith<NumberFormatException> { source.readDecimalLong() - fail() - } catch (expected: EOFException) { } } @Test fun codePoints() { + // TODO: remove this suppression once this issue is fixed. + // https://youtrack.jetbrains.com/issue/KT-60212 + if (isWasm()) return + sink.write("7f".decodeHex()) sink.emit() assertEquals(0x7f, source.readUtf8CodePoint().toLong()) @@ -1000,21 +998,24 @@ abstract class AbstractBufferedSourceTest internal constructor( val options = Options.of( "ROCK".encodeUtf8(), "SCISSORS".encodeUtf8(), - "PAPER".encodeUtf8() + "PAPER".encodeUtf8(), ) sink.writeUtf8("PAPER,SCISSORS,ROCK") sink.emit() assertEquals(2, source.select(options).toLong()) - assertEquals(','.toLong(), source.readByte().toLong()) + assertEquals(','.code.toLong(), source.readByte().toLong()) assertEquals(1, source.select(options).toLong()) - assertEquals(','.toLong(), source.readByte().toLong()) + assertEquals(','.code.toLong(), source.readByte().toLong()) assertEquals(0, source.select(options).toLong()) assertTrue(source.exhausted()) } /** Note that this test crashes the VM on Android. */ @Test fun selectSpanningMultipleSegments() { + if (factory.isOneByteAtATime && isBrowser()) { + return // This test times out on browsers. + } val commonPrefix = randomBytes(Segment.SIZE + 10) val a = Buffer().write(commonPrefix).writeUtf8("a").readByteString() val bc = Buffer().write(commonPrefix).writeUtf8("bc").readByteString() @@ -1036,7 +1037,7 @@ abstract class AbstractBufferedSourceTest internal constructor( val options = Options.of( "ROCK".encodeUtf8(), "SCISSORS".encodeUtf8(), - "PAPER".encodeUtf8() + "PAPER".encodeUtf8(), ) sink.writeUtf8("SPOCK") @@ -1049,7 +1050,7 @@ abstract class AbstractBufferedSourceTest internal constructor( val options = Options.of( "abcd".encodeUtf8(), "abce".encodeUtf8(), - "abcc".encodeUtf8() + "abcc".encodeUtf8(), ) sink.writeUtf8("abcc").writeUtf8("abcd").writeUtf8("abce") @@ -1063,7 +1064,7 @@ abstract class AbstractBufferedSourceTest internal constructor( val options = Options.of( "abcd".encodeUtf8(), "abce".encodeUtf8(), - "abcc".encodeUtf8() + "abcc".encodeUtf8(), ) sink.writeUtf8("abc") sink.emit() @@ -1075,7 +1076,7 @@ abstract class AbstractBufferedSourceTest internal constructor( val options = Options.of( "abcd".encodeUtf8(), "abc".encodeUtf8(), - "abcde".encodeUtf8() + "abcde".encodeUtf8(), ) sink.writeUtf8("abcdef") sink.emit() @@ -1086,7 +1087,7 @@ abstract class AbstractBufferedSourceTest internal constructor( @Test fun selectFromEmptySource() { val options = Options.of( "abc".encodeUtf8(), - "def".encodeUtf8() + "def".encodeUtf8(), ) assertEquals(-1, source.select(options).toLong()) } @@ -1132,6 +1133,10 @@ abstract class AbstractBufferedSourceTest internal constructor( } @Test fun peekLarge() { + if (factory.isOneByteAtATime) { + // When run on CI this causes out-of-memory errors. + return + } sink.writeUtf8("abcdef") sink.writeUtf8("g".repeat(2 * Segment.SIZE)) sink.writeUtf8("hij") @@ -1163,12 +1168,10 @@ abstract class AbstractBufferedSourceTest internal constructor( assertEquals("def", source.readUtf8(3)) - try { + val e = assertFailsWith<IllegalStateException> { peek.readUtf8() - fail() - } catch (e: IllegalStateException) { - assertEquals("Peek source is invalid because upstream source was used", e.message) } + assertEquals("Peek source is invalid because upstream source was used", e.message) } @Test fun peekSegmentThenInvalid() { @@ -1186,12 +1189,10 @@ abstract class AbstractBufferedSourceTest internal constructor( // Skip the rest of the buffered data peek.skip(peek.buffer.size) - try { + val e = assertFailsWith<IllegalStateException> { peek.readByte() - fail() - } catch (e: IllegalStateException) { - assertEquals("Peek source is invalid because upstream source was used", e.message) } + assertEquals("Peek source is invalid because upstream source was used", e.message) } @Test fun peekDoesntReadTooMuch() { diff --git a/okio/src/commonTest/kotlin/okio/AbstractFileSystemTest.kt b/okio/src/commonTest/kotlin/okio/AbstractFileSystemTest.kt deleted file mode 100644 index 669138f5..00000000 --- a/okio/src/commonTest/kotlin/okio/AbstractFileSystemTest.kt +++ /dev/null @@ -1,670 +0,0 @@ -/* - * Copyright (C) 2020 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 kotlinx.datetime.Clock -import kotlinx.datetime.Instant -import okio.ByteString.Companion.toByteString -import okio.Path.Companion.toPath -import okio.fakefilesystem.FakeFileSystem -import kotlin.random.Random -import kotlin.test.BeforeTest -import kotlin.test.Ignore -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertFalse -import kotlin.test.assertNull -import kotlin.test.assertTrue -import kotlin.time.ExperimentalTime -import kotlin.time.seconds - -/** This test assumes that okio-files/ is the current working directory when executed. */ -@ExperimentalTime -@ExperimentalFileSystem -abstract class AbstractFileSystemTest( - val clock: Clock, - val fileSystem: FileSystem, - val windowsLimitations: Boolean, - temporaryDirectory: Path -) { - val base: Path = temporaryDirectory / "${this::class.simpleName}-${randomToken()}" - private val isJs = fileSystem::class.simpleName?.startsWith("NodeJs") ?: false - - @BeforeTest - fun setUp() { - fileSystem.createDirectory(base) - } - - @Test - fun canonicalizeDotReturnsCurrentWorkingDirectory() { - if (fileSystem is FakeFileSystem || fileSystem is ForwardingFileSystem) return - val cwd = fileSystem.canonicalize(".".toPath()) - val cwdString = cwd.toString() - assertTrue(cwdString) { - cwdString.endsWith("okio${Path.DIRECTORY_SEPARATOR}okio") || - cwdString.endsWith("${Path.DIRECTORY_SEPARATOR}okio-parent-okio-test") || // JS - cwdString.contains("/CoreSimulator/Devices/") || // iOS simulator. - cwdString == "/" // Android emulator. - } - } - - @Test - fun canonicalizeNoSuchFile() { - assertFailsWith<FileNotFoundException> { - fileSystem.canonicalize(base / "no-such-file") - } - } - - @Test - fun list() { - val target = base / "list" - target.writeUtf8("hello, world!") - val entries = fileSystem.list(base) - assertTrue(entries.toString()) { target in entries } - } - - @Test - fun listResultsAreSorted() { - val fileA = base / "a" - val fileB = base / "b" - val fileC = base / "c" - val fileD = base / "d" - - // Create files in a different order than the sorted order, so a file system that returns files - // in creation-order or reverse-creation order won't pass by accident. - fileD.writeUtf8("fileD") - fileB.writeUtf8("fileB") - fileC.writeUtf8("fileC") - fileA.writeUtf8("fileA") - - val entries = fileSystem.list(base) - assertEquals(entries, listOf(fileA, fileB, fileC, fileD)) - } - - @Test - fun listNoSuchDirectory() { - assertFailsWith<FileNotFoundException> { - fileSystem.list(base / "no-such-directory") - } - } - - @Test - fun listFile() { - val target = base / "list" - target.writeUtf8("hello, world!") - assertFailsWith<IOException> { - fileSystem.list(target) - } - } - - @Test - fun fileSourceNoSuchDirectory() { - assertFailsWith<FileNotFoundException> { - fileSystem.source(base / "no-such-directory" / "file") - } - } - - @Test - fun fileSource() { - val path = base / "file-source" - path.writeUtf8("hello, world!") - - val source = fileSystem.source(path) - val buffer = Buffer() - assertTrue(source.read(buffer, 100L) == 13L) - assertEquals(-1L, source.read(buffer, 100L)) - assertEquals("hello, world!", buffer.readUtf8()) - source.close() - } - - @Test - fun readPath() { - val path = base / "read-path" - val string = "hello, read with a Path" - path.writeUtf8(string) - - val result = fileSystem.read(path) { - assertEquals("hello", readUtf8(5)) - assertEquals(", read with ", readUtf8(12)) - assertEquals("a Path", readUtf8()) - return@read "success" - } - assertEquals("success", result) - } - - @Test - fun fileSink() { - val path = base / "file-sink" - val sink = fileSystem.sink(path) - val buffer = Buffer().writeUtf8("hello, world!") - sink.write(buffer, buffer.size) - sink.close() - assertTrue(path in fileSystem.list(base)) - assertEquals(0, buffer.size) - assertEquals("hello, world!", path.readUtf8()) - } - - @Test - fun writePath() { - val path = base / "write-path" - val content = fileSystem.write(path) { - val string = "hello, write with a Path" - writeUtf8(string) - return@write string - } - assertTrue(path in fileSystem.list(base)) - assertEquals(content, path.readUtf8()) - } - - @Test - fun appendingSinkAppendsToExistingFile() { - val path = base / "appending-sink-appends-to-existing-file" - path.writeUtf8("hello, world!\n") - val sink = fileSystem.appendingSink(path) - val buffer = Buffer().writeUtf8("this is added later!") - sink.write(buffer, buffer.size) - sink.close() - assertTrue(path in fileSystem.list(base)) - assertEquals("hello, world!\nthis is added later!", path.readUtf8()) - } - - @Test - fun appendingSinkDoesNotImpactExistingFile() { - val path = base / "appending-sink-does-not-impact-existing-file" - path.writeUtf8("hello, world!\n") - val sink = fileSystem.appendingSink(path) - assertEquals("hello, world!\n", path.readUtf8()) - sink.close() - assertEquals("hello, world!\n", path.readUtf8()) - } - - @Test - fun appendingSinkCreatesNewFile() { - val path = base / "appending-sink-creates-new-file" - val sink = fileSystem.appendingSink(path) - val buffer = Buffer().writeUtf8("this is all there is!") - sink.write(buffer, buffer.size) - sink.close() - assertTrue(path in fileSystem.list(base)) - assertEquals("this is all there is!", path.readUtf8()) - } - - @Test - fun fileSinkFlush() { - val path = base / "file-sink" - val sink = fileSystem.sink(path) - - val buffer = Buffer().writeUtf8("hello,") - sink.write(buffer, buffer.size) - sink.flush() - assertEquals("hello,", path.readUtf8()) - - buffer.writeUtf8(" world!") - sink.write(buffer, buffer.size) - sink.close() - assertEquals("hello, world!", path.readUtf8()) - } - - @Test - fun fileSinkNoSuchDirectory() { - assertFailsWith<FileNotFoundException> { - fileSystem.sink(base / "no-such-directory" / "file") - } - } - - @Test - fun createDirectory() { - val path = base / "create-directory" - fileSystem.createDirectory(path) - assertTrue(path in fileSystem.list(base)) - } - - @Test - fun createDirectoryAlreadyExists() { - val path = base / "already-exists" - fileSystem.createDirectory(path) - assertFailsWith<IOException> { - fileSystem.createDirectory(path) - } - } - - @Test - fun createDirectoryParentDirectoryDoesNotExist() { - val path = base / "no-such-directory" / "created" - assertFailsWith<IOException> { - fileSystem.createDirectory(path) - } - } - - @Test - fun createDirectoriesSingle() { - val path = base / "create-directories-single" - fileSystem.createDirectories(path) - assertTrue(path in fileSystem.list(base)) - assertTrue(fileSystem.metadata(path).isDirectory) - } - - @Test - fun createDirectoriesAlreadyExists() { - val path = base / "already-exists" - fileSystem.createDirectory(path) - fileSystem.createDirectories(path) - assertTrue(fileSystem.metadata(path).isDirectory) - } - - @Test - fun createDirectoriesParentDirectoryDoesNotExist() { - fileSystem.createDirectories(base / "a" / "b" / "c") - assertTrue(base / "a" in fileSystem.list(base)) - assertTrue(base / "a" / "b" in fileSystem.list(base / "a")) - assertTrue(base / "a" / "b" / "c" in fileSystem.list(base / "a" / "b")) - assertTrue(fileSystem.metadata(base / "a" / "b" / "c").isDirectory) - } - - @Test - fun createDirectoriesParentIsFile() { - val file = base / "simple-file" - file.writeUtf8("just a file") - assertFailsWith<IOException> { - fileSystem.createDirectories(file / "child") - } - } - - @Test - fun atomicMoveFile() { - val source = base / "source" - source.writeUtf8("hello, world!") - val target = base / "target" - fileSystem.atomicMove(source, target) - assertEquals("hello, world!", target.readUtf8()) - assertTrue(source !in fileSystem.list(base)) - assertTrue(target in fileSystem.list(base)) - } - - @Test - fun atomicMoveDirectory() { - val source = base / "source" - fileSystem.createDirectory(source) - val target = base / "target" - fileSystem.atomicMove(source, target) - assertTrue(source !in fileSystem.list(base)) - assertTrue(target in fileSystem.list(base)) - } - - @Test - fun atomicMoveSourceIsTarget() { - val source = base / "source" - source.writeUtf8("hello, world!") - fileSystem.atomicMove(source, source) - assertEquals("hello, world!", source.readUtf8()) - assertTrue(source in fileSystem.list(base)) - } - - @Test - fun atomicMoveClobberExistingFile() { - val source = base / "source" - source.writeUtf8("hello, world!") - val target = base / "target" - target.writeUtf8("this file will be clobbered!") - fileSystem.atomicMove(source, target) - assertEquals("hello, world!", target.readUtf8()) - assertTrue(source !in fileSystem.list(base)) - assertTrue(target in fileSystem.list(base)) - } - - @Test - fun atomicMoveSourceDoesNotExist() { - val source = base / "source" - val target = base / "target" - assertFailsWith<FileNotFoundException> { - fileSystem.atomicMove(source, target) - } - } - - @Test - fun atomicMoveSourceIsFileAndTargetIsDirectory() { - val source = base / "source" - source.writeUtf8("hello, world!") - val target = base / "target" - fileSystem.createDirectory(target) - assertFailsWith<IOException> { - fileSystem.atomicMove(source, target) - } - } - - @Test - fun atomicMoveSourceIsDirectoryAndTargetIsFile() { - val source = base / "source" - fileSystem.createDirectory(source) - val target = base / "target" - target.writeUtf8("hello, world!") - expectIOExceptionOnEverythingButWindows { - fileSystem.atomicMove(source, target) - } - } - - @Test - fun copyFile() { - val source = base / "source" - source.writeUtf8("hello, world!") - val target = base / "target" - fileSystem.copy(source, target) - assertTrue(target in fileSystem.list(base)) - assertEquals("hello, world!", source.readUtf8()) - assertEquals("hello, world!", target.readUtf8()) - } - - @Test - fun copySourceDoesNotExist() { - val source = base / "source" - val target = base / "target" - assertFailsWith<FileNotFoundException> { - fileSystem.copy(source, target) - } - assertFalse(target in fileSystem.list(base)) - } - - @Test - fun copyTargetIsClobbered() { - val source = base / "source" - source.writeUtf8("hello, world!") - val target = base / "target" - target.writeUtf8("this file will be clobbered!") - fileSystem.copy(source, target) - assertTrue(target in fileSystem.list(base)) - assertEquals("hello, world!", target.readUtf8()) - } - - @Test - fun deleteFile() { - val path = base / "delete-file" - path.writeUtf8("delete me") - fileSystem.delete(path) - assertTrue(path !in fileSystem.list(base)) - } - - @Test - fun deleteEmptyDirectory() { - val path = base / "delete-empty-directory" - fileSystem.createDirectory(path) - fileSystem.delete(path) - assertTrue(path !in fileSystem.list(base)) - } - - @Test - fun deleteFailsOnNoSuchFile() { - val path = base / "no-such-file" - // TODO(jwilson): fix Windows to throw FileNotFoundException on deleting an absent file. - if (windowsLimitations) { - assertFailsWith<IOException> { - fileSystem.delete(path) - } - } else { - assertFailsWith<FileNotFoundException> { - fileSystem.delete(path) - } - } - } - - @Test - fun deleteFailsOnNonemptyDirectory() { - val path = base / "non-empty-directory" - fileSystem.createDirectory(path) - (path / "file.txt").writeUtf8("inside directory") - assertFailsWith<IOException> { - fileSystem.delete(path) - } - } - - @Test - fun deleteRecursivelyFile() { - val path = base / "delete-recursively-file" - path.writeUtf8("delete me") - fileSystem.deleteRecursively(path) - assertTrue(path !in fileSystem.list(base)) - } - - @Test - fun deleteRecursivelyEmptyDirectory() { - val path = base / "delete-recursively-empty-directory" - fileSystem.createDirectory(path) - fileSystem.deleteRecursively(path) - assertTrue(path !in fileSystem.list(base)) - } - - @Test - fun deleteRecursivelyFailsOnNoSuchFile() { - val path = base / "no-such-file" - assertFailsWith<FileNotFoundException> { - fileSystem.deleteRecursively(path) - } - } - - @Test - fun deleteRecursivelyNonemptyDirectory() { - val path = base / "delete-recursively-non-empty-directory" - fileSystem.createDirectory(path) - (path / "file.txt").writeUtf8("inside directory") - fileSystem.deleteRecursively(path) - assertTrue(path !in fileSystem.list(base)) - assertTrue((path / "file.txt") !in fileSystem.list(base)) - } - - @Test - fun deleteRecursivelyDeepHierarchy() { - fileSystem.createDirectory(base / "a") - fileSystem.createDirectory(base / "a" / "b") - fileSystem.createDirectory(base / "a" / "b" / "c") - (base / "a" / "b" / "c" / "d.txt").writeUtf8("inside deep hierarchy") - fileSystem.deleteRecursively(base / "a") - assertEquals(fileSystem.list(base), listOf()) - } - - @Test - fun fileMetadata() { - val minTime = clock.now().minFileSystemTime() - val path = base / "file-metadata" - path.writeUtf8("hello, world!") - val maxTime = clock.now().maxFileSystemTime() - - val metadata = fileSystem.metadata(path) - assertTrue(metadata.isRegularFile) - assertFalse(metadata.isDirectory) - assertEquals(13, metadata.size) - assertInRange(metadata.createdAt, minTime, maxTime) - assertInRange(metadata.lastModifiedAt, minTime, maxTime) - assertInRange(metadata.lastAccessedAt, minTime, maxTime) - } - - @Test - fun directoryMetadata() { - val minTime = clock.now().minFileSystemTime() - val path = base / "directory-metadata" - fileSystem.createDirectory(path) - val maxTime = clock.now().maxFileSystemTime() - - val metadata = fileSystem.metadata(path) - assertFalse(metadata.isRegularFile) - assertTrue(metadata.isDirectory) - // Note that the size check is omitted; we'd expect null but the JVM returns values like 64. - assertInRange(metadata.createdAt, minTime, maxTime) - assertInRange(metadata.lastModifiedAt, minTime, maxTime) - assertInRange(metadata.lastAccessedAt, minTime, maxTime) - } - - @Test - fun absentMetadataOrNull() { - val path = base / "no-such-file" - assertNull(fileSystem.metadataOrNull(path)) - } - - @Test - @Ignore - fun inaccessibleMetadata() { - // TODO(swankjesse): configure a test directory in CI that exists, but that this process doesn't - // have permission to read metadata of. Perhaps a file in another user's /home directory? - } - - @Test - fun absentMetadata() { - val path = base / "no-such-file" - assertFailsWith<FileNotFoundException> { - fileSystem.metadata(path) - } - } - - @Test - fun fileExists() { - val path = base / "file-exists" - assertFalse(fileSystem.exists(path)) - path.writeUtf8("hello, world!") - assertTrue(fileSystem.exists(path)) - } - - @Test - fun directoryExists() { - val path = base / "directory-exists" - assertFalse(fileSystem.exists(path)) - fileSystem.createDirectory(path) - assertTrue(fileSystem.exists(path)) - } - - @Test - fun deleteOpenForWritingFailsOnWindows() { - val file = base / "file.txt" - expectIOExceptionOnWindows(exceptJs = true) { - fileSystem.sink(file).use { - fileSystem.delete(file) - } - } - } - - @Test - fun deleteOpenForReadingFailsOnWindows() { - val file = base / "file.txt" - file.writeUtf8("abc") - expectIOExceptionOnWindows(exceptJs = true) { - fileSystem.source(file).use { - fileSystem.delete(file) - } - } - } - - @Test - fun renameSourceIsOpenFailsOnWindows() { - val from = base / "from.txt" - val to = base / "to.txt" - from.writeUtf8("source file") - to.writeUtf8("target file") - expectIOExceptionOnWindows(exceptJs = true) { - fileSystem.source(from).use { - fileSystem.atomicMove(from, to) - } - } - } - - @Test - fun renameTargetIsOpenFailsOnWindows() { - val from = base / "from.txt" - val to = base / "to.txt" - from.writeUtf8("source file") - to.writeUtf8("target file") - expectIOExceptionOnWindows { - fileSystem.source(to).use { - fileSystem.atomicMove(from, to) - } - } - } - - @Test - fun deleteContentsOfParentOfFileOpenForReadingFailsOnWindows() { - val parentA = (base / "a") - fileSystem.createDirectory(parentA) - val parentAB = parentA / "b" - fileSystem.createDirectory(parentAB) - val parentABC = parentAB / "c" - fileSystem.createDirectory(parentABC) - val file = parentABC / "file.txt" - file.writeUtf8("child file") - expectIOExceptionOnWindows { - fileSystem.source(file).use { - fileSystem.delete(file) - fileSystem.delete(parentABC) - fileSystem.delete(parentAB) - fileSystem.delete(parentA) - } - } - } - - private fun expectIOExceptionOnWindows(exceptJs: Boolean = false, block: () -> Unit) { - val expectCrash = windowsLimitations && (!isJs || !exceptJs) - try { - block() - assertFalse(expectCrash) - } catch (_: IOException) { - assertTrue(expectCrash) - } - } - - private fun expectIOExceptionOnEverythingButWindows(block: () -> Unit) { - try { - block() - assertTrue(windowsLimitations) - } catch (e: IOException) { - assertFalse(windowsLimitations) - } - } - - private fun randomToken() = Random.nextBytes(16).toByteString(0, 16).hex() - - fun Path.readUtf8(): String { - return fileSystem.source(this).buffer().use { - it.readUtf8() - } - } - - fun Path.writeUtf8(string: String) { - fileSystem.sink(this).buffer().use { - it.writeUtf8(string) - } - } - - /** - * Returns the earliest file system time that could be recorded for an event occurring at this - * instant. This truncates fractional seconds because most host file systems do not use precise - * timestamps for file metadata. - */ - private fun Instant.minFileSystemTime(): Instant { - return Instant.fromEpochSeconds(epochSeconds) - } - - /** - * Returns the latest file system time that could be recorded for an event occurring at this - * instant. This adds 2 seconds and truncates fractional seconds because file systems may defer - * assigning the timestamp. - * - * https://docs.microsoft.com/en-us/windows/win32/sysinfo/file-times - */ - private fun Instant.maxFileSystemTime(): Instant { - return Instant.fromEpochSeconds(plus(2.seconds).epochSeconds) - } - - private fun assertInRange(sampled: Instant?, minTime: Instant, maxTime: Instant) { - if (sampled == null) return - assertTrue("expected $sampled in $minTime..$maxTime") { sampled in minTime..maxTime } - } -} diff --git a/okio/src/commonTest/kotlin/okio/BufferCommonTest.kt b/okio/src/commonTest/kotlin/okio/BufferCommonTest.kt index 842faffe..cc946883 100644 --- a/okio/src/commonTest/kotlin/okio/BufferCommonTest.kt +++ b/okio/src/commonTest/kotlin/okio/BufferCommonTest.kt @@ -15,9 +15,9 @@ */ package okio -import okio.ByteString.Companion.encodeUtf8 import kotlin.test.Test import kotlin.test.assertEquals +import okio.ByteString.Companion.encodeUtf8 class BufferCommonTest { diff --git a/okio/src/commonTest/kotlin/okio/BufferedSourceFactory.kt b/okio/src/commonTest/kotlin/okio/BufferedSourceFactory.kt index b9836202..173bb841 100644 --- a/okio/src/commonTest/kotlin/okio/BufferedSourceFactory.kt +++ b/okio/src/commonTest/kotlin/okio/BufferedSourceFactory.kt @@ -19,7 +19,7 @@ package okio interface BufferedSourceFactory { class Pipe( var sink: BufferedSink, - var source: BufferedSource + var source: BufferedSource, ) val isOneByteAtATime: Boolean @@ -36,7 +36,7 @@ interface BufferedSourceFactory { val buffer = Buffer() return Pipe( buffer, - buffer + buffer, ) } } @@ -51,7 +51,7 @@ interface BufferedSourceFactory { val buffer = Buffer() return Pipe( buffer, - (buffer as Source).buffer() + (buffer as Source).buffer(), ) } } @@ -79,7 +79,7 @@ interface BufferedSourceFactory { if (result > 0L) sink.write(box.copy(), result) return result } - }.buffer() + }.buffer(), ) } } @@ -104,7 +104,7 @@ interface BufferedSourceFactory { } } }.buffer(), - buffer + buffer, ) } } @@ -118,7 +118,7 @@ interface BufferedSourceFactory { val buffer = Buffer() return Pipe( buffer, - buffer.peek() + buffer.peek(), ) } } @@ -133,7 +133,7 @@ interface BufferedSourceFactory { val buffer = Buffer() return Pipe( buffer, - (buffer as Source).buffer().peek() + (buffer as Source).buffer().peek(), ) } } @@ -144,7 +144,7 @@ interface BufferedSourceFactory { arrayOf(ONE_BYTE_AT_A_TIME_BUFFERED_SOURCE), arrayOf(ONE_BYTE_AT_A_TIME_BUFFER), arrayOf(PEEK_BUFFER), - arrayOf(PEEK_BUFFERED_SOURCE) + arrayOf(PEEK_BUFFERED_SOURCE), ) } } diff --git a/okio/src/commonTest/kotlin/okio/FakeClock.kt b/okio/src/commonTest/kotlin/okio/ByteStringMoreTests.kt index 31cf5503..d9721d16 100644 --- a/okio/src/commonTest/kotlin/okio/FakeClock.kt +++ b/okio/src/commonTest/kotlin/okio/ByteStringMoreTests.kt @@ -1,11 +1,11 @@ /* - * Copyright (C) 2020 Square, Inc. + * Copyright (C) 2022 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 + * 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, @@ -13,20 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package okio - -import kotlinx.datetime.Clock -import kotlinx.datetime.Instant -import kotlin.time.Duration -import kotlin.time.ExperimentalTime -@ExperimentalTime -internal class FakeClock : Clock { - var time = Instant.parse("2021-01-01T00:00:00Z") +package okio - override fun now() = time +import kotlin.test.Test +import kotlin.test.assertEquals +import okio.ByteString.Companion.toByteString - fun sleep(duration: Duration) { - time = time.plus(duration) +class ByteStringMoreTests { + @Test fun arrayToByteString() { + val actual = byteArrayOf(1, 2, 3, 4).toByteString() + val expected = ByteString.of(1, 2, 3, 4) + assertEquals(actual, expected) } } diff --git a/okio/src/commonTest/kotlin/okio/ByteStringTest.kt b/okio/src/commonTest/kotlin/okio/ByteStringTest.kt index c75c4581..9866e14b 100644 --- a/okio/src/commonTest/kotlin/okio/ByteStringTest.kt +++ b/okio/src/commonTest/kotlin/okio/ByteStringTest.kt @@ -16,10 +16,6 @@ package okio -import okio.ByteString.Companion.decodeBase64 -import okio.ByteString.Companion.decodeHex -import okio.ByteString.Companion.encodeUtf8 -import okio.internal.commonAsUtf8ToByteArray import kotlin.random.Random import kotlin.test.Test import kotlin.test.assertEquals @@ -29,6 +25,11 @@ import kotlin.test.assertNotEquals import kotlin.test.assertSame import kotlin.test.assertTrue import kotlin.test.fail +import okio.ByteString.Companion.decodeBase64 +import okio.ByteString.Companion.decodeHex +import okio.ByteString.Companion.encodeUtf8 +import okio.ByteString.Companion.toByteString +import okio.internal.commonAsUtf8ToByteArray class ByteStringTest : AbstractByteStringTest(ByteStringFactory.BYTE_STRING) class SegmentedByteStringTest : AbstractByteStringTest(ByteStringFactory.SEGMENTED_BYTE_STRING) @@ -36,14 +37,14 @@ class ByteStringOneBytePerSegmentTest : AbstractByteStringTest(ByteStringFactory class OkioEncoderTest : AbstractByteStringTest(ByteStringFactory.OKIO_ENCODER) abstract class AbstractByteStringTest internal constructor( - private val factory: ByteStringFactory + private val factory: ByteStringFactory, ) { @Test fun get() { val actual = factory.encodeUtf8("abc") assertEquals(3, actual.size) - assertEquals(actual[0], 'a'.toByte()) - assertEquals(actual[1], 'b'.toByte()) - assertEquals(actual[2], 'c'.toByte()) + assertEquals(actual[0], 'a'.code.toByte()) + assertEquals(actual[1], 'b'.code.toByte()) + assertEquals(actual[2], 'c'.code.toByte()) try { actual[-1] fail("no index out of bounds: -1") @@ -214,7 +215,7 @@ abstract class AbstractByteStringTest internal constructor( ( "d09dd0b020d0b1d0b5d180d0b5d0b3d18320d0bfd183d181" + "d182d18bd0bdd0bdd18bd18520d0b2d0bed0bbd0bd" - ).decodeHex() + ).decodeHex(), ) assertEquals(byteString.utf8(), bronzeHorseman) } @@ -277,7 +278,7 @@ abstract class AbstractByteStringTest internal constructor( assertEquals("AAAA", factory.encodeUtf8("\u0000\u0000\u0000").base64()) assertEquals( "SG93IG1hbnkgbGluZXMgb2YgY29kZSBhcmUgdGhlcmU/ICdib3V0IDIgbWlsbGlvbi4=", - factory.encodeUtf8("How many lines of code are there? 'bout 2 million.").base64() + factory.encodeUtf8("How many lines of code are there? 'bout 2 million.").base64(), ) } @@ -288,7 +289,7 @@ abstract class AbstractByteStringTest internal constructor( assertEquals("AAAA", factory.encodeUtf8("\u0000\u0000\u0000").base64Url()) assertEquals( "SG93IG1hbnkgbGluZXMgb2YgY29kZSBhcmUgdGhlcmU_ICdib3V0IDIgbWlsbGlvbi4=", - factory.encodeUtf8("How many lines of code are there? 'bout 2 million.").base64Url() + factory.encodeUtf8("How many lines of code are there? 'bout 2 million.").base64Url(), ) } @@ -313,7 +314,7 @@ abstract class AbstractByteStringTest internal constructor( ( "V2hhdCdzIHRvIGJlIHNjYXJlZCBhYm91dD8gSXQncyBqdXN0IGEgbGl0dGxlIGhpY2" + "N1cCBpbiB0aGUgcG93ZXIuLi4=" - ).decodeBase64()!!.utf8() + ).decodeBase64()!!.utf8(), ) // Uses two encoding styles. Malformed, but supported as a side-effect. assertEquals("ffffff".decodeHex(), "__//".decodeBase64()) @@ -358,11 +359,11 @@ abstract class AbstractByteStringTest internal constructor( @Test fun toStringOnShortText() { assertEquals( "[text=Tyrannosaur]", - factory.encodeUtf8("Tyrannosaur").toString() + factory.encodeUtf8("Tyrannosaur").toString(), ) assertEquals( "[text=təˈranəˌsôr]", - factory.decodeHex("74c999cb8872616ec999cb8c73c3b472").toString() + factory.decodeHex("74c999cb8872616ec999cb8c73c3b472").toString(), ) } @@ -379,7 +380,7 @@ abstract class AbstractByteStringTest internal constructor( assertEquals( "[size=517 text=Um, I'll tell you the problem with the scientific power that " + "you…]", - factory.encodeUtf8(raw).toString() + factory.encodeUtf8(raw).toString(), ) val war = ( "Սm, I'll 𝓽𝖾ll ᶌօ𝘂 ᴛℎ℮ 𝜚𝕣०bl𝖾m wі𝕥𝒽 𝘵𝘩𝐞 𝓼𝙘𝐢𝔢𝓷𝗍𝜄𝚏𝑖c 𝛠𝝾w𝚎𝑟 𝕥h⍺𝞃 𝛄𝓸𝘂'𝒓𝗲 υ𝖘𝓲𝗇ɡ 𝕙𝚎𝑟e, " + @@ -392,7 +393,7 @@ abstract class AbstractByteStringTest internal constructor( assertEquals( "[size=1496 text=Սm, I'll 𝓽𝖾ll ᶌօ𝘂 ᴛℎ℮ 𝜚𝕣०bl𝖾m wі𝕥𝒽 𝘵𝘩𝐞 𝓼𝙘𝐢𝔢𝓷𝗍𝜄𝚏𝑖c 𝛠𝝾w𝚎𝑟 𝕥h⍺𝞃 " + "𝛄𝓸𝘂…]", - factory.encodeUtf8(war).toString() + factory.encodeUtf8(war).toString(), ) } @@ -400,7 +401,7 @@ abstract class AbstractByteStringTest internal constructor( // Instead of emitting a literal newline in the toString(), these are escaped as "\n". assertEquals( "[text=a\\r\\nb\\nc\\rd\\\\e]", - factory.encodeUtf8("a\r\nb\nc\rd\\e").toString() + factory.encodeUtf8("a\r\nb\nc\rd\\e").toString(), ) } @@ -408,13 +409,13 @@ abstract class AbstractByteStringTest internal constructor( val byteString = factory.decodeHex( "" + "60b420bb3851d9d47acb933dbe70399bf6c92da33af01d4fb770e98c0325f41d3ebaf8986da712c82bcd4d55" + - "4bf0b54023c29b624de9ef9c2f931efc580f9afb" + "4bf0b54023c29b624de9ef9c2f931efc580f9afb", ) assertEquals( "[hex=" + "60b420bb3851d9d47acb933dbe70399bf6c92da33af01d4fb770e98c0325f41d3ebaf8986da712c82bcd4d55" + "4bf0b54023c29b624de9ef9c2f931efc580f9afb]", - byteString.toString() + byteString.toString(), ) } @@ -422,13 +423,13 @@ abstract class AbstractByteStringTest internal constructor( val byteString = factory.decodeHex( "" + "60b420bb3851d9d47acb933dbe70399bf6c92da33af01d4fb770e98c0325f41d3ebaf8986da712c82bcd4d55" + - "4bf0b54023c29b624de9ef9c2f931efc580f9afba1" + "4bf0b54023c29b624de9ef9c2f931efc580f9afba1", ) assertEquals( "[size=65 hex=" + "60b420bb3851d9d47acb933dbe70399bf6c92da33af01d4fb770e98c0325f41d3ebaf8986da712c82bcd4d55" + "4bf0b54023c29b624de9ef9c2f931efc580f9afb…]", - byteString.toString() + byteString.toString(), ) } @@ -441,7 +442,7 @@ abstract class AbstractByteStringTest internal constructor( factory.decodeHex("80"), factory.decodeHex("81"), factory.decodeHex("fe"), - factory.decodeHex("ff") + factory.decodeHex("ff"), ) val sortedByteStrings = originalByteStrings.toMutableList() @@ -479,7 +480,7 @@ abstract class AbstractByteStringTest internal constructor( factory.decodeHex("010101"), factory.decodeHex("7f0000"), factory.decodeHex("7f0000ffff"), - factory.decodeHex("ffffff") + factory.decodeHex("ffffff"), ) val sortedByteStrings = originalByteStrings.toMutableList() @@ -496,4 +497,101 @@ abstract class AbstractByteStringTest internal constructor( assertEquals("0e4dd66217fc8d2e298b78c8cd9392870dcd065d0ff675d0edff5bcd227837e9", sha256().hex()) assertEquals("483676b93c4417198b465083d196ec6a9fab8d004515874b8ff47e041f5f56303cc08179625030b8b5b721c09149a18f0f59e64e7ae099518cea78d3d83167e1", sha512().hex()) } + + @Test fun copyInto() { + val byteString = factory.encodeUtf8("abcdefgh") + val byteArray = "WwwwXxxxYyyyZzzz".encodeToByteArray() + byteString.copyInto(target = byteArray, byteCount = 5) + assertEquals("abcdexxxYyyyZzzz", byteArray.decodeToString()) + } + + @Test fun copyIntoFullRange() { + val byteString = factory.encodeUtf8("abcdefghijklmnop") + val byteArray = "WwwwXxxxYyyyZzzz".encodeToByteArray() + byteString.copyInto(target = byteArray, byteCount = 16) + assertEquals("abcdefghijklmnop", byteArray.decodeToString()) + } + + @Test fun copyIntoWithTargetOffset() { + val byteString = factory.encodeUtf8("abcdefgh") + val byteArray = "WwwwXxxxYyyyZzzz".encodeToByteArray() + byteString.copyInto(target = byteArray, targetOffset = 11, byteCount = 5) + assertEquals("WwwwXxxxYyyabcde", byteArray.decodeToString()) + } + + @Test fun copyIntoWithSourceOffset() { + val byteString = factory.encodeUtf8("abcdefgh") + val byteArray = "WwwwXxxxYyyyZzzz".encodeToByteArray() + byteString.copyInto(offset = 3, target = byteArray, byteCount = 5) + assertEquals("defghxxxYyyyZzzz", byteArray.decodeToString()) + } + + @Test fun copyIntoWithAllParameters() { + val byteString = factory.encodeUtf8("abcdefgh") + val byteArray = "WwwwXxxxYyyyZzzz".encodeToByteArray() + byteString.copyInto(offset = 3, target = byteArray, targetOffset = 11, byteCount = 5) + assertEquals("WwwwXxxxYyydefgh", byteArray.decodeToString()) + } + + @Test fun copyIntoBoundsChecks() { + val byteString = factory.encodeUtf8("abcdefgh") + val byteArray = "WwwwXxxxYyyyZzzz".encodeToByteArray() + assertFailsWith<IndexOutOfBoundsException> { + byteString.copyInto(offset = -1, target = byteArray, targetOffset = 1, byteCount = 1) + } + assertFailsWith<IndexOutOfBoundsException> { + byteString.copyInto(offset = 9, target = byteArray, targetOffset = 0, byteCount = 0) + } + assertFailsWith<IndexOutOfBoundsException> { + byteString.copyInto(offset = 1, target = byteArray, targetOffset = -1, byteCount = 1) + } + assertFailsWith<IndexOutOfBoundsException> { + byteString.copyInto(offset = 1, target = byteArray, targetOffset = 17, byteCount = 1) + } + assertFailsWith<IndexOutOfBoundsException> { + byteString.copyInto(offset = 7, target = byteArray, targetOffset = 1, byteCount = 2) + } + assertFailsWith<IndexOutOfBoundsException> { + byteString.copyInto(offset = 1, target = byteArray, targetOffset = 15, byteCount = 2) + } + } + + @Test fun copyEmptyAtBounds() { + val byteString = factory.encodeUtf8("abcdefgh") + val byteArray = "WwwwXxxxYyyyZzzz".encodeToByteArray() + byteString.copyInto(offset = 0, target = byteArray, targetOffset = 0, byteCount = 0) + assertEquals("WwwwXxxxYyyyZzzz", byteArray.decodeToString()) + byteString.copyInto(offset = 0, target = byteArray, targetOffset = 16, byteCount = 0) + assertEquals("WwwwXxxxYyyyZzzz", byteArray.decodeToString()) + byteString.copyInto(offset = 8, target = byteArray, targetOffset = 0, byteCount = 0) + assertEquals("WwwwXxxxYyyyZzzz", byteArray.decodeToString()) + } + + @Test + fun ofCopy() { + val bytes = "Hello, World!".encodeToByteArray() + val byteString = ByteString.of(*bytes) + // Verify that the bytes were copied out. + bytes[4] = 'a'.code.toByte() + assertEquals("Hello, World!", byteString.utf8()) + } + + @Test + fun ofCopyRange() { + val bytes = "Hello, World!".encodeToByteArray() + val byteString: ByteString = bytes.toByteString(2, 9) + // Verify that the bytes were copied out. + bytes[4] = 'a'.code.toByte() + assertEquals("llo, Worl", byteString.utf8()) + } + + @Test + fun getByteOutOfBounds() { + val byteString = factory.decodeHex("ab12") + try { + byteString[2] + fail() + } catch (expected: IndexOutOfBoundsException) { + } + } } diff --git a/okio/src/commonTest/kotlin/okio/CommonBufferTest.kt b/okio/src/commonTest/kotlin/okio/CommonBufferTest.kt index 292e3c5c..0dbaf021 100644 --- a/okio/src/commonTest/kotlin/okio/CommonBufferTest.kt +++ b/okio/src/commonTest/kotlin/okio/CommonBufferTest.kt @@ -15,13 +15,13 @@ */ package okio -import okio.ByteString.Companion.decodeHex import kotlin.random.Random import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith import kotlin.test.assertFalse import kotlin.test.assertTrue +import okio.ByteString.Companion.decodeHex /** * Tests solely for the behavior of Buffer's implementation. For generic BufferedSink or @@ -48,22 +48,22 @@ class CommonBufferTest { assertEquals("[size=0]", Buffer().toString()) assertEquals( "[text=a\\r\\nb\\nc\\rd\\\\e]", - Buffer().writeUtf8("a\r\nb\nc\rd\\e").toString() + Buffer().writeUtf8("a\r\nb\nc\rd\\e").toString(), ) assertEquals( "[text=Tyrannosaur]", - Buffer().writeUtf8("Tyrannosaur").toString() + Buffer().writeUtf8("Tyrannosaur").toString(), ) assertEquals( "[text=təˈranəˌsôr]", Buffer() .write("74c999cb8872616ec999cb8c73c3b472".decodeHex()) - .toString() + .toString(), ) assertEquals( "[hex=0000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000]", - Buffer().write(ByteArray(64)).toString() + Buffer().write(ByteArray(64)).toString(), ) } @@ -127,10 +127,16 @@ class CommonBufferTest { val segmentSizes = moveBytesBetweenBuffers('a'.repeat(size), 'b'.repeat(size)) assertEquals( listOf( - Segment.SIZE, Segment.SIZE, Segment.SIZE, 1, - Segment.SIZE, Segment.SIZE, Segment.SIZE, 1 + Segment.SIZE, + Segment.SIZE, + Segment.SIZE, + 1, + Segment.SIZE, + Segment.SIZE, + Segment.SIZE, + 1, ), - segmentSizes + segmentSizes, ) } @@ -240,14 +246,14 @@ class CommonBufferTest { buffer.writeUtf8('b'.repeat(halfSegment)) buffer.writeUtf8('c'.repeat(halfSegment)) buffer.writeUtf8('d'.repeat(halfSegment)) - assertEquals(0, buffer.indexOf('a'.toByte(), 0)) - assertEquals((halfSegment - 1).toLong(), buffer.indexOf('a'.toByte(), (halfSegment - 1).toLong())) - assertEquals(halfSegment.toLong(), buffer.indexOf('b'.toByte(), (halfSegment - 1).toLong())) - assertEquals((halfSegment * 2).toLong(), buffer.indexOf('c'.toByte(), (halfSegment - 1).toLong())) - assertEquals((halfSegment * 3).toLong(), buffer.indexOf('d'.toByte(), (halfSegment - 1).toLong())) - assertEquals((halfSegment * 3).toLong(), buffer.indexOf('d'.toByte(), (halfSegment * 2).toLong())) - assertEquals((halfSegment * 3).toLong(), buffer.indexOf('d'.toByte(), (halfSegment * 3).toLong())) - assertEquals((halfSegment * 4 - 1).toLong(), buffer.indexOf('d'.toByte(), (halfSegment * 4 - 1).toLong())) + assertEquals(0, buffer.indexOf('a'.code.toByte(), 0)) + assertEquals((halfSegment - 1).toLong(), buffer.indexOf('a'.code.toByte(), (halfSegment - 1).toLong())) + assertEquals(halfSegment.toLong(), buffer.indexOf('b'.code.toByte(), (halfSegment - 1).toLong())) + assertEquals((halfSegment * 2).toLong(), buffer.indexOf('c'.code.toByte(), (halfSegment - 1).toLong())) + assertEquals((halfSegment * 3).toLong(), buffer.indexOf('d'.code.toByte(), (halfSegment - 1).toLong())) + assertEquals((halfSegment * 3).toLong(), buffer.indexOf('d'.code.toByte(), (halfSegment * 2).toLong())) + assertEquals((halfSegment * 3).toLong(), buffer.indexOf('d'.code.toByte(), (halfSegment * 3).toLong())) + assertEquals((halfSegment * 4 - 1).toLong(), buffer.indexOf('d'.code.toByte(), (halfSegment * 4 - 1).toLong())) } @Test fun byteAt() { @@ -255,11 +261,11 @@ class CommonBufferTest { buffer.writeUtf8("a") buffer.writeUtf8('b'.repeat(Segment.SIZE)) buffer.writeUtf8("c") - assertEquals('a'.toLong(), buffer[0].toLong()) - assertEquals('a'.toLong(), buffer[0].toLong()) // getByte doesn't mutate! - assertEquals('c'.toLong(), buffer[buffer.size - 1].toLong()) - assertEquals('b'.toLong(), buffer[buffer.size - 2].toLong()) - assertEquals('b'.toLong(), buffer[buffer.size - 3].toLong()) + assertEquals('a'.code.toLong(), buffer[0].toLong()) + assertEquals('a'.code.toLong(), buffer[0].toLong()) // getByte doesn't mutate! + assertEquals('c'.code.toLong(), buffer[buffer.size - 1].toLong()) + assertEquals('b'.code.toLong(), buffer[buffer.size - 2].toLong()) + assertEquals('b'.code.toLong(), buffer[buffer.size - 3].toLong()) } @Test fun getByteOfEmptyBuffer() { @@ -279,7 +285,8 @@ class CommonBufferTest { } @Suppress("ReplaceAssertBooleanWithAssertEquality") - @Test fun equalsAndHashCodeEmpty() { + @Test + fun equalsAndHashCodeEmpty() { val a = Buffer() val b = Buffer() assertTrue(a == b) @@ -287,7 +294,8 @@ class CommonBufferTest { } @Suppress("ReplaceAssertBooleanWithAssertEquality") - @Test fun equalsAndHashCode() { + @Test + fun equalsAndHashCode() { val a = Buffer().writeUtf8("dog") val b = Buffer().writeUtf8("hotdog") assertFalse(a == b) @@ -299,7 +307,8 @@ class CommonBufferTest { } @Suppress("ReplaceAssertBooleanWithAssertEquality") - @Test fun equalsAndHashCodeSpanningSegments() { + @Test + fun equalsAndHashCodeSpanningSegments() { val data = ByteArray(1024 * 1024) val dice = Random(0) dice.nextBytes(data) @@ -323,13 +332,13 @@ class CommonBufferTest { val write1 = Buffer().writeUtf8( 'a'.repeat(Segment.SIZE) + 'b'.repeat(Segment.SIZE) + - 'c'.repeat(Segment.SIZE) + 'c'.repeat(Segment.SIZE), ) val source = Buffer().writeUtf8( 'a'.repeat(Segment.SIZE) + 'b'.repeat(Segment.SIZE) + - 'c'.repeat(Segment.SIZE) + 'c'.repeat(Segment.SIZE), ) val mockSink = MockSink() diff --git a/okio/src/commonTest/kotlin/okio/CommonOptionsTest.kt b/okio/src/commonTest/kotlin/okio/CommonOptionsTest.kt index bb45321d..ae8156d6 100644 --- a/okio/src/commonTest/kotlin/okio/CommonOptionsTest.kt +++ b/okio/src/commonTest/kotlin/okio/CommonOptionsTest.kt @@ -15,11 +15,11 @@ */ package okio -import okio.ByteString.Companion.encodeUtf8 import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith import kotlin.test.fail +import okio.ByteString.Companion.encodeUtf8 class CommonOptionsTest { /** Confirm that options prefers the first-listed option, not the longest or shortest one. */ @@ -32,12 +32,13 @@ class CommonOptionsTest { assertEquals( utf8Options("hotdog", "hoth", "hot").trieString(), """ - |hot - | -> 2 - | d - | og -> 0 - | h -> 1 - |""".trimMargin() + |hot + | -> 2 + | d + | og -> 0 + | h -> 1 + | + """.trimMargin(), ) } @@ -70,81 +71,82 @@ class CommonOptionsTest { "connectTimeout", "readTimeout", "writeTimeout", - "pingInterval" + "pingInterval", ) assertEquals( options.trieString(), """ - |a - | uthenticator -> 17 - |c - | a - | che -> 9 - | e - | rtificate - | C - | hainCleaner -> 13 - | P - | inner -> 15 - | o - | n - | nect - | T - | imeout -> 23 - | i - | on - | P - | ool -> 18 - | S - | pecs -> 3 - | o - | kieJar -> 8 - |d - | i - | spatcher -> 0 - | n - | s -> 19 - |e - | ventListenerFactory -> 6 - |f - | ollow - | R - | edirects -> 21 - | S - | slRedirects -> 20 - |h - | ostnameVerifier -> 14 - |i - | nter - | c - | eptors -> 4 - | n - | alCache -> 10 - |n - | etworkInterceptors -> 5 - |p - | i - | ngInterval -> 26 - | r - | o - | t - | ocols -> 2 - | x - | y -> 1 - |r - | e - | a - | dTimeout -> 24 - | t - | ryOnConnectionFailure -> 22 - |s - | o - | cketFactory -> 11 - | s - | lSocketFactory -> 12 - |w - | riteTimeout -> 25 - |""".trimMargin() + |a + | uthenticator -> 17 + |c + | a + | che -> 9 + | e + | rtificate + | C + | hainCleaner -> 13 + | P + | inner -> 15 + | o + | n + | nect + | T + | imeout -> 23 + | i + | on + | P + | ool -> 18 + | S + | pecs -> 3 + | o + | kieJar -> 8 + |d + | i + | spatcher -> 0 + | n + | s -> 19 + |e + | ventListenerFactory -> 6 + |f + | ollow + | R + | edirects -> 21 + | S + | slRedirects -> 20 + |h + | ostnameVerifier -> 14 + |i + | nter + | c + | eptors -> 4 + | n + | alCache -> 10 + |n + | etworkInterceptors -> 5 + |p + | i + | ngInterval -> 26 + | r + | o + | t + | ocols -> 2 + | x + | y -> 1 + |r + | e + | a + | dTimeout -> 24 + | t + | ryOnConnectionFailure -> 22 + |s + | o + | cketFactory -> 11 + | s + | lSocketFactory -> 12 + |w + | riteTimeout -> 25 + | + """.trimMargin(), ) assertSelect("", -1, options) assertSelect("a", -1, options) @@ -212,10 +214,11 @@ class CommonOptionsTest { assertEquals( options.trieString(), """ - |abc - | -> 1 - | A -> 0 - |""".trimMargin() + |abc + | -> 1 + | A -> 0 + | + """.trimMargin(), ) assertSelect("abc", 1, options) assertSelect("abcA", 0, options) @@ -228,35 +231,39 @@ class CommonOptionsTest { assertEquals( utf8Options("a", "ab", "abc", "abcd", "abcde").trieString(), """ - |a -> 0 - |""".trimMargin() + |a -> 0 + | + """.trimMargin(), ) assertEquals( utf8Options("abc", "a", "ab", "abe", "abcd", "abcf").trieString(), """ - |a - | -> 1 - | bc -> 0 - |""".trimMargin() + |a + | -> 1 + | bc -> 0 + | + """.trimMargin(), ) assertEquals( utf8Options("abc", "ab", "a").trieString(), """ - |a - | -> 2 - | b - | -> 1 - | c -> 0 - |""".trimMargin() + |a + | -> 2 + | b + | -> 1 + | c -> 0 + | + """.trimMargin(), ) assertEquals( utf8Options("abcd", "abce", "abc", "abcf", "abcg").trieString(), """ - |abc - | -> 2 - | d -> 0 - | e -> 1 - |""".trimMargin() + |abc + | -> 2 + | d -> 0 + | e -> 1 + | + """.trimMargin(), ) } diff --git a/okio/src/commonTest/kotlin/okio/CommonRealBufferedSinkTest.kt b/okio/src/commonTest/kotlin/okio/CommonRealBufferedSinkTest.kt index abbbae8b..eec7607a 100644 --- a/okio/src/commonTest/kotlin/okio/CommonRealBufferedSinkTest.kt +++ b/okio/src/commonTest/kotlin/okio/CommonRealBufferedSinkTest.kt @@ -47,7 +47,7 @@ class CommonRealBufferedSinkTest { @Test fun bufferedSinkFlush() { val sink = Buffer() val bufferedSink = (sink as Sink).buffer() - bufferedSink.writeByte('a'.toInt()) + bufferedSink.writeByte('a'.code) assertEquals(0, sink.size) bufferedSink.flush() assertEquals(0, bufferedSink.buffer.size) @@ -93,9 +93,9 @@ class CommonRealBufferedSinkTest { @Test fun closeWithExceptionWhenWriting() { val mockSink = MockSink() - mockSink.scheduleThrow(0, IOException()) + mockSink.scheduleThrow(0, IOException("boom")) val bufferedSink = mockSink.buffer() - bufferedSink.writeByte('a'.toInt()) + bufferedSink.writeByte('a'.code) assertFailsWith<IOException> { bufferedSink.close() } @@ -105,9 +105,9 @@ class CommonRealBufferedSinkTest { @Test fun closeWithExceptionWhenClosing() { val mockSink = MockSink() - mockSink.scheduleThrow(1, IOException()) + mockSink.scheduleThrow(1, IOException("boom")) val bufferedSink = mockSink.buffer() - bufferedSink.writeByte('a'.toInt()) + bufferedSink.writeByte('a'.code) assertFailsWith<IOException> { bufferedSink.close() } @@ -120,7 +120,7 @@ class CommonRealBufferedSinkTest { mockSink.scheduleThrow(0, IOException("first")) mockSink.scheduleThrow(1, IOException("second")) val bufferedSink = mockSink.buffer() - bufferedSink.writeByte('a'.toInt()) + bufferedSink.writeByte('a'.code) try { bufferedSink.close() fail() @@ -134,12 +134,12 @@ class CommonRealBufferedSinkTest { @Test fun operationsAfterClose() { val mockSink = MockSink() val bufferedSink = mockSink.buffer() - bufferedSink.writeByte('a'.toInt()) + bufferedSink.writeByte('a'.code) bufferedSink.close() // Test a sample set of methods. assertFailsWith<IllegalStateException> { - bufferedSink.writeByte('a'.toInt()) + bufferedSink.writeByte('a'.code) } assertFailsWith<IllegalStateException> { @@ -186,7 +186,7 @@ class CommonRealBufferedSinkTest { val write3 = Buffer().writeUtf8("c".repeat(Segment.SIZE)) val source = Buffer().writeUtf8( - "${"a".repeat(Segment.SIZE)}${"b".repeat(Segment.SIZE)}${"c".repeat(Segment.SIZE)}" + "${"a".repeat(Segment.SIZE)}${"b".repeat(Segment.SIZE)}${"c".repeat(Segment.SIZE)}", ) val mockSink = MockSink() @@ -196,7 +196,7 @@ class CommonRealBufferedSinkTest { mockSink.assertLog( "write($write1, ${write1.size})", "write($write2, ${write2.size})", - "write($write3, ${write3.size})" + "write($write3, ${write3.size})", ) } } diff --git a/okio/src/commonTest/kotlin/okio/CommonRealBufferedSourceTest.kt b/okio/src/commonTest/kotlin/okio/CommonRealBufferedSourceTest.kt index 4663919a..6756c5a5 100644 --- a/okio/src/commonTest/kotlin/okio/CommonRealBufferedSourceTest.kt +++ b/okio/src/commonTest/kotlin/okio/CommonRealBufferedSourceTest.kt @@ -36,7 +36,7 @@ class CommonRealBufferedSourceTest { ).buffer() assertEquals(6, buffer.size) - assertEquals(-1, bufferedSource.indexOf('e'.toByte(), 0, 4)) + assertEquals(-1, bufferedSource.indexOf('e'.code.toByte(), 0, 4)) assertEquals(2, buffer.size) } @@ -141,7 +141,7 @@ class CommonRealBufferedSourceTest { val write3 = Buffer().writeUtf8("c".repeat(Segment.SIZE)) val source = Buffer().writeUtf8( - "${"a".repeat(Segment.SIZE)}${"b".repeat(Segment.SIZE)}${"c".repeat(Segment.SIZE)}" + "${"a".repeat(Segment.SIZE)}${"b".repeat(Segment.SIZE)}${"c".repeat(Segment.SIZE)}", ) val mockSink = MockSink() @@ -150,7 +150,7 @@ class CommonRealBufferedSourceTest { mockSink.assertLog( "write($write1, ${write1.size})", "write($write2, ${write2.size})", - "write($write3, ${write3.size})" + "write($write3, ${write3.size})", ) } } diff --git a/okio/src/commonTest/kotlin/okio/FakeFileSystemTest.kt b/okio/src/commonTest/kotlin/okio/FakeFileSystemTest.kt deleted file mode 100644 index f57ecd64..00000000 --- a/okio/src/commonTest/kotlin/okio/FakeFileSystemTest.kt +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright (C) 2020 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.Path.Companion.toPath -import okio.fakefilesystem.FakeFileSystem -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertTrue -import kotlin.time.ExperimentalTime -import kotlin.time.minutes - -@ExperimentalTime -@ExperimentalFileSystem -class FakeWindowsFileSystemTest : FakeFileSystemTest( - clock = FakeClock(), - windowsLimitations = true, - temporaryDirectory = "C:\\".toPath(), -) - -@ExperimentalTime -@ExperimentalFileSystem -class FakeUnixFileSystemTest : FakeFileSystemTest( - clock = FakeClock(), - windowsLimitations = false, - temporaryDirectory = "/".toPath(), -) - -@ExperimentalTime -@ExperimentalFileSystem -abstract class FakeFileSystemTest internal constructor( - clock: FakeClock, - windowsLimitations: Boolean, - temporaryDirectory: Path -) : AbstractFileSystemTest( - clock = clock, - fileSystem = FakeFileSystem(windowsLimitations, clock = clock), - windowsLimitations = windowsLimitations, - temporaryDirectory = temporaryDirectory -) { - private val fakeFileSystem: FakeFileSystem = fileSystem as FakeFileSystem - private val fakeClock: FakeClock = clock - - @Test - fun openPathsIncludesOpenSink() { - val openPath = base / "open-file" - val sink = fileSystem.sink(openPath) - assertEquals(openPath, fakeFileSystem.openPaths.single()) - sink.close() - assertTrue(fakeFileSystem.openPaths.isEmpty()) - } - - @Test - fun openPathsIncludesOpenSource() { - val openPath = base / "open-file" - openPath.writeUtf8("hello, world!") - assertTrue(fakeFileSystem.openPaths.isEmpty()) - val source = fileSystem.source(openPath) - assertEquals(openPath, fakeFileSystem.openPaths.single()) - source.close() - assertTrue(fakeFileSystem.openPaths.isEmpty()) - } - - @Test - fun openPathsIsOpenOrder() { - val fileA = base / "a" - val fileB = base / "b" - val fileC = base / "c" - val fileD = base / "d" - - assertEquals(fakeFileSystem.openPaths, listOf()) - val sinkD = fileSystem.sink(fileD) - assertEquals(fakeFileSystem.openPaths, listOf(fileD)) - val sinkB = fileSystem.sink(fileB) - assertEquals(fakeFileSystem.openPaths, listOf(fileD, fileB)) - val sinkC = fileSystem.sink(fileC) - assertEquals(fakeFileSystem.openPaths, listOf(fileD, fileB, fileC)) - val sinkA = fileSystem.sink(fileA) - assertEquals(fakeFileSystem.openPaths, listOf(fileD, fileB, fileC, fileA)) - val sinkB2 = fileSystem.sink(fileB) - assertEquals(fakeFileSystem.openPaths, listOf(fileD, fileB, fileC, fileA, fileB)) - sinkD.close() - assertEquals(fakeFileSystem.openPaths, listOf(fileB, fileC, fileA, fileB)) - sinkB2.close() - assertEquals(fakeFileSystem.openPaths, listOf(fileB, fileC, fileA)) - sinkB.close() - assertEquals(fakeFileSystem.openPaths, listOf(fileC, fileA)) - sinkC.close() - assertEquals(fakeFileSystem.openPaths, listOf(fileA)) - sinkA.close() - assertEquals(fakeFileSystem.openPaths, listOf()) - } - - @Test - fun allPathsIncludesFile() { - val file = base / "all-files-includes-file" - file.writeUtf8("hello, world!") - assertEquals(fakeFileSystem.allPaths, setOf(base, file)) - } - - @Test - fun allPathsIsSorted() { - val fileA = base / "a" - val fileB = base / "b" - val fileC = base / "c" - val fileD = base / "d" - - // Create files in a different order than the sorted order, so a file system that returns files - // in creation-order or reverse-creation order won't pass by accident. - fileD.writeUtf8("fileD") - fileB.writeUtf8("fileB") - fileC.writeUtf8("fileC") - fileA.writeUtf8("fileA") - - assertEquals(fakeFileSystem.allPaths.toList(), listOf(base, fileA, fileB, fileC, fileD)) - } - - @Test - fun allPathsIncludesDirectory() { - val dir = base / "all-files-includes-directory" - fileSystem.createDirectory(dir) - assertEquals(fakeFileSystem.allPaths, setOf(base, dir)) - } - - @Test - fun allPathsDoesNotIncludeDeletedFile() { - val file = base / "all-files-does-not-include-deleted-file" - file.writeUtf8("hello, world!") - fileSystem.delete(file) - assertEquals(fakeFileSystem.allPaths, setOf(base)) - } - - @Test - fun allPathsDoesNotIncludeDeletedOpenFile() { - if (windowsLimitations) return // Can't delete open files with Windows' limitations. - - val file = base / "all-files-does-not-include-deleted-open-file" - val sink = fileSystem.sink(file) - assertEquals(fakeFileSystem.allPaths, setOf(base, file)) - fileSystem.delete(file) - assertEquals(fakeFileSystem.allPaths, setOf(base)) - sink.close() - } - - @Test - fun fileLastAccessedTime() { - val path = base / "file-last-accessed-time" - - fakeClock.sleep(1.minutes) - path.writeUtf8("hello, world!") - val createdAt = clock.now() - - fakeClock.sleep(1.minutes) - path.writeUtf8("hello again!") - val modifiedAt = clock.now() - - fakeClock.sleep(1.minutes) - path.readUtf8() - val accessedAt = clock.now() - - val metadata = fileSystem.metadata(path) - assertEquals(createdAt, metadata.createdAt) - assertEquals(modifiedAt, metadata.lastModifiedAt) - assertEquals(accessedAt, metadata.lastAccessedAt) - } - - @Test - fun directoryLastAccessedTime() { - val path = base / "directory-last-accessed-time" - - fakeClock.sleep(1.minutes) - fileSystem.createDirectory(path) - val createdAt = clock.now() - - fakeClock.sleep(1.minutes) - (path / "child").writeUtf8("hello world!") - val modifiedAt = clock.now() - - fakeClock.sleep(1.minutes) - fileSystem.list(path) - val accessedAt = clock.now() - - val metadata = fileSystem.metadata(path) - assertEquals(createdAt, metadata.createdAt) - assertEquals(modifiedAt, metadata.lastModifiedAt) - assertEquals(accessedAt, metadata.lastAccessedAt) - } - - @Test - fun checkNoOpenFilesThrowsOnOpenSource() { - val path = base / "check-no-open-files-open-source" - path.writeUtf8("hello, world!") - val exception = fileSystem.source(path).use { source -> - assertFailsWith<IllegalStateException> { - fakeFileSystem.checkNoOpenFiles() - } - } - - assertEquals( - """ - |expected 0 open files, but found: - | $path - """.trimMargin(), - exception.message - ) - assertEquals("file opened for reading here", exception.cause?.message) - - // Now that the source is closed this is safe. - fakeFileSystem.checkNoOpenFiles() - } - - @Test - fun checkNoOpenFilesThrowsOnOpenSink() { - val path = base / "check-no-open-files-open-sink" - val exception = fileSystem.sink(path).use { source -> - assertFailsWith<IllegalStateException> { - fakeFileSystem.checkNoOpenFiles() - } - } - - assertEquals( - """ - |expected 0 open files, but found: - | $path - """.trimMargin(), - exception.message - ) - assertEquals("file opened for writing here", exception.cause?.message) - - // Now that the source is closed this is safe. - fakeFileSystem.checkNoOpenFiles() - } - - @Test - fun createDirectoriesForVolumeLetterRoot() { - val path = "X:\\".toPath() - fileSystem.createDirectories(path) - assertTrue(fileSystem.metadata(path).isDirectory) - } - - @Test - fun createDirectoriesForChildOfVolumeLetterRoot() { - val path = "X:\\path".toPath() - fileSystem.createDirectories(path) - assertTrue(fileSystem.metadata(path).isDirectory) - } - - @Test - fun createDirectoriesForUnixRoot() { - val path = "/".toPath() - fileSystem.createDirectories(path) - assertTrue(fileSystem.metadata(path).isDirectory) - } - - @Test - fun createDirectoriesForChildOfUnixRoot() { - val path = "/path".toPath() - fileSystem.createDirectories(path) - assertTrue(fileSystem.metadata(path).isDirectory) - } - - @Test - fun createDirectoriesForUncRoot() { - val path = "\\\\server".toPath() - fileSystem.createDirectories(path) - assertTrue(fileSystem.metadata(path).isDirectory) - } - - @Test - fun createDirectoriesForChildOfUncRoot() { - val path = "\\\\server\\project".toPath() - fileSystem.createDirectories(path) - assertTrue(fileSystem.metadata(path).isDirectory) - } - - @Test - fun workingDirectoryMustBeAbsolute() { - val exception = assertFailsWith<IllegalArgumentException> { - FakeFileSystem(workingDirectory = "some/relative/path".toPath()) - } - assertEquals("expected an absolute path but was some/relative/path", exception.message) - } - - @Test - fun metadataForRootsGeneratedOnDemand() { - assertTrue(fileSystem.metadata("X:\\".toPath()).isDirectory) - assertTrue(fileSystem.metadata("/".toPath()).isDirectory) - assertTrue(fileSystem.metadata("\\\\server".toPath()).isDirectory) - } -} diff --git a/okio/src/commonTest/kotlin/okio/ForwardingFileSystemTest.kt b/okio/src/commonTest/kotlin/okio/ForwardingFileSystemTest.kt deleted file mode 100644 index 2f9669d5..00000000 --- a/okio/src/commonTest/kotlin/okio/ForwardingFileSystemTest.kt +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (C) 2020 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 kotlinx.datetime.Clock -import okio.Path.Companion.toPath -import okio.fakefilesystem.FakeFileSystem -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertTrue -import kotlin.time.ExperimentalTime - -@ExperimentalTime -@ExperimentalFileSystem -class ForwardingFileSystemTest : AbstractFileSystemTest( - clock = Clock.System, - fileSystem = object : ForwardingFileSystem(FakeFileSystem()) {}, - windowsLimitations = false, - temporaryDirectory = "/".toPath() -) { - @Test - fun pathBlocking() { - val forwardingFileSystem = object : ForwardingFileSystem(fileSystem) { - override fun delete(path: Path) { - throw IOException("synthetic failure!") - } - - override fun onPathParameter(path: Path, functionName: String, parameterName: String): Path { - if (path.name.contains("blocked")) throw IOException("blocked path!") - return path - } - } - - forwardingFileSystem.createDirectory(base / "okay") - assertFailsWith<IOException> { - forwardingFileSystem.createDirectory(base / "blocked") - } - } - - @Test - fun operationBlocking() { - val forwardingFileSystem = object : ForwardingFileSystem(fileSystem) { - override fun onPathParameter(path: Path, functionName: String, parameterName: String): Path { - if (functionName == "delete") throw IOException("blocked operation!") - return path - } - } - - forwardingFileSystem.createDirectory(base / "operation-blocking") - assertFailsWith<IOException> { - forwardingFileSystem.delete(base / "operation-blocking") - } - } - - @Test - fun pathMapping() { - val prefix = "/mapped" - val source = base / "source" - val mappedSource = (prefix + source).toPath() - val target = base / "target" - val mappedTarget = (prefix + target).toPath() - - source.writeUtf8("hello, world!") - - val forwardingFileSystem = object : ForwardingFileSystem(fileSystem) { - override fun onPathParameter(path: Path, functionName: String, parameterName: String): Path { - return path.toString().removePrefix(prefix).toPath() - } - - override fun onPathResult(path: Path, functionName: String): Path { - return (prefix + path).toPath() - } - } - - forwardingFileSystem.copy(mappedSource, mappedTarget) - assertTrue(target in fileSystem.list(base)) - assertTrue(mappedTarget in forwardingFileSystem.list(base)) - assertEquals("hello, world!", source.readUtf8()) - assertEquals("hello, world!", target.readUtf8()) - } - - /** - * Path mapping might impact the sort order. Confirm that list() returns elements in sorted order - * even if that order is different in the delegate file system. - */ - @Test - fun pathMappingImpactedBySorting() { - val az = base / "az" - val by = base / "by" - val cx = base / "cx" - az.writeUtf8("az") - by.writeUtf8("by") - cx.writeUtf8("cx") - - val forwardingFileSystem = object : ForwardingFileSystem(fileSystem) { - override fun onPathResult(path: Path, functionName: String): Path { - return path.parent!! / path.name.reversed() - } - } - - assertEquals(fileSystem.list(base), listOf(base / "az", base / "by", base / "cx")) - assertEquals(forwardingFileSystem.list(base), listOf(base / "xc", base / "yb", base / "za")) - } - - @Test - fun copyIsNotForwarded() { - val log = mutableListOf<String>() - - val delegate = object : ForwardingFileSystem(fileSystem) { - override fun copy(source: Path, target: Path) { - throw AssertionError("unexpected call to copy()") - } - } - - val forwardingFileSystem = object : ForwardingFileSystem(delegate) { - override fun onPathParameter(path: Path, functionName: String, parameterName: String): Path { - log += "$functionName($parameterName=$path)" - return path - } - } - - val source = base / "source" - source.writeUtf8("hello, world!") - val target = base / "target" - forwardingFileSystem.copy(source, target) - assertTrue(target in fileSystem.list(base)) - assertEquals("hello, world!", source.readUtf8()) - assertEquals("hello, world!", target.readUtf8()) - - assertEquals(log, listOf("source(file=$source)", "sink(file=$target)")) - } -} diff --git a/okio/src/commonTest/kotlin/okio/ForwardingSourceTest.kt b/okio/src/commonTest/kotlin/okio/ForwardingSourceTest.kt new file mode 100644 index 00000000..70c3fa45 --- /dev/null +++ b/okio/src/commonTest/kotlin/okio/ForwardingSourceTest.kt @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2022 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 kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class ForwardingSourceTest { + val source = Buffer().writeUtf8("Delegate") + + @Test + fun testForwardingSourceOverrides() { + val forwarder = "Forwarder" + val newSource = Buffer().writeUtf8(forwarder) + val forwardingSource = object : ForwardingSource(source) { + override fun read(sink: Buffer, byteCount: Long): Long { + return newSource.read(sink, byteCount) + } + } + + assertEquals("Forwarder", forwardingSource.buffer().readUtf8()) + } + + @Test + fun testForwardingSourceDelegates() { + val forwardingSource = object : ForwardingSource(source) { + } + + assertEquals("Delegate", forwardingSource.buffer().readUtf8()) + } + + @Test + fun testToString() { + val forwardingSource = object : ForwardingSource(source) { + } + + assertTrue(forwardingSource.toString().endsWith("([text=Delegate])")) + } +} diff --git a/okio/src/commonTest/kotlin/okio/HashingSinkTest.kt b/okio/src/commonTest/kotlin/okio/HashingSinkTest.kt index db1aeeec..bb88be7a 100644 --- a/okio/src/commonTest/kotlin/okio/HashingSinkTest.kt +++ b/okio/src/commonTest/kotlin/okio/HashingSinkTest.kt @@ -15,14 +15,14 @@ */ package okio +import kotlin.test.Test +import kotlin.test.assertEquals import okio.HashingSink.Companion.hmacSha1 import okio.HashingSink.Companion.hmacSha256 import okio.HashingSink.Companion.hmacSha512 import okio.HashingSink.Companion.sha1 import okio.HashingSink.Companion.sha256 import okio.HashingSink.Companion.sha512 -import kotlin.test.Test -import kotlin.test.assertEquals class HashingSinkTest { private val source = Buffer() diff --git a/okio/src/commonTest/kotlin/okio/HashingSourceTest.kt b/okio/src/commonTest/kotlin/okio/HashingSourceTest.kt index 83e2e264..7111edf4 100644 --- a/okio/src/commonTest/kotlin/okio/HashingSourceTest.kt +++ b/okio/src/commonTest/kotlin/okio/HashingSourceTest.kt @@ -15,15 +15,15 @@ */ package okio +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.fail import okio.HashingSource.Companion.hmacSha1 import okio.HashingSource.Companion.hmacSha256 import okio.HashingSource.Companion.hmacSha512 import okio.HashingSource.Companion.md5 import okio.HashingSource.Companion.sha1 import okio.HashingSource.Companion.sha256 -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.fail class HashingSourceTest { private val source = Buffer() @@ -82,11 +82,11 @@ class HashingSourceTest { val hashingSource = sha256(source) val bufferedSource = hashingSource.buffer() source.writeUtf8("a") - assertEquals('a'.toLong(), bufferedSource.readUtf8CodePoint().toLong()) + assertEquals('a'.code.toLong(), bufferedSource.readUtf8CodePoint().toLong()) source.writeUtf8("b") - assertEquals('b'.toLong(), bufferedSource.readUtf8CodePoint().toLong()) + assertEquals('b'.code.toLong(), bufferedSource.readUtf8CodePoint().toLong()) source.writeUtf8("c") - assertEquals('c'.toLong(), bufferedSource.readUtf8CodePoint().toLong()) + assertEquals('c'.code.toLong(), bufferedSource.readUtf8CodePoint().toLong()) assertEquals(HashingTest.SHA256_abc, hashingSource.hash) } diff --git a/okio/src/commonTest/kotlin/okio/HashingTest.kt b/okio/src/commonTest/kotlin/okio/HashingTest.kt index 1cae58d0..d290dc0c 100644 --- a/okio/src/commonTest/kotlin/okio/HashingTest.kt +++ b/okio/src/commonTest/kotlin/okio/HashingTest.kt @@ -15,10 +15,10 @@ */ package okio -import okio.ByteString.Companion.decodeHex -import okio.ByteString.Companion.encodeUtf8 import kotlin.test.Test import kotlin.test.assertEquals +import okio.ByteString.Companion.decodeHex +import okio.ByteString.Companion.encodeUtf8 class HashingTest { @Test fun byteStringMd5() { diff --git a/okio/src/commonTest/kotlin/okio/util.kt b/okio/src/commonTest/kotlin/okio/OkioTesting.kt index 953eef88..8dbdd2a1 100644 --- a/okio/src/commonTest/kotlin/okio/util.kt +++ b/okio/src/commonTest/kotlin/okio/OkioTesting.kt @@ -16,11 +16,6 @@ package okio import kotlin.random.Random -import kotlin.test.assertEquals - -fun Char.repeat(count: Int): String { - return toString().repeat(count) -} fun segmentSizes(buffer: Buffer): List<Int> { var segment = buffer.head ?: return emptyList() @@ -34,17 +29,6 @@ fun segmentSizes(buffer: Buffer): List<Int> { return sizes } -fun assertArrayEquals(a: ByteArray, b: ByteArray) { - assertEquals(a.contentToString(), b.contentToString()) -} - -fun randomBytes(length: Int): ByteString { - val random = Random(0) - val randomBytes = ByteArray(length) - random.nextBytes(randomBytes) - return ByteString.of(*randomBytes) -} - fun bufferWithRandomSegmentLayout(dice: Random, data: ByteArray): Buffer { val result = Buffer() @@ -92,3 +76,24 @@ fun makeSegments(source: ByteString): ByteString { } return buffer.snapshot() } + +/** + * Returns a string with all '\' slashes replaced with '/' slashes. This is useful for test + * assertions that intend to ignore slashes. + */ +fun Path.withUnixSlashes(): String { + return toString().replace('\\', '/') +} + +expect fun assertRelativeTo( + a: Path, + b: Path, + bRelativeToA: Path, + sameAsNio: Boolean = true, +) + +expect fun assertRelativeToFails( + a: Path, + b: Path, + sameAsNio: Boolean = true, +): IllegalArgumentException diff --git a/okio/src/commonTest/kotlin/okio/PathTest.kt b/okio/src/commonTest/kotlin/okio/PathTest.kt index 92754a78..cb2920d7 100644 --- a/okio/src/commonTest/kotlin/okio/PathTest.kt +++ b/okio/src/commonTest/kotlin/okio/PathTest.kt @@ -15,18 +15,21 @@ */ package okio -import okio.Path.Companion.toPath import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse +import kotlin.test.assertNotEquals import kotlin.test.assertNull import kotlin.test.assertTrue +import okio.Path.Companion.toPath -@ExperimentalFileSystem class PathTest { @Test fun unixRoot() { - val path = "/".toPath("/") + val path = "/".toPath() + assertEquals(path, path.normalized()) + assertEquals(path, path.root) + assertEquals(listOf(), path.segments) assertEquals("/", path.toString()) assertNull(path.parent) assertNull(path.volumeLetter) @@ -37,9 +40,12 @@ class PathTest { @Test fun unixAbsolutePath() { - val path = "/home/jesse/todo.txt".toPath("/") + val path = "/home/jesse/todo.txt".toPath() + assertEquals(path, path.normalized()) + assertEquals("/".toPath(), path.root) + assertEquals(listOf("home", "jesse", "todo.txt"), path.segments) assertEquals("/home/jesse/todo.txt", path.toString()) - assertEquals("/home/jesse".toPath("/"), path.parent) + assertEquals("/home/jesse".toPath(), path.parent) assertNull(path.volumeLetter) assertEquals("todo.txt", path.name) assertTrue(path.isAbsolute) @@ -48,9 +54,12 @@ class PathTest { @Test fun unixRelativePath() { - val path = "project/todo.txt".toPath("/") + val path = "project/todo.txt".toPath() + assertEquals(path, path.normalized()) + assertNull(path.root) + assertEquals(listOf("project", "todo.txt"), path.segments) assertEquals("project/todo.txt", path.toString()) - assertEquals("project".toPath("/"), path.parent) + assertEquals("project".toPath(), path.parent) assertNull(path.volumeLetter) assertEquals("todo.txt", path.name) assertFalse(path.isAbsolute) @@ -59,9 +68,12 @@ class PathTest { @Test fun unixRelativePathWithDots() { - val path = "../../project/todo.txt".toPath("/") + val path = "../../project/todo.txt".toPath() + assertEquals(path, path.normalized()) + assertNull(path.root) + assertEquals(listOf("..", "..", "project", "todo.txt"), path.segments) assertEquals("../../project/todo.txt", path.toString()) - assertEquals("../../project".toPath("/"), path.parent) + assertEquals("../../project".toPath(), path.parent) assertNull(path.volumeLetter) assertEquals("todo.txt", path.name) assertFalse(path.isAbsolute) @@ -70,7 +82,10 @@ class PathTest { @Test fun unixRelativeSeriesOfDotDots() { - val path = "../../..".toPath("/") + val path = "../../..".toPath() + assertEquals(path, path.normalized()) + assertNull(path.root) + assertEquals(listOf("..", "..", ".."), path.segments) assertEquals("../../..", path.toString()) assertNull(path.parent) assertNull(path.volumeLetter) @@ -81,7 +96,10 @@ class PathTest { @Test fun unixAbsoluteSeriesOfDotDots() { - val path = "/../../..".toPath("/") + val path = "/../../..".toPath() + assertEquals(path, path.normalized()) + assertEquals("/".toPath(), path.root) + assertEquals(listOf(), path.segments) assertEquals("/", path.toString()) assertNull(path.parent) assertNull(path.volumeLetter) @@ -92,7 +110,10 @@ class PathTest { @Test fun unixAbsoluteSingleDot() { - val path = "/.".toPath("/") + val path = "/.".toPath() + assertEquals(path, path.normalized()) + assertEquals("/".toPath(), path.root) + assertEquals(listOf(), path.segments) assertEquals("/", path.toString()) assertNull(path.parent) assertNull(path.volumeLetter) @@ -103,7 +124,10 @@ class PathTest { @Test fun unixRelativeDoubleDots() { - val path = "..".toPath("/") + val path = "..".toPath() + assertEquals(path, path.normalized()) + assertNull(path.root) + assertEquals(listOf(".."), path.segments) assertEquals("..", path.toString()) assertNull(path.parent) assertNull(path.volumeLetter) @@ -114,7 +138,10 @@ class PathTest { @Test fun unixRelativeSingleDot() { - val path = ".".toPath("/") + val path = ".".toPath() + assertEquals(path, path.normalized()) + assertNull(path.root) + assertEquals(listOf("."), path.segments) assertEquals(".", path.toString()) assertNull(path.parent) assertNull(path.volumeLetter) @@ -125,7 +152,10 @@ class PathTest { @Test fun windowsVolumeLetter() { - val path = "C:\\".toPath("\\") + val path = "C:\\".toPath() + assertEquals(path, path.normalized()) + assertEquals("C:\\".toPath(), path.root) + assertEquals(listOf(), path.segments) assertEquals("C:\\", path.toString()) assertNull(path.parent) assertEquals('C', path.volumeLetter) @@ -136,9 +166,12 @@ class PathTest { @Test fun windowsAbsolutePathWithVolumeLetter() { - val path = "C:\\Windows\\notepad.exe".toPath("\\") + val path = "C:\\Windows\\notepad.exe".toPath() + assertEquals(path, path.normalized()) + assertEquals("C:\\".toPath(), path.root) + assertEquals(listOf("Windows", "notepad.exe"), path.segments) assertEquals("C:\\Windows\\notepad.exe", path.toString()) - assertEquals("C:\\Windows".toPath("\\"), path.parent) + assertEquals("C:\\Windows".toPath(), path.parent) assertEquals('C', path.volumeLetter) assertEquals("notepad.exe", path.name) assertTrue(path.isAbsolute) @@ -147,7 +180,10 @@ class PathTest { @Test fun windowsAbsolutePath() { - val path = "\\".toPath("\\") + val path = "\\".toPath() + assertEquals(path, path.normalized()) + assertEquals("\\".toPath(), path.root) + assertEquals(listOf(), path.segments) assertEquals("\\", path.toString()) assertEquals(null, path.parent) assertNull(path.volumeLetter) @@ -158,9 +194,12 @@ class PathTest { @Test fun windowsAbsolutePathWithoutVolumeLetter() { - val path = "\\Windows\\notepad.exe".toPath("\\") + val path = "\\Windows\\notepad.exe".toPath() + assertEquals(path, path.normalized()) + assertEquals("\\".toPath(), path.root) + assertEquals(listOf("Windows", "notepad.exe"), path.segments) assertEquals("\\Windows\\notepad.exe", path.toString()) - assertEquals("\\Windows".toPath("\\"), path.parent) + assertEquals("\\Windows".toPath(), path.parent) assertNull(path.volumeLetter) assertEquals("notepad.exe", path.name) assertTrue(path.isAbsolute) @@ -169,9 +208,12 @@ class PathTest { @Test fun windowsRelativePathWithVolumeLetter() { - val path = "C:Windows\\notepad.exe".toPath("\\") + val path = "C:Windows\\notepad.exe".toPath() + assertEquals(path, path.normalized()) + assertNull(path.root) + assertEquals(listOf("C:Windows", "notepad.exe"), path.segments) assertEquals("C:Windows\\notepad.exe", path.toString()) - assertEquals("C:Windows".toPath("\\"), path.parent) + assertEquals("C:Windows".toPath(), path.parent) assertEquals('C', path.volumeLetter) assertEquals("notepad.exe", path.name) assertFalse(path.isAbsolute) @@ -180,7 +222,10 @@ class PathTest { @Test fun windowsVolumeLetterRelative() { - val path = "C:".toPath("\\") + val path = "C:".toPath() + assertEquals(path, path.normalized()) + assertNull(path.root) + assertEquals(listOf("C:"), path.segments) assertEquals("C:", path.toString()) assertNull(path.parent) assertEquals('C', path.volumeLetter) @@ -191,9 +236,12 @@ class PathTest { @Test fun windowsRelativePath() { - val path = "Windows\\notepad.exe".toPath("\\") + val path = "Windows\\notepad.exe".toPath() + assertEquals(path, path.normalized()) + assertNull(path.root) + assertEquals(listOf("Windows", "notepad.exe"), path.segments) assertEquals("Windows\\notepad.exe", path.toString()) - assertEquals("Windows".toPath("\\"), path.parent) + assertEquals("Windows".toPath(), path.parent) assertNull(path.volumeLetter) assertEquals("notepad.exe", path.name) assertFalse(path.isAbsolute) @@ -202,7 +250,10 @@ class PathTest { @Test fun windowsUncServer() { - val path = "\\\\server".toPath("\\") + val path = "\\\\server".toPath() + assertEquals(path, path.normalized()) + assertEquals("\\\\server".toPath(), path.root) + assertEquals(listOf(), path.segments) assertEquals("\\\\server", path.toString()) assertNull(path.parent) assertNull(path.volumeLetter) @@ -213,9 +264,12 @@ class PathTest { @Test fun windowsUncAbsolutePath() { - val path = "\\\\server\\project\\notes.txt".toPath("\\") + val path = "\\\\server\\project\\notes.txt".toPath() + assertEquals(path, path.normalized()) + assertEquals("\\\\server".toPath(), path.root) + assertEquals(listOf("project", "notes.txt"), path.segments) assertEquals("\\\\server\\project\\notes.txt", path.toString()) - assertEquals("\\\\server\\project".toPath("\\"), path.parent) + assertEquals("\\\\server\\project".toPath(), path.parent) assertNull(path.volumeLetter) assertEquals("notes.txt", path.name) assertTrue(path.isAbsolute) @@ -227,34 +281,42 @@ class PathTest { val root = "/".toPath() assertEquals("/home".toPath(), root / "home") assertEquals("/home/jesse".toPath(), root / "home" / "jesse") - assertEquals("/home".toPath(), root / "home" / "jesse" / "..") - assertEquals("/home/jake".toPath(), root / "home" / "jesse" / ".." / "jake") + assertEquals("/home/jesse/..".toPath(), root / "home" / "jesse" / "..") + assertEquals("/home/jesse/../jake".toPath(), root / "home" / "jesse" / ".." / "jake") } @Test fun relativePathTraversalWithDivOperator() { - val cwd = ".".toPath("/") - assertEquals("home".toPath("/"), cwd / "home") - assertEquals("home/jesse".toPath("/"), cwd / "home" / "jesse") - assertEquals("home".toPath("/"), cwd / "home" / "jesse" / "..") - assertEquals("home/jake".toPath("/"), cwd / "home" / "jesse" / ".." / "jake") + val slash = Path.DIRECTORY_SEPARATOR + val cwd = ".".toPath() + assertEquals("home".toPath(), cwd / "home") + assertEquals("home${slash}jesse".toPath(), cwd / "home" / "jesse") + assertEquals("home${slash}jesse$slash..".toPath(), cwd / "home" / "jesse" / "..") + assertEquals( + "home${slash}jesse$slash..${slash}jake".toPath(), + cwd / "home" / "jesse" / ".." / "jake", + ) } @Test fun relativePathTraversalWithDots() { - val cwd = ".".toPath("/") - assertEquals("..".toPath("/"), cwd / "..") - assertEquals("../..".toPath("/"), cwd / ".." / "..") - assertEquals("../../etc".toPath("/"), cwd / ".." / ".." / "etc") - assertEquals("../../etc/passwd".toPath("/"), cwd / ".." / ".." / "etc" / "passwd") + val slash = Path.DIRECTORY_SEPARATOR + val cwd = ".".toPath() + assertEquals("..".toPath(), cwd / "..") + assertEquals("..$slash..".toPath(), cwd / ".." / "..") + assertEquals("..$slash..${slash}etc".toPath(), cwd / ".." / ".." / "etc") + assertEquals( + "..$slash..${slash}etc${slash}passwd".toPath(), + cwd / ".." / ".." / "etc" / "passwd", + ) } @Test fun pathTraversalBaseIgnoredIfChildIsAnAbsolutePath() { - assertEquals("/home".toPath(), "".toPath("/") / "/home") - assertEquals("/home".toPath(), "relative".toPath("/") / "/home") - assertEquals("/home".toPath(), "/base".toPath("/") / "/home") - assertEquals("/home".toPath(), "/".toPath("/") / "/home") + assertEquals("/home".toPath(), "".toPath() / "/home") + assertEquals("/home".toPath(), "relative".toPath() / "/home") + assertEquals("/home".toPath(), "/base".toPath() / "/home") + assertEquals("/home".toPath(), "/".toPath() / "/home") } @Test @@ -264,6 +326,11 @@ class PathTest { assertEquals("/a", "/a/".toPath().toString()) assertEquals("/a/b/c", "/a/b/c".toPath().toString()) assertEquals("/a/b/c", "/a/b/c/".toPath().toString()) + assertEquals("/", "/".toPath(normalize = true).toString()) + assertEquals("/a", "/a".toPath(normalize = true).toString()) + assertEquals("/a", "/a/".toPath(normalize = true).toString()) + assertEquals("/a/b/c", "/a/b/c".toPath(normalize = true).toString()) + assertEquals("/a/b/c", "/a/b/c/".toPath(normalize = true).toString()) } @Test @@ -272,6 +339,10 @@ class PathTest { assertEquals("/", "/../".toPath().toString()) assertEquals("/", "/../..".toPath().toString()) assertEquals("/", "/../../".toPath().toString()) + assertEquals("/", "/..".toPath(normalize = true).toString()) + assertEquals("/", "/../".toPath(normalize = true).toString()) + assertEquals("/", "/../..".toPath(normalize = true).toString()) + assertEquals("/", "/../../".toPath(normalize = true).toString()) } @Test @@ -281,6 +352,11 @@ class PathTest { assertEquals("/a", "/a//".toPath().toString()) assertEquals("/a", "//a//".toPath().toString()) assertEquals("/a/b", "/a/b//".toPath().toString()) + assertEquals("/", "//".toPath(normalize = true).toString()) + assertEquals("/a", "//a".toPath(normalize = true).toString()) + assertEquals("/a", "/a//".toPath(normalize = true).toString()) + assertEquals("/a", "//a//".toPath(normalize = true).toString()) + assertEquals("/a/b", "/a/b//".toPath(normalize = true).toString()) } @Test @@ -294,6 +370,15 @@ class PathTest { assertEquals("/a", "//a/./".toPath().toString()) assertEquals("/a", "//a/./.".toPath().toString()) assertEquals("/a/b", "/a/./b/".toPath().toString()) + assertEquals("/", "/./".toPath(normalize = true).toString()) + assertEquals("/a", "/./a".toPath(normalize = true).toString()) + assertEquals("/a", "/a/./".toPath(normalize = true).toString()) + assertEquals("/a", "/a//.".toPath(normalize = true).toString()) + assertEquals("/a", "/./a//".toPath(normalize = true).toString()) + assertEquals("/a", "/a/.".toPath(normalize = true).toString()) + assertEquals("/a", "//a/./".toPath(normalize = true).toString()) + assertEquals("/a", "//a/./.".toPath(normalize = true).toString()) + assertEquals("/a/b", "/a/./b/".toPath(normalize = true).toString()) } @Test @@ -305,20 +390,37 @@ class PathTest { assertEquals("a/b", "a/b/".toPath().toString()) assertEquals("a/b/c/d", "a/b/c/d".toPath().toString()) assertEquals("a/b/c/d", "a/b/c/d/".toPath().toString()) + assertEquals(".", "".toPath(normalize = true).toString()) + assertEquals(".", ".".toPath(normalize = true).toString()) + assertEquals("a", "a/".toPath(normalize = true).toString()) + assertEquals("a/b", "a/b".toPath(normalize = true).toString()) + assertEquals("a/b", "a/b/".toPath(normalize = true).toString()) + assertEquals("a/b/c/d", "a/b/c/d".toPath(normalize = true).toString()) + assertEquals("a/b/c/d", "a/b/c/d/".toPath(normalize = true).toString()) } @Test fun stringToRelativePathWithTraversal() { assertEquals("..", "..".toPath().toString()) assertEquals("..", "../".toPath().toString()) - assertEquals(".", "a/..".toPath().toString()) - assertEquals(".", "a/../".toPath().toString()) - assertEquals("..", "a/../..".toPath().toString()) - assertEquals("..", "a/../../".toPath().toString()) - assertEquals("../..", "a/../../..".toPath().toString()) + assertEquals("a/..", "a/..".toPath().toString()) + assertEquals("a/..", "a/../".toPath().toString()) + assertEquals("a/../..", "a/../..".toPath().toString()) + assertEquals("a/../..", "a/../../".toPath().toString()) + assertEquals("a/../../..", "a/../../..".toPath().toString()) assertEquals("../../b", "../../b".toPath().toString()) - assertEquals("../../b", "a/../../../b".toPath().toString()) - assertEquals("../../c", "a/../../../b/../c".toPath().toString()) + assertEquals("a/../../../b", "a/../../../b".toPath().toString()) + assertEquals("a/../../../b/../c", "a/../../../b/../c".toPath().toString()) + assertEquals("..", "..".toPath(normalize = true).toString()) + assertEquals("..", "../".toPath(normalize = true).toString()) + assertEquals(".", "a/..".toPath(normalize = true).toString()) + assertEquals(".", "a/../".toPath(normalize = true).toString()) + assertEquals("..", "a/../..".toPath(normalize = true).toString()) + assertEquals("..", "a/../../".toPath(normalize = true).toString()) + assertEquals("../..", "a/../../..".toPath(normalize = true).toString()) + assertEquals("../../b", "../../b".toPath(normalize = true).toString()) + assertEquals("../../b", "a/../../../b".toPath(normalize = true).toString()) + assertEquals("../../c", "a/../../../b/../c".toPath(normalize = true).toString()) } @Test @@ -328,6 +430,11 @@ class PathTest { assertEquals("a/b", "a/b//".toPath().toString()) assertEquals("a/b", "a//b//".toPath().toString()) assertEquals("a/b/c", "a/b/c//".toPath().toString()) + assertEquals("a", "a//".toPath(normalize = true).toString()) + assertEquals("a/b", "a//b".toPath(normalize = true).toString()) + assertEquals("a/b", "a/b//".toPath(normalize = true).toString()) + assertEquals("a/b", "a//b//".toPath(normalize = true).toString()) + assertEquals("a/b/c", "a/b/c//".toPath(normalize = true).toString()) } @Test @@ -335,7 +442,7 @@ class PathTest { assertEquals(".", ".".toPath().toString()) assertEquals(".", "./".toPath().toString()) assertEquals(".", "././".toPath().toString()) - assertEquals(".", "././a/..".toPath().toString()) + assertEquals("a/..", "././a/..".toPath().toString()) assertEquals("a", "a/./".toPath().toString()) assertEquals("a/b", "a/./b".toPath().toString()) assertEquals("a/b", "a/b/./".toPath().toString()) @@ -345,6 +452,19 @@ class PathTest { assertEquals("a/b", "a//b/./".toPath().toString()) assertEquals("a/b", "a//b/./.".toPath().toString()) assertEquals("a/b/c", "a/b/./c/".toPath().toString()) + assertEquals(".", ".".toPath(normalize = true).toString()) + assertEquals(".", "./".toPath(normalize = true).toString()) + assertEquals(".", "././".toPath(normalize = true).toString()) + assertEquals(".", "././a/..".toPath(normalize = true).toString()) + assertEquals("a", "a/./".toPath(normalize = true).toString()) + assertEquals("a/b", "a/./b".toPath(normalize = true).toString()) + assertEquals("a/b", "a/b/./".toPath(normalize = true).toString()) + assertEquals("a/b", "a/b//.".toPath(normalize = true).toString()) + assertEquals("a/b", "a/./b//".toPath(normalize = true).toString()) + assertEquals("a/b", "a/b/.".toPath(normalize = true).toString()) + assertEquals("a/b", "a//b/./".toPath(normalize = true).toString()) + assertEquals("a/b", "a//b/./.".toPath(normalize = true).toString()) + assertEquals("a/b/c", "a/b/./c/".toPath(normalize = true).toString()) } @Test @@ -359,7 +479,299 @@ class PathTest { @Test fun windowsPathTraversalUp() { - assertEquals("C:\\z".toPath(), "C:\\x\\y\\..\\..\\..\\z".toPath()) - assertEquals("C:..\\z".toPath(), "C:x\\y\\..\\..\\..\\z".toPath()) + assertEquals("C:\\x\\y\\..\\..\\..\\z".toPath(), "C:\\x\\y\\..\\..\\..\\z".toPath()) + assertEquals("C:x\\y\\..\\..\\..\\z".toPath(), "C:x\\y\\..\\..\\..\\z".toPath()) + assertEquals("C:\\z".toPath(), "C:\\x\\y\\..\\..\\..\\z".toPath(normalize = true)) + assertEquals("C:..\\z".toPath(), "C:x\\y\\..\\..\\..\\z".toPath(normalize = true)) + } + + @Test + fun samePathDifferentSlashesAreNotEqual() { + assertNotEquals("/a".toPath(), "\\b".toPath()) + assertNotEquals("a/b".toPath(), "a\\b".toPath()) + } + + @Test + fun samePathNoSlashesAreEqual() { + assertEquals("a".toPath().parent!!, "a".toPath().parent!!) + assertEquals("a/b".toPath().parent!!, "a\\b".toPath().parent!!) + } + + @Test + fun relativeToWindowsPaths() { + val a = "C:\\Windows\\notepad.exe".toPath() + val b = "C:\\".toPath() + assertRelativeTo(a, b, "..\\..".toPath(), sameAsNio = false) + assertRelativeTo(b, a, "Windows\\notepad.exe".toPath(), sameAsNio = false) + + val c = "C:\\Windows\\".toPath() + val d = "C:\\Windows".toPath() + assertRelativeTo(c, d, ".".toPath()) + assertRelativeTo(d, c, ".".toPath()) + + val e = "C:\\Windows\\Downloads\\".toPath() + val f = "C:\\Windows\\Documents\\Hello.txt".toPath() + assertRelativeTo(e, f, "..\\Documents\\Hello.txt".toPath(), sameAsNio = false) + assertRelativeTo(f, e, "..\\..\\Downloads".toPath(), sameAsNio = false) + + val g = "C:\\Windows\\".toPath() + val h = "D:\\Windows\\".toPath() + assertRelativeToFails(g, h, sameAsNio = false) + assertRelativeToFails(h, g, sameAsNio = false) + } + + @Test + fun relativeToWindowsUncPaths() { + val a = "\\\\localhost\\c$\\development\\schema.proto".toPath() + val b = "\\\\localhost\\c$\\project\\notes.txt".toPath() + assertRelativeTo(a, b, "..\\..\\project\\notes.txt".toPath(), sameAsNio = false) + assertRelativeTo(b, a, "..\\..\\development\\schema.proto".toPath(), sameAsNio = false) + + val c = "C:\\Windows\\".toPath() + val d = "\\\\localhost\\c$\\project\\notes.txt".toPath() + assertRelativeToFails(c, d, sameAsNio = false) + assertRelativeToFails(d, c, sameAsNio = false) + } + + @Test + fun absoluteUnixRoot() { + val a = "/Users/jesse/hello.txt".toPath() + val b = "/".toPath() + assertRelativeTo(a, b, "../../..".toPath()) + assertRelativeTo(b, a, "Users/jesse/hello.txt".toPath()) + + val c = "/Users/jesse/hello.txt".toPath() + val d = "/Admin/Secret".toPath() + assertRelativeTo(c, d, "../../../Admin/Secret".toPath()) + assertRelativeTo(d, c, "../../Users/jesse/hello.txt".toPath()) + + val e = "/Users/".toPath() + val f = "/Users".toPath() + assertRelativeTo(e, f, ".".toPath()) + assertRelativeTo(f, e, ".".toPath()) + } + + // Note that we handle the normalized version of the paths when computing relative paths. + @Test + fun relativeToUnnormalizedPath() { + val a = "Users/../a".toPath() // `a` if normalized. + val b = "Users/b/../c".toPath() // `Users/c` if normalized. + assertRelativeToFails(a, b, sameAsNio = false) + assertRelativeToFails(b, a, sameAsNio = false) + assertRelativeTo(a.normalized(), b.normalized(), "../Users/c".toPath()) + assertRelativeTo(b.normalized(), a.normalized(), "../../a".toPath()) + } + + @Test + fun relativeToNormalizedPath() { + val a = "Users/../a".toPath(normalize = true) // results to `a`. + val b = "Users/b/../c".toPath(normalize = true) // results to `Users/c`. + assertRelativeTo(a, b, "../Users/c".toPath()) + assertRelativeTo(b, a, "../../a".toPath()) + } + + @Test + fun absoluteToRelative() { + val a = "/Users/jesse/hello.txt".toPath() + val b = "Desktop/goodbye.txt".toPath() + + var exception = assertRelativeToFails(a, b) + assertEquals( + "Paths of different roots cannot be relative to each other: " + + "Desktop/goodbye.txt and /Users/jesse/hello.txt", + exception.message, + ) + + exception = assertRelativeToFails(b, a) + assertEquals( + "Paths of different roots cannot be relative to each other: " + + "/Users/jesse/hello.txt and Desktop/goodbye.txt", + exception.message, + ) + } + + @Test + fun absoluteToAbsolute() { + val a = "/Users/jesse/hello.txt".toPath() + val b = "/Users/benoit/Desktop/goodbye.txt".toPath() + assertRelativeTo(a, b, "../../benoit/Desktop/goodbye.txt".toPath()) + assertRelativeTo(b, a, "../../../jesse/hello.txt".toPath()) + } + + @Test + fun absoluteToSelf() { + val a = "/Users/jesse/hello.txt".toPath() + assertRelativeTo(a, a, ".".toPath()) + + val b = "/Users/benoit/../jesse/hello.txt".toPath() + // NIO normalizes. + assertRelativeTo(a, b, "../../benoit/../jesse/hello.txt".toPath(), sameAsNio = false) + assertRelativeToFails(b, a, sameAsNio = false) + assertRelativeTo(b.normalized(), a, ".".toPath()) + assertRelativeTo(a, b.normalized(), ".".toPath()) + } + + @Test + fun relativeToSelf() { + val a = "Desktop/hello.txt".toPath() + assertRelativeTo(a, a, ".".toPath()) + + val b = "Documents/../Desktop/hello.txt".toPath() + // NIO normalizes. + assertRelativeTo(a, b, "../../Documents/../Desktop/hello.txt".toPath(), sameAsNio = false) + assertRelativeToFails(b, a, sameAsNio = false) + assertRelativeTo(a, b.normalized(), ".".toPath()) + assertRelativeTo(b.normalized(), a, ".".toPath()) + } + + @Test + fun relativeToRelative() { + val a = "Desktop/documents/resume.txt".toPath() + val b = "Desktop/documents/2021/taxes.txt".toPath() + assertRelativeTo(a, b, "../2021/taxes.txt".toPath(), sameAsNio = false) + assertRelativeTo(b, a, "../../resume.txt".toPath(), sameAsNio = false) + + val c = "documents/resume.txt".toPath() + val d = "downloads/2021/taxes.txt".toPath() + assertRelativeTo(c, d, "../../downloads/2021/taxes.txt".toPath(), sameAsNio = false) + assertRelativeTo(d, c, "../../../documents/resume.txt".toPath(), sameAsNio = false) + } + + @Test + fun relativeToRelativeWithMiddleDots() { + val a = "Desktop/documents/a...n".toPath() + val b = "Desktop/documents/m...z".toPath() + assertRelativeTo(a, b, "../m...z".toPath()) + assertRelativeTo(b, a, "../a...n".toPath()) + } + + @Test + fun relativeToRelativeWithMiddleDotsInCommonPrefix() { + val a = "Desktop/documents/a...n/red".toPath() + val b = "Desktop/documents/a...m/blue".toPath() + assertRelativeTo(a, b, "../../a...m/blue".toPath()) + assertRelativeTo(b, a, "../../a...n/red".toPath()) + } + + @Test + fun relativeToRelativeWithUpNavigationPrefix() { + // We can't navigate from 'taxes' to 'resumes' because we don't know the name of 'Documents'. + // /Users/jwilson/Documents/2021/Current + // /Users/jwilson/Documents/resumes + // /Users/jwilson/taxes + val a = "../../resumes".toPath() + val b = "../../../taxes".toPath() + assertRelativeTo(a, b, "../../taxes".toPath()) + assertRelativeToFails(b, a, sameAsNio = false) + } + + @Test + fun relativeToRelativeDifferentSlashes() { + val a = "Desktop/documents/resume.txt".toPath() + val b = "Desktop\\documents\\2021\\taxes.txt".toPath() + assertRelativeTo(a, b, "../2021/taxes.txt".toPath(), sameAsNio = false) + assertRelativeTo(b, a, "..\\..\\resume.txt".toPath(), sameAsNio = false) + + val c = "documents/resume.txt".toPath() + val d = "downloads\\2021\\taxes.txt".toPath() + assertRelativeTo(c, d, "../../downloads/2021/taxes.txt".toPath(), sameAsNio = false) + assertRelativeTo(d, c, "..\\..\\..\\documents\\resume.txt".toPath(), sameAsNio = false) + } + + @Test + fun windowsUncPathsDoNotDotDot() { + assertEquals( + """\\localhost\c$\Windows""", + """\\localhost\c$\Windows""".toPath().toString(), + ) + assertEquals( + """\\127.0.0.1\c$\Windows""", + """\\127.0.0.1\c$\Windows""".toPath().toString(), + ) + assertEquals( + """\\127.0.0.1\c$\Windows\..\Windows""", + """\\127.0.0.1\c$\Windows\..\Windows""".toPath().toString(), + ) + assertEquals( + """\\127.0.0.1\..\localhost\c$\Windows""", + """\\127.0.0.1\..\localhost\c$\Windows""".toPath().toString(), + ) + assertEquals( + """\\127.0.0.1\c$\..\d$""", + """\\127.0.0.1\c$\..\d$""".toPath().toString(), + ) + + assertEquals( + """\\localhost\c$\Windows""", + """\\localhost\c$\Windows""".toPath(normalize = true).toString(), + ) + assertEquals( + """\\127.0.0.1\c$\Windows""", + """\\127.0.0.1\c$\Windows""".toPath(normalize = true).toString(), + ) + assertEquals( + """\\127.0.0.1\c$\Windows""", + """\\127.0.0.1\c$\Windows\..\Windows""".toPath(normalize = true).toString(), + ) + assertEquals( + """\\127.0.0.1\localhost\c$\Windows""", + """\\127.0.0.1\..\localhost\c$\Windows""".toPath(normalize = true).toString(), + ) + assertEquals( + """\\127.0.0.1\d$""", + """\\127.0.0.1\c$\..\d$""".toPath(normalize = true).toString(), + ) + assertEquals( + """\\127.0.0.1\c$""", + """\\..\127.0.0.1\..\c$""".toPath(normalize = true).toString(), + ) + } + + @Test fun normalizeAbsolute() { + assertEquals("/", "/.".toPath(normalize = true).toString()) + assertEquals("/", "/.".toPath(normalize = false).toString()) + assertEquals("/", "/..".toPath(normalize = true).toString()) + assertEquals("/", "/..".toPath(normalize = false).toString()) + assertEquals("/", "/../..".toPath(normalize = true).toString()) + assertEquals("/", "/../..".toPath(normalize = false).toString()) + + assertEquals("/a/b", "/a/./b".toPath(normalize = true).toString()) + assertEquals("/a/b", "/a/./b".toPath(normalize = false).toString()) + assertEquals("/a/.../b", "/a/..././b".toPath(normalize = true).toString()) + assertEquals("/a/.../b", "/a/..././b".toPath(normalize = false).toString()) + assertEquals("/", "/a/..".toPath(normalize = true).toString()) + assertEquals("/a/..", "/a/..".toPath(normalize = false).toString()) + assertEquals("/b", "/../a/../b".toPath(normalize = true).toString()) + assertEquals("/a/../b", "/../a/../b".toPath(normalize = false).toString()) + } + + @Test fun normalizeRelative() { + assertEquals(".", ".".toPath(normalize = true).toString()) + assertEquals(".", ".".toPath(normalize = false).toString()) + assertEquals("..", "..".toPath(normalize = true).toString()) + assertEquals("..", "..".toPath(normalize = false).toString()) + assertEquals("../..", "../..".toPath(normalize = true).toString()) + assertEquals("../..", "../..".toPath(normalize = false).toString()) + + assertEquals("a/b", "a/./b".toPath(normalize = true).toString()) + assertEquals("a/b", "a/./b".toPath(normalize = false).toString()) + assertEquals("a/.../b", "a/..././b".toPath(normalize = true).toString()) + assertEquals("a/.../b", "a/..././b".toPath(normalize = false).toString()) + assertEquals(".", "a/..".toPath(normalize = true).toString()) + assertEquals("a/..", "a/..".toPath(normalize = false).toString()) + assertEquals("../b", "../a/../b".toPath(normalize = true).toString()) + assertEquals("../a/../b", "../a/../b".toPath(normalize = false).toString()) + } + + @Test fun normalized() { + val normalizedRoot = "/".toPath() + assertEquals(normalizedRoot, "/a/..".toPath(normalize = true)) + assertEquals(normalizedRoot, "/a/..".toPath(normalize = false).normalized()) + assertEquals(normalizedRoot, "/a/..".toPath(normalize = true).normalized()) + + val normalizedRelative = "../b".toPath() + assertEquals(normalizedRelative, "../a/../b".toPath(normalize = true)) + assertEquals(normalizedRelative, "../a/../b".toPath(normalize = false).normalized()) + assertEquals(normalizedRelative, "../a/../b".toPath(normalize = true).normalized()) } } diff --git a/okio/src/commonTest/kotlin/okio/SystemFileSystemTest.kt b/okio/src/commonTest/kotlin/okio/SystemFileSystemTest.kt deleted file mode 100644 index df1d620a..00000000 --- a/okio/src/commonTest/kotlin/okio/SystemFileSystemTest.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2020 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 kotlinx.datetime.Clock -import kotlin.time.ExperimentalTime - -@ExperimentalTime -@ExperimentalFileSystem -class SystemFileSystemTest : AbstractFileSystemTest( - clock = Clock.System, - fileSystem = FileSystem.SYSTEM, - windowsLimitations = Path.DIRECTORY_SEPARATOR == "\\", - temporaryDirectory = FileSystem.SYSTEM_TEMPORARY_DIRECTORY -) diff --git a/okio/src/commonTest/kotlin/okio/UnsafeCursorTest.kt b/okio/src/commonTest/kotlin/okio/UnsafeCursorTest.kt index 680ebdc2..10290eac 100644 --- a/okio/src/commonTest/kotlin/okio/UnsafeCursorTest.kt +++ b/okio/src/commonTest/kotlin/okio/UnsafeCursorTest.kt @@ -17,6 +17,7 @@ package okio import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertTrue class UnsafeCursorTest { @Test fun acquireForRead() { @@ -43,7 +44,7 @@ class UnsafeCursorTest { val cursor = buffer.readAndWriteUnsafe() try { while (cursor.next() != -1) { - cursor.data!!.fill('z'.toByte(), cursor.start, cursor.end) + cursor.data!!.fill('z'.code.toByte(), cursor.start, cursor.end) } } finally { cursor.close() @@ -58,7 +59,7 @@ class UnsafeCursorTest { val cursor = buffer.readAndWriteUnsafe() try { cursor.expandBuffer(100) - cursor.data!!.fill('z'.toByte(), cursor.start, cursor.start + 100) + cursor.data!!.fill('z'.code.toByte(), cursor.start, cursor.start + 100) cursor.resizeBuffer(100L) } finally { cursor.close() @@ -77,11 +78,15 @@ class UnsafeCursorTest { val cursor = buffer.readAndWriteUnsafe() try { cursor.resizeBuffer(100L) - cursor.data!!.fill('z'.toByte(), cursor.start, cursor.end) + cursor.data!!.fill('z'.code.toByte(), cursor.start, cursor.end) } finally { cursor.close() } assertEquals("z".repeat(100), buffer.readUtf8()) } + + @Test fun testUnsafeCursorIsClosable() { + assertTrue(Closeable::class.isInstance(Buffer.UnsafeCursor())) + } } diff --git a/okio/src/commonTest/kotlin/okio/Utf8KotlinTest.kt b/okio/src/commonTest/kotlin/okio/Utf8KotlinTest.kt index 1ade4179..337fe83a 100644 --- a/okio/src/commonTest/kotlin/okio/Utf8KotlinTest.kt +++ b/okio/src/commonTest/kotlin/okio/Utf8KotlinTest.kt @@ -16,17 +16,17 @@ package okio -import okio.ByteString.Companion.decodeHex -import okio.internal.commonAsUtf8ToByteArray import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith +import okio.ByteString.Companion.decodeHex +import okio.internal.commonAsUtf8ToByteArray class Utf8KotlinTest { @Test fun oneByteCharacters() { assertEncoded("00", 0x00) // Smallest 1-byte character. - assertEncoded("20", ' '.toInt()) - assertEncoded("7e", '~'.toInt()) + assertEncoded("20", ' '.code) + assertEncoded("7e", '~'.code) assertEncoded("7f", 0x7f) // Largest 1-byte character. } @@ -90,10 +90,10 @@ class Utf8KotlinTest { @Test fun highSurrogateFollowedByNonSurrogate() { assertStringEncoded("3fee8080", "\ud800\ue000") // "?\ue000": Following character is too high. - assertCodePointDecoded("f090ee8080", REPLACEMENT_CODE_POINT, '\ue000'.toInt()) + assertCodePointDecoded("f090ee8080", REPLACEMENT_CODE_POINT, '\ue000'.code) assertStringEncoded("3f61", "\ud800\u0061") // "?a": Following character is too low. - assertCodePointDecoded("f09061", REPLACEMENT_CODE_POINT, 'a'.toInt()) + assertCodePointDecoded("f09061", REPLACEMENT_CODE_POINT, 'a'.code) } @Test fun doubleLowSurrogate() { @@ -113,7 +113,7 @@ class Utf8KotlinTest { @Test fun writeSurrogateCodePoint() { assertStringEncoded("ed9fbf", "\ud7ff") // Below lowest surrogate is okay. - assertCodePointDecoded("ed9fbf", '\ud7ff'.toInt()) + assertCodePointDecoded("ed9fbf", '\ud7ff'.code) assertStringEncoded("3f", "\ud800") // Lowest surrogate gets '?'. assertCodePointDecoded("eda080", REPLACEMENT_CODE_POINT) @@ -122,7 +122,7 @@ class Utf8KotlinTest { assertCodePointDecoded("edbfbf", REPLACEMENT_CODE_POINT) assertStringEncoded("ee8080", "\ue000") // Above highest surrogate is okay. - assertCodePointDecoded("ee8080", '\ue000'.toInt()) + assertCodePointDecoded("ee8080", '\ue000'.code) } @Test fun size() { diff --git a/okio/src/commonTest/kotlin/okio/time.kt b/okio/src/commonTest/kotlin/okio/time.kt deleted file mode 100644 index 36fdf94a..00000000 --- a/okio/src/commonTest/kotlin/okio/time.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2020 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 kotlinx.datetime.Instant - -@ExperimentalFileSystem -internal val FileMetadata.createdAt: Instant? - get() { - val createdAt = createdAtMillis ?: return null - return Instant.fromEpochMilliseconds(createdAt) - } - -@ExperimentalFileSystem -internal val FileMetadata.lastModifiedAt: Instant? - get() { - val lastModifiedAt = lastModifiedAtMillis ?: return null - return Instant.fromEpochMilliseconds(lastModifiedAt) - } - -@ExperimentalFileSystem -internal val FileMetadata.lastAccessedAt: Instant? - get() { - val lastAccessedAt = lastAccessedAtMillis ?: return null - return Instant.fromEpochMilliseconds(lastAccessedAt) - } |