Rewrite "resource"-based JUnit5 tests into "fixture" based.

See `com.intellij.testFramework.junit5.showcase.resources` and use it as examples to create JUnit5-based tests

GitOrigin-RevId: 92c2831205d7bad28b1500f9fa510b93fed52e19
This commit is contained in:
Ilya.Kazakevich
2024-07-01 18:00:58 +02:00
committed by intellij-monorepo-bot
parent ab55356a54
commit 87e468e994
14 changed files with 96 additions and 596 deletions

View File

@@ -1,32 +0,0 @@
// 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.showcase.resources.disposable
import com.intellij.openapi.Disposable
import com.intellij.openapi.util.Disposer
import com.intellij.testFramework.junit5.TestApplication
import com.intellij.testFramework.junit5.resources.DisposableResource
import com.intellij.testFramework.junit5.showcase.resources.disposable.JUnit5ClassLevelFieldResource.Companion.disposable1
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Test
/**
* Class-level [Disposable] injected both into [disposable1] and arguments
*/
@TestApplication
@DisposableResource
class JUnit5ClassLevelFieldResource {
companion object {
@JvmStatic
private lateinit var disposable1: Disposable
}
@Test
fun test(disposable2: Disposable) {
assertNotNull(disposable1)
assertNotNull(disposable2)
assertEquals(disposable1, disposable1)
Disposer.register(disposable1, Disposer.newCheckedDisposable())
Disposer.register(disposable2, Disposer.newCheckedDisposable())
}
}

View File

@@ -1,63 +0,0 @@
// 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.showcase.resources.disposable
import com.intellij.openapi.Disposable
import com.intellij.openapi.util.Disposer
import com.intellij.testFramework.junit5.resources.ResourceExtensionApi
import com.intellij.testFramework.junit5.resources.asExtension
import com.intellij.testFramework.junit5.resources.create
import com.intellij.testFramework.junit5.resources.providers.DisposableProvider
import com.intellij.testFramework.junit5.showcase.resources.ResourceCounter
import com.intellij.testFramework.junit5.showcase.resources.disposable.JUnit5ClassLevelResource.Companion.checkLeaks
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotEquals
import org.junit.jupiter.api.Order
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.AfterAllCallback
import org.junit.jupiter.api.extension.RegisterExtension
/**
* Class level [Disposable] created programmatically.
* [checkLeaks] will be created before and destroyed ([AfterAllCallback]) after
*/
class JUnit5ClassLevelResource {
companion object {
@JvmStatic
@RegisterExtension
@Order(0)
val checkLeaks = AfterAllCallback {
resourceCounter.ensureEmpty()
}
@JvmStatic
@RegisterExtension
@Order(1)
val ext = DisposableProvider().asExtension()
private val resourceCounter = ResourceCounter()
}
@Test
fun test(disposable1: Disposable, disposable2: Disposable) {
assertEquals(disposable1, disposable2, "Class level must use same resource")
resourceCounter.acquire(disposable1)
resourceCounter.ensureNotEmpty()
}
@Test
fun testManualDestroy(): Unit = runBlocking {
val disposable = ext.create(disposeOnExit = false)
Disposer.dispose(disposable)
}
@Test
fun testManualResource(): Unit = runBlocking {
resourceCounter.acquire(ext.create())
// Manually created resources are different
assertNotEquals(ext.create(), ext.create())
}
}

View File

@@ -1,27 +0,0 @@
// 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.showcase.resources.disposable
import com.intellij.openapi.Disposable
import com.intellij.openapi.diagnostic.thisLogger
import com.intellij.openapi.util.Disposer
import com.intellij.testFramework.junit5.TestApplication
import com.intellij.testFramework.junit5.resources.DisposableResource
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
/**
* Class level disposable in automatic manner
*/
@TestApplication
@DisposableResource
class JUnit5ClassLevelResourceAnnotation {
@Test
fun test(disposable1: Disposable, disposable2: Disposable) {
assertEquals(disposable1, disposable2, "Class level must use same resource")
Disposer.register(disposable1, Disposable {
thisLogger().info("It must be disposed. Otherwise, leak would be reported")
})
}
}

View File

@@ -1,59 +0,0 @@
// 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.showcase.resources.disposable
import com.intellij.openapi.Disposable
import com.intellij.testFramework.junit5.TestApplication
import com.intellij.testFramework.junit5.resources.ResourceExtensionApi
import com.intellij.testFramework.junit5.resources.asExtension
import com.intellij.testFramework.junit5.resources.create
import com.intellij.testFramework.junit5.resources.providers.DisposableProvider
import com.intellij.testFramework.junit5.showcase.resources.ResourceCounter
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Order
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.AfterEachCallback
import org.junit.jupiter.api.extension.RegisterExtension
/**
* [Disposable] created on method level and disposed after each test.
* [checkLeaks] proofs it
*/
@TestApplication
class JUnit5MethodLevelResource {
@JvmField
@RegisterExtension
@Order(0)
val checkLeaks = AfterEachCallback {
resourceCounter.ensureEmpty()
}
@JvmField
@RegisterExtension
@Order(1)
val ext = DisposableProvider().asExtension()
private val resourceCounter = ResourceCounter()
private lateinit var disposableField: Disposable
@Test
fun test(disposable1: Disposable, disposable2: Disposable) {
assertEquals(disposable1, disposable2, "Instance level must use same resource")
assertEquals(disposable1, disposableField, "Instance level must use same resource")
resourceCounter.acquire(disposable1)
runBlocking {
resourceCounter.acquire(ext.create())
}
resourceCounter.ensureNotEmpty()
}
@Test
fun test2(disposable: Disposable) {
resourceCounter.ensureEmpty() // Method-level resource should already be destroyed
resourceCounter.acquire(disposable)
resourceCounter.acquire(disposableField)
}
}

View File

@@ -1,51 +0,0 @@
// 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.showcase.resources.full
import com.intellij.openapi.application.writeAction
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.findOrCreateFile
import com.intellij.openapi.vfs.writeText
import com.intellij.psi.PsiManager
import com.intellij.testFramework.junit5.resources.FullApplication
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir
import java.nio.file.Path
@Service(Service.Level.PROJECT)
private class MyService(val project: Project)
/**
* Test that gets all resources in a fully automated manner, thanks to [FullApplication]
*/
@FullApplication
class JUnit5Automatic {
@Test
/**
* [tempDir] is a JUnit5 temporary directory here
*/
fun funProjectModuleEditor(module: Module, project: Project, @TempDir tempDir: Path): Unit = runBlocking {
assertEquals(module.project, project.service<MyService>().project)
writeAction {
val file = LocalFileSystem.getInstance().findFileByNioFile(tempDir)!!
.findOrCreateFile("file.txt")
.apply { writeText("hello") }
FileEditorManager.getInstance(project).openFile(file).first()
Assertions.assertNotNull(PsiManager.getInstance(project).findFile(file)?.fileDocument)
}
}
@Test
fun funModule(module: Module) {
assertFalse(module.isDisposed)
}
}

View File

@@ -1,111 +0,0 @@
// 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.showcase.resources.full
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.guessProjectDir
import com.intellij.testFramework.junit5.TestApplication
import com.intellij.testFramework.junit5.resources.asExtension
import com.intellij.testFramework.junit5.resources.create
import com.intellij.testFramework.junit5.resources.providers.PathInfo
import com.intellij.testFramework.junit5.resources.providers.ProjectProvider
import com.intellij.testFramework.junit5.resources.providers.module.ModuleName
import com.intellij.testFramework.junit5.resources.providers.module.ModuleParams
import com.intellij.testFramework.junit5.resources.providers.module.ModulePersistenceType.Persistent
import com.intellij.testFramework.junit5.resources.providers.module.ModuleProvider
import com.intellij.testFramework.junit5.resources.providers.module.ProjectSource.ExplicitProject
import com.intellij.testFramework.junit5.resources.providers.module.ProjectSource.ProjectFromExtension
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Order
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.AfterEachCallback
import org.junit.jupiter.api.extension.RegisterExtension
import org.junit.jupiter.api.io.TempDir
import java.nio.file.Files
import java.nio.file.Path
import kotlin.io.path.name
import kotlin.io.path.pathString
/**
* Everything created manually here for fine-tuning freaks.
* [allModulesClearListener] checks that modules do not leak
*/
@TestApplication
class JUnit5Manually {
@JvmField
@Order(0)
@RegisterExtension
val allModulesClearListener = AfterEachCallback {
assertTrue(modules.all { it.isDisposed })
assertTrue(modules.all { it.project.isDisposed })
}
private val projectName = "myProject${hashCode()}"
private val explicitProjectPath = Files.createTempDirectory(projectName)
private val explicitModulePath = Files.createTempDirectory(projectName)
@JvmField
@RegisterExtension
@Order(1)
val projectExt = ProjectProvider { PathInfo(explicitProjectPath) }.asExtension()
@JvmField
@RegisterExtension
@Order(2)
val moduleExt = ModuleProvider {
ModuleParams(
name = ModuleName("${Math.random()}MyModule${Math.random()}"),
modulePersistenceType = Persistent { _, name ->
PathInfo(explicitModulePath.resolve(name.name), deletePathOnExit = true, closeFsOnExit = false)
},
projectSource = ProjectFromExtension
)
}.asExtension()
private val modules = mutableListOf<Module>()
@AfterEach
fun deletePath() {
Files.deleteIfExists(explicitProjectPath)
Files.deleteIfExists(explicitModulePath)
}
@Test
fun ensureModuleObeyParams(module: Module) {
assertTrue(module.moduleNioFile.pathString.startsWith(explicitModulePath.pathString)) {
"Module $module sits in a wrong dir"
}
}
@Test
fun ensureAutoProjectObeysParams(project: Project) {
val projectDir = project.guessProjectDir()!!.toNioPath().name
assertTrue(projectDir.startsWith(projectName)) {
"$projectDir is not correct"
}
}
@Test
fun createManually(@TempDir projectPath: Path): Unit = runBlocking {
val project1 = projectExt.create(PathInfo((projectPath)))
val project2 = projectExt.create(PathInfo(projectPath))
assertNotEquals(project1, project2)
val module = moduleExt.create(ModuleParams(
name = ModuleName("MyModule"),
modulePersistenceType = Persistent(),
projectSource = ExplicitProject(project1)
))
assertEquals(module.project, project1)
val module2 = moduleExt.create()
assertNotEquals(module, module2)
modules.add(module)
modules.add(module2)
}
}

View File

@@ -1,39 +0,0 @@
// 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.showcase.resources.full
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.testFramework.junit5.TestApplication
import com.intellij.testFramework.junit5.resources.ProjectResource
import com.intellij.testFramework.junit5.resources.ResourceExtensionApi
import com.intellij.testFramework.junit5.resources.providers.module.ModuleName
import com.intellij.testFramework.junit5.resources.providers.module.ModuleParams
import com.intellij.testFramework.junit5.resources.providers.module.ModuleProvider
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.RegisterExtension
/**
* Class-level [Module] with custom name
*/
@TestApplication
@ProjectResource
class JUnit5SemiAutomatic {
companion object {
private val ID = hashCode().toString()
@JvmStatic
@RegisterExtension
val moduleEx = ResourceExtensionApi.forProvider(ModuleProvider {
ModuleParams(name = ModuleName("$ID-MyTest"))
})
}
@Test
fun funProjectModule(module: Module, project: Project) {
assertEquals(module.project, project)
assertTrue(module.name.startsWith(ID))
}
}

View File

@@ -1,78 +1,28 @@
// 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.showcase.resources.module
import com.intellij.openapi.application.writeAction
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleManager
import com.intellij.testFramework.junit5.TestApplication
import com.intellij.testFramework.junit5.resources.ProjectResource
import com.intellij.testFramework.junit5.resources.ResourceExtensionApi
import com.intellij.testFramework.junit5.resources.create
import com.intellij.testFramework.junit5.resources.providers.PathInfo
import com.intellij.testFramework.junit5.resources.providers.module.ModuleName
import com.intellij.testFramework.junit5.resources.providers.module.ModuleParams
import com.intellij.testFramework.junit5.resources.providers.module.ModulePersistenceType.Persistent
import com.intellij.testFramework.junit5.resources.providers.module.ModuleProvider
import com.intellij.testFramework.junit5.showcase.resources.ResourceCounter
import com.intellij.testFramework.junit5.showcase.resources.module.JUnit5ClassLevelModule.Companion.counter
import kotlinx.coroutines.runBlocking
import com.intellij.testFramework.junit5.fixture.moduleFixture
import com.intellij.testFramework.junit5.fixture.projectFixture
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Order
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.AfterAllCallback
import org.junit.jupiter.api.extension.RegisterExtension
import org.junit.jupiter.api.io.TempDir
import java.nio.file.Path
/**
* [Module] created on class-level. Since module needs project, set [ProjectResource].
* [counter] ensures modules are disposed
*/
@TestApplication
@ProjectResource
class JUnit5ClassLevelModule {
companion object {
@Order(0)
@JvmStatic
@RegisterExtension
val leak = AfterAllCallback {
counter.ensureEmpty()
}
private val projectFixture = projectFixture()
private val moduleFixture = projectFixture.moduleFixture()
@JvmStatic
@RegisterExtension
@Order(1)
val ext = ResourceExtensionApi.forProvider(ModuleProvider())
private val counter = ResourceCounter()
}
@Test
fun test(module1: Module, module2: Module) {
fun test() {
val module1 = moduleFixture.get()
val module2 = moduleFixture.get()
Assertions.assertEquals(module1, module2, "Class level modules must be same")
Assertions.assertFalse(module1.isDisposed)
}
@Test
fun testManualDestroy(@TempDir tempDir: Path): Unit = runBlocking {
val module = ext.create(ModuleParams(modulePersistenceType = Persistent { _, _ -> PathInfo(tempDir.resolve("module.iml")) }), disposeOnExit = false)
Assertions.assertEquals(tempDir, module.moduleNioFile.parent)
counter.acquire(module)
writeAction {
ModuleManager.getInstance(module.project).disposeModule(module)
}
}
@Test
fun testManualResource(): Unit = runBlocking {
// Manually created projects are always different
val m1 = ext.create()
val m2 = ext.create()
val m3 = ext.create(ModuleParams(ModuleName("abc123")))
Assertions.assertNotEquals(m1, m2)
Assertions.assertNotEquals(m1, m3)
}
}

View File

@@ -3,38 +3,33 @@ package com.intellij.testFramework.junit5.showcase.resources.module
import com.intellij.openapi.module.Module
import com.intellij.testFramework.junit5.TestApplication
import com.intellij.testFramework.junit5.resources.NoInject
import com.intellij.testFramework.junit5.resources.ProjectResource
import com.intellij.testFramework.junit5.resources.asExtension
import com.intellij.testFramework.junit5.resources.providers.module.ModuleProvider
import com.intellij.testFramework.junit5.fixture.moduleFixture
import com.intellij.testFramework.junit5.fixture.projectFixture
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.RegisterExtension
/**
* Method-level [Module]s are destroyed after each test
*/
@TestApplication
@ProjectResource
class JUnit5MethodLevelModule {
@JvmField
@RegisterExtension
val ext = ModuleProvider().asExtension()
private val moduleFixture = projectFixture().moduleFixture()
@NoInject
private var moduleFromPreviousTest: Module? = null
@Test
fun test(module1: Module, module2: Module) {
fun test() {
val module1 = moduleFixture.get()
val module2 = moduleFixture.get()
Assertions.assertEquals(module1, module2, "Method level modules must be same")
Assertions.assertFalse(module1.isDisposed)
ensureNotModuleFromPrevTest(module1)
}
@Test
fun testDifferentModule(module: Module) {
ensureNotModuleFromPrevTest(module)
fun testDifferentModule() {
ensureNotModuleFromPrevTest(moduleFixture.get())
}
private fun ensureNotModuleFromPrevTest(module: Module) {

View File

@@ -1,21 +1,24 @@
// 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.showcase.resources.project
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Disposer
import com.intellij.testFramework.junit5.resources.FullApplication
import com.intellij.testFramework.junit5.TestApplication
import com.intellij.testFramework.junit5.fixture.projectFixture
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
/**
* Inject project in an instance field: one project for the whole class
*/
@FullApplication
class JUnit5ProjectInstanceField {
lateinit var project: Project
@TestApplication
class JUnit5ClassLeveLProject {
companion object {
private val projectFixture = projectFixture()
}
@Test
fun ensureProject() {
val project = projectFixture.get()
Assertions.assertFalse(project.isDisposed)
Disposer.register(project) {

View File

@@ -1,91 +0,0 @@
// 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.showcase.resources.project
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ex.ProjectManagerEx
import com.intellij.openapi.util.io.toNioPathOrNull
import com.intellij.testFramework.junit5.TestApplication
import com.intellij.testFramework.junit5.resources.asExtension
import com.intellij.testFramework.junit5.resources.create
import com.intellij.testFramework.junit5.resources.providers.PathInfo
import com.intellij.testFramework.junit5.resources.providers.ProjectProvider
import com.intellij.testFramework.junit5.showcase.resources.ResourceCounter
import com.intellij.testFramework.junit5.showcase.resources.project.JUnit5ClassLevelProject.Companion.counter
import kotlinx.coroutines.*
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Order
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.AfterAllCallback
import org.junit.jupiter.api.extension.RegisterExtension
import org.junit.jupiter.api.io.TempDir
import java.nio.file.Path
@Service(Service.Level.PROJECT)
private class MyProjectService(private val scope: CoroutineScope) {
init {
scope.launch {
while (true) {
delay(100) // Should be stopped after the test. Leakage will be reported
}
}
}
fun regCodeOnDispose(code: () -> Unit) {
scope.coroutineContext.job.invokeOnCompletion {
code()
}
}
}
/**
* Class-level project created once per test class.
* [counter] ensures it doesn't leak
*/
@TestApplication
class JUnit5ClassLevelProject {
companion object {
@Order(0)
@JvmStatic
@RegisterExtension
val leak = AfterAllCallback {
counter.ensureEmpty()
}
@Order(1)
@JvmStatic
@RegisterExtension
val ext = ProjectProvider().asExtension()
private val counter = ResourceCounter()
}
@Test
fun test(project1: Project, project2: Project) {
assertEquals(project1, project2, "Class level must use same resource")
assertFalse(project1.isDisposed)
counter.acquire()
project1.service<MyProjectService>().regCodeOnDispose {
counter.release()
}
}
@Test
fun testManualDestroy(@TempDir tempDir: Path): Unit = runBlocking {
val project = ext.create(PathInfo(tempDir, deletePathOnExit = false), disposeOnExit = true)
assertEquals(tempDir, project.basePath!!.toNioPathOrNull()!!)
Assertions.assertTrue(ProjectManagerEx.getInstanceEx().forceCloseProjectAsync(project, save = false))
}
@Test
fun testManualResource(): Unit = runBlocking {
// Manually created projects are always different
val p1 = ext.create()
val p2 = ext.create()
Assertions.assertNotEquals(p1, p2)
}
}

View File

@@ -1,22 +0,0 @@
// 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.showcase.resources.project
import com.intellij.openapi.project.Project
import com.intellij.testFramework.junit5.TestApplication
import com.intellij.testFramework.junit5.resources.ProjectResource
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Test
/**
* Getting a project created automatically on class-level
*/
@TestApplication
@ProjectResource
class JUnit5ClassLevelProjectAnnotation {
@Test
fun test(project: Project) {
assertFalse(project.isDisposed)
}
}

View File

@@ -1,26 +0,0 @@
// 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.showcase.resources.project
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Disposer
import com.intellij.testFramework.junit5.resources.FullApplication
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
/**
* Inject project in a static field: one project for the whole class
*/
@FullApplication
class JUnit5ProjectClassField {
companion object {
lateinit var project: Project
}
@Test
fun ensureProject() {
Assertions.assertFalse(project.isDisposed)
Disposer.register(project) {
}
}
}

View File

@@ -0,0 +1,73 @@
// 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.showcase.resources.project
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.util.Disposer
import com.intellij.testFramework.junit5.TestApplication
import com.intellij.testFramework.junit5.fixture.projectFixture
import com.intellij.testFramework.junit5.showcase.resources.ResourceCounter
import com.intellij.testFramework.junit5.showcase.resources.project.JUnit5TestLevelProject.Companion.counter
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.job
import kotlinx.coroutines.launch
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Test
@Service(Service.Level.PROJECT)
private class MyProjectService(private val scope: CoroutineScope) {
init {
scope.launch {
while (true) {
delay(100) // Should be stopped after the test. Leakage will be reported
}
}
}
fun regCodeOnDispose(code: () -> Unit) {
scope.coroutineContext.job.invokeOnCompletion {
code()
}
}
}
/**
* Class-level project created once per test class.
* [counter] ensures it doesn't leak
*/
@TestApplication
class JUnit5TestLevelProject {
companion object {
@AfterAll
@JvmStatic
fun ensureEmpty() {
counter.ensureEmpty()
}
@JvmStatic
private val counter = ResourceCounter()
}
private val projectFixture = projectFixture()
@Test
fun test() {
val project1 = projectFixture.get()
val project2 = projectFixture.get()
assertEquals(project1, project2, "Class level must use same resource")
assertFalse(project1.isDisposed)
Disposer.register(project1) {
}
counter.acquire()
project1.service<MyProjectService>().regCodeOnDispose {
counter.release()
}
}
}