diff --git a/its/plugin/pom.xml b/its/plugin/pom.xml index 2e9e18113da..3266219a6c1 100644 --- a/its/plugin/pom.xml +++ b/its/plugin/pom.xml @@ -22,6 +22,7 @@ plugins tests sonarlint-tests + sonarlint-rpc-tests diff --git a/its/plugin/sonarlint-rpc-tests/pom.xml b/its/plugin/sonarlint-rpc-tests/pom.xml new file mode 100644 index 00000000000..d0bbb21fc79 --- /dev/null +++ b/its/plugin/sonarlint-rpc-tests/pom.xml @@ -0,0 +1,169 @@ + + + 4.0.0 + + + org.sonarsource.javascript + javascript-it-plugin + 10.17.0-SNAPSHOT + + + javascript-it-plugin-sonarlint-rpc-tests + JavaScript :: IT :: Plugin :: SonarLint RPC Tests + + + -server + 10.8.0.79183 + + + + + + com.google.code.gson + gson + test + + + org.sonarsource.analyzer-commons + sonar-analyzer-commons + test + + + org.sonarsource.sonarlint.core + sonarlint-core + ${sonarlint.plugin.api.version} + + + org.sonarsource.sonarlint.core + sonarlint-rpc-protocol + ${sonarlint.plugin.api.version} + test + + + org.sonarsource.sonarlint.core + sonarlint-rpc-impl + ${sonarlint.plugin.api.version} + test + + + org.sonarsource.sonarlint.core + sonarlint-rpc-java-client + ${sonarlint.plugin.api.version} + test + + + org.sonarsource.sonarlint.ls + sonarlint-language-server + 3.13.0.75653 + test + + + org.awaitility + awaitility + 4.2.0 + test + + + org.junit.jupiter + junit-jupiter + test + + + org.assertj + assertj-core + + + com.google.code.findbugs + jsr305 + provided + + + commons-io + commons-io + test + + + org.awaitility + awaitility + + + + + + + maven-surefire-plugin + + + **/*Test.java + + + + + + + + + + qa + + + env.SONARSOURCE_QA + true + + + + + + maven-dependency-plugin + + + copy-plugin + generate-test-resources + + copy + + + + + ${project.groupId} + sonar-javascript-plugin + sonar-plugin + true + + + ${project.groupId} + sonar-javascript-plugin + win-x64 + sonar-plugin + true + + + ${project.groupId} + sonar-javascript-plugin + linux-x64 + sonar-plugin + true + + + ${project.groupId} + sonar-javascript-plugin + multi + sonar-plugin + true + + + ../../../sonar-plugin/sonar-javascript-plugin/target + true + true + + + + + + + + + + diff --git a/its/plugin/sonarlint-rpc-tests/src/test/java/com/sonar/javascript/it/plugin/sonarlint/tests/AbstractLanguageServerMediumTests.java b/its/plugin/sonarlint-rpc-tests/src/test/java/com/sonar/javascript/it/plugin/sonarlint/tests/AbstractLanguageServerMediumTests.java new file mode 100644 index 00000000000..520cd009bfe --- /dev/null +++ b/its/plugin/sonarlint-rpc-tests/src/test/java/com/sonar/javascript/it/plugin/sonarlint/tests/AbstractLanguageServerMediumTests.java @@ -0,0 +1,818 @@ +/* + * SonarLint Language Server + * Copyright (C) 2009-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 com.sonar.javascript.it.plugin.sonarlint.tests; + +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static org.sonarsource.sonarlint.ls.SonarLintLanguageServer.JUPYTER_NOTEBOOK_TYPE; +import static org.sonarsource.sonarlint.ls.settings.SettingsManager.DOTNET_DEFAULT_SOLUTION_PATH; +import static org.sonarsource.sonarlint.ls.settings.SettingsManager.OMNISHARP_LOAD_PROJECT_ON_DEMAND; +import static org.sonarsource.sonarlint.ls.settings.SettingsManager.OMNISHARP_PROJECT_LOAD_TIMEOUT; +import static org.sonarsource.sonarlint.ls.settings.SettingsManager.OMNISHARP_USE_MODERN_NET; +import static org.sonarsource.sonarlint.ls.settings.SettingsManager.SONARLINT_CONFIGURATION_NAMESPACE; +import static org.sonarsource.sonarlint.ls.settings.SettingsManager.VSCODE_FILE_EXCLUDES; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.net.ServerSocket; +import java.net.Socket; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; +import java.util.regex.Pattern; +import java.util.stream.Stream; +import javax.annotation.Nullable; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.ArrayUtils; +import org.assertj.core.api.iterable.ThrowingExtractor; +import org.eclipse.lsp4j.ClientCapabilities; +import org.eclipse.lsp4j.ClientInfo; +import org.eclipse.lsp4j.ConfigurationItem; +import org.eclipse.lsp4j.ConfigurationParams; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.DidChangeConfigurationParams; +import org.eclipse.lsp4j.DidChangeNotebookDocumentParams; +import org.eclipse.lsp4j.DidChangeTextDocumentParams; +import org.eclipse.lsp4j.DidChangeWorkspaceFoldersParams; +import org.eclipse.lsp4j.DidCloseNotebookDocumentParams; +import org.eclipse.lsp4j.DidCloseTextDocumentParams; +import org.eclipse.lsp4j.DidOpenNotebookDocumentParams; +import org.eclipse.lsp4j.DidOpenTextDocumentParams; +import org.eclipse.lsp4j.InitializeParams; +import org.eclipse.lsp4j.InitializedParams; +import org.eclipse.lsp4j.MessageActionItem; +import org.eclipse.lsp4j.MessageParams; +import org.eclipse.lsp4j.NotebookDocument; +import org.eclipse.lsp4j.NotebookDocumentChangeEvent; +import org.eclipse.lsp4j.NotebookDocumentClientCapabilities; +import org.eclipse.lsp4j.NotebookDocumentIdentifier; +import org.eclipse.lsp4j.NotebookDocumentSyncClientCapabilities; +import org.eclipse.lsp4j.ProgressParams; +import org.eclipse.lsp4j.PublishDiagnosticsParams; +import org.eclipse.lsp4j.ShowMessageRequestParams; +import org.eclipse.lsp4j.TextDocumentContentChangeEvent; +import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.eclipse.lsp4j.TextDocumentItem; +import org.eclipse.lsp4j.VersionedNotebookDocumentIdentifier; +import org.eclipse.lsp4j.VersionedTextDocumentIdentifier; +import org.eclipse.lsp4j.WindowClientCapabilities; +import org.eclipse.lsp4j.WorkDoneProgressCreateParams; +import org.eclipse.lsp4j.WorkspaceFolder; +import org.eclipse.lsp4j.WorkspaceFoldersChangeEvent; +import org.eclipse.lsp4j.jsonrpc.CompletableFutures; +import org.eclipse.lsp4j.jsonrpc.Launcher; +import org.eclipse.lsp4j.launch.LSPLauncher; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.sonarsource.sonarlint.core.rpc.protocol.client.binding.AssistBindingParams; +import org.sonarsource.sonarlint.core.rpc.protocol.client.binding.SuggestBindingParams; +import org.sonarsource.sonarlint.core.rpc.protocol.client.connection.SuggestConnectionParams; +import org.sonarsource.sonarlint.ls.ServerMain; +import org.sonarsource.sonarlint.ls.SonarLintExtendedLanguageClient; +import org.sonarsource.sonarlint.ls.SonarLintExtendedLanguageServer; +import org.sonarsource.sonarlint.ls.SonarLintLanguageServer; +import org.sonarsource.sonarlint.ls.commands.ShowAllLocationsCommand; +import org.sonarsource.sonarlint.ls.settings.SettingsManager; +import org.sonarsource.sonarlint.ls.telemetry.SonarLintTelemetry; +import picocli.CommandLine; + +public abstract class AbstractLanguageServerMediumTests { + protected static final boolean COMMERCIAL_ENABLED = System.getProperty("commercial") != null; + private static final String CSHARP_OSS_PATH = fullPathToJar("sonarcsharp"); + private static final String CSHARP_ENTERPRISE_PATH = COMMERCIAL_ENABLED ? fullPathToJar("csharpenterprise") : CSHARP_OSS_PATH; + + private static final Set staticTempDirs = new HashSet<>(); + private final Set instanceTempDirs = new HashSet<>(); + Path temp; + protected Set toBeClosed = new HashSet<>(); + protected Set notebooksToBeClosed = new HashSet<>(); + protected Set foldersToRemove = new HashSet<>(); + private static ServerSocket serverSocket; + protected static SonarLintExtendedLanguageServer lsProxy; + protected static FakeLanguageClient client; + private static List foundFileDtos = List.of(); + + @BeforeAll + static void startServer() throws Exception { + System.setProperty(SonarLintTelemetry.DISABLE_PROPERTY_KEY, "true"); + SettingsManager.setSonarLintUserHomeOverride(makeStaticTempDir()); + serverSocket = new ServerSocket(0); + var port = serverSocket.getLocalPort(); + + client = new FakeLanguageClient(); + + var executor = Executors.newFixedThreadPool(2); + var future = executor.submit(() -> { + Socket socket = serverSocket.accept(); + Launcher clientSideLauncher = new LSPLauncher.Builder() + .setLocalService(client) + .setRemoteInterface(SonarLintExtendedLanguageServer.class) + .setInput(socket.getInputStream()) + .setOutput(socket.getOutputStream()) + .create(); + clientSideLauncher.startListening(); + return clientSideLauncher.getRemoteProxy(); + }); + + var go = fullPathToJar("sonargo"); + var iac = fullPathToJar("sonariac"); + var html = fullPathToJar("sonarhtml"); + var java = fullPathToJar("sonarjava"); + var js = fullPathToJar("sonarjs"); + var php = fullPathToJar("sonarphp"); + var py = fullPathToJar("sonarpython"); + var text = fullPathToJar("sonartext"); + var xml = fullPathToJar("sonarxml"); + var omnisharp = fullPathToJar("sonarlintomnisharp"); + String[] languageServerArgs = new String[]{"-port", "" + port, "-analyzers", go, java, js, php, py, html, xml, text, iac, omnisharp}; + if (COMMERCIAL_ENABLED) { + var cfamily = fullPathToJar("cfamily"); + languageServerArgs = ArrayUtils.add(languageServerArgs, cfamily); + } + + try { + var cmd = new CommandLine(new ServerMain()); + var cmdOutput = new StringWriter(); + cmd.setErr(new PrintWriter(cmdOutput)); + cmd.setOut(new PrintWriter(cmdOutput)); + + var clonedArgs = ArrayUtils.clone(languageServerArgs); + executor.submit(() -> cmd.execute(clonedArgs)); + executor.shutdown(); + } catch (Exception e) { + e.printStackTrace(); + future.get(1, TimeUnit.SECONDS); + if (!future.isDone()) { + future.cancel(true); + } + throw e; + } + + lsProxy = future.get(); + } + //https://github.com/SonarSource/sonarlint-language-server/blob/63e5ceef866c7a08e6f7c7d9d0f4020200dec720/src/test/java/org/sonarsource/sonarlint/ls/mediumtests/LanguageServerMediumTests.java + //https://github.com/SonarSource/sonarlint-core/blob/ddb7cfb8ecdbc703263c2662cc697292099759c8/medium-tests/src/test/java/mediumtest/analysis/AnalysisMediumTests.java#L980 + protected static String fullPathToJar(String jarName) { + return Paths.get("target/plugins").resolve(jarName + ".jar").toAbsolutePath().toString(); + } + + protected static void initialize(Map initializeOptions, WorkspaceFolder... initFolders) throws InterruptedException, ExecutionException { + var initializeParams = getInitializeParams(initializeOptions, initFolders); + initializeParams.getCapabilities().setWindow(new WindowClientCapabilities()); + var initializeResult = lsProxy.initialize(initializeParams).get(); + assertThat(initializeResult.getServerInfo().getName()).isEqualTo("SonarLint Language Server"); + assertThat(initializeResult.getServerInfo().getVersion()).isNotBlank(); + if (SonarLintLanguageServer.isEnableNotebooks(initializeOptions)) { + assertThat(initializeResult.getCapabilities().getNotebookDocumentSync()).isNotNull(); + } else { + assertThat(initializeResult.getCapabilities().getNotebookDocumentSync()).isNull(); + } + lsProxy.initialized(new InitializedParams()); + } + + @NotNull + private static InitializeParams getInitializeParams(Map initializeOptions, WorkspaceFolder[] initFolders) { + var initializeParams = new InitializeParams(); + initializeParams.setTrace("messages"); + + var actualInitOptions = new HashMap<>(initializeOptions); + if (initializeOptions.containsKey("additionalAttributes")) { + var additionalAttributes = new HashMap<>((Map)initializeOptions.get("additionalAttributes")); + additionalAttributes.put("csharpOssPath", CSHARP_OSS_PATH); + additionalAttributes.put("csharpEnterprisePath", CSHARP_ENTERPRISE_PATH); + actualInitOptions.put("additionalAttributes", additionalAttributes); + } + initializeParams.setInitializationOptions(actualInitOptions); + + initializeParams.setWorkspaceFolders(List.of(initFolders)); + initializeParams.setClientInfo(new ClientInfo("SonarLint LS Medium tests", "1.0")); + var clientCapabilities = new ClientCapabilities(); + var notebookDocument = new NotebookDocumentClientCapabilities(); + var synchronization = new NotebookDocumentSyncClientCapabilities(); + synchronization.setDynamicRegistration(true); + synchronization.setExecutionSummarySupport(true); + notebookDocument.setSynchronization(synchronization); + clientCapabilities.setNotebookDocument(notebookDocument); + initializeParams.setCapabilities(clientCapabilities); + return initializeParams; + } + + @AfterAll + public static void stopServer() throws Exception { + staticTempDirs.forEach(tempDirPath -> FileUtils.deleteQuietly(tempDirPath.toFile())); + staticTempDirs.clear(); + System.clearProperty(SonarLintTelemetry.DISABLE_PROPERTY_KEY); + try { + if (lsProxy != null) { + // 20 seconds should be way enough time for the backend to stop + lsProxy.shutdown().get(20, SECONDS); + lsProxy.exit(); + } + } finally { + serverSocket.close(); + } + } + + @BeforeEach + void cleanup() throws Exception { + temp = makeInstanceTempDir(); + // Reset state on LS side + client.clear(); + toBeClosed.clear(); + notebooksToBeClosed.clear(); + + setupGlobalSettings(client.globalSettings); + setUpFolderSettings(client.folderSettings); + + notifyConfigurationChangeOnClient(); + verifyConfigurationChangeOnClient(); + } + + protected static void setUpFindFilesInFolderResponse(List foundFileDtos) { + AbstractLanguageServerMediumTests.foundFileDtos = foundFileDtos; + } + + protected void setupGlobalSettings(Map globalSettings) { + // do nothing by default + } + + protected void setUpFolderSettings(Map> folderSettings) { + // do nothing by default + } + + protected void verifyConfigurationChangeOnClient() { + // do nothing by default + } + + @AfterEach + final void closeFiles() { + // Close all opened files + for (var uri : toBeClosed) { + lsProxy.getTextDocumentService().didClose(new DidCloseTextDocumentParams(new TextDocumentIdentifier(uri))); + } + for (var uri : notebooksToBeClosed) { + lsProxy.getNotebookDocumentService().didClose(new DidCloseNotebookDocumentParams(new NotebookDocumentIdentifier(uri), List.of())); + } + foldersToRemove.forEach(folderUri -> lsProxy.getWorkspaceService().didChangeWorkspaceFolders( + new DidChangeWorkspaceFoldersParams(new WorkspaceFoldersChangeEvent(List.of(), List.of(new WorkspaceFolder(folderUri)))))); + instanceTempDirs.forEach(tempDirPath -> FileUtils.deleteQuietly(tempDirPath.toFile())); + instanceTempDirs.clear(); + } + + protected static void assertLogContains(String msg) { + assertLogContainsPattern("\\[.*\\] " + Pattern.quote(msg) + ".*"); + } + + protected static void assertLogContainsPattern(String msgPattern) { + await().atMost(10, SECONDS).untilAsserted(() -> assertThat(client.logs).anyMatch(p -> p.getMessage().matches(msgPattern))); + } + + protected String getUri(String filename) throws IOException { + var file = temp.resolve(filename); + Files.createFile(file); + return file.toUri().toString(); + } + + protected String getUri(String filename, Path tempDir) throws IOException { + var file = tempDir.resolve(filename); + Files.createFile(file); + return file.toUri().toString(); + } + + protected static void awaitLatch(CountDownLatch latch) { + try { + assertTrue(latch.await(15, TimeUnit.SECONDS)); + } catch (InterruptedException e) { + fail(e); + } + } + + protected static class FakeLanguageClient implements SonarLintExtendedLanguageClient { + + Map> diagnostics = new ConcurrentHashMap<>(); + Map> hotspots = new ConcurrentHashMap<>(); + Queue logs = new ConcurrentLinkedQueue<>(); + Map globalSettings = new HashMap<>(); + Map> folderSettings = new HashMap<>(); + Map javaConfigs = new HashMap<>(); + Map referenceBranchNameByFolder = new HashMap<>(); + Map scopeReadyForAnalysis = new HashMap<>(); + CountDownLatch settingsLatch = new CountDownLatch(0); + CountDownLatch showRuleDescriptionLatch = new CountDownLatch(0); + CountDownLatch suggestBindingLatch = new CountDownLatch(0); + CountDownLatch readyForTestsLatch = new CountDownLatch(0); + ShowAllLocationsCommand.Param showIssueParams; + ShowFixSuggestionParams showFixSuggestionParams; + SuggestBindingParams suggestedBindings; + ShowRuleDescriptionParams ruleDesc; + boolean isIgnoredByScm = false; + boolean shouldAnalyseFile = true; + final AtomicInteger needCompilationDatabaseCalls = new AtomicInteger(); + final Set openedLinks = new HashSet<>(); + final Set shownMessages = new HashSet<>(); + final Map newCodeDefinitionCache = new HashMap<>(); + + void clearHotspotsAndIssuesAndConfigScopeReadiness() { + scopeReadyForAnalysis.clear(); + diagnostics.clear(); + hotspots.clear(); + } + + void clear() { + diagnostics.clear(); + hotspots.clear(); + logs.clear(); + shownMessages.clear(); + globalSettings = new HashMap<>(); + setDisableTelemetry(globalSettings, true); + folderSettings.clear(); + settingsLatch = new CountDownLatch(0); + showRuleDescriptionLatch = new CountDownLatch(0); + suggestBindingLatch = new CountDownLatch(0); + readyForTestsLatch = new CountDownLatch(0); + needCompilationDatabaseCalls.set(0); + shouldAnalyseFile = true; + scopeReadyForAnalysis.clear(); + suggestedBindings = null; + } + + @Override + public void telemetryEvent(Object object) { + } + + List getDiagnostics(String uri) { + return diagnostics.getOrDefault(uri, List.of()); + } + + List getHotspots(String uri) { + return hotspots.getOrDefault(uri, List.of()); + } + + @Override + public void publishDiagnostics(PublishDiagnosticsParams diagnostics) { + this.diagnostics.put(diagnostics.getUri(), diagnostics.getDiagnostics()); + } + + @Override + public void publishSecurityHotspots(PublishDiagnosticsParams diagnostics) { + this.hotspots.put(diagnostics.getUri(), diagnostics.getDiagnostics()); + } + + @Override + public void showMessage(MessageParams messageParams) { + shownMessages.add(messageParams); + } + + @Override + public CompletableFuture showMessageRequest(ShowMessageRequestParams requestParams) { + return CompletableFuture.completedFuture(null); + } + + @Override + public void logMessage(MessageParams message) { + // SSLRSQBR-72 This log is produced by analyzers ProgressReport, and keeps coming long after the analysis has completed. Just ignore + // it + if (!message.getMessage().contains("1/1 source files have been analyzed")) { + logs.add(message); + } + System.out.println(message.getMessage()); + } + + @Override + public void notifyProgress(ProgressParams params) { + System.out.println(params); + } + + @Override + public CompletableFuture createProgress(WorkDoneProgressCreateParams params) { + return CompletableFuture.completedFuture(null); + } + + @Override + public CompletableFuture> configuration(ConfigurationParams configurationParams) { + return CompletableFutures.computeAsync(cancelToken -> { + List result; + try { + assertThat(configurationParams.getItems()).extracting(ConfigurationItem::getSection).containsExactly(SONARLINT_CONFIGURATION_NAMESPACE, + DOTNET_DEFAULT_SOLUTION_PATH, OMNISHARP_USE_MODERN_NET, OMNISHARP_LOAD_PROJECT_ON_DEMAND, OMNISHARP_PROJECT_LOAD_TIMEOUT, VSCODE_FILE_EXCLUDES); + result = new ArrayList<>(configurationParams.getItems().size()); + for (var item : configurationParams.getItems()) { + if (item.getScopeUri() == null && item.getSection().equals(SONARLINT_CONFIGURATION_NAMESPACE)) { + result.add(globalSettings); + } else if (item.getScopeUri() != null && !item.getSection().equals(SONARLINT_CONFIGURATION_NAMESPACE)) { + result + .add(Optional.ofNullable(folderSettings.get(item.getScopeUri())) + .orElseThrow(() -> new IllegalStateException("No settings mocked for workspaceFolderPath " + item.getScopeUri()))); + // we don't want to repeat the same setting for one folder 5 times :) + break; + } + } + } finally { + settingsLatch.countDown(); + } + return result; + }); + } + + @Override + public void readyForTests() { + readyForTestsLatch.countDown(); + } + + @Override + public CompletableFuture askSslCertificateConfirmation(SslCertificateConfirmationParams params) { + return null; + } + + @Override + public void showSoonUnsupportedVersionMessage(ShowSoonUnsupportedVersionMessageParams messageParams) { + } + + @Override + public void submitNewCodeDefinition(SubmitNewCodeDefinitionParams params) { + newCodeDefinitionCache.put(params.getFolderUri(), + new NewCodeDefinitionDto(params.getNewCodeDefinitionOrMessage(), params.isSupported())); + } + + @Override + public void suggestBinding(SuggestBindingParams binding) { + this.suggestedBindings = binding; + suggestBindingLatch.countDown(); + } + + @Override + public void suggestConnection(SuggestConnectionParams suggestConnectionParams) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + @Override + public CompletableFuture listFilesInFolder(FolderUriParams params) { + return CompletableFuture.completedFuture(new FindFileByNamesInScopeResponse(foundFileDtos)); + } + + @Override + public void showSonarLintOutput() { + } + + @Override + public void openJavaHomeSettings() { + } + + @Override + public void openPathToNodeSettings() { + } + + @Override + public void doNotShowMissingRequirementsMessageAgain() { + + } + + @Override + public CompletableFuture canShowMissingRequirementsNotification() { + return CompletableFuture.completedFuture(false); + } + + @Override + public void showRuleDescription(ShowRuleDescriptionParams params) { + this.ruleDesc = params; + showRuleDescriptionLatch.countDown(); + } + + @Override + public void showHotspot(ShowHotspotParams h) { + } + + @Override + public void showIssue(ShowAllLocationsCommand.Param issue) { + this.showIssueParams = issue; + } + + @Override + public void showIssueOrHotspot(ShowAllLocationsCommand.Param params) { + } + + @Override + public CompletableFuture isIgnoredByScm(String fileUri) { + return CompletableFutures.computeAsync(cancelToken -> isIgnoredByScm); + } + + @Override + public CompletableFuture shouldAnalyseFile(SonarLintExtendedLanguageServer.UriParams fileUri) { + return CompletableFutures.computeAsync(cancelToken -> new ShouldAnalyseFileCheckResult(shouldAnalyseFile, "reason")); + + } + + @Override + public CompletableFuture filterOutExcludedFiles(FileUrisParams params) { + return CompletableFutures.computeAsync(cancelToken -> new FileUrisResult(params.getFileUris())); + } + + @Override + public void maybeShowWiderLanguageSupportNotification(List languageLabel) { + + } + + @Override + public void showFirstSecretDetectionNotification() { + } + + @Override + public CompletableFuture getJavaConfig(String fileUri) { + return CompletableFutures.computeAsync(cancelToken -> javaConfigs.get(fileUri)); + } + + @Override + public void browseTo(String link) { + openedLinks.add(link); + } + + @Override + public void openConnectionSettings(boolean isSonarCloud) { + } + + @Override + public void removeBindingsForDeletedConnections(List connectionIds) { + + } + + @Override + public CompletableFuture assistCreatingConnection(CreateConnectionParams params) { + return CompletableFuture.completedFuture(new AssistCreatingConnectionResponse("connectionId")); + } + + @Override + public CompletableFuture assistBinding(AssistBindingParams params) { + return CompletableFuture.completedFuture(new AssistBindingResponse("folderUri")); + } + + @Override + public void showFixSuggestion(ShowFixSuggestionParams params) { + this.showFixSuggestionParams = params; + } + + @Override + public void setReferenceBranchNameForFolder(ReferenceBranchForFolder newReferenceBranch) { + referenceBranchNameByFolder.put(newReferenceBranch.getFolderUri(), newReferenceBranch.getBranchName()); + } + + @Override + public void needCompilationDatabase() { + this.needCompilationDatabaseCalls.incrementAndGet(); + } + + @Override + public void reportConnectionCheckResult(ConnectionCheckResult result) { + // NOP + } + + @Override + public CompletableFuture getTokenForServer(String serverId) { + return CompletableFutures.computeAsync(server -> "token"); + } + + } + + protected static void notifyConfigurationChangeOnClient() { + client.settingsLatch = new CountDownLatch(1); + lsProxy.getWorkspaceService().didChangeConfiguration(new DidChangeConfigurationParams(Map.of("sonarlint", client.globalSettings))); + awaitLatch(client.settingsLatch); + // workspace/configuration has been called by server, but give some time for the response to be processed (settings change listeners) + try { + Thread.sleep(200); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + protected static void setTestFilePattern(Map config, @Nullable String testFilePattern) { + if (testFilePattern != null) { + config.put("testFilePattern", testFilePattern); + } else { + config.remove("testFilePattern"); + } + } + + protected static void setPathToCompileCommands(Map config, @Nullable String pathToCompileCommands) { + if (pathToCompileCommands != null) { + config.put("pathToCompileCommands", pathToCompileCommands); + } else { + config.remove("pathToCompileCommands"); + } + } + + protected static void setDisableTelemetry(Map config, @Nullable Boolean disableTelemetry) { + if (disableTelemetry != null) { + config.put("disableTelemetry", disableTelemetry); + } else { + config.remove("disableTelemetry"); + } + } + + protected static void setShowAnalyzerLogs(Map config, @Nullable Boolean showAnalyzerLogs) { + if (showAnalyzerLogs != null) { + ((HashMap) config.computeIfAbsent("output", k -> new HashMap())).put("showAnalyzerLogs", showAnalyzerLogs); + } else { + config.remove("showAnalyzerLogs"); + } + } + + protected static void setShowVerboseLogs(Map config, @Nullable Boolean showVerboseLogs) { + if (showVerboseLogs != null) { + ((HashMap) config.computeIfAbsent("output", k -> new HashMap())).put("showVerboseLogs", showVerboseLogs); + } else { + config.remove("showVerboseLogs"); + } + } + + protected static void setAnalyzerProperties(Map config, Map analyzerProperties) { + if (analyzerProperties.isEmpty()) { + config.put("analyzerProperties", analyzerProperties); + } else { + config.remove("analyzerProperties"); + } + } + + protected static void addSonarQubeConnection(Map config, String connectionId, String url, String token) { + var connectedMode = (Map) config.computeIfAbsent("connectedMode", k -> new HashMap()); + var connections = (Map) connectedMode.computeIfAbsent("connections", k -> new HashMap()); + var sonarqubeConnections = (List) connections.computeIfAbsent("sonarqube", k -> new ArrayList<>()); + sonarqubeConnections.add(Map.of("connectionId", connectionId, "serverUrl", url, "token", token)); + } + + protected static void bindProject(Map config, String connectionId, String projectKey) { + var connectedMode = (Map) config.computeIfAbsent("connectedMode", k -> new HashMap()); + connectedMode.put("project", Map.of("connectionId", connectionId, "projectKey", projectKey)); + } + + protected static void setRulesConfig(Map config, String... ruleConfigs) { + if (ruleConfigs.length > 0) { + config.put("rules", buildRulesMap(ruleConfigs)); + } else { + config.remove("rules"); + } + } + + private static Map buildRulesMap(String... ruleConfigs) { + assertThat(ruleConfigs.length % 2).withFailMessage("ruleConfigs must contain 'rule:key', 'level' pairs").isZero(); + var rules = new Map.Entry[ruleConfigs.length / 2]; + for (var i = 0; i < ruleConfigs.length; i += 2) { + rules[i / 2] = Map.entry(ruleConfigs[i], Map.of("level", ruleConfigs[i + 1])); + } + return Map.ofEntries(rules); + } + + protected void didChange(String uri, String content) { + var docId = new VersionedTextDocumentIdentifier(uri, 1); + lsProxy.getTextDocumentService() + .didChange(new DidChangeTextDocumentParams(docId, List.of(new TextDocumentContentChangeEvent(content)))); + } + + protected void didChangeNotebook(String uri, String content) { + var docId = new VersionedNotebookDocumentIdentifier(1, uri); + lsProxy.getNotebookDocumentService() + .didChange(new DidChangeNotebookDocumentParams(docId, new NotebookDocumentChangeEvent())); + } + + protected void didOpen(String uri, String languageId, String content) { + lsProxy.getTextDocumentService() + .didOpen(new DidOpenTextDocumentParams(new TextDocumentItem(uri, languageId, 1, content))); + toBeClosed.add(uri); + } + + protected void didClose(String uri) { + lsProxy.getTextDocumentService() + .didClose(new DidCloseTextDocumentParams(new TextDocumentIdentifier(uri))); + toBeClosed.remove(uri); + } + + protected void didOpenNotebook(String uri, String... cellContents) { + var notebookDocument = new NotebookDocument(); + notebookDocument.setUri(uri); + notebookDocument.setNotebookType(JUPYTER_NOTEBOOK_TYPE); + notebookDocument.setVersion(1); + + var cellDocuments = new ArrayList(); + var cellIndex = new AtomicInteger(); + Stream.of(cellContents).forEach(cellContent -> { + var newCellDocument = new TextDocumentItem(); + newCellDocument.setText(cellContent); + newCellDocument.setUri(uri + "#" + cellIndex.incrementAndGet()); + newCellDocument.setLanguageId("python"); + newCellDocument.setVersion(1); + cellDocuments.add(newCellDocument); + }); + + lsProxy.getNotebookDocumentService().didOpen(new DidOpenNotebookDocumentParams(notebookDocument, cellDocuments)); + notebooksToBeClosed.add(uri); + } + + protected ThrowingExtractor withoutTimestamp() { + return p -> p.getMessage().replaceAll("\\[(\\w*)\\s+-\\s[\\d:.]*\\]", "[$1]"); + } + + protected ThrowingExtractor withoutTimestampAndMillis() { + return p -> p.getMessage().replaceAll("\\[(\\w*)\\s+-\\s[\\d:.]*\\]", "[$1]").replaceAll("\\d{2,4}ms", "XXXms"); + } + + protected Function code() { + return d -> d.getCode().getLeft(); + } + + protected Function endCharacter() { + return d -> d.getRange().getEnd().getCharacter(); + } + + protected Function endLine() { + return d -> d.getRange().getEnd().getLine(); + } + + protected Function startCharacter() { + return d -> d.getRange().getStart().getCharacter(); + } + + protected Function startLine() { + return d -> d.getRange().getStart().getLine(); + } + + + protected Map getFolderSettings(String folderUri) { + return client.folderSettings.computeIfAbsent(folderUri, f -> new HashMap<>()); + } + + /** + * Use this instead of {@link org.junit.jupiter.api.io.TempDir} that has issues on Windows + */ + protected Path makeInstanceTempDir() throws Exception { + var newTempDir = Files.createTempDirectory(null); + instanceTempDirs.add(newTempDir); + return newTempDir; + } + + /** + * Use this instead of {@link org.junit.jupiter.api.io.TempDir} that has issues on Windows + */ + protected static Path makeStaticTempDir() throws IOException { + var newTempDir = Files.createTempDirectory(null); + staticTempDirs.add(newTempDir); + return newTempDir; + } + + static class NewCodeDefinitionDto { + String newCodeDefinitionOrMessage; + boolean isSupported; + + public NewCodeDefinitionDto(String newCodeDefinitionOrMessage, boolean isSupported) { + this.newCodeDefinitionOrMessage = newCodeDefinitionOrMessage; + this.isSupported = isSupported; + } + + public String getNewCodeDefinitionOrMessage() { + return newCodeDefinitionOrMessage; + } + + public boolean isSupported() { + return isSupported; + } + } +} \ No newline at end of file diff --git a/its/plugin/sonarlint-rpc-tests/src/test/java/com/sonar/javascript/it/plugin/sonarlint/tests/SonarLintIntegrationTests.java b/its/plugin/sonarlint-rpc-tests/src/test/java/com/sonar/javascript/it/plugin/sonarlint/tests/SonarLintIntegrationTests.java new file mode 100644 index 00000000000..da94a67261e --- /dev/null +++ b/its/plugin/sonarlint-rpc-tests/src/test/java/com/sonar/javascript/it/plugin/sonarlint/tests/SonarLintIntegrationTests.java @@ -0,0 +1,397 @@ +///* +// * SonarOmnisharp ITs +// * Copyright (C) 2021-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 com.sonar.javascript.it.plugin.sonarlint.tests; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; + +import java.io.File; +import java.io.IOException; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.net.URI; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.CancellationException; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.filefilter.FalseFileFilter; +import org.apache.commons.io.filefilter.RegexFileFilter; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.sonarsource.sonarlint.core.rpc.client.ClientJsonRpcLauncher; +import org.sonarsource.sonarlint.core.rpc.client.ConfigScopeNotFoundException; +import org.sonarsource.sonarlint.core.rpc.client.ConnectionNotFoundException; +import org.sonarsource.sonarlint.core.rpc.client.SonarLintCancelChecker; +import org.sonarsource.sonarlint.core.rpc.client.SonarLintRpcClientDelegate; +import org.sonarsource.sonarlint.core.rpc.impl.BackendJsonRpcLauncher; +import org.sonarsource.sonarlint.core.rpc.protocol.SonarLintRpcServer; +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.backend.analysis.DidChangeAnalysisPropertiesParams; +import org.sonarsource.sonarlint.core.rpc.protocol.backend.config.binding.BindingSuggestionDto; +import org.sonarsource.sonarlint.core.rpc.protocol.backend.file.DidUpdateFileSystemParams; +import org.sonarsource.sonarlint.core.rpc.protocol.backend.initialize.ClientConstantInfoDto; +import org.sonarsource.sonarlint.core.rpc.protocol.backend.initialize.FeatureFlagsDto; +import org.sonarsource.sonarlint.core.rpc.protocol.backend.initialize.HttpConfigurationDto; +import org.sonarsource.sonarlint.core.rpc.protocol.backend.initialize.InitializeParams; +import org.sonarsource.sonarlint.core.rpc.protocol.backend.initialize.LanguageSpecificRequirements; +import org.sonarsource.sonarlint.core.rpc.protocol.backend.initialize.TelemetryClientConstantAttributesDto; +import org.sonarsource.sonarlint.core.rpc.protocol.backend.rules.UpdateStandaloneRulesConfigurationParams; +import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.TaintVulnerabilityDto; +import org.sonarsource.sonarlint.core.rpc.protocol.client.analysis.RawIssueDto; +import org.sonarsource.sonarlint.core.rpc.protocol.client.binding.AssistBindingParams; +import org.sonarsource.sonarlint.core.rpc.protocol.client.binding.AssistBindingResponse; +import org.sonarsource.sonarlint.core.rpc.protocol.client.binding.NoBindingSuggestionFoundParams; +import org.sonarsource.sonarlint.core.rpc.protocol.client.connection.AssistCreatingConnectionParams; +import org.sonarsource.sonarlint.core.rpc.protocol.client.connection.AssistCreatingConnectionResponse; +import org.sonarsource.sonarlint.core.rpc.protocol.client.connection.ConnectionSuggestionDto; +import org.sonarsource.sonarlint.core.rpc.protocol.client.event.DidReceiveServerHotspotEvent; +import org.sonarsource.sonarlint.core.rpc.protocol.client.fix.FixSuggestionDto; +import org.sonarsource.sonarlint.core.rpc.protocol.client.hotspot.HotspotDetailsDto; +import org.sonarsource.sonarlint.core.rpc.protocol.client.http.GetProxyPasswordAuthenticationResponse; +import org.sonarsource.sonarlint.core.rpc.protocol.client.http.ProxyDto; +import org.sonarsource.sonarlint.core.rpc.protocol.client.http.X509CertificateDto; +import org.sonarsource.sonarlint.core.rpc.protocol.client.issue.IssueDetailsDto; +import org.sonarsource.sonarlint.core.rpc.protocol.client.log.LogParams; +import org.sonarsource.sonarlint.core.rpc.protocol.client.message.MessageType; +import org.sonarsource.sonarlint.core.rpc.protocol.client.message.ShowSoonUnsupportedMessageParams; +import org.sonarsource.sonarlint.core.rpc.protocol.client.progress.ReportProgressParams; +import org.sonarsource.sonarlint.core.rpc.protocol.client.progress.StartProgressParams; +import org.sonarsource.sonarlint.core.rpc.protocol.client.smartnotification.ShowSmartNotificationParams; +import org.sonarsource.sonarlint.core.rpc.protocol.client.telemetry.TelemetryClientLiveAttributesResponse; +import org.sonarsource.sonarlint.core.rpc.protocol.common.ClientFileDto; +import org.sonarsource.sonarlint.core.rpc.protocol.common.Either; +import org.sonarsource.sonarlint.core.rpc.protocol.common.Language; +import org.sonarsource.sonarlint.core.rpc.protocol.common.TokenDto; +import org.sonarsource.sonarlint.core.rpc.protocol.common.UsernamePasswordDto; + +class SonarLintIntegrationTests { + + private static final String SOLUTION1_MODULE_KEY = "solution1"; + private static final String SOLUTION2_MODULE_KEY = "solution2"; + + private static final ClientConstantInfoDto IT_CLIENT_INFO = new ClientConstantInfoDto("clientName", "integrationTests"); + private static final TelemetryClientConstantAttributesDto IT_TELEMETRY_ATTRIBUTES = new TelemetryClientConstantAttributesDto("SLO# ITs", "SonarLint ITs", + "1.2.3", "4.5.6", Collections.emptyMap()); + + private static SonarLintRpcServer backend; + private static MockSonarLintRpcClientDelegate client; + + @BeforeAll + public static void prepare(@TempDir Path tmpDir) throws Exception { + var clientToServerOutputStream = new PipedOutputStream(); + var clientToServerInputStream = new PipedInputStream(clientToServerOutputStream); + + var serverToClientOutputStream = new PipedOutputStream(); + var serverToClientInputStream = new PipedInputStream(serverToClientOutputStream); + + client = new MockSonarLintRpcClientDelegate() { + @Override + public void log(LogParams params) { + System.out.println(params); + } + }; + new BackendJsonRpcLauncher(clientToServerInputStream, serverToClientOutputStream); + var clientLauncher = new ClientJsonRpcLauncher(serverToClientInputStream, clientToServerOutputStream, client); + backend = clientLauncher.getServerProxy(); + + var slHome = tmpDir.resolve("sonarlintHome"); + Files.createDirectories(slHome); + var pluginJar = FileUtils + .listFiles(Paths.get("../../../sonar-plugin/sonar-javascript-plugin/target/").toAbsolutePath().normalize().toFile(), new RegexFileFilter("^sonar-javascript-plugin-([0-9.]+)(-SNAPSHOT)*.jar$"), + FalseFileFilter.FALSE) + .iterator().next().toPath(); + + var featureFlags = new FeatureFlagsDto(false, false, false, false, true, false, false, false, false, false); + + backend.initialize( + new InitializeParams(IT_CLIENT_INFO, IT_TELEMETRY_ATTRIBUTES, HttpConfigurationDto.defaultConfig(), null, featureFlags, + slHome.resolve("storage"), + slHome.resolve("work"), + Set.of(pluginJar), Collections.emptyMap(), + Set.of(org.sonarsource.sonarlint.core.rpc.protocol.common.Language.JS, org.sonarsource.sonarlint.core.rpc.protocol.common.Language.TS, org.sonarsource.sonarlint.core.rpc.protocol.common.Language.CSS), Collections.emptySet(), Collections.emptySet(), Collections.emptyList(), Collections.emptyList(), slHome.toString(), Map.of(), + false, new LanguageSpecificRequirements(null, null), false, null)) + .get(); + } + + @BeforeEach + public void cleanupClient() { + client.clear(); + } + + @AfterEach + public void cleanupBackend() { + // Reset rules configuration + backend.getRulesService().updateStandaloneRulesConfiguration(new UpdateStandaloneRulesConfigurationParams(Map.of())); + } + + @AfterAll + @SuppressWarnings("java:S2925") + public static void stop() throws InterruptedException { + Thread.sleep(5000); + backend.shutdown().join(); + } + + @Test + void analyzeJSProject(@TempDir Path tmpDir) throws Exception { + Path baseDir = prepareTestSolution(tmpDir, "module-js"); + + var issues = analyzeJSFile(SOLUTION1_MODULE_KEY, baseDir.toString(), "index.js", "var i = 0;"); + + assertThat(issues) + .extracting(RawIssueDto::getRuleKey, RawIssueDto::getPrimaryMessage) + .containsOnly( + tuple("javascript:S3504", "Unexpected var, use let or const instead.")); + + issues = analyzeJSFile(SOLUTION1_MODULE_KEY, baseDir.toString(), "index.js", "let i = 0;"); + + assertThat(issues) + .isEmpty(); + } + + private Path prepareTestSolution(Path tmpDir, String name) throws IOException { + Path baseDir = tmpDir.toRealPath().resolve(name); + Files.createDirectories(baseDir); + FileUtils.copyDirectory(new File("src/test/projects/" + name), baseDir.toFile()); + return baseDir; + } + + private List analyzeJSFile(String configScopeId, String baseDir, String filePathStr, String content) throws Exception { + var filePath = Path.of("projects").resolve(baseDir).resolve(filePathStr); + var fileUri = filePath.toUri(); + changeContents(configScopeId, baseDir, filePathStr, content); + var analyzeResponse = analyzeFiles(configScopeId, List.of(fileUri)); + assertThat(analyzeResponse.getFailedAnalysisFiles()).isEmpty(); + + // it could happen that the notification is not yet received while the analysis request is finished. + // await().atMost(Duration.ofMillis(200)).untilAsserted(() -> assertThat(((MockSonarLintRpcClientDelegate) client).getRaisedIssues(configScopeId)).isNotEmpty()); + Thread.sleep(200); + var raisedIssues = ((MockSonarLintRpcClientDelegate) client).getRaisedIssues(configScopeId); + + ((MockSonarLintRpcClientDelegate) client).getRaisedIssues().clear(); + return raisedIssues != null ? raisedIssues : List.of(); + } + + private AnalyzeFilesResponse analyzeFiles(String configScopeId, List files) { + return backend.getAnalysisService().analyzeFilesAndTrack( + new AnalyzeFilesAndTrackParams(configScopeId, UUID.randomUUID(), files, new HashMap<>(), false, System.currentTimeMillis()) + ).join(); + } + + private void changeContents(String configScopeId, String baseDir, String filePathStr, String content) { + var filePath = Path.of("projects").resolve(baseDir).resolve(filePathStr); + var fileUri = filePath.toUri(); + backend.getFileService().didUpdateFileSystem(new DidUpdateFileSystemParams(List.of(), + List.of(new ClientFileDto(fileUri, Path.of(filePathStr), configScopeId, false, "UTF-8", filePath.toAbsolutePath(), content, Language.JS, true)))); + } + + private void updateProperties(String configScopeId, HashMap propertiesMap) { + backend.getAnalysisService().didSetUserAnalysisProperties(new DidChangeAnalysisPropertiesParams(configScopeId, propertiesMap)); + } + + static class MockSonarLintRpcClientDelegate implements SonarLintRpcClientDelegate { + private final Map> raisedIssues = new HashMap<>(); + private final List logs = new ArrayList<>(); + + public List getRaisedIssues(String configurationScopeId) { + var issues = raisedIssues.get(configurationScopeId); + return issues != null ? issues : List.of(); + } + + public Map> getRaisedIssues() { + return raisedIssues; + } + + public List getLogs() { + return logs; + } + + @Override + public void didRaiseIssue(String configurationScopeId, UUID analysisId, RawIssueDto rawIssue) { + raisedIssues.computeIfAbsent(configurationScopeId, k -> new ArrayList<>()).add(rawIssue); + } + + @Override + public void suggestBinding(Map> suggestionsByConfigScope) { + + } + + @Override + public void suggestConnection(Map> suggestionsByConfigScope) { + + } + + @Override + public void openUrlInBrowser(URL url) { + + } + + @Override + public void showMessage(MessageType type, String text) { + + } + + @Override + public void log(LogParams params) { + this.logs.add(params.getMessage()); + } + + @Override + public void showSoonUnsupportedMessage(ShowSoonUnsupportedMessageParams params) { + + } + + @Override + public void showSmartNotification(ShowSmartNotificationParams params) { + + } + + @Override + public String getClientLiveDescription() { + return ""; + } + + @Override + public void showHotspot(String configurationScopeId, HotspotDetailsDto hotspotDetails) { + + } + + @Override + public void showIssue(String configurationScopeId, IssueDetailsDto issueDetails) { + + } + + @Override + public void showFixSuggestion(String configurationScopeId, String issueKey, FixSuggestionDto fixSuggestion) { + + } + + @Override + public AssistCreatingConnectionResponse assistCreatingConnection(AssistCreatingConnectionParams params, SonarLintCancelChecker cancelChecker) throws CancellationException { + throw new CancellationException("Unsupported in ITS"); + } + + @Override + public AssistBindingResponse assistBinding(AssistBindingParams params, SonarLintCancelChecker cancelChecker) throws CancellationException { + throw new CancellationException("Unsupported in ITS"); + } + + @Override + public void startProgress(StartProgressParams params) throws UnsupportedOperationException { + + } + + @Override + public void reportProgress(ReportProgressParams params) { + + } + + @Override + public void didSynchronizeConfigurationScopes(Set configurationScopeIds) { + + } + + @Override + public Either getCredentials(String connectionId) throws ConnectionNotFoundException { + throw new ConnectionNotFoundException(); + } + + @Override + public List selectProxies(URI uri) { + return List.of(ProxyDto.NO_PROXY); + } + + @Override + public GetProxyPasswordAuthenticationResponse getProxyPasswordAuthentication(String host, int port, String protocol, String prompt, String scheme, URL targetHost) { + return new GetProxyPasswordAuthenticationResponse("", ""); + } + + @Override + public boolean checkServerTrusted(List chain, String authType) { + return false; + } + + @Override + public void didReceiveServerHotspotEvent(DidReceiveServerHotspotEvent params) { + + } + + @Override + public String matchSonarProjectBranch(String configurationScopeId, String mainBranchName, Set allBranchesNames, SonarLintCancelChecker cancelChecker) + throws ConfigScopeNotFoundException { + return mainBranchName; + } + + public boolean matchProjectBranch(String configurationScopeId, String branchNameToMatch, SonarLintCancelChecker cancelChecker) throws ConfigScopeNotFoundException { + return true; + } + + @Override + public void didChangeMatchedSonarProjectBranch(String configScopeId, String newMatchedBranchName) { + + } + + @Override + public TelemetryClientLiveAttributesResponse getTelemetryLiveAttributes() { + System.err.println("Telemetry should be disabled in ITs"); + throw new CancellationException("Telemetry should be disabled in ITs"); + } + + @Override + public void didChangeTaintVulnerabilities(String configurationScopeId, Set closedTaintVulnerabilityIds, List addedTaintVulnerabilities, + List updatedTaintVulnerabilities) { + + } + + @Override + public List listFiles(String configScopeId) { + return List.of(); + } + + @Override + public void noBindingSuggestionFound(NoBindingSuggestionFoundParams params) { + } + + @Override + public void didChangeAnalysisReadiness(Set configurationScopeIds, boolean areReadyForAnalysis) { + + } + + public void clear() { + raisedIssues.clear(); + logs.clear(); + } + + } +} diff --git a/its/plugin/sonarlint-rpc-tests/src/test/projects/module-js/index.js b/its/plugin/sonarlint-rpc-tests/src/test/projects/module-js/index.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/its/plugin/sonarlint-tests/src/test/resources/TestUtils.txt b/its/plugin/sonarlint-tests/src/test/resources/TestUtils.txt deleted file mode 100644 index 1a8c1482bb1..00000000000 --- a/its/plugin/sonarlint-tests/src/test/resources/TestUtils.txt +++ /dev/null @@ -1 +0,0 @@ -This file is used to locate the directory of the project in tests, so we can find relative path to the plugin jar file. diff --git a/its/plugin/tests/src/test/java/com/sonar/javascript/it/plugin/TestUtils.java b/its/plugin/tests/src/test/java/com/sonar/javascript/it/plugin/TestUtils.java index ae7b1a239bd..857e098bf2d 100644 --- a/its/plugin/tests/src/test/java/com/sonar/javascript/it/plugin/TestUtils.java +++ b/its/plugin/tests/src/test/java/com/sonar/javascript/it/plugin/TestUtils.java @@ -22,7 +22,6 @@ import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; -import java.net.URISyntaxException; import java.nio.file.CopyOption; import java.nio.file.FileVisitResult; import java.nio.file.Files; @@ -37,19 +36,7 @@ public class TestUtils { private static final File HOME; static { - File testResources; - try { - testResources = new File(TestUtils.class.getResource("/TestUtils.txt").toURI()); - } catch (URISyntaxException e) { - throw new IllegalStateException("failed to obtain HOME", e); - } - - HOME = - testResources // home/tests/src/test/resources - .getParentFile() // home/tests/src/test - .getParentFile() // home/tests/src - .getParentFile() // home/tests - .getParentFile(); // home + HOME = Path.of("../").toFile(); } public static File homeDir() { diff --git a/its/plugin/tests/src/test/resources/TestUtils.txt b/its/plugin/tests/src/test/resources/TestUtils.txt deleted file mode 100644 index d48aeee5586..00000000000 --- a/its/plugin/tests/src/test/resources/TestUtils.txt +++ /dev/null @@ -1 +0,0 @@ -Required to locate test home directory \ No newline at end of file