unit-tests: Assert library

This commit is contained in:
Ilya Matveev
2017-09-15 11:45:29 +03:00
committed by ilmat192
parent 74fdfa17f0
commit 02fb8d3eb1
9 changed files with 286 additions and 22 deletions
@@ -532,9 +532,9 @@ internal class TestProcessor (val context: KonanBackendContext): FileLoweringPas
})
}
// TODO: Get tests from Kotlin/JVM
// TODO: Support tests in objects
// TODO: Support beforeTest/afterTest for companions.
// TODO: Check beforeTest/afterTest for companions.
private fun createTestSuites(irFile: IrFile, annotationCollector: AnnotationCollector) {
annotationCollector.testClasses.filter {
it.value.functions.any { it.kind == FunctionKind.TEST }
+6 -1
View File
@@ -92,4 +92,9 @@ public annotation class Fixme
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 endSuite(suite: TestSuite)
fun start(testCase: TestCase)
fun pass(testCase: TestCase)
fun fail(testCase: TestCase, e: AssertionError)
fun error(testCase: TestCase, e: Throwable)
fun fail(testCase: TestCase, e: Throwable)
fun ignore(testCase: TestCase)
}
@@ -1,20 +1,21 @@
package konan.test
import kotlin.AssertionError
object TestRunner {
object SimpleTestListener: TestListener {
override fun startTesting(runner: TestRunner) = println("Starting testing\n")
override fun endTesting(runner: TestRunner) = println("Testing finished\n")
override fun startTesting(runner: TestRunner) = println("Starting testing")
override fun endTesting(runner: TestRunner) = println("Testing finished")
override fun startSuite(suite: TestSuite) = println("Starting test suite: $suite\n")
override fun endSuite(suite: TestSuite) = println("Test suite finished: $suite\n")
override fun startSuite(suite: TestSuite) = println("Starting test suite: $suite")
override fun endSuite(suite: TestSuite) = println("Test suite finished: $suite")
override fun pass(testCase: TestCase) = println("Pass: $testCase\n")
override fun fail(testCase: TestCase, e: AssertionError) = println("Fail: $testCase (Exception: ${e.message})\n")
override fun error(testCase: TestCase, e: Throwable) = println("Error: $testCase (Exception: ${e.message})\n")
override fun ignore(testCase: TestCase) = println("Ignore: $testCase\n")
override fun start(testCase: TestCase) = println("Start test case: $testCase")
override fun pass(testCase: TestCase) = println("Pass: $testCase")
override fun fail(testCase: TestCase, e: Throwable) {
println("Fail: $testCase. Exception:")
e.printStackTrace()
}
override fun ignore(testCase: TestCase) = println("Ignore: $testCase")
}
private val _suites = mutableListOf<TestSuite>()
@@ -29,7 +29,7 @@ abstract class AbstractTestSuite<F: Function<Unit>>(override val name: String):
override val suite: AbstractTestSuite<F>,
val testFunction: F
) : TestCase {
override fun toString(): String = "$suite.$name"
override fun toString(): String = "$name ($suite)"
}
private val _testCases = mutableMapOf<String, BasicTestCase<F>>()
@@ -53,13 +53,11 @@ abstract class AbstractTestSuite<F: Function<Unit>>(override val name: String):
doBeforeClass()
testCases.values.forEach {
try {
listener.start(it)
doTest(it)
listener.pass(it)
// TODO: merge catches?
} catch (e: AssertionError) {
listener.fail(it, e)
} catch (e: Throwable) {
listener.error(it, e)
listener.fail(it, e)
}
}
@@ -1,7 +1,5 @@
package kotlin.test
// TODO: Move test code in a separate library.
@Retention(AnnotationRetention.SOURCE)
@Target(AnnotationTarget.FUNCTION)
annotation class Test
@@ -14,6 +12,7 @@ annotation class BeforeClass
@Target(AnnotationTarget.FUNCTION)
annotation class AfterClass
// TODO: Rename Before -> BeforeEach, After -> AfterEach
@Retention(AnnotationRetention.SOURCE)
@Target(AnnotationTarget.FUNCTION)
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 }