mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-08 15:09:39 +07:00
[platform testFramework] provide ExtensionContext instead of the uniqueId in testFixtures
Merge-request: IJ-MR-150244 Merged-by: Shumaf Lovpache <soarex16@gmail.com> GitOrigin-RevId: 0760acef462e162d66b7d7465f55e1c2ba1939c8
This commit is contained in:
committed by
intellij-monorepo-bot
parent
0649285d74
commit
4d76d2cac6
25
platform/testFramework/junit5/src/fixture/TestContextImpl.kt
Normal file
25
platform/testFramework/junit5/src/fixture/TestContextImpl.kt
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.testFramework.junit5.fixture
|
||||
|
||||
import org.junit.jupiter.api.extension.ExtensionContext
|
||||
import org.junit.platform.commons.support.AnnotationSupport
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
|
||||
internal class TestContextImpl(private val context: ExtensionContext) : TestContext {
|
||||
override val uniqueId: String
|
||||
get() = context.uniqueId
|
||||
|
||||
override val testName: String
|
||||
get() = context.displayName
|
||||
|
||||
override fun <T : Annotation> findAnnotation(clazz: Class<T>): T? {
|
||||
var extContext: ExtensionContext? = context
|
||||
while (extContext != null) {
|
||||
extContext.element.flatMap { AnnotationSupport.findAnnotation(it, clazz) }.getOrNull()?.let {
|
||||
return it
|
||||
}
|
||||
extContext = extContext.parent.getOrNull()
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@ internal class TestFixtureExtension : BeforeAllCallback,
|
||||
for (field in fields) {
|
||||
field.isAccessible = true
|
||||
val fixture = field.get(testInstance) as TestFixtureImpl<*>
|
||||
pendingFixtures.add(fixture.init(testScope, context.uniqueId))
|
||||
pendingFixtures.add(fixture.init(testScope, TestContextImpl(context)))
|
||||
}
|
||||
awaitFixtureInitialization(testScope, pendingFixtures)
|
||||
context.getStore(ExtensionContext.Namespace.GLOBAL).put("TestFixtureExtension", testScope)
|
||||
|
||||
@@ -32,17 +32,17 @@ internal class TestFixtureImpl<T>(
|
||||
}
|
||||
}
|
||||
|
||||
fun init(testScope: CoroutineScope, uniqueId: String): Deferred<ScopedValue<T>> {
|
||||
fun init(testScope: CoroutineScope, context: TestContext): Deferred<ScopedValue<T>> {
|
||||
val state = _state
|
||||
if (state !is TestFixtureInitializer<*>) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return state as Deferred<ScopedValue<T>>
|
||||
}
|
||||
return initSync(testScope, uniqueId)
|
||||
return initSync(testScope, context)
|
||||
}
|
||||
|
||||
@Synchronized // for simplicity; can be made atomic if needed
|
||||
private fun initSync(testScope: CoroutineScope, uniqueId: String): Deferred<ScopedValue<T>> {
|
||||
private fun initSync(testScope: CoroutineScope, context: TestContext): Deferred<ScopedValue<T>> {
|
||||
val state = _state
|
||||
if (state !is TestFixtureInitializer<*>) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@@ -58,10 +58,10 @@ internal class TestFixtureImpl<T>(
|
||||
testScope.launch(CoroutineName(debugString)) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val initializer = state as TestFixtureInitializer<T>
|
||||
val scope = TestFixtureInitializerReceiverImpl<T>(testScope, uniqueId)
|
||||
val scope = TestFixtureInitializerReceiverImpl<T>(testScope, context)
|
||||
val (fixture, tearDown) = try {
|
||||
with(initializer) {
|
||||
scope.initFixture(uniqueId) as InitializedTestFixtureData<T>
|
||||
scope.initFixture(context) as InitializedTestFixtureData<T>
|
||||
}
|
||||
}
|
||||
catch (t: Throwable) {
|
||||
@@ -95,7 +95,7 @@ private typealias ScopedValue<T> = Pair<T, CoroutineScope>
|
||||
|
||||
private class TestFixtureInitializerReceiverImpl<T>(
|
||||
private val testScope: CoroutineScope,
|
||||
private val uniqueId: String,
|
||||
private val context: TestContext,
|
||||
) : TestFixtureInitializer.R<T> {
|
||||
|
||||
/**
|
||||
@@ -104,7 +104,7 @@ private class TestFixtureInitializerReceiverImpl<T>(
|
||||
private val _dependencies = LinkedHashSet<CoroutineScope>()
|
||||
|
||||
override suspend fun <T> TestFixture<T>.init(): T {
|
||||
val (fixture, fixtureScope) = (this as TestFixtureImpl<T>).init(testScope, uniqueId).await()
|
||||
val (fixture, fixtureScope) = (this as TestFixtureImpl<T>).init(testScope, context).await()
|
||||
_dependencies.add(fixtureScope)
|
||||
return fixture
|
||||
}
|
||||
|
||||
@@ -74,11 +74,11 @@ fun projectFixture(
|
||||
@TestOnly
|
||||
fun TestFixture<Project>.moduleFixture(
|
||||
name: String? = null,
|
||||
): TestFixture<Module> = testFixture(name ?: "unnamed module") { id ->
|
||||
): TestFixture<Module> = testFixture(name ?: "unnamed module") { context ->
|
||||
val project = this@moduleFixture.init()
|
||||
val manager = ModuleManager.getInstance(project)
|
||||
val module = writeAction {
|
||||
manager.newNonPersistentModule(name ?: id, "")
|
||||
manager.newNonPersistentModule(name ?: context.uniqueId, "")
|
||||
}
|
||||
initialized(module) {
|
||||
writeAction {
|
||||
@@ -123,8 +123,8 @@ fun TestFixture<Project>.moduleFixture(
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
fun disposableFixture(): TestFixture<Disposable> = testFixture { debugString ->
|
||||
val disposable = Disposer.newCheckedDisposable(debugString)
|
||||
fun disposableFixture(): TestFixture<Disposable> = testFixture { context ->
|
||||
val disposable = Disposer.newCheckedDisposable(context.uniqueId)
|
||||
initialized(disposable) {
|
||||
Disposer.dispose(disposable)
|
||||
}
|
||||
|
||||
@@ -38,18 +38,33 @@ sealed interface TestFixture<out T> {
|
||||
fun get(): T
|
||||
}
|
||||
|
||||
sealed interface TestContext {
|
||||
/**
|
||||
* Unique test or container ID, for example [org.junit.jupiter.api.extension.ExtensionContext.getUniqueId]
|
||||
*/
|
||||
val uniqueId: String
|
||||
|
||||
/**
|
||||
* Display name for the current test or container, for example [org.junit.jupiter.api.extension.ExtensionContext.getDisplayName]
|
||||
*/
|
||||
val testName: String
|
||||
|
||||
/**
|
||||
* Returns the annotation with which a test or container is marked or null if there is none.
|
||||
*/
|
||||
fun <T : Annotation> findAnnotation(clazz: Class<T>): T?
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the business logic for building a fixture (or resource) and destroying it.
|
||||
*/
|
||||
fun interface TestFixtureInitializer<T> {
|
||||
|
||||
/**
|
||||
* @param uniqueId unique test or container ID, same as [org.junit.jupiter.api.extension.ExtensionContext.getUniqueId]
|
||||
*
|
||||
* TODO consider passing whole ExtensionContext here
|
||||
* @param context [TestContext] which provides information about the test and allows to query annotations
|
||||
*/
|
||||
@OverrideOnly
|
||||
suspend fun R<T>.initFixture(uniqueId: String): InitializedTestFixture<T>
|
||||
suspend fun R<T>.initFixture(context: TestContext): InitializedTestFixture<T>
|
||||
|
||||
sealed interface InitializedTestFixture<T>
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.testFramework.junit5.fixture
|
||||
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNotNull
|
||||
import org.junit.jupiter.api.DynamicTest
|
||||
import org.junit.jupiter.api.DynamicTest.dynamicTest
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestFactory
|
||||
|
||||
private fun myFixture(): TestFixture<MyAnnotation?> = testFixture { context ->
|
||||
initialized(context.findAnnotation(MyAnnotation::class.java)) { }
|
||||
}
|
||||
|
||||
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
|
||||
private annotation class MyAnnotation(val x: Int = 0)
|
||||
|
||||
private fun doTestContextTest(fixture: TestFixture<MyAnnotation?>, expectedValue: Int) {
|
||||
val annotation = fixture.get()
|
||||
assertNotNull(annotation)
|
||||
assertEquals(expectedValue, annotation!!.x)
|
||||
}
|
||||
|
||||
@MyAnnotation(x = 1)
|
||||
@TestFixtures
|
||||
class FixtureContextTest {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
private val classLevelFixture = myFixture()
|
||||
}
|
||||
|
||||
private val testLevelFixture = myFixture()
|
||||
|
||||
@Test
|
||||
fun `class level annotation with class level fixture`() = doTestContextTest(classLevelFixture, 1)
|
||||
|
||||
@Test
|
||||
fun `class level annotation with test level fixture`() = doTestContextTest(testLevelFixture, 1)
|
||||
|
||||
@Test
|
||||
@MyAnnotation(x = 2)
|
||||
fun `test level annotation with class level fixture`() = doTestContextTest(classLevelFixture, 1)
|
||||
|
||||
@Test
|
||||
@MyAnnotation(x = 2)
|
||||
fun `test level annotation with test level fixture`() = doTestContextTest(testLevelFixture, 2)
|
||||
|
||||
@TestFactory
|
||||
@MyAnnotation(x = 3)
|
||||
fun `dynamic test`(): List<DynamicTest> {
|
||||
return buildList {
|
||||
repeat(3) {
|
||||
add(dynamicTest("test $it") {
|
||||
doTestContextTest(testLevelFixture, 3)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@MyAnnotation(x = 4)
|
||||
inner class InnerClass1 {
|
||||
@Test
|
||||
fun `inner class level annotation with outer class level fixture`() = doTestContextTest(classLevelFixture, 1)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class InnerClass2 {
|
||||
private val testLevelFixture = myFixture()
|
||||
|
||||
@Test
|
||||
fun `outer class level annotation with test level fixture`() = doTestContextTest(classLevelFixture, 1)
|
||||
|
||||
@Test
|
||||
@MyAnnotation(x = 5)
|
||||
fun `test level annotation with inner test level fixture`() = doTestContextTest(testLevelFixture, 5)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user