Skip to content

Commit

Permalink
chore: Enable concurrent test execution for test classes
Browse files Browse the repository at this point in the history
  • Loading branch information
warnyul committed May 2, 2024
1 parent 0047084 commit 021fb89
Show file tree
Hide file tree
Showing 4 changed files with 16 additions and 25 deletions.
4 changes: 2 additions & 2 deletions robolectric-extension/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ test {
showStackTraces = true
}
systemProperty 'junit.jupiter.execution.parallel.enabled', 'true'
systemProperty 'junit.jupiter.execution.parallel.mode.default ', 'same_thread'
systemProperty 'junit.jupiter.execution.parallel.mode.classes.default', 'same_thread'
systemProperty 'junit.jupiter.execution.parallel.mode.default ', 'concurrent'
systemProperty 'junit.jupiter.execution.parallel.mode.classes.default', 'concurrent'
systemProperty 'java.util.logging.config.file',
"${projectDir}/src/test/resources/logging.properties"
systemProperty 'robolectric.usePreinstrumentedJars', 'true'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ import tech.apter.junit.jupiter.robolectric.internal.extensions.hasTheSameParame
import tech.apter.junit.jupiter.robolectric.internal.extensions.outerMostDeclaringClass
import java.lang.reflect.Method
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.locks.Lock
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.withLock

internal data class TestClassContainer(val testClass: Class<*>)

Expand All @@ -32,7 +29,6 @@ internal class JUnit5RobolectricTestRunner(
) : RobolectricTestRunner(clazz, injector) {
private inline val logger get() = createLogger()
private val childrenCache = mutableListOf<FrameworkMethod>()
private val sandboxLock = Any()

override fun getChildren(): MutableList<FrameworkMethod> {
if (childrenCache.isEmpty()) {
Expand Down Expand Up @@ -60,12 +56,10 @@ internal class JUnit5RobolectricTestRunner(
}

override fun getSandbox(method: FrameworkMethod): AndroidSandbox {
synchronized(sandboxLock) {
val originalClassLoader = Thread.currentThread().contextClassLoader
Thread.currentThread().contextClassLoader = createParentClassLoader(testClass.javaClass)
return super.getSandbox(method).also {
Thread.currentThread().contextClassLoader = originalClassLoader
}
val originalClassLoader = Thread.currentThread().contextClassLoader
Thread.currentThread().contextClassLoader = createParentClassLoader(testClass.javaClass)
return super.getSandbox(method).also {
Thread.currentThread().contextClassLoader = originalClassLoader
}
}

Expand All @@ -74,7 +68,7 @@ internal class JUnit5RobolectricTestRunner(
frameworkMethod: FrameworkMethod,
bootstrappedMethod: Method,
) {
beforeTestLock(testClass.javaClass).withLock {
synchronized(beforeTestLock) {
logger.trace { "runBeforeTest ${bootstrappedMethod.declaringClass.simpleName}::${bootstrappedMethod.name}" }
super.beforeTest(sdkEnvironment, frameworkMethod, bootstrappedMethod)
}
Expand Down Expand Up @@ -130,26 +124,25 @@ internal class JUnit5RobolectricTestRunner(
) = validatePublicVoidNoArgJUnit5Methods(annotation, isStatic, errors)
}

private companion object {
private val beforeTestLocks = ConcurrentHashMap<String, ReentrantReadWriteLock>()
internal companion object {
private val beforeTestLock = Any()
private val sdkSandboxParentClassLoaderCache = ConcurrentHashMap<String, ClassLoader>()

private fun defaultInjectorBuilder() =
defaultInjector().bind(SandboxBuilder::class.java, JUnit5RobolectricSandboxBuilder::class.java)
.bind(MavenDependencyResolver::class.java, JUnit5MavenDependencyResolver::class.java)
.bind(SandboxManager::class.java, JUnit5RobolectricSandboxManager::class.java)

private fun beforeTestLock(testClass: Class<*>): Lock =
beforeTestLocks.getOrPut(testClass.outerMostDeclaringClass().name) {
ReentrantReadWriteLock()
}.writeLock()

private fun createParentClassLoader(testClass: Class<*>) =
sdkSandboxParentClassLoaderCache.getOrPut(testClass.outerMostDeclaringClass().name) {
createLogger().trace {
"parent class loader created for ${testClass.name.substringAfterLast('.')}"
}
SdkSandboxParentClassLoader(Thread.currentThread().contextClassLoader)
}

internal fun cleanCache() {
sdkSandboxParentClassLoaderCache.clear()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,9 @@ internal class JUnit5RobolectricTestRunnerHelper private constructor(testClass:
)
}

internal fun reset() {
internal fun shutdown() {
checkNotNull(interceptedClassLoader) { "interceptedClassLoader is not yet set" }
helperRunnerCache.values.forEach {
it.clearCachedRobolectricTestRunnerEnvironment()
}
JUnit5RobolectricTestRunner.cleanCache()
helperRunnerCache.clear()
Thread.currentThread().contextClassLoader = interceptedClassLoader
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class RobolectricLauncherInterceptor : LauncherInterceptor {

override fun close() {
logger.trace { "close" }
JUnit5RobolectricTestRunnerHelper.reset()
JUnit5RobolectricTestRunnerHelper.shutdown()
classLoaderReplaced.set(false)
}

Expand Down

0 comments on commit 021fb89

Please sign in to comment.