diff --git a/client/java-client-legacy/src/test/java/org/sonarsource/sonarlint/core/client/legacy/analysis/SonarLintAnalysisEngineTests.java b/client/java-client-legacy/src/test/java/org/sonarsource/sonarlint/core/client/legacy/analysis/SonarLintAnalysisEngineTests.java deleted file mode 100644 index d5ed937f58..0000000000 --- a/client/java-client-legacy/src/test/java/org/sonarsource/sonarlint/core/client/legacy/analysis/SonarLintAnalysisEngineTests.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * SonarLint Core - Java Client Legacy - * Copyright (C) 2016-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.sonarlint.core.client.legacy.analysis; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CompletableFuture; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.junit.jupiter.api.io.TempDir; -import org.mockito.ArgumentCaptor; -import org.sonarsource.sonarlint.core.analysis.AnalysisEngine; -import org.sonarsource.sonarlint.core.analysis.api.AnalysisResults; -import org.sonarsource.sonarlint.core.analysis.api.ClientInputFile; -import org.sonarsource.sonarlint.core.analysis.command.AnalyzeCommand; -import org.sonarsource.sonarlint.core.analysis.command.Command; -import org.sonarsource.sonarlint.core.client.utils.ClientLogOutput; -import org.sonarsource.sonarlint.core.commons.api.TextRange; -import org.sonarsource.sonarlint.core.commons.api.progress.ClientProgressMonitor; -import org.sonarsource.sonarlint.core.commons.log.SonarLintLogTester; -import org.sonarsource.sonarlint.core.rpc.protocol.SonarLintRpcServer; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.ActiveRuleDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalysisRpcService; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.GetAnalysisConfigResponse; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.GetGlobalConfigurationResponse; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.GetRuleDetailsResponse; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.rules.VulnerabilityProbability; -import org.sonarsource.sonarlint.core.rpc.protocol.common.CleanCodeAttribute; -import org.sonarsource.sonarlint.core.rpc.protocol.common.IssueSeverity; -import org.sonarsource.sonarlint.core.rpc.protocol.common.Language; -import org.sonarsource.sonarlint.core.rpc.protocol.common.RuleType; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -class SonarLintAnalysisEngineTests { - - - @TempDir - private Path basedir; - @RegisterExtension - private static final SonarLintLogTester logTester = new SonarLintLogTester(); - private static final String CONNECTION_ID = "connectionId"; - boolean issueWasReported = false; - GetAnalysisConfigResponse getAnalysisConfigResponse; - GetGlobalConfigurationResponse getGlobalConfigurationResponse; - AnalysisRpcService analysisRpcService; - SonarLintAnalysisEngine underTest; - SonarLintRpcServer backend; - AnalysisEngine analysisEngine; - - @BeforeEach - void init() { - var engineConfiguration = mock(EngineConfiguration.class); - var logOutput = mock(ClientLogOutput.class); - when(engineConfiguration.getLogOutput()).thenReturn(logOutput); - when(engineConfiguration.getWorkDir()).thenReturn(basedir.resolve("workDir")); - backend = mock(SonarLintRpcServer.class); - analysisRpcService = mock(AnalysisRpcService.class); - getGlobalConfigurationResponse = mock(GetGlobalConfigurationResponse.class); - when(getGlobalConfigurationResponse.getEnabledLanguages()).thenReturn(List.of(Language.JAVA)); - getAnalysisConfigResponse = mock(GetAnalysisConfigResponse.class); - when(getAnalysisConfigResponse.getActiveRules()) - .thenReturn(List.of(new ActiveRuleDto("java:S1481", - "java", Map.of(), "java"))); - when(analysisRpcService.getGlobalConnectedConfiguration(any())) - .thenReturn(CompletableFuture.completedFuture(getGlobalConfigurationResponse)); - when(analysisRpcService.getAnalysisConfig(any())) - .thenReturn(CompletableFuture.completedFuture(getAnalysisConfigResponse)); - when(analysisRpcService.getRuleDetails(any())) - .thenReturn(CompletableFuture.completedFuture(new GetRuleDetailsResponse(IssueSeverity.BLOCKER, - RuleType.BUG, CleanCodeAttribute.CLEAR, List.of(), VulnerabilityProbability.HIGH))); - when(backend.getAnalysisService()).thenReturn(analysisRpcService); - underTest = new SonarLintAnalysisEngine(engineConfiguration, backend, CONNECTION_ID); - analysisEngine = mock(AnalysisEngine.class); - when(analysisEngine.post(any(), any())) - .thenReturn(CompletableFuture.completedFuture(new AnalysisResults())); - underTest.analysisEngine.set(analysisEngine); - } - - @Test - void shouldSkipIssueReportingIfRuleWasDisabledDuringAnalysis() throws IOException { - var configScopeId = "configScopeId"; - var analysisConfiguration = mock(AnalysisConfiguration.class); - var logOutput = mock(ClientLogOutput.class); - var progressMonitor = mock(ClientProgressMonitor.class); - when(analysisConfiguration.baseDir()).thenReturn(basedir); - var file = mock(ClientInputFile.class); - when(file.isTest()).thenReturn(false); - when(file.uri()).thenReturn(basedir.resolve("workDir").resolve("FileUri.java").toUri()); - when(file.contents()).thenReturn("package devoxx;\n" + - "\n" + - "public class FileUri {\n" + - " public static void main(String[] args) {\n" + - " int i = 0;\n" + - " }\n" + - "}"); - when(file.relativePath()).thenReturn("FileUri.java"); - when(analysisConfiguration.inputFiles()) - .thenReturn(List.of(file)); - var issue = mock(org.sonarsource.sonarlint.core.analysis.api.Issue.class); - when(issue.getRuleKey()).thenReturn("java:S1481"); - when(issue.getMessage()).thenReturn("message"); - when(issue.getTextRange()).thenReturn(mock(TextRange.class)); - when(issue.flows()).thenReturn(List.of()); - var captor = ArgumentCaptor.forClass(Command.class); - - underTest.analyze(analysisConfiguration, rawIssue -> issueWasReported = true, logOutput, progressMonitor, configScopeId); - verify(analysisEngine).post(captor.capture(), any()); - ((AnalyzeCommand) captor.getValue()).getIssueListener().accept(issue); - - assertTrue(issueWasReported); - } - - @Test - void shouldSkipIssueReportingIfRuleWasDisabledDuringAnalysisFoo() throws IOException { - var configScopeId = "configScopeId"; - var analysisConfiguration = mock(AnalysisConfiguration.class); - var logOutput = mock(ClientLogOutput.class); - var progressMonitor = mock(ClientProgressMonitor.class); - when(analysisConfiguration.baseDir()).thenReturn(basedir); - var file = mock(ClientInputFile.class); - when(file.isTest()).thenReturn(false); - when(file.uri()).thenReturn(basedir.resolve("workDir").resolve("FileUri.java").toUri()); - when(file.contents()).thenReturn("package devoxx;\n" + - "\n" + - "public class FileUri {\n" + - " public static void main(String[] args) {\n" + - " int i = 0;\n" + - " }\n" + - "}"); - when(file.relativePath()).thenReturn("FileUri.java"); - when(analysisConfiguration.inputFiles()) - .thenReturn(List.of(file)); - var issue = mock(org.sonarsource.sonarlint.core.analysis.api.Issue.class); - when(issue.getRuleKey()).thenReturn("java:S1481"); - when(issue.getMessage()).thenReturn("message"); - when(issue.getTextRange()).thenReturn(mock(TextRange.class)); - when(issue.flows()).thenReturn(List.of()); - when(analysisRpcService.getRuleDetails(any())) - .thenThrow(new RuntimeException()); - var captor = ArgumentCaptor.forClass(Command.class); - - underTest.analyze(analysisConfiguration, rawIssue -> issueWasReported = true, logOutput, progressMonitor, configScopeId); - verify(analysisEngine).post(captor.capture(), any()); - ((AnalyzeCommand) captor.getValue()).getIssueListener().accept(issue); - - assertFalse(issueWasReported); - } -} diff --git a/medium-tests/src/test/java/mediumtest/ConnectedHotspotMediumTests.java b/medium-tests/src/test/java/mediumtest/ConnectedHotspotMediumTests.java index 93f7937a4b..389a0f74ab 100644 --- a/medium-tests/src/test/java/mediumtest/ConnectedHotspotMediumTests.java +++ b/medium-tests/src/test/java/mediumtest/ConnectedHotspotMediumTests.java @@ -19,55 +19,36 @@ */ package mediumtest; -import java.io.File; -import java.io.IOException; -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.UUID; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import mediumtest.fixtures.SonarLintBackendFixture; import mediumtest.fixtures.SonarLintTestRpcServer; import mediumtest.fixtures.TestPlugin; -import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.io.TempDir; -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.client.legacy.analysis.AnalysisConfiguration; -import org.sonarsource.sonarlint.core.client.legacy.analysis.EngineConfiguration; -import org.sonarsource.sonarlint.core.client.legacy.analysis.RawIssue; -import org.sonarsource.sonarlint.core.client.legacy.analysis.RawIssueListener; -import org.sonarsource.sonarlint.core.client.legacy.analysis.SonarLintAnalysisEngine; -import org.sonarsource.sonarlint.core.commons.IssueSeverity; import org.sonarsource.sonarlint.core.commons.log.SonarLintLogTester; -import org.sonarsource.sonarlint.core.rpc.protocol.common.Language; -import org.sonarsource.sonarlint.core.rpc.protocol.common.TextRangeDto; -import testutils.MockWebServerExtensionWithProtobuf; -import testutils.TestUtils; +import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesAndTrackParams; +import org.sonarsource.sonarlint.core.rpc.protocol.common.ClientFileDto; +import org.sonarsource.sonarlint.core.rpc.protocol.common.IssueSeverity; +import static mediumtest.fixtures.ServerFixture.newSonarQubeServer; import static mediumtest.fixtures.SonarLintBackendFixture.newBackend; +import static mediumtest.fixtures.SonarLintBackendFixture.newFakeClient; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.tuple; -import static org.mockito.Mockito.mock; -import static testutils.TestUtils.createNoOpLogOutput; +import static org.awaitility.Awaitility.await; +import static org.sonarsource.sonarlint.core.rpc.protocol.common.Language.JAVA; +import static testutils.AnalysisUtils.createFile; class ConnectedHotspotMediumTests { - @RegisterExtension - private static final SonarLintLogTester logTester = new SonarLintLogTester(); - - @AfterEach - void stop() { - if (engine != null) { - engine.stop(); - engine = null; - } - } - private SonarLintTestRpcServer backend; + private static SonarLintBackendFixture.FakeSonarLintRpcClient client; @AfterEach void stopBackend() throws ExecutionException, InterruptedException { @@ -77,95 +58,53 @@ void stopBackend() throws ExecutionException, InterruptedException { } @Test - void should_not_locally_detect_hotspots_when_connected_to_a_never_synced_server(@TempDir Path baseDir) throws Exception { - createStorageAndEngine(null); - var inputFile = prepareJavaInputFile(baseDir); - - final List issues = new ArrayList<>(); - engine.analyze(AnalysisConfiguration.builder() - .setBaseDir(baseDir) - .addInputFile(inputFile) - .setModuleKey("key") - .build(), - new StoreIssueListener(issues), null, null, CONFIG_SCOPE_ID); - - assertThat(issues).isEmpty(); - } - - @Test - void should_locally_detect_hotspots_when_connected_to_sonarqube_9_9_plus(@TempDir Path baseDir) throws Exception { - createStorageAndEngine("9.9"); - var inputFile = prepareJavaInputFile(baseDir); - - final List issues = new ArrayList<>(); - engine.analyze(AnalysisConfiguration.builder() - .setBaseDir(baseDir) - .addInputFile(inputFile) - .setModuleKey("key") - .build(), - new StoreIssueListener(issues), null, null, CONFIG_SCOPE_ID); - - assertThat(issues).extracting("ruleKey", "textRange", "inputFile.path", "severity") - .usingRecursiveFieldByFieldElementComparator() - .containsOnly(tuple("java:S5852", new TextRangeDto(3, 28, 3, 35), inputFile.getPath(), IssueSeverity.BLOCKER)); - } - - private void createStorageAndEngine(String serverVersion) { + void should_locally_detect_hotspots_when_connected_to_sonarqube_9_9_plus(@TempDir Path baseDir) { + var inputFile = createFile(baseDir, "Foo.java", "public class Foo {\n" + + "\n" + + " void foo() {\n" + + " String password = \"blue\";\n" + + " }\n" + + "}\n"); + client = newFakeClient() + .withInitialFs(CONFIG_SCOPE_ID, List.of( + new ClientFileDto(inputFile.toUri(), baseDir.relativize(inputFile), CONFIG_SCOPE_ID, false, null, inputFile, null, null, true) + )) + .build(); + var projectKey = "projectKey"; + var branchName = "main"; + var server = newSonarQubeServer("9.9") + .withQualityProfile("qpKey", qualityProfile -> qualityProfile + .withLanguage("java").withActiveRule("java:S2068", activeRule -> activeRule + .withSeverity(org.sonarsource.sonarlint.core.rpc.protocol.common.IssueSeverity.BLOCKER) + )) + .withProject(projectKey, + project -> project + .withQualityProfile("qpKey") + .withBranch(branchName)) + .withPlugin(TestPlugin.JAVA) + .start(); backend = newBackend() - .withSonarQubeConnection(CONNECTION_ID, mockWebServer.url("/"), storage -> storage - .withServerVersion(serverVersion) - .withPlugin(TestPlugin.JAVA) - .withProject(JAVA_MODULE_KEY, project -> project - .withRuleSet("java", ruleSet -> ruleSet - .withActiveRule("java:S5852", "BLOCKER")))) - .withBoundConfigScope(CONFIG_SCOPE_ID, CONNECTION_ID, JAVA_MODULE_KEY) + .withFullSynchronization() .withSecurityHotspotsEnabled() - .withExtraEnabledLanguagesInConnectedMode(Language.JAVA) - .build(); - - var config = EngineConfiguration.builder() - .setSonarLintUserHome(backend.getUserHome()) - .setLogOutput(createNoOpLogOutput()) - .setModulesProvider(() -> List.of(new ClientModuleInfo("key", mock(ClientModuleFileSystem.class)))) - .build(); - engine = new SonarLintAnalysisEngine(config, backend, CONNECTION_ID); - } - - private ClientInputFile prepareJavaInputFile(Path baseDir) throws IOException { - return prepareInputFile(baseDir, "Foo.java", - "public class Foo {\n" - + " public void foo() {\n" - + " java.util.regex.Pattern.compile(\".*PATH=\\\"(.*)\\\"; export PATH;.*\");\n" - + " }\n" - + "}", - false); - } - - private ClientInputFile prepareInputFile(Path baseDir, String relativePath, String content, final boolean isTest) throws IOException { - final var file = new File(baseDir.toFile(), relativePath); - FileUtils.write(file, content, StandardCharsets.UTF_8); - return TestUtils.createInputFile(file.toPath(), relativePath, isTest); + .withSonarQubeConnection(CONNECTION_ID, server) + .withBoundConfigScope(CONFIG_SCOPE_ID, CONNECTION_ID, projectKey) + .withExtraEnabledLanguagesInConnectedMode(JAVA) + .build(client); + client.waitForSynchronization(); + + var analysisId = UUID.randomUUID(); + var analysisResult = backend.getAnalysisService().analyzeFilesAndTrack( + new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, analysisId, List.of(inputFile.toUri()), Map.of(), true, System.currentTimeMillis())) + .join(); + assertThat(analysisResult.getFailedAnalysisFiles()).isEmpty(); + await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> assertThat(client.getRaisedHotspotsForScopeIdAsList(CONFIG_SCOPE_ID)).isNotEmpty()); + + var hotspot = client.getRaisedHotspotsForScopeIdAsList(CONFIG_SCOPE_ID).get(0); + assertThat(hotspot.getRuleKey()).isEqualTo("java:S2068"); + assertThat(hotspot.getSeverity()).isEqualTo(IssueSeverity.BLOCKER); } - static class StoreIssueListener implements RawIssueListener { - private final List issues; - - StoreIssueListener(List issues) { - this.issues = issues; - } - - @Override - public void handle(RawIssue rawIssue) { - issues.add(rawIssue); - } - } - - @RegisterExtension - private final MockWebServerExtensionWithProtobuf mockWebServer = new MockWebServerExtensionWithProtobuf(); - private static final String CONNECTION_ID = StringUtils.repeat("very-long-id", 30); private static final String CONFIG_SCOPE_ID = "configScopeId"; - private static final String JAVA_MODULE_KEY = "test-project-2"; - private static SonarLintAnalysisEngine engine; } diff --git a/medium-tests/src/test/java/mediumtest/ConnectedIssueMediumTests.java b/medium-tests/src/test/java/mediumtest/ConnectedIssueMediumTests.java index 554d5c30e7..bc8beac8b0 100644 --- a/medium-tests/src/test/java/mediumtest/ConnectedIssueMediumTests.java +++ b/medium-tests/src/test/java/mediumtest/ConnectedIssueMediumTests.java @@ -19,203 +19,146 @@ */ package mediumtest; -import java.io.File; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.UUID; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; import mediumtest.fixtures.SonarLintTestRpcServer; import mediumtest.fixtures.TestPlugin; -import org.apache.commons.io.FileUtils; -import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.io.TempDir; -import org.sonarsource.api.sonarlint.SonarLintSide; -import org.sonarsource.sonarlint.core.analysis.api.ClientInputFile; -import org.sonarsource.sonarlint.core.analysis.api.ClientModuleFileEvent; -import org.sonarsource.sonarlint.core.analysis.api.ClientModuleFileSystem; -import org.sonarsource.sonarlint.core.analysis.api.ClientModuleInfo; -import org.sonarsource.sonarlint.core.analysis.container.module.ModuleContainer; -import org.sonarsource.sonarlint.core.analysis.sonarapi.SonarLintModuleFileSystem; -import org.sonarsource.sonarlint.core.client.legacy.analysis.AnalysisConfiguration; -import org.sonarsource.sonarlint.core.client.legacy.analysis.EngineConfiguration; -import org.sonarsource.sonarlint.core.client.legacy.analysis.PluginDetails; -import org.sonarsource.sonarlint.core.client.legacy.analysis.RawIssue; -import org.sonarsource.sonarlint.core.client.legacy.analysis.SonarLintAnalysisEngine; -import org.sonarsource.sonarlint.core.commons.IssueSeverity; +import org.sonarsource.sonarlint.core.rpc.protocol.common.IssueSeverity; import org.sonarsource.sonarlint.core.commons.log.SonarLintLogTester; -import org.sonarsource.sonarlint.core.nodejs.NodeJsHelper; -import org.sonarsource.sonarlint.core.rpc.protocol.common.TextRangeDto; -import org.sonarsource.sonarlint.plugin.api.module.file.ModuleFileEvent; -import org.sonarsource.sonarlint.plugin.api.module.file.ModuleFileListener; -import testutils.OnDiskTestClientInputFile; -import testutils.TestUtils; +import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesAndTrackParams; +import org.sonarsource.sonarlint.core.rpc.protocol.common.ClientFileDto; -import static mediumtest.fixtures.ClientFileSystemFixtures.aClientFileSystemWith; -import static mediumtest.fixtures.ClientFileSystemFixtures.anEmptyClientFileSystem; +import static mediumtest.fixtures.ServerFixture.newSonarQubeServer; import static mediumtest.fixtures.SonarLintBackendFixture.newBackend; +import static mediumtest.fixtures.SonarLintBackendFixture.newFakeClient; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; -import static org.mockito.Mockito.mock; +import static org.awaitility.Awaitility.await; import static org.sonarsource.sonarlint.core.rpc.protocol.common.Language.JAVA; -import static org.sonarsource.sonarlint.core.rpc.protocol.common.Language.JS; -import static testutils.TestUtils.createNoOpLogOutput; +import static testutils.AnalysisUtils.createFile; class ConnectedIssueMediumTests { @RegisterExtension private static final SonarLintLogTester logTester = new SonarLintLogTester(); - private static final String EMPTY_PROJECT_KEY = "test-project"; - private static final String CONNECTION_ID = StringUtils.repeat("very-long-id", 30); - private static final String JAVA_MODULE_KEY = "test-project-2"; - // TODO engine API - private static SonarLintAnalysisEngine engine; + private static final String CONFIG_SCOPE_ID = "configScopeId"; + private static final String PROJECT_KEY = "test-project"; + private static final String CONNECTION_ID = "connectionId"; private static SonarLintTestRpcServer backend; - @BeforeAll - static void prepare(@TempDir Path slHome) { - var nodeJsHelper = new NodeJsHelper(); - var detectedNodeJs = nodeJsHelper.detect(null); - - var config = EngineConfiguration.builder() - .setSonarLintUserHome(slHome) - .setLogOutput(createNoOpLogOutput()) - .setModulesProvider(() -> List.of(new ClientModuleInfo("key", mock(ClientModuleFileSystem.class)))) - .build(); - backend = newBackend() - .withSonarQubeConnection(CONNECTION_ID, storage -> storage.withPlugins(TestPlugin.JAVASCRIPT, TestPlugin.JAVA) - .withProject(EMPTY_PROJECT_KEY) - .withProject(JAVA_MODULE_KEY, project -> project - .withRuleSet("java", ruleSet -> ruleSet - .withActiveRule("java:S106", "MAJOR") - .withActiveRule("java:S1220", "MINOR") - .withActiveRule("java:S1481", "BLOCKER")))) - .withBoundConfigScope(JAVA_MODULE_KEY, CONNECTION_ID, JAVA_MODULE_KEY) - .withClientNodeJsPath(detectedNodeJs.getPath()) - .withEnabledLanguageInStandaloneMode(JAVA) - .withEnabledLanguageInStandaloneMode(JS) - .build(); - engine = new SonarLintAnalysisEngine(config, backend, CONNECTION_ID); - } - @AfterAll static void stop() throws ExecutionException, InterruptedException { - if (engine != null) { - engine.stop(); - engine = null; - } if (backend != null) { backend.shutdown().get(); } } @Test - void testContainerInfo() { - assertThat(engine.getPluginDetails()).extracting(PluginDetails::key).containsOnly("java", "javascript"); - } - - @Test - void simpleJavaBound(@TempDir Path baseDir) throws Exception { - var inputFile = prepareJavaInputFile(baseDir); - - final List issues = new ArrayList<>(); - engine.analyze(AnalysisConfiguration.builder() - .setBaseDir(baseDir) - .addInputFile(inputFile) - .setModuleKey("key") - .build(), - issues::add, null, null, JAVA_MODULE_KEY); - - assertThat(issues).extracting("ruleKey", "textRange", "inputFile.path", "severity") + void simpleJavaBound(@TempDir Path baseDir) { + var inputFile = createFile(baseDir, "Foo.java", + "public class Foo {\n" + + " public void foo() {\n" + + " int x;\n" + + " System.out.println(\"Foo\");\n" + + " System.out.println(\"Foo\"); //NOSONAR\n" + + " }\n" + + "}"); + var client = newFakeClient() + .withInitialFs(CONFIG_SCOPE_ID, List.of( + new ClientFileDto(inputFile.toUri(), baseDir.relativize(inputFile), CONFIG_SCOPE_ID, false, null, inputFile, null, null, true) + )) + .build(); + var projectKey = "projectKey"; + var branchName = "main"; + var server = newSonarQubeServer() + .withQualityProfile("qpKey", qualityProfile -> qualityProfile + .withLanguage("java") + .withActiveRule("java:S106", activeRule -> activeRule + .withSeverity(IssueSeverity.MAJOR)) + .withActiveRule("java:S1220", activeRule -> activeRule + .withSeverity(IssueSeverity.MINOR)) + .withActiveRule("java:S1481", activeRule -> activeRule + .withSeverity(IssueSeverity.BLOCKER)) + ) + .withProject(projectKey, + project -> project + .withQualityProfile("qpKey") + .withBranch(branchName)) + .withPlugin(TestPlugin.JAVA) + .start(); + backend = newBackend() + .withFullSynchronization() + .withSecurityHotspotsEnabled() + .withSonarQubeConnection(CONNECTION_ID, server) + .withBoundConfigScope(CONFIG_SCOPE_ID, CONNECTION_ID, projectKey) + .withExtraEnabledLanguagesInConnectedMode(JAVA) + .build(client); + client.waitForSynchronization(); + + var analysisId = UUID.randomUUID(); + var analysisResult = backend.getAnalysisService().analyzeFilesAndTrack( + new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, analysisId, List.of(inputFile.toUri()), Map.of(), true, System.currentTimeMillis())) + .join(); + assertThat(analysisResult.getFailedAnalysisFiles()).isEmpty(); + await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> assertThat(client.getRaisedIssuesForScopeIdAsList(CONFIG_SCOPE_ID)).isNotEmpty()); + + var issues = client.getRaisedIssuesForScopeIdAsList(CONFIG_SCOPE_ID); + assertThat(issues).extracting("ruleKey", "severity") .usingRecursiveFieldByFieldElementComparator() .containsOnly( - tuple("java:S106", new TextRangeDto(4, 4, 4, 14), inputFile.getPath(), IssueSeverity.MAJOR), - tuple("java:S1220", null, inputFile.getPath(), IssueSeverity.MINOR), - tuple("java:S1481", new TextRangeDto(3, 8, 3, 9), inputFile.getPath(), IssueSeverity.BLOCKER)); + tuple("java:S106", IssueSeverity.MAJOR), + tuple("java:S1220", IssueSeverity.MINOR), + tuple("java:S1481", IssueSeverity.BLOCKER)); } @Test void emptyQPJava(@TempDir Path baseDir) throws IOException { - var inputFile = prepareJavaInputFile(baseDir); - - final List issues = new ArrayList<>(); - engine.analyze(AnalysisConfiguration.builder() - .setBaseDir(baseDir) - .addInputFile(inputFile) - .setModuleKey("key") - .build(), - issues::add, null, null, EMPTY_PROJECT_KEY); - - assertThat(issues).isEmpty(); - } - - @Test - void declare_module_should_create_a_module_container_with_loaded_extensions() throws Exception { - engine - .declareModule(new ClientModuleInfo("key", aClientFileSystemWith(new OnDiskTestClientInputFile(Paths.get("main.py"), "main.py", false, StandardCharsets.UTF_8, null)))).get(); - - ModuleContainer moduleContainer = engine.getAnalysisEngine().getModuleRegistry().getContainerFor("key"); - - assertThat(moduleContainer).isNotNull(); - assertThat(moduleContainer.getComponentsByType(SonarLintModuleFileSystem.class)).isNotEmpty(); - } - - @Test - void stop_module_should_stop_the_module_container() throws Exception { - engine - .declareModule(new ClientModuleInfo("key", aClientFileSystemWith(new OnDiskTestClientInputFile(Paths.get("main.py"), "main.py", false, StandardCharsets.UTF_8, null)))).get(); - ModuleContainer moduleContainer = engine.getAnalysisEngine().getModuleRegistry().getContainerFor("key"); - - engine.stopModule("key").get(); - - assertThat(moduleContainer.getSpringContext().isActive()).isFalse(); - } - - @Test - void should_forward_module_file_event_to_listener() throws Exception { - // should not be located in global container in real life but easier for testing - var moduleFileListener = new FakeModuleFileListener(); - engine.getAnalysisEngine().getGlobalAnalysisContainer().add(moduleFileListener); - var clientInputFile = new OnDiskTestClientInputFile(Paths.get("main.py"), "main.py", false, StandardCharsets.UTF_8, null); - engine.declareModule(new ClientModuleInfo("moduleKey", anEmptyClientFileSystem())).get(); - - engine.fireModuleFileEvent("moduleKey", ClientModuleFileEvent.of(clientInputFile, ModuleFileEvent.Type.CREATED)).get(); - - assertThat(moduleFileListener.events).hasSize(1); - } - - @SonarLintSide(lifespan = "MODULE") - static class FakeModuleFileListener implements ModuleFileListener { - private final List events = new ArrayList<>(); - - @Override - public void process(ModuleFileEvent event) { - events.add(event); - } - } - - private ClientInputFile prepareJavaInputFile(Path baseDir) throws IOException { - return prepareInputFile(baseDir, "Foo.java", + var inputFile = createFile(baseDir, "Foo.java", "public class Foo {\n" + " public void foo() {\n" + " int x;\n" + " System.out.println(\"Foo\");\n" + " System.out.println(\"Foo\"); //NOSONAR\n" + " }\n" - + "}", - false); - } - - private ClientInputFile prepareInputFile(Path baseDir, String relativePath, String content, final boolean isTest) throws IOException { - final var file = new File(baseDir.toFile(), relativePath); - FileUtils.write(file, content, StandardCharsets.UTF_8); - return TestUtils.createInputFile(file.toPath(), relativePath, isTest); + + "}"); + var client = newFakeClient() + .withInitialFs(CONFIG_SCOPE_ID, List.of( + new ClientFileDto(inputFile.toUri(), baseDir.relativize(inputFile), CONFIG_SCOPE_ID, false, null, inputFile, null, null, true) + )) + .build(); + var projectKey = "projectKey"; + var branchName = "main"; + var server = newSonarQubeServer() + .withQualityProfile("qpKey", qualityProfile -> qualityProfile.withLanguage("java")) + .withProject(projectKey, + project -> project + .withQualityProfile("qpKey") + .withBranch(branchName)) + .withPlugin(TestPlugin.JAVA) + .start(); + backend = newBackend() + .withFullSynchronization() + .withSecurityHotspotsEnabled() + .withSonarQubeConnection(CONNECTION_ID, server) + .withBoundConfigScope(CONFIG_SCOPE_ID, CONNECTION_ID, projectKey) + .withExtraEnabledLanguagesInConnectedMode(JAVA) + .build(client); + client.waitForSynchronization(); + + var analysisId = UUID.randomUUID(); + var analysisResult = backend.getAnalysisService().analyzeFilesAndTrack( + new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, analysisId, List.of(inputFile.toUri()), Map.of(), true, System.currentTimeMillis())) + .join(); + assertThat(analysisResult.getFailedAnalysisFiles()).isEmpty(); + await().during(2, TimeUnit.SECONDS).untilAsserted(() -> assertThat(client.getRaisedIssuesForScopeIdAsList(CONFIG_SCOPE_ID)).isEmpty()); } - } diff --git a/medium-tests/src/test/java/mediumtest/StandaloneNoPluginMediumTests.java b/medium-tests/src/test/java/mediumtest/StandaloneNoPluginMediumTests.java index fd359c18af..4a88649370 100644 --- a/medium-tests/src/test/java/mediumtest/StandaloneNoPluginMediumTests.java +++ b/medium-tests/src/test/java/mediumtest/StandaloneNoPluginMediumTests.java @@ -19,77 +19,63 @@ */ package mediumtest; -import com.google.common.collect.LinkedListMultimap; -import com.google.common.collect.Multimap; -import java.io.File; -import java.io.IOException; -import java.nio.charset.StandardCharsets; +import java.nio.file.Path; import java.util.List; -import org.apache.commons.io.FileUtils; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import mediumtest.fixtures.SonarLintBackendFixture; +import mediumtest.fixtures.SonarLintTestRpcServer; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -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.client.legacy.analysis.AnalysisConfiguration; -import org.sonarsource.sonarlint.core.client.legacy.analysis.EngineConfiguration; -import org.sonarsource.sonarlint.core.client.legacy.analysis.SonarLintAnalysisEngine; -import org.sonarsource.sonarlint.core.client.utils.ClientLogOutput; -import org.sonarsource.sonarlint.core.commons.api.SonarLanguage; -import testutils.TestUtils; +import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesAndTrackParams; +import org.sonarsource.sonarlint.core.rpc.protocol.common.ClientFileDto; import static mediumtest.fixtures.SonarLintBackendFixture.newBackend; +import static mediumtest.fixtures.SonarLintBackendFixture.newFakeClient; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; +import static org.awaitility.Awaitility.await; +import static testutils.AnalysisUtils.createFile; class StandaloneNoPluginMediumTests { - private static final String CONFIGURATION_SCOPE_ID = "configScopeId"; - private SonarLintAnalysisEngine engine; - - @TempDir - private File baseDir; - private final Multimap logs = LinkedListMultimap.create(); - - @BeforeEach - void prepare() { - ClientLogOutput logOutput = (msg, level) -> logs.put(level, msg); - engine = new SonarLintAnalysisEngine(EngineConfiguration.builder() - .setLogOutput(logOutput) - .setModulesProvider(() -> List.of(new ClientModuleInfo("key", mock(ClientModuleFileSystem.class)))) - .build(), newBackend().build(), null); - } + private static final String CONFIG_SCOPE_ID = "configScopeId"; + private SonarLintTestRpcServer backend; + private static SonarLintBackendFixture.FakeSonarLintRpcClient client; @AfterEach - void stop() { - engine.stop(); + void stop() throws ExecutionException, InterruptedException { + if (backend != null) { + backend.shutdown().get(); + } } @Test - void dont_fail_and_detect_language_even_if_no_plugin() throws Exception { - - assertThat(engine.getPluginDetails()).isEmpty(); - - var inputFile = prepareInputFile("foo.js", "function foo() {var x;}", false); - - var results = engine.analyze( - AnalysisConfiguration.builder() - .setBaseDir(baseDir.toPath()) - .addInputFile(inputFile) - .build(), - i -> { - }, null, null, CONFIGURATION_SCOPE_ID); - - assertThat(results.indexedFileCount()).isEqualTo(1); - assertThat(results.languagePerFile()).containsEntry(inputFile, SonarLanguage.JS); - } + void dont_fail_and_detect_language_even_if_no_plugin(@TempDir Path baseDir) { + var inputFile = createFile(baseDir, "Foo.java", "public class Foo {\n" + + "\n" + + " void foo() {\n" + + " String password = \"blue\";\n" + + " }\n" + + "}\n"); + client = newFakeClient() + .withInitialFs(CONFIG_SCOPE_ID, List.of( + new ClientFileDto(inputFile.toUri(), baseDir.relativize(inputFile), CONFIG_SCOPE_ID, false, null, inputFile, null, null, true) + )) + .build(); + backend = newBackend() + .withSecurityHotspotsEnabled() + .withUnboundConfigScope(CONFIG_SCOPE_ID) + .build(client); - private ClientInputFile prepareInputFile(String relativePath, String content, final boolean isTest) throws IOException { - final var file = new File(baseDir, relativePath); - FileUtils.write(file, content, StandardCharsets.UTF_8); - return TestUtils.createInputFile(file.toPath(), relativePath, isTest); + var analysisId = UUID.randomUUID(); + var analysisResult = backend.getAnalysisService().analyzeFilesAndTrack( + new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, analysisId, List.of(inputFile.toUri()), Map.of(), true, System.currentTimeMillis())) + .join(); + assertThat(analysisResult.getFailedAnalysisFiles()).isEmpty(); + await().during(2, TimeUnit.SECONDS).untilAsserted(() -> assertThat(client.getRaisedIssuesForScopeIdAsList(CONFIG_SCOPE_ID)).isEmpty()); } }