mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[junit5] ability to specify log level for testing
followup for IJ-CR-181003 GitOrigin-RevId: a736aa6b750ebd0061117c2d183ac66ffe286360
This commit is contained in:
committed by
intellij-monorepo-bot
parent
cfe8841906
commit
162f677c8e
@@ -5,7 +5,6 @@ import com.intellij.codeInsight.multiverse.*
|
||||
import com.intellij.openapi.application.edtWriteAction
|
||||
import com.intellij.openapi.application.readAction
|
||||
import com.intellij.openapi.application.writeAction
|
||||
import com.intellij.openapi.diagnostic.LogLevel
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.project.rootManager
|
||||
import com.intellij.platform.testFramework.junit5.projectStructure.fixture.withSharedSourceEnabled
|
||||
@@ -13,7 +12,7 @@ import com.intellij.psi.PsiDirectory
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.impl.PsiManagerEx
|
||||
import com.intellij.testFramework.common.timeoutRunBlocking
|
||||
import com.intellij.testFramework.junit5.LogLevelWithClass
|
||||
import com.intellij.testFramework.junit5.EnableTracingFor
|
||||
import com.intellij.testFramework.junit5.TestApplication
|
||||
import com.intellij.testFramework.junit5.fixture.*
|
||||
import kotlinx.coroutines.ensureActive
|
||||
@@ -24,9 +23,8 @@ import org.junit.jupiter.api.Test
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@TestApplication
|
||||
@LogLevelWithClass(
|
||||
category = CodeInsightContextManagerImpl::class,
|
||||
level = LogLevel.TRACE
|
||||
@EnableTracingFor(
|
||||
categoryClasses = [CodeInsightContextManagerImpl::class],
|
||||
)
|
||||
internal class FileContextTest {
|
||||
companion object {
|
||||
|
||||
@@ -4,7 +4,6 @@ package com.intellij.psi.impl.file.impl
|
||||
import com.intellij.codeInsight.multiverse.*
|
||||
import com.intellij.openapi.application.readAction
|
||||
import com.intellij.openapi.application.writeAction
|
||||
import com.intellij.openapi.diagnostic.LogLevel
|
||||
import com.intellij.openapi.diagnostic.thisLogger
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
@@ -17,7 +16,7 @@ import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.impl.PsiManagerImpl
|
||||
import com.intellij.psi.util.PsiUtilCore.ensureValid
|
||||
import com.intellij.testFramework.common.timeoutRunBlocking
|
||||
import com.intellij.testFramework.junit5.LogLevelWithClass
|
||||
import com.intellij.testFramework.junit5.EnableTracingFor
|
||||
import com.intellij.testFramework.junit5.TestApplication
|
||||
import com.intellij.testFramework.junit5.fixture.fileOrDirInProjectFixture
|
||||
import com.intellij.testFramework.junit5.fixture.moduleInProjectFixture
|
||||
@@ -27,13 +26,9 @@ import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.fail
|
||||
|
||||
@LogLevelWithClass(
|
||||
category = CodeInsightContextManagerImpl::class,
|
||||
level = LogLevel.TRACE
|
||||
)
|
||||
@com.intellij.testFramework.junit5.LogLevel(
|
||||
category = "#com.intellij.psi.impl.file.impl.MultiverseFileViewProviderCache",
|
||||
level = LogLevel.TRACE
|
||||
@EnableTracingFor(
|
||||
categories = ["#com.intellij.psi.impl.file.impl.MultiverseFileViewProviderCache"],
|
||||
categoryClasses = [CodeInsightContextManagerImpl::class]
|
||||
)
|
||||
@TestApplication
|
||||
internal class FileMoveTest {
|
||||
|
||||
+4
-9
@@ -4,7 +4,6 @@ package com.intellij.psi.impl.file.impl
|
||||
import com.intellij.codeInsight.multiverse.*
|
||||
import com.intellij.openapi.application.readAction
|
||||
import com.intellij.openapi.application.writeAction
|
||||
import com.intellij.openapi.diagnostic.LogLevel
|
||||
import com.intellij.openapi.diagnostic.thisLogger
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
@@ -18,7 +17,7 @@ import com.intellij.psi.impl.PsiManagerImpl
|
||||
import com.intellij.psi.util.PsiUtilCore.ensureValid
|
||||
import com.intellij.testFramework.IndexingTestUtil
|
||||
import com.intellij.testFramework.common.timeoutRunBlocking
|
||||
import com.intellij.testFramework.junit5.LogLevelWithClass
|
||||
import com.intellij.testFramework.junit5.EnableTracingFor
|
||||
import com.intellij.testFramework.junit5.TestApplication
|
||||
import com.intellij.testFramework.junit5.fixture.fileOrDirInProjectFixture
|
||||
import com.intellij.testFramework.junit5.fixture.moduleInProjectFixture
|
||||
@@ -28,13 +27,9 @@ import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.fail
|
||||
|
||||
@LogLevelWithClass(
|
||||
category = CodeInsightContextManagerImpl::class,
|
||||
level = LogLevel.TRACE
|
||||
)
|
||||
@com.intellij.testFramework.junit5.LogLevel(
|
||||
category = "#com.intellij.psi.impl.file.impl.MultiverseFileViewProviderCache",
|
||||
level = LogLevel.TRACE
|
||||
@EnableTracingFor(
|
||||
categories = ["#com.intellij.psi.impl.file.impl.MultiverseFileViewProviderCache"],
|
||||
categoryClasses = [CodeInsightContextManagerImpl::class],
|
||||
)
|
||||
@TestApplication
|
||||
internal class FileMoveWithWaitForIndexingTest {
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.testFramework.junit5
|
||||
|
||||
import com.intellij.testFramework.junit5.impl.EnableTracingExtension
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* Enables tracing of the specified logging categories inside the annotated test class or method.
|
||||
* The corresponding messages will be present in the log file on disk.
|
||||
* Also, they will be visible in <DEBUG log> which is printed to str-error if the test fails.
|
||||
*
|
||||
* After the annotated test/function finishes, the logger will be restored to DEBUG level.
|
||||
*
|
||||
* For convenience, you can specify logging categories by their fully qualified names and/or by their classes.
|
||||
* If both categories and categoryClasses are specified, all of them will be enabled.
|
||||
*/
|
||||
@TestOnly
|
||||
@Repeatable
|
||||
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
|
||||
@ExtendWith(EnableTracingExtension::class)
|
||||
annotation class EnableTracingFor(
|
||||
val categories: Array<String> = [],
|
||||
val categoryClasses: Array<KClass<*>> = [],
|
||||
)
|
||||
@@ -1,27 +0,0 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.testFramework.junit5
|
||||
|
||||
import com.intellij.openapi.diagnostic.LogLevel
|
||||
import com.intellij.testFramework.junit5.impl.LogLevelExtension
|
||||
import com.intellij.testFramework.junit5.impl.LogLevelWithClassExtension
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@TestOnly
|
||||
@Repeatable
|
||||
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
|
||||
@ExtendWith(LogLevelExtension::class)
|
||||
annotation class LogLevel(
|
||||
val category: String,
|
||||
val level: LogLevel,
|
||||
)
|
||||
|
||||
@TestOnly
|
||||
@Repeatable
|
||||
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
|
||||
@ExtendWith(LogLevelWithClassExtension::class)
|
||||
annotation class LogLevelWithClass(
|
||||
val category: KClass<*>,
|
||||
val level: LogLevel,
|
||||
)
|
||||
@@ -0,0 +1,54 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.testFramework.junit5.impl
|
||||
|
||||
import com.intellij.openapi.diagnostic.LogLevel
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.testFramework.junit5.EnableTracingFor
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.junit.jupiter.api.extension.ExtensionContext
|
||||
import org.junit.jupiter.api.extension.InvocationInterceptor
|
||||
import org.junit.platform.commons.support.AnnotationSupport
|
||||
|
||||
@TestOnly
|
||||
internal class EnableTracingExtension : AbstractInvocationInterceptor() {
|
||||
override fun <T> intercept(invocation: InvocationInterceptor.Invocation<T>, context: ExtensionContext): T {
|
||||
val annotations = collectAnnotations<EnableTracingFor>(context).ifEmpty {
|
||||
return invocation.proceed()
|
||||
}
|
||||
|
||||
val loggers = annotations.flatMap { annotation ->
|
||||
annotation.categories.map { Logger.getInstance(it) } +
|
||||
annotation.categoryClasses.map { Logger.getInstance(it.java) }
|
||||
}
|
||||
|
||||
return doWithTracingEnabled(loggers, invocation)
|
||||
}
|
||||
|
||||
private fun <T> doWithTracingEnabled(
|
||||
infos: List<Logger>,
|
||||
invocation: InvocationInterceptor.Invocation<T>,
|
||||
): T {
|
||||
val valuesBefore = infos.map { info -> enableTracing(info) }
|
||||
|
||||
try {
|
||||
return invocation.proceed()
|
||||
}
|
||||
finally {
|
||||
valuesBefore.asReversed().forEach { it() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun enableTracing(logger: Logger): () -> Unit {
|
||||
logger.setLevel(LogLevel.TRACE)
|
||||
return {
|
||||
logger.setLevel(LogLevel.INFO)
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <reified C : Annotation> collectAnnotations(context: ExtensionContext): List<C> {
|
||||
val annotations =
|
||||
AnnotationSupport.findRepeatableAnnotations(context.testClass, C::class.java) +
|
||||
AnnotationSupport.findRepeatableAnnotations(context.element, C::class.java)
|
||||
return annotations.distinct()
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.testFramework.junit5.impl
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.testFramework.junit5.LogLevel
|
||||
import com.intellij.testFramework.junit5.LogLevelWithClass
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.junit.jupiter.api.extension.ExtensionContext
|
||||
import org.junit.jupiter.api.extension.InvocationInterceptor
|
||||
import org.junit.platform.commons.support.AnnotationSupport
|
||||
|
||||
@TestOnly
|
||||
internal class LogLevelExtension : AbstractInvocationInterceptor() {
|
||||
override fun <T> intercept(invocation: InvocationInterceptor.Invocation<T>, context: ExtensionContext): T {
|
||||
val annotations = collectAnnotations<LogLevel>(context).ifEmpty {
|
||||
return invocation.proceed()
|
||||
}
|
||||
|
||||
val infos = annotations.map {
|
||||
LogLevelInfo(Logger.getInstance(it.category), it.level)
|
||||
}
|
||||
|
||||
return doWithLogLevelInstalled(infos, invocation)
|
||||
}
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
internal class LogLevelWithClassExtension : AbstractInvocationInterceptor() {
|
||||
override fun <T> intercept(invocation: InvocationInterceptor.Invocation<T>, context: ExtensionContext): T {
|
||||
val annotations = collectAnnotations<LogLevelWithClass>(context).ifEmpty {
|
||||
return invocation.proceed()
|
||||
}
|
||||
|
||||
val infos = annotations.map {
|
||||
LogLevelInfo(Logger.getInstance(it.category.java), it.level)
|
||||
}
|
||||
|
||||
return doWithLogLevelInstalled(infos, invocation)
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <reified C : Annotation> collectAnnotations(context: ExtensionContext): List<C> {
|
||||
val annotations =
|
||||
AnnotationSupport.findRepeatableAnnotations(context.testClass, C::class.java) +
|
||||
AnnotationSupport.findRepeatableAnnotations(context.element, C::class.java)
|
||||
return annotations.distinct()
|
||||
}
|
||||
|
||||
private data class LogLevelInfo(
|
||||
val logger: Logger,
|
||||
val level: com.intellij.openapi.diagnostic.LogLevel,
|
||||
)
|
||||
|
||||
private fun <T> doWithLogLevelInstalled(
|
||||
infos: List<LogLevelInfo>,
|
||||
invocation: InvocationInterceptor.Invocation<T>,
|
||||
): T {
|
||||
val valuesBefore = infos.map { info ->
|
||||
setLogLevel(info)
|
||||
}
|
||||
try {
|
||||
return invocation.proceed()
|
||||
}
|
||||
finally {
|
||||
for (handle in valuesBefore.asReversed()) {
|
||||
handle()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setLogLevel(info: LogLevelInfo): () -> Unit {
|
||||
val (logger, logLevel) = info
|
||||
|
||||
val prevLevel = logger.level
|
||||
logger.level = logLevel
|
||||
return {
|
||||
logger.level = prevLevel
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2000-2025 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
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.testFramework.junit5.EnableTracingFor
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
// MyBusinessLogic logger works with the TRACE level inside the test run.
|
||||
@EnableTracingFor(categoryClasses = [MyBusinessLogic::class])
|
||||
class JUnit5EnableTracingInTest {
|
||||
|
||||
@Test
|
||||
fun `traces are written to log file`() {
|
||||
MyBusinessLogic().run()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class MyBusinessLogic {
|
||||
private val LOG = Logger.getInstance(MyBusinessLogic::class.java)
|
||||
|
||||
fun run() {
|
||||
LOG.trace("MyBusinessLogic#run started")
|
||||
|
||||
// doing stuff ...
|
||||
|
||||
LOG.info("MyBusinessLogic#run finished")
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,10 @@ package com.intellij.openapi.diagnostic;
|
||||
import com.intellij.util.ArrayUtilRt;
|
||||
import com.intellij.util.ExceptionUtil;
|
||||
import org.apache.log4j.Level;
|
||||
import org.jetbrains.annotations.*;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.Collection;
|
||||
@@ -488,17 +491,6 @@ public abstract class Logger {
|
||||
error(getClass() + " should override '#setLevel(LogLevel)'");
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public LogLevel getLevel() {
|
||||
if (isTraceEnabled()) {
|
||||
return LogLevel.TRACE;
|
||||
}
|
||||
if (isDebugEnabled()) {
|
||||
return LogLevel.DEBUG;
|
||||
}
|
||||
return LogLevel.INFO;
|
||||
}
|
||||
|
||||
private static final boolean ourRethrowCE = "true".equals(System.getProperty("idea.log.rethrow.ce", "true"));
|
||||
|
||||
public static boolean shouldRethrow(@NotNull Throwable t) {
|
||||
|
||||
Reference in New Issue
Block a user