Skip to content

Commit

Permalink
Ensure that only a single test class is executed per thread at any gi…
Browse files Browse the repository at this point in the history
…ven time (#925)
  • Loading branch information
raniejade authored Sep 19, 2020
1 parent 2da7cfa commit 15d9746
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 18 deletions.
2 changes: 0 additions & 2 deletions integration-test/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,6 @@ spek2 {
tasks {
afterEvaluate {
val runSpekJvmTest by getting(Test::class) {
// enable concurrent discovery
systemProperty("spek2.discovery.concurrent", "")
filter {
includeTestsMatching("org.spekframework.spek2.*")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,29 @@ import org.spekframework.spek2.runtime.scope.ScopeImpl
import org.spekframework.spek2.runtime.scope.TestScopeImpl
import kotlin.coroutines.EmptyCoroutineContext

interface TestHandle {
fun await()
}

expect class TestRunner(concurrency: Int) {
fun runTest(test: suspend () -> Unit): TestHandle
}

class Executor {
suspend fun execute(request: ExecutionRequest) {
fun execute(request: ExecutionRequest, concurrency: Int) {
val runner = TestRunner(concurrency)
request.executionListener.executionStart()
// note that this call will be run in parallel depending on the CoroutineDispatcher used
supervisorScope {
request.roots.map { async { execute(it, request.executionListener) } }
.forEach { job ->
try {
job.await()
} catch (e: Throwable) {
println("An error has occurred: ${e.message}")
}
request.roots
.map {
runner.runTest { execute(it, request.executionListener) }
}
.forEach { handle ->
try {
handle.await()
} catch (e: Throwable) {
println("An error has occurred: ${e.message}")
}
}
}
request.executionListener.executionFinish()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,12 @@ class SpekRuntime {

fun execute(request: ExecutionRequest) {
doRunBlocking {
if (isConcurrentExecutionEnabled(false)) {
withContext(Dispatchers.Default) {
Executor().execute(request)
}
val concurrency = if (isConcurrentExecutionEnabled(false)) {
getExecutionParallelism()
} else {
Executor().execute(request)
1
}
Executor().execute(request, concurrency)
}
}

Expand Down Expand Up @@ -113,6 +112,7 @@ class SpekRuntime {
}

expect fun isConcurrentDiscoveryEnabled(default: Boolean): Boolean
expect fun getExecutionParallelism(): Int
expect fun isConcurrentExecutionEnabled(default: Boolean): Boolean
expect fun getGlobalTimeoutSetting(default: Long): Long

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,41 @@ package org.spekframework.spek2.runtime

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.runBlocking
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ExecutionException
import java.util.concurrent.Executors

actual fun doRunBlocking(block: suspend CoroutineScope.() -> Unit) {
runBlocking {
block()
}
}

actual class TestRunner actual constructor(concurrency: Int) {
private val executor = Executors.newFixedThreadPool(concurrency) { r ->
Thread(r).also {
it.isDaemon = true
}
}

actual fun runTest(test: suspend () -> Unit): TestHandle {
val handle = CompletableFuture<Unit>()

executor.submit {
runBlocking {
test()
handle.complete(null)
}
}

return object : TestHandle {
override fun await() {
try {
handle.get()
} catch (e: ExecutionException) {
throw e.cause!!
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,8 @@ actual fun isConcurrentExecutionEnabled(default: Boolean): Boolean {

actual fun measureTime(block: () -> Unit): Long {
return measureTimeMillis(block)
}

actual fun getExecutionParallelism(): Int {
return Runtime.getRuntime().availableProcessors()
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,17 @@ actual fun doRunBlocking(block: suspend CoroutineScope.() -> Unit) {
block()
}
}

actual class TestRunner actual constructor(concurrency: Int) {
actual fun runTest(test: suspend () -> Unit): TestHandle {
doRunBlocking {
test()
}

return object : TestHandle {
override fun await() {
// do nothing
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,8 @@ actual fun isConcurrentExecutionEnabled(default: Boolean): Boolean {
@UseExperimental(ExperimentalTime::class)
actual fun measureTime(block: () -> Unit): Long {
return MonoClock.measureTime(block).inMilliseconds.toLong()
}

actual fun getExecutionParallelism(): Int {
return 1
}

0 comments on commit 15d9746

Please sign in to comment.