From 17dc60aac9dc2377800764e977da26f31b5fca59 Mon Sep 17 00:00:00 2001 From: Alexander Shabalin Date: Mon, 23 Oct 2023 16:11:12 +0200 Subject: [PATCH] [K/N] Migrate runtime/text tests to new testing infra ^KT-61259 --- .../backend.native/tests/build.gradle | 44 +- .../tests/runtime/basic/print_utf8.kt | 37 ++ .../{text/utf8.out => basic/print_utf8.out} | 0 .../tests/runtime/text/chars0.kt | 109 ---- .../tests/runtime/text/indexof.kt | 38 -- .../tests/runtime/text/parse0.kt | 146 ------ .../tests/runtime/text/string0.kt | 19 - .../tests/runtime/text/string0.out | 6 - .../tests/runtime/text/string_builder0.kt | 277 ---------- .../tests/runtime/text/string_builder0.out | 1 - .../tests/runtime/text/string_builder1.kt | 14 - .../tests/runtime/text/string_builder1.out | 5 - .../tests/runtime/text/to_string0.kt | 50 -- .../tests/runtime/text/to_string0.out | 1 - .../backend.native/tests/runtime/text/trim.kt | 54 -- .../tests/runtime/text/trim.out | 1 - .../backend.native/tests/runtime/text/utf8.kt | 477 ------------------ .../runtime/test/text/CharNativeTest.kt | 49 ++ .../test/text/StringBuilderNativeTest.kt | 218 ++++++++ .../test/text/StringEncodingTestNative.kt | 209 ++++++++ .../runtime/test/text/StringNativeTest.kt | 48 ++ .../text/StringNumberConversionNativeTest.kt | 130 +++++ 22 files changed, 693 insertions(+), 1240 deletions(-) create mode 100644 kotlin-native/backend.native/tests/runtime/basic/print_utf8.kt rename kotlin-native/backend.native/tests/runtime/{text/utf8.out => basic/print_utf8.out} (100%) delete mode 100644 kotlin-native/backend.native/tests/runtime/text/chars0.kt delete mode 100644 kotlin-native/backend.native/tests/runtime/text/indexof.kt delete mode 100644 kotlin-native/backend.native/tests/runtime/text/parse0.kt delete mode 100644 kotlin-native/backend.native/tests/runtime/text/string0.kt delete mode 100644 kotlin-native/backend.native/tests/runtime/text/string0.out delete mode 100644 kotlin-native/backend.native/tests/runtime/text/string_builder0.kt delete mode 100644 kotlin-native/backend.native/tests/runtime/text/string_builder0.out delete mode 100644 kotlin-native/backend.native/tests/runtime/text/string_builder1.kt delete mode 100644 kotlin-native/backend.native/tests/runtime/text/string_builder1.out delete mode 100644 kotlin-native/backend.native/tests/runtime/text/to_string0.kt delete mode 100644 kotlin-native/backend.native/tests/runtime/text/to_string0.out delete mode 100644 kotlin-native/backend.native/tests/runtime/text/trim.kt delete mode 100644 kotlin-native/backend.native/tests/runtime/text/trim.out delete mode 100644 kotlin-native/backend.native/tests/runtime/text/utf8.kt create mode 100644 kotlin-native/runtime/test/text/StringBuilderNativeTest.kt create mode 100644 kotlin-native/runtime/test/text/StringNumberConversionNativeTest.kt diff --git a/kotlin-native/backend.native/tests/build.gradle b/kotlin-native/backend.native/tests/build.gradle index ec40adc8a61..9897e173a3b 100644 --- a/kotlin-native/backend.native/tests/build.gradle +++ b/kotlin-native/backend.native/tests/build.gradle @@ -2299,49 +2299,9 @@ tasks.register("listof1", KonanLocalTest) { source = "datagen/literals/listof1.kt" } -tasks.register("string_builder0", KonanLocalTest) { +tasks.register("print_utf8", KonanLocalTest) { useGoldenData = true - source = "runtime/text/string_builder0.kt" -} - -tasks.register("string_builder1", KonanLocalTest) { - useGoldenData = true - source = "runtime/text/string_builder1.kt" -} - -tasks.register("string0", KonanLocalTest) { - useGoldenData = true - source = "runtime/text/string0.kt" -} - -tasks.register("parse0", KonanLocalTest) { - source = "runtime/text/parse0.kt" -} - -tasks.register("to_string0", KonanLocalTest) { - useGoldenData = true - source = "runtime/text/to_string0.kt" -} - -tasks.register("trim", KonanLocalTest) { - useGoldenData = true - source = "runtime/text/trim.kt" -} - -tasks.register("chars0", KonanLocalTest) { - disabled = isAggressiveGC // TODO: Investigate why too slow - source = "runtime/text/chars0.kt" - expectedExitStatus = 0 -} - -tasks.register("indexof", KonanLocalTest) { - source = "runtime/text/indexof.kt" -} - -tasks.register("utf8", KonanLocalTest) { - enabled = !isAggressiveGC // TODO: Investigate why too slow - useGoldenData = true - source = "runtime/text/utf8.kt" + source = "runtime/basic/print_utf8.kt" } tasks.register("catch1", KonanLocalTest) { diff --git a/kotlin-native/backend.native/tests/runtime/basic/print_utf8.kt b/kotlin-native/backend.native/tests/runtime/basic/print_utf8.kt new file mode 100644 index 00000000000..1cffba08da5 --- /dev/null +++ b/kotlin-native/backend.native/tests/runtime/basic/print_utf8.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package runtime.basic.print_utf8 + +import kotlin.test.* +import kotlinx.cinterop.toKString + +fun convertUtf8to16(byteArray: ByteArray, action: (String) -> Unit) { + byteArray.decodeToString().let { action(it) } + byteArray.toKString().let { action(it) } +} + +@Test +fun testPrint() { + // Valid strings. + println("Hello") + println("Привет") + println("\uD800\uDC00") + println("") + + // Illegal surrogate pair -> default output + println("\uDC00\uD800") + // Lone surrogate -> default output + println("\uD80012") + println("\uDC0012") + println("12\uD800") + + // https://github.com/JetBrains/kotlin-native/issues/1091 + val array = byteArrayOf(0xF0.toByte(), 0x9F.toByte(), 0x98.toByte(), 0xA5.toByte()) + convertUtf8to16(array) { badString -> + assertEquals(2, badString.length) + println(badString) + } +} diff --git a/kotlin-native/backend.native/tests/runtime/text/utf8.out b/kotlin-native/backend.native/tests/runtime/basic/print_utf8.out similarity index 100% rename from kotlin-native/backend.native/tests/runtime/text/utf8.out rename to kotlin-native/backend.native/tests/runtime/basic/print_utf8.out diff --git a/kotlin-native/backend.native/tests/runtime/text/chars0.kt b/kotlin-native/backend.native/tests/runtime/text/chars0.kt deleted file mode 100644 index 7963474321d..00000000000 --- a/kotlin-native/backend.native/tests/runtime/text/chars0.kt +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license - * that can be found in the LICENSE file. - */ -@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class) - -package runtime.text.chars0 - -import kotlin.test.* - -fun testIsSupplementaryCodePoint() { - assertFalse(Char.isSupplementaryCodePoint(-1)) - for (c in 0..0xFFFF) { - assertFalse(Char.isSupplementaryCodePoint(c.toInt())) - } - for (c in 0xFFFF + 1..0x10FFFF) { - assertTrue(Char.isSupplementaryCodePoint(c)) - } - assertFalse(Char.isSupplementaryCodePoint(0x10FFFF + 1)) -} - -fun testIsSurrogatePair() { - assertFalse(Char.isSurrogatePair('\u0000', '\u0000')) - assertFalse(Char.isSurrogatePair('\u0000', '\uDC00')) - assertTrue( Char.isSurrogatePair('\uD800', '\uDC00')) - assertTrue( Char.isSurrogatePair('\uD800', '\uDFFF')) - assertTrue( Char.isSurrogatePair('\uDBFF', '\uDFFF')) - assertFalse(Char.isSurrogatePair('\uDBFF', '\uF000')) -} - -fun testToChars() { - assertTrue(charArrayOf('\uD800', '\uDC00').contentEquals(Char.toChars(0x010000))) - assertTrue(charArrayOf('\uD800', '\uDC01').contentEquals(Char.toChars(0x010001))) - assertTrue(charArrayOf('\uD801', '\uDC01').contentEquals(Char.toChars(0x010401))) - assertTrue(charArrayOf('\uDBFF', '\uDFFF').contentEquals(Char.toChars(0x10FFFF))) - - try { - Char.toChars(Int.MAX_VALUE) - throw AssertionError() - } catch (e: IllegalArgumentException) {} -} - -fun testToCodePoint() { - assertEquals(0x010000, Char.toCodePoint('\uD800', '\uDC00')) - assertEquals(0x010001, Char.toCodePoint('\uD800', '\uDC01')) - assertEquals(0x010401, Char.toCodePoint('\uD801', '\uDC01')) - assertEquals(0x10FFFF, Char.toCodePoint('\uDBFF', '\uDFFF')) -} - -// TODO: Uncomment when such operations are supported for supplementary codepoints and the API is public. -fun testCase() { - /* - assertEquals('A'.toInt(), Char.toUpperCase('a'.toInt())) - assertEquals('A'.toInt(), Char.toUpperCase('A'.toInt())) - assertEquals('1'.toInt(), Char.toUpperCase('1'.toInt())) - - assertEquals('a'.toInt(), Char.toLowerCase('A'.toInt())) - assertEquals('a'.toInt(), Char.toLowerCase('a'.toInt())) - assertEquals('1'.toInt(), Char.toLowerCase('1'.toInt())) - - assertEquals(0x010400, Char.toUpperCase(0x010428)) - assertEquals(0x010400, Char.toUpperCase(0x010400)) - assertEquals(0x10FFFF, Char.toUpperCase(0x10FFFF)) - assertEquals(0x110000, Char.toUpperCase(0x110000)) - - assertEquals(0x010428, Char.toLowerCase(0x010400)) - assertEquals(0x010428, Char.toLowerCase(0x010428)) - assertEquals(0x10FFFF, Char.toLowerCase(0x10FFFF)) - assertEquals(0x110000, Char.toLowerCase(0x110000)) - */ -} - -fun testCategory() { - assertTrue('\n' in CharCategory.CONTROL) - assertTrue('1' in CharCategory.DECIMAL_DIGIT_NUMBER) - assertTrue(' ' in CharCategory.SPACE_SEPARATOR) - assertTrue('a' in CharCategory.LOWERCASE_LETTER) - assertTrue('A' in CharCategory.UPPERCASE_LETTER) - assertTrue('<' in CharCategory.MATH_SYMBOL) - assertTrue(';' in CharCategory.OTHER_PUNCTUATION) - assertTrue('_' in CharCategory.CONNECTOR_PUNCTUATION) - assertTrue('$' in CharCategory.CURRENCY_SYMBOL) - assertTrue('\u2029' in CharCategory.PARAGRAPH_SEPARATOR) -} - -fun testIsHighSurrogate() { - assertTrue('\uD800'.isHighSurrogate()) - assertTrue('\uDBFF'.isHighSurrogate()) - assertFalse('\uDC00'.isHighSurrogate()) - assertFalse('\uDFFF'.isHighSurrogate()) -} - -fun testIsLowSurrogate() { - assertFalse('\uD800'.isLowSurrogate()) - assertFalse('\uDBFF'.isLowSurrogate()) - assertTrue('\uDC00'.isLowSurrogate()) - assertTrue('\uDFFF'.isLowSurrogate()) -} - -@Test fun runTest() { - testIsSurrogatePair() - testToChars() - testToCodePoint() - testIsSupplementaryCodePoint() - testCase() - testCategory() - testIsHighSurrogate() - testIsLowSurrogate() -} \ No newline at end of file diff --git a/kotlin-native/backend.native/tests/runtime/text/indexof.kt b/kotlin-native/backend.native/tests/runtime/text/indexof.kt deleted file mode 100644 index 6e44c6891cd..00000000000 --- a/kotlin-native/backend.native/tests/runtime/text/indexof.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license - * that can be found in the LICENSE file. - */ - -package runtime.text.indexof - -import kotlin.test.* - -@Test fun runTest() { - var str = "Hello World!!" // for indexOf String - var ch = 'a' // for indexOf Char - - assertEquals(6, str.indexOf("World", 0)) - assertEquals(6, str.indexOf("World", -1)) - - assertEquals(-1, str.indexOf(ch, 0)) - - str = "Kotlin/Native" - assertEquals(-1, str.indexOf("/", str.length + 1)) - assertEquals(-1, str.indexOf("/", Int.MAX_VALUE)) - assertEquals(str.length, str.indexOf("", Int.MAX_VALUE)) - assertEquals(1, str.indexOf("", 1)) - - assertEquals(8, str.indexOf(ch, 1)) - assertEquals(-1, str.indexOf(ch, str.length - 1)) - - str = "" - assertEquals(-1, str.indexOf("a", -3)) - assertEquals(0, str.indexOf("", 0)) - - assertEquals(-1, str.indexOf(ch, -3)) - assertEquals(-1, str.indexOf(ch, 10)) - - ch = 0.toChar() - assertEquals(-1, str.indexOf(ch, -3)) - assertEquals(-1, str.indexOf(ch, 10)) -} \ No newline at end of file diff --git a/kotlin-native/backend.native/tests/runtime/text/parse0.kt b/kotlin-native/backend.native/tests/runtime/text/parse0.kt deleted file mode 100644 index 926ad5e4ae0..00000000000 --- a/kotlin-native/backend.native/tests/runtime/text/parse0.kt +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license - * that can be found in the LICENSE file. - */ -@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class) - -package runtime.text.parse0 - -import kotlin.test.* -import kotlin.native.OsFamily -import kotlin.native.Platform - -@Test -fun runTest() { - assertEquals(false, "false".toBoolean()) - assertEquals(true, "true".toBoolean()) - - assertEquals(-1, "-1".toByte()) - assertEquals(10, "a".toByte(16)) - - assertEquals(170, "aa".toShort(16)) - assertEquals(30, "11110".toInt(2)) - - assertEquals(4294967295, "ffffffff".toLong(16)) - - assertFailsWith { - "ffffffff".toLong(10) - } -} - -@Test -fun checkDouble() { - // ===== toDouble() parsing ======= - assertEquals(0.5, "0.5".toDouble()) - assertEquals(-5000000000.0, "-00000000000000000000.5e10".toDouble()) - assertEquals(-0.005, "-00000000000000000000.5e-2".toDouble()) - assertEquals(50000000000.0, "+5e10".toDouble()) - assertEquals(50000000000.0, " +5e10 ".toDouble()) - assertEquals(0.052, "+5.2e-2".toDouble()) - assertEquals(520.0, "+5.2e2d".toDouble()) - assertEquals(0.052, "+5.2e-2d".toDouble()) - assertEquals(52340000000.0, "+5.234e+10d".toDouble()) - assertEquals(5.234E123, "+5.234e+123d".toDouble()) - assertEquals(5.234E123, "+5.234e+123f".toDouble()) - assertEquals(5.234E123, "+5.234e+123".toDouble()) - assertEquals(5.5, "5.5f".toDouble()) - - assertEquals(1.0 / 0.0, "+Infinity".toDouble()) - assertEquals(1.0 / 0.0, "Infinity".toDouble()) - assertEquals(-1.0 / 0.0, "-Infinity".toDouble()) - assertTrue("Infinity".toDouble().isInfinite(), "Infinity is expected for parsing Infinity") - - assertTrue("+NaN".toDouble().isNaN(), "NaN is expected for parsing +NaN") - assertTrue("NaN".toDouble().isNaN(), "NaN is expected for parsing NaN") - assertTrue("-NaN".toDouble().isNaN(), "NaN is expected for parsing -NaN") - - if (Platform.osFamily != OsFamily.WASM) { - assertFailsWith { - "+-5.0".toDouble() - } - assertFailsWith { - "d".toDouble() - } - assertFailsWith { - "5.5.3e123d".toDouble() - } - - // regression of incorrect processing of long lines - such values returned Infinity - assertFailsWith { - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".toDouble() - } - assertFailsWith { - "+-my free text with different letters $3213#. e ".toDouble() - } - assertFailsWith { - "eeeeeEEEEEeeeeeee".toDouble() - } - assertFailsWith { - "InfinityN".toDouble() - } - assertFailsWith { - "NaNPICEZy".toDouble() - } - } -} - -@Test -fun checkFloat() { - // ===== toFloat() parsing ======= - assertEquals(0.5f, "0.5".toFloat()) - assertEquals(-5000000000f, "-00000000000000000000.5e10f".toFloat()) - assertEquals(-0.005f, "-00000000000000000000.5e-2f".toFloat()) - assertEquals(50000000000f, "+5e10".toFloat()) - assertEquals(50000000000f, " +5e10 ".toFloat()) - assertEquals(0.052f, "+5.2e-2f".toFloat()) - assertEquals(520f, "+5.2e2f".toFloat()) - assertEquals(0.052f, "+5.2e-2f".toFloat()) - assertEquals(52340000000f, "+5.234e+10f".toFloat()) - assertEquals(1.0F / 0.0F, "+5.234e+123f".toFloat()) - - - assertEquals(1.0F / 0.0F, "+Infinity".toFloat()) - assertEquals(1.0F / 0.0F, "Infinity".toFloat()) - assertEquals(-1.0F / 0.0F, "-Infinity".toFloat()) - assertTrue("Infinity".toFloat().isInfinite(), "Infinity is expected for parsing Infinity") - - assertTrue("+NaN".toFloat().isNaN(), "NaN is expected for parsing +NaN") - assertTrue("NaN".toFloat().isNaN(), "NaN is expected for parsing NaN") - assertTrue("-NaN".toFloat().isNaN(), "NaN is expected for parsing -NaN") - - if (Platform.osFamily != OsFamily.WASM) { - assertFailsWith { - "+-5.0f".toFloat() - } - assertFailsWith { - "f".toFloat() - } - assertFailsWith { - "5.5.3e123f".toFloat() - } - - // regression of incorrect processing of long lines - such values returned Infinity - assertFailsWith { - // should be more than 38 symbols - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".toFloat() - } - assertFailsWith { - // should be more than 38 symbols - "this string is not a numb3r, am I right?????????????".toFloat() - } - assertFailsWith { - // should be more than 38 symbols - "+-my free text with different letters $3213#. e ".toFloat() - } - assertFailsWith { - // should be more than 38 symbols - "eeeeeEEEEEeeeeeee".toFloat() - } - assertFailsWith { - "InfinityN".toFloat() - } - assertFailsWith { - "NaNPICEZy".toFloat() - } - } -} \ No newline at end of file diff --git a/kotlin-native/backend.native/tests/runtime/text/string0.kt b/kotlin-native/backend.native/tests/runtime/text/string0.kt deleted file mode 100644 index b4d5eb55ec6..00000000000 --- a/kotlin-native/backend.native/tests/runtime/text/string0.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license - * that can be found in the LICENSE file. - */ - -package runtime.text.string0 - -import kotlin.test.* - -@Test fun runTest() { - val str = "hello" - println(str.equals("HElLo", true)) - val strI18n = "Привет" - println(strI18n.equals("прИВет", true)) - println(strI18n.toUpperCase()) - println(strI18n.toLowerCase()) - println("пока".capitalize()) - println("http://jetbrains.com".startsWith("http://")) -} \ No newline at end of file diff --git a/kotlin-native/backend.native/tests/runtime/text/string0.out b/kotlin-native/backend.native/tests/runtime/text/string0.out deleted file mode 100644 index 305ef9c1e26..00000000000 --- a/kotlin-native/backend.native/tests/runtime/text/string0.out +++ /dev/null @@ -1,6 +0,0 @@ -true -true -ПРИВЕТ -привет -Пока -true diff --git a/kotlin-native/backend.native/tests/runtime/text/string_builder0.kt b/kotlin-native/backend.native/tests/runtime/text/string_builder0.kt deleted file mode 100644 index eca6f122858..00000000000 --- a/kotlin-native/backend.native/tests/runtime/text/string_builder0.kt +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license - * that can be found in the LICENSE file. - */ - -package runtime.text.string_builder0 - -import kotlin.test.* - -// Utils ==================================================================================================== -fun assertTrue(cond: Boolean) { - if (!cond) - throw AssertionError("Condition expected to be true") -} - -fun assertFalse(cond: Boolean) { - if (cond) - throw AssertionError("Condition expected to be false") -} - -fun assertEquals(value1: String, value2: String) { - if (value1 != value2) - throw AssertionError("FAIL: '" + value1 + "' != '" + value2 + "'") -} - -fun assertEquals(value1: Int, value2: Int) { - if (value1 != value2) - throw AssertionError("FAIL" + value1.toString() + " != " + value2.toString()) -} - -fun assertEquals(builder: StringBuilder, content: String) = assertEquals(builder.toString(), content) - -// IndexOutOfBoundsException. -fun assertException(body: () -> Unit) { - try { - body() - throw AssertionError ("Test failed: no IndexOutOfBoundsException on wrong indices") - } catch (e: IndexOutOfBoundsException) { - } catch (e: IllegalArgumentException) {} -} - -// Insert =================================================================================================== -fun testInsertString(initial: String, index: Int, toInsert: String, expected: String) { - assertEquals(StringBuilder(initial).insert(index, toInsert), expected) - assertEquals(StringBuilder(initial).insert(index, toInsert.toCharArray()), expected) - assertEquals(StringBuilder(initial).insert(index, toInsert as CharSequence), expected) -} - -fun testInsertStringException(initial: String, index: Int, toInsert: String) { - assertException { StringBuilder(initial).insert(index, toInsert) } - assertException { StringBuilder(initial).insert(index, toInsert.toCharArray()) } - assertException { StringBuilder(initial).insert(index, toInsert as CharSequence) } -} - -fun testInsertSingle(value: Byte) { - assertEquals(StringBuilder("abcd").insert(0, value), value.toString() + "abcd") - assertEquals(StringBuilder("abcd").insert(4, value), "abcd" + value.toString()) - assertEquals(StringBuilder("abcd").insert(2, value), "ab" + value.toString() + "cd") - assertEquals(StringBuilder("").insert(0, value), value.toString()) -} - -fun testInsertSingle(value: Short) { - assertEquals(StringBuilder("abcd").insert(0, value), value.toString() + "abcd") - assertEquals(StringBuilder("abcd").insert(4, value), "abcd" + value.toString()) - assertEquals(StringBuilder("abcd").insert(2, value), "ab" + value.toString() + "cd") - assertEquals(StringBuilder("").insert(0, value), value.toString()) -} - -fun testInsertSingle(value: Int) { - assertEquals(StringBuilder("abcd").insert(0, value), value.toString() + "abcd") - assertEquals(StringBuilder("abcd").insert(4, value), "abcd" + value.toString()) - assertEquals(StringBuilder("abcd").insert(2, value), "ab" + value.toString() + "cd") - assertEquals(StringBuilder("").insert(0, value), value.toString()) -} - -fun testInsertSingle(value: Long) { - assertEquals(StringBuilder("abcd").insert(0, value), value.toString() + "abcd") - assertEquals(StringBuilder("abcd").insert(4, value), "abcd" + value.toString()) - assertEquals(StringBuilder("abcd").insert(2, value), "ab" + value.toString() + "cd") - assertEquals(StringBuilder("").insert(0, value), value.toString()) -} - -fun testInsertSingle(value: Float) { - assertEquals(StringBuilder("abcd").insert(0, value), value.toString() + "abcd") - assertEquals(StringBuilder("abcd").insert(4, value), "abcd" + value.toString()) - assertEquals(StringBuilder("abcd").insert(2, value), "ab" + value.toString() + "cd") - assertEquals(StringBuilder("").insert(0, value), value.toString()) -} - -fun testInsertSingle(value: Double) { - assertEquals(StringBuilder("abcd").insert(0, value), value.toString() + "abcd") - assertEquals(StringBuilder("abcd").insert(4, value), "abcd" + value.toString()) - assertEquals(StringBuilder("abcd").insert(2, value), "ab" + value.toString() + "cd") - assertEquals(StringBuilder("").insert(0, value), value.toString()) -} - -fun testInsertSingle(value: Any?) { - assertEquals(StringBuilder("abcd").insert(0, value), value.toString() + "abcd") - assertEquals(StringBuilder("abcd").insert(4, value), "abcd" + value.toString()) - assertEquals(StringBuilder("abcd").insert(2, value), "ab" + value.toString() + "cd") - assertEquals(StringBuilder("").insert(0, value), value.toString()) -} - -fun testInsertSingle(value: Char) { - assertEquals(StringBuilder("abcd").insert(0, value), value.toString() + "abcd") - assertEquals(StringBuilder("abcd").insert(4, value), "abcd" + value.toString()) - assertEquals(StringBuilder("abcd").insert(2, value), "ab" + value.toString() + "cd") - assertEquals(StringBuilder("").insert(0, value), value.toString()) -} - -fun testInsert() { - // String/CharSequence/CharArray. - testInsertString("abcd", 0, "12", "12abcd") - testInsertString("abcd", 4, "12", "abcd12") - testInsertString("abcd", 2, "12", "ab12cd") - testInsertString("", 0, "12", "12") - testInsertStringException("a", -1, "1") - testInsertStringException("a", 2, "1") - - // Null inserting. - assertEquals(StringBuilder("abcd").insert(0, null as CharSequence?), "nullabcd") - assertEquals(StringBuilder("abcd").insert(4, null as CharSequence?), "abcdnull") - assertEquals(StringBuilder("abcd").insert(2, null as CharSequence?), "abnullcd") - assertEquals(StringBuilder("").insert(0, null as CharSequence?), "null") - - // Subsequence of CharSequence. - // Insert in the beginning. - assertEquals(StringBuilder("abcd").insertRange(0, "1234", 0, 0), "abcd") // 0 symbols - assertEquals(StringBuilder("abcd").insertRange(0, "1234", 0, 1), "1abcd") // 1 symbol - assertEquals(StringBuilder("abcd").insertRange(0, "1234", 1, 3), "23abcd") // 2 symbols - - // Insert in the end. - assertEquals(StringBuilder("abcd").insertRange(4, "1234", 0, 0), "abcd") - assertEquals(StringBuilder("abcd").insertRange(4, "1234", 0, 1), "abcd1") - assertEquals(StringBuilder("abcd").insertRange(4, "1234", 1, 3), "abcd23") - - // Insert in the middle. - assertEquals(StringBuilder("abcd").insertRange(2, "1234", 0, 0), "abcd") - assertEquals(StringBuilder("abcd").insertRange(2, "1234", 0, 1), "ab1cd") - assertEquals(StringBuilder("abcd").insertRange(2, "1234", 1, 3), "ab23cd") - - // Incorrect indices. - assertException { StringBuilder("a").insertRange(-1, "1", 0, 0) } - assertException { StringBuilder("a").insertRange(2, "1", 0, 0) } - assertException { StringBuilder("a").insertRange(1, "1", -1, 0) } - assertException { StringBuilder("a").insertRange(1, "1", 0, 2) } - assertException { StringBuilder("a").insertRange(1, "123", 2, 0) } - - // Other types. - testInsertSingle(true) - testInsertSingle(42.toByte()) - testInsertSingle(42.toShort()) - testInsertSingle(42.toInt()) - testInsertSingle(42.toLong()) - testInsertSingle(42.2.toFloat()) - testInsertSingle(42.2.toDouble()) - testInsertSingle(object { - override fun toString(): String { - return "Object" - } - }) - testInsertSingle('a') -} - -// Reverse ================================================================================================== -fun testReverse(original: String, reversed: String, reversedBack: String) { - assertEquals(StringBuilder(original).reverse(), reversed) - assertEquals(StringBuilder(reversed).reverse(), reversedBack) -} - -fun testReverse() { - var builder = StringBuilder("123456") - assertTrue(builder === builder.reverse()) - assertEquals(builder, "654321") - - builder.setLength(1) - assertEquals(builder, "6") - - builder.setLength(0) - assertEquals(builder, "") - - var str: String = "a" - testReverse(str, str, str) - - str = "ab" - testReverse(str, "ba", str) - - str = "abcdef" - testReverse(str, "fedcba", str) - - str = "abcdefg" - testReverse(str, "gfedcba", str) - - str = "\ud800\udc00" - testReverse(str, str, str) - - str = "\udc00\ud800" - testReverse(str, "\ud800\udc00", "\ud800\udc00") - - str = "a\ud800\udc00" - testReverse(str, "\ud800\udc00a", str) - - str = "ab\ud800\udc00" - testReverse(str, "\ud800\udc00ba", str) - - str = "abc\ud800\udc00" - testReverse(str, "\ud800\udc00cba", str) - - str = "\ud800\udc00\udc01\ud801\ud802\udc02" - testReverse(str, "\ud802\udc02\ud801\udc01\ud800\udc00", - "\ud800\udc00\ud801\udc01\ud802\udc02") - - str = "\ud800\udc00\ud801\udc01\ud802\udc02" - testReverse(str, "\ud802\udc02\ud801\udc01\ud800\udc00", str) - - str = "\ud800\udc00\udc01\ud801a" - testReverse(str, "a\ud801\udc01\ud800\udc00", - "\ud800\udc00\ud801\udc01a") - - str = "a\ud800\udc00\ud801\udc01" - testReverse(str, "\ud801\udc01\ud800\udc00a", str) - - str = "\ud800\udc00\udc01\ud801ab" - testReverse(str, "ba\ud801\udc01\ud800\udc00", - "\ud800\udc00\ud801\udc01ab") - - str = "ab\ud800\udc00\ud801\udc01" - testReverse(str, "\ud801\udc01\ud800\udc00ba", str) - - str = "\ud800\udc00\ud801\udc01" - testReverse(str, "\ud801\udc01\ud800\udc00", str) - - str = "a\ud800\udc00z\ud801\udc01" - testReverse(str, "\ud801\udc01z\ud800\udc00a", str) - - str = "a\ud800\udc00bz\ud801\udc01" - testReverse(str, "\ud801\udc01zb\ud800\udc00a", str) - - str = "abc\ud802\udc02\ud801\udc01\ud800\udc00" - testReverse(str, "\ud800\udc00\ud801\udc01\ud802\udc02cba", str) - - str = "abcd\ud802\udc02\ud801\udc01\ud800\udc00" - testReverse(str, "\ud800\udc00\ud801\udc01\ud802\udc02dcba", str) -} - -// Basic ==================================================================================================== -fun testBasic() { - val sb = StringBuilder() - assertEquals(0, sb.length) - assertEquals("", sb.toString()) - sb.append(1) - assertEquals(1, sb.length) - assertEquals("1", sb.toString()) - sb.append(", ") - assertEquals(3, sb.length) - assertEquals("1, ", sb.toString()) - sb.append(true) - assertEquals(7, sb.length) - assertEquals("1, true", sb.toString()) - sb.append(12345678L) - assertEquals(15, sb.length) - assertEquals("1, true12345678", sb.toString()) - sb.append(null as CharSequence?) - assertEquals(19, sb.length) - assertEquals("1, true12345678null", sb.toString()) - - sb.setLength(0) - assertEquals(0, sb.length) - assertEquals("", sb.toString()) -} - -@Test fun runTest() { - testBasic() - testInsert() - testReverse() - println("OK") -} \ No newline at end of file diff --git a/kotlin-native/backend.native/tests/runtime/text/string_builder0.out b/kotlin-native/backend.native/tests/runtime/text/string_builder0.out deleted file mode 100644 index d86bac9de59..00000000000 --- a/kotlin-native/backend.native/tests/runtime/text/string_builder0.out +++ /dev/null @@ -1 +0,0 @@ -OK diff --git a/kotlin-native/backend.native/tests/runtime/text/string_builder1.kt b/kotlin-native/backend.native/tests/runtime/text/string_builder1.kt deleted file mode 100644 index 3b3681f5f14..00000000000 --- a/kotlin-native/backend.native/tests/runtime/text/string_builder1.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license - * that can be found in the LICENSE file. - */ - -package runtime.text.string_builder1 - -import kotlin.test.* - -@Test fun runTest() { - val a = StringBuilder() - a.append("Hello").appendLine("Kotlin").appendLine(42).appendLine(0.1).appendLine(true) - println(a.toString()) -} diff --git a/kotlin-native/backend.native/tests/runtime/text/string_builder1.out b/kotlin-native/backend.native/tests/runtime/text/string_builder1.out deleted file mode 100644 index 35ef9390476..00000000000 --- a/kotlin-native/backend.native/tests/runtime/text/string_builder1.out +++ /dev/null @@ -1,5 +0,0 @@ -HelloKotlin -42 -0.1 -true - diff --git a/kotlin-native/backend.native/tests/runtime/text/to_string0.kt b/kotlin-native/backend.native/tests/runtime/text/to_string0.kt deleted file mode 100644 index 573dce8ef38..00000000000 --- a/kotlin-native/backend.native/tests/runtime/text/to_string0.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license - * that can be found in the LICENSE file. - */ - -package runtime.text.to_string0 - -import kotlin.test.* - -// Based on Apache Harmony tests. - -fun assertEquals(actual: String, expected: String, msg: String) { - if (actual != expected) throw AssertionError("$msg. Actual: $actual. Expected: $expected") -} - -fun testIntToStringWithRadix() { - assertEquals(2147483647.toString(8), "17777777777", "Octal string") - assertEquals(2147483647.toString(16), "7fffffff", "Hex string") - assertEquals(2147483647.toString(2), "1111111111111111111111111111111", "Binary string") - assertEquals(2147483647.toString(10), "2147483647", "Decimal string") - - assertEquals((-2147483647).toString(8), "-17777777777", "Octal string") - assertEquals((-2147483647).toString(16), "-7fffffff", "Hex string") - assertEquals((-2147483647).toString(2), "-1111111111111111111111111111111", "Binary string") - assertEquals((-2147483647).toString(10), "-2147483647", "Decimal string") - - assertEquals((-2147483648).toString(8), "-20000000000", "Octal string") - assertEquals((-2147483648).toString(16), "-80000000", "Hex string") - assertEquals((-2147483648).toString(2), "-10000000000000000000000000000000", "Binary string") - assertEquals((-2147483648).toString(10), "-2147483648", "Decimal string") -} - -fun testLongToStringWithRadix() { - assertEquals(100000000L.toString(10), "100000000", "Decimal string") - assertEquals(68719476735L.toString(16), "fffffffff", "Hex string") - assertEquals(8589934591L.toString(8), "77777777777", "Octal string") - assertEquals(8796093022207L.toString(2), "1111111111111111111111111111111111111111111", "Binary string") - - assertEquals((-0x7fffffffffffffffL - 1).toString(10), "-9223372036854775808", "Min decimal string") - assertEquals(0x7fffffffffffffffL.toString(10), "9223372036854775807", "Max decimal string") - assertEquals((-0x7fffffffffffffffL - 1).toString(16), "-8000000000000000", "Min hex string") - assertEquals(0x7fffffffffffffffL.toString(16), "7fffffffffffffff", "Max hex string") - -} - -@Test fun runTest() { - testIntToStringWithRadix() - testLongToStringWithRadix() - println("OK") -} \ No newline at end of file diff --git a/kotlin-native/backend.native/tests/runtime/text/to_string0.out b/kotlin-native/backend.native/tests/runtime/text/to_string0.out deleted file mode 100644 index d86bac9de59..00000000000 --- a/kotlin-native/backend.native/tests/runtime/text/to_string0.out +++ /dev/null @@ -1 +0,0 @@ -OK diff --git a/kotlin-native/backend.native/tests/runtime/text/trim.kt b/kotlin-native/backend.native/tests/runtime/text/trim.kt deleted file mode 100644 index 9b61a870769..00000000000 --- a/kotlin-native/backend.native/tests/runtime/text/trim.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license - * that can be found in the LICENSE file. - */ - -package runtime.text.trim - -import kotlin.test.* - -/** - * Tests correct conversions of floats/doubles along with trimming of leading/trailing whitespaces. - * String.trim() trims all whitespaces (see kotlin/text/Strings.kt) while String.toFloat() uses - * the same approach as java.lang.Float.parseFloat() - */ -@Test fun runTest() { - convertToFloatingPoint() - convertWithWhitespaces() - trimWhitespaces() - - println("OK") -} - -private fun convertToFloatingPoint() { - assertEquals(expected = 3.14F, actual = " 3.14 ".toFloat(), message = "String float should be trimmed") - assertEquals(expected = 3.14, actual = " 3.14 ".toDouble(), message = "String double should be trimmed") - assertEquals(expected = 7.15F, actual = "\u0019 7.15 ".toFloat(), message = "String float should be trimmed") - assertEquals(expected = 42.3, actual = "\n 42.3 ".toDouble(), message = "String double should be trimmed") -} - -private fun convertWithWhitespaces() { - val s = "\u0009 \u000A 2.71 \u000D" - assertEquals(expected = 2.71F, actual = s.toFloat(), - message = "String should be cleared of LF, CR, TAB and converted to Float") - assertEquals(expected = 2.71, actual = s.toDouble(), - message = "String should be cleared of LF, CR, TAB and converted to Double") - - // Special symbols should not be trimmed during String to Float/Double conversion - assertFailsWith { "\u202F3.14".toFloat() } - assertFailsWith { "\u20293.14".toDouble() } - assertFailsWith { "3.14\u200B".toDouble() } - assertFailsWith { "3.14\u200B ABC".toDouble() } -} - -private fun trimWhitespaces() { - assertEquals(expected = "String", actual = " String".trim(), message = "Trim leading spaces") - assertEquals(expected = "String ", actual = " String ".trimStart(), message = "Trim start") - assertEquals(expected = " String", actual = " String \t ".trimEnd(), message = "Trim end") - - assertEquals(expected = "String", actual = "\u0020 \u202FString\u2028\u2029".trim(), - message = "Trim special whitespaces") - assertEquals(expected = "\u1FFFString", actual = "\u00A0 \u1FFFString".trim(), - message = "Trim special whitespace but should left a unicode symbol") - assertEquals(expected = "String\tSTR", actual = " \nString\tSTR ".trim(), message = "Trim newline") -} diff --git a/kotlin-native/backend.native/tests/runtime/text/trim.out b/kotlin-native/backend.native/tests/runtime/text/trim.out deleted file mode 100644 index d86bac9de59..00000000000 --- a/kotlin-native/backend.native/tests/runtime/text/trim.out +++ /dev/null @@ -1 +0,0 @@ -OK diff --git a/kotlin-native/backend.native/tests/runtime/text/utf8.kt b/kotlin-native/backend.native/tests/runtime/text/utf8.kt deleted file mode 100644 index 426e80e5b37..00000000000 --- a/kotlin-native/backend.native/tests/runtime/text/utf8.kt +++ /dev/null @@ -1,477 +0,0 @@ -/* - * Copyright 2010-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license - * that can be found in the LICENSE file. - */ -@file:OptIn(kotlinx.cinterop.ExperimentalForeignApi::class) - -package runtime.text.utf8 - -import kotlin.test.* -import kotlin.reflect.KClass -import kotlinx.cinterop.toKString - -// -------------------------------------- Utils -------------------------------------- -fun assertEquals(expected: ByteArray, actual: ByteArray, message: String) = - assertTrue(expected.contentEquals(actual), message) - - -fun checkUtf16to8(string: String, expected: IntArray, conversion: String.() -> ByteArray) { - expected.forEach { - assertTrue(it in Byte.MIN_VALUE..Byte.MAX_VALUE, "Expected array contains illegal values") - } - val expectedBytes = ByteArray(expected.size) { i -> expected[i].toByte() } - val actual = string.conversion() - assertEquals(expectedBytes, actual, """ - Assert failed for string: $string - Expected: ${expected.joinToString()} - Actual: ${actual.joinToString()} - """.trimIndent()) -} - -// Utils for checking successful UTF-16 to UTF-8 conversion. -fun checkUtf16to8Replacing(string: String, expected: IntArray) { - checkUtf16to8(string, expected) { encodeToByteArray() } -} -fun checkUtf16to8Throwing(string: String, expected: IntArray) { - checkUtf16to8(string, expected) { encodeToByteArray(throwOnInvalidSequence = true) } -} -fun checkValidUtf16to8(string: String, expected: IntArray) { - checkUtf16to8Replacing(string, expected) - checkUtf16to8Throwing(string, expected) -} - -fun checkUtf16to8Replacing(string: String, expected: IntArray, start: Int, size: Int) { - checkUtf16to8(string, expected) { encodeToByteArray(start, start + size) } -} -fun checkUtf16to8Throwing(string: String, expected: IntArray, start: Int, size: Int) { - checkUtf16to8(string, expected) { encodeToByteArray(start, start + size, true) } -} -fun checkValidUtf16to8(string: String, expected: IntArray, start: Int, size: Int) { - checkUtf16to8Replacing(string, expected, start, size) - checkUtf16to8Throwing(string, expected, start, size) -} - - -// Utils for checking successful UTF-8 to UTF-16 conversion. -fun checkUtf8to16(expected: String, array: IntArray, conversion: ByteArray.() -> String) { - array.forEach { - assertTrue(it in Byte.MIN_VALUE..Byte.MAX_VALUE, "Expected array contains illegal values") - } - val arrayBytes = ByteArray(array.size) { i -> array[i].toByte() } - val actual = arrayBytes.conversion() - assertEquals(expected, actual, """ - Assert failed for string: $expected - Expected: $expected - Actual: $actual - """.trimIndent()) -} - -fun checkZeroTerminatedUtf8to16Replacing(expected: String, array: IntArray) { - checkUtf8to16(expected, array) { toKString() } - checkUtf8to16(expected, array.copyOf(array.size + 1)) { toKString() } -} -fun checkUtf8to16Replacing(expected: String, array: IntArray) { - checkUtf8to16(expected, array) { decodeToString() } - checkZeroTerminatedUtf8to16Replacing(expected, array) -} -fun checkZeroTerminatedUtf8to16Throwing(expected: String, array: IntArray) { - checkUtf8to16(expected, array) { toKString(throwOnInvalidSequence = true) } - checkUtf8to16(expected, array.copyOf(array.size + 1)) { toKString(throwOnInvalidSequence = true) } -} -fun checkUtf8to16Throwing(expected: String, array: IntArray) { - checkUtf8to16(expected, array) { decodeToString(throwOnInvalidSequence = true) } - checkZeroTerminatedUtf8to16Throwing(expected, array) -} -fun checkValidUtf8to16(expected: String, array: IntArray) { - checkUtf8to16Replacing(expected, array) - checkUtf8to16Throwing(expected, array) -} -fun checkValidZeroTerminatedUtf8to16(expected: String, array: IntArray) { - checkZeroTerminatedUtf8to16Replacing(expected, array) - checkZeroTerminatedUtf8to16Throwing(expected, array) -} - -fun checkZeroTerminatedUtf8to16Replacing(expected: String, array: IntArray, start: Int, size: Int) { - checkUtf8to16(expected, array) { toKString(start, start + size) } - checkUtf8to16(expected, array.copyOf(array.size + 1)) { toKString(start, start + size) } -} -fun checkUtf8to16Replacing(expected: String, array: IntArray, start: Int, size: Int) { - checkUtf8to16(expected, array) { decodeToString(start, start + size) } - checkZeroTerminatedUtf8to16Replacing(expected, array, start, size) -} -fun checkZeroTerminatedUtf8to16Throwing(expected: String, array: IntArray, start: Int, size: Int) { - checkUtf8to16(expected, array) { toKString(start, start + size, true) } - checkUtf8to16(expected, array.copyOf(array.size + 1)) { toKString(start, start + size, true) } -} -fun checkUtf8to16Throwing(expected: String, array: IntArray, start: Int, size: Int) { - checkUtf8to16(expected, array) { decodeToString(start, start + size, true) } - checkZeroTerminatedUtf8to16Throwing(expected, array, start, size) -} -fun checkValidUtf8to16(expected: String, array: IntArray, start: Int, size: Int) { - checkUtf8to16Replacing(expected, array, start, size) - checkUtf8to16Throwing(expected, array, start, size) -} -fun checkValidZeroTerminatedUtf8to16(expected: String, array: IntArray, start: Int, size: Int) { - checkZeroTerminatedUtf8to16Replacing(expected, array, start, size) - checkZeroTerminatedUtf8to16Throwing(expected, array, start, size) -} - - -// Utils for checking malformed UTF-16 to UTF-8 conversion. -fun checkThrows(e: KClass, string: String, action: () -> Unit) { - var exception: Throwable? = null - try { - action() - } catch (e: Throwable) { - exception = e - } - assertNotNull(exception, "No exception was thrown for string: $string") - assertTrue(e.isInstance(exception),""" - Wrong exception was thrown for string: $string - Expected: ${e.qualifiedName} - Actual: ${exception::class.qualifiedName}: $exception} - """.trimIndent()) -} - -fun checkUtf16to8Throws(string: String) { - checkThrows(CharacterCodingException::class, string) { string.encodeToByteArray(throwOnInvalidSequence = true) } -} -fun checkUtf16to8Throws(string: String, start: Int, size: Int) { - checkThrows(CharacterCodingException::class, string) { string.encodeToByteArray(start, start + size, true) } -} - - -// Utils for checking malformed UTF-8 to UTF-16 conversion. -fun checkUtf8to16Throws(e: KClass, string: String, array: IntArray, conversion: ByteArray.() -> String) - = checkThrows(e, string) { - array.forEach { - assertTrue(it in Byte.MIN_VALUE..Byte.MAX_VALUE, "Expected array contains illegal values") - } - val arrayBytes = ByteArray(array.size) { i -> array[i].toByte() } - arrayBytes.conversion() -} - -fun checkZeroTerminatedUtf8to16Throws(string: String, array: IntArray) { - checkUtf8to16Throws(CharacterCodingException::class, string, array) { toKString(throwOnInvalidSequence = true) } - checkUtf8to16Throws(CharacterCodingException::class, string, array.copyOf(array.size + 1)) { toKString(throwOnInvalidSequence = true) } -} -fun checkUtf8to16Throws(string: String, array: IntArray) { - checkUtf8to16Throws(CharacterCodingException::class, string, array) { decodeToString(throwOnInvalidSequence = true) } - checkZeroTerminatedUtf8to16Throws(string, array) -} -fun checkZeroTerminatedUtf8to16Throws(string: String, array: IntArray, start: Int, size: Int) { - checkUtf8to16Throws(CharacterCodingException::class, string, array) { toKString(start, start + size, true) } - checkUtf8to16Throws(CharacterCodingException::class, string, array.copyOf(array.size + 1)) { toKString(start, start + size, true) } -} -fun checkUtf8to16Throws(string: String, array: IntArray, start: Int, size: Int) { - checkUtf8to16Throws(CharacterCodingException::class, string, array) { decodeToString(start, start + size, true) } - checkZeroTerminatedUtf8to16Throws(string, array, start, size) -} - - -// Utils for checking String to floating-point number conversion. -fun checkDoubleConversionThrows(string: String) = checkThrows(NumberFormatException::class, string) { - string.toDouble() -} - -fun checkFloatConversionThrows(string: String) = checkThrows(NumberFormatException::class, string) { - string.toFloat() -} - - -// Utils for checking invalid-bounds-exception thrown by UTF-16 to UTF-8 conversion. -fun checkOutOfBoundsUtf16to8Replacing(e: KClass, string: String, start: Int, size: Int) { - checkThrows(e, string) { string.encodeToByteArray(start, start + size) } -} -fun checkOutOfBoundsUtf16to8Throwing(e: KClass, string: String, start: Int, size: Int) { - checkThrows(e, string) { string.encodeToByteArray(start, start + size, true) } -} -fun checkOutOfBoundsUtf16to8(e: KClass, string: String, start: Int, size: Int) { - checkOutOfBoundsUtf16to8Replacing(e, string, start, size) - checkOutOfBoundsUtf16to8Throwing(e, string, start, size) -} - - -// Utils for checking invalid-bounds-exception thrown by UTF-8 to UTF-16 conversion. -fun checkOutOfBoundsZeroTerminatedUtf8to16Replacing(e: KClass, string: String, byteArray: ByteArray, start: Int, size: Int) { - checkThrows(e, string) { byteArray.toKString(start, start + size) } -} -fun checkOutOfBoundsUtf8to16Replacing(e: KClass, string: String, byteArray: ByteArray, start: Int, size: Int) { - checkThrows(e, string) { byteArray.decodeToString(start, start + size) } - checkOutOfBoundsZeroTerminatedUtf8to16Replacing(e, string, byteArray, start, size) -} -fun checkOutOfBoundsZeroTerminatedUtf8to16Throwing(e: KClass, string: String, byteArray: ByteArray, start: Int, size: Int) { - checkThrows(e, string) { byteArray.toKString(start, start + size, true) } -} -fun checkOutOfBoundsUtf8to16Throwing(e: KClass, string: String, byteArray: ByteArray, start: Int, size: Int) { - checkThrows(e, string) { byteArray.decodeToString(start, start + size, true) } - checkOutOfBoundsZeroTerminatedUtf8to16Throwing(e, string, byteArray, start, size) -} -fun checkOutOfBoundsUtf8to16(e: KClass, string: String, byteArray: ByteArray, start: Int, size: Int) { - checkOutOfBoundsUtf8to16Replacing(e, string, byteArray, start, size) - checkOutOfBoundsUtf8to16Throwing(e, string, byteArray, start, size) -} -fun checkOutOfBoundsZeroTerminatedUtf8to16(e: KClass, string: String, byteArray: ByteArray, start: Int, size: Int) { - checkOutOfBoundsZeroTerminatedUtf8to16Replacing(e, string, byteArray, start, size) - checkOutOfBoundsZeroTerminatedUtf8to16Throwing(e, string, byteArray, start, size) -} - - -// Util for performing action on result of UTF-8 to UTF-16 conversion. -fun convertUtf8to16(byteArray: ByteArray, action: (String) -> Unit) { - byteArray.decodeToString().let { action(it) } - byteArray.toKString().let { action(it) } -} - -// ------------------------- Test UTF-16 to UTF-8 conversion ------------------------- -fun test16to8() { - // Valid strings. - checkValidUtf16to8("Hello", intArrayOf('H'.toInt(), 'e'.toInt(), 'l'.toInt(), 'l'.toInt(), 'o'.toInt())) - checkValidUtf16to8("Привет", intArrayOf(-48, -97, -47, -128, -48, -72, -48, -78, -48, -75, -47, -126)) - checkValidUtf16to8("\uD800\uDC00", intArrayOf(-16, -112, -128, -128)) - checkValidUtf16to8("", intArrayOf()) - - // Test manual conversion with replacement. - // Illegal surrogate pair -> replace with default - checkUtf16to8Replacing("\uDC00\uD800", intArrayOf(-17, -65, -67, -17, -65, -67)) - // Different kinds of input - checkUtf16to8Replacing("\uD800\uDC001\uDC00\uD800", - intArrayOf(-16, -112, -128, -128, '1'.toInt(), -17, -65, -67, -17, -65, -67)) - // Lone surrogate - replace with default - checkUtf16to8Replacing("\uD80012", intArrayOf(-17, -65, -67, '1'.toInt(), '2'.toInt())) - checkUtf16to8Replacing("\uDC0012", intArrayOf(-17, -65, -67, '1'.toInt(), '2'.toInt())) - checkUtf16to8Replacing("12\uD800", intArrayOf('1'.toInt(), '2'.toInt(), -17, -65, -67)) - - // Test manual conversion with an exception if an input is invalid. - // Illegal surrogate pair -> throw - checkUtf16to8Throws("\uDC00\uD800") - // Different kinds of input (including illegal one) -> throw - checkUtf16to8Throws("\uD800\uDC001\uDC00\uD800") - // Lone surrogate - throw - checkUtf16to8Throws("\uD80012") - checkUtf16to8Throws("\uDC0012") - checkUtf16to8Throws("12\uD800") - - // Test double parsing. - assertEquals(4.2, "4.2".toDouble()) - // Illegal surrogate pair -> throw - checkDoubleConversionThrows("\uDC00\uD800") - // Different kinds of input (including illegal one) -> throw - checkDoubleConversionThrows("\uD800\uDC001\uDC00\uD800") - // Lone surrogate - throw - checkDoubleConversionThrows("\uD80012") - checkDoubleConversionThrows("\uDC0012") - checkDoubleConversionThrows("12\uD800") - - // Test float parsing. - assertEquals(4.2F, "4.2".toFloat()) - // Illegal surrogate pair -> throw - checkFloatConversionThrows("\uDC00\uD800") - // Different kinds of input (including illegal one) -> throw - checkFloatConversionThrows("\uD800\uDC001\uDC00\uD800") - // Lone surrogate - throw - checkFloatConversionThrows("\uD80012") - checkFloatConversionThrows("\uDC0012") - checkFloatConversionThrows("12\uD800") -} - -fun test16to8CustomBorders() { - // Valid strings. - checkValidUtf16to8("Hello!", intArrayOf('H'.toInt(), 'e'.toInt()), 0, 2) - checkValidUtf16to8("Hello!", intArrayOf('e'.toInt(), 'l'.toInt()), 1, 2) - checkValidUtf16to8("Hello!", intArrayOf('o'.toInt(), '!'.toInt()), 4, 2) - checkValidUtf16to8("Hello!", intArrayOf(), 0, 0) - checkValidUtf16to8("Hello!", intArrayOf(), 6, 0) - - checkValidUtf16to8("\uD800\uDC00\uD800\uDC00\uD800\uDC00\uD800\uDC00", - intArrayOf(-16, -112, -128, -128, -16, -112, -128, -128), 0, 4) - checkValidUtf16to8("\uD800\uDC00\uD800\uDC00\uD800\uDC00\uD800\uDC00", - intArrayOf(-16, -112, -128, -128, -16, -112, -128, -128), 2, 4) - checkValidUtf16to8("\uD800\uDC00\uD800\uDC00\uD800\uDC00\uD800\uDC00", - intArrayOf(-16, -112, -128, -128, -16, -112, -128, -128), 4, 4) - - // Index out of bound - checkOutOfBoundsUtf16to8(IndexOutOfBoundsException::class, "Hello", -1, 4) - checkOutOfBoundsUtf16to8(IndexOutOfBoundsException::class, "Hello", 5, 10) - checkOutOfBoundsUtf16to8(IndexOutOfBoundsException::class, "Hello", 2, 10) - checkOutOfBoundsUtf16to8(IllegalArgumentException::class, "Hello", 3, -2) - - // Test manual conversion with replacement and custom borders. - // Illegal surrogate pair -> replace with default - checkUtf16to8Replacing("\uDC00\uD80012", - intArrayOf(-17, -65, -67, -17, -65, -67, '1'.toInt()), 0, 3) - checkUtf16to8Replacing("1\uDC00\uD8002", - intArrayOf(-17, -65, -67, -17, -65, -67, '2'.toInt()), 1, 3) - checkUtf16to8Replacing("12\uDC00\uD800", - intArrayOf('2'.toInt(), -17, -65, -67, -17, -65, -67), 1, 3) - - // Lone surrogate - replace with default - checkUtf16to8Replacing("1\uD800\uDC002", intArrayOf('1'.toInt(), -17, -65, -67), 0, 2) - checkUtf16to8Replacing("1\uD800\uDC002", intArrayOf(-17, -65, -67, '2'.toInt()), 2, 2) - - // Test manual conversion with an exception if an input is invalid and custom borders. - // Illegal surrogate pair -> throw - checkUtf16to8Throws("\uDC00\uD80012", 0, 3) - checkUtf16to8Throws("1\uDC00\uD8002", 1, 3) - checkUtf16to8Throws("12\uDC00\uD800", 1, 3) - - // Lone surrogate -> throw - checkUtf16to8Throws("1\uD800\uDC002", 0, 2) - checkUtf16to8Throws("1\uD800\uDC002", 2, 2) -} - -fun testPrint() { - // Valid strings. - println("Hello") - println("Привет") - println("\uD800\uDC00") - println("") - - // Illegal surrogate pair -> default output - println("\uDC00\uD800") - // Lone surrogate -> default output - println("\uD80012") - println("\uDC0012") - println("12\uD800") - - // https://github.com/JetBrains/kotlin-native/issues/1091 - val array = byteArrayOf(0xF0.toByte(), 0x9F.toByte(), 0x98.toByte(), 0xA5.toByte()) - convertUtf8to16(array) { badString -> - assertEquals(2, badString.length) - println(badString) - } -} - -// ------------------------- Test UTF-8 to UTF-16 conversion ------------------------- -fun test8to16() { - // Valid strings. - checkValidUtf8to16("Hello", intArrayOf('H'.toInt(), 'e'.toInt(), 'l'.toInt(), 'l'.toInt(), 'o'.toInt())) - checkValidUtf8to16("Привет", intArrayOf(-48, -97, -47, -128, -48, -72, -48, -78, -48, -75, -47, -126)) - checkValidUtf8to16("\uD800\uDC00", intArrayOf(-16, -112, -128, -128)) - checkValidUtf8to16("", intArrayOf()) - - // Test manual conversion with replacement. - // Incorrect UTF-8 lead character. - checkUtf8to16Replacing("\uFFFD1", intArrayOf(-1, '1'.toInt())) - - // Incomplete codepoint. - checkUtf8to16Replacing("\uFFFD1", intArrayOf(-16, -97, -104, '1'.toInt())) - checkUtf8to16Replacing("\uFFFD1\uFFFD", intArrayOf(-16, -97, -104, '1'.toInt(), -16, -97, -104)) - - // Test manual conversion with exception throwing - // Incorrect UTF-8 lead character -> throw. - checkUtf8to16Throws("\uFFFD1", intArrayOf(-1, '1'.toInt())) - - // Incomplete codepoint -> throw. - checkUtf8to16Throws("\uFFFD1", intArrayOf(-16, -97, -104, '1'.toInt())) - checkUtf8to16Throws("\uFFFD1\uFFFD", intArrayOf(-16, -97, -104, '1'.toInt(), -16, -97, -104)) -} - -fun test8to16CustomBorders() { - // Valid strings. - checkValidUtf8to16("He", - intArrayOf('H'.toInt(), 'e'.toInt(), 'l'.toInt(), 'l'.toInt(), 'o'.toInt()),0, 2) - checkValidUtf8to16("ll", - intArrayOf('H'.toInt(), 'e'.toInt(), 'l'.toInt(), 'l'.toInt(), 'o'.toInt()), 2, 2) - checkValidUtf8to16("lo", - intArrayOf('H'.toInt(), 'e'.toInt(), 'l'.toInt(), 'l'.toInt(), 'o'.toInt()), 3, 2) - checkValidUtf8to16("", - intArrayOf('H'.toInt(), 'e'.toInt(), 'l'.toInt(), 'l'.toInt(), 'o'.toInt()), 0, 0) - - checkValidUtf8to16("\uD800\uDC00\uD800\uDC00", - intArrayOf(-16, -112, -128, -128, -16, -112, -128, -128, -16, -112, -128, -128, -16, -112, -128, -128), - 0, 8) - checkValidUtf8to16("\uD800\uDC00\uD800\uDC00", - intArrayOf(-16, -112, -128, -128, -16, -112, -128, -128, -16, -112, -128, -128, -16, -112, -128, -128), - 4, 8) - checkValidUtf8to16("\uD800\uDC00\uD800\uDC00", - intArrayOf(-16, -112, -128, -128, -16, -112, -128, -128, -16, -112, -128, -128, -16, -112, -128, -128), - 8, 8) - - // Index out of bound - val helloArray = byteArrayOf('H'.toByte(), 'e'.toByte(), 'l'.toByte(), 'l'.toByte(), 'o'.toByte()) - checkOutOfBoundsUtf8to16(IndexOutOfBoundsException::class, "Hello", helloArray, -1, 4) - checkOutOfBoundsUtf8to16(IndexOutOfBoundsException::class, "Hello", helloArray, 5, 10) - checkOutOfBoundsUtf8to16(IndexOutOfBoundsException::class, "Hello", helloArray, 2, 10) - checkOutOfBoundsUtf8to16(IndexOutOfBoundsException::class, "Hello", helloArray, 10, 0) - checkOutOfBoundsUtf8to16(IllegalArgumentException::class, "Hello", helloArray, 3, -2) - - // Test manual conversion with replacement and custom borders. - // Incorrect UTF-8 lead character. - checkUtf8to16Replacing("\uFFFD1", intArrayOf(-1, '1'.toInt(), '2'.toInt()), 0, 2) - checkUtf8to16Replacing("\uFFFD2", intArrayOf('1'.toInt(), -1, '2'.toInt(), '3'.toInt()), 1, 2) - checkUtf8to16Replacing("2\uFFFD", intArrayOf('1'.toInt(), '2'.toInt(), -1), 1, 2) - - // Incomplete codepoint. - checkUtf8to16Replacing("\uFFFD1", intArrayOf(-16, -97, -104, '1'.toInt(), '2'.toInt()), 0, 4) - checkUtf8to16Replacing("\uFFFD2", intArrayOf('1'.toInt(), -16, -97, -104, '2'.toInt(), '3'.toInt()), 1, 4) - checkUtf8to16Replacing("2\uFFFD", intArrayOf('1'.toInt(), '2'.toInt(), -16, -97, -104), 1, 4) - - // Test manual conversion with an exception if an input is invalid and custom borders. - // Incorrect UTF-8 lead character. - checkUtf8to16Throws("\uFFFD1", intArrayOf(-1, '1'.toInt(), '2'.toInt()), 0, 2) - checkUtf8to16Throws("\uFFFD2", intArrayOf('1'.toInt(), -1, '2'.toInt(), '3'.toInt()), 1, 2) - checkUtf8to16Throws("2\uFFFD", intArrayOf('1'.toInt(), '2'.toInt(), -1), 1, 2) - - // Incomplete codepoint. - checkUtf8to16Throws("\uFFFD1", intArrayOf(-16, -97, -104, '1'.toInt(), '2'.toInt()), 0, 4) - checkUtf8to16Throws("\uFFFD2", intArrayOf('1'.toInt(), -16, -97, -104, '2'.toInt(), '3'.toInt()), 1, 4) - checkUtf8to16Throws("2\uFFFD", intArrayOf('1'.toInt(), '2'.toInt(), -16, -97, -104), 1, 4) -} - -// ----------------- Test zero-terminated UTF-8 to UTF-16 conversion ----------------- -fun testZeroTerminated8To16() { - // Valid strings. - checkValidZeroTerminatedUtf8to16("Hell", intArrayOf('H'.toInt(), 'e'.toInt(), 'l'.toInt(), 'l'.toInt(), 0, 'o'.toInt())) - checkValidZeroTerminatedUtf8to16("При", intArrayOf(-48, -97, -47, -128, -48, -72, 0, -48, -78, 0, -48, -75, -47, -126)) - checkValidZeroTerminatedUtf8to16("\uD800\uDC00", intArrayOf(-16, -112, -128, -128, 0, -16, -112, -128, -128)) - checkValidZeroTerminatedUtf8to16("", intArrayOf(0, 'H'.toInt())) - - // Test manual conversion with replacement. - // Incorrect UTF-8 lead character. - checkZeroTerminatedUtf8to16Replacing("\uFFFD", intArrayOf(-1, 0, '1'.toInt())) - - // Incomplete codepoint. - checkZeroTerminatedUtf8to16Replacing("\uFFFD", intArrayOf(-16, -97, -104, 0, '1'.toInt())) - checkZeroTerminatedUtf8to16Replacing("\uFFFD1", intArrayOf(-16, -97, -104, '1'.toInt(), 0, -16, -97, -104)) - - // Test manual conversion with exception throwing - // Incorrect UTF-8 lead character -> throw. - checkZeroTerminatedUtf8to16Throws("\uFFFD", intArrayOf(-1, 0, '1'.toInt())) - - // Incomplete codepoint -> throw. - checkZeroTerminatedUtf8to16Throws("\uFFFD", intArrayOf(-16, -97, -104, 0, '1'.toInt())) - checkZeroTerminatedUtf8to16Throws("\uFFFD1", intArrayOf(-16, -97, -104, '1'.toInt(), 0, -16, -97, -104)) -} - -fun testZeroTerminated8To16CustomBorders() { - val array = intArrayOf('a'.toInt(), 'a'.toInt(), 'a'.toInt(), 0, 'b'.toInt(), 'b'.toInt(), 'b'.toInt(), 0) - checkValidZeroTerminatedUtf8to16("aaa", array, 0, 5) - checkValidZeroTerminatedUtf8to16("a", array, 2, 2) - checkValidZeroTerminatedUtf8to16("", array, 3, 2) - checkValidZeroTerminatedUtf8to16("bb", array, 4, 2) - checkValidZeroTerminatedUtf8to16("bbb", array, 4, 3) - checkValidZeroTerminatedUtf8to16("bbb", array, 4, 4) - checkValidZeroTerminatedUtf8to16("bb", array, 5, 3) - checkValidZeroTerminatedUtf8to16("b", array, 6, 2) - checkValidZeroTerminatedUtf8to16("", array, 7, 1) - checkValidZeroTerminatedUtf8to16("", array, 8, 0) - - val byteArray = ByteArray(array.size) { array[it].toByte() } - checkOutOfBoundsZeroTerminatedUtf8to16(IndexOutOfBoundsException::class, "aaa0bbb0", byteArray, -1, 4) - checkOutOfBoundsZeroTerminatedUtf8to16(IndexOutOfBoundsException::class, "aaa0bbb0", byteArray, 8, 10) - checkOutOfBoundsZeroTerminatedUtf8to16(IndexOutOfBoundsException::class, "aaa0bbb0", byteArray, 2, 10) - checkOutOfBoundsZeroTerminatedUtf8to16(IndexOutOfBoundsException::class, "aaa0bbb0", byteArray, 10, 0) - checkOutOfBoundsZeroTerminatedUtf8to16(IllegalArgumentException::class, "aaa0bbb0", byteArray, 3, -2) -} - -// ------------------------------------ Run tests ------------------------------------ -@Test fun runTest() { - test16to8() - test16to8CustomBorders() - test8to16() - test8to16CustomBorders() - testZeroTerminated8To16() - testZeroTerminated8To16CustomBorders() - testPrint() -} diff --git a/kotlin-native/runtime/test/text/CharNativeTest.kt b/kotlin-native/runtime/test/text/CharNativeTest.kt index 7c83bb45077..e698c2caaa4 100644 --- a/kotlin-native/runtime/test/text/CharNativeTest.kt +++ b/kotlin-native/runtime/test/text/CharNativeTest.kt @@ -63,10 +63,59 @@ class CharNativeTest { assertEquals("\u0069\u0307", '\u0130'.lowercase()) } + @Test fun titlecase() { // titlecase = titlecaseChar = char != uppercaseChar assertEquals('\u10F0'.titlecaseChar().toString(), '\u10F0'.titlecase()) assertEquals('\u10F0', '\u10F0'.titlecaseChar()) assertNotEquals('\u10F0', '\u10F0'.uppercaseChar()) } + + @Test + fun testIsSupplementaryCodePoint() { + assertFalse(Char.isSupplementaryCodePoint(-1)) + for (c in 0..0xFFFF) { + assertFalse(Char.isSupplementaryCodePoint(c.toInt())) + } + for (c in 0xFFFF + 1..0x10FFFF) { + assertTrue(Char.isSupplementaryCodePoint(c)) + } + assertFalse(Char.isSupplementaryCodePoint(0x10FFFF + 1)) + } + + @Test + fun isSurrogatePair() { + assertFalse(Char.isSurrogatePair('\u0000', '\u0000')) + assertFalse(Char.isSurrogatePair('\u0000', '\uDC00')) + assertTrue( Char.isSurrogatePair('\uD800', '\uDC00')) + assertTrue( Char.isSurrogatePair('\uD800', '\uDFFF')) + assertTrue( Char.isSurrogatePair('\uDBFF', '\uDFFF')) + assertFalse(Char.isSurrogatePair('\uDBFF', '\uF000')) + } + + @Test + fun toChars() { + assertContentEquals(Char.toChars(0x010000), charArrayOf('\uD800', '\uDC00')) + assertContentEquals(Char.toChars(0x010001), charArrayOf('\uD800', '\uDC01')) + assertContentEquals(Char.toChars(0x010401), charArrayOf('\uD801', '\uDC01')) + assertContentEquals(Char.toChars(0x10FFFF), charArrayOf('\uDBFF', '\uDFFF')) + + assertFailsWith { Char.toChars(Int.MAX_VALUE) } + } + + @Test + fun toCodePoint() { + assertEquals(0x010000, Char.toCodePoint('\uD800', '\uDC00')) + assertEquals(0x010001, Char.toCodePoint('\uD800', '\uDC01')) + assertEquals(0x010401, Char.toCodePoint('\uD801', '\uDC01')) + assertEquals(0x10FFFF, Char.toCodePoint('\uDBFF', '\uDFFF')) + } + + @Test + fun category() { + assertTrue('<' in CharCategory.MATH_SYMBOL) + assertTrue(';' in CharCategory.OTHER_PUNCTUATION) + assertTrue('_' in CharCategory.CONNECTOR_PUNCTUATION) + assertTrue('$' in CharCategory.CURRENCY_SYMBOL) + } } diff --git a/kotlin-native/runtime/test/text/StringBuilderNativeTest.kt b/kotlin-native/runtime/test/text/StringBuilderNativeTest.kt new file mode 100644 index 00000000000..55aa33ab68b --- /dev/null +++ b/kotlin-native/runtime/test/text/StringBuilderNativeTest.kt @@ -0,0 +1,218 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package test.text + +import kotlin.test.* + +// Native-specific part of stdlib/test/text/StringBuilderTest.kt +class StringBuilderNativeTest { + @Test + fun insertCharSequence() { + StringBuilder("my insert CharSequence test").let { sb -> + sb.insert(0, null as CharSequence?) + assertEquals("nullmy insert CharSequence test", sb.toString()) + sb.insert(2, null as CharSequence?) + assertEquals("nunullllmy insert CharSequence test", sb.toString()) + sb.insert(sb.length, null as CharSequence?) + assertEquals("nunullllmy insert CharSequence testnull", sb.toString()) + sb.insert(2, "12" as CharSequence) + assertEquals("nu12nullllmy insert CharSequence testnull", sb.toString()) + sb.insert(sb.length, "12" as CharSequence) + assertEquals("nu12nullllmy insert CharSequence testnull12", sb.toString()) + } + } + + @Test + fun insertString() { + StringBuilder("my insert String test").let { sb -> + sb.insertRange(0, "1234", 0, 0) + assertEquals("my insert String test", sb.toString()) + sb.insertRange(0, "1234", 0, 1) + assertEquals("1my insert String test", sb.toString()) + sb.insertRange(0, "1234", 1, 3) + assertEquals("231my insert String test", sb.toString()) + sb.insertRange(2, "1234", 0, 0) + assertEquals("231my insert String test", sb.toString()) + sb.insertRange(2, "1234", 0, 1) + assertEquals("2311my insert String test", sb.toString()) + sb.insertRange(2, "1234", 1, 3) + assertEquals("232311my insert String test", sb.toString()) + sb.insertRange(sb.length, "1234", 0, 0) + assertEquals("232311my insert String test", sb.toString()) + sb.insertRange(sb.length, "1234", 0, 1) + assertEquals("232311my insert String test1", sb.toString()) + sb.insertRange(sb.length, "1234", 1, 3) + assertEquals("232311my insert String test123", sb.toString()) + + assertFailsWith { sb.insert(-1, "_") } + assertFailsWith { sb.insert(sb.length + 1, "_") } + assertFails { sb.insertRange(0, "null", -1, 0) } + assertFails { sb.insertRange(0, "null", 0, 5) } + assertFails { sb.insertRange(0, "null", 2, 1) } + } + } + + @Test + fun insertByte() { + StringBuilder().let { sb -> + sb.insert(0, 42.toByte()) + assertEquals("42", sb.toString()) + sb.insert(0, 13.toByte()) + assertEquals("1342", sb.toString()) + sb.insert(3, -1.toByte()) + assertEquals("134-12", sb.toString()) + + assertFailsWith { sb.insert(-1, 42.toByte()) } + assertFailsWith { sb.insert(sb.length + 1, 42.toByte()) } + } + } + + @Test + fun insertShort() { + StringBuilder().let { sb -> + sb.insert(0, 42.toShort()) + assertEquals("42", sb.toString()) + sb.insert(0, 13.toShort()) + assertEquals("1342", sb.toString()) + sb.insert(3, -1.toShort()) + assertEquals("134-12", sb.toString()) + + assertFailsWith { sb.insert(-1, 42.toShort()) } + assertFailsWith { sb.insert(sb.length + 1, 42.toShort()) } + } + } + + @Test + fun insertInt() { + StringBuilder().let { sb -> + sb.insert(0, 42.toInt()) + assertEquals("42", sb.toString()) + sb.insert(0, 13.toInt()) + assertEquals("1342", sb.toString()) + sb.insert(3, -1.toInt()) + assertEquals("134-12", sb.toString()) + + assertFailsWith { sb.insert(-1, 42.toInt()) } + assertFailsWith { sb.insert(sb.length + 1, 42.toInt()) } + } + } + + @Test + fun insertLong() { + StringBuilder().let { sb -> + sb.insert(0, 42.toLong()) + assertEquals("42", sb.toString()) + sb.insert(0, 13.toLong()) + assertEquals("1342", sb.toString()) + sb.insert(3, -1.toLong()) + assertEquals("134-12", sb.toString()) + + assertFailsWith { sb.insert(-1, 42.toLong()) } + assertFailsWith { sb.insert(sb.length + 1, 42.toLong()) } + } + } + + @Test + fun insertFloat() { + StringBuilder().let { sb -> + sb.insert(0, 42.2.toFloat()) + assertEquals("42.2", sb.toString()) + sb.insert(0, 13.1.toFloat()) + assertEquals("13.142.2", sb.toString()) + sb.insert(3, -1.toFloat()) + assertEquals("13.-1.0142.2", sb.toString()) + + assertFailsWith { sb.insert(-1, 42.2.toFloat()) } + assertFailsWith { sb.insert(sb.length + 1, 42.2.toFloat()) } + } + } + + @Test + fun insertDouble() { + StringBuilder().let { sb -> + sb.insert(0, 42.2.toDouble()) + assertEquals("42.2", sb.toString()) + sb.insert(0, 13.1.toDouble()) + assertEquals("13.142.2", sb.toString()) + sb.insert(3, -1.toDouble()) + assertEquals("13.-1.0142.2", sb.toString()) + + assertFailsWith { sb.insert(-1, 42.2.toDouble()) } + assertFailsWith { sb.insert(sb.length + 1, 42.2.toDouble()) } + } + } + + @Test + fun testReverse() { + val sb = StringBuilder("123456") + assertTrue(sb === sb.reverse()) + assertEquals("654321", sb.toString()) + + sb.setLength(1) + assertEquals("6", sb.toString()) + + sb.setLength(0) + assertEquals("", sb.toString()) + } + + @Test + fun testDoubleReverse() { + fun assertReversed(original: String, reversed: String, reversedBack: String = original) { + assertEquals(reversed, StringBuilder(original).reverse().toString()) + assertEquals(reversedBack, StringBuilder(reversed).reverse().toString()) + } + + assertReversed("a", "a") + assertReversed("ab", "ba") + assertReversed("abcdef", "fedcba") + assertReversed("abcdefg", "gfedcba") + assertReversed("\ud800\udc00", "\ud800\udc00") + assertReversed("\udc00\ud800", "\ud800\udc00", "\ud800\udc00") + assertReversed("a\ud800\udc00", "\ud800\udc00a") + assertReversed("ab\ud800\udc00", "\ud800\udc00ba") + assertReversed("abc\ud800\udc00", "\ud800\udc00cba") + assertReversed("\ud800\udc00\udc01\ud801\ud802\udc02", "\ud802\udc02\ud801\udc01\ud800\udc00", "\ud800\udc00\ud801\udc01\ud802\udc02") + assertReversed("\ud800\udc00\ud801\udc01\ud802\udc02", "\ud802\udc02\ud801\udc01\ud800\udc00") + assertReversed("\ud800\udc00\udc01\ud801a", "a\ud801\udc01\ud800\udc00", "\ud800\udc00\ud801\udc01a") + assertReversed("a\ud800\udc00\ud801\udc01", "\ud801\udc01\ud800\udc00a") + assertReversed("\ud800\udc00\udc01\ud801ab", "ba\ud801\udc01\ud800\udc00", "\ud800\udc00\ud801\udc01ab") + assertReversed("ab\ud800\udc00\ud801\udc01", "\ud801\udc01\ud800\udc00ba") + assertReversed("\ud800\udc00\ud801\udc01", "\ud801\udc01\ud800\udc00") + assertReversed("a\ud800\udc00z\ud801\udc01", "\ud801\udc01z\ud800\udc00a") + assertReversed("a\ud800\udc00bz\ud801\udc01", "\ud801\udc01zb\ud800\udc00a") + assertReversed("abc\ud802\udc02\ud801\udc01\ud800\udc00", "\ud800\udc00\ud801\udc01\ud802\udc02cba") + assertReversed("abcd\ud802\udc02\ud801\udc01\ud800\udc00", "\ud800\udc00\ud801\udc01\ud802\udc02dcba") + } + + @Test + fun appendLong() { + val times = 100 + val expected = (12345678L until (12345678L + times)).fold("") { res, idx -> res + idx } + + val sb = StringBuilder() + repeat(times) { sb.append(12345678L + it) } + assertEquals(expected, sb.toString()) + + val cornerCase = listOf(0L, -1L, Long.MIN_VALUE, Long.MAX_VALUE) + val expectedCornerCase = cornerCase.fold("") { res, e -> res + e } + for (v in cornerCase) sb.append(v) + assertEquals(expected + expectedCornerCase, sb.toString()) + } + + @Test + fun appendNullCharSequence() { + val sb = StringBuilder() + sb.append(null as CharSequence?) + assertEquals("null", sb.toString()) + } + + @Test + fun appendLine() { + val sb = StringBuilder() + sb.appendLine("abc").appendLine(42).appendLine(0.1) + assertEquals("abc\n42\n0.1\n", sb.toString()) + } +} \ No newline at end of file diff --git a/kotlin-native/runtime/test/text/StringEncodingTestNative.kt b/kotlin-native/runtime/test/text/StringEncodingTestNative.kt index d8040d73881..be566656186 100644 --- a/kotlin-native/runtime/test/text/StringEncodingTestNative.kt +++ b/kotlin-native/runtime/test/text/StringEncodingTestNative.kt @@ -6,7 +6,216 @@ package test.text +import kotlin.reflect.KClass +import kotlin.test.* +import kotlinx.cinterop.toKString +import test.assertArrayContentEquals internal actual val surrogateCodePointDecoding: String = "\uFFFD".repeat(3) internal actual val surrogateCharEncoding: ByteArray = byteArrayOf(0xEF.toByte(), 0xBF.toByte(), 0xBD.toByte()) + +// Native-specific part of stdlib/test/text/StringEncodingTest.kt +class StringEncodingTestNative { + private fun bytes(vararg elements: Int) = ByteArray(elements.size) { + val v = elements[it] + require(v in Byte.MIN_VALUE..Byte.MAX_VALUE) + v.toByte() + } + + private fun testEncoding(isWellFormed: Boolean, expected: ByteArray, string: String) { + assertArrayContentEquals(expected, string.encodeToByteArray()) + if (!isWellFormed) { + assertFailsWith { string.encodeToByteArray(throwOnInvalidSequence = true) } + } else { + assertArrayContentEquals(expected, string.encodeToByteArray(throwOnInvalidSequence = true)) + assertEquals(string, string.encodeToByteArray(throwOnInvalidSequence = true).decodeToString()) + } + } + + private fun testEncoding(isWellFormed: Boolean, expected: ByteArray, string: String, startIndex: Int, endIndex: Int) { + assertArrayContentEquals(expected, string.encodeToByteArray(startIndex, endIndex)) + if (!isWellFormed) { + assertFailsWith { string.encodeToByteArray(startIndex, endIndex, true) } + } else { + assertArrayContentEquals(expected, string.encodeToByteArray(startIndex, endIndex, true)) + string.encodeToByteArray(startIndex, endIndex, true).decodeToString() + } + } + + @Test + fun encodeToByteArray() { + // Valid strings. + testEncoding(true, bytes('H'.toInt(), 'e'.toInt(), 'l'.toInt(), 'l'.toInt(), 'o'.toInt()), "Hello") + testEncoding(true, bytes(-48, -97, -47, -128, -48, -72, -48, -78, -48, -75, -47, -126), "Привет") + + // Different kinds of input + testEncoding(false, bytes(-16, -112, -128, -128, '1'.toInt(), -17, -65, -67, -17, -65, -67), "\uD800\uDC001\uDC00\uD800") + // Lone surrogate + testEncoding(false, bytes(-17, -65, -67, '1'.toInt(), '2'.toInt()), "\uD80012", ) + testEncoding(false, bytes(-17, -65, -67, '1'.toInt(), '2'.toInt()), "\uDC0012") + testEncoding(false, bytes('1'.toInt(), '2'.toInt(), -17, -65, -67), "12\uD800") + } + + @Test + fun encodeToByteArraySlice() { + // Valid strings. + testEncoding(true, bytes(-16, -112, -128, -128, -16, -112, -128, -128), "\uD800\uDC00\uD800\uDC00\uD800\uDC00\uD800\uDC00", 0, 4) + testEncoding(true, bytes(-16, -112, -128, -128, -16, -112, -128, -128), "\uD800\uDC00\uD800\uDC00\uD800\uDC00\uD800\uDC00", 2, 6) + testEncoding(true, bytes(-16, -112, -128, -128, -16, -112, -128, -128), "\uD800\uDC00\uD800\uDC00\uD800\uDC00\uD800\uDC00", 4, 8) + + // Illegal surrogate pair + testEncoding(false, bytes(-17, -65, -67, -17, -65, -67, '1'.toInt()), "\uDC00\uD80012", 0, 3) + testEncoding(false, bytes(-17, -65, -67, -17, -65, -67, '2'.toInt()), "1\uDC00\uD8002", 1, 4) + testEncoding(false, bytes('2'.toInt(), -17, -65, -67, -17, -65, -67), "12\uDC00\uD800", 1, 4) + // Lone surrogate + testEncoding(false, bytes('1'.toInt(), -17, -65, -67), "1\uD800\uDC002", 0, 2) + testEncoding(false, bytes(-17, -65, -67, '2'.toInt()), "1\uD800\uDC002", 2, 4) + } + + private fun testDecoding(isWellFormed: Boolean, expected: String, bytes: ByteArray) { + assertEquals(expected, bytes.decodeToString()) + if (!isWellFormed) { + assertFailsWith { bytes.decodeToString(throwOnInvalidSequence = true) } + } else { + assertEquals(expected, bytes.decodeToString(throwOnInvalidSequence = true)) + assertArrayContentEquals(bytes, bytes.decodeToString(throwOnInvalidSequence = true).encodeToByteArray()) + } + } + + private fun testDecoding(isWellFormed: Boolean, expected: String, bytes: ByteArray, startIndex: Int, endIndex: Int) { + assertEquals(expected, bytes.decodeToString(startIndex, endIndex)) + if (!isWellFormed) { + assertFailsWith { bytes.decodeToString(startIndex, endIndex, true) } + } else { + assertEquals(expected, bytes.decodeToString(startIndex, endIndex, true)) + assertArrayContentEquals( + bytes.sliceArray(startIndex until endIndex), + bytes.decodeToString(startIndex, endIndex, true).encodeToByteArray() + ) + } + } + + @Test + fun decodeToString() { + // Valid strings. + testDecoding(true, "Hello", bytes('H'.toInt(), 'e'.toInt(), 'l'.toInt(), 'l'.toInt(), 'o'.toInt())) + testDecoding(true, "Привет", bytes(-48, -97, -47, -128, -48, -72, -48, -78, -48, -75, -47, -126)) + testDecoding(true, "\uD800\uDC00", bytes(-16, -112, -128, -128)) + + // Incorrect UTF-8 lead character. + testDecoding(false, "\uFFFD1", bytes(-1, '1'.toInt())) + + // Incomplete codepoint. + testDecoding(false, "\uFFFD1", bytes(-16, -97, -104, '1'.toInt())) + testDecoding(false, "\uFFFD1\uFFFD", bytes(-16, -97, -104, '1'.toInt(), -16, -97, -104)) + } + + @Test + fun decodeToStringSlice() { + // Valid strings. + testDecoding(true, "\uD800\uDC00\uD800\uDC00", bytes(-16, -112, -128, -128, -16, -112, -128, -128, -16, -112, -128, -128, -16, -112, -128, -128), 0, 8) + testDecoding(true, "\uD800\uDC00\uD800\uDC00", bytes(-16, -112, -128, -128, -16, -112, -128, -128, -16, -112, -128, -128, -16, -112, -128, -128), 4, 12) + testDecoding(true, "\uD800\uDC00\uD800\uDC00", bytes(-16, -112, -128, -128, -16, -112, -128, -128, -16, -112, -128, -128, -16, -112, -128, -128), 8, 16) + + // Incorrect UTF-8 lead character. + testDecoding(false, "\uFFFD1", bytes(-1, '1'.toInt(), '2'.toInt()), 0, 2) + testDecoding(false, "\uFFFD2", bytes('1'.toInt(), -1, '2'.toInt(), '3'.toInt()), 1, 3) + testDecoding(false, "2\uFFFD", bytes('1'.toInt(), '2'.toInt(), -1), 1, 3) + + // Incomplete codepoint. + testDecoding(false, "\uFFFD1", bytes(-16, -97, -104, '1'.toInt(), '2'.toInt()), 0, 4) + testDecoding(false, "\uFFFD2", bytes('1'.toInt(), -16, -97, -104, '2'.toInt(), '3'.toInt()), 1, 5) + testDecoding(false, "2\uFFFD", bytes('1'.toInt(), '2'.toInt(), -16, -97, -104), 1, 5) + } + + private fun testToKString(isWellFormed: Boolean, expected: String, bytes: ByteArray) { + assertEquals(expected, bytes.toKString()) + assertEquals(expected, bytes.copyOf(bytes.size + 1).toKString()) + if (!isWellFormed) { + assertFailsWith { bytes.toKString(throwOnInvalidSequence = true) } + assertFailsWith { bytes.copyOf(bytes.size + 1).toKString(throwOnInvalidSequence = true) } + } else { + assertEquals(expected, bytes.toKString(throwOnInvalidSequence = true)) + assertEquals(expected, bytes.copyOf(bytes.size + 1).toKString(throwOnInvalidSequence = true)) + } + } + + private fun testToKString(isWellFormed: Boolean, expected: String, bytes: ByteArray, startIndex: Int, endIndex: Int) { + assertEquals(expected, bytes.toKString(startIndex, endIndex)) + assertEquals(expected, bytes.copyOf(bytes.size + 1).toKString(startIndex, endIndex)) + if (!isWellFormed) { + assertFailsWith { bytes.toKString(startIndex, endIndex, true) } + assertFailsWith { bytes.copyOf(bytes.size + 1).toKString(startIndex, endIndex, true) } + } else { + assertEquals(expected, bytes.toKString(startIndex, endIndex, true)) + assertEquals(expected, bytes.copyOf(bytes.size + 1).toKString(startIndex, endIndex, true)) + } + } + + @Test + fun toKString() { + // Valid strings. + testToKString(true, "Hello", bytes('H'.toInt(), 'e'.toInt(), 'l'.toInt(), 'l'.toInt(), 'o'.toInt())) + testToKString(true, "Hell", bytes('H'.toInt(), 'e'.toInt(), 'l'.toInt(), 'l'.toInt(), 0, 'o'.toInt())) + testToKString(true, "Привет", bytes(-48, -97, -47, -128, -48, -72, -48, -78, -48, -75, -47, -126)) + testToKString(true, "При", bytes(-48, -97, -47, -128, -48, -72, 0, -48, -78, 0, -48, -75, -47, -126)) + testToKString(true, "\uD800\uDC00", bytes(-16, -112, -128, -128)) + testToKString(true, "\uD800\uDC00", bytes(-16, -112, -128, -128, 0, -16, -112, -128, -128)) + testToKString(true, "", bytes()) + testToKString(true, "", bytes(0, 'H'.toInt())) + + // Incorrect UTF-8 lead character. + testToKString(false, "\uFFFD1", bytes(-1, '1'.toInt())) + testToKString(false, "\uFFFD", bytes(-1, 0, '1'.toInt())) + + // Incomplete codepoint. + testToKString(false, "\uFFFD1", bytes(-16, -97, -104, '1'.toInt())) + testToKString(false, "\uFFFD", bytes(-16, -97, -104, 0, '1'.toInt())) + testToKString(false, "\uFFFD1\uFFFD", bytes(-16, -97, -104, '1'.toInt(), -16, -97, -104)) + testToKString(false, "\uFFFD1", bytes(-16, -97, -104, '1'.toInt(), 0, -16, -97, -104)) + } + + @Test + fun toKStringSlice() { + assertFailsWith { bytes('H'.toInt(), 'e'.toInt(), 'l'.toInt(), 'l'.toInt(), 'o'.toInt()).toKString(-1, 3) } + assertFailsWith { bytes('H'.toInt(), 'e'.toInt(), 'l'.toInt(), 'l'.toInt(), 'o'.toInt()).toKString(5, 15) } + assertFailsWith { bytes('H'.toInt(), 'e'.toInt(), 'l'.toInt(), 'l'.toInt(), 'o'.toInt()).toKString(2, 12) } + assertFailsWith { bytes('H'.toInt(), 'e'.toInt(), 'l'.toInt(), 'l'.toInt(), 'o'.toInt()).toKString(10, 10) } + assertFailsWith { bytes('H'.toInt(), 'e'.toInt(), 'l'.toInt(), 'l'.toInt(), 'o'.toInt()).toKString(3, 1) } + assertFailsWith { bytes('a'.toInt(), 'a'.toInt(), 'a'.toInt(), 0, 'b'.toInt(), 'b'.toInt(), 'b'.toInt(), 0).toKString(-1, 3) } + assertFailsWith { bytes('a'.toInt(), 'a'.toInt(), 'a'.toInt(), 0, 'b'.toInt(), 'b'.toInt(), 'b'.toInt(), 0).toKString(8, 18) } + assertFailsWith { bytes('a'.toInt(), 'a'.toInt(), 'a'.toInt(), 0, 'b'.toInt(), 'b'.toInt(), 'b'.toInt(), 0).toKString(2, 12) } + assertFailsWith { bytes('a'.toInt(), 'a'.toInt(), 'a'.toInt(), 0, 'b'.toInt(), 'b'.toInt(), 'b'.toInt(), 0).toKString(10, 10) } + assertFailsWith { bytes('a'.toInt(), 'a'.toInt(), 'a'.toInt(), 0, 'b'.toInt(), 'b'.toInt(), 'b'.toInt(), 0).toKString(3, 1) } + + // Valid strings. + testToKString(true, "He", bytes('H'.toInt(), 'e'.toInt(), 'l'.toInt(), 'l'.toInt(), 'o'.toInt()), 0, 2) + testToKString(true, "ll", bytes('H'.toInt(), 'e'.toInt(), 'l'.toInt(), 'l'.toInt(), 'o'.toInt()), 2, 4) + testToKString(true, "lo", bytes('H'.toInt(), 'e'.toInt(), 'l'.toInt(), 'l'.toInt(), 'o'.toInt()), 3, 5) + testToKString(true, "", bytes('H'.toInt(), 'e'.toInt(), 'l'.toInt(), 'l'.toInt(), 'o'.toInt()), 0, 0) + testToKString(true, "\uD800\uDC00\uD800\uDC00", bytes(-16, -112, -128, -128, -16, -112, -128, -128, -16, -112, -128, -128, -16, -112, -128, -128), 0, 8) + testToKString(true, "\uD800\uDC00\uD800\uDC00", bytes(-16, -112, -128, -128, -16, -112, -128, -128, -16, -112, -128, -128, -16, -112, -128, -128), 4, 12) + testToKString(true, "\uD800\uDC00\uD800\uDC00", bytes(-16, -112, -128, -128, -16, -112, -128, -128, -16, -112, -128, -128, -16, -112, -128, -128), 8, 16) + testToKString(true, "aaa", bytes('a'.toInt(), 'a'.toInt(), 'a'.toInt(), 0, 'b'.toInt(), 'b'.toInt(), 'b'.toInt(), 0), 0, 5) + testToKString(true, "a", bytes('a'.toInt(), 'a'.toInt(), 'a'.toInt(), 0, 'b'.toInt(), 'b'.toInt(), 'b'.toInt(), 0), 2, 4) + testToKString(true, "", bytes('a'.toInt(), 'a'.toInt(), 'a'.toInt(), 0, 'b'.toInt(), 'b'.toInt(), 'b'.toInt(), 0), 3, 5) + testToKString(true, "bb", bytes('a'.toInt(), 'a'.toInt(), 'a'.toInt(), 0, 'b'.toInt(), 'b'.toInt(), 'b'.toInt(), 0), 4, 6) + testToKString(true, "bbb", bytes('a'.toInt(), 'a'.toInt(), 'a'.toInt(), 0, 'b'.toInt(), 'b'.toInt(), 'b'.toInt(), 0), 4, 7) + testToKString(true, "bbb", bytes('a'.toInt(), 'a'.toInt(), 'a'.toInt(), 0, 'b'.toInt(), 'b'.toInt(), 'b'.toInt(), 0), 4, 8) + testToKString(true, "bb", bytes('a'.toInt(), 'a'.toInt(), 'a'.toInt(), 0, 'b'.toInt(), 'b'.toInt(), 'b'.toInt(), 0), 5, 8) + testToKString(true, "b", bytes('a'.toInt(), 'a'.toInt(), 'a'.toInt(), 0, 'b'.toInt(), 'b'.toInt(), 'b'.toInt(), 0), 6, 8) + testToKString(true, "", bytes('a'.toInt(), 'a'.toInt(), 'a'.toInt(), 0, 'b'.toInt(), 'b'.toInt(), 'b'.toInt(), 0), 7, 8) + testToKString(true, "", bytes('a'.toInt(), 'a'.toInt(), 'a'.toInt(), 0, 'b'.toInt(), 'b'.toInt(), 'b'.toInt(), 0), 8, 8) + + // Incorrect UTF-8 lead character. + testToKString(false, "\uFFFD1", bytes(-1, '1'.toInt(), '2'.toInt()), 0, 2) + testToKString(false, "\uFFFD2", bytes('1'.toInt(), -1, '2'.toInt(), '3'.toInt()), 1, 3) + testToKString(false, "2\uFFFD", bytes('1'.toInt(), '2'.toInt(), -1), 1, 3) + + // Incomplete codepoint. + testToKString(false, "\uFFFD1", bytes(-16, -97, -104, '1'.toInt(), '2'.toInt()), 0, 4) + testToKString(false, "\uFFFD2", bytes('1'.toInt(), -16, -97, -104, '2'.toInt(), '3'.toInt()), 1, 5) + testToKString(false, "2\uFFFD", bytes('1'.toInt(), '2'.toInt(), -16, -97, -104), 1, 5) + } +} \ No newline at end of file diff --git a/kotlin-native/runtime/test/text/StringNativeTest.kt b/kotlin-native/runtime/test/text/StringNativeTest.kt index 8115f7be039..9abcb9ba947 100644 --- a/kotlin-native/runtime/test/text/StringNativeTest.kt +++ b/kotlin-native/runtime/test/text/StringNativeTest.kt @@ -16,6 +16,8 @@ class StringNativeTest { // Surrogate pairs assertEquals("\uD801\uDC4F\uD806\uDCD0\uD81B\uDE7F", "\uD801\uDC27\uD806\uDCB0\uD81B\uDE5F".lowercase()) + assertEquals("\ud801\udc28", "\ud801\udc28".lowercase()) + assertEquals("\ud801\udc28", "\ud801\udc00".lowercase()) // Special Casing // LATIN CAPITAL LETTER I WITH DOT ABOVE @@ -102,5 +104,51 @@ class StringNativeTest { fun uppercase() { // Non-ASCII assertEquals("\u00DE\u03A9\u0403\uA779", "\u00FE\u03A9\u0453\uA77A".uppercase()) + + // Surrogate pairs + assertEquals("\ud801\udc00", "\ud801\udc28".uppercase()) + assertEquals("\ud801\udc00", "\ud801\udc00".uppercase()) + } + + @Test + fun capitalize() { + // Non-ASCII + assertEquals("\u00DE\u03A9\u0453\uA77A", "\u00FE\u03A9\u0453\uA77A".capitalize()) + } + + @Test fun indexOfString() { + assertEquals(1, "bceded".indexOf("ced", -1)) + + assertEquals(-1, "bceded".indexOf("e", 7)) + assertEquals(-1, "bceded".indexOf("e", Int.MAX_VALUE)) + assertEquals(6, "bceded".indexOf("", Int.MAX_VALUE)) + + assertEquals(-1, "".indexOf("a", -3)) + assertEquals(0, "".indexOf("", 0)) + } + + @Test + fun indexOfChar() { + assertEquals(-1, "bcedef".indexOf('e', 5)) + + assertEquals(-1, "".indexOf('a', -3)) + assertEquals(-1, "".indexOf('a', 10)) + + assertEquals(-1, "".indexOf(0.toChar(), -3)) + assertEquals(-1, "".indexOf(0.toChar(), 10)) + } + + @Test + fun equalsIgnoreCase() { + assertTrue("hello".equals("HElLo", true)) + assertTrue("Привет".equals("прИВет", true)) + } + + @Test + fun trim() { + assertEquals(expected = "String", actual = "\u0020 \u202FString\u2028\u2029".trim(), + message = "Trim special whitespaces") + assertEquals(expected = "\u1FFFString", actual = "\u00A0 \u1FFFString".trim(), + message = "Trim special whitespace but should left a unicode symbol") } } diff --git a/kotlin-native/runtime/test/text/StringNumberConversionNativeTest.kt b/kotlin-native/runtime/test/text/StringNumberConversionNativeTest.kt new file mode 100644 index 00000000000..8c06855f6c9 --- /dev/null +++ b/kotlin-native/runtime/test/text/StringNumberConversionNativeTest.kt @@ -0,0 +1,130 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package test.text + +import kotlin.test.* + +// Native-specific part of stdlib/test/text/StringNumberConversionTest.kt +class StringNumberConversionNativeTest { + @Test + fun toDouble() { + assertEquals(0.5, "0.5".toDouble()) + assertEquals(-5000000000.0, "-00000000000000000000.5e10".toDouble()) + assertEquals(-0.005, "-00000000000000000000.5e-2".toDouble()) + assertEquals(50000000000.0, "+5e10".toDouble()) + assertEquals(50000000000.0, " +5e10 ".toDouble()) + assertEquals(520.0, "+5.2e2d".toDouble()) + assertEquals(0.052, "+5.2e-2d".toDouble()) + assertEquals(52340000000.0, "+5.234e+10d".toDouble()) + assertEquals(5.234E123, "+5.234e+123d".toDouble()) + assertEquals(5.234E123, "+5.234e+123f".toDouble()) + assertEquals(5.234E123, "+5.234e+123".toDouble()) + assertEquals(5.5, "5.5f".toDouble()) + assertEquals(2.71, "\u0009 \u000A 2.71 \u000D".toDouble()) + assertEquals(42.3, "\n 42.3 ".toDouble()) + + assertFailsWith { + "+-5.0".toDouble() + } + assertFailsWith { + "d".toDouble() + } + assertFailsWith { + "5.5.3e123d".toDouble() + } + + // regression of incorrect processing of long lines - such values returned Infinity + assertFailsWith { + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".toDouble() + } + assertFailsWith { + "+-my free text with different letters $3213#. e ".toDouble() + } + assertFailsWith { + "eeeeeEEEEEeeeeeee".toDouble() + } + assertFailsWith { + "InfinityN".toDouble() + } + assertFailsWith { + "NaNPICEZy".toDouble() + } + + assertFailsWith { "\u20293.14".toDouble() } + assertFailsWith { "3.14\u200B".toDouble() } + assertFailsWith { "3.14\u200B ABC".toDouble() } + + // Illegal surrogate pair + assertFailsWith { "\uDC00\uD800".toDouble() } + // Different kinds of input (including illegal one) + assertFailsWith { "\uD800\uDC001\uDC00\uD800".toDouble() } + // Lone surrogate + assertFailsWith { "\uD80012".toDouble() } + assertFailsWith { "\uDC0012".toDouble() } + assertFailsWith { "12\uD800".toDouble() } + } + + @Test + fun toFloat() { + assertEquals(0.5f, "0.5".toFloat()) + assertEquals(-5000000000f, "-00000000000000000000.5e10f".toFloat()) + assertEquals(-0.005f, "-00000000000000000000.5e-2f".toFloat()) + assertEquals(50000000000f, "+5e10".toFloat()) + assertEquals(50000000000f, " +5e10 ".toFloat()) + assertEquals(0.052f, "+5.2e-2f".toFloat()) + assertEquals(520f, "+5.2e2f".toFloat()) + assertEquals(0.052f, "+5.2e-2f".toFloat()) + assertEquals(52340000000f, "+5.234e+10f".toFloat()) + assertEquals(Float.POSITIVE_INFINITY, "+5.234e+123f".toFloat()) + assertEquals(7.15f, "\u0019 7.15 ".toFloat()) + assertEquals(2.71f, "\u0009 \u000A 2.71 \u000D".toFloat()) + + assertFailsWith { + "+-5.0f".toFloat() + } + assertFailsWith { + "f".toFloat() + } + assertFailsWith { + "5.5.3e123f".toFloat() + } + + // regression of incorrect processing of long lines - such values returned Infinity + assertFailsWith { + // should be more than 38 symbols + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".toFloat() + } + assertFailsWith { + // should be more than 38 symbols + "this string is not a numb3r, am I right?????????????".toFloat() + } + assertFailsWith { + // should be more than 38 symbols + "+-my free text with different letters $3213#. e ".toFloat() + } + assertFailsWith { + // should be more than 38 symbols + "eeeeeEEEEEeeeeeee".toFloat() + } + assertFailsWith { + "InfinityN".toFloat() + } + assertFailsWith { + "NaNPICEZy".toFloat() + } + + assertFailsWith { "\u202F3.14".toFloat() } + + // Illegal surrogate pair + assertFailsWith { "\uDC00\uD800".toFloat() } + // Different kinds of input (including illegal one) + assertFailsWith { "\uD800\uDC001\uDC00\uD800".toFloat() } + // Lone surrogate + assertFailsWith { "\uD80012".toFloat() } + assertFailsWith { "\uDC0012".toFloat() } + assertFailsWith { "12\uD800".toFloat() } + } +} \ No newline at end of file