Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace timeout + timeUnit with Duration #30

Merged
merged 2 commits into from
Jun 15, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
32 changes: 32 additions & 0 deletions CHANGELOG.md
@@ -0,0 +1,32 @@
Changelog
=========

This project follows [semantic versioning](http://semver.org/).

Version 2.0.0, released YYYY-MM-DD TODO
----------------------------------

[Closed Issues](https://github.com/kotlintest/kotlintest/issues?utf8=%E2%9C%93&q=is%3Aclosed+milestone%3A2.0)

### Added

nothing

### Changed

#### Replaced `timeout` + `timeUnit` with `Duration` ([#29](https://github.com/kotlintest/kotlintest/issues/29))

You can now write `config(timeout = 2.seconds)` instead of
`config(timeout = 2, timeoutUnit = TimeUnit.SECONDS)`.

### Deprecated

nothing

### Removed

nothing

### Fixed

nothing
4 changes: 2 additions & 2 deletions README.md
Expand Up @@ -228,7 +228,7 @@ Each test can be configured with various parameters. After the test block, invok
* `invocations` - the number of times to run this test. Useful if you have a non-deterministic test and you want to run that particular test a set number of times. Defaults to 1.
* `threads` - Allows the invocation of this test to be parallelized by setting the number of threads to use in a thread pool executor for this test. If invocations is 1 (the default) then this parameter will have no effect. Similarly, if you set invocations to a value less than or equal to the number threads, then each invocation will have its own thread.
* `ignored` - If set to true then this test is ignored. Can be useful if a test needs to be temporarily disabled.
* `timeout` / `timeoutUnit` - sets a timeout for this test. If the test has not finished in that time then the test fails. Useful for code that is non-deterministic and might not finish.
* `timeout` - sets a timeout for this test. If the test has not finished in that time then the test fails. Useful for code that is non-deterministic and might not finish. Timeout is of type `Duration` which can be instantiated like `2.seconds`, `3.minutes` and so on.
* `tag` / `tags` - a list of String tags that can be set on a test. Then by invoking the test runner with a system property of testTags, you can control which tests are run. For example, tests that require a linux based O/S might be tagged with "linux" then gradle could be invoked with gradle test -DtestTags=linux. Another example might be tagging database tags that you only want to run on a server that has a database installed. Any test that has no tags is always run.

Examples of setting config:
Expand All @@ -251,7 +251,7 @@ class MyTests : WordSpec() {
"return the length of the string" {
"sammy".length shouldBe 5
"".length shouldBe 0
}.config(timeout=2, timeoutUnit=TimeUnit.SECONDS)
}.config(timeout = 2.seconds)
}
}
}
Expand Down
31 changes: 31 additions & 0 deletions src/main/kotlin/io/kotlintest/Duration.kt
@@ -0,0 +1,31 @@
package io.kotlintest

import java.util.concurrent.TimeUnit

data class Duration(val amount: Long, val timeUnit: TimeUnit) {

companion object {
// Actually limited to 9223372036854775807 days, so unless you are very patient, it is unlimited ;-)
val unlimited = Duration(amount = Long.MAX_VALUE, timeUnit = TimeUnit.DAYS)
}

val Long.days: Duration
get() = Duration(this, TimeUnit.DAYS)

val Long.hours: Duration
get() = Duration(this, TimeUnit.HOURS)

val Long.microseconds: Duration
get() = Duration(this, TimeUnit.MICROSECONDS)

val Long.milliseconds: Duration
get() = Duration(this, TimeUnit.MILLISECONDS)

val Long.nanoseconds: Duration
get() = Duration(this, TimeUnit.NANOSECONDS)

val Long.seconds: Duration
get() = Duration(this, TimeUnit.SECONDS)

val nanoseconds: Long = timeUnit.toNanos(amount)
}
9 changes: 4 additions & 5 deletions src/main/kotlin/io/kotlintest/Eventually.kt
@@ -1,20 +1,19 @@
package io.kotlintest

import java.util.concurrent.TimeUnit

interface Eventually {

fun eventually(duration: Long, unit: TimeUnit, f: () -> Unit): Unit {
val end = System.nanoTime() + unit.toNanos(duration)
fun eventually(duration: Duration, f: () -> Unit): Unit {
val end = System.nanoTime() + duration.nanoseconds
var times = 0
while (System.nanoTime() < end) {
try {
f()
return
} catch (e: Exception) {
// ignore and proceed
}
times++
}
throw TestFailedException("Test failed after $duration $unit; attempted $times times")
throw TestFailedException("Test failed after ${duration.amount} ${duration.timeUnit}; attempted $times times")
}
}
18 changes: 8 additions & 10 deletions src/main/kotlin/io/kotlintest/KTestJUnitRunner.kt
Expand Up @@ -52,31 +52,29 @@ class KTestJUnitRunner(val testClass: Class<TestBase>) : Runner() {
return systemTags.isEmpty() || testcase.config.tags.isEmpty() || systemTags.intersect(testcase.config.tags).isNotEmpty()
}

private fun runTest(testcase: TestCase, notifier: RunNotifier, desc: Description): Unit {
private fun runTest(testcase: TestCase, notifier: RunNotifier, description: Description): Unit {

fun executorForTests(): ExecutorService =
if (testcase.config.threads < 2) Executors.newSingleThreadExecutor()
else Executors.newFixedThreadPool(testcase.config.threads)

val executor = executorForTests()
notifier.fireTestStarted(desc)
notifier.fireTestStarted(description)
for (j in 1..testcase.config.invocations) {
executor.submit {
try {
testcase.test()
} catch(e: Throwable) {
notifier.fireTestFailure(Failure(desc, e))
notifier.fireTestFailure(Failure(description, e))
}
}
}
notifier.fireTestFinished(desc)
notifier.fireTestFinished(description)
executor.shutdown()
if (testcase.config.timeout > 0) {
if (!executor.awaitTermination(testcase.config.timeout, testcase.config.timeoutUnit)) {
notifier.fireTestFailure(Failure(desc, TestTimedOutException(testcase.config.timeout, testcase.config.timeoutUnit)))
}
} else {
executor.awaitTermination(1, TimeUnit.DAYS)
val timeout = testcase.config.timeout
val terminated = executor.awaitTermination(timeout.amount, timeout.timeUnit)
if (!terminated) {
notifier.fireTestFailure(Failure(description, TestTimedOutException(timeout.amount, timeout.timeUnit)))
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/main/kotlin/io/kotlintest/specs/FlatSpec.kt
@@ -1,5 +1,6 @@
package io.kotlintest.specs

import io.kotlintest.Duration
import io.kotlintest.TestCase
import io.kotlintest.TestConfig
import io.kotlintest.TestSuite
Expand All @@ -14,12 +15,11 @@ abstract class FlatSpec : PropertyTesting() {

fun String.config(invocations: Int = 1,
ignored: Boolean = false,
timeout: Long = 0,
timeoutUnit: TimeUnit = TimeUnit.MILLISECONDS,
timeout: Duration = Duration.unlimited,
threads: Int = 1,
tag: String? = null,
tags: List<String> = listOf()): Pair<String, TestConfig> =
Pair(this, TestConfig(ignored, invocations, timeout, timeoutUnit, threads, tags))
Pair(this, TestConfig(ignored, invocations, timeout, threads, tags))

// allows us to write "name of test" { test here }
operator fun String.invoke(test: () -> Unit): Pair<String, () -> Unit> = Pair(this, test)
Expand Down
9 changes: 2 additions & 7 deletions src/main/kotlin/io/kotlintest/testcase.kt
@@ -1,7 +1,5 @@
package io.kotlintest

import java.util.concurrent.TimeUnit

data class TestSuite(val name: String, val nestedSuites: MutableList<TestSuite>, val cases: MutableList<io.kotlintest.TestCase>) {
companion object {
fun empty(name: String) = TestSuite(name, mutableListOf<TestSuite>(), mutableListOf<io.kotlintest.TestCase>())
Expand All @@ -10,8 +8,7 @@ data class TestSuite(val name: String, val nestedSuites: MutableList<TestSuite>,

data class TestConfig(var ignored: Boolean = false,
var invocations: Int = 1,
var timeout: Long = 0,
var timeoutUnit: TimeUnit = TimeUnit.MILLISECONDS,
var timeout: Duration = Duration.unlimited,
var threads: Int = 1,
var tags: List<String> = listOf())

Expand All @@ -22,15 +19,13 @@ data class TestCase(val suite: TestSuite,

fun config(invocations: Int = 1,
ignored: Boolean = false,
timeout: Long = 0,
timeoutUnit: TimeUnit = TimeUnit.MILLISECONDS,
timeout: Duration = Duration.unlimited,
threads: Int = 1,
tag: String? = null,
tags: List<String> = listOf()): Unit {
config.invocations = invocations
config.ignored = ignored
config.timeout = timeout
config.timeoutUnit = timeoutUnit
config.threads = threads
config.tags = tags
if (tag != null)
Expand Down