Skip to content

Commit

Permalink
x
Browse files Browse the repository at this point in the history
  • Loading branch information
warnyul committed May 2, 2024
1 parent 3fa9788 commit ac8c5f8
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 44 deletions.
2 changes: 1 addition & 1 deletion robolectric-extension/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ test {
}
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.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 @@ -9,7 +9,9 @@ import org.junit.jupiter.api.extension.ExtensionContext
import org.junit.jupiter.api.extension.InvocationInterceptor
import org.junit.jupiter.api.extension.ReflectiveInvocationContext
import tech.apter.junit.jupiter.robolectric.internal.JUnit5RobolectricTestRunnerHelper
import tech.apter.junit.jupiter.robolectric.internal.validation.TestClassValidator
import tech.apter.junit.jupiter.robolectric.internal.extensions.createLogger
import tech.apter.junit.jupiter.robolectric.internal.validation.TestMethodValidator
import java.lang.reflect.Method
import kotlin.jvm.optionals.getOrNull

Expand All @@ -28,12 +30,14 @@ class RobolectricExtension :

override fun beforeAll(context: ExtensionContext) {
logger.trace { "beforeAll ${context.requiredTestClass.simpleName}" }
TestClassValidator(context.requiredTestClass).validate()
}

override fun beforeEach(context: ExtensionContext) {
logger.trace {
"beforeEach ${context.requiredTestClass.simpleName}::${context.requiredTestMethod.name}"
}
TestMethodValidator(context.requiredTestMethod).validate()
val testRunnerHelper = testRunnerHelper(context.requiredTestClass)
testRunnerHelper.beforeEach(context.requiredTestMethod)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,10 @@ package tech.apter.junit.jupiter.robolectric.internal

import com.google.common.annotations.VisibleForTesting
import org.junit.jupiter.api.extension.InvocationInterceptor
import org.junit.runners.model.FrameworkMethod
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode
import org.robolectric.annotation.LooperMode
import org.robolectric.internal.bytecode.Sandbox
import tech.apter.junit.jupiter.robolectric.RobolectricExtension
import tech.apter.junit.jupiter.robolectric.internal.extensions.createLogger
import tech.apter.junit.jupiter.robolectric.internal.extensions.isExtendedWithRobolectric
import tech.apter.junit.jupiter.robolectric.internal.extensions.isNestedTest
import java.lang.reflect.Method
import java.util.concurrent.Callable
import java.util.concurrent.ConcurrentHashMap
Expand All @@ -23,12 +18,6 @@ internal class JUnit5RobolectricTestRunnerHelper private constructor(testClass:
private var _robolectricTestRunner: JUnit5RobolectricTestRunner? = null

init {
validateNestedTestClassCanNotOverrideRuntimeSdk(testClass)
validateNestedTestClassCanNotApplyAnnotations(
testClass,
LooperMode::class.java,
GraphicsMode::class.java,
)
createTestEnvironmentForClass(testClass)
}

Expand All @@ -44,35 +33,6 @@ internal class JUnit5RobolectricTestRunnerHelper private constructor(testClass:
}
}

private fun validateNestedTestClassCanNotOverrideRuntimeSdk(testClass: Class<*>) {
val config = testClass.getAnnotation(Config::class.java)
if (testClass.isNestedTest && config != null && config.sdk.isNotEmpty()) {
error("Robolectric runtime sdk cannot be overwritten on a nested test class: ${testClass.name}")
}
}

private fun validateNestedTestClassCanNotApplyAnnotations(
testClass: Class<*>,
vararg annotationsClasses: Class<out Annotation>,
) {
annotationsClasses.forEach { annotationClass ->
val annotation = testClass.getAnnotation(annotationClass)
if (annotation != null) {
error("")
}
}
}

private fun validateNestedTestMethodsCanNotOverrideRuntimeSdk(testMethod: FrameworkMethod) {
val config = testMethod.getAnnotation(Config::class.java)
if (config != null && config.sdk.isNotEmpty()) {
error(
"Robolectric runtime sdk cannot be overwritten on a test method: " +
"${testMethod.declaringClass.simpleName}::${testMethod.name}"
)
}
}

private fun createTestEnvironmentForClass(testClass: Class<*>) {
_robolectricTestRunner = JUnit5RobolectricTestRunner(testClass)
}
Expand All @@ -81,7 +41,6 @@ internal class JUnit5RobolectricTestRunnerHelper private constructor(testClass:
val frameworkMethod = robolectricTestRunner.frameworkMethod(testMethod)
val sdkEnvironment = robolectricTestRunner.sdkEnvironment(frameworkMethod)
logger.trace { "$sdkEnvironment beforeEach(${testMethod.name})" }
validateNestedTestMethodsCanNotOverrideRuntimeSdk(frameworkMethod)
sdkEnvironment.runOnMainThreadWithRobolectric {
robolectricTestRunner.runBeforeTest(sdkEnvironment, frameworkMethod, testMethod)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package tech.apter.junit.jupiter.robolectric.internal.validation

import org.junit.jupiter.api.parallel.Execution
import org.junit.jupiter.api.parallel.ExecutionMode
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode
import org.robolectric.annotation.LooperMode
import tech.apter.junit.jupiter.robolectric.internal.extensions.isNestedTest

internal class TestClassValidator(private val testClass: Class<*>) {

fun validate() {
validateParallelModeDefault()
validateTestWithNestedTestsCanNotBeExecutedConcurrently(testClass)
validateNestedTestCanNotBeExecutedConcurrently(testClass)
validateNestedTestClassCanNotOverrideRuntimeSdk(testClass)
validateNestedTestClassCanNotApplyAnnotations(
testClass,
LooperMode::class.java,
GraphicsMode::class.java,
)
}

private fun validateNestedTestClassCanNotOverrideRuntimeSdk(testClass: Class<*>) {
val config = testClass.getAnnotation(Config::class.java)
if (testClass.isNestedTest && config != null && config.sdk.isNotEmpty()) {
error("Robolectric runtime sdk cannot be used on nested test class: ${testClass.name}")
}
}

private fun validateNestedTestClassCanNotApplyAnnotations(
testClass: Class<*>,
vararg annotationsClasses: Class<out Annotation>,
) {
annotationsClasses.forEach { annotationClass ->
val annotation = testClass.getAnnotation(annotationClass)
if (annotation != null) {
error("${annotationClass.simpleName} annotation cannot be used on a nested test class: ${testClass.name}")
}
}
}

private fun validateParallelModeDefault() {
val parallelModeDefault = System.getProperty("junit.jupiter.execution.parallel.mode.default")
if (parallelModeDefault == "concurrent") {
error("junit.jupiter.execution.parallel.mode.default=concurrent is not supported with Robolectric")
}
}

private fun validateTestWithNestedTestsCanNotBeExecutedConcurrently(
testClass: Class<*>,
) {
fun Class<*>.isDeclaredNestedTestClasses() = declaredClasses.any { it.isNestedTest }
fun Class<*>.isConcurrent() =
(System.getProperty("junit.jupiter.execution.parallel.mode.classes.default") == "concurrent" &&
executionMode() != ExecutionMode.SAME_THREAD) ||
executionMode() == ExecutionMode.CONCURRENT

if (testClass.declaringClass == null) {
if (testClass.isDeclaredNestedTestClasses() && testClass.isConcurrent()) {
error(
"${testClass.simpleName} must be annotated with @Execution(ExecutionMode.SAME_THREAD). " +
"Because it declared nested test classes. " +
"Or system property junit.jupiter.execution.parallel.mode.classes.default=same_thread must be set"
)
}
}
}

private fun validateNestedTestCanNotBeExecutedConcurrently(testClass: Class<*>) {
if (testClass.isNestedTest && testClass.executionMode() == ExecutionMode.CONCURRENT) {
error("Concurrent execution mode not allowed on test class: ${testClass.simpleName}")
}
}

private fun Class<*>.executionMode() = getAnnotation(Execution::class.java)?.value
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package tech.apter.junit.jupiter.robolectric.internal.validation

import org.junit.jupiter.api.parallel.Execution
import org.junit.jupiter.api.parallel.ExecutionMode
import org.robolectric.annotation.Config
import java.lang.reflect.Method

internal class TestMethodValidator(private val testMethod: Method) {

fun validate() {
validateTestMethodCanNotBeExecutedConcurrently(testMethod)
validateTestMethodsCanNotOverrideRuntimeSdk(testMethod)
}

private fun validateTestMethodsCanNotOverrideRuntimeSdk(testMethod: Method) {
val config = testMethod.getAnnotation(Config::class.java)
if (config != null && config.sdk.isNotEmpty()) {
error(
"Robolectric runtime sdk cannot be overwritten on a test method: " +
"${testMethod.declaringClass.simpleName}::${testMethod.name}"
)
}
}

private fun validateTestMethodCanNotBeExecutedConcurrently(testMethod: Method) {
if (testMethod.getAnnotation(Execution::class.java)?.value == ExecutionMode.CONCURRENT) {
error("${testMethod.name} cannot be annotated @Execution(ExecutionMode.CONCURRENT)")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import kotlin.test.assertContains
@ExtendWith(RobolectricExtension::class)
@Config(qualifiers = "fr")
@DisplayName("Given a test class with fr qualifier config")
@Execution(ExecutionMode.CONCURRENT)
@Execution(ExecutionMode.SAME_THREAD)
class RobolectricExtensionFrQualifierSelfTest {
@Test
fun `then runtime environment's qualifiers should contains fr`() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import kotlin.test.assertTrue
@ExtendWith(RobolectricExtension::class)
@Config(application = RobolectricExtensionSelfTest.MyTestApplication::class)
@DisplayName("Given a test class extended with robolectric")
@Execution(ExecutionMode.CONCURRENT)
@Execution(ExecutionMode.SAME_THREAD)
class RobolectricExtensionSelfTest {
@Test
fun shouldInitializeAndBindApplicationButNotCallOnCreate() {
Expand Down

0 comments on commit ac8c5f8

Please sign in to comment.