Introduce assertContentEquals in kotlin-test #KT-32996

This commit is contained in:
Abduqodiri Qurbonzoda
2021-03-11 07:33:03 +03:00
parent 44e6483090
commit 5f4a4fd8ae
6 changed files with 430 additions and 2 deletions

View File

@@ -17,7 +17,7 @@ configureSourcesJar()
configureJavadocJar()
tasks.withType(org.jetbrains.kotlin.gradle.dsl.KotlinCompile) {
kotlinOptions.freeCompilerArgs += "-Xallow-kotlin-package"
kotlinOptions.freeCompilerArgs += ["-Xallow-kotlin-package", "-Xopt-in=kotlin.RequiresOptIn"]
}
compileKotlinCommon {

View File

@@ -0,0 +1,110 @@
/*
* 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
import kotlin.contracts.contract
internal fun <T> assertIterableContentEquals(
typeName: String,
message: String?,
expected: T?,
actual: T?,
iterator: T.() -> Iterator<*>
) {
if (checkReferenceAndNullEquality(typeName, message, expected, actual, Any?::toString)) return
var index = 0
val expectedIt = expected.iterator()
val actualIt = actual.iterator()
while (expectedIt.hasNext() && actualIt.hasNext()) {
val expectedElement = expectedIt.next()
val actualElement = actualIt.next()
if (expectedElement != actualElement) {
fail(messagePrefix(message) + elementsDifferMessage(typeName, index, expectedElement, actualElement))
}
index++
}
if (expectedIt.hasNext()) {
check(!actualIt.hasNext())
fail(messagePrefix(message) + "$typeName lengths differ. Expected length is bigger than $index, actual length is $index.")
}
if (actualIt.hasNext()) {
check(!expectedIt.hasNext())
fail(messagePrefix(message) + "$typeName lengths differ. Expected length is $index, actual length is bigger than $index.")
}
}
internal fun <T> assertArrayContentEquals(
message: String?,
expected: T?,
actual: T?,
size: (T) -> Int,
get: T.(Int) -> Any?,
contentToString: T?.() -> String,
contentEquals: T?.(T?) -> Boolean
) {
if (expected.contentEquals(actual)) return
val typeName = "Array"
if (checkReferenceAndNullEquality(typeName, message, expected, actual, contentToString)) return
val expectedSize = size(expected)
val actualSize = size(actual)
if (expectedSize != actualSize) {
val sizesDifferMessage = "$typeName sizes differ. Expected size is $expectedSize, actual size is $actualSize."
val toString = "Expected <${expected.contentToString()}>, actual <${actual.contentToString()}>."
fail(messagePrefix(message) + sizesDifferMessage + "\n" + toString)
}
for (index in 0 until expectedSize) {
val expectedElement = expected.get(index)
val actualElement = actual.get(index)
if (expectedElement != actualElement) {
val elementsDifferMessage = elementsDifferMessage(typeName, index, expectedElement, actualElement)
val toString = "Expected <${expected.contentToString()}>, actual <${actual.contentToString()}>."
fail(messagePrefix(message) + elementsDifferMessage + "\n" + toString)
}
}
}
private fun <T> checkReferenceAndNullEquality(
typeName: String,
message: String?,
expected: T?,
actual: T?,
contentToString: T?.() -> String
): Boolean {
contract {
returns(false) implies (expected != null && actual != null)
}
if (expected === actual) {
return true
}
if (expected == null) {
fail(messagePrefix(message) + "Expected <null> $typeName, actual <${actual.contentToString()}>.")
}
if (actual == null) {
fail(messagePrefix(message) + "Expected non-null $typeName <${expected.contentToString()}>, actual <null>.")
}
return false
}
private fun elementsDifferMessage(typeName: String, index: Int, expectedElement: Any?, actualElement: Any?): String =
"$typeName elements differ at index $index. Expected element <$expectedElement>, actual element <${actualElement}>."

View File

@@ -96,6 +96,160 @@ fun assertNull(actual: Any?, message: String? = null) {
asserter.assertNull(message, actual)
}
/**
* 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].
*
* The elements are compared for equality with the [equals][Any.equals] function.
* For floating point numbers it means that `NaN` is equal to itself and `-0.0` is not equal to `0.0`.
*/
@SinceKotlin("1.5")
fun <@OnlyInputTypes T> assertContentEquals(expected: Iterable<T>?, actual: Iterable<T>?, message: String? = null) {
assertIterableContentEquals("Iterable", message, expected, actual, Iterable<*>::iterator)
}
/**
* Asserts that the [expected] sequence is *structurally* equal to the [actual] sequence,
* i.e. contains the same number of the same elements in the same order, with an optional [message].
*
* The elements are compared for equality with the [equals][Any.equals] function.
* For floating point numbers it means that `NaN` is equal to itself and `-0.0` is not equal to `0.0`.
*/
@SinceKotlin("1.5")
fun <@OnlyInputTypes T> assertContentEquals(expected: Sequence<T>?, actual: Sequence<T>?, message: String? = null) {
assertIterableContentEquals("Sequence", message, expected, actual, Sequence<*>::iterator)
}
/**
* Asserts that the [expected] array is *structurally* equal to the [actual] array,
* i.e. contains the same number of the same elements in the same order, with an optional [message].
*
* The elements are compared for equality with the [equals][Any.equals] function.
* For floating point numbers it means that `NaN` is equal to itself and `-0.0` is not equal to `0.0`.
*/
@SinceKotlin("1.5")
fun <@OnlyInputTypes T> assertContentEquals(expected: Array<T>?, actual: Array<T>?, message: String? = null) {
assertArrayContentEquals(message, expected, actual, { it.size }, Array<*>::get, Array<*>?::contentToString, Array<*>?::contentEquals)
}
/**
* Asserts that the [expected] array is *structurally* equal to the [actual] array,
* i.e. contains the same number of the same elements in the same order, with an optional [message].
*/
@SinceKotlin("1.5")
fun assertContentEquals(expected: ByteArray?, actual: ByteArray?, message: String? = null) {
assertArrayContentEquals(message, expected, actual, { it.size }, ByteArray::get, ByteArray?::contentToString, ByteArray?::contentEquals)
}
/**
* Asserts that the [expected] array is *structurally* equal to the [actual] array,
* i.e. contains the same number of the same elements in the same order, with an optional [message].
*/
@SinceKotlin("1.5")
fun assertContentEquals(expected: ShortArray?, actual: ShortArray?, message: String? = null) {
assertArrayContentEquals(message, expected, actual, { it.size }, ShortArray::get, ShortArray?::contentToString, ShortArray?::contentEquals)
}
/**
* Asserts that the [expected] array is *structurally* equal to the [actual] array,
* i.e. contains the same number of the same elements in the same order, with an optional [message].
*/
@SinceKotlin("1.5")
fun assertContentEquals(expected: IntArray?, actual: IntArray?, message: String? = null) {
assertArrayContentEquals(message, expected, actual, { it.size }, IntArray::get, IntArray?::contentToString, IntArray?::contentEquals)
}
/**
* Asserts that the [expected] array is *structurally* equal to the [actual] array,
* i.e. contains the same number of the same elements in the same order, with an optional [message].
*/
@SinceKotlin("1.5")
fun assertContentEquals(expected: LongArray?, actual: LongArray?, message: String? = null) {
assertArrayContentEquals(message, expected, actual, { it.size }, LongArray::get, LongArray?::contentToString, LongArray?::contentEquals)
}
/**
* Asserts that the [expected] array is *structurally* equal to the [actual] array,
* i.e. contains the same number of the same elements in the same order, with an optional [message].
*
* The elements are compared for equality with the [equals][Any.equals] function.
* For floating point numbers it means that `NaN` is equal to itself and `-0.0` is not equal to `0.0`.
*/
@SinceKotlin("1.5")
fun assertContentEquals(expected: FloatArray?, actual: FloatArray?, message: String? = null) {
assertArrayContentEquals(message, expected, actual, { it.size }, FloatArray::get, FloatArray?::contentToString, FloatArray?::contentEquals)
}
/**
* Asserts that the [expected] array is *structurally* equal to the [actual] array,
* i.e. contains the same number of the same elements in the same order, with an optional [message].
*
* The elements are compared for equality with the [equals][Any.equals] function.
* For floating point numbers it means that `NaN` is equal to itself and `-0.0` is not equal to `0.0`.
*/
@SinceKotlin("1.5")
fun assertContentEquals(expected: DoubleArray?, actual: DoubleArray?, message: String? = null) {
assertArrayContentEquals(message, expected, actual, { it.size }, DoubleArray::get, DoubleArray?::contentToString, DoubleArray?::contentEquals)
}
/**
* Asserts that the [expected] array is *structurally* equal to the [actual] array,
* i.e. contains the same number of the same elements in the same order, with an optional [message].
*/
@SinceKotlin("1.5")
fun assertContentEquals(expected: BooleanArray?, actual: BooleanArray?, message: String? = null) {
assertArrayContentEquals(message, expected, actual, { it.size }, BooleanArray::get, BooleanArray?::contentToString, BooleanArray?::contentEquals)
}
/**
* Asserts that the [expected] array is *structurally* equal to the [actual] array,
* i.e. contains the same number of the same elements in the same order, with an optional [message].
*/
@SinceKotlin("1.5")
fun assertContentEquals(expected: CharArray?, actual: CharArray?, message: String? = null) {
assertArrayContentEquals(message, expected, actual, { it.size }, CharArray::get, CharArray?::contentToString, CharArray?::contentEquals)
}
/**
* Asserts that the [expected] array is *structurally* equal to the [actual] array,
* i.e. contains the same number of the same elements in the same order, with an optional [message].
*/
@SinceKotlin("1.5")
@OptIn(ExperimentalUnsignedTypes::class)
fun assertContentEquals(expected: UByteArray?, actual: UByteArray?, message: String? = null) {
assertArrayContentEquals(message, expected, actual, { it.size }, UByteArray::get, UByteArray?::contentToString, UByteArray?::contentEquals)
}
/**
* Asserts that the [expected] array is *structurally* equal to the [actual] array,
* i.e. contains the same number of the same elements in the same order, with an optional [message].
*/
@SinceKotlin("1.5")
@OptIn(ExperimentalUnsignedTypes::class)
fun assertContentEquals(expected: UShortArray?, actual: UShortArray?, message: String? = null) {
assertArrayContentEquals(message, expected, actual, { it.size }, UShortArray::get, UShortArray?::contentToString, UShortArray?::contentEquals)
}
/**
* Asserts that the [expected] array is *structurally* equal to the [actual] array,
* i.e. contains the same number of the same elements in the same order, with an optional [message].
*/
@SinceKotlin("1.5")
@OptIn(ExperimentalUnsignedTypes::class)
fun assertContentEquals(expected: UIntArray?, actual: UIntArray?, message: String? = null) {
assertArrayContentEquals(message, expected, actual, { it.size }, UIntArray::get, UIntArray?::contentToString, UIntArray?::contentEquals)
}
/**
* Asserts that the [expected] array is *structurally* equal to the [actual] array,
* i.e. contains the same number of the same elements in the same order, with an optional [message].
*/
@SinceKotlin("1.5")
@OptIn(ExperimentalUnsignedTypes::class)
fun assertContentEquals(expected: ULongArray?, actual: ULongArray?, message: String? = null) {
assertArrayContentEquals(message, expected, actual, { it.size }, ULongArray::get, ULongArray?::contentToString, ULongArray?::contentEquals)
}
/** Marks a test as having failed if this point in the execution path is reached, with an optional [message]. */
fun fail(message: String? = null): Nothing {
asserter.fail(message)

View File

@@ -0,0 +1,162 @@
/*
* 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 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<Int> = listOf(1, 2, 3)
val range: Iterable<Int> = 1..3
assertContentEquals(list, list) // ref equality
assertContentEquals(list, range) // elements equal
assertContentEquals(null as Iterable<Int>?, null as Iterable<Int>?) // null equality
testFailureMessage("Expected <null> Iterable, actual <[1, 2, 3]>.") {
assertContentEquals(null, list)
}
testFailureMessage("Expected non-null Iterable <1..3>, actual <null>.") {
assertContentEquals(range, null)
}
testFailureMessage("Iterable elements differ at index 1. Expected element <2>, actual element <3>.") {
assertContentEquals(range, listOf(1, 3, 2))
}
testFailureMessage("Iterable lengths differ. Expected length is bigger than 3, actual length is 3.") {
assertContentEquals(1..4, list)
}
testFailureMessage("Iterable lengths differ. Expected length is 3, actual length is bigger than 3.") {
assertContentEquals(list, 1..4)
}
}
@Test
fun testAssertContentEqualsSequence() {
val sequence1: Sequence<Int> = object : Sequence<Int> {
override fun iterator(): Iterator<Int> = listOf(1, 2, 3).iterator()
override fun toString(): String = "[1, 2, 3]"
}
val sequence2: Sequence<Int> = object : Sequence<Int> {
override fun iterator(): Iterator<Int> = (1..3).iterator()
override fun toString(): String = "1..3"
}
assertContentEquals(sequence1, sequence1) // ref equality
assertContentEquals(sequence1, sequence2) // elements equal
assertContentEquals(null as Iterable<Int>?, null as Iterable<Int>?) // null equality
testFailureMessage("Expected <null> Sequence, actual <[1, 2, 3]>.") {
assertContentEquals(null, sequence1)
}
testFailureMessage("Expected non-null Sequence <1..3>, actual <null>.") {
assertContentEquals(sequence2, null)
}
testFailureMessage("Sequence elements differ at index 1. Expected element <2>, actual element <3>.") {
assertContentEquals(sequence2, sequenceOf(1, 3, 2))
}
testFailureMessage("Sequence lengths differ. Expected length is bigger than 3, actual length is 3.") {
assertContentEquals((1..4).asSequence(), sequence1)
}
testFailureMessage("Sequence lengths differ. Expected length is 3, actual length is bigger than 3.") {
assertContentEquals(sequence1, (1..4).asSequence())
}
}
@Test
fun testAssertContentEqualsArray() {
val array1: Array<Int> = arrayOf(1, 2, 3)
val array2: Array<Int> = arrayOf(1, 2, 3)
assertContentEquals(array1, array1) // ref equality
assertContentEquals(array1, array2) // elements equal
assertContentEquals(null as Array<Int>?, null as Array<Int>?) // null equality
testFailureMessage("Expected <null> Array, actual <[1, 2, 3]>.") {
assertContentEquals(null, array1)
}
testFailureMessage("Expected non-null Array <[1, 2, 3]>, actual <null>.") {
assertContentEquals(array2, null)
}
testFailureMessage("Array elements differ at index 1. Expected element <2>, actual element <3>.\nExpected <[1, 2, 3]>, actual <[1, 3, 2]>.") {
assertContentEquals(array2, arrayOf(1, 3, 2))
}
testFailureMessage("Array sizes differ. Expected size is 4, actual size is 3.\nExpected <[0, -1, -2, -3]>, actual <[1, 2, 3]>.") {
assertContentEquals(Array(4) { -it }, array1)
}
testFailureMessage("Array sizes differ. Expected size is 3, actual size is 4.\nExpected <[1, 2, 3]>, actual <[0, -1, -2, -3]>.") {
assertContentEquals(array1, Array(4) { -it })
}
}
@Test
fun testAssertContentEqualsDoubleArray() {
val array1: DoubleArray = doubleArrayOf(1.0, Double.NaN, 3.0)
val array2: DoubleArray = doubleArrayOf(1.0, Double.NaN, 3.0)
assertContentEquals(array1, array1) // ref equality
assertContentEquals(array1, array2) // elements equal
assertContentEquals(null as DoubleArray?, null as DoubleArray?) // null equality
testFailureMessage("Expected <null> Array, actual <${array1.contentToString()}>.") {
assertContentEquals(null, array1)
}
testFailureMessage("Expected non-null Array <${array2.contentToString()}>, actual <null>.") {
assertContentEquals(array2, null)
}
val sameSizeArray = doubleArrayOf(1.0, Double.NaN, 2.0)
testFailureMessage("Array elements differ at index 2. Expected element <${array2[2]}>, actual element <${sameSizeArray[2]}>.\nExpected <${array2.contentToString()}>, actual <${sameSizeArray.contentToString()}>.") {
assertContentEquals(array2, sameSizeArray)
}
val largerArray = DoubleArray(4) { -it.toDouble() }
testFailureMessage("Array sizes differ. Expected size is 4, actual size is 3.\nExpected <${largerArray.contentToString()}>, actual <${array1.contentToString()}>.") {
assertContentEquals(largerArray, array1)
}
testFailureMessage("Array sizes differ. Expected size is 3, actual size is 4.\nExpected <${array1.contentToString()}>, actual <${largerArray.contentToString()}>.") {
assertContentEquals(array1, largerArray)
}
}
@OptIn(ExperimentalUnsignedTypes::class)
@Test
fun testAssertContentEqualsULongArray() {
val array1: ULongArray = ulongArrayOf(1u, 2u, 3u)
val array2: ULongArray = ulongArrayOf(1u, 2u, 3u)
assertContentEquals(array1, array1) // ref equality
assertContentEquals(array1, array2) // elements equal
assertContentEquals(null as ULongArray?, null as ULongArray?) // null equality
testFailureMessage("Expected <null> Array, actual <[1, 2, 3]>.") {
assertContentEquals(null, array1)
}
testFailureMessage("Expected non-null Array <[1, 2, 3]>, actual <null>.") {
assertContentEquals(array2, null)
}
testFailureMessage("Array elements differ at index 1. Expected element <2>, actual element <3>.\nExpected <[1, 2, 3]>, actual <[1, 3, 2]>.") {
assertContentEquals(array2, ulongArrayOf(1u, 3u, 2u))
}
testFailureMessage("Array sizes differ. Expected size is 4, actual size is 3.\nExpected <[4, 3, 2, 1]>, actual <[1, 2, 3]>.") {
assertContentEquals(ULongArray(4) { 4uL - it.toUInt() }, array1)
}
testFailureMessage("Array sizes differ. Expected size is 3, actual size is 4.\nExpected <[1, 2, 3]>, actual <[4, 3, 2, 1]>.") {
assertContentEquals(array1, ULongArray(4) { 4uL - it.toUInt() })
}
}
}

View File

@@ -208,7 +208,7 @@ class BasicAssertionsTest {
}
private fun checkFailedAssertion(assertion: () -> Unit): AssertionError {
internal fun checkFailedAssertion(assertion: () -> Unit): AssertionError {
return assertFailsWith<AssertionError> { withDefaultAsserter(assertion) }
}

View File

@@ -46,6 +46,7 @@ compileKotlin {
"-Xnormalize-constructor-calls=enable",
"-Xopt-in=kotlin.contracts.ExperimentalContracts",
"-Xsuppress-deprecated-jvm-target-warning",
"-Xopt-in=kotlin.RequiresOptIn",
]
kotlinOptions.moduleName = project.archivesBaseName
}
@@ -54,6 +55,7 @@ compileTestKotlin {
kotlinOptions.freeCompilerArgs = [
"-Xallow-kotlin-package",
"-Xsuppress-deprecated-jvm-target-warning",
"-Xopt-in=kotlin.RequiresOptIn",
]
}