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.
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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)
}

}
Original file line number Diff line number Diff line change
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()
}
}
}
Original file line number Diff line number Diff line change
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
Loading

0 comments on commit ca86424

Please sign in to comment.