diff --git a/its/plugin/pom.xml b/its/plugin/pom.xml index b23513f23c1..3589c6d12ab 100644 --- a/its/plugin/pom.xml +++ b/its/plugin/pom.xml @@ -24,6 +24,38 @@ false + + + org.junit.jupiter + junit-jupiter + test + + + org.junit.platform + junit-platform-suite + test + + + org.sonarsource.sonarlint.core + sonarlint-core-test-utils + ${sonarlint.plugin.api.version} + test + + + + org.slf4j + slf4j-api + ${slf4j.test.version} + test + + + org.awaitility + awaitility + 4.3.0 + test + + + it-plugin diff --git a/its/plugin/tests/src/test/java/com/sonar/it/java/suite/JavaTestSuite.java b/its/plugin/tests/src/test/java/com/sonar/it/java/suite/JavaTestSuite.java index 4a8d5cb099e..5df38098276 100644 --- a/its/plugin/tests/src/test/java/com/sonar/it/java/suite/JavaTestSuite.java +++ b/its/plugin/tests/src/test/java/com/sonar/it/java/suite/JavaTestSuite.java @@ -48,7 +48,7 @@ Struts139Test.class, JavaClasspathTest.class, SuppressWarningTest.class, - SonarLintTest.class, + SonarLintIntegrationTest.class, ExternalReportTest.class, DuplicationTest.class, MultiModuleTelemetryTest.class diff --git a/its/plugin/tests/src/test/java/com/sonar/it/java/suite/SonarLintIntegrationTest.java b/its/plugin/tests/src/test/java/com/sonar/it/java/suite/SonarLintIntegrationTest.java new file mode 100644 index 00000000000..438c7d76797 --- /dev/null +++ b/its/plugin/tests/src/test/java/com/sonar/it/java/suite/SonarLintIntegrationTest.java @@ -0,0 +1,90 @@ +/* + * SonarQube Java + * Copyright (C) SonarSource Sàrl + * mailto:info AT sonarsource DOT com + * + * You can redistribute and/or modify this program under the terms of + * the Sonar Source-Available License Version 1, as published by SonarSource Sàrl. + * + * 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 Sonar Source-Available License for more details. + * + * You should have received a copy of the Sonar Source-Available License + * along with this program; if not, see https://sonarsource.com/license/ssal/ + */ +package com.sonar.it.java.suite; + +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesAndTrackParams; +import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesResponse; +import org.sonarsource.sonarlint.core.rpc.protocol.client.issue.RaisedIssueDto; +import org.sonarsource.sonarlint.core.rpc.protocol.common.ClientFileDto; +import org.sonarsource.sonarlint.core.rpc.protocol.common.Language; +import org.sonarsource.sonarlint.core.test.utils.SonarLintBackendFixture; +import org.sonarsource.sonarlint.core.test.utils.SonarLintTestRpcServer; +import org.sonarsource.sonarlint.core.test.utils.junit5.SonarLintTest; +import org.sonarsource.sonarlint.core.test.utils.junit5.SonarLintTestHarness; +import org.sonarsource.sonarlint.core.test.utils.plugins.Plugin; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.junit.Assert.fail; + +public class SonarLintIntegrationTest { + private static final String CONFIG_SCOPE_ID = "CONFIG_SCOPE_ID"; + + private static Path pluginPath; + + private SonarLintBackendFixture.FakeSonarLintRpcClient client; + private SonarLintTestRpcServer backend; + + @SonarLintTest + void test() { + fail("fail () in SLIT"); + } + + private List analyzeFileAndGetIssues(URI fileUri) { + UUID analysisId = UUID.randomUUID(); + AnalyzeFilesAndTrackParams params = new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, analysisId, List.of(fileUri), Map.of(), false); + AnalyzeFilesResponse analysisResult = + backend + .getAnalysisService() + .analyzeFilesAndTrack(params) + .join(); + assertThat(analysisResult.getFailedAnalysisFiles()).isEmpty(); + await() + .atMost(15, TimeUnit.SECONDS) + .untilAsserted(() -> assertThat(client.getRaisedIssuesForScopeIdAsList(CONFIG_SCOPE_ID)).isNotEmpty()); + return client.getRaisedIssuesForScopeId(CONFIG_SCOPE_ID).get(fileUri); + } + + private void initWithFiles(SonarLintTestHarness harness, Path baseDir, ClientFileDto fileDTOs) { + client = harness + .newFakeClient() + .withInitialFs(CONFIG_SCOPE_ID, baseDir, List.of(fileDTOs)) + .build(); + + backend = harness + .newBackend() + .withStandaloneEmbeddedPluginAndEnabledLanguage(new Plugin(Set.of(Language.RUBY), pluginPath, "", "")) + .withUnboundConfigScope(CONFIG_SCOPE_ID) + .start(client); + } + + private static ClientFileDto createFile(Path folderPath, String fileName, String content) throws IOException { + Path filePath = folderPath.resolve(fileName); + Files.writeString(filePath, content); + return new ClientFileDto( + filePath.toUri(), folderPath.relativize(filePath), CONFIG_SCOPE_ID, false, null, filePath, null, Language.RUBY, true); + } +} diff --git a/its/plugin/tests/src/test/java/com/sonar/it/java/suite/SonarLintTest.java b/its/plugin/tests/src/test/java/com/sonar/it/java/suite/SonarLintTest.java deleted file mode 100644 index 9734109709c..00000000000 --- a/its/plugin/tests/src/test/java/com/sonar/it/java/suite/SonarLintTest.java +++ /dev/null @@ -1,391 +0,0 @@ -/* - * SonarQube Java - * Copyright (C) SonarSource Sàrl - * mailto:info AT sonarsource DOT com - * - * You can redistribute and/or modify this program under the terms of - * the Sonar Source-Available License Version 1, as published by SonarSource Sàrl. - * - * 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 Sonar Source-Available License for more details. - * - * You should have received a copy of the Sonar Source-Available License - * along with this program; if not, see https://sonarsource.com/license/ssal/ - */ -package com.sonar.it.java.suite; - -import com.google.common.io.Files; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.function.Consumer; -import java.util.stream.Stream; -import org.apache.commons.io.FileUtils; -import org.jetbrains.annotations.Nullable; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.sonar.api.batch.fs.InputFile; -import org.sonarsource.sonarlint.core.analysis.AnalysisEngine; -import org.sonarsource.sonarlint.core.analysis.api.ActiveRule; -import org.sonarsource.sonarlint.core.analysis.api.AnalysisConfiguration; -import org.sonarsource.sonarlint.core.analysis.api.AnalysisEngineConfiguration; -import org.sonarsource.sonarlint.core.analysis.api.AnalysisResults; -import org.sonarsource.sonarlint.core.analysis.api.ClientInputFile; -import org.sonarsource.sonarlint.core.analysis.api.ClientModuleFileSystem; -import org.sonarsource.sonarlint.core.analysis.api.ClientModuleInfo; -import org.sonarsource.sonarlint.core.analysis.api.Issue; -import org.sonarsource.sonarlint.core.analysis.command.AnalyzeCommand; -import org.sonarsource.sonarlint.core.analysis.command.RegisterModuleCommand; -import org.sonarsource.sonarlint.core.commons.api.SonarLanguage; -import org.sonarsource.sonarlint.core.commons.log.LogOutput; -import org.sonarsource.sonarlint.core.commons.log.SonarLintLogger; -import org.sonarsource.sonarlint.core.commons.progress.ProgressMonitor; -import org.sonarsource.sonarlint.core.plugin.commons.PluginsLoader; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.tuple; - -public class SonarLintTest { - private static final LogOutput NOOP_LOG_OUTPUT = new LogOutput() { - @Override - public void log(@Nullable String formattedMessage, Level level, @Nullable String stacktrace) { - /*Don't pollute logs*/ - } - }; - - @ClassRule - public static TemporaryFolder temp = new TemporaryFolder(); - - private static AnalysisEngine sonarlintEngine; - private static File baseDir; - - private final ProgressMonitor progressMonitor = new ProgressMonitor(null); - - @BeforeClass - public static void prepare() throws Exception { - AnalysisEngineConfiguration config = AnalysisEngineConfiguration.builder() - .setWorkDir(temp.getRoot().toPath()) - .build(); - - SonarLintLogger.setTarget(NOOP_LOG_OUTPUT); - var pluginJarLocation = Set.of(JavaTestSuite.JAVA_PLUGIN_LOCATION.getFile().toPath()); - var enabledLanguages = Set.of(SonarLanguage.JAVA); - var pluginConfiguration = new PluginsLoader.Configuration(pluginJarLocation, enabledLanguages, false, Optional.empty()); - var loadedPlugins = new PluginsLoader().load(pluginConfiguration, Set.of()).getLoadedPlugins(); - - sonarlintEngine = new AnalysisEngine(config, loadedPlugins, NOOP_LOG_OUTPUT); - baseDir = temp.newFolder(); - } - - @AfterClass - public static void stop() { - SonarLintLogger.setTarget(null); - sonarlintEngine.stop(); - } - - @Test - public void simpleJava() throws Exception { - ClientInputFile inputFile = prepareInputFile("Foo.java", """ - public class Foo { - public void foo() { - int x; - System.out.println("Foo"); - System.out.println("Foo"); //NOSONAR - } - } - """, - false); - - final List issues = new ArrayList<>(); - AnalysisConfiguration configuration = AnalysisConfiguration.builder() - .setBaseDir(baseDir.toPath()) - .addInputFile(inputFile) - .addActiveRules( - new ActiveRule("java:S106", SonarLanguage.JAVA.name()), - new ActiveRule("java:S1220", SonarLanguage.JAVA.name()), - new ActiveRule("java:S1481", SonarLanguage.JAVA.name()) - ).build(); - - ClientModuleFileSystem clientFileSystem = getClientModuleFileSystem(inputFile); - sonarlintEngine.post(new RegisterModuleCommand(new ClientModuleInfo("myModule", clientFileSystem)), progressMonitor).get(); - var command = new AnalyzeCommand("myModule", configuration, issues::add, NOOP_LOG_OUTPUT); - sonarlintEngine.post(command, progressMonitor).get(); - - assertThat(issues).extracting("ruleKey", "startLine", "inputFile.path", "overriddenImpacts").containsExactlyInAnyOrder( - tuple("java:S106", 4, inputFile.getPath(), Map.of()), - tuple("java:S1220", null, inputFile.getPath(), Map.of()), - tuple("java:S1481", 3, inputFile.getPath(), Map.of()) - ); - } - - @Test - public void simpleTestFileJava() throws Exception { - ClientInputFile inputFile = prepareInputFile("FooTest.java", - "public class FooTest {\n" - + " @org.junit.Test\n" - + " @org.junit.Ignore\n" - + " public void testName() throws Exception {\n" // S1607(ignored test) - + " Foo foo = new Foo();\n" - + " org.assertj.core.api.Assertions.assertThat(foo.isFooActive());\n" // S2970(incomplete assertions) - requires semantic - + " java.lang.Thread.sleep(Long.MAX_VALUE);" // S2925(thread.sleep in test) - + " }\n\n" - - + " private static class Foo {" - + " public boolean isFooActive() {" - + " return false;" - + " }" - + " }" - + "}", - true); - - final List issues = new ArrayList<>(); - AnalysisConfiguration configuration = AnalysisConfiguration.builder() - .setBaseDir(baseDir.toPath()) - .addInputFile(inputFile) - .addActiveRules( - new ActiveRule("java:S1220", SonarLanguage.JAVA.name()), - new ActiveRule("java:S2925", SonarLanguage.JAVA.name()) - ).build(); - ClientModuleFileSystem clientFileSystem = getClientModuleFileSystem(inputFile); - - sonarlintEngine.post(new RegisterModuleCommand(new ClientModuleInfo("myModule", clientFileSystem)), progressMonitor).get(); - var command = new AnalyzeCommand("myModule", configuration, issues::add, NOOP_LOG_OUTPUT); - sonarlintEngine.post(command, progressMonitor).get(); - - assertThat(issues).extracting("ruleKey", "startLine", "inputFile.path", "overriddenImpacts").containsOnly( - tuple("java:S2925", 7, inputFile.getPath(), Map.of()), - // expected issue - tuple("java:S1220", null, inputFile.getPath(), Map.of())); - } - - @Test - public void supportJavaSuppressWarning() throws Exception { - ClientInputFile inputFile = prepareInputFile("Foo.java", """ - public class Foo { - @SuppressWarnings("java:S106") - public void foo() { - int x; - System.out.println("Foo"); - System.out.println("Foo"); //NOSONAR - } - } - """, - false); - - final List issues = new ArrayList<>(); - AnalysisConfiguration configuration = AnalysisConfiguration.builder() - .setBaseDir(baseDir.toPath()) - .addInputFile(inputFile) - .addActiveRules( - new ActiveRule("java:S1220", SonarLanguage.JAVA.name()), - new ActiveRule("java:S1481", SonarLanguage.JAVA.name()) - ) - .build(); - - - ClientModuleFileSystem clientFileSystem = getClientModuleFileSystem(inputFile); - - sonarlintEngine.post(new RegisterModuleCommand(new ClientModuleInfo("myModule", clientFileSystem)), progressMonitor).get(); - var command = new AnalyzeCommand("myModule", configuration, issues::add, NOOP_LOG_OUTPUT); - sonarlintEngine.post(command, progressMonitor).get(); - - assertThat(issues).extracting("ruleKey", "startLine", "inputFile.path", "overriddenImpacts").containsOnly( - tuple("java:S1220", null, inputFile.getPath(), Map.of()), - tuple("java:S1481", 4, inputFile.getPath(), Map.of())); - } - - @Test - public void parse_error_should_report_analysis_error() throws Exception { - ClientInputFile inputFile = prepareInputFile("ParseError.java", "class ParseError {", false); - final List issues = new ArrayList<>(); - AnalysisConfiguration configuration = AnalysisConfiguration.builder() - .setBaseDir(baseDir.toPath()) - .addInputFile(inputFile) - .build(); - - ClientModuleFileSystem clientFileSystem = getClientModuleFileSystem(inputFile); - - sonarlintEngine.post(new RegisterModuleCommand(new ClientModuleInfo("myModule", clientFileSystem)), progressMonitor).get(); - var command = new AnalyzeCommand("myModule", configuration, issues::add, NOOP_LOG_OUTPUT); - AnalysisResults analysisResults = sonarlintEngine.post(command, progressMonitor).get(); - - assertThat(issues).isEmpty(); - assertThat(analysisResults.failedAnalysisFiles()).hasSize(1); - } - - @Test - public void sonarlint_cancelled_analysis_logs_but_does_not_rethrow_exception() throws Exception { - List logLevels = new ArrayList<>(); - List errorLogs = new ArrayList<>(); - - AnalysisEngineConfiguration engineConfiguration = AnalysisEngineConfiguration.builder() - .setWorkDir(temp.getRoot().toPath()) - .build(); - - LogOutput levelCollector = new LogOutput() { - @Override - public void log(String formattedMessage, Level level, @Nullable String stacktrace) { - logLevels.add(level); - } - }; - SonarLintLogger.setTarget(levelCollector); - var pluginJarLocation = Set.of(JavaTestSuite.JAVA_PLUGIN_LOCATION.getFile().toPath()); - var enabledLanguages = Set.of(SonarLanguage.JAVA); - var pluginConfiguration = new PluginsLoader.Configuration(pluginJarLocation, enabledLanguages, false, Optional.empty()); - var loadedPlugins = new PluginsLoader().load(pluginConfiguration, Set.of()).getLoadedPlugins(); - - AnalysisEngine specificSonarlintEngine = new AnalysisEngine(engineConfiguration, loadedPlugins, NOOP_LOG_OUTPUT); - - ClientInputFile inputFile = prepareInputFile("Foo.java", """ - public class Foo { - @SuppressWarnings("java:S106") - public void foo() { - int x; - System.out.println("Foo"); - System.out.println("Foo"); //NOSONAR - } - } - """, - false); - - AnalysisConfiguration analysisConfiguration = AnalysisConfiguration.builder() - .setBaseDir(baseDir.toPath()) - .addActiveRules( - new ActiveRule("java:S1220", SonarLanguage.JAVA.name()), - new ActiveRule("java:S1481", SonarLanguage.JAVA.name()) - ) - .addInputFile(inputFile) - .build(); - - final List issues = new ArrayList<>(); - - CancellableProgressMonitor cancellableProgressMonitor = new CancellableProgressMonitor(); - - ClientModuleFileSystem clientFileSystem = getClientModuleFileSystem(inputFile); - - specificSonarlintEngine.post(new RegisterModuleCommand(new ClientModuleInfo("myModule", clientFileSystem)), progressMonitor).get(); - Consumer issueListener = issue -> { - if (!issues.isEmpty()) { - cancellableProgressMonitor.isCanceled = true; - throw new MyCancelException(); - } - issues.add(issue); - }; - LogOutput errorCollector = new LogOutput() { - @Override - public void log(@Nullable String formattedMessage, Level level, @Nullable String stacktrace) { - if (level == LogOutput.Level.ERROR) { - errorLogs.add(formattedMessage); - } - } - }; - var command = new AnalyzeCommand("myModule", - analysisConfiguration, - issueListener, - errorCollector - ); - specificSonarlintEngine.post(command, cancellableProgressMonitor).get(); - - // Check that there were no error logs prior to the analysis, as the log levels are not collected DURING the analysis - assertThat(logLevels).doesNotContain(LogOutput.Level.ERROR); - assertThat(errorLogs) - .containsOnly("Error executing sensor: 'JavaSensor'"); - assertThat(issues).hasSize(1); - } - - private ClientInputFile prepareInputFile(String relativePath, String content, final boolean isTest) throws IOException { - final File file = new File(baseDir, relativePath); - FileUtils.write(file, content, StandardCharsets.UTF_8); - return createInputFile(file.toPath(), isTest); - } - - private ClientInputFile createInputFile(final Path path, final boolean isTest) { - return new ClientInputFile() { - - @Override - public String getPath() { - return path.toString(); - } - - @Override - public String relativePath() { - return baseDir.toPath().relativize(path).toString(); - } - - @Override - public URI uri() { - return path.toUri(); - } - - @Override - public boolean isTest() { - return isTest; - } - - @Override - public Charset getCharset() { - return StandardCharsets.UTF_8; - } - - @Override - public G getClientObject() { - return null; - } - - @Override - public InputStream inputStream() throws IOException { - return new FileInputStream(path.toFile()); - } - - @Override - public String contents() throws IOException { - return Files.asCharSource(path.toFile(), StandardCharsets.UTF_8).read(); - } - }; - } - - private static ClientModuleFileSystem getClientModuleFileSystem(ClientInputFile inputFile) { - return new ClientModuleFileSystem() { - @Override - public Stream files(String s, InputFile.Type type) { - return Stream.of(inputFile); - } - - @Override - public Stream files() { - return Stream.of(inputFile); - } - }; - } - - static class MyCancelException extends RuntimeException { - } - - static class CancellableProgressMonitor extends ProgressMonitor { - boolean isCanceled = false; - - CancellableProgressMonitor() { - super(null); - } - - @Override - public boolean isCanceled() { - return isCanceled; - } - } -} diff --git a/pom.xml b/pom.xml index 0d14f365ebf..4aafcd1a639 100644 --- a/pom.xml +++ b/pom.xml @@ -94,7 +94,7 @@ at SonarSource/sonarqube/gradle.properties --> 13.0.0.3026 - 10.13.0.79996 + 11.3.0.85510 its/**,java-checks-test-sources/** 2.22.0.4796 6.1.0.3962 @@ -114,7 +114,10 @@ org.jacoco.agent-${version.jacoco.plugin}-runtime.jar mockito-core-${mockito-core.version}.jar + 1.7.30 + + 2.0.18