From e0344b6c7d224698e16a27b9d2d8a35fd106199c Mon Sep 17 00:00:00 2001 From: Georgii Borovinskikh <117642191+georgii-borovinskikh-sonarsource@users.noreply.github.com> Date: Fri, 15 Mar 2024 10:36:52 +0100 Subject: [PATCH] Add SLCoreService with SLOOP initialization & shutdown (#5297) Part of #5291 --- src/Core/Binding/IConfigScopeUpdater.cs | 29 ++ .../MefServices/ActiveSolutionBoundTracker.cs | 1 - src/Integration/Service/VersionHelper.cs | 2 +- .../SLCoreTestProcessRunner.cs | 1 + src/SLCore.UnitTests/SLCoreHandleTests.cs | 310 ++++++++++++++++++ src/SLCore.UnitTests/SLCoreRpcFactoryTests.cs | 1 + .../State/ActiveConfigScopeTrackerTests.cs | 4 +- .../State/AliveConnectionTrackerTests.cs | 2 +- .../State}/ConfigScopeUpdaterTests.cs | 6 +- .../ISLCoreConfigurationProvider.cs | 29 ++ .../Configuration/ISLCoreConstantsProvider.cs | 30 ++ .../ISLCoreEmbeddedPluginJarLocator.cs} | 10 +- .../Configuration/ISLCoreFoldersProvider.cs | 26 ++ .../ISLCoreLocator.cs | 4 +- src/SLCore/Configuration/SLCoreFolders.cs | 23 ++ .../SLCoreLaunchParameters.cs | 4 +- src/SLCore/Core/ISLCoreServiceProvider.cs | 2 +- .../Core/Process/ISLCoreProcessFactory.cs | 2 + src/SLCore/Core/Process/SLCoreProcess.cs | 1 + .../Core/Process/SLCoreProcessFactory.cs | 1 + src/SLCore/ISLCoreRpcFactory.cs | 3 +- src/SLCore/SLCore.csproj | 10 +- src/SLCore/SLCoreHandle.cs | 125 +++++++ ....Designer.cs => SLCoreStrings.Designer.cs} | 15 +- .../{Strings.resx => SLCoreStrings.resx} | 0 src/SLCore/State/ActiveConfigScopeTracker.cs | 4 +- src/SLCore/State/AliveConnectionTracker.cs | 2 +- .../State}/ConfigScopeUpdater.cs | 22 +- src/SLCore/State/ServerConnectionsProvider.cs | 2 +- 29 files changed, 619 insertions(+), 52 deletions(-) create mode 100644 src/Core/Binding/IConfigScopeUpdater.cs create mode 100644 src/SLCore.UnitTests/SLCoreHandleTests.cs rename src/{Integration.UnitTests/MefServices => SLCore.UnitTests/State}/ConfigScopeUpdaterTests.cs (97%) create mode 100644 src/SLCore/Configuration/ISLCoreConfigurationProvider.cs create mode 100644 src/SLCore/Configuration/ISLCoreConstantsProvider.cs rename src/{ConnectedMode/Binding/IServerConnectionConfigurationProvider.cs => SLCore/Configuration/ISLCoreEmbeddedPluginJarLocator.cs} (76%) create mode 100644 src/SLCore/Configuration/ISLCoreFoldersProvider.cs rename src/SLCore/{Core/Process => Configuration}/ISLCoreLocator.cs (91%) create mode 100644 src/SLCore/Configuration/SLCoreFolders.cs rename src/SLCore/{Core/Process => Configuration}/SLCoreLaunchParameters.cs (85%) create mode 100644 src/SLCore/SLCoreHandle.cs rename src/SLCore/{Strings.Designer.cs => SLCoreStrings.Designer.cs} (87%) rename src/SLCore/{Strings.resx => SLCoreStrings.resx} (100%) rename src/{Integration/MefServices => SLCore/State}/ConfigScopeUpdater.cs (82%) diff --git a/src/Core/Binding/IConfigScopeUpdater.cs b/src/Core/Binding/IConfigScopeUpdater.cs new file mode 100644 index 000000000..638f21ff8 --- /dev/null +++ b/src/Core/Binding/IConfigScopeUpdater.cs @@ -0,0 +1,29 @@ +/* + * SonarLint for Visual Studio + * 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. + */ + +namespace SonarLint.VisualStudio.Core.Binding; + +/// +/// Updates current active configuration scope of SLCore +/// +public interface IConfigScopeUpdater +{ + void UpdateConfigScopeForCurrentSolution(BoundSonarQubeProject currentBinding); +} diff --git a/src/Integration/MefServices/ActiveSolutionBoundTracker.cs b/src/Integration/MefServices/ActiveSolutionBoundTracker.cs index 7c303f311..e1349435a 100644 --- a/src/Integration/MefServices/ActiveSolutionBoundTracker.cs +++ b/src/Integration/MefServices/ActiveSolutionBoundTracker.cs @@ -27,7 +27,6 @@ using SonarLint.VisualStudio.Core; using SonarLint.VisualStudio.Core.Binding; using SonarLint.VisualStudio.Integration.State; -using SonarLint.VisualStudio.SLCore.Core; using SonarQube.Client; using Task = System.Threading.Tasks.Task; diff --git a/src/Integration/Service/VersionHelper.cs b/src/Integration/Service/VersionHelper.cs index 828c02d4e..9fb9a8e8f 100644 --- a/src/Integration/Service/VersionHelper.cs +++ b/src/Integration/Service/VersionHelper.cs @@ -25,7 +25,7 @@ namespace SonarLint.VisualStudio.Integration.Service { - internal static class VersionHelper + public static class VersionHelper { private const char PrereleaseSeparator = '-'; diff --git a/src/SLCore.IntegrationTests/SLCoreTestProcessRunner.cs b/src/SLCore.IntegrationTests/SLCoreTestProcessRunner.cs index 5866f3b6d..f72586481 100644 --- a/src/SLCore.IntegrationTests/SLCoreTestProcessRunner.cs +++ b/src/SLCore.IntegrationTests/SLCoreTestProcessRunner.cs @@ -23,6 +23,7 @@ using System.IO; using System.Threading; using Microsoft.VisualStudio.Threading; +using SonarLint.VisualStudio.SLCore.Configuration; using SonarLint.VisualStudio.SLCore.Core; using SonarLint.VisualStudio.SLCore.Core.Process; using SonarLint.VisualStudio.SLCore.Protocol; diff --git a/src/SLCore.UnitTests/SLCoreHandleTests.cs b/src/SLCore.UnitTests/SLCoreHandleTests.cs new file mode 100644 index 000000000..6ccf33119 --- /dev/null +++ b/src/SLCore.UnitTests/SLCoreHandleTests.cs @@ -0,0 +1,310 @@ +/* + * SonarLint for Visual Studio + * 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. + */ + +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ClearExtensions; +using SonarLint.VisualStudio.Core; +using SonarLint.VisualStudio.Core.Binding; +using SonarLint.VisualStudio.SLCore.Configuration; +using SonarLint.VisualStudio.SLCore.Core; +using SonarLint.VisualStudio.SLCore.Service.Connection.Models; +using SonarLint.VisualStudio.SLCore.Service.Lifecycle; +using SonarLint.VisualStudio.SLCore.Service.Lifecycle.Models; +using SonarLint.VisualStudio.SLCore.Service.Telemetry; +using SonarLint.VisualStudio.SLCore.State; +using Language = SonarLint.VisualStudio.SLCore.Common.Models.Language; + +namespace SonarLint.VisualStudio.SLCore.UnitTests; + +[TestClass] +public class SLCoreHandleTests +{ + private const string StorageRoot = "storageRootSl"; + private const string WorkDir = "workDirSl"; + private const string UserHome = "userHomeSl"; + + private static readonly ClientConstantsDto ClientConstants = new(default, default); + private static readonly FeatureFlagsDto FeatureFlags = new(default, default, default, default, default, default, default); + private static readonly TelemetryClientConstantAttributesDto TelemetryConstants = new(default, default, default, default, default); + + private static readonly SonarQubeConnectionConfigurationDto SonarQubeConnection1 = new("sq1", true, "http://localhost/"); + private static readonly SonarQubeConnectionConfigurationDto SonarQubeConnection2 = new("sq2", true, "https://next.sonarqube.org/"); + private static readonly SonarCloudConnectionConfigurationDto SonarCloudConnection = new("sc", true, "https://sonarcloud.io/"); + + private static readonly BoundSonarQubeProject Binding = new(); + + private static readonly List JarList = new() { "jar1" }; + + [TestMethod] + public async Task Initialize_ThrowsIfServicesUnavailable() + { + var testSubject = CreateTestSubject(out var slCoreRpcFactory, out _, out _, out _, out _, out _, out _, out _); + SetUpSLCoreRpcFactory(slCoreRpcFactory, out var rpc); + SetUpSLCoreRpc(rpc, out var serviceProvider); + serviceProvider.TryGetTransientService(out Arg.Any()).ReturnsForAnyArgs(false); + + var act = () => testSubject.InitializeAsync(); + + await act.Should().ThrowAsync().WithMessage(SLCoreStrings.ServiceProviderNotInitialized); + } + + [TestMethod] + public async Task Initialize_SuccessfullyInitializesInCorrectOrder() + { + var testSubject = CreateTestSubject(out var slCoreRpcFactory, + out var constantsProvider, + out var foldersProvider, + out var connectionsProvider, + out var jarLocator, + out var activeSolutionBoundTracker, + out var configScopeUpdater, + out var threadHandling); + + SetUpSuccessfulInitialization(slCoreRpcFactory, + constantsProvider, + foldersProvider, + connectionsProvider, + jarLocator, + activeSolutionBoundTracker, + out var lifecycleManagement, + out var telemetryService, + out _); + + await testSubject.InitializeAsync(); + + Received.InOrder(() => + { + threadHandling.ThrowIfOnUIThread(); + slCoreRpcFactory.StartNewRpcInstance(); + lifecycleManagement.InitializeAsync(Arg.Is(parameters => + parameters.clientConstantInfo == ClientConstants + && parameters.featureFlags == FeatureFlags + && parameters.storageRoot == StorageRoot + && parameters.workDir == WorkDir + && parameters.embeddedPluginPaths == JarList + && parameters.connectedModeEmbeddedPluginPathsByKey.Count == 0 + && parameters.enabledLanguagesInStandaloneMode.SequenceEqual(new[] + { + Language.C, + Language.CPP, + Language.CS, + Language.VBNET, + Language.JS, + Language.TS, + Language.CSS, + Language.SECRETS + }) + && parameters.extraEnabledLanguagesInConnectedMode.Count == 0 + && parameters.sonarQubeConnections.SequenceEqual(new[] { SonarQubeConnection1, SonarQubeConnection2 }) + && parameters.sonarCloudConnections.SequenceEqual(new[] { SonarCloudConnection }) + && parameters.sonarlintUserHome == UserHome + && parameters.standaloneRuleConfigByKey.Count == 0 + && !parameters.isFocusOnNewCode + && parameters.telemetryConstantAttributes == TelemetryConstants + && parameters.clientNodeJsPath == null)); + telemetryService.DisableTelemetry(); + configScopeUpdater.UpdateConfigScopeForCurrentSolution(Binding); + }); + } + + [TestMethod] + public async Task Dispose_Initialized_ShutsDownAndDisposesRpc() + { + var testSubject = CreateTestSubject(out var slCoreRpcFactory, + out var constantsProvider, + out var foldersProvider, + out var connectionsProvider, + out var jarLocator, + out var activeSolutionBoundTracker, + out _, + out var threadHandling); + + SetUpSuccessfulInitialization(slCoreRpcFactory, + constantsProvider, + foldersProvider, + connectionsProvider, + jarLocator, + activeSolutionBoundTracker, + out var lifecycleManagement, + out _, + out var rpc); + await testSubject.InitializeAsync(); + + var serviceProvider = rpc.ServiceProvider; + serviceProvider.ClearReceivedCalls(); + testSubject.Dispose(); + + + serviceProvider.Received().TryGetTransientService(out Arg.Any()); + threadHandling.ReceivedWithAnyArgs().Run(() => Task.FromResult(0)); + lifecycleManagement.Received().ShutdownAsync(); + rpc.Received().Dispose(); + } + + [TestMethod] + public async Task Dispose_ConnectionDied_DisposesRpc() + { + var testSubject = CreateTestSubject(out var slCoreRpcFactory, + out var constantsProvider, + out var foldersProvider, + out var connectionsProvider, + out var jarLocator, + out var activeSolutionBoundTracker, + out _, + out var threadHandling); + + SetUpSuccessfulInitialization(slCoreRpcFactory, + constantsProvider, + foldersProvider, + connectionsProvider, + jarLocator, + activeSolutionBoundTracker, + out var lifecycleManagement, + out _, + out var rpc); + await testSubject.InitializeAsync(); + + var slCoreServiceProvider = rpc.ServiceProvider; + slCoreServiceProvider.ClearSubstitute(); + slCoreServiceProvider.ClearReceivedCalls(); + slCoreServiceProvider.TryGetTransientService(out Arg.Any()).Returns(false); + testSubject.Dispose(); + + slCoreServiceProvider.ReceivedWithAnyArgs().TryGetTransientService(out Arg.Any()); + rpc.Received().Dispose(); + threadHandling.DidNotReceiveWithAnyArgs().Run(() => Task.FromResult(0)); + lifecycleManagement.DidNotReceive().ShutdownAsync(); + } + + [TestMethod] + public void Dispose_NotInitialized_DoesNothing() + { + var testSubject = CreateTestSubject(out _, + out _, + out _, + out _, + out _, + out _, + out _, + out var threadHandling); + + var act = () => testSubject.Dispose(); + + act.Should().NotThrow(); + threadHandling.DidNotReceiveWithAnyArgs().Run(() => Task.FromResult(0)); + } + + private void SetUpSuccessfulInitialization(ISLCoreRpcFactory slCoreRpcFactory, + ISLCoreConstantsProvider constantsProvider, + ISLCoreFoldersProvider foldersProvider, + IServerConnectionsProvider connectionsProvider, + ISLCoreEmbeddedPluginJarLocator jarLocator, + IActiveSolutionBoundTracker activeSolutionBoundTracker, + out ILifecycleManagementSLCoreService lifecycleManagement, + out ITelemetrySLCoreService telemetry, + out ISLCoreRpc rpc) + { + SetUpSLCoreRpcFactory(slCoreRpcFactory, out rpc); + SetUpSLCoreRpc(rpc, out var serviceProvider); + SetUpSLCoreServiceProvider(serviceProvider, out lifecycleManagement, out telemetry); + constantsProvider.ClientConstants.Returns(ClientConstants); + constantsProvider.FeatureFlags.Returns(FeatureFlags); + constantsProvider.TelemetryConstants.Returns(TelemetryConstants); + foldersProvider.GetWorkFolders().Returns(new SLCoreFolders(StorageRoot, WorkDir, UserHome)); + connectionsProvider.GetServerConnections().Returns(new Dictionary + { + { SonarQubeConnection1.connectionId, SonarQubeConnection1 }, + { SonarQubeConnection2.connectionId, SonarQubeConnection2 }, + { SonarCloudConnection.connectionId, SonarCloudConnection } + }); + jarLocator.ListJarFiles().Returns(JarList); + activeSolutionBoundTracker.CurrentConfiguration.Returns(new BindingConfiguration(Binding, SonarLintMode.Connected, "dir")); + } + + #region RpcSetUp + + private void SetUpSLCoreRpcFactory(ISLCoreRpcFactory slCoreRpcFactory, out ISLCoreRpc slCoreRpc) + { + slCoreRpc = Substitute.For(); + slCoreRpcFactory.StartNewRpcInstance().Returns(slCoreRpc); + } + + private void SetUpSLCoreRpc(ISLCoreRpc slCoreRpc, out ISLCoreServiceProvider slCoreServiceProvider) + { + slCoreServiceProvider = Substitute.For(); + slCoreRpc.ServiceProvider.Returns(slCoreServiceProvider); + } + + private void SetUpSLCoreServiceProvider(ISLCoreServiceProvider slCoreServiceProvider, + out ILifecycleManagementSLCoreService lifecycleManagementSlCoreService, out ITelemetrySLCoreService telemetrySlCoreService) + { + var managementService = Substitute.For(); + lifecycleManagementSlCoreService = managementService; + var telemetryService = Substitute.For(); + telemetrySlCoreService = telemetryService; + slCoreServiceProvider.TryGetTransientService(out Arg.Any()).Returns(x => + { + x[0] = managementService; + return true; + }); + slCoreServiceProvider.TryGetTransientService(out Arg.Any()).Returns(x => + { + x[0] = telemetryService; + return true; + }); + } + + #endregion + + private SLCoreHandle CreateTestSubject(out ISLCoreRpcFactory slCoreRpcFactory, + out ISLCoreConstantsProvider constantsProvider, + out ISLCoreFoldersProvider slCoreFoldersProvider, + out IServerConnectionsProvider serverConnectionConfigurationProvider, + out ISLCoreEmbeddedPluginJarLocator slCoreEmbeddedPluginJarProvider, + out IActiveSolutionBoundTracker activeSolutionBoundTracker, + out IConfigScopeUpdater configScopeUpdater, + out IThreadHandling threadHandling) + { + slCoreRpcFactory = Substitute.For(); + constantsProvider = Substitute.For(); + slCoreFoldersProvider = Substitute.For(); + serverConnectionConfigurationProvider = Substitute.For(); + slCoreEmbeddedPluginJarProvider = Substitute.For(); + activeSolutionBoundTracker = Substitute.For(); + configScopeUpdater = Substitute.For(); + threadHandling = Substitute.ForPartsOf(); + + return new SLCoreHandle(slCoreRpcFactory, + constantsProvider, + slCoreFoldersProvider, + serverConnectionConfigurationProvider, + slCoreEmbeddedPluginJarProvider, + activeSolutionBoundTracker, + configScopeUpdater, + threadHandling); + } + + internal class AnySLCoreService : Arg.AnyType, ISLCoreService + { + } +} diff --git a/src/SLCore.UnitTests/SLCoreRpcFactoryTests.cs b/src/SLCore.UnitTests/SLCoreRpcFactoryTests.cs index 7ac3ef572..e625415ef 100644 --- a/src/SLCore.UnitTests/SLCoreRpcFactoryTests.cs +++ b/src/SLCore.UnitTests/SLCoreRpcFactoryTests.cs @@ -19,6 +19,7 @@ */ using NSubstitute; +using SonarLint.VisualStudio.SLCore.Configuration; using SonarLint.VisualStudio.SLCore.Core; using SonarLint.VisualStudio.SLCore.Core.Process; diff --git a/src/SLCore.UnitTests/State/ActiveConfigScopeTrackerTests.cs b/src/SLCore.UnitTests/State/ActiveConfigScopeTrackerTests.cs index 888fc510b..2b7bef6d5 100644 --- a/src/SLCore.UnitTests/State/ActiveConfigScopeTrackerTests.cs +++ b/src/SLCore.UnitTests/State/ActiveConfigScopeTrackerTests.cs @@ -116,7 +116,7 @@ public void SetCurrentConfigScope_ServiceUnavailable_Throws() var act = () => testSubject.SetCurrentConfigScope("id"); - act.Should().ThrowExactly().WithMessage(Strings.ServiceProviderNotInitialized); + act.Should().ThrowExactly().WithMessage(SLCoreStrings.ServiceProviderNotInitialized); VerifyThreadHandling(threadHandling); } @@ -147,7 +147,7 @@ public void RemoveCurrentConfigScope_ServiceUnavailable_Throws() var act = () => testSubject.RemoveCurrentConfigScope(); - act.Should().ThrowExactly().WithMessage(Strings.ServiceProviderNotInitialized); + act.Should().ThrowExactly().WithMessage(SLCoreStrings.ServiceProviderNotInitialized); VerifyThreadHandling(threadHandling); } diff --git a/src/SLCore.UnitTests/State/AliveConnectionTrackerTests.cs b/src/SLCore.UnitTests/State/AliveConnectionTrackerTests.cs index a6017830c..58673494b 100644 --- a/src/SLCore.UnitTests/State/AliveConnectionTrackerTests.cs +++ b/src/SLCore.UnitTests/State/AliveConnectionTrackerTests.cs @@ -140,7 +140,7 @@ public void Refresh_ServiceUnavailable_Throws() var act = () => testSubject.RefreshConnectionList(); - act.Should().ThrowExactly().WithMessage(Strings.ServiceProviderNotInitialized); + act.Should().ThrowExactly().WithMessage(SLCoreStrings.ServiceProviderNotInitialized); } [TestMethod] diff --git a/src/Integration.UnitTests/MefServices/ConfigScopeUpdaterTests.cs b/src/SLCore.UnitTests/State/ConfigScopeUpdaterTests.cs similarity index 97% rename from src/Integration.UnitTests/MefServices/ConfigScopeUpdaterTests.cs rename to src/SLCore.UnitTests/State/ConfigScopeUpdaterTests.cs index 18dacbf48..3c7abbdcb 100644 --- a/src/Integration.UnitTests/MefServices/ConfigScopeUpdaterTests.cs +++ b/src/SLCore.UnitTests/State/ConfigScopeUpdaterTests.cs @@ -18,18 +18,14 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using System; using System.Threading.Tasks; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; using SonarLint.VisualStudio.Core; using SonarLint.VisualStudio.Core.Binding; using SonarLint.VisualStudio.SLCore.Common.Helpers; using SonarLint.VisualStudio.SLCore.State; -using SonarLint.VisualStudio.TestInfrastructure; using SonarQube.Client.Models; -namespace SonarLint.VisualStudio.Integration.UnitTests.MefServices; +namespace SonarLint.VisualStudio.SLCore.UnitTests.State; [TestClass] public class ConfigScopeUpdaterTests diff --git a/src/SLCore/Configuration/ISLCoreConfigurationProvider.cs b/src/SLCore/Configuration/ISLCoreConfigurationProvider.cs new file mode 100644 index 000000000..d21ff3e66 --- /dev/null +++ b/src/SLCore/Configuration/ISLCoreConfigurationProvider.cs @@ -0,0 +1,29 @@ +/* + * SonarLint for Visual Studio + * 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. + */ + +namespace SonarLint.VisualStudio.SLCore.Configuration; + +internal interface ISLCoreConfigurationProvider : ISLCoreLocator, + ISLCoreConstantsProvider, + ISLCoreFoldersProvider, + ISLCoreEmbeddedPluginJarLocator +{ + // placeholder for implementation +} diff --git a/src/SLCore/Configuration/ISLCoreConstantsProvider.cs b/src/SLCore/Configuration/ISLCoreConstantsProvider.cs new file mode 100644 index 000000000..19074a002 --- /dev/null +++ b/src/SLCore/Configuration/ISLCoreConstantsProvider.cs @@ -0,0 +1,30 @@ +/* + * SonarLint for Visual Studio + * 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. + */ + +using SonarLint.VisualStudio.SLCore.Service.Lifecycle.Models; + +namespace SonarLint.VisualStudio.SLCore.Configuration; + +internal interface ISLCoreConstantsProvider +{ + ClientConstantsDto ClientConstants { get; } + FeatureFlagsDto FeatureFlags { get; } + TelemetryClientConstantAttributesDto TelemetryConstants { get; } +} diff --git a/src/ConnectedMode/Binding/IServerConnectionConfigurationProvider.cs b/src/SLCore/Configuration/ISLCoreEmbeddedPluginJarLocator.cs similarity index 76% rename from src/ConnectedMode/Binding/IServerConnectionConfigurationProvider.cs rename to src/SLCore/Configuration/ISLCoreEmbeddedPluginJarLocator.cs index 1e021681d..9464b4a35 100644 --- a/src/ConnectedMode/Binding/IServerConnectionConfigurationProvider.cs +++ b/src/SLCore/Configuration/ISLCoreEmbeddedPluginJarLocator.cs @@ -19,12 +19,10 @@ */ using System.Collections.Generic; -using SonarLint.VisualStudio.SLCore.Service.Connection.Models; -namespace SonarLint.VisualStudio.ConnectedMode.Binding +namespace SonarLint.VisualStudio.SLCore.Configuration; + +internal interface ISLCoreEmbeddedPluginJarLocator { - public interface IServerConnectionConfigurationProvider - { - IEnumerable GetServerConnectionConfiguration(); - } + List ListJarFiles(); } diff --git a/src/SLCore/Configuration/ISLCoreFoldersProvider.cs b/src/SLCore/Configuration/ISLCoreFoldersProvider.cs new file mode 100644 index 000000000..4ef3c4f13 --- /dev/null +++ b/src/SLCore/Configuration/ISLCoreFoldersProvider.cs @@ -0,0 +1,26 @@ +/* + * SonarLint for Visual Studio + * 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. + */ + +namespace SonarLint.VisualStudio.SLCore.Configuration; + +internal interface ISLCoreFoldersProvider +{ + SLCoreFolders GetWorkFolders(); +} diff --git a/src/SLCore/Core/Process/ISLCoreLocator.cs b/src/SLCore/Configuration/ISLCoreLocator.cs similarity index 91% rename from src/SLCore/Core/Process/ISLCoreLocator.cs rename to src/SLCore/Configuration/ISLCoreLocator.cs index e7642a049..7e60ae278 100644 --- a/src/SLCore/Core/Process/ISLCoreLocator.cs +++ b/src/SLCore/Configuration/ISLCoreLocator.cs @@ -18,9 +18,9 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -namespace SonarLint.VisualStudio.SLCore.Core.Process; +namespace SonarLint.VisualStudio.SLCore.Configuration; -internal interface ISLCoreLocator +public interface ISLCoreLocator { SLCoreLaunchParameters LocateExecutable(); } diff --git a/src/SLCore/Configuration/SLCoreFolders.cs b/src/SLCore/Configuration/SLCoreFolders.cs new file mode 100644 index 000000000..8f01e8551 --- /dev/null +++ b/src/SLCore/Configuration/SLCoreFolders.cs @@ -0,0 +1,23 @@ +/* + * SonarLint for Visual Studio + * 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. + */ + +namespace SonarLint.VisualStudio.SLCore.Configuration; + +internal record SLCoreFolders(string StorageRoot, string WorkDir, string SonarlintUserHome); diff --git a/src/SLCore/Core/Process/SLCoreLaunchParameters.cs b/src/SLCore/Configuration/SLCoreLaunchParameters.cs similarity index 85% rename from src/SLCore/Core/Process/SLCoreLaunchParameters.cs rename to src/SLCore/Configuration/SLCoreLaunchParameters.cs index 48d288df8..472793c96 100644 --- a/src/SLCore/Core/Process/SLCoreLaunchParameters.cs +++ b/src/SLCore/Configuration/SLCoreLaunchParameters.cs @@ -18,6 +18,6 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -namespace SonarLint.VisualStudio.SLCore.Core.Process; +namespace SonarLint.VisualStudio.SLCore.Configuration; -internal record SLCoreLaunchParameters(string PathToExecutable, string LaunchArguments); +public record SLCoreLaunchParameters(string PathToExecutable, string LaunchArguments); diff --git a/src/SLCore/Core/ISLCoreServiceProvider.cs b/src/SLCore/Core/ISLCoreServiceProvider.cs index 915f01185..c8c65496b 100644 --- a/src/SLCore/Core/ISLCoreServiceProvider.cs +++ b/src/SLCore/Core/ISLCoreServiceProvider.cs @@ -89,7 +89,7 @@ public bool TryGetTransientService(out TService service) where TServic } catch (Exception ex) { - logger.WriteLine(Strings.SLCoreServiceProvider_CreateServiceError, ex.Message); + logger.WriteLine(SLCoreStrings.SLCoreServiceProvider_CreateServiceError, ex.Message); return false; } cache.Add(serviceType, cachedService); diff --git a/src/SLCore/Core/Process/ISLCoreProcessFactory.cs b/src/SLCore/Core/Process/ISLCoreProcessFactory.cs index 0b0513ee7..7bc80feb8 100644 --- a/src/SLCore/Core/Process/ISLCoreProcessFactory.cs +++ b/src/SLCore/Core/Process/ISLCoreProcessFactory.cs @@ -18,6 +18,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +using SonarLint.VisualStudio.SLCore.Configuration; + namespace SonarLint.VisualStudio.SLCore.Core.Process; internal interface ISLCoreProcessFactory diff --git a/src/SLCore/Core/Process/SLCoreProcess.cs b/src/SLCore/Core/Process/SLCoreProcess.cs index aaa25f031..e566fc1e2 100644 --- a/src/SLCore/Core/Process/SLCoreProcess.cs +++ b/src/SLCore/Core/Process/SLCoreProcess.cs @@ -21,6 +21,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; +using SonarLint.VisualStudio.SLCore.Configuration; namespace SonarLint.VisualStudio.SLCore.Core.Process; diff --git a/src/SLCore/Core/Process/SLCoreProcessFactory.cs b/src/SLCore/Core/Process/SLCoreProcessFactory.cs index 24c9efb34..46ebec3b6 100644 --- a/src/SLCore/Core/Process/SLCoreProcessFactory.cs +++ b/src/SLCore/Core/Process/SLCoreProcessFactory.cs @@ -20,6 +20,7 @@ using System.ComponentModel.Composition; using System.Diagnostics.CodeAnalysis; +using SonarLint.VisualStudio.SLCore.Configuration; namespace SonarLint.VisualStudio.SLCore.Core.Process; diff --git a/src/SLCore/ISLCoreRpcFactory.cs b/src/SLCore/ISLCoreRpcFactory.cs index f298515f7..41000d7f3 100644 --- a/src/SLCore/ISLCoreRpcFactory.cs +++ b/src/SLCore/ISLCoreRpcFactory.cs @@ -21,12 +21,13 @@ using System; using System.ComponentModel.Composition; using System.Threading.Tasks; +using SonarLint.VisualStudio.SLCore.Configuration; using SonarLint.VisualStudio.SLCore.Core; using SonarLint.VisualStudio.SLCore.Core.Process; namespace SonarLint.VisualStudio.SLCore; -public interface ISLCoreRpcFactory +internal interface ISLCoreRpcFactory { ISLCoreRpc StartNewRpcInstance(); } diff --git a/src/SLCore/SLCore.csproj b/src/SLCore/SLCore.csproj index f2ac21e6b..c6d55f727 100644 --- a/src/SLCore/SLCore.csproj +++ b/src/SLCore/SLCore.csproj @@ -21,17 +21,17 @@ - + True True - Strings.resx + SLCoreStrings.resx - - ResXFileCodeGenerator - Strings.Designer.cs + + PublicResXFileCodeGenerator + SLCoreStrings.Designer.cs diff --git a/src/SLCore/SLCoreHandle.cs b/src/SLCore/SLCoreHandle.cs new file mode 100644 index 000000000..d4d523255 --- /dev/null +++ b/src/SLCore/SLCoreHandle.cs @@ -0,0 +1,125 @@ +/* + * SonarLint for Visual Studio + * 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. + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SonarLint.VisualStudio.Core; +using SonarLint.VisualStudio.Core.Binding; +using SonarLint.VisualStudio.SLCore.Configuration; +using SonarLint.VisualStudio.SLCore.Service.Connection.Models; +using SonarLint.VisualStudio.SLCore.Service.Lifecycle; +using SonarLint.VisualStudio.SLCore.Service.Rules.Models; +using SonarLint.VisualStudio.SLCore.Service.Telemetry; +using SonarLint.VisualStudio.SLCore.State; +using Language = SonarLint.VisualStudio.SLCore.Common.Models.Language; + +namespace SonarLint.VisualStudio.SLCore; + +internal sealed class SLCoreHandle : IDisposable +{ + private readonly IActiveSolutionBoundTracker activeSolutionBoundTracker; + private readonly ISLCoreRpcFactory slCoreRpcFactory; + private readonly IServerConnectionsProvider serverConnectionConfigurationProvider; + private readonly IConfigScopeUpdater configScopeUpdater; + private readonly ISLCoreConstantsProvider constantsProvider; + private readonly ISLCoreFoldersProvider slCoreFoldersProvider; + private readonly ISLCoreEmbeddedPluginJarLocator slCoreEmbeddedPluginJarProvider; + private readonly IThreadHandling threadHandling; + private ISLCoreRpc slCoreRpc; + + + public SLCoreHandle(ISLCoreRpcFactory slCoreRpcFactory, ISLCoreConstantsProvider constantsProvider, ISLCoreFoldersProvider slCoreFoldersProvider, + IServerConnectionsProvider serverConnectionConfigurationProvider, ISLCoreEmbeddedPluginJarLocator slCoreEmbeddedPluginJarProvider, + IActiveSolutionBoundTracker activeSolutionBoundTracker, IConfigScopeUpdater configScopeUpdater, IThreadHandling threadHandling) + { + this.slCoreRpcFactory = slCoreRpcFactory; + this.constantsProvider = constantsProvider; + this.slCoreFoldersProvider = slCoreFoldersProvider; + this.serverConnectionConfigurationProvider = serverConnectionConfigurationProvider; + this.slCoreEmbeddedPluginJarProvider = slCoreEmbeddedPluginJarProvider; + this.activeSolutionBoundTracker = activeSolutionBoundTracker; + this.configScopeUpdater = configScopeUpdater; + this.threadHandling = threadHandling; + } + + public async Task InitializeAsync() + { + threadHandling.ThrowIfOnUIThread(); + + slCoreRpc = slCoreRpcFactory.StartNewRpcInstance(); + + if (!slCoreRpc.ServiceProvider.TryGetTransientService(out ILifecycleManagementSLCoreService lifecycleManagementSlCoreService) || + !slCoreRpc.ServiceProvider.TryGetTransientService(out ITelemetrySLCoreService telemetrySlCoreService)) + { + throw new InvalidOperationException(SLCoreStrings.ServiceProviderNotInitialized); + } + + var serverConnectionConfigurations = serverConnectionConfigurationProvider.GetServerConnections(); + var (storageRoot, workDir, sonarlintUserHome) = slCoreFoldersProvider.GetWorkFolders(); + + await lifecycleManagementSlCoreService.InitializeAsync(new InitializeParams( + constantsProvider.ClientConstants, + new HttpConfigurationDto(new SslConfigurationDto()), + constantsProvider.FeatureFlags, + storageRoot, + workDir, + embeddedPluginPaths: slCoreEmbeddedPluginJarProvider.ListJarFiles(), + connectedModeEmbeddedPluginPathsByKey: new Dictionary(), + enabledLanguagesInStandaloneMode: new List + { + Language.C, + Language.CPP, + Language.CS, + Language.VBNET, + Language.JS, + Language.TS, + Language.CSS, + Language.SECRETS + }, + extraEnabledLanguagesInConnectedMode: new List(), + serverConnectionConfigurations.Values.OfType().ToList(), + serverConnectionConfigurations.Values.OfType().ToList(), + sonarlintUserHome, + standaloneRuleConfigByKey: new Dictionary(), + isFocusOnNewCode: false, + constantsProvider.TelemetryConstants, + null)); + + telemetrySlCoreService.DisableTelemetry(); + + configScopeUpdater.UpdateConfigScopeForCurrentSolution(activeSolutionBoundTracker.CurrentConfiguration.Project); + } + + public void Dispose() + { + if (slCoreRpc?.ServiceProvider?.TryGetTransientService(out ILifecycleManagementSLCoreService lifecycleManagementSlCoreService) ?? false) + { + threadHandling.Run(async () => + { + await lifecycleManagementSlCoreService.ShutdownAsync(); + return 0; + }); + } + + slCoreRpc?.Dispose(); + } +} diff --git a/src/SLCore/Strings.Designer.cs b/src/SLCore/SLCoreStrings.Designer.cs similarity index 87% rename from src/SLCore/Strings.Designer.cs rename to src/SLCore/SLCoreStrings.Designer.cs index 0044437e1..d557f4f87 100644 --- a/src/SLCore/Strings.Designer.cs +++ b/src/SLCore/SLCoreStrings.Designer.cs @@ -1,6 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. +// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -21,24 +22,24 @@ namespace SonarLint.VisualStudio.SLCore { [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Strings { + public class SLCoreStrings { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Strings() { + internal SLCoreStrings() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { + public static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SonarLint.VisualStudio.SLCore.Strings", typeof(Strings).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SonarLint.VisualStudio.SLCore.SLCoreStrings", typeof(SLCoreStrings).Assembly); resourceMan = temp; } return resourceMan; @@ -50,7 +51,7 @@ internal Strings() { /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { + public static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } @@ -62,7 +63,7 @@ internal Strings() { /// /// Looks up a localized string similar to Service Provider is unavailable. /// - internal static string ServiceProviderNotInitialized { + public static string ServiceProviderNotInitialized { get { return ResourceManager.GetString("ServiceProviderNotInitialized", resourceCulture); } @@ -71,7 +72,7 @@ internal static string ServiceProviderNotInitialized { /// /// Looks up a localized string similar to "[SLCoreServiceProvider]Cannot Create Service. Error: {0}". /// - internal static string SLCoreServiceProvider_CreateServiceError { + public static string SLCoreServiceProvider_CreateServiceError { get { return ResourceManager.GetString("SLCoreServiceProvider_CreateServiceError", resourceCulture); } diff --git a/src/SLCore/Strings.resx b/src/SLCore/SLCoreStrings.resx similarity index 100% rename from src/SLCore/Strings.resx rename to src/SLCore/SLCoreStrings.resx diff --git a/src/SLCore/State/ActiveConfigScopeTracker.cs b/src/SLCore/State/ActiveConfigScopeTracker.cs index e283e043f..0be7b7a9f 100644 --- a/src/SLCore/State/ActiveConfigScopeTracker.cs +++ b/src/SLCore/State/ActiveConfigScopeTracker.cs @@ -94,7 +94,7 @@ public void SetCurrentConfigScope(string id, string connectionId = null, string if (!serviceProvider.TryGetTransientService(out IConfigurationScopeSLCoreService configurationScopeService)) { - throw new InvalidOperationException(Strings.ServiceProviderNotInitialized); + throw new InvalidOperationException(SLCoreStrings.ServiceProviderNotInitialized); } var configurationScopeDto = new ConfigurationScopeDto(id, @@ -126,7 +126,7 @@ public void RemoveCurrentConfigScope() if (!serviceProvider.TryGetTransientService(out IConfigurationScopeSLCoreService configurationScopeService)) { - throw new InvalidOperationException(Strings.ServiceProviderNotInitialized); + throw new InvalidOperationException(SLCoreStrings.ServiceProviderNotInitialized); } using (asyncLock.Acquire()) diff --git a/src/SLCore/State/AliveConnectionTracker.cs b/src/SLCore/State/AliveConnectionTracker.cs index 3a0542513..4749a5cd3 100644 --- a/src/SLCore/State/AliveConnectionTracker.cs +++ b/src/SLCore/State/AliveConnectionTracker.cs @@ -73,7 +73,7 @@ public void RefreshConnectionList() if (!serviceProvider.TryGetTransientService(out IConnectionConfigurationSLCoreService connectionConfigurationService)) { - throw new InvalidOperationException(Strings.ServiceProviderNotInitialized); + throw new InvalidOperationException(SLCoreStrings.ServiceProviderNotInitialized); } using (asyncLock.Acquire()) diff --git a/src/Integration/MefServices/ConfigScopeUpdater.cs b/src/SLCore/State/ConfigScopeUpdater.cs similarity index 82% rename from src/Integration/MefServices/ConfigScopeUpdater.cs rename to src/SLCore/State/ConfigScopeUpdater.cs index 8a51b0f94..eb5c16e76 100644 --- a/src/Integration/MefServices/ConfigScopeUpdater.cs +++ b/src/SLCore/State/ConfigScopeUpdater.cs @@ -24,14 +24,8 @@ using SonarLint.VisualStudio.Core; using SonarLint.VisualStudio.Core.Binding; using SonarLint.VisualStudio.SLCore.Common.Helpers; -using SonarLint.VisualStudio.SLCore.State; -namespace SonarLint.VisualStudio.Integration; - -internal interface IConfigScopeUpdater -{ - void UpdateConfigScopeForCurrentSolution(BoundSonarQubeProject currentBinding); -} +namespace SonarLint.VisualStudio.SLCore.State; [Export(typeof(IConfigScopeUpdater))] [PartCreationPolicy(CreationPolicy.Shared)] @@ -56,13 +50,13 @@ public void UpdateConfigScopeForCurrentSolution(BoundSonarQubeProject currentBin var solutionName = solutionInfoProvider.GetSolutionName(); threadHandling.RunOnBackgroundThread(() => - { - HandleConfigScopeUpdateInternal(solutionName, - connectionIdHelper.GetConnectionIdFromUri(currentBinding?.ServerUri, - currentBinding?.Organization?.Key), - currentBinding?.ProjectKey); - return Task.FromResult(0); - }).Forget(); + { + HandleConfigScopeUpdateInternal(solutionName, + connectionIdHelper.GetConnectionIdFromUri(currentBinding?.ServerUri, + currentBinding?.Organization?.Key), + currentBinding?.ProjectKey); + return Task.FromResult(0); + }).Forget(); } private void HandleConfigScopeUpdateInternal(string solutionName, string connectionId, string projectKey) diff --git a/src/SLCore/State/ServerConnectionsProvider.cs b/src/SLCore/State/ServerConnectionsProvider.cs index 3aae1f1d6..09eaa32e1 100644 --- a/src/SLCore/State/ServerConnectionsProvider.cs +++ b/src/SLCore/State/ServerConnectionsProvider.cs @@ -26,7 +26,7 @@ namespace SonarLint.VisualStudio.SLCore.State; -internal interface IServerConnectionsProvider +public interface IServerConnectionsProvider { Dictionary GetServerConnections(); }