From 2f4aa207925308dc69d25ba13faa58f95f14af6f Mon Sep 17 00:00:00 2001 From: Georgii Ustinov Date: Thu, 21 Mar 2024 11:02:19 +0200 Subject: [PATCH] [Java. Logging] Add tests for java logging resolve placeholders IDEA-342484 GitOrigin-RevId: 79f8501d6742a8174806e33abadeab3919101519 --- .../testFramework/logging/LoggingTestUtils.kt | 11 + ...gingArgumentSymbolReferenceProviderTest.kt | 352 +++++++++++++++++- 2 files changed, 355 insertions(+), 8 deletions(-) diff --git a/jvm/jvm-analysis-internal-testFramework/src/com/intellij/jvm/analysis/internal/testFramework/logging/LoggingTestUtils.kt b/jvm/jvm-analysis-internal-testFramework/src/com/intellij/jvm/analysis/internal/testFramework/logging/LoggingTestUtils.kt index 7f6b86154a07..402bc1365ed5 100644 --- a/jvm/jvm-analysis-internal-testFramework/src/com/intellij/jvm/analysis/internal/testFramework/logging/LoggingTestUtils.kt +++ b/jvm/jvm-analysis-internal-testFramework/src/com/intellij/jvm/analysis/internal/testFramework/logging/LoggingTestUtils.kt @@ -71,6 +71,7 @@ object LoggingTestUtils { void fatal(String message, Object... params); void error(Supplier var1, Throwable var2); void info(String message, Supplier... params); + void log(Level level, String message, Object... params); LogBuilder atInfo(); LogBuilder atDebug(); LogBuilder atWarn(); @@ -106,6 +107,16 @@ object LoggingTestUtils { void log(String format, Supplier... params); } """.trimIndent()) + fixture.addClass(""" + package org.apache.logging.log4j; + public enum Level { + OFF, + INFO, + FATAL, + ERROR, + ALL + } + """.trimIndent()) } fun addIdeaLog(fixture: JavaCodeInsightTestFixture) { diff --git a/jvm/jvm-analysis-java-tests/testSrc/com/intellij/logging/resolve/JavaLoggingArgumentSymbolReferenceProviderTest.kt b/jvm/jvm-analysis-java-tests/testSrc/com/intellij/logging/resolve/JavaLoggingArgumentSymbolReferenceProviderTest.kt index c60c2d4dc3f6..ae8f7bf115b3 100644 --- a/jvm/jvm-analysis-java-tests/testSrc/com/intellij/logging/resolve/JavaLoggingArgumentSymbolReferenceProviderTest.kt +++ b/jvm/jvm-analysis-java-tests/testSrc/com/intellij/logging/resolve/JavaLoggingArgumentSymbolReferenceProviderTest.kt @@ -14,7 +14,7 @@ import org.jetbrains.uast.ULiteralExpression class JavaLoggingArgumentSymbolReferenceProviderTest : LoggingArgumentSymbolReferenceProviderTestBase() { override fun getProjectDescriptor(): LightProjectDescriptor = JAVA_LATEST_WITH_LATEST_JDK - fun `test log4j2`() { + fun `test log4j2 info`() { myFixture.configureByText("Logging.java", """ import org.apache.logging.log4j.*; class Logging { @@ -27,21 +27,357 @@ class JavaLoggingArgumentSymbolReferenceProviderTest : LoggingArgumentSymbolRefe doTest(mapOf(TextRange(1, 3) to "i")) } + fun `test log4j2 debug`() { + myFixture.configureByText("Logging.java", """ + import org.apache.logging.log4j.*; + class Logging { + private static final Logger LOG = LogManager.getLogger(); + void m(int i) { + LOG.debug("{}", i); + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i")) + } - private fun doTest(bindings : Map) { - val literal = PsiTreeUtil.getParentOfType(myFixture.file.findElementAt(myFixture.editor.caretModel.offset), PsiLiteralExpression::class.java) - TestCase.assertFalse(literal == null) - val refs: Collection = PsiSymbolReferenceService.getService().getReferences(literal!!) - refs.forEach {ref -> - assertEquals(literal, ref.element) + fun `test log4j2 trace`() { + myFixture.configureByText("Logging.java", """ + import org.apache.logging.log4j.*; + class Logging { + private static final Logger LOG = LogManager.getLogger(); + void m(int i) { + LOG.trace("{}", i); + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i")) + } + + fun `test log4j2 fatal`() { + myFixture.configureByText("Logging.java", """ + import org.apache.logging.log4j.*; + class Logging { + private static final Logger LOG = LogManager.getLogger(); + void m(int i) { + LOG.fatal("{}", i); + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i")) + } + + fun `test log4j2 error`() { + myFixture.configureByText("Logging.java", """ + import org.apache.logging.log4j.*; + class Logging { + private static final Logger LOG = LogManager.getLogger(); + void m(int i) { + LOG.error("{}", i); + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i")) + } + + fun `test log4j2 log`() { + myFixture.configureByText("Logging.java", """ + import org.apache.logging.log4j.*; + class Logging { + private static final Logger LOG = LogManager.getLogger(); + void m(int i) { + LOG.log(Level.ALL, "{}", i); + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i")) + } + + fun `test log4j2 formatted logger`() { + myFixture.configureByText("Logging.java", """ + import org.apache.logging.log4j.*; + class Logging { + private static final Logger LOG = LogManager.getFormatterLogger(Logging.class); + void m(int i) { + LOG.info("%d", i); + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i")) + } + + fun `test log4j2 default logger builder`() { + myFixture.configureByText("Logging.java", """ + import org.apache.logging.log4j.*; + class Logging { + private static final Logger LOG = LogManager.getLogger(); + void m(int i) { + LOG.atInfo().log("{}", i); + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i")) + } + + fun `test log4j2 formatted logger builder`() { + myFixture.configureByText("Logging.java", """ + import org.apache.logging.log4j.*; + class Logging { + private static final Logger LOG = LogManager.getFormatterLogger(Logging.class); + void m(int i) { + LOG.atInfo().log("%d", i); + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i")) + } + + fun `test log4j2 respects exception with multiple arguments`() { + myFixture.configureByText("Logging.java", """ + import org.apache.logging.log4j.*; + class Logging { + private static final Logger LOG = LogManager.getLogger(); + void m(int i) { + LOG.info("{} {}", i, new Exception()); + } + } + """.trimIndent()) + + doTest(mapOf(TextRange(1, 3) to "i", TextRange(4, 6) to "new Exception()")) + } + + fun `test log4j2 respects exception as single argument`() { + myFixture.configureByText("Logging.java", """ + import org.apache.logging.log4j.*; + class Logging { + private static final Logger LOG = LogManager.getLogger(); + void m(int i) { + LOG.info("{}", new Exception()); + } + } + """.trimIndent()) + + doTest(emptyMap()) + } + + fun `test log4j2 respects exception in builder logger with multiple arguments`() { + myFixture.configureByText("Logging.java", """ + import org.apache.logging.log4j.*; + class Logging { + private static final Logger LOG = LogManager.getLogger(); + void m(int i) { + LOG.atInfo().log("{} {}", i, new Exception()); + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i", TextRange(4, 6) to "new Exception()")) + } + + fun `test log4j2 respects exception in builder logger as single argument`() { + myFixture.configureByText("Logging.java", """ + import org.apache.logging.log4j.*; + class Logging { + private static final Logger LOG = LogManager.getLogger(); + void m(int i) { + LOG.atInfo().log("{}", new Exception()); + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "new Exception()")) + } + + fun `test slf4j info`() { + myFixture.configureByText("Logging.java", """ + import org.slf4j.*; + class Logging { + private static final Logger LOG = LoggerFactory.getLogger(Logging.class); + void m(int i) { + LOG.info("{}", i); + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i")) + } + + fun `test slf4j debug`() { + myFixture.configureByText("Logging.java", """ + import org.slf4j.*; + class Logging { + private static final Logger LOG = LoggerFactory.getLogger(Logging.class); + void m(int i) { + LOG.debug("{}", i); + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i")) + } + + fun `test slf4j error`() { + myFixture.configureByText("Logging.java", """ + import org.slf4j.*; + class Logging { + private static final Logger LOG = LoggerFactory.getLogger(Logging.class); + void m(int i) { + LOG.error("{}", i); + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i")) + } + + fun `test slf4j warn`() { + myFixture.configureByText("Logging.java", """ + import org.slf4j.*; + class Logging { + private static final Logger LOG = LoggerFactory.getLogger(Logging.class); + void m(int i) { + LOG.warn("{}", i); + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i")) + } + + + fun `test slf4j trace`() { + myFixture.configureByText("Logging.java", """ + import org.slf4j.*; + class Logging { + private static final Logger LOG = LoggerFactory.getLogger(Logging.class); + void m(int i) { + LOG.trace("{}", i); + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i")) + } + + + fun `test slf4j builder simple`() { + myFixture.configureByText("Logging.java", """ + import org.slf4j.*; + class Logging { + private static final Logger LOG = LoggerFactory.getLogger(Logging.class); + void m(int i) { + LOG.atInfo().log("{}", i); + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i")) + } + + fun `test slf4j builder with setMessage`() { + myFixture.configureByText("Logging.java", """ + import org.slf4j.*; + class Logging { + private static final Logger LOG = LoggerFactory.getLogger(Logging.class); + void m(int i) { + LOG.atInfo().addArgument("foo").setMessage("{} {}").addArgument("bar").log(); + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "\"foo\"", TextRange(4, 6) to "\"bar\"")) + } + + fun `test slf4j builder considers only last message`() { + myFixture.configureByText("Logging.java", """ + import org.slf4j.*; + class Logging { + private static final Logger LOG = LoggerFactory.getLogger(Logging.class); + void m(int i) { + LOG.atInfo().addArgument("foo").setMessage("{} {} {}").setMessage("{} {}").addArgument("bar").log(); + } + } + """.trimIndent()) + doTest(emptyMap()) + } + + fun `test slf4j builder combined args`() { + myFixture.configureByText("Logging.java", """ + import org.slf4j.*; + class Logging { + private static final Logger LOG = LoggerFactory.getLogger(Logging.class); + void m(int i) { + LOG.atInfo().addArgument("foo").addArgument("bar").log("{} {} {}", "baz"); + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "\"foo\"", TextRange(4, 6) to "\"bar\"", TextRange(7, 9) to "\"baz\"")) + } + + fun `test slf4j builder respects last argument as exception in log`() { + myFixture.configureByText("Logging.java", """ + import org.slf4j.*; + class Logging { + private static final Logger LOG = LoggerFactory.getLogger(Logging.class); + void m(int i) { + LOG.atInfo().addArgument("foo").addArgument("bar").log("{} {} {}", new Throwable()); + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "\"foo\"", TextRange(4, 6) to "\"bar\"")) + } + + fun `test slf4j builder respects last argument as exception in setCause`() { + myFixture.configureByText("Logging.java", """ + import org.slf4j.*; + class Logging { + private static final Logger LOG = LoggerFactory.getLogger(Logging.class); + void m(int i) { + LOG.atInfo().addArgument("foo").addArgument("bar").setCause(new Throwable()).log("{} {} {}"); + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "\"foo\"", TextRange(4, 6) to "\"bar\"")) + } + + fun `test should resolve in multiline string`() { + val multilineString = "\"\"\"\n" + + "{}\n" + + "\"\"\"" + myFixture.configureByText("Logging.java", """ + import org.slf4j.*; + class Logging { + private static final Logger LOG = LoggerFactory.getLogger(Logging.class); + void m(int i) { + LOG.info($multilineString, i); + } + } + """.trimIndent()) + doTest(mapOf(TextRange(4, 6) to "i")) + } + + fun `test should not resolve with string concatenation`() { + myFixture.configureByText("Logging.java", """ + import org.slf4j.*; + class Logging { + private static final Logger LOG = LoggerFactory.getLogger(Logging.class); + void m(int i) { + LOG.info("{} " + "}", i); + } + } + """.trimIndent()) + doTest(emptyMap()) + } + + private fun doTest(bindings: Map) { + val literalExpression = PsiTreeUtil.getParentOfType(myFixture.file.findElementAt(myFixture.editor.caretModel.offset), PsiLiteralExpression::class.java) + TestCase.assertFalse(literalExpression == null) + val refs: Collection = PsiSymbolReferenceService.getService().getReferences(literalExpression!!) + TestCase.assertEquals(bindings.size, refs.size) + val usedRanges = mutableSetOf() + refs.forEach { ref -> + assertEquals(literalExpression, ref.element) val symbols = ref.resolveReference() - assertEquals(bindings.size, symbols.size) + assertEquals(1, symbols.size) val symbol = symbols.single() assertTrue(symbol is LoggingArgumentSymbol) val formatSymbol = symbol as LoggingArgumentSymbol assertTrue(formatSymbol.getPlaceholderString() is ULiteralExpression) val expressionText = formatSymbol.expression.text + TestCase.assertFalse(ref.rangeInElement in usedRanges) assertEquals(bindings[ref.rangeInElement], expressionText) + usedRanges.add(ref.rangeInElement) } + TestCase.assertTrue(bindings.map { it.key }.toSet() == usedRanges) } } \ No newline at end of file