Add assertEquals and assertNotEquals for floating point numbers to kotlin-test #KT-8364

This commit is contained in:
Renee Vandervelde
2020-07-18 11:39:53 -05:00
committed by Abduqodiri Qurbonzoda
parent ff59b1c2ef
commit d520c37454
3 changed files with 178 additions and 1 deletions
@@ -63,11 +63,35 @@ fun <@OnlyInputTypes T> assertEquals(expected: T, actual: T, message: String? =
asserter.assertEquals(message, expected, actual)
}
/** Asserts that the difference between the [actual] and the [expected] is within an [absoluteTolerance], with an optional [message]. */
@SinceKotlin("1.5")
fun assertEquals(expected: Double, actual: Double, absoluteTolerance: Double, message: String? = null) {
checkDoublesAreEqual(expected, actual, absoluteTolerance, message)
}
/** Asserts that the difference between the [actual] and the [expected] is within an [absoluteTolerance], with an optional [message]. */
@SinceKotlin("1.5")
fun assertEquals(expected: Float, actual: Float, absoluteTolerance: Float, message: String? = null) {
checkFloatsAreEqual(expected, actual, absoluteTolerance, message)
}
/** Asserts that the [actual] value is not equal to the illegal value, with an optional [message]. */
fun <@OnlyInputTypes T> assertNotEquals(illegal: T, actual: T, message: String? = null) {
asserter.assertNotEquals(message, illegal, actual)
}
/** Asserts that the difference between the [actual] and the [illegal] is not within an [absoluteTolerance], with an optional [message]. */
@SinceKotlin("1.5")
fun assertNotEquals(illegal: Double, actual: Double, absoluteTolerance: Double, message: String? = null) {
checkDoublesAreEqual(illegal, actual, absoluteTolerance, message, shouldFail = true)
}
/** Asserts that the difference between the [actual] and the [illegal] is not within an [absoluteTolerance], with an optional [message]. */
@SinceKotlin("1.5")
fun assertNotEquals(illegal: Float, actual: Float, absoluteTolerance: Float, message: String? = null) {
checkFloatsAreEqual(illegal, actual, absoluteTolerance, message, shouldFail = true)
}
/** Asserts that [expected] is the same instance as [actual], with an optional [message]. */
fun <@OnlyInputTypes T> assertSame(expected: T, actual: T, message: String? = null) {
asserter.assertSame(message, expected, actual)
@@ -5,8 +5,48 @@
package kotlin.test
import kotlin.math.abs
internal fun messagePrefix(message: String?) = if (message == null) "" else "$message. "
internal expect fun lookupAsserter(): Asserter
@PublishedApi // required to get stable name as it's called from box tests
internal fun overrideAsserter(value: Asserter?): Asserter? = _asserter.also { _asserter = value }
internal fun overrideAsserter(value: Asserter?): Asserter? = _asserter.also { _asserter = value }
private fun checkAbsoluteTolerance(absoluteTolerance: Double) {
require(absoluteTolerance >= 0.0) { "Illegal negative absolute tolerance <$absoluteTolerance>." }
require(!absoluteTolerance.isNaN()) { "Illegal NaN absolute tolerance <$absoluteTolerance>." }
}
internal fun checkDoublesAreEqual(
expected: Double,
actual: Double,
absoluteTolerance: Double,
message: String?,
shouldFail: Boolean = false
) {
checkAbsoluteTolerance(absoluteTolerance)
val equal = expected.toBits() == actual.toBits() || abs(expected - actual) <= absoluteTolerance
asserter.assertTrue(
{ messagePrefix(message) + "Expected <$expected> with absolute tolerance <$absoluteTolerance>, actual <$actual>." },
equal != shouldFail
)
}
internal fun checkFloatsAreEqual(
expected: Float,
actual: Float,
absoluteTolerance: Float,
message: String?,
shouldFail: Boolean = false
) {
checkAbsoluteTolerance(absoluteTolerance.toDouble())
val equal = expected.toBits() == actual.toBits() || abs(expected - actual) <= absoluteTolerance
asserter.assertTrue(
{ messagePrefix(message) + "Expected <$expected> with absolute tolerance <$absoluteTolerance>, actual <$actual>." },
equal != shouldFail
)
}
@@ -88,6 +88,119 @@ class BasicAssertionsTest {
checkFailedAssertion { assertSame(instance1, instance2) }
}
@Test
fun testAssertEqualsDouble() {
assertEquals(0.01, 0.02, .01)
// 0.0, -0.0
assertEquals(0.0, 0.0, 0.0)
assertEquals(0.0, -0.0, 0.0)
assertEquals(-0.0, 0.0, 0.0)
assertEquals(0.0, 0.0, -0.0)
assertEquals(0.0, -0.0, -0.0)
assertEquals(-0.0, 0.0, -0.0)
// NaN
val nans = doubleArrayOf(Double.NaN, Double.fromBits(0xFFF80000L shl 32))
for (nan1 in nans) {
assertTrue(nan1.isNaN())
for (nan2 in nans) {
assertEquals(nan1, nan2, 0.1)
assertEquals(nan1, nan2, 0.0)
}
}
// MIN_VALUE, MAX_VALUE
assertEquals(Double.MAX_VALUE, Double.MAX_VALUE, 0.0)
assertEquals(Double.MIN_VALUE, Double.MIN_VALUE, 0.0)
assertEquals(Double.MAX_VALUE, Double.MIN_VALUE, Double.MAX_VALUE)
assertEquals(Double.MIN_VALUE, Double.MAX_VALUE, Double.MAX_VALUE)
// POSITIVE_INFINITY, NEGATIVE_INFINITY
assertEquals(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 0.0)
assertEquals(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, 0.0)
assertEquals(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY)
assertEquals(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY)
}
@Test
fun testAssertEqualsDoubleFails() {
checkFailedAssertion { assertEquals(0.01, 1.03, .01) }
// negative absoluteTolerance
assertFailsWith<IllegalArgumentException> { assertEquals(0.01, 1.03, -5.0) }
}
@Test
fun testAssertEqualsFloat() {
assertEquals(0.01f, 0.02f, .01f)
// 0.0, -0.0
assertEquals(0.0f, 0.0f, 0.0f)
assertEquals(0.0f, -0.0f, 0.0f)
assertEquals(-0.0f, 0.0f, 0.0f)
assertEquals(0.0f, 0.0f, -0.0f)
assertEquals(0.0f, -0.0f, -0.0f)
assertEquals(-0.0f, 0.0f, -0.0f)
// NaN
val nans = floatArrayOf(Float.NaN, Float.fromBits(0xFFC00000.toInt()))
for (nan1 in nans) {
assertTrue(nan1.isNaN())
for (nan2 in nans) {
assertEquals(nan1, nan2, 0.1f)
assertEquals(nan1, nan2, 0.0f)
}
}
// MIN_VALUE, MAX_VALUE
assertEquals(Float.MAX_VALUE, Float.MAX_VALUE, 0.0f)
assertEquals(Float.MIN_VALUE, Float.MIN_VALUE, 0.0f)
assertEquals(Float.MAX_VALUE, Float.MIN_VALUE, Float.MAX_VALUE)
assertEquals(Float.MIN_VALUE, Float.MAX_VALUE, Float.MAX_VALUE)
// POSITIVE_INFINITY, NEGATIVE_INFINITY
assertEquals(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, 0.0f)
assertEquals(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, 0.0f)
assertEquals(Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY)
assertEquals(Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY)
}
@Test
fun testAssertEqualsFloatFails() {
checkFailedAssertion { assertEquals(0.01f, 1.03f, .01f) }
// negative absoluteTolerance
assertFailsWith<IllegalArgumentException> { assertEquals(0.01f, 1.03f, -5.0f) }
}
@Test
fun testAssertNotEqualsDouble() {
assertNotEquals(0.1, 0.3, 0.1)
}
@Test
fun testAssertNotEqualsDoubleFails() {
checkFailedAssertion { assertNotEquals(0.1, 0.11, 0.1) }
// negative absoluteTolerance
assertFailsWith<IllegalArgumentException> { assertNotEquals(0.1, 0.11, -0.001) }
}
@Test
fun testAssertNotEqualsFloat() {
assertNotEquals(0.1f, 0.3f, 0.1f)
}
@Test
fun testAssertNotEqualsFloatFails() {
checkFailedAssertion { assertNotEquals(0.1f, 0.11f, .1f) }
// negative absoluteTolerance
assertFailsWith<IllegalArgumentException> { assertNotEquals(0.1f, 0.11f, -0.001f) }
}
@Test
fun testAssertTrue() {
assertTrue(true)