diff options
Diffstat (limited to 'src/test')
10 files changed, 277 insertions, 236 deletions
diff --git a/src/test/java/org/sonarsource/scanner/cli/CliTest.java b/src/test/java/org/sonarsource/scanner/cli/CliTest.java index 6b2622a..5cb05d8 100644 --- a/src/test/java/org/sonarsource/scanner/cli/CliTest.java +++ b/src/test/java/org/sonarsource/scanner/cli/CliTest.java @@ -19,7 +19,14 @@ */ package org.sonarsource.scanner.cli; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.slf4j.event.Level; +import testutils.LogTester; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatNoException; @@ -28,9 +35,12 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; class CliTest { + + @RegisterExtension + LogTester logTester = new LogTester(); + private final Exit exit = mock(Exit.class); - private Logs logs = new Logs(System.out, System.err); - private Cli cli = new Cli(exit, logs); + private Cli cli = new Cli(exit); @Test void should_parse_empty_arguments() { @@ -52,18 +62,16 @@ class CliTest { @Test void should_warn_on_duplicate_properties() { - logs = mock(Logs.class); - cli = new Cli(exit, logs); + cli = new Cli(exit); cli.parse(new String[]{"-D", "foo=bar", "--define", "foo=baz"}); - verify(logs).warn("Property 'foo' with value 'bar' is overridden with value 'baz'"); + assertThat(logTester.logs(Level.WARN)).contains("Property 'foo' with value 'bar' is overridden with value 'baz'"); } @Test void should_fail_on_missing_prop() { - logs = mock(Logs.class); - cli = new Cli(exit, logs); + cli = new Cli(exit); cli.parse(new String[]{"-D"}); - verify(logs).error("Missing argument for option -D/--define"); + assertThat(logTester.logs(Level.ERROR)).contains("Missing argument for option -D/--define"); verify(exit).exit(Exit.INTERNAL_ERROR); } @@ -132,31 +140,32 @@ class CliTest { assertThat(cli.properties().get("sonar.verbose")).isNull(); } - @Test - void should_show_usage() { - logs = mock(Logs.class); - cli = new Cli(exit, logs); - cli.parse(new String[]{"-h"}); - verify(logs).info("usage: sonar-scanner [options]"); + @ParameterizedTest + @ValueSource(strings = {"-h", "--help"}) + void should_show_usage(String arg) { + var baos = parseAndCaptureStdOut(arg); + assertThat(baos.toString()).contains("usage: sonar-scanner [options]"); verify(exit).exit(Exit.SUCCESS); } - @Test - void should_show_usage_full() { - logs = mock(Logs.class); - cli = new Cli(exit, logs); - cli.parse(new String[]{"--help"}); - verify(logs).info("usage: sonar-scanner [options]"); - verify(exit).exit(Exit.SUCCESS); + private ByteArrayOutputStream parseAndCaptureStdOut(String arg) { + var baos = new ByteArrayOutputStream(); + var savedOut = System.out; + try { + System.setOut(new PrintStream(baos)); + cli = new Cli(exit); + cli.parse(new String[]{arg}); + } finally { + System.setOut(savedOut); + } + return baos; } @Test void should_show_usage_on_bad_syntax() { - logs = mock(Logs.class); - cli = new Cli(exit, logs); - cli.parse(new String[]{"-w"}); - verify(logs).error("Unrecognized option: -w"); - verify(logs).info("usage: sonar-scanner [options]"); + var baos = parseAndCaptureStdOut("-w"); + assertThat(baos.toString()).contains("usage: sonar-scanner [options]"); + assertThat(logTester.logs(Level.ERROR)).contains("Unrecognized option: -w"); verify(exit).exit(Exit.INTERNAL_ERROR); } diff --git a/src/test/java/org/sonarsource/scanner/cli/ConfTest.java b/src/test/java/org/sonarsource/scanner/cli/ConfTest.java index ea031a0..82bd955 100644 --- a/src/test/java/org/sonarsource/scanner/cli/ConfTest.java +++ b/src/test/java/org/sonarsource/scanner/cli/ConfTest.java @@ -43,9 +43,8 @@ class ConfTest { private final Map<String, String> env = new HashMap<>(); private final Properties args = new Properties(); - private final Logs logs = new Logs(System.out, System.err); private final Cli cli = mock(Cli.class); - private final Conf conf = new Conf(cli, logs, env); + private final Conf conf = new Conf(cli, env); @BeforeEach void initConf() { diff --git a/src/test/java/org/sonarsource/scanner/cli/LogsTest.java b/src/test/java/org/sonarsource/scanner/cli/LogsTest.java deleted file mode 100644 index 1bb1abe..0000000 --- a/src/test/java/org/sonarsource/scanner/cli/LogsTest.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * SonarScanner CLI - * Copyright (C) 2011-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonarsource.scanner.cli; - -import java.io.PrintStream; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentMatchers; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.sonarsource.scanner.lib.LogOutput; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; - -class LogsTest { - @Mock - private PrintStream stdOut; - - @Mock - private PrintStream stdErr; - - private Logs logs; - - @BeforeEach - void setUp() { - MockitoAnnotations.initMocks(this); - logs = new Logs(stdOut, stdErr); - } - - @Test - void testInfo() { - logs.info("info"); - verify(stdOut).println("INFO: info"); - verifyNoMoreInteractions(stdOut, stdErr); - } - - @Test - void testWarn() { - logs.warn("warn"); - verify(stdOut).println("WARN: warn"); - verifyNoMoreInteractions(stdOut, stdErr); - } - - @Test - void testWarnWithTimestamp() { - logs.setDebugEnabled(true); - logs.warn("warn"); - verify(stdOut).println(ArgumentMatchers.matches("\\d\\d:\\d\\d:\\d\\d.\\d\\d\\d WARN: warn")); - verifyNoMoreInteractions(stdOut, stdErr); - } - - @Test - void testError() { - Exception e = new NullPointerException("exception"); - logs.error("error1"); - verify(stdErr).println("ERROR: error1"); - - logs.error("error2", e); - verify(stdErr).println("ERROR: error2"); - verify(stdErr).println(e); - // other interactions to print the exception.. - } - - @Test - void testDebug() { - logs.setDebugEnabled(true); - - logs.debug("debug"); - verify(stdOut).println(ArgumentMatchers.matches("\\d\\d:\\d\\d:\\d\\d.\\d\\d\\d DEBUG: debug$")); - - logs.setDebugEnabled(false); - logs.debug("debug"); - verifyNoMoreInteractions(stdOut, stdErr); - } - - @Test - void should_forward_logs() { - var mockedLogs = mock(Logs.class); - var logOutput = new Logs.LogOutputAdapter(mockedLogs); - - String msg = "test"; - - logOutput.log(msg, LogOutput.Level.DEBUG); - verify(mockedLogs).debug(msg); - verifyNoMoreInteractions(mockedLogs); - reset(mockedLogs); - - logOutput.log(msg, LogOutput.Level.INFO); - verify(mockedLogs).info(msg); - verifyNoMoreInteractions(mockedLogs); - reset(mockedLogs); - - logOutput.log(msg, LogOutput.Level.ERROR); - verify(mockedLogs).error(msg); - verifyNoMoreInteractions(mockedLogs); - reset(mockedLogs); - - logOutput.log(msg, LogOutput.Level.WARN); - verify(mockedLogs).warn(msg); - verifyNoMoreInteractions(mockedLogs); - reset(mockedLogs); - - logOutput.log(msg, LogOutput.Level.TRACE); - verify(mockedLogs).debug(msg); - verifyNoMoreInteractions(mockedLogs); - reset(mockedLogs); - } -} diff --git a/src/test/java/org/sonarsource/scanner/cli/MainTest.java b/src/test/java/org/sonarsource/scanner/cli/MainTest.java index 94bd544..7b6ccfd 100644 --- a/src/test/java/org/sonarsource/scanner/cli/MainTest.java +++ b/src/test/java/org/sonarsource/scanner/cli/MainTest.java @@ -23,47 +23,42 @@ import java.util.Map; import java.util.Properties; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.ArgumentCaptor; import org.mockito.InOrder; -import org.mockito.Mock; import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; +import org.slf4j.LoggerFactory; +import org.slf4j.event.Level; import org.sonar.api.utils.MessageException; import org.sonarsource.scanner.lib.ScannerEngineBootstrapper; import org.sonarsource.scanner.lib.ScannerEngineFacade; import org.sonarsource.scanner.lib.ScannerProperties; +import testutils.LogTester; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -public class MainTest { - - @Mock - private Exit exit; - @Mock - private Cli cli; - @Mock - private Conf conf; - @Mock - private Properties properties; - @Mock - private ScannerEngineBootstrapperFactory scannerEngineBootstrapperFactory; - @Mock - private ScannerEngineBootstrapper bootstrapper; - @Mock - private ScannerEngineFacade engine; - @Mock - private Logs logs; +class MainTest { + + @RegisterExtension + LogTester logTester = new LogTester(); + + private final Exit exit = mock(); + private final Cli cli = mock(); + private final Conf conf = mock(); + private final Properties properties = mock(); + private final ScannerEngineBootstrapperFactory scannerEngineBootstrapperFactory = mock(); + private final ScannerEngineBootstrapper bootstrapper = mock(); + private final ScannerEngineFacade engine = mock(); @BeforeEach void setUp() { - MockitoAnnotations.initMocks(this); when(scannerEngineBootstrapperFactory.create(any(Properties.class), any(String.class))).thenReturn(bootstrapper); when(bootstrapper.bootstrap()).thenReturn(engine); when(conf.properties()).thenReturn(properties); @@ -72,7 +67,7 @@ public class MainTest { @Test void should_execute_scanner_engine() { when(cli.getInvokedFrom()).thenReturn(""); - Main main = new Main(exit, cli, conf, scannerEngineBootstrapperFactory, logs); + Main main = new Main(exit, cli, conf, scannerEngineBootstrapperFactory); main.analyze(); verify(exit).exit(Exit.SUCCESS); @@ -89,11 +84,11 @@ public class MainTest { doThrow(e).when(engine).analyze(any()); when(cli.getInvokedFrom()).thenReturn(""); when(cli.isDebugEnabled()).thenReturn(true); - Main main = new Main(exit, cli, conf, scannerEngineBootstrapperFactory, logs); + Main main = new Main(exit, cli, conf, scannerEngineBootstrapperFactory); main.analyze(); verify(exit).exit(Exit.INTERNAL_ERROR); - verify(logs).error("Error during SonarScanner CLI execution", e); + assertThat(logTester.logs(Level.ERROR)).contains("Error during SonarScanner CLI execution"); } @Test @@ -104,13 +99,13 @@ public class MainTest { when(cli.getInvokedFrom()).thenReturn(""); when(cli.isDebugEnabled()).thenReturn(true); - Main main = new Main(exit, cli, conf, scannerEngineBootstrapperFactory, logs); + Main main = new Main(exit, cli, conf, scannerEngineBootstrapperFactory); main.analyze(); verify(bootstrapper).bootstrap(); verify(engine, never()).analyze(any()); verify(exit).exit(Exit.INTERNAL_ERROR); - verify(logs).error("Error during SonarScanner CLI execution", e); + assertThat(logTester.logs(Level.ERROR)).contains("Error during SonarScanner CLI execution"); } @Test @@ -118,8 +113,8 @@ public class MainTest { Exception e = createException(false); testException(e, false, false, Exit.INTERNAL_ERROR); - verify(logs).error("Error during SonarScanner CLI execution", e); - verify(logs).error("Re-run SonarScanner CLI using the -X switch to enable full debug logging."); + assertThat(logTester.logs(Level.ERROR)).contains("Error during SonarScanner CLI execution"); + assertThat(logTester.logs(Level.ERROR)).contains("Re-run SonarScanner CLI using the -X switch to enable full debug logging."); } @Test @@ -127,12 +122,11 @@ public class MainTest { Exception e = createException(true); testException(e, false, false, Exit.USER_ERROR); - verify(logs, times(5)).error(anyString()); - verify(logs).error("Error during SonarScanner CLI execution"); - verify(logs).error("my message"); - verify(logs).error("Caused by: A functional cause"); - verify(logs).error(""); - verify(logs).error("Re-run SonarScanner CLI using the -X switch to enable full debug logging."); + assertThat(logTester.logs(Level.ERROR)).containsOnly("Error during SonarScanner CLI execution", + "my message", + "Caused by: A functional cause", + "", + "Re-run SonarScanner CLI using the -X switch to enable full debug logging."); } @Test @@ -140,11 +134,10 @@ public class MainTest { Exception e = createException(true); testException(e, false, true, Exit.USER_ERROR); - verify(logs, times(4)).error(anyString()); - verify(logs).error("Error during SonarScanner CLI execution"); - verify(logs).error("my message"); - verify(logs).error("Caused by: A functional cause"); - verify(logs).error(""); + assertThat(logTester.logs(Level.ERROR)).containsOnly("Error during SonarScanner CLI execution", + "my message", + "Caused by: A functional cause", + ""); } @Test @@ -152,8 +145,7 @@ public class MainTest { Exception e = createException(true); testException(e, true, false, Exit.USER_ERROR); - verify(logs, times(1)).error(anyString(), any(Throwable.class)); - verify(logs).error("Error during SonarScanner CLI execution", e); + assertThat(logTester.logs(Level.ERROR)).containsOnly("Error during SonarScanner CLI execution"); } @Test @@ -161,8 +153,7 @@ public class MainTest { Exception e = createException(true); testException(e, true, true, Exit.USER_ERROR); - verify(logs, times(1)).error(anyString(), any(Throwable.class)); - verify(logs).error("Error during SonarScanner CLI execution", e); + assertThat(logTester.logs(Level.ERROR)).containsOnly("Error during SonarScanner CLI execution"); } @Test @@ -170,8 +161,7 @@ public class MainTest { Exception e = createException(false); testException(e, true, false, Exit.INTERNAL_ERROR); - verify(logs).error("Error during SonarScanner CLI execution", e); - verify(logs, never()).error("Re-run SonarScanner CLI using the -X switch to enable full debug logging."); + assertThat(logTester.logs(Level.ERROR)).containsOnly("Error during SonarScanner CLI execution"); } private void testException(Exception e, boolean debugEnabled, boolean isEmbedded, int expectedExitCode) { @@ -184,7 +174,7 @@ public class MainTest { when(scannerEngineBootstrapperFactory.create(any(Properties.class), any(String.class))).thenReturn(bootstrapper); - Main main = new Main(exit, cli, conf, scannerEngineBootstrapperFactory, logs); + Main main = new Main(exit, cli, conf, scannerEngineBootstrapperFactory); main.analyze(); verify(exit).exit(expectedExitCode); @@ -208,7 +198,7 @@ public class MainTest { when(cli.getInvokedFrom()).thenReturn(""); when(conf.properties()).thenReturn(p); - Main main = new Main(exit, cli, conf, scannerEngineBootstrapperFactory, logs); + Main main = new Main(exit, cli, conf, scannerEngineBootstrapperFactory); main.analyze(); InOrder inOrder = Mockito.inOrder(exit, scannerEngineBootstrapperFactory); @@ -225,10 +215,10 @@ public class MainTest { when(conf.properties()).thenReturn(p); when(cli.getInvokedFrom()).thenReturn(""); - Main main = new Main(exit, cli, conf, scannerEngineBootstrapperFactory, logs); + Main main = new Main(exit, cli, conf, scannerEngineBootstrapperFactory); main.analyze(); - verify(logs).info("SonarScanner CLI analysis skipped"); + assertThat(logTester.logs(Level.INFO)).contains("SonarScanner CLI analysis skipped"); InOrder inOrder = Mockito.inOrder(exit, scannerEngineBootstrapperFactory); inOrder.verify(exit, times(1)).exit(Exit.SUCCESS); @@ -245,9 +235,9 @@ public class MainTest { when(cli.getInvokedFrom()).thenReturn(""); when(conf.properties()).thenReturn(p); - Main main = new Main(exit, cli, conf, scannerEngineBootstrapperFactory, logs); + Main main = new Main(exit, cli, conf, scannerEngineBootstrapperFactory); main.analyze(); - verify(logs).info("Communicating with SonarQube Server 5.5"); + assertThat(logTester.logs(Level.INFO)).contains("Communicating with SonarQube Server 5.5"); } @Test @@ -257,9 +247,9 @@ public class MainTest { when(conf.properties()).thenReturn(p); when(cli.getInvokedFrom()).thenReturn(""); - Main main = new Main(exit, cli, conf, scannerEngineBootstrapperFactory, logs); + Main main = new Main(exit, cli, conf, scannerEngineBootstrapperFactory); main.analyze(); - verify(logs).info("Communicating with SonarCloud"); + assertThat(logTester.logs(Level.INFO)).contains("Communicating with SonarCloud"); } @Test @@ -290,7 +280,7 @@ public class MainTest { Properties actualProps = execute(propKey, propValue); // Logger used for callback should have debug enabled - verify(logs).setDebugEnabled(true); + assertThat(LoggerFactory.getLogger(getClass()).isDebugEnabled()).isTrue(); return actualProps; } @@ -302,7 +292,7 @@ public class MainTest { when(conf.properties()).thenReturn(p); when(cli.getInvokedFrom()).thenReturn(""); - Main main = new Main(exit, cli, conf, scannerEngineBootstrapperFactory, logs); + Main main = new Main(exit, cli, conf, scannerEngineBootstrapperFactory); main.analyze(); ArgumentCaptor<Properties> propertiesCapture = ArgumentCaptor.forClass(Properties.class); diff --git a/src/test/java/org/sonarsource/scanner/cli/ScannerEngineBootstrapperFactoryTest.java b/src/test/java/org/sonarsource/scanner/cli/ScannerEngineBootstrapperFactoryTest.java index cfaca6f..c25ddb1 100644 --- a/src/test/java/org/sonarsource/scanner/cli/ScannerEngineBootstrapperFactoryTest.java +++ b/src/test/java/org/sonarsource/scanner/cli/ScannerEngineBootstrapperFactoryTest.java @@ -36,8 +36,7 @@ import static org.mockito.Mockito.when; class ScannerEngineBootstrapperFactoryTest { private final Properties props = new Properties(); - private final Logs logs = mock(Logs.class); - private final ScannerEngineBootstrapperFactory underTest = new ScannerEngineBootstrapperFactory(logs); + private final ScannerEngineBootstrapperFactory underTest = new ScannerEngineBootstrapperFactory(); @Test void should_create_engine_bootstrapper_and_pass_app_and_properties() { diff --git a/src/test/java/org/sonarsource/scanner/cli/Slf4jLogOutputTest.java b/src/test/java/org/sonarsource/scanner/cli/Slf4jLogOutputTest.java new file mode 100644 index 0000000..adf29f4 --- /dev/null +++ b/src/test/java/org/sonarsource/scanner/cli/Slf4jLogOutputTest.java @@ -0,0 +1,51 @@ +/* + * SonarScanner CLI + * Copyright (C) 2011-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.scanner.cli; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.event.Level; +import org.sonarsource.scanner.lib.LogOutput; +import testutils.LogTester; + +import static org.assertj.core.api.Assertions.assertThat; + +class Slf4jLogOutputTest { + + @RegisterExtension + LogTester logTester = new LogTester().setLevel(Level.TRACE); + + @Test + void make_coverage_happy() { + var underTest = new Slf4jLogOutput(); + underTest.log("trace", LogOutput.Level.TRACE); + underTest.log("debug", LogOutput.Level.DEBUG); + underTest.log("info", LogOutput.Level.INFO); + underTest.log("warn", LogOutput.Level.WARN); + underTest.log("error", LogOutput.Level.ERROR); + + assertThat(logTester.logs(Level.TRACE)).containsOnly("trace"); + assertThat(logTester.logs(Level.DEBUG)).containsOnly("debug"); + assertThat(logTester.logs(Level.INFO)).containsOnly("info"); + assertThat(logTester.logs(Level.WARN)).containsOnly("warn"); + assertThat(logTester.logs(Level.ERROR)).containsOnly("error"); + } + +} diff --git a/src/test/java/org/sonarsource/scanner/cli/StatsTest.java b/src/test/java/org/sonarsource/scanner/cli/StatsTest.java index a39e9ec..4895640 100644 --- a/src/test/java/org/sonarsource/scanner/cli/StatsTest.java +++ b/src/test/java/org/sonarsource/scanner/cli/StatsTest.java @@ -19,25 +19,24 @@ */ package org.sonarsource.scanner.cli; -import java.io.PrintStream; import org.junit.jupiter.api.Test; -import org.mockito.Mockito; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.event.Level; +import testutils.LogTester; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; class StatsTest { - private final PrintStream stdOut = mock(PrintStream.class); - private final PrintStream stdErr = mock(PrintStream.class); - private final Logs logs = new Logs(stdOut, stdErr); + @RegisterExtension + LogTester logTester = new LogTester(); @Test void shouldPrintStats() { - new Stats(logs).start().stop(); + new Stats().start().stop(); - verify(stdOut).println(Mockito.contains("Total time: ")); - verify(stdOut).println(Mockito.contains("Final Memory: ")); + assertThat(logTester.logs(Level.INFO)).hasSize(2); + assertThat(logTester.logs(Level.INFO).get(0)).startsWith("Total time: "); + assertThat(logTester.logs(Level.INFO).get(1)).startsWith("Final Memory: "); } @Test diff --git a/src/test/java/org/sonarsource/scanner/cli/SystemInfoTest.java b/src/test/java/org/sonarsource/scanner/cli/SystemInfoTest.java index 7344270..b372d9c 100644 --- a/src/test/java/org/sonarsource/scanner/cli/SystemInfoTest.java +++ b/src/test/java/org/sonarsource/scanner/cli/SystemInfoTest.java @@ -21,18 +21,21 @@ package org.sonarsource.scanner.cli; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.event.Level; import org.sonarsource.scanner.cli.SystemInfo.System2; +import testutils.LogTester; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; class SystemInfoTest { + @RegisterExtension + LogTester logTester = new LogTester(); + private final System2 mockSystem = mock(System2.class); - private final Logs logs = mock(Logs.class); @BeforeEach void setUp() { @@ -76,18 +79,14 @@ class SystemInfoTest { mockJava(); when(mockSystem.getenv("SONAR_SCANNER_OPTS")).thenReturn("arg"); - SystemInfo.print(logs); + SystemInfo.print(); verify(mockSystem).getProperty("java.version"); verify(mockSystem).getProperty("os.version"); verify(mockSystem).getenv("SONAR_SCANNER_OPTS"); - verify(logs, never()).info("SonarScanner null"); - verify(logs).info("SonarScanner CLI " + ScannerVersion.version()); - verify(logs).info("Java 1.9 oracle (64-bit)"); - verify(logs).info("linux 2.5 x64"); - verify(logs).info("SONAR_SCANNER_OPTS=arg"); - verifyNoMoreInteractions(logs); + assertThat(logTester.logs(Level.INFO)) + .containsOnly("SonarScanner CLI " + ScannerVersion.version(), "Java 1.9 oracle (64-bit)", "linux 2.5 x64", "SONAR_SCANNER_OPTS=arg"); } @Test @@ -97,8 +96,8 @@ class SystemInfoTest { when(mockSystem.getenv("SONAR_SCANNER_OPTS")) .thenReturn("-Dsonar.login=login -Dsonar.whatever=whatever -Dsonar.password=password -Dsonar.whatever2=whatever2 -Dsonar.token=token"); - SystemInfo.print(logs); + SystemInfo.print(); - verify(logs).info("SONAR_SCANNER_OPTS=-Dsonar.login=* -Dsonar.whatever=whatever -Dsonar.password=* -Dsonar.whatever2=whatever2 -Dsonar.token=*"); + assertThat(logTester.logs(Level.INFO)).contains("SONAR_SCANNER_OPTS=-Dsonar.login=* -Dsonar.whatever=whatever -Dsonar.password=* -Dsonar.whatever2=whatever2 -Dsonar.token=*"); } } diff --git a/src/test/java/testutils/ConcurrentListAppender.java b/src/test/java/testutils/ConcurrentListAppender.java new file mode 100644 index 0000000..b31e34e --- /dev/null +++ b/src/test/java/testutils/ConcurrentListAppender.java @@ -0,0 +1,32 @@ +/* + * SonarScanner CLI + * Copyright (C) 2011-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package testutils; + +import ch.qos.logback.core.AppenderBase; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; + +public class ConcurrentListAppender<E> extends AppenderBase<E> { + public final Queue<E> list = new ConcurrentLinkedQueue<E>(); + + protected void append(E e) { + list.add(e); + } +} diff --git a/src/test/java/testutils/LogTester.java b/src/test/java/testutils/LogTester.java new file mode 100644 index 0000000..1708479 --- /dev/null +++ b/src/test/java/testutils/LogTester.java @@ -0,0 +1,91 @@ +/* + * SonarScanner CLI + * Copyright (C) 2011-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package testutils; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.LoggingEvent; +import java.util.List; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.slf4j.LoggerFactory; +import org.slf4j.event.Level; + +public class LogTester implements BeforeEachCallback, AfterEachCallback { + + private final ConcurrentListAppender<ILoggingEvent> listAppender = new ConcurrentListAppender<>(); + + public LogTester() { + setLevel(Level.INFO); + } + + /** + * Change log level. + * By default, INFO logs are enabled when LogTester is started. + */ + public LogTester setLevel(Level level) { + getRootLogger().setLevel(ch.qos.logback.classic.Level.fromLocationAwareLoggerInteger(level.toInt())); + return this; + } + + private static ch.qos.logback.classic.Logger getRootLogger() { + return (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); + } + + /** + * Logs in chronological order (item at index 0 is the oldest one) + */ + public List<String> logs() { + return listAppender.list.stream().map(e -> (LoggingEvent) e) + .map(LoggingEvent::getFormattedMessage) + .toList(); + } + + /** + * Logs in chronological order (item at index 0 is the oldest one) for + * a given level + */ + public List<String> logs(Level level) { + return listAppender.list.stream().map(e -> (LoggingEvent) e) + .filter(e -> e.getLevel().equals(ch.qos.logback.classic.Level.fromLocationAwareLoggerInteger(level.toInt()))) + .map(LoggingEvent::getFormattedMessage) + .toList(); + } + + public LogTester clear() { + listAppender.list.clear(); + return this; + } + + @Override + public void beforeEach(ExtensionContext context) throws Exception { + getRootLogger().addAppender(listAppender); + listAppender.start(); + } + + @Override + public void afterEach(ExtensionContext context) throws Exception { + listAppender.stop(); + listAppender.list.clear(); + getRootLogger().detachAppender(listAppender); + // Reset the level for following-up test suites + setLevel(Level.INFO); + } +} |