Skip to content

Commit

Permalink
Make parallel execution configurable (by default it is sequential) (#911
Browse files Browse the repository at this point in the history
)
  • Loading branch information
raniejade authored Aug 6, 2020
1 parent dea115e commit 408a6b9
Show file tree
Hide file tree
Showing 16 changed files with 91 additions and 83 deletions.
9 changes: 8 additions & 1 deletion .github/workflows/check-pr-core.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
include:
- os: ubuntu-latest
task: spekLinuxTest spekJvmTest
- os: macos-latest
task: spekMacosTest spekJvmTest
- os: windows-latest
task: spekWindowsTest spekJvmTest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
Expand All @@ -31,7 +38,7 @@ jobs:
- uses: eskatos/gradle-command-action@v1
with:
build-root-directory: integration-test
arguments: check -PexcludeIdePlugins
arguments: ${{ matrix.task }} -PexcludeIdePlugins -i
runner_junit5:
runs-on: ubuntu-latest
steps:
Expand Down
4 changes: 2 additions & 2 deletions buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ repositories {
}

dependencies {
api(kotlin("gradle-plugin", version = "1.4-M3"))
api("org.jetbrains.dokka:dokka-gradle-plugin:0.9.17")
api(kotlin("gradle-plugin", version = "1.4.0-rc"))
api("org.jetbrains.dokka:dokka-gradle-plugin:1.4.0-rc")
api("com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4")
api("gradle.plugin.org.jetbrains.intellij.plugins:gradle-intellij-plugin:0.4.10")
api("com.github.jengelman.gradle.plugins:shadow:4.0.2")
Expand Down
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/Dependencies.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ object Dependencies {
val mockitoCore = "org.mockito:mockito-core:2.23.4"
val mockitoInline = "org.mockito:mockito-inline:2.23.4"

val kotlinCoroutinesCore = "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7-1.4-M3"
val kotlinCoroutinesCore = "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8-1.4.0-rc"

val autoService = "com.google.auto.service:auto-service:1.0-rc4"
}
3 changes: 2 additions & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
kotlin.mpp.enableGranularSourceSetsMetadata=true
# This causes problems with composite builds!
# kotlin.mpp.enableGranularSourceSetsMetadata=true
org.gradle.jvmArgs=-Xmx3G
20 changes: 6 additions & 14 deletions integration-test/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,38 +10,29 @@ repositories {
}

kotlin {
metadata {}
jvm {}
js {}
linuxX64("linux") {}
macosX64("macos") {}
mingwX64("windows") {}

sourceSets {
val commonMain by getting {
dependencies {
implementation(kotlin("stdlib"))
}
}
val commonMain by getting

val commonTest by getting {
dependencies {
implementation("org.spekframework.spek2:spek-dsl")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8-1.4.0-rc")
implementation("org.spekframework.spek2:spek-runtime")
implementation(kotlin("test"))
}
}

jvm {
compilations["main"].defaultSourceSet {
dependencies {
implementation(kotlin("stdlib-jdk8"))
}
}

compilations["test"].defaultSourceSet {
dependencies {
runtimeOnly(kotlin("reflect"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.7-1.4-M3")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.8-1.4.0-rc")
implementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.0.0")
implementation("org.mockito:mockito-core:2.23.4")
runtimeOnly("org.spekframework.spek2:spek-runner-junit5")
Expand Down Expand Up @@ -112,7 +103,8 @@ spek2 {
tasks {
afterEvaluate {
val runSpekJvmTest by getting(Test::class) {
systemProperty("spek2.discovery.concurrent", "")
systemProperty("spek2.discovery.parallel.enabled", "")
systemProperty("spek2.execution.parallel.enabled", "")
filter {
includeTestsMatching("org.spekframework.spek2.*")
}
Expand Down
2 changes: 1 addition & 1 deletion integration-test/buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ repositories {
}

dependencies {
implementation(kotlin("gradle-plugin", version = "1.4-M3"))
implementation(kotlin("gradle-plugin", version = "1.4.0-rc"))
}
2 changes: 2 additions & 0 deletions integration-test/gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
# This causes problems with composite builds!
# kotlin.mpp.enableGranularSourceSetsMetadata=true
org.gradle.jvmargs=-Xmx3G
12 changes: 1 addition & 11 deletions integration-test/settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,18 +1,8 @@
includeBuild("../") {
dependencySubstitution {
substitute(module("org.spekframework.spek2:spek-gradle-plugin:0.1.0")).with(project(":spek-gradle-plugin"))
substitute(module("org.spekframework.spek2:spek-gradle-plugin")).with(project(":spek-gradle-plugin"))
substitute(module("org.spekframework.spek2:spek-dsl")).with(project(":spek-dsl"))
substitute(module("org.spekframework.spek2:spek-dsl-metadata")).with(project(":spek-dsl"))
substitute(module("org.spekframework.spek2:spek-dsl-jvm")).with(project(":spek-dsl"))
substitute(module("org.spekframework.spek2:spek-dsl-linux")).with(project(":spek-dsl"))
substitute(module("org.spekframework.spek2:spek-dsl-windows")).with(project(":spek-dsl"))
substitute(module("org.spekframework.spek2:spek-dsl-macos")).with(project(":spek-dsl"))
substitute(module("org.spekframework.spek2:spek-runtime")).with(project(":spek-runtime"))
substitute(module("org.spekframework.spek2:spek-runtime-metadata")).with(project(":spek-runtime"))
substitute(module("org.spekframework.spek2:spek-runtime-jvm")).with(project(":spek-runtime"))
substitute(module("org.spekframework.spek2:spek-runtime-linux")).with(project(":spek-runtime"))
substitute(module("org.spekframework.spek2:spek-runtime-windows")).with(project(":spek-runtime"))
substitute(module("org.spekframework.spek2:spek-runtime-macos")).with(project(":spek-runtime"))
substitute(module("org.spekframework.spek2:spek-runner-junit5")).with(project(":spek-runner-junit5"))
substitute(module("org.spekframework.spek2:spek-kotlin-compiler-plugin-jvm")).with(project(":spek-kotlin-compiler-plugin-jvm"))
substitute(module("org.spekframework.spek2:spek-kotlin-compiler-plugin-native")).with(project(":spek-kotlin-compiler-plugin-native"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ class SpekTestHelper {
val runtime = SpekRuntime()
val recorder = ExecutionRecorder()

val discoveryResult = runtime.discover(DiscoveryRequest(context, listOf(*paths)))
val discoveryResult = runtime.discoverAsync(DiscoveryRequest(context, listOf(*paths)))
runtime.executeAsync(ExecutionRequest(discoveryResult.roots, recorder))

return recorder
Expand Down
21 changes: 0 additions & 21 deletions spek-dsl/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -69,27 +69,6 @@ kotlin {
}
}
}

sourceSets {
val commonMain by getting {
dependencies {
implementation(kotlin("stdlib-common"))
api(Dependencies.kotlinCoroutinesCore)
}
}

val jvmMain by getting {
dependencies {
implementation(kotlin("stdlib-jdk8"))
}
}

val jsMain by getting {
dependencies {
implementation(kotlin("stdlib-js"))
}
}
}
}

val stubJavaDocJar by tasks.registering(Jar::class) {
Expand Down
13 changes: 11 additions & 2 deletions spek-runtime/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ kotlin {

}

js {
mavenPublication {
groupId = "org.spekframework.spek2"
artifactId = "spek-runtime-js"
pom {
name.set("Spek Runtime JS")
description.set("Kotlin JS module for spek-runtime")
}
}
}

linuxX64("linux") {
mavenPublication {
groupId = "org.spekframework.spek2"
Expand Down Expand Up @@ -64,15 +75,13 @@ kotlin {
val commonMain by getting {
dependencies {
api(project(":spek-dsl"))
implementation(kotlin("stdlib-common"))
implementation(Dependencies.kotlinCoroutinesCore)
}
}

jvm {
compilations["main"].defaultSourceSet {
dependencies {
implementation(kotlin("stdlib-jdk8"))
implementation(kotlin("reflect"))
implementation(Dependencies.classgraph)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.spekframework.spek2.runtime

import kotlinx.coroutines.*
import kotlinx.coroutines.withTimeoutOrNull
import org.spekframework.spek2.dsl.Skip
import org.spekframework.spek2.runtime.execution.ExecutionListener
import org.spekframework.spek2.runtime.execution.ExecutionRequest
Expand All @@ -15,7 +14,17 @@ import kotlin.coroutines.EmptyCoroutineContext
class Executor {
suspend fun execute(request: ExecutionRequest) {
request.executionListener.executionStart()
request.roots.forEach { execute(it, request.executionListener) }
// 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) {
e.printStackTrace()
}
}
}
request.executionListener.executionFinish()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,31 +41,39 @@ class SpekRuntime {
}
}

suspend fun discoverAsync(discoveryRequest: DiscoveryRequest): DiscoveryResult {
val scopes = mutableListOf<GroupScopeImpl>()
coroutineScope {
filterScopes(discoveryRequest).collect { scope ->
scopes.add(scope)
}
}

return DiscoveryResult(scopes)
}

@OptIn(ExperimentalTime::class)
fun discover(discoveryRequest: DiscoveryRequest): DiscoveryResult {
val scopes = mutableListOf<GroupScopeImpl>()
println("Spek discovery started.")
lateinit var results: DiscoveryResult
println("spek2: Discovery started.")
val time = measureTime {
doRunBlocking {
if (isConcurrentDiscoveryEnabled(false)) {
if (isParallelDiscoveryEnabled(false)) {
println("spek2: Running discovery phase in parallel.")
withContext(Dispatchers.Default) {
filterScopes(discoveryRequest).collect { scope ->
scopes.add(scope)
}
results = discoverAsync(discoveryRequest)
}
} else {
filterScopes(discoveryRequest).collect { scope ->
scopes.add(scope)
}
results = discoverAsync(discoveryRequest)
}
}
}

println("Spek discovery completed in ${time.inMilliseconds} ms")
return DiscoveryResult(scopes)
println("spek2: Discovery completed in ${time.inMilliseconds} ms")
return results
}


// For internal use only!
suspend fun executeAsync(request: ExecutionRequest) {
Executor().execute(request)
}
Expand All @@ -74,9 +82,16 @@ class SpekRuntime {
fun execute(request: ExecutionRequest) {
doRunBlocking {
val job = coroutineScope {
// inherit the dispatcher from doRunBlocking (which is an event loop backed by the main thread)
launch {
executeAsync(request)
if (isParallelExecutionEnabled(false)) {
println("spek2: Running execution phase in parallel.")
launch(Dispatchers.Default) {
executeAsync(request)
}
} else {
// inherit the dispatcher from doRunBlocking (which is an event loop backed by the main thread)
launch {
executeAsync(request)
}
}
}

Expand Down Expand Up @@ -121,5 +136,6 @@ class SpekRuntime {
}
}

expect fun isConcurrentDiscoveryEnabled(default: Boolean): Boolean
expect fun isParallelDiscoveryEnabled(default: Boolean): Boolean
expect fun isParallelExecutionEnabled(default: Boolean): Boolean
expect fun getGlobalTimeoutSetting(default: Long): Long
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import org.spekframework.spek2.runtime.discovery.DiscoveryContext
import kotlin.reflect.KClass
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.primaryConstructor
import kotlin.streams.toList

object JvmDiscoveryContextFactory {
// classgraph scan is already done in parallel, this property allows it to be overriden
Expand Down Expand Up @@ -50,7 +49,6 @@ object JvmDiscoveryContextFactory {
.enableClassInfo()
// any jar on the classpath won't be scanned
.disableJarScanning()
.verbose()

if (testDirs.isNotEmpty()) {
cg.overrideClasspath(System.getProperty("java.class.path"), *testDirs.toTypedArray())
Expand All @@ -66,7 +64,7 @@ object JvmDiscoveryContextFactory {
}

private fun filterScanResult(scanResult: ScanResult) : List<KClass<out Spek>> {
return scanResult.getSubclasses(Spek::class.qualifiedName!!).stream()
return scanResult.getSubclasses(Spek::class.qualifiedName!!)
.map { it.loadClass() as Class<out Spek> }
.filter { !it.isAnonymousClass }
.map { it.kotlin }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package org.spekframework.spek2.runtime

actual fun getGlobalTimeoutSetting(default: Long): Long {
var override = System.getProperty("SPEK_TIMEOUT")?.toLong()
if (override == null) {
override = System.getProperty("spek2.timeout")?.toLong()
}
var override = System.getProperty("spek2.execution.test.timeout")?.toLong()
return override ?: default
}

actual fun isConcurrentDiscoveryEnabled(default: Boolean): Boolean {
return System.getProperty("spek2.discovery.concurrent") != null || default
actual fun isParallelDiscoveryEnabled(default: Boolean): Boolean {
return System.getProperty("spek2.discovery.parallel.enabled") != null || default
}

actual fun isParallelExecutionEnabled(default: Boolean): Boolean {
return System.getProperty("spek2.execution.parallel.enabled") != null || default
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ actual fun getGlobalTimeoutSetting(default: Long): Long {
return default
}

actual fun isConcurrentDiscoveryEnabled(default: Boolean): Boolean {
actual fun isParallelDiscoveryEnabled(default: Boolean): Boolean {
return default
}

actual fun isParallelExecutionEnabled(default: Boolean): Boolean {
return default
}

0 comments on commit 408a6b9

Please sign in to comment.