diff --git a/libraries/kotlin.test/common/src/main/kotlin/kotlin/test/Assertions.kt b/libraries/kotlin.test/common/src/main/kotlin/kotlin/test/Assertions.kt index de48e23dc97..529935a8e11 100644 --- a/libraries/kotlin.test/common/src/main/kotlin/kotlin/test/Assertions.kt +++ b/libraries/kotlin.test/common/src/main/kotlin/kotlin/test/Assertions.kt @@ -136,6 +136,195 @@ fun assertNull(actual: Any?, message: String? = null) { asserter.assertNull(message, actual) } +/** Asserts that the [iterable] contains the specified [element], with an optional [message]. */ +@SinceKotlin("1.5") +fun <@OnlyInputTypes T> assertContains(iterable: Iterable, element: T, message: String? = null) { + asserter.assertTrue( + { messagePrefix(message) + "Expected the collection to contain the element.\nCollection <$iterable>, element <$element>." }, + iterable.contains(element) + ) +} + +/** Asserts that the [sequence] contains the specified [element], with an optional [message]. */ +@SinceKotlin("1.5") +fun <@OnlyInputTypes T> assertContains(sequence: Sequence, element: T, message: String? = null) { + asserter.assertTrue( + { messagePrefix(message) + "Expected the sequence to contain the element.\nSequence <$sequence>, element <$element>." }, + sequence.contains(element) + ) +} + +/** Asserts that the [array] contains the specified [element], with an optional [message]. */ +@SinceKotlin("1.5") +fun <@OnlyInputTypes T> assertContains(array: Array, element: T, message: String? = null) { + assertArrayContains(array, element, message, Array::contains, Array::contentToString) +} + +/** Asserts that the [array] contains the specified [element], with an optional [message]. */ +@SinceKotlin("1.5") +fun assertContains(array: ByteArray, element: Byte, message: String? = null) { + assertArrayContains(array, element, message, ByteArray::contains, ByteArray::contentToString) +} + +/** Asserts that the [array] contains the specified [element], with an optional [message]. */ +@SinceKotlin("1.5") +fun assertContains(array: ShortArray, element: Short, message: String? = null) { + assertArrayContains(array, element, message, ShortArray::contains, ShortArray::contentToString) +} + +/** Asserts that the [array] contains the specified [element], with an optional [message]. */ +@SinceKotlin("1.5") +fun assertContains(array: IntArray, element: Int, message: String? = null) { + assertArrayContains(array, element, message, IntArray::contains, IntArray::contentToString) +} + +/** Asserts that the [array] contains the specified [element], with an optional [message]. */ +@SinceKotlin("1.5") +fun assertContains(array: LongArray, element: Long, message: String? = null) { + assertArrayContains(array, element, message, LongArray::contains, LongArray::contentToString) +} + +/** Asserts that the [array] contains the specified [element], with an optional [message]. */ +@SinceKotlin("1.5") +fun assertContains(array: BooleanArray, element: Boolean, message: String? = null) { + assertArrayContains(array, element, message, BooleanArray::contains, BooleanArray::contentToString) +} + +/** Asserts that the [array] contains the specified [element], with an optional [message]. */ +@SinceKotlin("1.5") +fun assertContains(array: CharArray, element: Char, message: String? = null) { + assertArrayContains(array, element, message, CharArray::contains, CharArray::contentToString) +} + +/** Asserts that the [array] contains the specified [element], with an optional [message]. */ +@SinceKotlin("1.5") +@OptIn(ExperimentalUnsignedTypes::class) +fun assertContains(array: UByteArray, element: UByte, message: String? = null) { + assertArrayContains(array, element, message, UByteArray::contains, UByteArray::contentToString) +} + +/** Asserts that the [array] contains the specified [element], with an optional [message]. */ +@SinceKotlin("1.5") +@OptIn(ExperimentalUnsignedTypes::class) +fun assertContains(array: UShortArray, element: UShort, message: String? = null) { + assertArrayContains(array, element, message, UShortArray::contains, UShortArray::contentToString) +} + +/** Asserts that the [array] contains the specified [element], with an optional [message]. */ +@SinceKotlin("1.5") +@OptIn(ExperimentalUnsignedTypes::class) +fun assertContains(array: UIntArray, element: UInt, message: String? = null) { + assertArrayContains(array, element, message, UIntArray::contains, UIntArray::contentToString) +} + +/** Asserts that the [array] contains the specified [element], with an optional [message]. */ +@SinceKotlin("1.5") +@OptIn(ExperimentalUnsignedTypes::class) +fun assertContains(array: ULongArray, element: ULong, message: String? = null) { + assertArrayContains(array, element, message, ULongArray::contains, ULongArray::contentToString) +} + +@kotlin.internal.InlineOnly +private inline fun <@OnlyInputTypes A, E> assertArrayContains( + array: A, + element: E, + message: String? = null, + contains: A.(E) -> Boolean, + crossinline contentToString: A.() -> String +) { + asserter.assertTrue( + { messagePrefix(message) + "Expected the array to contain the element.\nArray <${array.contentToString()}>, element <${element.toString()}>." }, // Explicitly call toString(): KT-45684 + array.contains(element) + ) +} + +/** Asserts that the [range] contains the specified [value], with an optional [message]. */ +@SinceKotlin("1.5") +fun assertContains(range: IntRange, value: Int, message: String? = null) { + assertRangeContains(range, value, message, IntRange::contains) +} + +/** Asserts that the [range] contains the specified [value], with an optional [message]. */ +@SinceKotlin("1.5") +fun assertContains(range: LongRange, value: Long, message: String? = null) { + assertRangeContains(range, value, message, LongRange::contains) +} + +/** Asserts that the [range] contains the specified [value], with an optional [message]. */ +@SinceKotlin("1.5") +fun > assertContains(range: ClosedRange, value: T, message: String? = null) { + assertRangeContains(range, value, message, ClosedRange::contains) +} + +/** Asserts that the [range] contains the specified [value], with an optional [message]. */ +@SinceKotlin("1.5") +fun assertContains(range: CharRange, value: Char, message: String? = null) { + assertRangeContains(range, value, message, CharRange::contains) +} + +/** Asserts that the [range] contains the specified [value], with an optional [message]. */ +@SinceKotlin("1.5") +@OptIn(ExperimentalUnsignedTypes::class) +fun assertContains(range: UIntRange, value: UInt, message: String? = null) { + assertRangeContains(range, value, message, UIntRange::contains) +} + +/** Asserts that the [range] contains the specified [value], with an optional [message]. */ +@SinceKotlin("1.5") +@OptIn(ExperimentalUnsignedTypes::class) +fun assertContains(range: ULongRange, value: ULong, message: String? = null) { + assertRangeContains(range, value, message, ULongRange::contains) +} + +@kotlin.internal.InlineOnly +private inline fun assertRangeContains(range: R, value: V, message: String? = null, contains: R.(V) -> Boolean) { + asserter.assertTrue( + { messagePrefix(message) + "Expected the range <$range> to contain the value <${value.toString()}>." }, // Explicitly call toString(): KT-45684 + range.contains(value) + ) +} + +/** Asserts that the [map] contains the specified [key], with an optional [message]. */ +@SinceKotlin("1.5") +fun <@OnlyInputTypes K, V> assertContains(map: Map, key: K, message: String? = null) { + asserter.assertTrue({ messagePrefix(message) + "Expected the map to contain the key.\nMap <$map>, key <$key>." }, map.containsKey(key)) +} + +/** + * Asserts that the [charSequence] contains the specified [char], with an optional [message]. + * + * @param ignoreCase `true` to ignore character case when comparing characters. By default `false`. + */ +@SinceKotlin("1.5") +fun assertContains(charSequence: CharSequence, char: Char, ignoreCase: Boolean = false, message: String? = null) { + asserter.assertTrue( + { messagePrefix(message) + "Expected the char sequence to contain the char.\nCharSequence <$charSequence>, char <$char>, ignoreCase <$ignoreCase>." }, + charSequence.contains(char, ignoreCase) + ) +} + +/** + * Asserts that the [charSequence] contains the specified [other] char sequence as a substring, with an optional [message]. + * + * @param ignoreCase `true` to ignore character case when comparing strings. By default `false`. + */ +@SinceKotlin("1.5") +fun assertContains(charSequence: CharSequence, other: CharSequence, ignoreCase: Boolean = false, message: String? = null) { + asserter.assertTrue( + { messagePrefix(message) + "Expected the char sequence to contain the substring.\nCharSequence <$charSequence>, substring <$other>, ignoreCase <$ignoreCase>." }, + charSequence.contains(other, ignoreCase) + ) +} + +/** Asserts that the [charSequence] contains at least one match of the specified regular expression [regex], with an optional [message]. */ +@SinceKotlin("1.5") +fun assertContains(charSequence: CharSequence, regex: Regex, message: String? = null) { + asserter.assertTrue( + { messagePrefix(message) + "Expected the char sequence to contain the regular expression.\nCharSequence <$charSequence>, regex <$regex>." }, + charSequence.contains(regex) + ) +} + /** * Asserts that the [expected] iterable is *structurally* equal to the [actual] iterable, * i.e. contains the same number of the same elements in the same order, with an optional [message]. diff --git a/libraries/kotlin.test/common/src/test/kotlin/kotlin/test/tests/AssertContainsTest.kt b/libraries/kotlin.test/common/src/test/kotlin/kotlin/test/tests/AssertContainsTest.kt new file mode 100644 index 00000000000..bf5e40ee08d --- /dev/null +++ b/libraries/kotlin.test/common/src/test/kotlin/kotlin/test/tests/AssertContainsTest.kt @@ -0,0 +1,155 @@ +/* + * Copyright 2010-2021 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 kotlin.test.tests + +import kotlin.test.* + +class AssertContainsTest { + + @Test + fun testAssertContainsIterable() { + val list = listOf(1, 2, 3) + + assertContains(list, 2) + + testFailureMessage("Expected the collection to contain the element.\nCollection <$list>, element .") { + assertContains(list as List, null) + } + testFailureMessage("Expected the collection to contain the element.\nCollection <$list>, element <5>.") { + assertContains(list, 5) + } + } + + @Test + fun testAssertContainsSequence() { + val sequence = generateSequence(1) { it + 1 }.take(3) + + assertContains(sequence, 2) + + testFailureMessage("Expected the sequence to contain the element.\nSequence <$sequence>, element .") { + assertContains(sequence as Sequence, null) + } + testFailureMessage("Expected the sequence to contain the element.\nSequence <$sequence>, element <5>.") { + assertContains(sequence, 5) + } + } + + @Test + fun testAssertContainsArray() { + val array = arrayOf("Kot", "lin", "test") + + assertContains(array, "test") + + testFailureMessage("Expected the array to contain the element.\nArray <${array.contentToString()}>, element .") { + @Suppress("UNCHECKED_CAST") + assertContains(array as Array, null) + } + testFailureMessage("Expected the array to contain the element.\nArray <${array.contentToString()}>, element <5>.") { + assertContains(array, "5") + } + } + + @Test + fun testAssertContainsCharArray() { + val array = charArrayOf('x', 'y', 'z') + + assertContains(array, 'z') + + testFailureMessage("Expected the array to contain the element.\nArray <${array.contentToString()}>, element <5>.") { + assertContains(array, '5') + } + } + + @OptIn(ExperimentalUnsignedTypes::class) + @Test + fun testAssertContainsUnsignedArray() { + val array = ulongArrayOf(0u, ULong.MAX_VALUE, 2u) + + assertContains(array, ULong.MAX_VALUE) + + testFailureMessage("Expected the array to contain the element.\nArray <${array.contentToString()}>, element <5>.") { + assertContains(array, 5u) + } + } + + @Test + fun testAssertContainsIntRange() { + val range = -5..5 + + assertContains(range, 0) + + testFailureMessage("Expected the range <-5..5> to contain the value <10>.") { + assertContains(range, 10) + } + } + + @Test + fun testAssertContainsCharRange() { + val range = 'a'..'y' + + assertContains(range, 'f') + + testFailureMessage("Expected the range to contain the value .") { + assertContains(range, 'A') + } + } + + @Test + fun testAssertContainsDoubleRange() { + val range = 0.5..0.55 + + assertContains(range, 0.52) + + val one = 1.0 + testFailureMessage("Expected the range <$range> to contain the value <$one>.") { + assertContains(range, one) + } + } + + @Test + fun testAssertContainsMap() { + val map = mapOf( + "apple" to "green", + "banana" to "yellow", + "orange" to "orange", + ) + + assertContains(map, "apple") + + testFailureMessage("Expected the map to contain the key.\nMap <$map>, key .") { + assertContains(map, "pineapple") + } + } + + @Test + fun testAssertContainsCharSequence() { + val string = "Pineapple" + + assertContains(string, 'e') + assertContains(string, 'N', ignoreCase = true) + assertContains(string, "app") + assertContains(string, "ApP", ignoreCase = true) + assertContains(string, Regex("[a-zA-Z]")) + + testFailureMessage("Expected the char sequence to contain the char.\nCharSequence <$string>, char , ignoreCase .") { + assertContains(string, 'N') + } + testFailureMessage("Expected the char sequence to contain the char.\nCharSequence <$string>, char , ignoreCase .") { + assertContains(string, 'x', ignoreCase = true) + } + testFailureMessage("Expected the char sequence to contain the substring.\nCharSequence <$string>, substring , ignoreCase .") { + assertContains(string, "ApP") + } + testFailureMessage("Expected the char sequence to contain the substring.\nCharSequence <$string>, substring , ignoreCase .") { + assertContains(string, "Appleseed", ignoreCase = true) + } + + val digit = Regex("[0-9]") + testFailureMessage("Expected the char sequence to contain the regular expression.\nCharSequence <$string>, regex <$digit>.") { + assertContains(string, digit) + } + } +} \ No newline at end of file diff --git a/libraries/kotlin.test/common/src/test/kotlin/kotlin/test/tests/AssertContentEqualsTest.kt b/libraries/kotlin.test/common/src/test/kotlin/kotlin/test/tests/AssertContentEqualsTest.kt index bd286f1812b..ac16cbb7607 100644 --- a/libraries/kotlin.test/common/src/test/kotlin/kotlin/test/tests/AssertContentEqualsTest.kt +++ b/libraries/kotlin.test/common/src/test/kotlin/kotlin/test/tests/AssertContentEqualsTest.kt @@ -9,11 +9,6 @@ import kotlin.test.* class AssertContentEqualsTest { - private fun testFailureMessage(expected: String, block: () -> Unit) { - val exception = checkFailedAssertion(block) - assertEquals(expected, exception.message, "Wrong assertion message") - } - @Test fun testAssertContentEqualsIterable() { val list: Iterable = listOf(1, 2, 3) @@ -159,4 +154,4 @@ class AssertContentEqualsTest { assertContentEquals(array1, ULongArray(4) { 4uL - it.toUInt() }) } } -} \ No newline at end of file +} diff --git a/libraries/kotlin.test/common/src/test/kotlin/kotlin/test/tests/BasicAssertionsTest.kt b/libraries/kotlin.test/common/src/test/kotlin/kotlin/test/tests/BasicAssertionsTest.kt index 0c2574f0caa..730aab377f9 100644 --- a/libraries/kotlin.test/common/src/test/kotlin/kotlin/test/tests/BasicAssertionsTest.kt +++ b/libraries/kotlin.test/common/src/test/kotlin/kotlin/test/tests/BasicAssertionsTest.kt @@ -244,6 +244,11 @@ class BasicAssertionsTest { } +internal fun testFailureMessage(expected: String, block: () -> Unit) { + val exception = checkFailedAssertion(block) + assertEquals(expected, exception.message, "Wrong assertion message") +} + internal fun checkFailedAssertion(assertion: () -> Unit): AssertionError { return assertFailsWith { withDefaultAsserter(assertion) } }