unit-tests: Assert library
This commit is contained in:
+2
-2
@@ -532,9 +532,9 @@ internal class TestProcessor (val context: KonanBackendContext): FileLoweringPas
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Get tests from Kotlin/JVM
|
||||||
// TODO: Support tests in objects
|
// TODO: Support tests in objects
|
||||||
// TODO: Support beforeTest/afterTest for companions.
|
// TODO: Check beforeTest/afterTest for companions.
|
||||||
private fun createTestSuites(irFile: IrFile, annotationCollector: AnnotationCollector) {
|
private fun createTestSuites(irFile: IrFile, annotationCollector: AnnotationCollector) {
|
||||||
annotationCollector.testClasses.filter {
|
annotationCollector.testClasses.filter {
|
||||||
it.value.functions.any { it.kind == FunctionKind.TEST }
|
it.value.functions.any { it.kind == FunctionKind.TEST }
|
||||||
|
|||||||
@@ -92,4 +92,9 @@ public annotation class Fixme
|
|||||||
|
|
||||||
public annotation class Escapes(val who: Int)
|
public annotation class Escapes(val who: Int)
|
||||||
|
|
||||||
public annotation class PointsTo(vararg val onWhom: Int)
|
public annotation class PointsTo(vararg val onWhom: Int)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Need to be fixed because of header/impl notation
|
||||||
|
*/
|
||||||
|
public annotation class FixmeMultiplatform
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ interface TestListener {
|
|||||||
fun startSuite(suite: TestSuite)
|
fun startSuite(suite: TestSuite)
|
||||||
fun endSuite(suite: TestSuite)
|
fun endSuite(suite: TestSuite)
|
||||||
|
|
||||||
|
fun start(testCase: TestCase)
|
||||||
fun pass(testCase: TestCase)
|
fun pass(testCase: TestCase)
|
||||||
fun fail(testCase: TestCase, e: AssertionError)
|
fun fail(testCase: TestCase, e: Throwable)
|
||||||
fun error(testCase: TestCase, e: Throwable)
|
|
||||||
fun ignore(testCase: TestCase)
|
fun ignore(testCase: TestCase)
|
||||||
}
|
}
|
||||||
@@ -1,20 +1,21 @@
|
|||||||
package konan.test
|
package konan.test
|
||||||
|
|
||||||
import kotlin.AssertionError
|
|
||||||
|
|
||||||
object TestRunner {
|
object TestRunner {
|
||||||
|
|
||||||
object SimpleTestListener: TestListener {
|
object SimpleTestListener: TestListener {
|
||||||
override fun startTesting(runner: TestRunner) = println("Starting testing\n")
|
override fun startTesting(runner: TestRunner) = println("Starting testing")
|
||||||
override fun endTesting(runner: TestRunner) = println("Testing finished\n")
|
override fun endTesting(runner: TestRunner) = println("Testing finished")
|
||||||
|
|
||||||
override fun startSuite(suite: TestSuite) = println("Starting test suite: $suite\n")
|
override fun startSuite(suite: TestSuite) = println("Starting test suite: $suite")
|
||||||
override fun endSuite(suite: TestSuite) = println("Test suite finished: $suite\n")
|
override fun endSuite(suite: TestSuite) = println("Test suite finished: $suite")
|
||||||
|
|
||||||
override fun pass(testCase: TestCase) = println("Pass: $testCase\n")
|
override fun start(testCase: TestCase) = println("Start test case: $testCase")
|
||||||
override fun fail(testCase: TestCase, e: AssertionError) = println("Fail: $testCase (Exception: ${e.message})\n")
|
override fun pass(testCase: TestCase) = println("Pass: $testCase")
|
||||||
override fun error(testCase: TestCase, e: Throwable) = println("Error: $testCase (Exception: ${e.message})\n")
|
override fun fail(testCase: TestCase, e: Throwable) {
|
||||||
override fun ignore(testCase: TestCase) = println("Ignore: $testCase\n")
|
println("Fail: $testCase. Exception:")
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
override fun ignore(testCase: TestCase) = println("Ignore: $testCase")
|
||||||
}
|
}
|
||||||
|
|
||||||
private val _suites = mutableListOf<TestSuite>()
|
private val _suites = mutableListOf<TestSuite>()
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ abstract class AbstractTestSuite<F: Function<Unit>>(override val name: String):
|
|||||||
override val suite: AbstractTestSuite<F>,
|
override val suite: AbstractTestSuite<F>,
|
||||||
val testFunction: F
|
val testFunction: F
|
||||||
) : TestCase {
|
) : TestCase {
|
||||||
override fun toString(): String = "$suite.$name"
|
override fun toString(): String = "$name ($suite)"
|
||||||
}
|
}
|
||||||
|
|
||||||
private val _testCases = mutableMapOf<String, BasicTestCase<F>>()
|
private val _testCases = mutableMapOf<String, BasicTestCase<F>>()
|
||||||
@@ -53,13 +53,11 @@ abstract class AbstractTestSuite<F: Function<Unit>>(override val name: String):
|
|||||||
doBeforeClass()
|
doBeforeClass()
|
||||||
testCases.values.forEach {
|
testCases.values.forEach {
|
||||||
try {
|
try {
|
||||||
|
listener.start(it)
|
||||||
doTest(it)
|
doTest(it)
|
||||||
listener.pass(it)
|
listener.pass(it)
|
||||||
// TODO: merge catches?
|
|
||||||
} catch (e: AssertionError) {
|
|
||||||
listener.fail(it, e)
|
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
listener.error(it, e)
|
listener.fail(it, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package kotlin.test
|
package kotlin.test
|
||||||
|
|
||||||
// TODO: Move test code in a separate library.
|
|
||||||
|
|
||||||
@Retention(AnnotationRetention.SOURCE)
|
@Retention(AnnotationRetention.SOURCE)
|
||||||
@Target(AnnotationTarget.FUNCTION)
|
@Target(AnnotationTarget.FUNCTION)
|
||||||
annotation class Test
|
annotation class Test
|
||||||
@@ -14,6 +12,7 @@ annotation class BeforeClass
|
|||||||
@Target(AnnotationTarget.FUNCTION)
|
@Target(AnnotationTarget.FUNCTION)
|
||||||
annotation class AfterClass
|
annotation class AfterClass
|
||||||
|
|
||||||
|
// TODO: Rename Before -> BeforeEach, After -> AfterEach
|
||||||
@Retention(AnnotationRetention.SOURCE)
|
@Retention(AnnotationRetention.SOURCE)
|
||||||
@Target(AnnotationTarget.FUNCTION)
|
@Target(AnnotationTarget.FUNCTION)
|
||||||
annotation class Before
|
annotation class Before
|
||||||
|
|||||||
@@ -0,0 +1,239 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A number of common helper methods for writing unit tests. Copied from Kotlin repo.
|
||||||
|
*/
|
||||||
|
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
||||||
|
|
||||||
|
package kotlin.test
|
||||||
|
|
||||||
|
import konan.FixmeMultiplatform
|
||||||
|
import konan.FixmeReflection
|
||||||
|
import kotlin.internal.InlineOnly
|
||||||
|
import kotlin.internal.OnlyInputTypes
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current adapter providing assertion implementations
|
||||||
|
*/
|
||||||
|
val asserter: Asserter
|
||||||
|
get() = _asserter ?: lookupAsserter()
|
||||||
|
|
||||||
|
/** Used to override current asserter internally */
|
||||||
|
internal var _asserter: Asserter? = null
|
||||||
|
|
||||||
|
/** Asserts that the given [block] returns `true`. */
|
||||||
|
fun assertTrue(message: String? = null, block: () -> Boolean): Unit = assertTrue(block(), message)
|
||||||
|
|
||||||
|
/** Asserts that the expression is `true` with an optional [message]. */
|
||||||
|
fun assertTrue(actual: Boolean, message: String? = null) {
|
||||||
|
return asserter.assertTrue(message ?: "Expected value to be true.", actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Asserts that the given [block] returns `false`. */
|
||||||
|
fun assertFalse(message: String? = null, block: () -> Boolean): Unit = assertFalse(block(), message)
|
||||||
|
|
||||||
|
/** Asserts that the expression is `false` with an optional [message]. */
|
||||||
|
fun assertFalse(actual: Boolean, message: String? = null) {
|
||||||
|
return asserter.assertTrue(message ?: "Expected value to be false.", !actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Asserts that the [expected] value is equal to the [actual] value, with an optional [message]. */
|
||||||
|
fun <@OnlyInputTypes T> assertEquals(expected: T, actual: T, message: String? = null) {
|
||||||
|
asserter.assertEquals(message, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 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 [actual] value is not `null`, with an optional [message]. */
|
||||||
|
fun <T : Any> assertNotNull(actual: T?, message: String? = null): T {
|
||||||
|
asserter.assertNotNull(message, actual)
|
||||||
|
return actual!!
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Asserts that the [actual] value is not `null`, with an optional [message] and a function [block] to process the not-null value. */
|
||||||
|
fun <T : Any, R> assertNotNull(actual: T?, message: String? = null, block: (T) -> R) {
|
||||||
|
asserter.assertNotNull(message, actual)
|
||||||
|
if (actual != null) {
|
||||||
|
block(actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Asserts that the [actual] value is `null`, with an optional [message]. */
|
||||||
|
fun assertNull(actual: Any?, message: String? = null) {
|
||||||
|
asserter.assertNull(message, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Asserts that given function [block] returns the given [expected] value. */
|
||||||
|
fun <@OnlyInputTypes T> expect(expected: T, block: () -> T) {
|
||||||
|
assertEquals(expected, block())
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Asserts that given function [block] returns the given [expected] value and use the given [message] if it fails. */
|
||||||
|
fun <@OnlyInputTypes T> expect(expected: T, message: String?, block: () -> T) {
|
||||||
|
assertEquals(expected, block(), message)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Asserts that given function [block] fails by throwing an exception. */
|
||||||
|
fun assertFails(block: () -> Unit): Throwable = assertFails(null, block)
|
||||||
|
|
||||||
|
/** Asserts that given function [block] fails by throwing an exception. */
|
||||||
|
@SinceKotlin("1.1")
|
||||||
|
fun assertFails(message: String?, block: () -> Unit): Throwable {
|
||||||
|
try {
|
||||||
|
block()
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
assertEquals(e.message, e.message) // success path assertion for qunit
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
asserter.fail(messagePrefix(message) + "Expected an exception to be thrown, but was completed successfully.")
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Asserts that a [block] fails with a specific exception of type [T] being thrown.
|
||||||
|
* Since inline method doesn't allow to trace where it was invoked, it is required to pass a [message] to distinguish this method call from others.
|
||||||
|
*/
|
||||||
|
@InlineOnly
|
||||||
|
@FixmeReflection // TODO: Implement as in Kotlin/JVM when KClass is available.
|
||||||
|
// TODO: An incorrect llvm module is generated if this method is called. Uncomment it when
|
||||||
|
@Suppress("UNUSED_PARAMETER")
|
||||||
|
inline fun <reified T : Throwable> assertFailsWith(message: String? = null, noinline block: () -> Unit) /*: T*/ {
|
||||||
|
// try {
|
||||||
|
// block()
|
||||||
|
// } catch (e: Throwable) {
|
||||||
|
// if (e is T) {
|
||||||
|
// return e
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Suppress("INVISIBLE_MEMBER")
|
||||||
|
// asserter.fail(messagePrefix(message) + "Unxepected exception type: $e")
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Suppress("INVISIBLE_MEMBER")
|
||||||
|
// val msg = messagePrefix(message)
|
||||||
|
// asserter.fail(msg + "Expected an exception to be thrown, but was completed successfully.")
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Asserts that a [block] fails with a specific exception of type [exceptionClass] being thrown. */
|
||||||
|
//@FixmeReflection
|
||||||
|
//fun <T : Throwable> assertFailsWith(exceptionClass: KClass<T>, block: () -> Unit): T = assertFailsWith(exceptionClass, null, block)
|
||||||
|
|
||||||
|
|
||||||
|
// From AssertionsH.kt
|
||||||
|
/**
|
||||||
|
* Comments out a block of test code until it is implemented while keeping a link to the code
|
||||||
|
* to implement in your unit test output
|
||||||
|
*/
|
||||||
|
@FixmeMultiplatform
|
||||||
|
@Suppress("UNUSED_PARAMETER")
|
||||||
|
/*header */ inline fun todo(block: () -> Unit) {
|
||||||
|
println("TODO")
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Asserts that a [block] fails with a specific exception of type [exceptionClass] being thrown. */
|
||||||
|
//@FixmeMultiplatform
|
||||||
|
//@FixmeReflection
|
||||||
|
//header fun <T : Throwable> assertFailsWith(exceptionClass: KClass<T>, message: String?, block: () -> Unit): T
|
||||||
|
|
||||||
|
// From Assertions.kt
|
||||||
|
/**
|
||||||
|
* Abstracts the logic for performing assertions.
|
||||||
|
*/
|
||||||
|
interface Asserter {
|
||||||
|
/**
|
||||||
|
* Fails the current test with the specified message.
|
||||||
|
*
|
||||||
|
* @param message the message to report.
|
||||||
|
*/
|
||||||
|
fun fail(message: String?): Nothing
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the specified value is `true`.
|
||||||
|
*
|
||||||
|
* @param lazyMessage the function to return a message to report if the assertion fails.
|
||||||
|
*/
|
||||||
|
fun assertTrue(lazyMessage: () -> String?, actual: Boolean): Unit {
|
||||||
|
if (!actual) {
|
||||||
|
fail(lazyMessage())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the specified value is `true`.
|
||||||
|
*
|
||||||
|
* @param message the message to report if the assertion fails.
|
||||||
|
*/
|
||||||
|
fun assertTrue(message: String?, actual: Boolean): Unit {
|
||||||
|
assertTrue({ message }, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the specified values are equal.
|
||||||
|
*
|
||||||
|
* @param message the message to report if the assertion fails.
|
||||||
|
*/
|
||||||
|
fun assertEquals(message: String?, expected: Any?, actual: Any?): Unit {
|
||||||
|
assertTrue({ messagePrefix(message) + "Expected <$expected>, actual <$actual>." }, actual == expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the specified values are not equal.
|
||||||
|
*
|
||||||
|
* @param message the message to report if the assertion fails.
|
||||||
|
*/
|
||||||
|
fun assertNotEquals(message: String?, illegal: Any?, actual: Any?): Unit {
|
||||||
|
assertTrue({ messagePrefix(message) + "Illegal value: <$actual>." }, actual != illegal)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the specified value is `null`.
|
||||||
|
*
|
||||||
|
* @param message the message to report if the assertion fails.
|
||||||
|
*/
|
||||||
|
fun assertNull(message: String?, actual: Any?): Unit {
|
||||||
|
assertTrue({ messagePrefix(message) + "Expected value to be null, but was: <$actual>." }, actual == null)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the specified value is not `null`.
|
||||||
|
*
|
||||||
|
* @param message the message to report if the assertion fails.
|
||||||
|
*/
|
||||||
|
fun assertNotNull(message: String?, actual: Any?): Unit {
|
||||||
|
assertTrue({ messagePrefix(message) + "Expected value to be not null." }, actual != null)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks applicability and provides Asserter instance
|
||||||
|
*/
|
||||||
|
interface AsserterContributor {
|
||||||
|
/**
|
||||||
|
* Provides [Asserter] instance or `null` depends on the current context.
|
||||||
|
*
|
||||||
|
* @return asserter instance or null if it is not applicable now
|
||||||
|
*/
|
||||||
|
fun contribute(): Asserter?
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package kotlin.test
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default [Asserter] implementation for Kotlin/Native.
|
||||||
|
*/
|
||||||
|
object DefaultAsserter : Asserter {
|
||||||
|
override fun fail(message: String?): Nothing {
|
||||||
|
if (message == null)
|
||||||
|
throw AssertionError()
|
||||||
|
else
|
||||||
|
throw AssertionError(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package kotlin.test
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
|
internal fun messagePrefix(message: String?) = if (message == null) "" else "$message. "
|
||||||
|
|
||||||
|
internal fun lookupAsserter(): Asserter = DefaultAsserter
|
||||||
|
|
||||||
|
@PublishedApi // required to get stable name as it's called from box tests
|
||||||
|
internal fun overrideAsserter(value: Asserter?): Asserter? = _asserter.also { _asserter = value }
|
||||||
Reference in New Issue
Block a user