Navigation Menu

Skip to content

Commit

Permalink
Upgraded remaining durations to use kotlin.time
Browse files Browse the repository at this point in the history
  • Loading branch information
sksamuel committed Sep 4, 2019
1 parent 0ea6969 commit ca86424
Show file tree
Hide file tree
Showing 11 changed files with 201 additions and 166 deletions.
Expand Up @@ -24,8 +24,8 @@ class AssertionCounterTest : FunSpec() {
"AssertionMode.Error assertion mode should fail the test if no assertions were present" -> {
execute(testCase) {
when (it.status) {
TestStatus.Error, TestStatus.Failure -> complete(TestResult.success(it.durationMs))
else -> complete(TestResult.error(RuntimeException("Should have failed"), it.durationMs))
TestStatus.Error, TestStatus.Failure -> complete(TestResult.success(it.duration))
else -> complete(TestResult.error(RuntimeException("Should have failed"), it.duration))
}
}
}
Expand Down
36 changes: 20 additions & 16 deletions kotlintest-core/src/commonMain/kotlin/io/kotlintest/TestResult.kt
@@ -1,26 +1,30 @@
package io.kotlintest

import kotlin.time.Duration
import kotlin.time.ExperimentalTime

@UseExperimental(ExperimentalTime::class)
data class TestResult(val status: TestStatus,
val error: Throwable?,
val reason: String?,
val durationMs: Long,
val duration: Duration,
val metaData: Map<String, Any?> = emptyMap()) {
companion object {
fun success(durationMs: Long) = TestResult(TestStatus.Success, null, null, durationMs)
val Ignored = TestResult(TestStatus.Ignored, null, null, 0)
fun failure(e: AssertionError, durationMs: Long) = TestResult(TestStatus.Failure, e, null, durationMs)
fun error(t: Throwable, durationMs: Long) = TestResult(TestStatus.Error, t, null, durationMs)
fun ignored(reason: String?) = TestResult(TestStatus.Ignored, null, reason, 0)
}
companion object {
fun success(duration: Duration) = TestResult(TestStatus.Success, null, null, duration)
val Ignored = TestResult(TestStatus.Ignored, null, null, Duration.ZERO)
fun failure(e: AssertionError, duration: Duration) = TestResult(TestStatus.Failure, e, null, duration)
fun error(t: Throwable, duration: Duration) = TestResult(TestStatus.Error, t, null, duration)
fun ignored(reason: String?) = TestResult(TestStatus.Ignored, null, reason, Duration.ZERO)
}
}

enum class TestStatus {
// the test was skipped completely
Ignored,
// the test was successful
Success,
// the test failed because of some exception that was not an assertion error
Error,
// the test ran but an assertion failed
Failure
// the test was skipped completely
Ignored,
// the test was successful
Success,
// the test failed because of some exception that was not an assertion error
Error,
// the test ran but an assertion failed
Failure
}
Expand Up @@ -8,8 +8,9 @@ import io.kotlintest.TestResult
import io.kotlintest.TestStatus
import io.kotlintest.TestType
import io.kotlintest.core.fromSpecClass
import java.time.Duration
import kotlin.reflect.KClass
import kotlin.time.ExperimentalTime
import kotlin.time.milliseconds

private val isWindows = System.getProperty("os.name").contains("win")

Expand Down Expand Up @@ -45,6 +46,7 @@ class MochaConsoleWriter(private val term: TermColors,

override fun hasErrors(): Boolean = errors

@UseExperimental(ExperimentalTime::class)
private fun testLine(testCase: TestCase, result: TestResult): String {
val name = when (result.status) {
TestStatus.Failure, TestStatus.Error -> term.brightRed(testCase.name + " *** FAILED ***")
Expand All @@ -57,7 +59,7 @@ class MochaConsoleWriter(private val term: TermColors,
TestStatus.Ignored -> IgnoredSymbol
}
val duration = when (testCase.type) {
TestType.Test -> durationString(result.durationMs)
TestType.Test -> durationString(result.duration.toLongMilliseconds())
else -> ""
}
return "$margin${testCase.description.indent()} ${symbol.print(term)} $name $duration".padEnd(80, ' ')
Expand Down Expand Up @@ -136,9 +138,10 @@ class MochaConsoleWriter(private val term: TermColors,

private fun padNewLines(str: String, pad: String): String = str.lines().joinToString("\n") { "$pad$it" }

@UseExperimental(ExperimentalTime::class)
override fun engineFinished(t: Throwable?) {

val duration = Duration.ofMillis(System.currentTimeMillis() - start)
val duration = (System.currentTimeMillis() - start).milliseconds

val ignored = results.filter { it.value.status == TestStatus.Ignored }
val failed = results.filter { it.value.status == TestStatus.Failure || it.value.status == TestStatus.Error }
Expand All @@ -148,7 +151,7 @@ class MochaConsoleWriter(private val term: TermColors,
val specDistinctCount = specs.distinct().size

println()
println(term.brightWhite("${margin}KotlinTest completed in ${duration.seconds} seconds / ${duration.toMillis()} milliseconds"))
println(term.brightWhite("${margin}KotlinTest completed in ${duration.toLongMilliseconds()} seconds / ${duration.toLongMilliseconds()} milliseconds"))
println("${margin}Executed $specDistinctCount specs containing ${failed.size + passed.size + ignored.size} tests")
println("$margin${passed.size} passed, ${failed.size} failed, ${ignored.size} ignored")
if (failed.isNotEmpty()) {
Expand Down
Expand Up @@ -7,7 +7,10 @@ import io.kotlintest.matchers.string.shouldContain
import io.kotlintest.matchers.string.shouldContainInOrder
import io.kotlintest.runner.console.BasicConsoleWriter
import io.kotlintest.specs.FunSpec
import kotlin.time.Duration
import kotlin.time.ExperimentalTime

@UseExperimental(ExperimentalTime::class)
class BasicConsoleWriterTest : FunSpec() {

init {
Expand All @@ -25,15 +28,15 @@ class BasicConsoleWriterTest : FunSpec() {
writer.engineStarted(emptyList())
writer.beforeSpecClass(this@BasicConsoleWriterTest::class)
writer.enterTestCase(test1)
writer.exitTestCase(test1, TestResult.success(0))
writer.exitTestCase(test1, TestResult.success(Duration.ZERO))
writer.enterTestCase(test2)
writer.exitTestCase(test2, TestResult.error(RuntimeException("wibble boom"), 0))
writer.exitTestCase(test2, TestResult.error(RuntimeException("wibble boom"), Duration.ZERO))
writer.enterTestCase(test3)
writer.exitTestCase(test3, TestResult.failure(AssertionError("wobble vablam"), 0))
writer.exitTestCase(test3, TestResult.failure(AssertionError("wobble vablam"), Duration.ZERO))
writer.enterTestCase(test4)
writer.exitTestCase(test4, TestResult.ignored("don't like it"))
writer.enterTestCase(test5)
writer.exitTestCase(test5, TestResult.success(0))
writer.exitTestCase(test5, TestResult.success(Duration.ZERO))
writer.afterSpecClass(this@BasicConsoleWriterTest::class, null)
writer.engineFinished(null)
}
Expand Down Expand Up @@ -70,12 +73,12 @@ class BasicConsoleWriterTest : FunSpec() {
writer.enterTestCase(test1)
writer.enterTestCase(test2)
writer.enterTestCase(test3)
writer.exitTestCase(test3, TestResult.failure(AssertionError("wobble vablam"), 0))
writer.exitTestCase(test2, TestResult.error(RuntimeException("wibble boom"), 0))
writer.exitTestCase(test1, TestResult.success(0))
writer.exitTestCase(test3, TestResult.failure(AssertionError("wobble vablam"), Duration.ZERO))
writer.exitTestCase(test2, TestResult.error(RuntimeException("wibble boom"), Duration.ZERO))
writer.exitTestCase(test1, TestResult.success(Duration.ZERO))
writer.enterTestCase(test4)
writer.enterTestCase(test5)
writer.exitTestCase(test5, TestResult.success(0))
writer.exitTestCase(test5, TestResult.success(Duration.ZERO))
writer.exitTestCase(test4, TestResult.ignored("don't like it"))
writer.afterSpecClass(this@BasicConsoleWriterTest::class, null)
writer.engineFinished(null)
Expand Down
Expand Up @@ -18,7 +18,10 @@ import io.kotlintest.core.sourceRef
import io.kotlintest.specs.FunSpec
import io.kotlintest.tables.row
import kotlin.reflect.KClass
import kotlin.time.Duration
import kotlin.time.ExperimentalTime

@ExperimentalTime
class TeamCityConsoleWriterTest : FunSpec() {

private val klass: KClass<out Spec> = TeamCityConsoleWriterTest::class
Expand Down Expand Up @@ -78,15 +81,15 @@ class TeamCityConsoleWriterTest : FunSpec() {

test("after test should write testSuiteFinished for container success") {
captureStandardOut {
TeamCityConsoleWriter().afterTestCaseExecution(testCaseContainer, TestResult.success(0))
TeamCityConsoleWriter().afterTestCaseExecution(testCaseContainer, TestResult.success(Duration.ZERO))
} shouldBe "\n##teamcity[testSuiteFinished name='my test container']\n"
}

test("after test should insert dummy test and write testSuiteFinished for container error") {
captureStandardErr {
captureStandardOut {
TeamCityConsoleWriter().afterTestCaseExecution(testCaseContainer,
TestResult.error(AssertionError("wibble"), 0))
TestResult.error(AssertionError("wibble"), Duration.ZERO))
} shouldBe "\n" +
"##teamcity[testStarted name='my test container <init>']\n" +
"##teamcity[testFailed name='my test container <init>' message='wibble']\n" +
Expand All @@ -103,15 +106,15 @@ class TeamCityConsoleWriterTest : FunSpec() {

test("after test should write testFinished for test success") {
captureStandardOut {
TeamCityConsoleWriter().afterTestCaseExecution(testCaseTest, TestResult.success(0))
TeamCityConsoleWriter().afterTestCaseExecution(testCaseTest, TestResult.success(Duration.ZERO))
} shouldBe "\n##teamcity[testFinished name='my test case']\n"
}

test("afterTestCaseExecution for errored test should write stack trace for error to std err, and write testFailed to std out") {
captureStandardOut {
captureStandardErr {
TeamCityConsoleWriter().afterTestCaseExecution(testCaseTest,
TestResult.error(AssertionError("wibble"), 0)
TestResult.error(AssertionError("wibble"), Duration.ZERO)
)
} shouldStartWith "\njava.lang.AssertionError: wibble\n" +
"\tat com.sksamuel.kotlintest.runner.console.TeamCityConsoleWriter"
Expand All @@ -123,7 +126,7 @@ class TeamCityConsoleWriterTest : FunSpec() {
captureStandardErr {
TeamCityConsoleWriter().afterTestCaseExecution(
testCaseTest,
TestResult.failure(AssertionError("wibble"), 0)
TestResult.failure(AssertionError("wibble"), Duration.ZERO)
)
} shouldStartWith "\njava.lang.AssertionError: wibble\n" +
"\tat com.sksamuel.kotlintest.runner.console.TeamCityConsoleWriter"
Expand All @@ -148,7 +151,7 @@ class TeamCityConsoleWriterTest : FunSpec() {
}

captureStandardOut {
TeamCityConsoleWriter().afterTestCaseExecution(testCaseTest, TestResult.failure(error, 0))
TeamCityConsoleWriter().afterTestCaseExecution(testCaseTest, TestResult.failure(error, Duration.ZERO))
} shouldBe "\n##teamcity[testFailed name='my test case' message='Test failed']\n"
}
}
Expand Down
Expand Up @@ -16,7 +16,9 @@ import kotlinx.coroutines.withContext
import org.slf4j.LoggerFactory
import java.util.concurrent.*
import java.util.concurrent.atomic.AtomicReference
import kotlin.time.Duration
import kotlin.time.ExperimentalTime
import kotlin.time.milliseconds

/**
* The [TestCaseExecutor] is responsible for preparing and executing a single [TestCase].
Expand All @@ -36,6 +38,7 @@ import kotlin.time.ExperimentalTime
*
* The executor can be shared between multiple tests as it is thread safe.
*/
@UseExperimental(ExperimentalTime::class)
class TestCaseExecutor(private val listener: TestEngineListener,
private val executor: ExecutorService,
private val scheduler: ScheduledExecutorService) {
Expand Down Expand Up @@ -75,7 +78,7 @@ class TestCaseExecutor(private val listener: TestEngineListener,

} catch (t: Throwable) {
t.printStackTrace()
listener.exitTestCase(testCase, TestResult.error(t, System.currentTimeMillis() - start))
listener.exitTestCase(testCase, TestResult.error(t, (System.currentTimeMillis() - start).milliseconds))
}
}

Expand Down Expand Up @@ -184,7 +187,7 @@ class TestCaseExecutor(private val listener: TestEngineListener,
executor.shutdown()
}

val result = buildTestResult(error.get(), emptyMap(), System.currentTimeMillis() - start)
val result = buildTestResult(error.get(), emptyMap(), (System.currentTimeMillis() - start).milliseconds)

listener.afterTestCaseExecution(testCase, result)
return result
Expand Down Expand Up @@ -223,11 +226,11 @@ class TestCaseExecutor(private val listener: TestEngineListener,

private fun buildTestResult(error: Throwable?,
metadata: Map<String, Any?>,
durationMs: Long): TestResult = when (error) {
null -> TestResult(TestStatus.Success, null, null, durationMs, metadata)
is AssertionError -> TestResult(TestStatus.Failure, error, null, durationMs, metadata)
is SkipTestException -> TestResult(TestStatus.Ignored, null, error.reason, durationMs, metadata)
else -> TestResult(TestStatus.Error, error, null, durationMs, metadata)
duration: Duration): TestResult = when (error) {
null -> TestResult(TestStatus.Success, null, null, duration, metadata)
is AssertionError -> TestResult(TestStatus.Failure, error, null, duration, metadata)
is SkipTestException -> TestResult(TestStatus.Ignored, null, error.reason, duration, metadata)
else -> TestResult(TestStatus.Error, error, null, duration, metadata)
}

}
Expand Up @@ -5,46 +5,53 @@ import io.kotlintest.TestResult
import io.kotlintest.extensions.SpecLevelExtension
import io.kotlintest.extensions.TestCaseExtension
import io.kotlintest.specs.StringSpec
import kotlin.time.Duration
import kotlin.time.ExperimentalTime

// this tests that we can manipulate the result of a test case from an extension
@ExperimentalTime
class TestCaseExtensionAroundAdviceTest : StringSpec() {

class WibbleException : RuntimeException()
class WibbleException : RuntimeException()

object MyExt : TestCaseExtension {
override suspend fun intercept(testCase: TestCase, execute: suspend (TestCase, suspend (TestResult) -> Unit) -> Unit, complete: suspend (TestResult) -> Unit) {
when {
testCase.description.name == "test1" -> complete(TestResult.Ignored)
testCase.description.name == "test2" -> execute(testCase) {
when (it.error) {
is WibbleException -> complete(TestResult.success(0))
else -> complete(it)
}
}
testCase.description.name == "test3" -> if (testCase.config.enabled) throw RuntimeException() else execute(testCase) { complete(it) }
testCase.description.name == "test4" -> execute(testCase.copy(config = testCase.config.copy(enabled = false))) { complete(it) }
else -> execute(testCase) { complete(it) }
object MyExt : TestCaseExtension {
override suspend fun intercept(testCase: TestCase,
execute: suspend (TestCase, suspend (TestResult) -> Unit) -> Unit,
complete: suspend (TestResult) -> Unit) {
when {
testCase.description.name == "test1" -> complete(TestResult.Ignored)
testCase.description.name == "test2" -> execute(testCase) {
when (it.error) {
is WibbleException -> complete(TestResult.success(Duration.ZERO))
else -> complete(it)
}
}
testCase.description.name == "test3" ->
if (testCase.config.enabled) throw RuntimeException() else execute(testCase) { complete(it) }
testCase.description.name == "test4" ->
execute(testCase.copy(config = testCase.config.copy(enabled = false))) { complete(it) }
else -> execute(testCase) { complete(it) }
}
}
}
}
}

override fun extensions(): List<SpecLevelExtension> = listOf(MyExt)
override fun extensions(): List<SpecLevelExtension> = listOf(MyExt)

init {
// this exception should not be thrown as the extension will skip evaluation of the test
"test1" {
throw RuntimeException()
}
// this exception will be thrown but then the test extension will override the failed result to return a success
"test2" {
throw WibbleException()
}
// the config for this test should be carried through to the extension
"test3".config(enabled = false) {
}
// config for this test should be overriden so that the test is actually disabled, and therefore the exception will not be thrown
"test4".config(enabled = true) {
throw RuntimeException()
}
}
init {
// this exception should not be thrown as the extension will skip evaluation of the test
"test1" {
throw RuntimeException()
}
// this exception will be thrown but then the test extension will override the failed result to return a success
"test2" {
throw WibbleException()
}
// the config for this test should be carried through to the extension
"test3".config(enabled = false) {
}
// config for this test should be overriden so that the test is actually disabled, and therefore the exception will not be thrown
"test4".config(enabled = true) {
throw RuntimeException()
}
}
}
Expand Up @@ -17,8 +17,9 @@ import org.junit.platform.engine.TestDescriptor
import org.junit.platform.engine.TestExecutionResult
import org.junit.platform.engine.UniqueId
import org.junit.platform.engine.support.descriptor.EngineDescriptor
import kotlin.time.Duration
import kotlin.time.ExperimentalTime
import kotlin.time.milliseconds
import kotlin.time.minutes

@ExperimentalTime
class JUnitTestRunnerListenerTest : WordSpec({
Expand Down Expand Up @@ -137,7 +138,7 @@ class JUnitTestRunnerListenerTest : WordSpec({

val spec = JUnitTestRunnerListenerTest()
val tc = TestCase.container(spec.description().append("my test"), spec) { }
.copy(config = TestCaseConfig(invocations = 3, timeout = Duration.ofMinutes(2)))
.copy(config = TestCaseConfig(invocations = 3, timeout = 2.minutes))

listener.beforeSpecClass(spec::class)

Expand All @@ -164,9 +165,9 @@ class JUnitTestRunnerListenerTest : WordSpec({

val spec = JUnitTestRunnerListenerTest()
val tc1 = TestCase.container(spec.description().append("test1"), spec) { }
.copy(config = TestCaseConfig(timeout = Duration.ofMinutes(2)))
.copy(config = TestCaseConfig(timeout = 2.minutes))
val tc2 = TestCase.container(tc1.description.append("test2"), spec) { }
.copy(config = TestCaseConfig(timeout = Duration.ofMinutes(2)))
.copy(config = TestCaseConfig(timeout = 2.minutes))

listener.beforeSpecClass(spec::class)
listener.enterTestCase(tc1)
Expand Down

0 comments on commit ca86424

Please sign in to comment.