From f445b8f7771017d4602dde4be2a051840b9eb1cb Mon Sep 17 00:00:00 2001 From: Vasileios Naskos <168648790+vnaskos-sonar@users.noreply.github.com> Date: Mon, 14 Oct 2024 15:04:57 +0000 Subject: [PATCH] SLVS-1408 Delete old TeamExplorer controllers and workflows (#5716) --- .../ConnectSectionViewModelTests.cs | 4 +- .../HostedCommandControllerBaseTests.cs | 93 -- .../Progress/ProgressStepRunnerTests.cs | 137 --- .../ReferencesTests.cs | 17 - .../SectionControllerTests.cs | 498 --------- .../app.config | 4 - .../CommonStyles.xaml | 209 +--- .../ConnectSectionView.xaml.cs | 14 +- .../ConnectSectionViewController.cs} | 37 +- .../ConnectSectionViewModel.cs | 64 +- .../SectionController.cs | 391 ------- .../Binding/AutoBindTriggerTests.cs | 109 -- .../Binding/BindingControllerTests.cs | 542 ---------- .../Binding/BindingWorkflowTests.cs | 171 ---- .../BasicCredentialsValidatorTests.cs | 116 --- .../Connection/ConnectControllerTests.cs | 476 --------- .../Connection/ConnectDialogViewModelTests.cs | 138 --- .../ConnectionInformationDialogTests.cs | 114 --- .../Connection/ConnectionWorkflowTests.cs | 516 ---------- .../Connection/UriValidatorTests.cs | 314 ------ .../HostedCommandControllerBaseTests.cs | 93 -- .../ActiveSolutionBoundTrackerTests.cs | 741 +++++--------- ...ectedModeWindowEventBasedSchedulerTests.cs | 130 --- .../MefServices/VsSessionHostTests.cs | 385 ------- .../ProgressNotificationListenerTests.cs | 97 -- .../State/StateManagerTests.cs | 650 ------------ .../State/TransferableVisualStateTests.cs | 92 -- .../TeamExplorer/ProjectViewModelTests.cs | 124 --- .../TeamExplorer/ServerViewModelTests.cs | 156 --- .../WPF/ContextualCommandViewModelTests.cs | 214 ---- .../WPF/ContextualCommandsCollectionTests.cs | 84 -- .../IsValidOrganizationKeyConverterTests.cs | 102 -- ...ectViewModelToBindingArgsConverterTests.cs | 75 -- ...rojectViewModelVisibilityConverterTests.cs | 231 ----- src/Integration.UnitTests/app.config | 4 - ...egration.Vsix_Baseline_WithStrongNames.txt | 13 +- ...ation.Vsix_Baseline_WithoutStrongNames.txt | 13 +- src/Integration/Binding/AutoBindTrigger.cs | 68 -- src/Integration/Binding/BindingController.cs | 189 ---- src/Integration/Binding/BindingWorkflow.cs | 160 --- src/Integration/Binding/IBindingWorkflow.cs | 32 - .../Binding/IBindingWorkflowExecutor.cs | 30 - ...BasicAuthenticationCredentialsValidator.cs | 126 --- .../Connection/ConnectConfiguration.cs | 27 - .../Connection/ConnectionController.cs | 236 ----- .../Connection/ConnectionInformationDialog.cs | 102 -- .../Connection/ConnectionWorkflow.cs | 293 ------ .../IConnectionInformationProvider.cs | 35 - .../Connection/IConnectionWorkflowExecutor.cs | 29 - .../UI/ConnectionInfoDialogView.xaml | 216 ---- .../UI/ConnectionInfoDialogView.xaml.cs | 94 -- .../UI/ConnectionInfoDialogViewModel.cs | 166 --- .../UI/IsValidOrganizationKeyConverter.cs | 63 -- .../UI/OrganizationSelectionWindow.xaml | 139 --- .../UI/OrganizationSelectionWindow.xaml.cs | 75 -- src/Integration/Connection/UriValidator.cs | 169 --- .../HostedCommandControllerBase.cs | 72 -- src/Integration/Integration.csproj | 5 - .../MefServices/ActiveSolutionBoundTracker.cs | 37 +- .../ConnectedModeWindowEventBasedScheduler.cs | 89 -- src/Integration/MefServices/IHost.cs | 66 -- src/Integration/MefServices/VsSessionHost.cs | 278 ----- .../Progress/IProgressControlHost.cs | 31 - .../Progress/IProgressStepRunnerWrapper.cs | 30 - .../Progress/ProgressNotificationListener.cs | 94 -- .../Progress/ProgressStepRunner.cs | 180 ---- src/Integration/Resources/Strings.Designer.cs | 958 +----------------- src/Integration/Resources/Strings.resx | 422 +------- src/Integration/State/IStateManager.cs | 71 -- src/Integration/State/StateManager.cs | 377 ------- .../State/TransferableVisualState.cs | 143 --- .../TeamExplorer/IConnectSectionView.cs | 26 - .../TeamExplorer/ISectionController.cs | 73 -- .../TeamExplorer/IUserNotification.cs | 40 - .../TeamExplorer/NotificationIds.cs | 34 - .../TeamExplorer/ProjectViewModel.cs | 115 --- .../TeamExplorer/ServerViewModel.cs | 125 --- .../WPF/ContextualCommandViewModel.cs | 165 --- .../WPF/ContextualCommandsCollection.cs | 43 - .../ProjectViewModelToBindingArgsConverter.cs | 52 - .../ProjectViewModelVisibilityConverter.cs | 64 -- src/SonarLint.VSSpecificAssemblies.props | 4 - .../ConfigurableConnectSectionViewModel.cs | 38 - ...nfigurableConnectionInformationProvider.cs | 57 -- .../Framework/ConfigurableHost.cs | 99 -- .../ConfigurableProgressControlHost.cs | 40 - .../ConfigurableProgressStepRunner.cs | 47 - .../ConfigurableSectionController.cs | 134 --- .../Framework/ConfigurableStateManager.cs | 174 ---- .../Framework/ConfigurableUriValidator.cs | 72 -- .../Framework/ConfigurableUserNotification.cs | 117 --- 91 files changed, 305 insertions(+), 13484 deletions(-) delete mode 100644 src/Integration.TeamExplorer.UnitTests/HostedCommandControllerBaseTests.cs delete mode 100644 src/Integration.TeamExplorer.UnitTests/Progress/ProgressStepRunnerTests.cs delete mode 100644 src/Integration.TeamExplorer.UnitTests/SectionControllerTests.cs rename src/{Integration/TeamExplorer/IConnectSectionViewModel.cs => Integration.TeamExplorer/ConnectSectionViewController.cs} (50%) delete mode 100644 src/Integration.TeamExplorer/SectionController.cs delete mode 100644 src/Integration.UnitTests/Binding/AutoBindTriggerTests.cs delete mode 100644 src/Integration.UnitTests/Binding/BindingControllerTests.cs delete mode 100644 src/Integration.UnitTests/Binding/BindingWorkflowTests.cs delete mode 100644 src/Integration.UnitTests/Connection/BasicCredentialsValidatorTests.cs delete mode 100644 src/Integration.UnitTests/Connection/ConnectControllerTests.cs delete mode 100644 src/Integration.UnitTests/Connection/ConnectDialogViewModelTests.cs delete mode 100644 src/Integration.UnitTests/Connection/ConnectionInformationDialogTests.cs delete mode 100644 src/Integration.UnitTests/Connection/ConnectionWorkflowTests.cs delete mode 100644 src/Integration.UnitTests/Connection/UriValidatorTests.cs delete mode 100644 src/Integration.UnitTests/HostedCommandControllerBaseTests.cs delete mode 100644 src/Integration.UnitTests/MefServices/ConnectedModeWindowEventBasedSchedulerTests.cs delete mode 100644 src/Integration.UnitTests/MefServices/VsSessionHostTests.cs delete mode 100644 src/Integration.UnitTests/Progress/ProgressNotificationListenerTests.cs delete mode 100644 src/Integration.UnitTests/State/StateManagerTests.cs delete mode 100644 src/Integration.UnitTests/State/TransferableVisualStateTests.cs delete mode 100644 src/Integration.UnitTests/TeamExplorer/ProjectViewModelTests.cs delete mode 100644 src/Integration.UnitTests/TeamExplorer/ServerViewModelTests.cs delete mode 100644 src/Integration.UnitTests/WPF/ContextualCommandViewModelTests.cs delete mode 100644 src/Integration.UnitTests/WPF/ContextualCommandsCollectionTests.cs delete mode 100644 src/Integration.UnitTests/WPF/IsValidOrganizationKeyConverterTests.cs delete mode 100644 src/Integration.UnitTests/WPF/ProjectViewModelToBindingArgsConverterTests.cs delete mode 100644 src/Integration.UnitTests/WPF/ProjectViewModelVisibilityConverterTests.cs delete mode 100644 src/Integration/Binding/AutoBindTrigger.cs delete mode 100644 src/Integration/Binding/BindingController.cs delete mode 100644 src/Integration/Binding/BindingWorkflow.cs delete mode 100644 src/Integration/Binding/IBindingWorkflow.cs delete mode 100644 src/Integration/Binding/IBindingWorkflowExecutor.cs delete mode 100644 src/Integration/Connection/BasicAuthenticationCredentialsValidator.cs delete mode 100644 src/Integration/Connection/ConnectConfiguration.cs delete mode 100644 src/Integration/Connection/ConnectionController.cs delete mode 100644 src/Integration/Connection/ConnectionInformationDialog.cs delete mode 100644 src/Integration/Connection/ConnectionWorkflow.cs delete mode 100644 src/Integration/Connection/IConnectionInformationProvider.cs delete mode 100644 src/Integration/Connection/IConnectionWorkflowExecutor.cs delete mode 100644 src/Integration/Connection/UI/ConnectionInfoDialogView.xaml delete mode 100644 src/Integration/Connection/UI/ConnectionInfoDialogView.xaml.cs delete mode 100644 src/Integration/Connection/UI/ConnectionInfoDialogViewModel.cs delete mode 100644 src/Integration/Connection/UI/IsValidOrganizationKeyConverter.cs delete mode 100644 src/Integration/Connection/UI/OrganizationSelectionWindow.xaml delete mode 100644 src/Integration/Connection/UI/OrganizationSelectionWindow.xaml.cs delete mode 100644 src/Integration/Connection/UriValidator.cs delete mode 100644 src/Integration/HostedCommandControllerBase.cs delete mode 100644 src/Integration/MefServices/ConnectedModeWindowEventBasedScheduler.cs delete mode 100644 src/Integration/MefServices/IHost.cs delete mode 100644 src/Integration/MefServices/VsSessionHost.cs delete mode 100644 src/Integration/Progress/IProgressControlHost.cs delete mode 100644 src/Integration/Progress/IProgressStepRunnerWrapper.cs delete mode 100644 src/Integration/Progress/ProgressNotificationListener.cs delete mode 100644 src/Integration/Progress/ProgressStepRunner.cs delete mode 100644 src/Integration/State/IStateManager.cs delete mode 100644 src/Integration/State/StateManager.cs delete mode 100644 src/Integration/State/TransferableVisualState.cs delete mode 100644 src/Integration/TeamExplorer/IConnectSectionView.cs delete mode 100644 src/Integration/TeamExplorer/ISectionController.cs delete mode 100644 src/Integration/TeamExplorer/IUserNotification.cs delete mode 100644 src/Integration/TeamExplorer/NotificationIds.cs delete mode 100644 src/Integration/TeamExplorer/ProjectViewModel.cs delete mode 100644 src/Integration/TeamExplorer/ServerViewModel.cs delete mode 100644 src/Integration/WPF/ContextualCommandViewModel.cs delete mode 100644 src/Integration/WPF/ContextualCommandsCollection.cs delete mode 100644 src/Integration/WPF/ProjectViewModelToBindingArgsConverter.cs delete mode 100644 src/Integration/WPF/ProjectViewModelVisibilityConverter.cs delete mode 100644 src/TestInfrastructure/Framework/ConfigurableConnectSectionViewModel.cs delete mode 100644 src/TestInfrastructure/Framework/ConfigurableConnectionInformationProvider.cs delete mode 100644 src/TestInfrastructure/Framework/ConfigurableHost.cs delete mode 100644 src/TestInfrastructure/Framework/ConfigurableProgressControlHost.cs delete mode 100644 src/TestInfrastructure/Framework/ConfigurableProgressStepRunner.cs delete mode 100644 src/TestInfrastructure/Framework/ConfigurableSectionController.cs delete mode 100644 src/TestInfrastructure/Framework/ConfigurableStateManager.cs delete mode 100644 src/TestInfrastructure/Framework/ConfigurableUriValidator.cs delete mode 100644 src/TestInfrastructure/Framework/ConfigurableUserNotification.cs diff --git a/src/Integration.TeamExplorer.UnitTests/ConnectSectionViewModelTests.cs b/src/Integration.TeamExplorer.UnitTests/ConnectSectionViewModelTests.cs index 81b8dc749c..9199d7eb35 100644 --- a/src/Integration.TeamExplorer.UnitTests/ConnectSectionViewModelTests.cs +++ b/src/Integration.TeamExplorer.UnitTests/ConnectSectionViewModelTests.cs @@ -18,8 +18,6 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; using SonarLint.VisualStudio.Integration.TeamExplorer; namespace SonarLint.VisualStudio.Integration.UnitTests.TeamExplorer @@ -36,4 +34,4 @@ public void ConnectSectionViewModel_Ctor_IsVisibleAndExpanded() vm.IsExpanded.Should().BeTrue(); } } -} \ No newline at end of file +} diff --git a/src/Integration.TeamExplorer.UnitTests/HostedCommandControllerBaseTests.cs b/src/Integration.TeamExplorer.UnitTests/HostedCommandControllerBaseTests.cs deleted file mode 100644 index f4d7f5ab20..0000000000 --- a/src/Integration.TeamExplorer.UnitTests/HostedCommandControllerBaseTests.cs +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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 FluentAssertions; -using Microsoft.VisualStudio.OLE.Interop; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SonarLint.VisualStudio.TestInfrastructure; - -namespace SonarLint.VisualStudio.Integration.UnitTests -{ - [TestClass] - public class HostedCommandControllerBaseTests - { - [TestMethod] - public void Ctor_InvalidArgument_Throws() - { - // Arrange - Action act = () => new TestHostedController(null); - - // Act & Assert - act.Should().Throw().And.ParamName.Should().Be("serviceProvider"); - } - - [TestMethod] - public void Ctor_ValidArgument_InitializedCorrectly() - { - // Arrange - var serviceProvider = new ConfigurableServiceProvider(); - - // Act - var testSubject = new TestHostedController(serviceProvider); - - // Assert - testSubject.ServiceProvider.Should().BeSameAs(serviceProvider); - } - - [TestMethod] - public void Ctor_QueryStatus() - { - // Arrange - var serviceProvider = new ConfigurableServiceProvider(); - var testSubject = (IOleCommandTarget)new TestHostedController(serviceProvider); - var guid = Guid.NewGuid(); - - // Act - var result = testSubject.QueryStatus(ref guid, 0, null, IntPtr.Zero); - - // Assert - result.Should().Be((int)Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_UNKNOWNGROUP); - } - - [TestMethod] - public void Ctor_QueryExec() - { - // Arrange - var serviceProvider = new ConfigurableServiceProvider(); - var testSubject = (IOleCommandTarget)new TestHostedController(serviceProvider); - var guid = Guid.NewGuid(); - - // Act - var result = testSubject.Exec(ref guid, 0, 0, IntPtr.Zero, IntPtr.Zero); - - // Assert - result.Should().Be((int)Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_UNKNOWNGROUP); - } - - internal class TestHostedController : HostedCommandControllerBase - { - public TestHostedController(System.IServiceProvider serviceProvider) - : base(serviceProvider) - { - } - } - } -} diff --git a/src/Integration.TeamExplorer.UnitTests/Progress/ProgressStepRunnerTests.cs b/src/Integration.TeamExplorer.UnitTests/Progress/ProgressStepRunnerTests.cs deleted file mode 100644 index 3c8f73c591..0000000000 --- a/src/Integration.TeamExplorer.UnitTests/Progress/ProgressStepRunnerTests.cs +++ /dev/null @@ -1,137 +0,0 @@ -/* - * 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 FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SonarLint.VisualStudio.Integration.Progress; -using SonarLint.VisualStudio.Progress.Controller; -using SonarLint.VisualStudio.Progress.Observation; -using SonarLint.VisualStudio.TestInfrastructure; - -/* Note: these tests are in this assembly because they have an indirect dependency on - * the TeamFoundation.Controls assembly - they create instances of the ProgressControl, - * which imports resources from the TF assembly. - */ -namespace SonarLint.VisualStudio.Integration.UnitTests.Progress -{ - [TestClass] - public class ProgressStepRunnerTests - { - [TestInitialize] - public void TestInitialize() - { - ThreadHelper.SetCurrentThreadAsUIThread(); - } - - [TestCleanup] - public void TestCleanup() - { - ProgressStepRunner.Reset(); - } - - [TestMethod] - public void ProgressStepRunner_OnFinished() - { - // Arrange - ConfigurableProgressEvents progressEvents = new ConfigurableProgressEvents(); - ProgressControllerResult? result = null; - Action action = (r) => result = r; - - foreach (ProgressControllerResult progressResult in Enum.GetValues(typeof(ProgressControllerResult))) - { - result = null; - progressEvents.RunOnFinished(action); - - // Act - progressEvents.SimulateFinished(progressResult); - - // Assert - result.Should().Be(progressResult, "Action was not called"); - progressEvents.AssertNoFinishedEventHandlers(); - } - } - - [TestMethod] - public void ProgressStepRunner_Observe() - { - // Arrange - ConfigurableProgressController controller = new ConfigurableProgressController(); - ConfigurableProgressControlHost host = new ConfigurableProgressControlHost(); - controller.AddSteps(new ConfigurableProgressStep());// Needs at least one - - // Act - using (ProgressObserver observer1 = ProgressStepRunner.Observe(controller, host)) - { - // Assert - observer1.Should().NotBeNull("Unexpected return value"); - ProgressStepRunner.ObservedControllers[controller].Should().Be(observer1); - host.ProgressControl.Should().NotBeNull(); - } - } - - [TestMethod] - public void ProgressStepRunner_ChangeHost() - { - // Arrange - ConfigurableProgressController controller = new ConfigurableProgressController(); - controller.AddSteps(new ConfigurableProgressStep());// Needs at least one - ConfigurableProgressControlHost host1 = new ConfigurableProgressControlHost(); - ProgressObserver observer = ProgressStepRunner.Observe(controller, host1); - - // Act - ConfigurableProgressControlHost host2 = new ConfigurableProgressControlHost(); - ProgressStepRunner.ChangeHost(host2); - - // Assert - using (var newObserver = ProgressStepRunner.ObservedControllers[controller]) - { - newObserver.Should().NotBeNull(); - observer.Should().NotBe(newObserver); - newObserver.State.Should().Be(observer.State, "State was not transferred"); - host2.ProgressControl.Should().NotBeNull(); - } - } - - [TestMethod] - public void ProgressStepRunner_AbortAll() - { - // Arrange - ConfigurableProgressController controller1 = new ConfigurableProgressController(); - controller1.AddSteps(new ConfigurableProgressStep());// Needs at least one - ConfigurableProgressControlHost host1 = new ConfigurableProgressControlHost(); - ProgressObserver observer1 = ProgressStepRunner.Observe(controller1, host1); - ConfigurableProgressController controller2 = new ConfigurableProgressController(); - controller2.AddSteps(new ConfigurableProgressStep());// Needs at least one - ConfigurableProgressControlHost host2 = new ConfigurableProgressControlHost(); - ProgressObserver observer2 = ProgressStepRunner.Observe(controller2, host2); - - // Act - ProgressStepRunner.AbortAll(); - - // Assert - controller1.NumberOfAbortRequests.Should().Be(1); - controller2.NumberOfAbortRequests.Should().Be(1); - - observer1.IsFinished.Should().BeTrue(); - observer2.IsFinished.Should().BeTrue(); - } - } -} diff --git a/src/Integration.TeamExplorer.UnitTests/ReferencesTests.cs b/src/Integration.TeamExplorer.UnitTests/ReferencesTests.cs index fbb5c4deb7..b20bd60680 100644 --- a/src/Integration.TeamExplorer.UnitTests/ReferencesTests.cs +++ b/src/Integration.TeamExplorer.UnitTests/ReferencesTests.cs @@ -18,11 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using System.Collections.Generic; -using System.Linq; using System.Reflection; -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; using SonarLint.VisualStudio.Integration.TeamExplorer; using SonarLint.VisualStudio.TestInfrastructure; @@ -31,19 +27,6 @@ namespace SonarLint.VisualStudio.Integration.UnitTests [TestClass] public class ReferencesTests { - [TestMethod] - public void MicrosoftTeamFoundationClient_EnsureCorrectVersion() - { - var expectedDllVersions = new Dictionary - { - { "VS2022", 16 } // 2022's dll is still 16 and not 17 - }; - - var tfClientAssemblyVersion = AssemblyHelper.GetVersionOfReferencedAssembly( - typeof(TeamExplorerController), "Microsoft.TeamFoundation.Client"); - - AssertIsCorrectMajorVersion(tfClientAssemblyVersion.Major, expectedDllVersions); - } [TestMethod] public void MicrosoftTeamFoundationControls_EnsureCorrectVersion() diff --git a/src/Integration.TeamExplorer.UnitTests/SectionControllerTests.cs b/src/Integration.TeamExplorer.UnitTests/SectionControllerTests.cs deleted file mode 100644 index c3bf99bcb8..0000000000 --- a/src/Integration.TeamExplorer.UnitTests/SectionControllerTests.cs +++ /dev/null @@ -1,498 +0,0 @@ -/* - * 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.ComponentModel.Design; -using FluentAssertions; -using Microsoft.TeamFoundation.Controls; -using Microsoft.VisualStudio.Shell; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using SonarLint.VisualStudio.ConnectedMode.Binding; -using SonarLint.VisualStudio.ConnectedMode.Shared; -using SonarLint.VisualStudio.Integration.Binding; -using SonarLint.VisualStudio.Integration.TeamExplorer; -using SonarLint.VisualStudio.Integration.WPF; -using SonarLint.VisualStudio.TestInfrastructure; -using SonarQube.Client; -using SonarQube.Client.Models; -using TF_IOleCommandTarget = Microsoft.TeamFoundation.Client.CommandTarget.IOleCommandTarget; -using TF_OLECMD = Microsoft.TeamFoundation.Client.CommandTarget.OLECMD; -using VS_IOleCommandTarget = Microsoft.VisualStudio.OLE.Interop.IOleCommandTarget; -using VS_OLECMD = Microsoft.VisualStudio.OLE.Interop.OLECMD; -using VS_OLEConstants = Microsoft.VisualStudio.OLE.Interop.Constants; - -namespace SonarLint.VisualStudio.Integration.UnitTests.TeamExplorer -{ - [TestClass] - public class SectionControllerTests - { - private ConfigurableServiceProvider serviceProvider; - private Mock sonarQubeServiceMock; - private ConfigurableHost host; - - [TestInitialize] - public void TestInitialize() - { - this.serviceProvider = new ConfigurableServiceProvider(assertOnUnexpectedServiceRequest: false); - - this.sonarQubeServiceMock = new Mock(); - this.host = new ConfigurableHost() - { - SonarQubeService = this.sonarQubeServiceMock.Object - }; - this.serviceProvider.RegisterService(typeof(IProjectSystemHelper), new ConfigurableVsProjectSystemHelper(this.serviceProvider)); - } - - #region Tests - - [TestMethod] - public void MefCtor_CheckIsExported() - { - // Note: we're supplying a ConfigurableHost instance because an empty Mock host won't work here - // - we'd get an exception when the SectionController is disposed - MefTestHelpers.CheckTypeCanBeImported( - MefTestHelpers.CreateExport(), - MefTestHelpers.CreateExport(new ConfigurableHost()), - MefTestHelpers.CreateExport(), - MefTestHelpers.CreateExport(), - MefTestHelpers.CreateExport(), - MefTestHelpers.CreateExport()); - } - - [TestMethod] - public void SectionController_Initialization() - { - // Act - SectionController testSubject = this.CreateTestSubject(); - - // Constructor time initialization - testSubject.ConnectCommand.Should().NotBeNull("ConnectCommand is not initialized"); - testSubject.BindCommand.Should().NotBeNull("BindCommand is not initialized"); - testSubject.BrowseToUrlCommand.Should().NotBeNull("BrowseToUrlCommand is not initialized"); - testSubject.BrowseToProjectDashboardCommand.Should().NotBeNull("BrowseToProjectDashboardCommand is not initialized"); - testSubject.DisconnectCommand.Should().NotBeNull("DisconnectCommand is not initialized"); - testSubject.RefreshCommand.Should().NotBeNull("RefreshCommand is not initialized"); - testSubject.ToggleShowAllProjectsCommand.Should().NotBeNull("ToggleShowAllProjectsCommand is not initialized"); - - // Case 1: first time initialization - // Assert - testSubject.View.Should().NotBeNull("Failed to get the View"); - ((ISectionController)testSubject).View.Should().NotBeNull("Failed to get the View as ConnectSectionView"); - testSubject.ViewModel.Should().NotBeNull("Failed to get the ViewModel"); - - // Case 2: re-initialization with connection - this.host.TestStateManager.IsConnected = true; - ReInitialize(testSubject, this.host); - - // Assert - AssertCommandsInSync(testSubject); - testSubject.View.Should().NotBeNull("Failed to get the View"); - testSubject.ViewModel.Should().NotBeNull("Failed to get the ViewModel"); - - // Case 3: re-initialization with no connection - this.host.TestStateManager.IsConnected = false; - ReInitialize(testSubject, this.host); - - // Assert - AssertCommandsInSync(testSubject); - testSubject.View.Should().NotBeNull("Failed to get the View"); - testSubject.ViewModel.Should().NotBeNull("Failed to get the ViewModel"); - - // Case 4: Dispose - testSubject.Dispose(); - - // Assert - testSubject.ConnectCommand.Should().BeNull("ConnectCommand is not cleared"); - testSubject.RefreshCommand.Should().BeNull("RefreshCommand is not cleared"); - testSubject.DisconnectCommand.Should().BeNull("DisconnectCommand is not cleared"); - testSubject.BindCommand.Should().BeNull("BindCommand is not ;"); - testSubject.ToggleShowAllProjectsCommand.Should().BeNull("ToggleShowAllProjectsCommand is not cleared"); - testSubject.BrowseToUrlCommand.Should().BeNull("BrowseToUrlCommand is not cleared"); - testSubject.BrowseToProjectDashboardCommand.Should().BeNull("BrowseToProjectDashboardCommand is not cleared"); - } - - [TestMethod] - public void SectionController_RespondsToIsBusyChanged() - { - // Arrange - SectionController testSubject = this.CreateTestSubject(); - ITeamExplorerSection viewModel = testSubject.ViewModel; - - // Act - this.host.TestStateManager.SetAndInvokeBusyChanged(true); - - // Assert - viewModel.IsBusy.Should().BeTrue(); - - // Act again (different value) - this.host.TestStateManager.SetAndInvokeBusyChanged(false); - - // Assert (should change) - viewModel.IsBusy.Should().BeFalse(); - - // Dispose - testSubject.Dispose(); - - // Act again(different value) - this.host.TestStateManager.SetAndInvokeBusyChanged(true); - - // Assert (should remain the same) - viewModel.IsBusy.Should().BeFalse(); - } - - [TestMethod] - public void SectionController_IOleCommandTargetQueryStatus() - { - // Arrange - var testSubject = this.CreateTestSubject(); - TF_IOleCommandTarget testSubjectCommandTarget = testSubject; - testSubject.CommandTargets.Clear(); - var command1 = new TestCommandTarget(); - var command2 = new TestCommandTarget(); - var command3 = new TestCommandTarget(); - testSubject.CommandTargets.Add(command1); - testSubject.CommandTargets.Add(command2); - testSubject.CommandTargets.Add(command3); - Guid group = Guid.Empty; - uint cCmds = 0; - var prgCmds = new TF_OLECMD[0]; - IntPtr pCmdText = IntPtr.Zero; - - // Case 1 : no commands handling the request - // Act+Verify - testSubjectCommandTarget.QueryStatus(ref group, cCmds, prgCmds, pCmdText).Should().Be(SectionController.CommandNotHandled); - command1.QueryStatusNumberOfCalls.Should().Be(1); - command2.QueryStatusNumberOfCalls.Should().Be(1); - command3.QueryStatusNumberOfCalls.Should().Be(1); - - // Case 2 : the last command is handling the request - command3.QueryStatusReturnsResult = (int)VS_OLEConstants.OLECMDERR_E_CANCELED; - // Act+Verify - testSubjectCommandTarget.QueryStatus(ref group, cCmds, prgCmds, pCmdText).Should().Be((int)VS_OLEConstants.OLECMDERR_E_CANCELED); - command1.QueryStatusNumberOfCalls.Should().Be(2); - command2.QueryStatusNumberOfCalls.Should().Be(2); - command3.QueryStatusNumberOfCalls.Should().Be(2); - - // Case 3 : the first command is handling the request - command1.QueryStatusReturnsResult = (int)VS_OLEConstants.OLECMDERR_E_DISABLED; - // Act+Verify - testSubjectCommandTarget.QueryStatus(ref group, cCmds, prgCmds, pCmdText).Should().Be((int)VS_OLEConstants.OLECMDERR_E_DISABLED); - command1.QueryStatusNumberOfCalls.Should().Be(3); - command2.QueryStatusNumberOfCalls.Should().Be(2); - command3.QueryStatusNumberOfCalls.Should().Be(2); - } - - [TestMethod] - public void SectionController_IOleCommandTargetQueryStatus_OLECMD_Conversion() - { - // Arrange - var testSubject = this.CreateTestSubject(); - TF_IOleCommandTarget testSubjectCommandTarget = testSubject; - testSubject.CommandTargets.Clear(); - var command1 = new TestCommandTarget(); - testSubject.CommandTargets.Add(command1); - Guid group = Guid.Empty; - uint cCmds = 0; - IntPtr pCmdText = IntPtr.Zero; - - // Case 1 : null input TF_OLECMD - // Act+Verify - testSubjectCommandTarget.QueryStatus(ref group, cCmds, null, pCmdText).Should().Be(SectionController.CommandNotHandled); - command1.QueryStatusNumberOfCalls.Should().Be(1); - command1.CommandArguments.Should().BeNull(); - - // Case 2 : multiple OLECMD values - var prgCmds = new TF_OLECMD[] - { - new TF_OLECMD { cmdf = 1, cmdID = 2 }, - new TF_OLECMD { cmdf = 3, cmdID = 4 }, - }; - - // Act+Verify - testSubjectCommandTarget.QueryStatus(ref group, cCmds, prgCmds, pCmdText).Should().Be(SectionController.CommandNotHandled); - command1.QueryStatusNumberOfCalls.Should().Be(2); - command1.CommandArguments.Should().NotBeNull(); - command1.CommandArguments.Length.Should().Be(2); - - command1.CommandArguments[0].cmdf.Should().Be(1); - command1.CommandArguments[0].cmdID.Should().Be(2); - command1.CommandArguments[1].cmdf.Should().Be(3); - command1.CommandArguments[1].cmdID.Should().Be(4); - } - - [TestMethod] - public void SectionController_IOleCommandTargetQueryStatus_OLE_Constants_SanityCheck() - { - // Sanity check that the TF and VS OLE constants are the same - Microsoft.TeamFoundation.Client.CommandTarget.OleConstants.OLECMDERR_E_UNKNOWNGROUP. - Should().Be((int)Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_UNKNOWNGROUP); - - Microsoft.TeamFoundation.Client.CommandTarget.OleConstants.OLECMDERR_E_DISABLED. - Should().Be((int)Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_DISABLED); - - - Microsoft.TeamFoundation.Client.CommandTarget.OleConstants.OLECMDERR_E_CANCELED. - Should().Be((int)Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_CANCELED); - } - - [TestMethod] - public void SectionController_DisconnectCommand() - { - // Arrange - var sectionController = this.CreateTestSubject(); - var connection = new ConnectionInformation(new Uri("http://connected")); - int setProjectsCalled = 0; - this.host.TestStateManager.SetProjectsAction = (conn, projects) => - { - setProjectsCalled++; - conn.Should().Be(connection); - projects.Should().BeNull("Expecting the project to be reset to null"); - }; - - // Case 1: No connection - // Act + Assert CanExecute - sectionController.DisconnectCommand.CanExecute(null).Should().BeFalse(); - setProjectsCalled.Should().Be(0); - - // Case 2: Connected - this.host.TestStateManager.ConnectedServers.Add(connection); - - // Act + Assert CanExecute - sectionController.DisconnectCommand.CanExecute(null).Should().BeTrue(); - setProjectsCalled.Should().Be(0); - - // Act + Assert Execute - sectionController.DisconnectCommand.Execute(null); - setProjectsCalled.Should().Be(1); - sonarQubeServiceMock.Verify(x => x.Disconnect(), Times.Once); - } - - [TestMethod] - public void SectionController_ReconnectCommand() - { - // Arrange - var sectionController = this.CreateTestSubject(); - var connection = new ConnectionInformation(new Uri("http://connected")); - int setProjectsCalled = 0; - this.host.TestStateManager.SetProjectsAction = (conn, projects) => - { - setProjectsCalled++; - conn.Should().Be(connection); - projects.Should().BeNull("Expecting the project to be reset to null"); - }; - - // The commands under test work only on fully loaded solution - var projectSystemHelper = (ConfigurableVsProjectSystemHelper)this.serviceProvider.GetService(); - projectSystemHelper.SetIsSolutionFullyOpened(true); - - // Case 1: No connection - // Act + Assert CanExecute - sectionController.DisconnectCommand.CanExecute(null).Should().BeFalse(); - sectionController.ReconnectCommand.CanExecute(null).Should().BeFalse(); // Same as DisconnectCommand - setProjectsCalled.Should().Be(0); - - // Case 2: Connected - this.host.TestStateManager.ConnectedServers.Add(connection); - - // Act + Assert CanExecute - sectionController.DisconnectCommand.CanExecute(null).Should().BeTrue(); - sectionController.ReconnectCommand.CanExecute(null).Should().BeTrue(); // Same as DisconnectCommand - setProjectsCalled.Should().Be(0); - - // Act + Assert Execute - // Reconnect command cannot be tested without significant refactoring - // because the executed code that shows UI cannot be mocked or replaced. - ////sectionController.ReconnectCommand.Execute(null); - ////setProjectsCalled.Should().Be(1); - ////sonarQubeServiceMock.Verify(x => x.Disconnect(), Times.Once); - } - - [TestMethod] - public void SectionController_ToggleShowAllProjectsCommand() - { - // Arrange - var testSubject = this.CreateTestSubject(); - var connInfo = new ConnectionInformation(new Uri("http://localhost")); - var projectInfo = new SonarQubeProject("p1", "proj1"); - var server = new ServerViewModel(connInfo); - var project = new ProjectViewModel(server, projectInfo); - server.Projects.Add(project); - - // Case 1: No bound projects - project.IsBound = false; - - // Act + Assert CanExecute - testSubject.ToggleShowAllProjectsCommand.CanExecute(server).Should().BeFalse(); - - // Case 2: Bound - project.IsBound = true; - - // Act + Assert - testSubject.ToggleShowAllProjectsCommand.CanExecute(server).Should().BeTrue(); - - // Assert execution - bool original = server.ShowAllProjects; - - // Act - testSubject.ToggleShowAllProjectsCommand.Execute(server); - - // Assert - server.ShowAllProjects.Should().Be(!original); - - // Act - testSubject.ToggleShowAllProjectsCommand.Execute(server); - - // Assert - server.ShowAllProjects.Should().Be(original); - } - - [TestMethod] - public void SectionController_BrowseToUrlCommand() - { - // Arrange - var webBrowser = new ConfigurableWebBrowser(); - var testSubject = this.CreateTestSubject(webBrowser); - - // Case 1: Empty URL - // Act + Assert CanExecute - testSubject.BrowseToUrlCommand.CanExecute(null).Should().BeFalse(); - - // Case 2: Bad URL - // Act + Assert CanExecute - testSubject.BrowseToUrlCommand.CanExecute("not a Uri").Should().BeFalse(); - - // Case 3: Good URL - const string goodUrl = "http://localhost"; - - // Act + Assert CanExecute - testSubject.BrowseToUrlCommand.CanExecute(goodUrl).Should().BeTrue(); - - // Act + Assert Execute - testSubject.BrowseToUrlCommand.Execute(goodUrl); - webBrowser.NavigatedUrls.Should().HaveCount(1); - webBrowser.NavigatedUrls.Should().Contain(goodUrl); - } - - [TestMethod] - public void SectionController_BrowseToProjectDashboardCommand() - { - // Arrange - var webBrowser = new ConfigurableWebBrowser(); - var testSubject = this.CreateTestSubject(webBrowser); - var serverUrl = new Uri("http://my-sonar-server:5555"); - var connectionInfo = new ConnectionInformation(serverUrl); - var projectInfo = new SonarQubeProject("p1", ""); - var expectedUrl = new Uri(serverUrl, "/foobar"); - this.sonarQubeServiceMock.Setup(x => x.GetProjectDashboardUrl("p1")) - .Returns(expectedUrl); - - // Case 1: Null parameter - // Act + Assert CanExecute - testSubject.BrowseToProjectDashboardCommand.CanExecute(null).Should().BeFalse(); - - // Case 2: Project VM is set but SQ server is not connected - var serverViewModel = new ServerViewModel(connectionInfo); - var projectViewModel = new ProjectViewModel(serverViewModel, projectInfo); - - this.sonarQubeServiceMock.Setup(x => x.IsConnected).Returns(false); - - // Act + Assert CanExecute - testSubject.BrowseToProjectDashboardCommand.CanExecute(projectViewModel).Should().BeFalse(); - - // Case 3: Project VM is set and SQ server is connected - this.sonarQubeServiceMock.Setup(x => x.IsConnected).Returns(true); - - // Act + Assert CanExecute - testSubject.BrowseToProjectDashboardCommand.CanExecute(projectViewModel).Should().BeTrue(); - - // Act + Assert Execute - testSubject.BrowseToProjectDashboardCommand.Execute(projectViewModel); - webBrowser.NavigatedUrls.Should().HaveCount(1); - webBrowser.NavigatedUrls.Should().Contain(expectedUrl.ToString()); - } - - #endregion Tests - - #region Helpers - - private static void ReInitialize(SectionController controller, IHost host) - { - host.ClearActiveSection(); - host.VisualStateManager.ManagedState.ConnectedServers.Clear(); - controller.Initialize(null, new Microsoft.TeamFoundation.Controls.SectionInitializeEventArgs(new ServiceContainer(), null)); - bool refreshCalled = false; - controller.RefreshCommand = new RelayCommand(c => refreshCalled = true); - controller.Refresh(); - refreshCalled.Should().BeTrue("Refresh command execution was expected"); - } - - private class TestCommandTarget : VS_IOleCommandTarget - { - public int QueryStatusNumberOfCalls { get; private set; } - - public VS_OLECMD[] CommandArguments { get; private set; } - - #region IOleCommandTarget - - int VS_IOleCommandTarget.Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) - { - throw new NotImplementedException(); - } - - int VS_IOleCommandTarget.QueryStatus(ref Guid pguidCmdGroup, uint cCmds, VS_OLECMD[] prgCmds, IntPtr pCmdText) - { - this.QueryStatusNumberOfCalls++; - this.CommandArguments = prgCmds; - return this.QueryStatusReturnsResult; - } - - #endregion IOleCommandTarget - - #region Test helpers - - public int QueryStatusReturnsResult - { - get; - set; - } = SectionController.CommandNotHandled; - - #endregion Test helpers - } - - private SectionController CreateTestSubject(IWebBrowser webBrowser = null) - { - var controller = new SectionController(serviceProvider, host, webBrowser ?? new ConfigurableWebBrowser(), Mock.Of(), Mock.Of(), Mock.Of()); - controller.Initialize(null, new SectionInitializeEventArgs(new ServiceContainer(), null)); - return controller; - } - - private void AssertCommandsInSync(SectionController section) - { - ConnectSectionViewModel viewModel = (ConnectSectionViewModel)section.ViewModel; - - viewModel.ConnectCommand.Should().Be(section.ConnectCommand, "ConnectCommand is not initialized"); - viewModel.BindCommand.Should().Be(section.BindCommand, "BindCommand is not initialized"); - viewModel.BrowseToUrlCommand.Should().Be(section.BrowseToUrlCommand, "BrowseToUrlCommand is not initialized"); - } - - #endregion Helpers - } -} diff --git a/src/Integration.TeamExplorer.UnitTests/app.config b/src/Integration.TeamExplorer.UnitTests/app.config index 093e6c7180..b6e58fb077 100644 --- a/src/Integration.TeamExplorer.UnitTests/app.config +++ b/src/Integration.TeamExplorer.UnitTests/app.config @@ -14,10 +14,6 @@ - - - - diff --git a/src/Integration.TeamExplorer/CommonStyles.xaml b/src/Integration.TeamExplorer/CommonStyles.xaml index 7d37f14761..d2117b5305 100644 --- a/src/Integration.TeamExplorer/CommonStyles.xaml +++ b/src/Integration.TeamExplorer/CommonStyles.xaml @@ -1,11 +1,8 @@  + xmlns:vsutilities="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Utilities"> @@ -17,210 +14,17 @@ - - - - - - - - - - - - - - - - - - - - - - - - F1 - M 1,6 L 1,15 15,15 15,6 z - M 2,6 L 2,4 14,4 14,6 - M 3,4 L 3,2 13,2 13,4 - F1 M 12.34,12.68 C 12.34,7.07 7.71,2.5 2,2.5 @@ -242,17 +46,6 @@ EndLineCap="Round"/> - - - - - - - - diff --git a/src/Integration.TeamExplorer/ConnectSectionView.xaml.cs b/src/Integration.TeamExplorer/ConnectSectionView.xaml.cs index 42e6de2921..2248021d9f 100644 --- a/src/Integration.TeamExplorer/ConnectSectionView.xaml.cs +++ b/src/Integration.TeamExplorer/ConnectSectionView.xaml.cs @@ -18,28 +18,16 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using System.Windows.Controls; -using SonarLint.VisualStudio.Integration.Progress; - namespace SonarLint.VisualStudio.Integration.TeamExplorer { /// /// Interaction logic for ConnectSectionView.xaml /// - public partial class ConnectSectionView : UserControl, IConnectSectionView, IProgressControlHost + public partial class ConnectSectionView { public ConnectSectionView() { InitializeComponent(); } - - void IProgressControlHost.Host(ProgressControl progressControl) - { - this.HostProgressControl(progressControl); - } - - protected virtual void HostProgressControl(ProgressControl control) - { - } } } diff --git a/src/Integration/TeamExplorer/IConnectSectionViewModel.cs b/src/Integration.TeamExplorer/ConnectSectionViewController.cs similarity index 50% rename from src/Integration/TeamExplorer/IConnectSectionViewModel.cs rename to src/Integration.TeamExplorer/ConnectSectionViewController.cs index cae04e5d58..f46ab1dcb8 100644 --- a/src/Integration/TeamExplorer/IConnectSectionViewModel.cs +++ b/src/Integration.TeamExplorer/ConnectSectionViewController.cs @@ -18,27 +18,28 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using SonarLint.VisualStudio.ConnectedMode.Binding; -using SonarLint.VisualStudio.Integration.State; -using SonarLint.VisualStudio.Integration.WPF; -using SonarLint.VisualStudio.Integration.Connection; +using System.Diagnostics.CodeAnalysis; +using Microsoft.TeamFoundation.Controls; +using Microsoft.TeamFoundation.Controls.WPF.TeamExplorer; -namespace SonarLint.VisualStudio.Integration.TeamExplorer -{ - /// - /// Representation of the connect section - /// - internal interface IConnectSectionViewModel - { - TransferableVisualState State { get; set; } - - bool IsBusy { get; set; } - - ICommand ConnectCommand { get; set; } +namespace SonarLint.VisualStudio.Integration.TeamExplorer; - ICommand BindCommand { get; set; } +// Exclude from coverage because it is only responsible in creating the View, +// and it's temporary until the Team Explorer components are fully removed +[ExcludeFromCodeCoverage] +[TeamExplorerSection(SectionId, SonarQubePage.PageId, Priority)] +internal class ConnectSectionViewController : TeamExplorerSectionBase +{ + private const string SectionId = "25AB05EF-8132-453E-A990-55587C0C5CD3"; + private const int Priority = 300; - ICommand BrowseToUrlCommand { get; set; } + protected override object CreateView(SectionInitializeEventArgs e) + { + return new ConnectSectionView(); + } + protected override ITeamExplorerSection CreateViewModel(SectionInitializeEventArgs e) + { + return new ConnectSectionViewModel(); } } diff --git a/src/Integration.TeamExplorer/ConnectSectionViewModel.cs b/src/Integration.TeamExplorer/ConnectSectionViewModel.cs index 782e67bbfa..cbed7572ca 100644 --- a/src/Integration.TeamExplorer/ConnectSectionViewModel.cs +++ b/src/Integration.TeamExplorer/ConnectSectionViewModel.cs @@ -18,77 +18,19 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using System; -using System.Windows.Input; -using Microsoft.TeamFoundation.Controls; using Microsoft.TeamFoundation.Controls.WPF.TeamExplorer; -using SonarLint.VisualStudio.ConnectedMode.Binding; -using SonarLint.VisualStudio.Integration.Connection; -using SonarLint.VisualStudio.Integration.State; -using SonarLint.VisualStudio.Integration.WPF; +using SonarLint.VisualStudio.Integration.Resources; namespace SonarLint.VisualStudio.Integration.TeamExplorer { - internal class ConnectSectionViewModel : TeamExplorerSectionViewModelBase, - IConnectSectionViewModel, - IUserNotification /* Most of it implemented by TeamExplorerSectionViewModelBase */ + internal class ConnectSectionViewModel : TeamExplorerSectionViewModelBase { - private TransferableVisualState state; - private ICommand connectCommand; - private ICommand bindCommand; - private ICommand browseToUrl; public ConnectSectionViewModel() { - this.Title = Resources.Strings.ConnectSectionTitle; + this.Title = Strings.ConnectSectionTitle; this.IsExpanded = true; this.IsVisible = true; } - - #region IUserNotification - - public void ShowNotificationError(string message, Guid notificationId, ICommand associatedCommand) - { - this.ShowNotification(message, NotificationType.Error, NotificationFlags.NoTooltips/*No need for them since we don't use hyperlinks*/, associatedCommand, notificationId); - } - - public void ShowNotificationWarning(string message, Guid notificationId, ICommand associatedCommand) - { - this.ShowNotification(message, NotificationType.Warning, NotificationFlags.NoTooltips/*No need for them since we don't use hyperlinks*/, associatedCommand, notificationId); - } - - #endregion - - #region Properties - - public TransferableVisualState State - { - get { return this.state; } - set { this.SetAndRaisePropertyChanged(ref this.state, value); } - } - - #endregion - - #region Commands - - public ICommand ConnectCommand - { - get { return this.connectCommand; } - set { SetAndRaisePropertyChanged(ref this.connectCommand, value); } - } - - public ICommand BindCommand - { - get { return this.bindCommand; } - set { SetAndRaisePropertyChanged(ref this.bindCommand, value); } - } - - public ICommand BrowseToUrlCommand - { - get { return this.browseToUrl; } - set { SetAndRaisePropertyChanged(ref this.browseToUrl, value); } - } - - #endregion } } diff --git a/src/Integration.TeamExplorer/SectionController.cs b/src/Integration.TeamExplorer/SectionController.cs deleted file mode 100644 index 71c3b53ce8..0000000000 --- a/src/Integration.TeamExplorer/SectionController.cs +++ /dev/null @@ -1,391 +0,0 @@ -/* - * 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.ComponentModel.Composition; -using System.Diagnostics; -using System.Linq; -using System.Windows.Input; -using Microsoft.TeamFoundation.Controls; -using Microsoft.TeamFoundation.Controls.WPF.TeamExplorer; -using Microsoft.VisualStudio.Shell; -using SonarLint.VisualStudio.ConnectedMode.Binding; -using SonarLint.VisualStudio.ConnectedMode.Shared; -using SonarLint.VisualStudio.Integration.Binding; -using SonarLint.VisualStudio.Integration.Connection; -using SonarLint.VisualStudio.Integration.Progress; -using SonarLint.VisualStudio.Integration.WPF; -using SonarQube.Client.Models; -using IVSOleCommandTarget = Microsoft.VisualStudio.OLE.Interop.IOleCommandTarget; -using TF_OLECMD = Microsoft.TeamFoundation.Client.CommandTarget.OLECMD; -using VS_OLECMD = Microsoft.VisualStudio.OLE.Interop.OLECMD; - -namespace SonarLint.VisualStudio.Integration.TeamExplorer -{ - /// - /// Controller for the SonarLint section in team explorer tool window - /// The class is responsible for view and view model creation also hosting the commands - /// relevant during the life time of the section (initialized when activated and disposed when navigated to a different section). - /// - [TeamExplorerSection(SectionController.SectionId, SonarQubePage.PageId, SectionController.Priority)] - internal class SectionController : TeamExplorerSectionBase, ISectionController - { - public const string SectionId = "25AB05EF-8132-453E-A990-55587C0C5CD3"; - public const int Priority = 300; - - internal const int CommandNotHandled = (int)Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_UNKNOWNGROUP; - - private readonly IServiceProvider serviceProvider; - private readonly IWebBrowser webBrowser; - private readonly IAutoBindTrigger autoBindTrigger; - private readonly ISharedBindingConfigProvider sharedBindingConfigProvider; - private readonly ICredentialStoreService credentialStoreService; - - [ImportingConstructor] - public SectionController( - [Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider, - IHost host, - IWebBrowser webBrowser, - IAutoBindTrigger autoBindTrigger, - ISharedBindingConfigProvider sharedBindingConfigProvider, - ICredentialStoreService credentialStoreService) - { - this.serviceProvider = serviceProvider; - this.Host = host; - this.webBrowser = webBrowser; - this.autoBindTrigger = autoBindTrigger; - this.sharedBindingConfigProvider = sharedBindingConfigProvider; - this.credentialStoreService = credentialStoreService; - } - - internal /*for testing purposes*/ List CommandTargets - { - get; - } = new List(); - - protected IHost Host - { - get; - } - - #region IConnectSection - - IProgressControlHost ISectionController.ProgressHost - { - get { return (IProgressControlHost)this.View; } - } - - IConnectSectionView ISectionController.View - { - get { return (ConnectSectionView)this.View; } - } - - [System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", - "S3215:\"interface\" instances should not be cast to concrete types", - Justification = "The base class is not defined by us, so we can't force the type to be something else", - Scope = "member", - Target = "~P:SonarLint.VisualStudio.Integration.TeamExplorer.SectionController.SonarLint#VisualStudio#Integration#TeamExplorer#IConnectSection#ViewModel")] - IConnectSectionViewModel ISectionController.ViewModel - { - get { return (ConnectSectionViewModel)this.ViewModel; } - } - - IUserNotification ISectionController.UserNotifications - { - get { return (IUserNotification)this.ViewModel; } - } - - #endregion - - #region TeamExplorerSectionBase overrides - - protected override object CreateView(SectionInitializeEventArgs e) - { - return new ConnectSectionView(); - } - - protected override ITeamExplorerSection CreateViewModel(SectionInitializeEventArgs e) - { - return new ConnectSectionViewModel(); - } - - public override void Initialize(object sender, SectionInitializeEventArgs e) - { - // Create the View & ViewModel - base.Initialize(sender, e); - - this.Host.VisualStateManager.IsBusyChanged += this.OnIsBusyChanged; - - this.InitializeControllerCommands(); - this.InitializeProvidedCommands(); - this.SyncCommands(); - - this.Host.SetActiveSection(this); - } - - public override void Dispose() - { - this.Host.ClearActiveSection(); - - this.Host.VisualStateManager.IsBusyChanged -= this.OnIsBusyChanged; - this.CleanControllerCommands(); - this.CleanProvidedCommands(); - this.SyncCommands(); - - // Dispose the View & ViewModel - base.Dispose(); - } - - public override void Refresh() - { - base.Refresh(); - - if (this.RefreshCommand.CanExecute(null)) - { - this.RefreshCommand.Execute(null); - } - } - - /// - /// Delegate QueryStatus to commands - /// - protected override int IOleCommandTargetQueryStatus(ref Guid pguidCmdGroup, uint cCmds, - TF_OLECMD[] prgCmds, IntPtr pCmdText) - { - var result = CommandNotHandled; - - var vsPrgCmds = GetVsPrgCmds(prgCmds); - - foreach (var commandTarget in this.CommandTargets) - { - result = commandTarget.QueryStatus(ref pguidCmdGroup, cCmds, vsPrgCmds, pCmdText); - - // If handed, stop the loop - if (result != CommandNotHandled) - { - break; - } - } - - return result; - } - - private void OnIsBusyChanged(object sender, bool isBusy) - { - ((ISectionController)this).ViewModel.IsBusy = isBusy; - } - - #endregion - - #region Commands - - public ICommand ConnectCommand - { - get; - private set; - } - - public ICommand BindCommand - { - get; - private set; - } - - public ICommand BrowseToUrlCommand - { - get; - private set; - } - - public ICommand BrowseToProjectDashboardCommand - { - get; - private set; - } - - public ICommand ReconnectCommand { get; private set; } - - public ICommand RefreshCommand - { - get; - internal /*for test purposes*/ set; - } - - public ICommand DisconnectCommand - { - get; - private set; - } - - public ICommand ToggleShowAllProjectsCommand - { - get; - private set; - } - - private void InitializeControllerCommands() - { - // Due to complexity of connect and bind we "outsource" the controlling part - // to separate controllers which just expose commands - var connectionController = new Connection.ConnectionController(serviceProvider, Host, autoBindTrigger, sharedBindingConfigProvider, credentialStoreService); - var bindingController = new Binding.BindingController(serviceProvider, Host); - - this.CommandTargets.Add(connectionController); - this.CommandTargets.Add(bindingController); - - this.ConnectCommand = connectionController.ConnectCommand; - this.RefreshCommand = connectionController.RefreshCommand; - this.BindCommand = bindingController.BindCommand; - } - - private void CleanControllerCommands() - { - this.CommandTargets.Clear(); - - this.ConnectCommand = null; - this.RefreshCommand = null; - this.BindCommand = null; - - ISectionController section = (ISectionController)this; - if (section.ViewModel != null) - { - section.ViewModel.ConnectCommand = null; - section.ViewModel.BindCommand = null; - section.ViewModel.BrowseToUrlCommand = null; - } - } - - private void InitializeProvidedCommands() - { - // Simple commands provided by this class directly - this.DisconnectCommand = new RelayCommand(this.Disconnect, this.CanDisconnect); - this.ToggleShowAllProjectsCommand = new RelayCommand(this.ToggleShowAllProjects, this.CanToggleShowAllProjects); - this.BrowseToUrlCommand = new RelayCommand(this.ExecBrowseToUrl, this.CanExecBrowseToUrl); - this.BrowseToProjectDashboardCommand = new RelayCommand(this.ExecBrowseToProjectDashboard, this.CanExecBrowseToProjectDashboard); - this.ReconnectCommand = new RelayCommand(() => - { - this.Disconnect(); - this.ConnectCommand.Execute(null); - }, - this.CanDisconnect); - } - - private void CleanProvidedCommands() - { - this.DisconnectCommand = null; - this.ToggleShowAllProjectsCommand = null; - this.BrowseToUrlCommand = null; - this.BrowseToProjectDashboardCommand = null; - } - - private void SyncCommands() - { - ISectionController section = (ISectionController)this; - if (section.ViewModel != null) - { - section.ViewModel.ConnectCommand = this.ConnectCommand; - section.ViewModel.BindCommand = this.BindCommand; - section.ViewModel.BrowseToUrlCommand = this.BrowseToUrlCommand; - } - } - - private bool CanDisconnect() - { - // We just support one at the moment, will need to pass in the server as an argument to this - // command once we will want to support more than once connected server - return this.Host.VisualStateManager.GetConnectedServers().Any(); - } - - private void Disconnect() - { - Debug.Assert(this.CanDisconnect()); - - // Disconnect all (all being one) - this.Host.VisualStateManager.GetConnectedServers().ToList().ForEach(c => this.Host.VisualStateManager.SetProjects(c, null)); - this.Host.SonarQubeService.Disconnect(); - } - - private bool CanToggleShowAllProjects(ServerViewModel server) - { - return server.Projects.Any(x => x.IsBound); - } - - private void ToggleShowAllProjects(ServerViewModel server) - { - server.ShowAllProjects = !server.ShowAllProjects; - } - - private bool CanExecBrowseToUrl(string url) - { - return Uri.IsWellFormedUriString(url, UriKind.Absolute); - } - - private void ExecBrowseToUrl(string url) - { - Debug.Assert(this.CanExecBrowseToUrl(url), "Should not be able to execute!"); - - this.webBrowser.NavigateTo(url); - } - - private bool CanExecBrowseToProjectDashboard(ProjectViewModel project) - { - if (project != null && this.Host.SonarQubeService.IsConnected) - { - var url = this.Host.SonarQubeService.GetProjectDashboardUrl(project.Project.Key); - return this.CanExecBrowseToUrl(url.ToString()); - } - - return false; - } - - private void ExecBrowseToProjectDashboard(ProjectViewModel project) - { - Debug.Assert(this.CanExecBrowseToProjectDashboard(project), $"Shouldn't be able to execute {nameof(this.BrowseToProjectDashboardCommand)}"); - - var url = this.Host.SonarQubeService.GetProjectDashboardUrl(project.Project.Key); - this.webBrowser.NavigateTo(url.ToString()); - } - - #endregion Commands - - #region VS IOleCommandTarget conversion - - /* - * The TeamFoundation client defines an IOleCommandTarget interface that is identical to the - * VS IOleComandTarget. We want to limit our references to the TF-specific assemblies, so we - * define our commands using the VS version. - * This means we need to convert from the TF OLECMD structure to the equivalent VS OLECMD - * when we are forwarding calls from this class to our VS OLE commands. - */ - - private static VS_OLECMD[] GetVsPrgCmds(TF_OLECMD[] prgCmds) => - prgCmds?.Select(ConvertTFtoVSOleCmd).ToArray(); - - private static VS_OLECMD ConvertTFtoVSOleCmd(TF_OLECMD cmd) => - new VS_OLECMD - { - cmdf = cmd.cmdf, - cmdID = cmd.cmdID - }; - - #endregion VS IOleCommandTarget conversion - } -} diff --git a/src/Integration.UnitTests/Binding/AutoBindTriggerTests.cs b/src/Integration.UnitTests/Binding/AutoBindTriggerTests.cs deleted file mode 100644 index 6e432b9e16..0000000000 --- a/src/Integration.UnitTests/Binding/AutoBindTriggerTests.cs +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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 Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using SonarLint.VisualStudio.ConnectedMode.Binding; -using SonarLint.VisualStudio.Integration.Binding; -using SonarLint.VisualStudio.Integration.TeamExplorer; -using SonarLint.VisualStudio.Integration.WPF; -using SonarLint.VisualStudio.Progress.Controller; -using SonarLint.VisualStudio.TestInfrastructure; -using SonarQube.Client.Models; - -namespace SonarLint.VisualStudio.Integration.UnitTests.Binding; - -[TestClass] -public class AutoBindTriggerTests -{ - private static readonly ConnectionInformation ConnectionInformation = new ConnectionInformation(new Uri("http://localhost")); - - [TestMethod] - public void MefCtor_CheckIsExported() - => MefTestHelpers.CheckTypeCanBeImported( - MefTestHelpers.CreateExport()); - - [TestMethod] - public void MefCtor_CheckIsSingleton() - => MefTestHelpers.CheckIsSingletonMefComponent(); - - [TestMethod] - public void TriggerAfterSuccessfulWorkflow_SchedulesAutobindOnFinish() - { - var testSubject = CreateTestSubject(); - var workflowProgressMock = new Mock(); - - testSubject.TriggerAfterSuccessfulWorkflow(workflowProgressMock.Object, null, null); - - workflowProgressMock.VerifyAdd(x => x.Finished += It.IsAny>(), Times.Once); - } - - [TestMethod] - public void AutobindIfPossible_FailedWorkflow_DoesNothing() - { - var (hostMock, bindCommandMock) = CreateHostMock(); - var testSubject = CreateTestSubject(hostMock.Object); - - testSubject.AutobindIfPossible(ProgressControllerResult.Failed, "project", ConnectionInformation); - - bindCommandMock.Verify(x => x.Execute(It.IsAny()), Times.Never); - } - - [TestMethod] - public void AutobindIfPossible_AutoBindNotRequested_DoesNothing() - { - var (hostMock, bindCommandMock) = CreateHostMock(); - var testSubject = CreateTestSubject(hostMock.Object); - - testSubject.AutobindIfPossible(ProgressControllerResult.Succeeded, null, ConnectionInformation); - - bindCommandMock.Verify(x => x.Execute(It.IsAny()), Times.Never); - } - - [TestMethod] - public void AutobindIfPossible_AutobindPossible_Binds() - { - var (hostMock, bindCommandMock) = CreateHostMock(); - var testSubject = CreateTestSubject(hostMock.Object); - - testSubject.AutobindIfPossible(ProgressControllerResult.Succeeded, "project", ConnectionInformation); - - bindCommandMock.Verify( - x => x.Execute(It.Is(args => - args.ProjectToBind.ServerProjectKey == "project" && args.ProjectToBind.ServerConnection.ServerUri == ConnectionInformation.ServerUri)), Times.Once); - } - - private static (Mock hostMock, Mock> bindCommandMock) CreateHostMock() - { - var hostMock = new Mock(); - var activeSectionMock = new Mock(); - var bindCommandMock = new Mock>(); - hostMock.SetupGet(x => x.ActiveSection).Returns(activeSectionMock.Object); - activeSectionMock.SetupGet(x => x.BindCommand).Returns(bindCommandMock.Object); - return (hostMock, bindCommandMock); - } - - private static AutoBindTrigger CreateTestSubject(IHost host = null) - { - host ??= Mock.Of(); - return new AutoBindTrigger(host); - } -} diff --git a/src/Integration.UnitTests/Binding/BindingControllerTests.cs b/src/Integration.UnitTests/Binding/BindingControllerTests.cs deleted file mode 100644 index d33a3f5aae..0000000000 --- a/src/Integration.UnitTests/Binding/BindingControllerTests.cs +++ /dev/null @@ -1,542 +0,0 @@ -/* - * 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. - */ -// todo remove https://sonarsource.atlassian.net/browse/SLVS-1408 -// using System; -// using System.Linq; -// using FluentAssertions; -// using Microsoft.VisualStudio.ComponentModelHost; -// using Microsoft.VisualStudio.OLE.Interop; -// using Microsoft.VisualStudio.Shell.Interop; -// using Microsoft.VisualStudio.TestTools.UnitTesting; -// using Moq; -// using SonarLint.VisualStudio.ConnectedMode.Binding; -// using SonarLint.VisualStudio.Core; -// using SonarLint.VisualStudio.Core.Binding; -// using SonarLint.VisualStudio.Integration.Binding; -// using SonarLint.VisualStudio.Integration.Resources; -// using SonarLint.VisualStudio.Integration.TeamExplorer; -// using SonarLint.VisualStudio.Integration.WPF; -// using SonarLint.VisualStudio.Progress.Controller; -// using SonarLint.VisualStudio.TestInfrastructure; -// using SonarQube.Client; -// using SonarQube.Client.Models; -// -// namespace SonarLint.VisualStudio.Integration.UnitTests.Binding -// { -// [TestClass] -// public class BindingControllerTests -// { -// private ConfigurableHost host; -// private Mock sonarQubeService; -// private ConfigurableVsProjectSystemHelper projectSystemHelper; -// private TestBindingWorkflow workflow; -// private ConfigurableServiceProvider serviceProvider; -// private SolutionMock solutionMock; -// private Mock knownUIContexts; -// private DTEMock dteMock; -// private ConfigurableConfigurationProvider configProvider; -// private Mock folderWorkspaceServiceMock; -// private TestLogger logger; -// -// private static readonly BoundServerProject ValidProject = new BoundServerProject("solution", "projectKey", new ServerConnection.SonarQube(new Uri("http://any"))); -// private static readonly BindCommandArgs ValidBindingArgs = new BindCommandArgs(ValidProject); -// -// [TestInitialize] -// public void TestInitialize() -// { -// sonarQubeService = new Mock(); -// workflow = new TestBindingWorkflow(); -// serviceProvider = new ConfigurableServiceProvider(); -// dteMock = new DTEMock(); -// serviceProvider.RegisterService(typeof(SDTE), dteMock); -// solutionMock = new SolutionMock(); -// knownUIContexts = new Mock(); -// projectSystemHelper = new ConfigurableVsProjectSystemHelper(serviceProvider); -// configProvider = new ConfigurableConfigurationProvider(); -// -// logger = new TestLogger(); -// serviceProvider.RegisterService(typeof(ILogger), logger); -// -// folderWorkspaceServiceMock = new Mock(); -// var bindingProcessFactoryMock = new Mock(); -// -// var mefHost = ConfigurableComponentModel.CreateWithExports( -// MefTestHelpers.CreateExport(projectSystemHelper), -// MefTestHelpers.CreateExport(folderWorkspaceServiceMock.Object), -// MefTestHelpers.CreateExport(configProvider), -// MefTestHelpers.CreateExport(bindingProcessFactoryMock.Object)); -// -// serviceProvider.RegisterService(typeof(SComponentModel), mefHost); -// -// host = new ConfigurableHost() -// { -// SonarQubeService = sonarQubeService.Object, -// Logger = logger -// }; -// -// configProvider.FolderPathToReturn = "c:\\test"; -// } -// -// #region Tests -// -// [TestMethod] -// public void BindingController_Ctor() -// { -// // Arrange -// BindingController testSubject = CreateBindingController(); -// -// // Assert -// testSubject.BindCommand.Should().NotBeNull("The Bind command should never be null"); -// } -// -// [TestMethod] -// public void Ctor_NullArgs_ThrowsArgumentNullException() -// { -// Action act = () => new BindingController(null, Mock.Of()); -// act.Should().ThrowExactly().And.ParamName.Should().Be("serviceProvider"); -// -// act = () => new BindingController(Mock.Of(), null); -// act.Should().ThrowExactly().And.ParamName.Should().Be("host"); -// } -// -// [TestMethod] -// public void BindingController_BindCommand_Status() -// { -// // Arrange -// BindCommandArgs bindingArgs = CreateBindingArguments("key1", "name1", "http://localhost"); -// BindingController testSubject = PrepareCommandForExecution(); -// -// // Case 1: All the requirements are set -// // Act + Assert -// testSubject.BindCommand.CanExecute(bindingArgs).Should().BeTrue("All the requirement should be satisfied for the command to be enabled"); -// -// // Case 2: project is null -// // Act + Assert -// testSubject.BindCommand.CanExecute(null) -// .Should().BeFalse("Project is null"); -// -// // Case 3: No connection -// host.TestStateManager.IsConnected = false; -// // Act + Assert -// testSubject.BindCommand.CanExecute(bindingArgs) -// .Should().BeFalse("No connection"); -// -// // Case 4: busy -// host.TestStateManager.IsConnected = true; -// host.VisualStateManager.IsBusy = true; -// // Act + Assert -// testSubject.BindCommand.CanExecute(bindingArgs) -// .Should().BeFalse("Connecting"); -// } -// -// [TestMethod] -// [DataRow(false, false, false)] -// [DataRow(false, true, false)] -// [DataRow(true, false, false)] -// [DataRow(true, true, true)] -// public void BindingController_BindCommand_Status_UIContexts( -// bool slnExistsAndIsFullyLoaded, -// bool slnExistsAndNotBuildingAndNotDebugging, -// bool expected) -// { -// // Arrange -// BindCommandArgs bindArgs = CreateBindingArguments("proj1", "name1", "http://localhost:9000"); -// BindingController testSubject = PrepareCommandForExecution(); -// -// SetKnownUIContexts(slnExistsAndIsFullyLoaded, slnExistsAndNotBuildingAndNotDebugging); -// -// // Act + Assert -// testSubject.BindCommand.CanExecute(bindArgs).Should().Be(expected); -// } -// -// [TestMethod] -// public void BindingController_BindCommand_Status_NonManagedProject() -// { -// // Arrange -// BindCommandArgs bindArgs = CreateBindingArguments("proj1", "name1", "http://localhost:9000"); -// BindingController testSubject = PrepareCommandForExecution(); -// SetKnownUIContexts(true, true); -// -// // No managed projects -// projectSystemHelper.Projects = null; -// -// // Act + Assert -// testSubject.BindCommand.CanExecute(bindArgs).Should().BeFalse("No managed projects"); -// } -// -// [TestMethod] -// public void BindingController_BindCommand_Status_NoProjects() -// { -// // Arrange -// BindCommandArgs bindArgs = CreateBindingArguments("proj1", "name1", "http://localhost:9000"); -// BindingController testSubject = PrepareCommandForExecution(); -// SetKnownUIContexts(true, false); -// -// // No projects at all -// solutionMock.RemoveProject(solutionMock.Projects.Single()); -// -// // Act + Assert -// testSubject.BindCommand.CanExecute(bindArgs).Should().BeFalse("No projects"); -// } -// -// [TestMethod] -// public void BindingController_BindCommand_Execution() -// { -// // Arrange -// BindingController testSubject = PrepareCommandForExecution(); -// -// // Act -// BindCommandArgs bindingArgs1 = CreateBindingArguments("1", "name1", "http://localhost"); -// testSubject.BindCommand.Execute(bindingArgs1); -// -// // Assert -// workflow.BindingArgs.ProjectKey.Should().Be("1"); -// workflow.BindingArgs.ProjectName.Should().Be("name1"); -// -// // Act, bind a different project -// BindCommandArgs bingingArgs2 = CreateBindingArguments("2", "name2", "http://localhost"); -// testSubject.BindCommand.Execute(bingingArgs2); -// -// // Assert -// workflow.BindingArgs.ProjectKey.Should().Be("2"); -// workflow.BindingArgs.ProjectName.Should().Be("name2"); -// } -// -// [TestMethod] -// public void BindingController_SetBindingInProgress() -// { -// // Arrange -// BindCommandArgs bindingArgs = CreateBindingArguments("key1", "name1", "http://localhost"); -// ConnectionInformation otherConnection = new ConnectionInformation(new Uri("http://otherConnection")); -// BindCommandArgs bindingInProgressArgs = new BindCommandArgs("another.key", "another.name", otherConnection); -// -// BindingController testSubject = PrepareCommandForExecution(); -// var progressEvents = new ConfigurableProgressEvents(); -// -// foreach (var controllerResult in (ProgressControllerResult[])Enum.GetValues(typeof(ProgressControllerResult))) -// { -// dteMock.ToolWindows.SolutionExplorer.Window.Active = false; -// -// // Sanity -// testSubject.BindCommand.CanExecute(bindingArgs).Should().BeTrue(); -// -// // Act - disable -// testSubject.SetBindingInProgress(progressEvents, bindingInProgressArgs); -// -// // Assert -// testSubject.BindCommand.CanExecute(bindingArgs).Should().BeFalse("Binding is in progress so should not be enabled"); -// -// // Act - finish -// progressEvents.SimulateFinished(controllerResult); -// -// // Assert -// testSubject.BindCommand.CanExecute(bindingArgs).Should().BeTrue("Binding is finished with result: {0}", controllerResult); -// if (controllerResult == ProgressControllerResult.Succeeded) -// { -// dteMock.ToolWindows.SolutionExplorer.Window.Active.Should().BeTrue("SolutionExplorer window supposed to be activated"); -// } -// else -// { -// dteMock.ToolWindows.SolutionExplorer.Window.Active.Should().BeFalse("SolutionExplorer window is not supposed to be activated"); -// } -// } -// } -// -// [TestMethod] -// public void BindingController_BindingFinished() -// { -// // Arrange -// var bindingArgs = new BindCommandArgs("key1", "name1", new ConnectionInformation(new Uri("http://localhost"))); -// -// BindingController testSubject = PrepareCommandForExecution(); -// var progressEvents = new ConfigurableProgressEvents(); -// -// foreach (ProgressControllerResult result in Enum.GetValues(typeof(ProgressControllerResult)).OfType()) -// { -// // Arrange -// testSubject.SetBindingInProgress(progressEvents, bindingArgs); -// testSubject.IsBindingInProgress.Should().BeTrue(); -// -// // Act -// progressEvents.SimulateFinished(result); -// -// // Assert -// testSubject.IsBindingInProgress.Should().BeFalse(); -// -// if (result == ProgressControllerResult.Succeeded) -// { -// host.TestStateManager.AssignedProjectKey.Should().Be("key1"); -// } -// else -// { -// host.TestStateManager.AssignedProjectKey.Should().BeNull(); -// } -// } -// } -// -// [TestMethod] -// public void BindingController_BindingFinished_Navigation() -// { -// // Arrange -// var bindingArgs = new BindCommandArgs("key2", "", new ConnectionInformation(new Uri("http://myUri"))); -// -// BindingController testSubject = PrepareCommandForExecution(); -// var progressEvents = new ConfigurableProgressEvents(); -// var teController = new ConfigurableTeamExplorerController(); -// -// var mefExports = MefTestHelpers.CreateExport(teController); -// var mefModel = ConfigurableComponentModel.CreateWithExports(mefExports); -// serviceProvider.RegisterService(typeof(SComponentModel), mefModel, replaceExisting: true); -// -// // Case 1: On non-successful binding no navigation will occur -// foreach (ProgressControllerResult nonSuccuess in new[] { ProgressControllerResult.Cancelled, ProgressControllerResult.Failed }) -// { -// // Act -// testSubject.SetBindingInProgress(progressEvents, bindingArgs); -// progressEvents.SimulateFinished(nonSuccuess); -// -// // Assert -// teController.ShowConnectionsPageCallsCount.Should().Be(0); -// dteMock.ToolWindows.SolutionExplorer.Window.Active.Should().BeFalse(); -// } -// -// // Case 2: Successful binding (should navigate to solution explorer) -// -// // Act -// testSubject.SetBindingInProgress(progressEvents, bindingArgs); -// progressEvents.SimulateFinished(ProgressControllerResult.Succeeded); -// -// // Assert -// teController.ShowConnectionsPageCallsCount.Should().Be(0); -// dteMock.ToolWindows.SolutionExplorer.Window.Active.Should().BeTrue(); -// } -// -// [TestMethod] -// public void BindingController_SetBindingInProgress_Notifications() -// { -// // Arrange -// var bindingArgs = new BindCommandArgs("key2", "", new ConnectionInformation(new Uri("http://myUri"))); -// -// BindingController testSubject = PrepareCommandForExecution(); -// var section = ConfigurableSectionController.CreateDefault(); -// host.SetActiveSection(section); -// var progressEvents = new ConfigurableProgressEvents(); -// host.ActiveSection.UserNotifications.ShowNotificationError("Need to make sure that this is clear once started", NotificationIds.FailedToBindId, new RelayCommand(() => { })); -// ConfigurableUserNotification userNotifications = (ConfigurableUserNotification)section.UserNotifications; -// -// foreach (ProgressControllerResult result in Enum.GetValues(typeof(ProgressControllerResult)).OfType()) -// { -// // Act - start -// testSubject.SetBindingInProgress(progressEvents, bindingArgs); -// -// // Assert -// userNotifications.AssertNoNotification(NotificationIds.FailedToBindId); -// -// // Act - finish -// progressEvents.SimulateFinished(result); -// -// // Assert -// if (result == ProgressControllerResult.Succeeded) -// { -// userNotifications.AssertNoNotification(NotificationIds.FailedToBindId); -// } -// else -// { -// userNotifications.AssertNotification(NotificationIds.FailedToBindId, Strings.FailedToToBindSolution); -// } -// } -// } -// -// [TestMethod] -// public void BindingController_BindCommand_OnQueryStatus() -// { -// // Arrange -// BindingController testSubject = CreateBindingController(); -// bool canExecuteChanged = false; -// testSubject.BindCommand.CanExecuteChanged += (o, e) => canExecuteChanged = true; -// -// // Act -// Guid notUsed = Guid.Empty; -// ((IOleCommandTarget)testSubject).QueryStatus(ref notUsed, 0, new OLECMD[0], IntPtr.Zero); -// -// // Assert -// canExecuteChanged.Should().BeTrue("The command needs to invalidate the previous CanExecute state using CanExecuteChanged event"); -// } -// -// [TestMethod] -// public void BindingController_InConnectedMode_IsFirstBindingIsFalse() -// { -// // Arrange -// configProvider.ModeToReturn = SonarLintMode.Connected; -// configProvider.ProjectToReturn = ValidProject; -// -// var bindingProcessFactory = new Mock(); -// var bindingProcess = Mock.Of(); -// bindingProcessFactory.Setup(x => x.Create(ValidBindingArgs)).Returns(bindingProcess); -// -// // Act -// var actual = BindingController.CreateBindingProcess(ValidBindingArgs, bindingProcessFactory.Object, logger); -// -// // Assert -// actual.Should().BeSameAs(bindingProcess); -// logger.AssertOutputStrings(Strings.Bind_UpdatingNewStyleBinding); -// } -// -// [TestMethod] -// public void CanExecute_SolutionNotLoaded_NotFolderWorkspace_False() -// { -// SetupSolutionState(isSolutionLoaded: false, isSolutionNotBuilding: true, isOpenAsFolder: false, hasProjects: true); -// -// var testSubject = CreateBindingController(); -// -// var canExecute = testSubject.BindCommand.CanExecute(CreateBindingArguments("project1", "name1", "http://localhost")); -// canExecute.Should().BeFalse(); -// } -// -// [TestMethod] -// public void CanExecute_SolutionIsBuilding_NotFolderWorkspace_False() -// { -// SetupSolutionState(isSolutionLoaded: true, isSolutionNotBuilding: false, isOpenAsFolder: false, hasProjects: true); -// -// var testSubject = CreateBindingController(); -// -// var canExecute = testSubject.BindCommand.CanExecute(CreateBindingArguments("project1", "name1", "http://localhost")); -// canExecute.Should().BeFalse(); -// } -// -// [TestMethod] -// public void CanExecute_NoProjects_NotFolderWorkspace_False() -// { -// SetupSolutionState(isSolutionLoaded: true, isSolutionNotBuilding: true, isOpenAsFolder: false, hasProjects: false); -// -// var testSubject = CreateBindingController(); -// -// var canExecute = testSubject.BindCommand.CanExecute(CreateBindingArguments("project1", "name1", "http://localhost")); -// canExecute.Should().BeFalse(); -// } -// -// [TestMethod] -// public void CanExecute_NoProjects_FolderWorkspace_True() -// { -// SetupSolutionState(isSolutionLoaded: false, isSolutionNotBuilding: true, isOpenAsFolder: true, hasProjects: false); -// -// var testSubject = CreateBindingController(); -// -// var canExecute = testSubject.BindCommand.CanExecute(CreateBindingArguments("project1", "name1", "http://localhost")); -// canExecute.Should().BeTrue(); -// } -// -// [TestMethod] -// public void CanExecute_SolutionNotLoaded_FolderWorkspace_True() -// { -// SetupSolutionState(isSolutionLoaded: false, isSolutionNotBuilding: true, isOpenAsFolder: true, hasProjects: true); -// -// var testSubject = CreateBindingController(); -// -// var canExecute = testSubject.BindCommand.CanExecute(CreateBindingArguments("project1", "name1", "http://localhost")); -// canExecute.Should().BeTrue(); -// } -// -// [TestMethod] -// public void CanExecute_SolutionIsBuilding_FolderWorkspace_True() -// { -// SetupSolutionState(isSolutionLoaded: true, isSolutionNotBuilding: false, isOpenAsFolder: true, hasProjects: true); -// -// var testSubject = CreateBindingController(); -// -// var canExecute = testSubject.BindCommand.CanExecute(CreateBindingArguments("project1", "name1", "http://localhost")); -// canExecute.Should().BeTrue(); -// } -// -// #endregion Tests -// -// #region Helpers -// -// private static BindCommandArgs CreateBindingArguments(string key, string name, string serverUri) -// { -// return new BindCommandArgs(key, name, new ConnectionInformation(new Uri(serverUri))); -// } -// -// private BindingController PrepareCommandForExecution() -// { -// SetupSolutionState(isSolutionLoaded: true, isSolutionNotBuilding: true, isOpenAsFolder: false, hasProjects: true); -// -// var testSubject = CreateBindingController(); -// -// // Sanity -// testSubject.BindCommand.CanExecute(CreateBindingArguments("project1", "name1", "http://localhost")).Should().BeTrue("All the requirement should be satisfied for the command to be enabled"); -// -// return testSubject; -// } -// -// private void SetupSolutionState(bool isSolutionLoaded, bool isSolutionNotBuilding, bool isOpenAsFolder, bool hasProjects) -// { -// host.TestStateManager.IsConnected = true; -// -// host.SetActiveSection(ConfigurableSectionController.CreateDefault()); -// -// SetKnownUIContexts(isSolutionLoaded, isSolutionNotBuilding); -// -// if (hasProjects) -// { -// var project1 = solutionMock.AddOrGetProject("project1"); -// projectSystemHelper.Projects = new[] { project1 }; -// } -// -// folderWorkspaceServiceMock.Setup(x => x.IsFolderWorkspace()).Returns(isOpenAsFolder); -// } -// -// private class TestBindingWorkflow : IBindingWorkflowExecutor -// { -// public BindCommandArgs BindingArgs { get; private set; } -// -// #region IBindingWorkflowExecutor. -// -// void IBindingWorkflowExecutor.BindProject(BindCommandArgs bindingArgs) -// { -// bindingArgs.Should().NotBeNull(); -// BindingArgs = bindingArgs; -// } -// -// #endregion IBindingWorkflowExecutor. -// } -// -// private BindingController CreateBindingController() -// { -// return new BindingController(serviceProvider, host, workflow, knownUIContexts.Object); -// } -// -// private void SetKnownUIContexts(bool slnExistsAndFullyLoaded_IsActive, bool slnExistsAndNotBuildingAndNotDebugging_IsActive) -// { -// knownUIContexts.Reset(); -// knownUIContexts.SetupGet(x => x.SolutionExistsAndFullyLoadedContext).Returns(CreateContext(slnExistsAndFullyLoaded_IsActive)); -// knownUIContexts.SetupGet(x => x.SolutionExistsAndNotBuildingAndNotDebuggingContext).Returns(CreateContext(slnExistsAndNotBuildingAndNotDebugging_IsActive)); -// } -// -// private static IUIContext CreateContext(bool isActive) -// { -// var context = new Mock(); -// context.Setup(x => x.IsActive).Returns(isActive); -// return context.Object; -// } -// -// #endregion Helpers -// } -// } diff --git a/src/Integration.UnitTests/Binding/BindingWorkflowTests.cs b/src/Integration.UnitTests/Binding/BindingWorkflowTests.cs deleted file mode 100644 index 24d290571b..0000000000 --- a/src/Integration.UnitTests/Binding/BindingWorkflowTests.cs +++ /dev/null @@ -1,171 +0,0 @@ -/* - * 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.Globalization; -using System.Threading; -using System.Threading.Tasks; -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using SonarLint.VisualStudio.ConnectedMode.Binding; -using SonarLint.VisualStudio.Integration.Binding; -using SonarLint.VisualStudio.Integration.Resources; -using SonarLint.VisualStudio.TestInfrastructure; - -namespace SonarLint.VisualStudio.Integration.UnitTests -{ - [TestClass] - public class BindingWorkflowTests - { - private ConfigurableHost host; - private Mock mockBindingProcess; - - private BindingWorkflow testSubject; - - [TestInitialize] - public void TestInitialize() - { - this.host = new ConfigurableHost(); - this.mockBindingProcess = new Mock(); - - this.testSubject = new BindingWorkflow(new ConfigurableServiceProvider(), host, mockBindingProcess.Object); - } - - #region Tests - - [TestMethod] - public void Ctor_ArgChecks() - { - var serviceProvider = Mock.Of(); - var host = Mock.Of(); - var bindingProcess = Mock.Of(); - - // 1. Null serviceProvider - Action act = () => new BindingWorkflow(null, host, bindingProcess); - act.Should().ThrowExactly().And.ParamName.Should().Be("serviceProvider"); - - // 2. Null host - act = () => new BindingWorkflow(serviceProvider, null, bindingProcess); - act.Should().ThrowExactly().And.ParamName.Should().Be("host"); - - // 3. Null binding process - act = () => new BindingWorkflow(serviceProvider, host, null); - act.Should().ThrowExactly().And.ParamName.Should().Be("bindingProcess"); - } - - [TestMethod] - public async Task BindingWorkflow_DownloadQualityProfile_Success() - { - // Arrange - ConfigurableProgressController controller = new ConfigurableProgressController(); - var notifications = new ConfigurableProgressStepExecutionEvents(); - - SetDownloadQPResult(true); - - // Act - await testSubject.DownloadQualityProfileAsync(controller, notifications, CancellationToken.None) - .ConfigureAwait(false); - - // Assert - controller.NumberOfAbortRequests.Should().Be(0); - } - - [TestMethod] - public async Task BindingWorkflow_DownloadQualityProfile_Fails_WorkflowAborted() - { - // Arrange - ConfigurableProgressController controller = new ConfigurableProgressController(); - var notifications = new ConfigurableProgressStepExecutionEvents(); - - SetDownloadQPResult(false); - - // Act - await testSubject.DownloadQualityProfileAsync(controller, notifications, CancellationToken.None) - .ConfigureAwait(false); - - // Assert - controller.NumberOfAbortRequests.Should().Be(1); - } - - private void SetDownloadQPResult(bool result) - { - mockBindingProcess.Setup(x => x.DownloadQualityProfileAsync(It.IsAny>(), - It.IsAny())).Returns(Task.FromResult(result)); - } - - [TestMethod] - public async Task BindingWorkflow_SaveServerExclusions_Success() - { - // Arrange - ConfigurableProgressController controller = new ConfigurableProgressController(); - var notifications = new ConfigurableProgressStepExecutionEvents(); - - SetSaveServerExclusionsResult(true); - - // Act - await testSubject.SaveServerExclusionsAsync(controller, notifications, CancellationToken.None) - .ConfigureAwait(false); - - // Assert - controller.NumberOfAbortRequests.Should().Be(0); - mockBindingProcess.Verify(bp => bp.SaveServerExclusionsAsync(CancellationToken.None), Times.Once); - } - - [TestMethod] - public async Task BindingWorkflow_SaveServerExclusions_Fails_WorkflowAborted() - { - // Arrange - ConfigurableProgressController controller = new ConfigurableProgressController(); - var notifications = new ConfigurableProgressStepExecutionEvents(); - - SetSaveServerExclusionsResult(false); - - // Act - await testSubject.SaveServerExclusionsAsync(controller, notifications, CancellationToken.None) - .ConfigureAwait(false); - - // Assert - controller.NumberOfAbortRequests.Should().Be(1); - mockBindingProcess.Verify(bp => bp.SaveServerExclusionsAsync(CancellationToken.None), Times.Once); - } - - private void SetSaveServerExclusionsResult(bool result) - { - mockBindingProcess.Setup(x => x.SaveServerExclusionsAsync( - It.IsAny())).Returns(Task.FromResult(result)); - } - - [TestMethod] - public void BindingWorkflow_EmitBindingCompleteMessage() - { - // Arrange - var notificationsOk = new ConfigurableProgressStepExecutionEvents(); - - // Act - testSubject.EmitBindingCompleteMessage(notificationsOk); - - // Assert - notificationsOk.AssertProgressMessages(string.Format(CultureInfo.CurrentCulture, Strings.FinishedSolutionBindingWorkflowSuccessful)); - } - - #endregion Tests - } -} diff --git a/src/Integration.UnitTests/Connection/BasicCredentialsValidatorTests.cs b/src/Integration.UnitTests/Connection/BasicCredentialsValidatorTests.cs deleted file mode 100644 index b981a42302..0000000000 --- a/src/Integration.UnitTests/Connection/BasicCredentialsValidatorTests.cs +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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.Linq; -using System.Security; -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SonarLint.VisualStudio.Integration.Connection; -using SonarQube.Client.Helpers; - -namespace SonarLint.VisualStudio.Integration.UnitTests.Connection -{ - [TestClass] - public class BasicAuthenticationCredentialsValidatorTests - { - private static readonly char[] InvalidCharactersSubset = new[] - { - ':', // colon - }; - - private static readonly char[] ValidCharacters = Enumerable.Range(1, 127) - .Select(i => (char)i) - .Except(InvalidCharactersSubset) - .ToArray(); - - [TestMethod] - public void BasicCredentialsValidator_IsUsernameValid() - { - // Arrange - var validator = new BasicAuthenticationCredentialsValidator(); - - // Valid characters only - VerifyUsername(validator, new string(ValidCharacters), expectedValid: true); - - // Invalid characters - foreach (char c in InvalidCharactersSubset) - { - VerifyUsername(validator, $"admin{c}bad", expectedValid: false); - } - } - - [TestMethod] - [Description("Verify that credentials are valid only if the username and password are both empty or both non-empty.")] - public void BasicCredentialsValidator_IsValid_UsernameAndPasswordRequiredCombinations() - { - // Arrange - var validator = new BasicAuthenticationCredentialsValidator(); - - // Valid - user name and password - VerifyUsernameAndPassword(validator, string.Empty, string.Empty, expectedValid: true); - VerifyUsernameAndPassword(validator, "admin", "letmein", expectedValid: true); - - // Valid - just a token, no password - VerifyUsernameAndPassword(validator, "eab759ea1ba04baa58dab0ac9e964f475a51ffe5", string.Empty, expectedValid: true); - VerifyUsernameAndPassword(validator, "user name or token - can't tell which", string.Empty, expectedValid: true); - - // Not valid - VerifyUsernameAndPassword(validator, string.Empty, "letmein", expectedValid: false); - } - - #region Helpers - - private void VerifyUsernameAndPassword(BasicAuthenticationCredentialsValidator validator, string username, string password, bool expectedValid) - { - SecureString securePassword = password.ToSecureString(); - - validator.Update(username, securePassword); - - bool result = validator.IsValid; - - if (expectedValid) - { - result.Should().BeTrue($"Username '{username}' and password '{password}' should be valid"); - } - else - { - result.Should().BeFalse($"Username '{username}' and password '{password}' should be invalid"); - } - } - - private void VerifyUsername(BasicAuthenticationCredentialsValidator validator, string username, bool expectedValid) - { - validator.UpdateUsername(username); - - bool result = validator.IsUsernameValid; - - if (expectedValid) - { - result.Should().BeTrue("Username '{0}' should be valid", username); - } - else - { - result.Should().BeFalse($"Username '{username}' should be invalid"); - } - } - - #endregion Helpers - } -} \ No newline at end of file diff --git a/src/Integration.UnitTests/Connection/ConnectControllerTests.cs b/src/Integration.UnitTests/Connection/ConnectControllerTests.cs deleted file mode 100644 index 8039a66cc4..0000000000 --- a/src/Integration.UnitTests/Connection/ConnectControllerTests.cs +++ /dev/null @@ -1,476 +0,0 @@ -/* - * 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.Globalization; -using FluentAssertions; -using Microsoft.Alm.Authentication; -using Microsoft.VisualStudio.ComponentModelHost; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using SonarLint.VisualStudio.ConnectedMode.Binding; -using SonarLint.VisualStudio.ConnectedMode.Shared; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Integration.Binding; -using SonarLint.VisualStudio.Integration.Connection; -using SonarLint.VisualStudio.Integration.Resources; -using SonarLint.VisualStudio.Progress.Controller; -using SonarLint.VisualStudio.TestInfrastructure; -using SonarQube.Client; -using SonarQube.Client.Helpers; -using SonarQube.Client.Models; - -namespace SonarLint.VisualStudio.Integration.UnitTests.Connection -{ - [TestClass] - public class ConnectControllerTests - { - private ConfigurableHost host; - private Mock sonarQubeServiceMock; - private ConfigurableConnectionInformationProvider connectionProvider; - private ConfigurableServiceProvider serviceProvider; - private ConfigurableSonarLintSettings settings; - private Mock solutionInfoProvider; - private TestLogger logger; - private Mock sharedBindingConfigProvider; - private Mock credentialsStore; - - [TestInitialize] - public void TestInit() - { - this.serviceProvider = new ConfigurableServiceProvider(); - - logger = new TestLogger(); - serviceProvider.RegisterService(typeof(ILogger), logger); - - this.sonarQubeServiceMock = new Mock(); - this.connectionProvider = new ConfigurableConnectionInformationProvider(); - this.settings = new ConfigurableSonarLintSettings(); - this.solutionInfoProvider = new Mock(); - this.host = new ConfigurableHost() - { - SonarQubeService = this.sonarQubeServiceMock.Object, - Logger = logger - }; - this.sharedBindingConfigProvider = new Mock(); - this.credentialsStore = new Mock(); - - IComponentModel componentModel = ConfigurableComponentModel.CreateWithExports( - new [] - { - MefTestHelpers.CreateExport(solutionInfoProvider.Object), - MefTestHelpers.CreateExport(settings) - }); - this.serviceProvider.RegisterService(typeof(SComponentModel), componentModel); - } - - #region Tests - - [TestMethod] - public void ConnectionController_Ctor_ArgumentChecks() - { - Exceptions.Expect(() => new ConnectionController(null, Mock.Of(), Mock.Of(), sharedBindingConfigProvider.Object, credentialsStore.Object)); - Exceptions.Expect(() => new ConnectionController(Mock.Of(), null, Mock.Of(), sharedBindingConfigProvider.Object, credentialsStore.Object)); - Exceptions.Expect(() => new ConnectionController(Mock.Of(), Mock.Of(), null, sharedBindingConfigProvider.Object, credentialsStore.Object)); - } - - [TestMethod] - public void ConnectionController_DefaultState() - { - // Arrange - var testSubject = CreateTestSubject(); - - // Assert - testSubject.ConnectCommand.Should().NotBeNull("Connected command should not be null"); - testSubject.RefreshCommand.Should().NotBeNull("Refresh command should not be null"); - testSubject.WorkflowExecutor.Should().NotBeNull("Need to be able to execute the workflow"); - testSubject.IsConnectionInProgress.Should().BeFalse("Connection is not in progress"); - } - - [TestMethod] - public void ConnectionController_ConnectCommand_SolutionFully_Open_Status() - { - // Arrange - var testSubject = CreateTestSubject(); - this.solutionInfoProvider.Setup(x => x.IsSolutionFullyOpened()).Returns(true); - - // Case 1: has connection, is busy - this.host.TestStateManager.IsConnected = true; - this.host.VisualStateManager.IsBusy = true; - - // Act + Assert - testSubject.ConnectCommand.CanExecute(null).Should().BeFalse("Connected already and busy"); - - // Case 2: has connection, not busy - this.host.TestStateManager.IsConnected = true; - this.host.VisualStateManager.IsBusy = false; - - // Act + Assert - testSubject.ConnectCommand.CanExecute(null).Should().BeFalse("Connected already"); - - // Case 3: no connection, is busy - this.host.TestStateManager.IsConnected = false; - this.host.VisualStateManager.IsBusy = true; - - // Act + Assert - testSubject.ConnectCommand.CanExecute(null).Should().BeFalse("Busy"); - - // Case 4: no connection, not busy - this.host.TestStateManager.IsConnected = false; - this.host.VisualStateManager.IsBusy = false; - - // Act + Assert - testSubject.ConnectCommand.CanExecute(null).Should().BeTrue("No connection and not busy"); - } - - [TestMethod] - public void ConnectionController_ConnectCommand_SolutionNotFully_Open_Status_AlwaysFalse() - { - // Arrange - var testSubject = CreateTestSubject(); - this.solutionInfoProvider.Setup(x => x.IsSolutionFullyOpened()).Returns(false); - - // Case 1: has connection, is busy - this.host.TestStateManager.IsConnected = true; - this.host.VisualStateManager.IsBusy = true; - - // Act + Assert - testSubject.ConnectCommand.CanExecute(null).Should().BeFalse("Solution not fully open"); - - // Case 2: has connection, not busy - this.host.TestStateManager.IsConnected = true; - this.host.VisualStateManager.IsBusy = false; - - // Act + Assert - testSubject.ConnectCommand.CanExecute(null).Should().BeFalse("Solution not fully open"); - - // Case 3: no connection, is busy - this.host.TestStateManager.IsConnected = false; - this.host.VisualStateManager.IsBusy = true; - - // Act + Assert - testSubject.ConnectCommand.CanExecute(null).Should().BeFalse("Busy"); - - // Case 4: no connection, not busy - this.host.TestStateManager.IsConnected = false; - this.host.VisualStateManager.IsBusy = false; - - // Act + Assert - testSubject.ConnectCommand.CanExecute(null).Should().BeFalse("Solution not fully open"); - } - - private static Mock CreateWorkflow() - { - var workflow = new Mock(); - return workflow; - } - - [TestMethod] - public void ConnectionController_ConnectCommand_Execution() - { - // Arrange - var connectionWorkflowMock = CreateWorkflow(); - connectionWorkflowMock.Setup(x => x.EstablishConnection(It.IsAny(), It.IsAny())); - ConnectionController testSubject = new ConnectionController(this.serviceProvider, this.host, null, - this.connectionProvider, connectionWorkflowMock.Object, sharedBindingConfigProvider.Object, credentialsStore.Object); - this.solutionInfoProvider.Setup(x => x.IsSolutionFullyOpened()).Returns(true); - - // Case 1: connection provider return null connection - this.connectionProvider.ConnectionInformationToReturn = null; - - // Sanity - testSubject.ConnectCommand.CanExecute(null).Should().BeTrue("Should be possible to execute"); - - // Sanity - testSubject.LastAttemptedConnection.Should().BeNull("No previous attempts to connect"); - - // Act - testSubject.ConnectCommand.Execute(null); - - // Assert - connectionWorkflowMock.Verify(x => x.EstablishConnection(It.IsAny(), It.IsAny()), Times.Never); - - // Case 2: connection provider returns a valid connection - var expectedConnection = new ConnectionInformation(new Uri("https://127.0.0.0")); - this.connectionProvider.ConnectionInformationToReturn = expectedConnection; - // Sanity - testSubject.LastAttemptedConnection.Should().BeNull("Previous attempt returned null"); - - // Act - testSubject.ConnectCommand.Execute(null); - - // Assert - connectionWorkflowMock.Verify(x => x.EstablishConnection(It.IsAny(), null), Times.Once); - // Case 3: existing connection, change to a different one - var existingConnection = expectedConnection; - this.host.TestStateManager.IsConnected = true; - - // Sanity - testSubject.LastAttemptedConnection.Should().Be(existingConnection, "Unexpected last attempted connection"); - - // Assert - testSubject.ConnectCommand.CanExecute(null).Should().BeFalse("Should not be able to connect if an existing connecting is present"); - } - - [TestMethod] - public void ConnectionController_ConnectCommand_SharedConfigAndCredentialsPresent_AutoBinds() - { - var connectionWorkflowMock = CreateWorkflow(); - SetupConnectionWorkflow(connectionWorkflowMock); - SetUpOpenSolution(); - var connectionProviderMock = new Mock(); - var testSubject = new ConnectionController(this.serviceProvider, this.host, null, connectionProviderMock.Object, - connectionWorkflowMock.Object, sharedBindingConfigProvider.Object, credentialsStore.Object); - var sharedBindingConfig = new SharedBindingConfigModel { ProjectKey = "projectKey", Uri = new Uri("https://sonarcloud.io"), Organization = "Org" }; - sharedBindingConfigProvider.Setup(mock => mock.GetSharedBinding()).Returns(sharedBindingConfig); - credentialsStore.Setup(mock => mock.ReadCredentials(It.IsAny())).Returns(new Credential("user", "pwd")); - - - testSubject.ConnectCommand.Execute(new ConnectConfiguration(){UseSharedBinding = true}); - - connectionWorkflowMock.Verify(x => - x.EstablishConnection(It.IsAny(), "projectKey"), - Times.Once); - connectionProviderMock.Verify(x => x.GetConnectionInformation(It.IsAny()), - Times.Never); - } - - [TestMethod] - public void ConnectionController_ConnectCommand_SharedConfig_AsksForCredentialsPresent_AutoBinds() - { - var connectionWorkflowMock = CreateWorkflow(); - SetupConnectionWorkflow(connectionWorkflowMock); - SetUpOpenSolution(); - var connectionProviderMock = new Mock(); - var testSubject = new ConnectionController(this.serviceProvider, this.host, null, connectionProviderMock.Object, - connectionWorkflowMock.Object, sharedBindingConfigProvider.Object, credentialsStore.Object); - var sharedBindingConfig = new SharedBindingConfigModel { ProjectKey = "projectKey", Uri = new Uri("https://sonarcloud.io"), Organization = "Org" }; - sharedBindingConfigProvider.Setup(mock => mock.GetSharedBinding()).Returns(sharedBindingConfig); - var connectionInformation = - new ConnectionInformation(sharedBindingConfig.Uri, "user", "pwd".ToSecureString()) - { - Organization = new SonarQubeOrganization(sharedBindingConfig.Organization, string.Empty) - }; - SetupConnectionProvider(connectionProviderMock, connectionInformation); - - testSubject.ConnectCommand.Execute(new ConnectConfiguration(){UseSharedBinding = true}); - - connectionWorkflowMock.Verify(x => - x.EstablishConnection(connectionInformation, "projectKey"), - Times.Once); - connectionProviderMock.Verify(x => - x.GetConnectionInformation(It.Is(c => - c.ServerUri == sharedBindingConfig.Uri && c.Organization.Key == sharedBindingConfig.Organization)), - Times.Once); - } - - [TestMethod] - public void ConnectionController_ConnectCommand_ConnectionConfigNotPresent_DoesNotAutoBind() - { - TestDisabledSharedConfig(null); - } - - [TestMethod] - public void ConnectionController_ConnectCommand_SharedConfigDisable_DoesNotAutoBind() - { - TestDisabledSharedConfig(new ConnectConfiguration() {UseSharedBinding = false}); - } - - private void TestDisabledSharedConfig(ConnectConfiguration config) - { - var connectionWorkflowMock = CreateWorkflow(); - SetupConnectionWorkflow(connectionWorkflowMock); - SetUpOpenSolution(); - var connectionProviderMock = new Mock(); - var sharedBindingConfig = new SharedBindingConfigModel { ProjectKey = "projectKey", Uri = new Uri("https://sonarcloud.io"), Organization = "Org" }; - sharedBindingConfigProvider.Setup(mock => mock.GetSharedBinding()).Returns(sharedBindingConfig); - credentialsStore.Setup(mock => mock.ReadCredentials(It.IsAny())).Returns(new Credential("user", "pwd")); - var expectedConnection = new ConnectionInformation(new Uri("https://127.0.0.0")); - SetupConnectionProvider(connectionProviderMock, expectedConnection); - - var testSubject = new ConnectionController(this.serviceProvider, this.host, null, - connectionProviderMock.Object, connectionWorkflowMock.Object, sharedBindingConfigProvider.Object, credentialsStore.Object); - - testSubject.ConnectCommand.Execute(config); - - connectionWorkflowMock.Verify(x => - x.EstablishConnection(It.IsAny(), null), - Times.Once); - connectionProviderMock.Verify(x => - x.GetConnectionInformation(It.IsAny()), - Times.Once); - } - - [TestMethod] - public void ConnectionController_ConnectCommand_SharedConfigNotPresentDoesNotAutoBind() - { - var connectionWorkflowMock = CreateWorkflow(); - SetupConnectionWorkflow(connectionWorkflowMock); - SetUpOpenSolution(); - var connectionProviderMock = new Mock(); - var expectedConnection = new ConnectionInformation(new Uri("https://127.0.0.0")); - SetupConnectionProvider(connectionProviderMock, expectedConnection); - - var testSubject = new ConnectionController(this.serviceProvider, this.host, null, - connectionProviderMock.Object, connectionWorkflowMock.Object, sharedBindingConfigProvider.Object, credentialsStore.Object); - - testSubject.ConnectCommand.Execute(new ConnectConfiguration() { UseSharedBinding = true }); - - connectionWorkflowMock.Verify(x => - x.EstablishConnection(It.IsAny(), null), - Times.Once); - connectionProviderMock.Verify(x => x.GetConnectionInformation(It.IsAny()), - Times.Once); - } - - [TestMethod] - public void ConnectionController_RefreshCommand_Status() - { - // Arrange - var testSubject = CreateTestSubject(); - var connection = new ConnectionInformation(new Uri("http://connection")); - - // Case 1: Has connection and busy - this.host.TestStateManager.IsConnected = true; - this.host.VisualStateManager.IsBusy = true; - - // Act + Assert - testSubject.RefreshCommand.CanExecute(null).Should().BeFalse("Busy"); - - // Case 2: no connection, not busy, no connection argument - this.host.TestStateManager.IsConnected = false; - this.host.VisualStateManager.IsBusy = false; - - // Act + Assert - testSubject.RefreshCommand.CanExecute(null).Should().BeFalse("Nothing to refresh"); - - // Case 3: no connection, is busy, has connection argument - this.host.VisualStateManager.IsBusy = true; - - // Act + Assert - testSubject.RefreshCommand.CanExecute(connection).Should().BeFalse("Busy"); - - // Case 4: no connection, not busy, has connection argument - this.host.VisualStateManager.IsBusy = false; - - // Act + Assert - testSubject.RefreshCommand.CanExecute(connection).Should().BeTrue("Has connection argument and not busy"); - - // Case 5: has connection, not busy, no connection argument - this.host.TestStateManager.IsConnected = true; - // Act + Assert - testSubject.RefreshCommand.CanExecute(null).Should().BeTrue("Has connection and not busy"); - - // Case 6: has connection, not busy, connection argument the same as the existing connection - // Act + Assert - testSubject.RefreshCommand.CanExecute(connection).Should().BeTrue("Has connection and not busy"); - } - - [TestMethod] - public void ConnectionController_RefreshCommand_Execution() - { - // Arrange - var connectionWorkflowMock = CreateWorkflow(); - ConnectionController testSubject = new ConnectionController(serviceProvider, host, null, connectionProvider, - connectionWorkflowMock.Object, sharedBindingConfigProvider.Object, credentialsStore.Object); - this.connectionProvider.ConnectionInformationToReturn = new ConnectionInformation(new Uri("http://notExpected")); - var connection = new ConnectionInformation(new Uri("http://Expected")); - // Sanity - testSubject.RefreshCommand.CanExecute(connection).Should().BeTrue("Should be possible to execute"); - - // Sanity - testSubject.LastAttemptedConnection.Should().BeNull("No previous attempts to connect"); - - // Act - testSubject.RefreshCommand.Execute(connection); - - // Assert - connectionWorkflowMock.Verify(x => x.EstablishConnection(It.IsAny(), null), Times.Once); - testSubject.LastAttemptedConnection.ServerUri.Should().Be(connection.ServerUri, "Unexpected last attempted connection"); - testSubject.LastAttemptedConnection.Should().NotBe(connection, "LastAttemptedConnection should be a clone"); - } - - [TestMethod] - public void ConnectionController_SetConnectionInProgress() - { - // Arrange - var connectionWorkflowMock = CreateWorkflow(); - ConnectionController testSubject = new ConnectionController(serviceProvider, host, null, connectionProvider, - connectionWorkflowMock.Object, sharedBindingConfigProvider.Object, credentialsStore.Object); - this.solutionInfoProvider.Setup(x => x.IsSolutionFullyOpened()).Returns(true); - this.connectionProvider.ConnectionInformationToReturn = null; - var progressEvents = new ConfigurableProgressEvents(); - var connectionInfo = new ConnectionInformation(new Uri("http://refreshConnection")); - - // Sanity - testSubject.ConnectCommand.CanExecute(null).Should().BeTrue(); - testSubject.RefreshCommand.CanExecute(connectionInfo).Should().BeTrue(); - - foreach (var controllerResult in (ProgressControllerResult[])Enum.GetValues(typeof(ProgressControllerResult))) - { - logger.Reset(); - - // Act - disable - testSubject.SetConnectionInProgress(progressEvents); - - // Assert - testSubject.ConnectCommand.CanExecute(null).Should().BeFalse("Connection is in progress so should not be enabled"); - testSubject.RefreshCommand.CanExecute(connectionInfo).Should().BeFalse("Connection is in progress so should not be enabled"); - logger.AssertOutputStrings(0); - - // Act - log progress - string message = controllerResult.ToString(); - progressEvents.SimulateStepExecutionChanged(message, double.NaN); - - // Assert prefix - logger.AssertOutputStrings(string.Format(CultureInfo.CurrentCulture, Strings.ConnectingToSonarQubePrefixMessageFormat, message)); - - // Act - finish - progressEvents.SimulateFinished(controllerResult); - - // Assert - testSubject.ConnectCommand.CanExecute(null).Should().BeTrue("Connection is finished with result: {0}", controllerResult); - testSubject.RefreshCommand.CanExecute(connectionInfo).Should().BeTrue("Connection is finished with result: {0}", controllerResult); - } - } - - #endregion Tests - private static void SetupConnectionWorkflow(Mock connectionWorkflowMock) - { - connectionWorkflowMock.Setup(x => - x.EstablishConnection(It.IsAny(), It.IsAny())); - } - - private void SetUpOpenSolution() - { - this.solutionInfoProvider.Setup(x => x.IsSolutionFullyOpened()).Returns(true); - } - - private static void SetupConnectionProvider(Mock connectionProviderMock, ConnectionInformation expectedConnection) - { - connectionProviderMock - .Setup(x => x.GetConnectionInformation(It.IsAny())) - .Returns(expectedConnection); - } - - private ConnectionController CreateTestSubject() => - new ConnectionController(serviceProvider, host, Mock.Of(), sharedBindingConfigProvider.Object, credentialsStore.Object); - } -} diff --git a/src/Integration.UnitTests/Connection/ConnectDialogViewModelTests.cs b/src/Integration.UnitTests/Connection/ConnectDialogViewModelTests.cs deleted file mode 100644 index 93e5f8ccb6..0000000000 --- a/src/Integration.UnitTests/Connection/ConnectDialogViewModelTests.cs +++ /dev/null @@ -1,138 +0,0 @@ -/* - * 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.ComponentModel; -using System.Linq; -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SonarLint.VisualStudio.Integration.Connection.UI; - -namespace SonarLint.VisualStudio.Integration.UnitTests.Connection -{ - [TestClass] - public class ConnectDialogViewModelTests - { - #region Tests - - [TestMethod] - public void ConnectDialogViewModel_GetErrorForProperty_ServerUrlRaw_NoValidationErrorWhenPristine() - { - // Test case 1: new model has pristine URL - // Arrange - var testSubject1 = new ConnectionInfoDialogViewModel(); - - // Act - string validationError1 = GetErrorForProperty(testSubject1, nameof(testSubject1.ServerUrlRaw)); - - // Assert - testSubject1.IsUrlPristine.Should().BeTrue("Server URL should be pristine on initial view model construction"); - validationError1.Should().BeNull("Validation error should be null on initial view model construction"); - - // Test case 2a: setting raw server url makes it 'dirty' valid URL - // Arrange - var testSubject2a = new ConnectionInfoDialogViewModel - { - - // Act - ServerUrlRaw = "http://localhost" - }; - string validationError2a = GetErrorForProperty(testSubject2a, nameof(testSubject2a.ServerUrlRaw)); - - // Assert - testSubject2a.IsUrlPristine.Should().BeFalse("Server URL should no longer be pristine once set to a valid URL"); - validationError2a.Should().BeNull("Validation error should be null for valid URL"); - - // Test case 2b: setting raw server url makes it 'dirty' invalid URL - // Arrange - var testSubject2b = new ConnectionInfoDialogViewModel - { - - // Act - ServerUrlRaw = "not-a-valid-url" - }; - string validationError2b = GetErrorForProperty(testSubject2b, nameof(testSubject2b.ServerUrlRaw)); - - // Assert - testSubject2b.IsUrlPristine.Should().BeFalse("Server URL should no longer be pristine once set to an invalid URL"); - validationError2b.Should().NotBeNull("Validation error should not be null for invalid URL"); - - // Test case 3: clearing a non-pristine view model should still be non-pristine - // Arrange - var testSubject3 = new ConnectionInfoDialogViewModel - { - ServerUrlRaw = "blah" // Makes url field dirty - }; - testSubject3.IsUrlPristine.Should().BeFalse("URL should be made dirty before clearing the field"); // Sanity check - - // Act - testSubject3.ServerUrlRaw = null; // Clear field - - // Assert - testSubject3.IsUrlPristine.Should().BeFalse("Server URL should still be non-pristine even after clearing the field"); - } - - [TestMethod] - public void ConnectDialogViewModel_ServerUrl_SetOnlyWhenServerUrlRawIsValid() - { - // Arrange -#pragma warning disable IDE0017 // Simplify object initialization - var model = new ConnectionInfoDialogViewModel(); -#pragma warning restore IDE0017 // Simplify object initialization - - // Test: - // Invalid entry does not set ServerUrl - model.ServerUrlRaw = "http:/localhost/"; - model.ServerUrl.Should().BeNull(); - - // Valid entry updates ServerUrl - model.ServerUrlRaw = "http://localhost/"; - model.ServerUrl.Should().NotBeNull(); - model.ServerUrl.Should().Be(new Uri(model.ServerUrlRaw), "Uri property should match raw string property"); - } - - [TestMethod] - public void ConnectDialogViewModel_ShowSecurityWarning_InsecureUriScheme_IsTrue() - { -#pragma warning disable IDE0017 // Simplify object initialization - var model = new ConnectionInfoDialogViewModel(); -#pragma warning restore IDE0017 // Simplify object initialization - - model.ServerUrlRaw = "http://hostname"; - model.ShowSecurityWarning.Should().BeTrue("Security warning should be visible"); - - model.ServerUrlRaw = "https://hostname"; - model.ShowSecurityWarning.Should().BeFalse("Security warning should not be visible"); - } - - #endregion Tests - - #region Helpers - - private static string GetErrorForProperty(ConnectionInfoDialogViewModel viewModel, string propertyName) - { - var errors = ((INotifyDataErrorInfo)viewModel).GetErrors(propertyName) as IEnumerable; - return errors?.FirstOrDefault(); - } - - #endregion Helpers - } -} diff --git a/src/Integration.UnitTests/Connection/ConnectionInformationDialogTests.cs b/src/Integration.UnitTests/Connection/ConnectionInformationDialogTests.cs deleted file mode 100644 index a9c841da87..0000000000 --- a/src/Integration.UnitTests/Connection/ConnectionInformationDialogTests.cs +++ /dev/null @@ -1,114 +0,0 @@ -/* - * 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.Security; -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SonarLint.VisualStudio.Integration.Connection; -using SonarLint.VisualStudio.Integration.Connection.UI; -using SonarQube.Client.Helpers; -using SonarQube.Client.Models; -using SonarLint.VisualStudio.TestInfrastructure; - -namespace SonarLint.VisualStudio.Integration.UnitTests.Connection -{ - [TestClass] - public class ConnectionInformationDialogTests - { - [TestMethod] - public void ConnectionInformationDialog_CreateConnectionInformation_NullArgumentChecks() - { - // Arrange - ConnectionInfoDialogViewModel viewModel = ConnectionInformationDialog.CreateViewModel(null); - - // Test 1: null viewModel - Exceptions.Expect(() => - { - ConnectionInformationDialog.CreateConnectionInformation(null, new SecureString()); - }); - - // Test 2: null password - Exceptions.Expect(() => - { - ConnectionInformationDialog.CreateConnectionInformation(viewModel, null); - }); - } - - [TestMethod] - public void ConnectionInformationDialog_CreateConnectionInformation_InvalidModel_ReturnsNull() - { - // Arrange - ConnectionInfoDialogViewModel viewModel = ConnectionInformationDialog.CreateViewModel(null); - viewModel.IsValid.Should().BeFalse("Empty view model should be invalid"); - var emptyPassword = new SecureString(); - - // Act - ConnectionInformation connInfo; - using (var assertIgnoreScope = new AssertIgnoreScope()) - { - connInfo = ConnectionInformationDialog.CreateConnectionInformation(viewModel, emptyPassword); - } - - // Assert - connInfo.Should().BeNull("No ConnectionInformation should be returned with an invalid model"); - } - - [TestMethod] - public void ConnectionInformationDialog_CreateConnectionInformation_ValidModel_ReturnsConnectionInformation() - { - // Arrange - var serverUrl = "https://localhost"; - var username = "admin"; - var inputPlaintextPassword = "letmein"; - var securePassword = inputPlaintextPassword.ToSecureString(); - - ConnectionInfoDialogViewModel viewModel = ConnectionInformationDialog.CreateViewModel(null); - viewModel.ServerUrlRaw = serverUrl; - viewModel.Username = username; - viewModel.ValidateCredentials(securePassword); - - // Act - ConnectionInformation connInfo = ConnectionInformationDialog.CreateConnectionInformation(viewModel, securePassword); - - // Assert - connInfo.Should().NotBeNull("ConnectionInformation should be returned"); - connInfo.ServerUri.Should().Be(new Uri(serverUrl), "Server URI returned was different"); - connInfo.UserName.Should().Be(username, "Username returned was different"); - - string outputPlaintextPassword = connInfo.Password.ToUnsecureString(); - outputPlaintextPassword.Should().Be(inputPlaintextPassword, "Password returned was different"); - } - - [TestMethod] - public void ConnectionInformationDialog_CreateConnectionInformation_WithExistingConnection() - { - // Arrange - var connectionInformation = new ConnectionInformation(new Uri("http://blablabla"), "admin", "P@ssword1".ToSecureString()); - - // Act - ConnectionInfoDialogViewModel viewModel = ConnectionInformationDialog.CreateViewModel(connectionInformation); - - // Assert - viewModel.ServerUrl.Should().Be(connectionInformation.ServerUri, "Unexpected ServerUrl"); - viewModel.Username.Should().Be(null, "Not expecting the user name to be populated"); - } - } -} diff --git a/src/Integration.UnitTests/Connection/ConnectionWorkflowTests.cs b/src/Integration.UnitTests/Connection/ConnectionWorkflowTests.cs deleted file mode 100644 index 7541ccb55e..0000000000 --- a/src/Integration.UnitTests/Connection/ConnectionWorkflowTests.cs +++ /dev/null @@ -1,516 +0,0 @@ -/* - * 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.Windows.Input; -using Microsoft.Alm.Authentication; -using Microsoft.VisualStudio.ComponentModelHost; -using Moq; -using SonarLint.VisualStudio.ConnectedMode.Binding; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Core.CFamily; -using SonarLint.VisualStudio.Integration.Binding; -using SonarLint.VisualStudio.Integration.Connection; -using SonarLint.VisualStudio.Integration.Resources; -using SonarLint.VisualStudio.Integration.TeamExplorer; -using SonarLint.VisualStudio.Integration.WPF; -using SonarLint.VisualStudio.TestInfrastructure; -using SonarQube.Client; -using SonarQube.Client.Helpers; -using SonarQube.Client.Models; - -namespace SonarLint.VisualStudio.Integration.UnitTests.Connection -{ - [TestClass] - public class ConnectionWorkflowTests - { - private ConfigurableServiceProvider serviceProvider; - private Mock sonarQubeServiceMock; - private ConfigurableHost host; - private ConfigurableSonarLintSettings settings; - private Mock credentialStoreMock; - private Mock folderWorkspaceService; - private TestLogger logger; - - [TestInitialize] - public void TestInit() - { - this.serviceProvider = new ConfigurableServiceProvider(); - this.sonarQubeServiceMock = new Mock(); - this.host = new ConfigurableHost(); - this.host.SetActiveSection(ConfigurableSectionController.CreateDefault()); - this.host.SonarQubeService = this.sonarQubeServiceMock.Object; - - this.settings = new ConfigurableSonarLintSettings(); - - folderWorkspaceService = new Mock(); - folderWorkspaceService.Setup(x => x.IsFolderWorkspace()).Returns(false); - - this.credentialStoreMock = new Mock(); - - var mefModel = ConfigurableComponentModel.CreateWithExports( - MefTestHelpers.CreateExport(folderWorkspaceService.Object), - MefTestHelpers.CreateExport(settings), - MefTestHelpers.CreateExport(credentialStoreMock.Object)); - - this.serviceProvider.RegisterService(typeof(SComponentModel), mefModel); - - logger = new TestLogger(); - host.Logger = logger; - serviceProvider.RegisterService(typeof(ILogger), logger); - } - - #region Tests - - [TestMethod] - public void ConnectionWorkflow_ConnectionStep_WhenGivenANullServiceProvider_ThrowsArgumentNullException() - { - // Arrange & Act - Action act = () => new ConnectionWorkflow(null, Mock.Of(), string.Empty, new RelayCommand(() => { })); - - act.Should().ThrowExactly().And.ParamName.Should().Be("serviceProvider"); - } - - [TestMethod] - public void ConnectionWorkflow_ConnectionStep_WhenGivenANullHost_ThrowsArgumentNullException() - { - // Arrange & Act - Action act = () => new ConnectionWorkflow(Mock.Of(), null, string.Empty, new RelayCommand(() => { })); - - act.Should().ThrowExactly().And.ParamName.Should().Be("host"); - } - - [TestMethod] - public void ConnectionWorkflow_ConnectionStep_WhenGivenANullParentCommand_ThrowsArgumentNullException() - { - // Arrange & Act - Action act = () => new ConnectionWorkflow(Mock.Of(), this.host, string.Empty, null); - - act.Should().ThrowExactly().And.ParamName.Should().Be("parentCommand"); - } - - [TestMethod] - public void ConnectionWorkflow_ConnectionStep_ConnectedServerCanBeGetAndSet() - { - // Arrange - var connectionInfo = new ConnectionInformation(new Uri("http://server")); - ConnectionWorkflow testSubject = CreateTestSubject(serviceProvider, host, new RelayCommand(() => { })); - - // Act - testSubject.ConnectedServer = connectionInfo; - - // Assert - connectionInfo.Should().Be(testSubject.ConnectedServer); - } - - [TestMethod] - public async Task ConnectionWorkflow_ConnectionStep_WhenNoProjectsOnServer_SuccessfulConnection() - { - // Arrange - var connectionInfo = new ConnectionInformation(new Uri("http://server"), "user", "pass".ToSecureString()); - var projects = Array.Empty(); - this.sonarQubeServiceMock.Setup(x => x.ConnectAsync(connectionInfo, It.IsAny())) - .Returns(Task.Delay(0)); - this.sonarQubeServiceMock.Setup(x => x.GetAllProjectsAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(projects); - bool projectChangedCallbackCalled = false; - this.host.TestStateManager.SetProjectsAction = (c, p) => - { - projectChangedCallbackCalled = true; - c.Should().Be(connectionInfo, "Unexpected connection"); - CollectionAssert.AreEqual(projects, p.ToArray(), "Unexpected projects"); - }; - - var controller = new ConfigurableProgressController(); - var executionEvents = new ConfigurableProgressStepExecutionEvents(); - string connectionMessage = connectionInfo.ServerUri.ToString(); - var testSubject = CreateTestSubject(serviceProvider, host, new RelayCommand(AssertIfCalled)); - - // Act - await testSubject.ConnectionStepAsync(connectionInfo, controller, executionEvents, CancellationToken.None); - - // Assert - controller.NumberOfAbortRequests.Should().Be(0); - AssertServiceDisconnectNotCalled(); - executionEvents.AssertProgressMessages( - connectionMessage, - Strings.ConnectionStepValidatinCredentials, - Strings.ConnectionStepRetrievingProjects, - Strings.ConnectionResultSuccess); - projectChangedCallbackCalled.Should().BeTrue("ConnectedProjectsCallaback was not called"); - this.sonarQubeServiceMock.Verify(x => x.ConnectAsync(It.IsAny(), - It.IsAny()), Times.Once()); - testSubject.ConnectedServer.Should().Be(connectionInfo); - ((ConfigurableUserNotification)this.host.ActiveSection.UserNotifications).AssertNoShowErrorMessages(); - ((ConfigurableUserNotification)this.host.ActiveSection.UserNotifications).AssertNoNotification(NotificationIds.FailedToConnectId); - - AssertCredentialsStored(connectionInfo); - } - - [TestMethod] - public async Task ConnectionWorkflow_ConnectionStep_Credentials_Invalid() - { - // Arrange - var connectionInfo = new ConnectionInformation(new Uri("http://server"), "user", "pass".ToSecureString()); - this.sonarQubeServiceMock.Setup(x => x.ConnectAsync(connectionInfo, It.IsAny())) - .Throws(new Exception()); - - var controller = new ConfigurableProgressController(); - var executionEvents = new ConfigurableProgressStepExecutionEvents(); - string connectionMessage = connectionInfo.ServerUri.ToString(); - var testSubject = CreateTestSubject(serviceProvider, host, new RelayCommand(AssertIfCalled)); - - // Act - Func act = async () => await testSubject.ConnectionStepAsync(connectionInfo, controller, executionEvents, CancellationToken.None); - await act.Should().ThrowExactlyAsync(); - - // Assert - controller.NumberOfAbortRequests.Should().Be(1); - AssertServiceDisconnectCalled(); - executionEvents.AssertProgressMessages( - connectionMessage, - Strings.ConnectionStepValidatinCredentials, - Strings.ConnectionResultFailure); - - this.sonarQubeServiceMock.Verify( - x => x.ConnectAsync(It.IsAny(), It.IsAny()), - Times.Once()); - - testSubject.ConnectedServer.Should().Be(null); - - ((ConfigurableUserNotification)this.host.ActiveSection.UserNotifications).AssertNoShowErrorMessages(); - ((ConfigurableUserNotification)this.host.ActiveSection.UserNotifications).AssertNotification(NotificationIds.FailedToConnectId); - - AssertCredentialsNotStored(); // Connection was rejected by SonarQube - } - - [TestMethod] - public async Task ConnectionWorkflow_ConnectionStep_WhenFolderWorkspace_SuccessfulConnection() - { - // Arrange - folderWorkspaceService.Setup(x => x.IsFolderWorkspace()).Returns(true); - - var connectionInfo = new ConnectionInformation(new Uri("http://server"), "user", "pass".ToSecureString()); - var projects = new List { new SonarQubeProject("project1", "") }; - this.sonarQubeServiceMock.Setup(x => x.ConnectAsync(connectionInfo, It.IsAny())) - .Returns(Task.Delay(0)); - this.sonarQubeServiceMock.Setup(x => x.GetAllProjectsAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(projects); - - var controller = new ConfigurableProgressController(); - var executionEvents = new ConfigurableProgressStepExecutionEvents(); - string connectionMessage = connectionInfo.ServerUri.ToString(); - var testSubject = CreateTestSubject(serviceProvider, host, new RelayCommand(AssertIfCalled)); - - // Act - await testSubject.ConnectionStepAsync(connectionInfo, controller, executionEvents, CancellationToken.None); - - // Assert - controller.NumberOfAbortRequests.Should().Be(0); - AssertServiceDisconnectNotCalled(); - executionEvents.AssertProgressMessages( - connectionMessage, - Strings.ConnectionStepValidatinCredentials, - Strings.ConnectionStepRetrievingProjects, - Strings.ConnectionResultSuccess); - - sonarQubeServiceMock.Verify(x=> x.GetAllPluginsAsync(CancellationToken.None), Times.Never); - - this.sonarQubeServiceMock.Verify(x => x.ConnectAsync(It.IsAny(), - It.IsAny()), Times.Once()); - testSubject.ConnectedServer.Should().Be(connectionInfo); - ((ConfigurableUserNotification)this.host.ActiveSection.UserNotifications).AssertNoShowErrorMessages(); - ((ConfigurableUserNotification)this.host.ActiveSection.UserNotifications).AssertNoNotification(NotificationIds.FailedToConnectId); - - AssertCredentialsStored(connectionInfo); - } - - [TestMethod] - public async Task ConnectionWorkflow_ConnectionStep_UnsuccessfulConnection() - { - // Arrange - this.sonarQubeServiceMock.Setup(x => x.GetAllProjectsAsync(It.IsAny(), It.IsAny())) - .Returns(() => { throw new Exception(); }); - var connectionInfo = new ConnectionInformation(new Uri("http://server")); - bool projectChangedCallbackCalled = false; - this.host.TestStateManager.SetProjectsAction = (c, p) => - { - projectChangedCallbackCalled = true; - c.Should().Be(connectionInfo, "Unexpected connection"); - p.Should().BeNull("Not expecting any projects"); - }; - var controller = new ConfigurableProgressController(); - var executionEvents = new ConfigurableProgressStepExecutionEvents(); - string connectionMessage = connectionInfo.ServerUri.ToString(); - var testSubject = CreateTestSubject(serviceProvider, host, new RelayCommand(AssertIfCalled)); - - // Act - Func act = async () => await testSubject.ConnectionStepAsync(connectionInfo, controller, executionEvents, CancellationToken.None); - await act.Should().ThrowExactlyAsync(); - - // Assert - executionEvents.AssertProgressMessages( - connectionMessage, - Strings.ConnectionStepValidatinCredentials, - Strings.ConnectionStepRetrievingProjects, - Strings.ConnectionResultFailure); - projectChangedCallbackCalled.Should().BeFalse("Callback should not have been called"); - this.sonarQubeServiceMock.Verify(x => x.ConnectAsync(It.IsAny(), - It.IsAny()), Times.Once()); - this.host.VisualStateManager.IsConnected.Should().BeFalse(); - ((ConfigurableUserNotification)this.host.ActiveSection.UserNotifications).AssertNotification(NotificationIds.FailedToConnectId, Strings.ConnectionFailed); - - // Act (reconnect with same bad connection) - executionEvents.Reset(); - projectChangedCallbackCalled = false; - Func act2 = async () => await testSubject.ConnectionStepAsync(connectionInfo, controller, executionEvents, CancellationToken.None); - await act2.Should().ThrowExactlyAsync(); - - // Assert - executionEvents.AssertProgressMessages( - connectionMessage, - Strings.ConnectionStepValidatinCredentials, - Strings.ConnectionStepRetrievingProjects, - Strings.ConnectionResultFailure); - projectChangedCallbackCalled.Should().BeFalse("Callback should not have been called"); - this.sonarQubeServiceMock.Verify(x => x.ConnectAsync(It.IsAny(), - It.IsAny()), Times.Exactly(2)); - this.host.VisualStateManager.IsConnected.Should().BeFalse(); - ((ConfigurableUserNotification)this.host.ActiveSection.UserNotifications).AssertNotification(NotificationIds.FailedToConnectId, Strings.ConnectionFailed); - - // Canceled connections - CancellationTokenSource tokenSource = new CancellationTokenSource(); - executionEvents.Reset(); - projectChangedCallbackCalled = false; - CancellationToken token = tokenSource.Token; - tokenSource.Cancel(); - - // Act - Func act3 = async () => await testSubject.ConnectionStepAsync(connectionInfo, controller, executionEvents, token); - await act3.Should().ThrowExactlyAsync(); - - // Assert - executionEvents.AssertProgressMessages( - connectionMessage, - Strings.ConnectionStepValidatinCredentials, - Strings.ConnectionStepRetrievingProjects, - Strings.ConnectionResultCancellation); - projectChangedCallbackCalled.Should().BeFalse("Callback should not have been called"); - this.sonarQubeServiceMock.Verify(x => x.ConnectAsync(It.IsAny(), - It.IsAny()), Times.Exactly(3)); - this.host.VisualStateManager.IsConnected.Should().BeFalse(); - ((ConfigurableUserNotification)this.host.ActiveSection.UserNotifications).AssertNotification(NotificationIds.FailedToConnectId, Strings.ConnectionFailed); - - AssertCredentialsNotStored(); // Username and password are null - } - - [TestMethod] - public async Task ConnectionWorkflow_ConnectionStep_10KProjectsAndKeyIsNotPartOfIt() - { - // Arrange - var connectionInfo = new ConnectionInformation(new Uri("http://server"), "user", "pass".ToSecureString()); - var projects = Enumerable.Range(1, 10000) - .Select(i => new SonarQubeProject($"project-{i}", $"Project {i}")) - .ToList(); - this.sonarQubeServiceMock.Setup(x => x.ConnectAsync(connectionInfo, It.IsAny())) - .Returns(Task.Delay(0)); - this.sonarQubeServiceMock.Setup(x => x.GetAllProjectsAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(projects); - bool projectChangedCallbackCalled = false; - - const string boundProjectKey = "whatever-key"; - const string boundProjectName = "whatever-name"; - - this.host.TestStateManager.SetProjectsAction = (c, p) => - { - projectChangedCallbackCalled = true; - c.Should().Be(connectionInfo, "Unexpected connection"); - var expectedList = new List(projects); - expectedList.Insert(0, new SonarQubeProject(boundProjectKey, boundProjectName)); - p.Should().BeEquivalentTo(expectedList); - }; - this.host.VisualStateManager.BoundProjectKey = boundProjectKey; - this.host.VisualStateManager.BoundProjectName = boundProjectName; - - var controller = new ConfigurableProgressController(); - var executionEvents = new ConfigurableProgressStepExecutionEvents(); - string connectionMessage = connectionInfo.ServerUri.ToString(); - var testSubject = CreateTestSubject(serviceProvider, host, new RelayCommand(AssertIfCalled)); - - // Act - await testSubject.ConnectionStepAsync(connectionInfo, controller, executionEvents, CancellationToken.None); - - // Assert - controller.NumberOfAbortRequests.Should().Be(0); - AssertServiceDisconnectNotCalled(); - executionEvents.AssertProgressMessages( - connectionMessage, - Strings.ConnectionStepValidatinCredentials, - Strings.ConnectionStepRetrievingProjects, - Strings.ConnectionResultSuccess); - projectChangedCallbackCalled.Should().BeTrue("ConnectedProjectsCallaback was not called"); - this.sonarQubeServiceMock.Verify(x => x.ConnectAsync(It.IsAny(), - It.IsAny()), Times.Once()); - testSubject.ConnectedServer.Should().Be(connectionInfo); - ((ConfigurableUserNotification)this.host.ActiveSection.UserNotifications).AssertNoShowErrorMessages(); - ((ConfigurableUserNotification)this.host.ActiveSection.UserNotifications).AssertNoNotification(NotificationIds.FailedToConnectId); - - AssertCredentialsStored(connectionInfo); - - logger.OutputStrings.Where(x => x.Contains("whatever-key")) - .Should().HaveCountGreaterOrEqualTo(1); - } - - [TestMethod] - public async Task ConnectionWorkflow_ConnectionStep_10KProjectsAndKeyIsPartOfIt() - { - // Arrange - var connectionInfo = new ConnectionInformation(new Uri("http://server"), "user", "pass".ToSecureString()); - var projects = Enumerable.Range(1, 10000) - .Select(i => new SonarQubeProject($"project-{i}", $"Project {i}")) - .ToList(); - this.sonarQubeServiceMock.Setup(x => x.ConnectAsync(connectionInfo, It.IsAny())) - .Returns(Task.Delay(0)); - this.sonarQubeServiceMock.Setup(x => x.GetAllProjectsAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(projects); - bool projectChangedCallbackCalled = false; - - this.host.TestStateManager.SetProjectsAction = (c, p) => - { - projectChangedCallbackCalled = true; - c.Should().Be(connectionInfo, "Unexpected connection"); - p.Should().Equal(projects); - }; - this.host.VisualStateManager.BoundProjectKey = projects[0].Key; - this.host.VisualStateManager.BoundProjectName = projects[0].Name; - - var controller = new ConfigurableProgressController(); - var executionEvents = new ConfigurableProgressStepExecutionEvents(); - string connectionMessage = connectionInfo.ServerUri.ToString(); - var testSubject = CreateTestSubject(serviceProvider, host, new RelayCommand(AssertIfCalled)); - - // Act - await testSubject.ConnectionStepAsync(connectionInfo, controller, executionEvents, CancellationToken.None); - - // Assert - controller.NumberOfAbortRequests.Should().Be(0); - AssertServiceDisconnectNotCalled(); - executionEvents.AssertProgressMessages( - connectionMessage, - Strings.ConnectionStepValidatinCredentials, - Strings.ConnectionStepRetrievingProjects, - Strings.ConnectionResultSuccess); - projectChangedCallbackCalled.Should().BeTrue("ConnectedProjectsCallaback was not called"); - this.sonarQubeServiceMock.Verify(x => x.ConnectAsync(It.IsAny(), - It.IsAny()), Times.Once()); - testSubject.ConnectedServer.Should().Be(connectionInfo); - ((ConfigurableUserNotification)this.host.ActiveSection.UserNotifications).AssertNoShowErrorMessages(); - ((ConfigurableUserNotification)this.host.ActiveSection.UserNotifications).AssertNoNotification(NotificationIds.FailedToConnectId); - - AssertCredentialsStored(connectionInfo); - } - - [TestMethod] - public async Task ConnectionWorkflow_ConnectionStep_AutoBindProjectNotFound_ShowError() - { - //this test describes a hacky way to fail an auto bind when the project is not found (by throwing), show a relevant error message, but not fail connection (by not aborting) - - // Arrange - folderWorkspaceService.Setup(x => x.IsFolderWorkspace()).Returns(true); - - var connectionInfo = new ConnectionInformation(new Uri("http://server"), "user", "pass".ToSecureString()); - var projects = new List { new SonarQubeProject("project1", "") }; - this.sonarQubeServiceMock.Setup(x => x.ConnectAsync(connectionInfo, It.IsAny())) - .Returns(Task.Delay(0)); - this.sonarQubeServiceMock.Setup(x => x.GetAllProjectsAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(projects); - - var controller = new ConfigurableProgressController(); - var executionEvents = new ConfigurableProgressStepExecutionEvents(); - string connectionMessage = connectionInfo.ServerUri.ToString(); - var testSubject = CreateTestSubject(serviceProvider, host, new RelayCommand(AssertIfCalled), "nonexistingproject"); - - // Act - Func act = async () => await testSubject.ConnectionStepAsync(connectionInfo, controller, executionEvents, CancellationToken.None); - await act.Should().ThrowExactlyAsync(); - - // Assert - controller.NumberOfAbortRequests.Should().Be(0); - AssertServiceDisconnectNotCalled(); - executionEvents.AssertProgressMessages( - connectionMessage, - Strings.ConnectionStepValidatinCredentials, - Strings.ConnectionStepRetrievingProjects, - Strings.ConnectionResultSuccess); - - this.sonarQubeServiceMock.Verify(x => x.ConnectAsync(It.IsAny(), - It.IsAny()), Times.Once()); - testSubject.ConnectedServer.Should().Be(connectionInfo); - AssertCredentialsStored(connectionInfo); - - ((ConfigurableUserNotification)this.host.ActiveSection.UserNotifications).AssertNoShowErrorMessages(); - ((ConfigurableUserNotification)this.host.ActiveSection.UserNotifications).AssertNoNotification(NotificationIds.FailedToConnectId); - ((ConfigurableUserNotification)this.host.ActiveSection.UserNotifications).AssertNotification(NotificationIds.FailedToFindBoundProjectKeyId); - } - - #endregion Tests - - #region Helpers - - private static void AssertIfCalled() - { - FluentAssertions.Execution.Execute.Assertion.FailWith("Command not expected to be called"); - } - - public void AssertServiceDisconnectCalled() - { - sonarQubeServiceMock.Verify(x => x.Disconnect(), Times.Once); - } - - public void AssertServiceDisconnectNotCalled() - { - sonarQubeServiceMock.Verify(x => x.Disconnect(), Times.Never); - } - - private void AssertCredentialsStored(ConnectionInformation connectionInfo) - { - this.credentialStoreMock.Verify( - x => x.WriteCredentials( - It.Is(uri => uri == connectionInfo.ServerUri), - It.Is(credential => - credential.Username == connectionInfo.UserName && - credential.Password == connectionInfo.Password.ToUnsecureString())), - Times.Once()); - } - - private void AssertCredentialsNotStored() - { - this.credentialStoreMock.Verify( - x => x.WriteCredentials(It.IsAny(), It.IsAny()), - Times.Never()); - } - - private static ConnectionWorkflow CreateTestSubject(IServiceProvider serviceProvider, IHost host, ICommand parentCommand, string autoBindProjectKey = null) - { - return new ConnectionWorkflow(serviceProvider, host, autoBindProjectKey, parentCommand, new NoOpThreadHandler()); - } - - #endregion Helpers - } -} diff --git a/src/Integration.UnitTests/Connection/UriValidatorTests.cs b/src/Integration.UnitTests/Connection/UriValidatorTests.cs deleted file mode 100644 index 0d7ed37ab9..0000000000 --- a/src/Integration.UnitTests/Connection/UriValidatorTests.cs +++ /dev/null @@ -1,314 +0,0 @@ -/* - * 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 FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SonarLint.VisualStudio.Integration.Connection; -using SonarLint.VisualStudio.TestInfrastructure; - -namespace SonarLint.VisualStudio.Integration.UnitTests.Connection -{ - [TestClass] - public class UriValidatorTests - { - private string secureScheme; - private string insecureScheme; - private string unsupportedScheme; - private ConfigurableUriValidator validator; - - [TestInitialize] - public void TestInitialize() - { - this.secureScheme = "safe"; - this.insecureScheme = "unsafe"; - this.unsupportedScheme = "unknown"; - - var supportedSchemes = new HashSet(new[] { this.secureScheme, this.insecureScheme }); - var insecureSchemes = new HashSet(new[] { this.insecureScheme }); - - this.validator = new ConfigurableUriValidator(supportedSchemes, insecureSchemes); - } - - [TestMethod] - public void UriValidator_Ctor_ArgumentNullException() - { - var emptySet = new HashSet(); - - Exceptions.Expect(() => new UriValidator(null)); - Exceptions.Expect(() => new UriValidator(null, emptySet)); - Exceptions.Expect(() => new UriValidator(emptySet, null)); - } - - [TestMethod] - public void UriValidator_IsSupportedScheme_LowercaseValidator_CaseInsensitive() - { - var lowercaseValidator = new UriValidator( - supportedSchemes: new HashSet(new[] { "case" }), - insecureSchemes: new HashSet(new[] { "case" }) - ); - - VerifyIsSupportedSchemeCaseSensitivity(lowercaseValidator); - } - - [TestMethod] - public void UriValidator_IsSupportedScheme_UppercaseValidator_CaseInsensitive() - { - var uppercaseValidator = new UriValidator( - supportedSchemes: new HashSet(new[] { "CASE" }), - insecureSchemes: new HashSet(new[] { "CASE" }) - ); - - VerifyIsSupportedSchemeCaseSensitivity(uppercaseValidator); - } - - [TestMethod] - public void UriValidator_IsSupportedScheme_MixedValidator_CaseInsensitive() - { - var mixedcaseValidator = new UriValidator( - supportedSchemes: new HashSet(new[] { "cAsE" }), - insecureSchemes: new HashSet(new[] { "cAsE" }) - ); - - VerifyIsSupportedSchemeCaseSensitivity(mixedcaseValidator); - } - - [TestMethod] - public void UriValidator_IsInsecureScheme_LowercaseValidator_CaseInsensitive() - { - var lowercaseValidator = new UriValidator( - supportedSchemes: new HashSet(new[] { "case" }), - insecureSchemes: new HashSet(new[] { "case" }) - ); - - VerifyIsInsecureSchemeCaseSensitivity(lowercaseValidator); - } - - [TestMethod] - public void UriValidator_IsInsecureScheme_UppercaseValidator_CaseInsensitive() - { - var uppercaseValidator = new UriValidator( - supportedSchemes: new HashSet(new[] { "CASE" }), - insecureSchemes: new HashSet(new[] { "CASE" }) - ); - - VerifyIsInsecureSchemeCaseSensitivity(uppercaseValidator); - } - - [TestMethod] - public void UriValidator_IsInsecureScheme_MixedcaseValidator_CaseInsensitive() - { - var mixedcaseValidator = new UriValidator( - supportedSchemes: new HashSet(new[] { "cAsE" }), - insecureSchemes: new HashSet(new[] { "cAsE" }) - ); - - VerifyIsInsecureSchemeCaseSensitivity(mixedcaseValidator); - } - - [TestMethod] - public void UriValidator_IsSupportedScheme_SupportedSchemes() - { - // Test - bool isSecureUriSupported = this.validator.IsSupportedScheme(this.SecureUri); - bool isInsecureUriSupported = this.validator.IsSupportedScheme(this.InsecureUri); - bool isInsupportedUriSupported = this.validator.IsSupportedScheme(this.UnsupportedUri); - - // Assert - isSecureUriSupported.Should().BeTrue(); - isInsecureUriSupported.Should().BeTrue(); - isInsupportedUriSupported.Should().BeFalse(); - } - - [TestMethod] - public void UriValidator_IsInsecureScheme_InsecureSchemesAreInsecure() - { - // Test - bool isSecureUriSecure = this.validator.IsInsecureScheme(this.SecureUri); - bool isInsecureUriSecure = this.validator.IsInsecureScheme(this.InsecureUri); - - // Assert - isSecureUriSecure.Should().BeFalse(); - isInsecureUriSecure.Should().BeTrue(); - } - - [TestMethod] - public void UriValidator_IsSecureScheme_UnsupportedScheme_ReturnsFalse() - { - // Test - bool isSecure = this.validator.IsInsecureScheme(this.UnsupportedUri); - - // Assert - isSecure.Should().BeFalse(); - } - - [TestMethod] - public void UriValidator_Ctor_InsecureSchemesIsSubsetOfSupportedSchemes() - { - // Arrange - var supportedSchemes = new HashSet(new[] { "a", "b" }); - var insecureSchemesSubset = new HashSet(new[] { "b" }); - var insecureSchemesNotSubset = new HashSet(new[] { "b", "c" }); - - // Test 1: is a subset - new UriValidator(supportedSchemes, insecureSchemesSubset); - - // Test 2: not a subset - Exceptions.Expect(() => - { - new UriValidator(supportedSchemes, insecureSchemesNotSubset); - }); - } - - [TestMethod] - public void UriValidator_IsValidUri_String_NullOrEmptyUri_IsInvalid() - { - // Arrange - string nullUriString = null; - string emptyUriString = string.Empty; - - // Test - bool nullResult = this.validator.IsValidUri(nullUriString); - bool emptyResult = this.validator.IsValidUri(emptyUriString); - - // Assert - nullResult.Should().BeFalse(); - emptyResult.Should().BeFalse(); - } - - [TestMethod] - public void UriValidator_IsValidUri_Uri_NullUri_ThrowsException() - { - // Arrange - Uri uri = null; - - // Test - Exceptions.Expect(() => this.validator.IsValidUri(uri)); - } - - [TestMethod] - public void UriValidator_IsValidUri_String_AbsoluteUrisOnly() - { - // Arrange - string relativeUriString = "/Home/Index"; - - // Test - bool result = this.validator.IsValidUri(relativeUriString); - - // Assert - result.Should().BeFalse(); - } - - [TestMethod] - public void UriValidator_IsValidUri_IncompleteUri_IsInvalid() - { - string uriString = "http:/"; - - bool result = this.validator.IsValidUri(uriString); - - result.Should().BeFalse(); - } - - [TestMethod] - public void UriValidator_IsValidUri_AmbiguousScheme_IsInvalid() - { - string uriString = "//localhost"; - - bool result = this.validator.IsValidUri(uriString); - - result.Should().BeFalse(); - } - - [TestMethod] - public void UriValidator_IsValidUri_UriWithPort_IsValid() - { - // Arrange - string uriString = CreateUriString(this.secureScheme, "localhost:9001"); - - // Test - bool result = this.validator.IsValidUri(uriString); - - // Assert - result.Should().BeTrue(); - } - - [TestMethod] - public void UriValidator_IsValidUri_LongUriWithFragments_IsValid() - { - // Arrange - string uriString = CreateUriString(this.secureScheme, "localhost:9001/this/is/a/longer/uri/that/should/be?valid=true"); - - // Test - bool result = this.validator.IsValidUri(uriString); - - // Assert - result.Should().BeTrue(); - } - - #region Helpers - - private Uri InsecureUri => CreateUri(this.insecureScheme, "localhost"); - - private Uri SecureUri => CreateUri(this.secureScheme, "localhost"); - - private Uri UnsupportedUri => CreateUri(this.unsupportedScheme, "localhost"); - - private static string CreateUriString(string scheme, string remainingUri) => $"{scheme}://{remainingUri}"; - - private static Uri CreateUri(string scheme, string remainingUri) => new Uri(CreateUriString(scheme, remainingUri)); - - private void VerifyIsInsecureSchemeCaseSensitivity(UriValidator validator) - { - Uri lowercaseUri = CreateUri("case", "localhost"); - Uri uppercaseUri = CreateUri("CASE", "localhost"); - Uri mixedcaseUri = CreateUri("cAsE", "localhost"); - - // Test - bool lowercaseInsecure = validator.IsInsecureScheme(lowercaseUri); - bool uppercaseInsecure = validator.IsInsecureScheme(uppercaseUri); - bool mixedcaseInsecure = validator.IsInsecureScheme(mixedcaseUri); - - // Assert - lowercaseInsecure.Should().BeTrue("Lowercase scheme should be insecure"); - uppercaseInsecure.Should().BeTrue("Uppercase scheme should be insecure"); - mixedcaseInsecure.Should().BeTrue("Mixed-case scheme should be insecure"); - } - - private void VerifyIsSupportedSchemeCaseSensitivity(UriValidator validator) - { - Uri lowercaseUri = CreateUri("case", "localhost"); - Uri uppercaseUri = CreateUri("CASE", "localhost"); - Uri mixedcaseUri = CreateUri("cAsE", "localhost"); - - // Test - bool lowercaseSupported = validator.IsSupportedScheme(lowercaseUri); - bool uppercaseSupported = validator.IsSupportedScheme(uppercaseUri); - bool mixedcaseSupported = validator.IsSupportedScheme(mixedcaseUri); - - // Assert - lowercaseSupported.Should().BeTrue("Lowercase scheme should be supported"); - uppercaseSupported.Should().BeTrue("Uppercase scheme should be supported"); - mixedcaseSupported.Should().BeTrue("Mixed-case scheme should be supported"); - } - - #endregion Helpers - } -} diff --git a/src/Integration.UnitTests/HostedCommandControllerBaseTests.cs b/src/Integration.UnitTests/HostedCommandControllerBaseTests.cs deleted file mode 100644 index f4d7f5ab20..0000000000 --- a/src/Integration.UnitTests/HostedCommandControllerBaseTests.cs +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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 FluentAssertions; -using Microsoft.VisualStudio.OLE.Interop; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SonarLint.VisualStudio.TestInfrastructure; - -namespace SonarLint.VisualStudio.Integration.UnitTests -{ - [TestClass] - public class HostedCommandControllerBaseTests - { - [TestMethod] - public void Ctor_InvalidArgument_Throws() - { - // Arrange - Action act = () => new TestHostedController(null); - - // Act & Assert - act.Should().Throw().And.ParamName.Should().Be("serviceProvider"); - } - - [TestMethod] - public void Ctor_ValidArgument_InitializedCorrectly() - { - // Arrange - var serviceProvider = new ConfigurableServiceProvider(); - - // Act - var testSubject = new TestHostedController(serviceProvider); - - // Assert - testSubject.ServiceProvider.Should().BeSameAs(serviceProvider); - } - - [TestMethod] - public void Ctor_QueryStatus() - { - // Arrange - var serviceProvider = new ConfigurableServiceProvider(); - var testSubject = (IOleCommandTarget)new TestHostedController(serviceProvider); - var guid = Guid.NewGuid(); - - // Act - var result = testSubject.QueryStatus(ref guid, 0, null, IntPtr.Zero); - - // Assert - result.Should().Be((int)Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_UNKNOWNGROUP); - } - - [TestMethod] - public void Ctor_QueryExec() - { - // Arrange - var serviceProvider = new ConfigurableServiceProvider(); - var testSubject = (IOleCommandTarget)new TestHostedController(serviceProvider); - var guid = Guid.NewGuid(); - - // Act - var result = testSubject.Exec(ref guid, 0, 0, IntPtr.Zero, IntPtr.Zero); - - // Assert - result.Should().Be((int)Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_UNKNOWNGROUP); - } - - internal class TestHostedController : HostedCommandControllerBase - { - public TestHostedController(System.IServiceProvider serviceProvider) - : base(serviceProvider) - { - } - } - } -} diff --git a/src/Integration.UnitTests/MefServices/ActiveSolutionBoundTrackerTests.cs b/src/Integration.UnitTests/MefServices/ActiveSolutionBoundTrackerTests.cs index 72f88d38f8..963d6c0891 100644 --- a/src/Integration.UnitTests/MefServices/ActiveSolutionBoundTrackerTests.cs +++ b/src/Integration.UnitTests/MefServices/ActiveSolutionBoundTrackerTests.cs @@ -18,19 +18,10 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using System; -using System.Collections.Generic; using System.Linq.Expressions; using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using System.Windows.Media.Animation; -using FluentAssertions; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.VisualStudio; -using Microsoft.VisualStudio.ComponentModelHost; using Microsoft.VisualStudio.Shell.Interop; -using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using SonarLint.VisualStudio.Core; using SonarLint.VisualStudio.Core.Binding; @@ -44,370 +35,267 @@ namespace SonarLint.VisualStudio.Integration.UnitTests [TestClass] public class ActiveSolutionBoundTrackerTests { - private uint boundSolutionUIContextCookie = 999; - + private readonly BoundServerProject boundSonarQubeProject = new("solution", "key", new ServerConnection.SonarQube(new Uri("http://localhost:9000"))); private readonly Expression> connectMethod = x => x.ConnectAsync(It.IsAny(), It.IsAny()); private readonly Expression> disconnectMethod = x => x.Disconnect(); private ConfigurableServiceProvider serviceProvider; - private SolutionMock solutionMock; + private ConfigurableConfigurationProvider configProvider; private ConfigurableActiveSolutionTracker activeSolutionTracker; - private ConfigurableHost host; + private SolutionMock solutionMock; private Mock vsMonitorMock; private Mock loggerMock; private Mock sonarQubeServiceMock; - private ConfigurableConfigurationProvider configProvider; + + private uint boundSolutionUiContextCookie = 999; private bool isMockServiceConnected; [TestInitialize] public void TestInitialize() { - serviceProvider = new ConfigurableServiceProvider(false); - - host = new ConfigurableHost(); - var mefHost = MefTestHelpers.CreateExport(host); - + configProvider = new ConfigurableConfigurationProvider(); activeSolutionTracker = new ConfigurableActiveSolutionTracker(); - var mefAST = MefTestHelpers.CreateExport(activeSolutionTracker); - - var mefModel = ConfigurableComponentModel.CreateWithExports(mefHost, mefAST); - serviceProvider.RegisterService(typeof(SComponentModel), mefModel, replaceExisting: true); - solutionMock = new SolutionMock(); - serviceProvider.RegisterService(typeof(SVsSolution), solutionMock); - - configProvider = new ConfigurableConfigurationProvider(); - loggerMock = new Mock(); - - vsMonitorMock = new Mock(); vsMonitorMock - .Setup(x => x.GetCmdUIContextCookie(ref BoundSolutionUIContext.Guid, out boundSolutionUIContextCookie)) + .Setup(x => x.GetCmdUIContextCookie(ref BoundSolutionUIContext.Guid, out boundSolutionUiContextCookie)) .Returns(VSConstants.S_OK); - + + serviceProvider = new ConfigurableServiceProvider(false); + serviceProvider.RegisterService(typeof(SVsSolution), solutionMock); serviceProvider.RegisterService(typeof(SVsShellMonitorSelection), vsMonitorMock.Object, replaceExisting: true); } [TestMethod] public void ActiveSolutionBoundTracker_Initialisation_Unbound() { - // Arrange - host.VisualStateManager.ClearBoundProject(); - - // Act - using (var testSubject = CreateTestSubject(this.host, this.activeSolutionTracker, this.configProvider, loggerMock.Object)) - { - // Assert - testSubject.CurrentConfiguration.Mode.Should().Be(SonarLintMode.Standalone, "Unbound solution should report false activation"); - } + using var testSubject = CreateTestSubject(activeSolutionTracker, configProvider, loggerMock.Object); + + testSubject.CurrentConfiguration.Mode.Should().Be(SonarLintMode.Standalone, "Unbound solution should report false activation"); } [TestMethod] public void ActiveSolutionBoundTracker_Initialisation_Bound() { - // Arrange - this.ConfigureSolutionBinding(new BoundServerProject("solution", "key", new ServerConnection.SonarQube(new Uri("http://localhost:9000")))); + ConfigureSolutionBinding(boundSonarQubeProject); - // Act - using (var testSubject = CreateTestSubject(this.host, this.activeSolutionTracker, this.configProvider, loggerMock.Object)) - { - // Assert - testSubject.CurrentConfiguration.Mode.Should().Be(SonarLintMode.Connected, "Bound solution should report true activation"); - } + using var testSubject = CreateTestSubject(activeSolutionTracker, configProvider, loggerMock.Object); + + testSubject.CurrentConfiguration.Mode.Should().Be(SonarLintMode.Connected, "Bound solution should report true activation"); } [TestMethod] public void ActiveSolutionBoundTracker_When_ConnectAsync_Throws_Write_To_Output() { + sonarQubeServiceMock = new Mock(); + // We want to directly jump to Connect + sonarQubeServiceMock.SetupGet(x => x.IsConnected).Returns(false); + ConfigureSolutionBinding(boundSonarQubeProject); + + // ConnectAsync should throw + sonarQubeServiceMock + .SetupSequence(x => x.ConnectAsync(It.IsAny(), It.IsAny())) + .Throws() + .Throws() + .Throws(new HttpRequestException("http request", new Exception("something happened"))); + // Arrange - var sonarQubeServiceMock = new Mock(); - this.host.SonarQubeService = sonarQubeServiceMock.Object; - - using (var activeSolutionBoundTracker = CreateTestSubject(this.host, this.activeSolutionTracker, this.configProvider, this.loggerMock.Object)) + using var activeSolutionBoundTracker = CreateTestSubject(activeSolutionTracker, configProvider, loggerMock.Object, sonarQubeService: sonarQubeServiceMock.Object); + + // Act + // Throwing errors will put the connection and binding out of sync, which + // cause a Debug.Assert in the product code that we need to suppress + using (new AssertIgnoreScope()) { - // We want to directly jump to Connect - sonarQubeServiceMock.SetupGet(x => x.IsConnected).Returns(false); - ConfigureSolutionBinding(new BoundServerProject("solution", "key", new ServerConnection.SonarQube(new Uri("http://localhost:9000")))); - - // ConnectAsync should throw - sonarQubeServiceMock - .SetupSequence(x => x.ConnectAsync(It.IsAny(), It.IsAny())) - .Throws() - .Throws() - .Throws(new HttpRequestException("http request", new Exception("something happened"))); - - // Act - // Throwing errors will put the connection and binding out of sync, which - // cause a Debug.Assert in the product code that we need to suppress - using (new AssertIgnoreScope()) - { - this.activeSolutionTracker.SimulateActiveSolutionChanged(isSolutionOpen: true); - this.activeSolutionTracker.SimulateActiveSolutionChanged(isSolutionOpen: false); - this.activeSolutionTracker.SimulateActiveSolutionChanged(isSolutionOpen: true); - } - - // Assert - this.loggerMock - .Verify(x => x.WriteLine(It.Is(s => s.StartsWith("SonarQube request failed:")), It.IsAny()), Times.Exactly(2)); - this.loggerMock - .Verify(x => x.WriteLine(It.Is(s => s.StartsWith("SonarQube request timed out or was canceled"))), Times.Once); + activeSolutionTracker.SimulateActiveSolutionChanged(isSolutionOpen: true); + activeSolutionTracker.SimulateActiveSolutionChanged(isSolutionOpen: false); + activeSolutionTracker.SimulateActiveSolutionChanged(isSolutionOpen: true); } + + // Assert + loggerMock + .Verify(x => x.WriteLine(It.Is(s => s.StartsWith("SonarQube request failed:")), It.IsAny()), Times.Exactly(2)); + loggerMock + .Verify(x => x.WriteLine(It.Is(s => s.StartsWith("SonarQube request timed out or was canceled"))), Times.Once); } [TestMethod] public void ActiveSolutionBoundTracker_BoundProject_PassedToConfigScopeUpdater() { - var boundProject = new BoundServerProject("solution", "key", new ServerConnection.SonarQube(new Uri("http://localhost:9000"))); - var configScopeUpdaterMock = new Mock(); - ConfigureService(isConnected: false); - ConfigureSolutionBinding(boundProject); - - using (var testSubject = CreateTestSubject(this.host, this.activeSolutionTracker, this.configProvider, - loggerMock.Object, configScopeUpdater: configScopeUpdaterMock.Object)) - { - activeSolutionTracker.SimulateActiveSolutionChanged(isSolutionOpen: true); + ConfigureSolutionBinding(boundSonarQubeProject); + using var testSubject = CreateTestSubject( + activeSolutionTracker, + configProvider, + loggerMock.Object, + configScopeUpdater: configScopeUpdaterMock.Object, + sonarQubeService: sonarQubeServiceMock.Object); + + activeSolutionTracker.SimulateActiveSolutionChanged(isSolutionOpen: true); - configScopeUpdaterMock.Verify(x => x.UpdateConfigScopeForCurrentSolution(It.Is(s => s.ServerProjectKey == boundProject.ServerProjectKey && s.ServerConnection.ServerUri == boundProject.ServerConnection.ServerUri))); - configScopeUpdaterMock.VerifyNoOtherCalls(); - } + configScopeUpdaterMock.Verify(x => x.UpdateConfigScopeForCurrentSolution(It.Is(s => + s.ServerProjectKey == boundSonarQubeProject.ServerProjectKey + && s.ServerConnection.ServerUri == boundSonarQubeProject.ServerConnection.ServerUri))); + configScopeUpdaterMock.VerifyNoOtherCalls(); } [TestMethod] public void ActiveSolutionBoundTracker_UnBoundProject_NullPassedToConfigScopeUpdater() { var configScopeUpdaterMock = new Mock(); - ConfigureService(isConnected: false); - - using (var testSubject = CreateTestSubject(this.host, this.activeSolutionTracker, this.configProvider, - loggerMock.Object, configScopeUpdater: configScopeUpdaterMock.Object)) - { - activeSolutionTracker.SimulateActiveSolutionChanged(isSolutionOpen: true); + using var testSubject = CreateTestSubject( + activeSolutionTracker, + configProvider, + loggerMock.Object, + configScopeUpdater: configScopeUpdaterMock.Object, + sonarQubeService: sonarQubeServiceMock.Object); + + activeSolutionTracker.SimulateActiveSolutionChanged(isSolutionOpen: true); - configScopeUpdaterMock.Verify(x => x.UpdateConfigScopeForCurrentSolution(null)); - configScopeUpdaterMock.VerifyNoOtherCalls(); - } + configScopeUpdaterMock.Verify(x => x.UpdateConfigScopeForCurrentSolution(null)); + configScopeUpdaterMock.VerifyNoOtherCalls(); } [TestMethod] public void ActiveSolutionBoundTracker_Changes() { - var boundProject = new BoundServerProject("solution", "key", new ServerConnection.SonarQube(new Uri("http://localhost:9000"))); - var configScopeUpdaterMock = new Mock(); ConfigureService(isConnected: false); - ConfigureSolutionBinding(boundProject); - - using (var testSubject = CreateTestSubject(this.host, this.activeSolutionTracker, this.configProvider, loggerMock.Object, configScopeUpdater: configScopeUpdaterMock.Object)) - { - var eventCounter = new EventCounter(testSubject); - - // Sanity - testSubject.CurrentConfiguration.Mode.Should().Be(SonarLintMode.Connected, "Initially bound"); - eventCounter.PreSolutionBindingChangedCount.Should().Be(0, "no events raised during construction"); - eventCounter.SolutionBindingChangedCount.Should().Be(0, "no events raised during construction"); - eventCounter.PreSolutionBindingUpdatedCount.Should().Be(0, "no events raised during construction"); - eventCounter.SolutionBindingUpdatedCount.Should().Be(0, "no events raised during construction"); - VerifyAndResetBoundSolutionUIContextMock(isActive: true); - - // Case 1: Clear bound project - ConfigureSolutionBinding(null); - - // Act - testSubject.HandleBindingChange(true); - - // Assert - testSubject.CurrentConfiguration.Mode.Should().Be(SonarLintMode.Standalone, "Unbound solution should report false activation"); - eventCounter.PreSolutionBindingChangedCount.Should().Be(1, "Unbind should trigger reanalysis"); - eventCounter.SolutionBindingChangedCount.Should().Be(1, "Unbind should trigger reanalysis"); - eventCounter.PreSolutionBindingUpdatedCount.Should().Be(0, "Unbind should not trigger change"); - eventCounter.SolutionBindingUpdatedCount.Should().Be(0, "Unbind should not trigger change"); - configScopeUpdaterMock.Verify(x => x.UpdateConfigScopeForCurrentSolution(It.IsAny()), Times.Exactly(1)); - VerifyAndResetBoundSolutionUIContextMock(isActive: false); - - VerifyServiceDisconnect(Times.Never()); - VerifyServiceConnect(Times.Never()); - - // Case 2: Set bound project - ConfigureSolutionBinding(boundProject); - // Act - testSubject.HandleBindingChange(false); - - // Assert - testSubject.CurrentConfiguration.Mode.Should().Be(SonarLintMode.Connected, "Bound solution should report true activation"); - eventCounter.PreSolutionBindingChangedCount.Should().Be(2, "Bind should trigger reanalysis"); - eventCounter.SolutionBindingChangedCount.Should().Be(2, "Bind should trigger reanalysis"); - eventCounter.PreSolutionBindingUpdatedCount.Should().Be(0, "Bind should not trigger update event"); - eventCounter.SolutionBindingUpdatedCount.Should().Be(0, "Bind should not trigger update event"); - configScopeUpdaterMock.Verify(x => x.UpdateConfigScopeForCurrentSolution(It.IsAny()), Times.Exactly(2)); - VerifyAndResetBoundSolutionUIContextMock(isActive: true); - - // Notifications from the Team Explorer should not trigger connect/disconnect - VerifyServiceDisconnect(Times.Never()); - VerifyServiceConnect(Times.Never()); - - // Case 3: Bound solution unloaded -> disconnect - ConfigureSolutionBinding(null); - // Act - activeSolutionTracker.SimulateActiveSolutionChanged(isSolutionOpen: false); - - // Assert - testSubject.CurrentConfiguration.Mode.Should().Be(SonarLintMode.Standalone, "Should respond to solution change event and report unbound"); - eventCounter.PreSolutionBindingChangedCount.Should().Be(3, "Solution change should trigger reanalysis"); - eventCounter.SolutionBindingChangedCount.Should().Be(3, "Solution change should trigger reanalysis"); - eventCounter.PreSolutionBindingUpdatedCount.Should().Be(0, "Solution change should not trigger update event"); - eventCounter.SolutionBindingUpdatedCount.Should().Be(0, "Solution change should not trigger update event"); - configScopeUpdaterMock.Verify(x => x.UpdateConfigScopeForCurrentSolution(It.IsAny()), Times.Exactly(3)); - VerifyAndResetBoundSolutionUIContextMock(isActive: false); - - // Closing an unbound solution should not call disconnect/connect - VerifyServiceDisconnect(Times.Never()); - VerifyServiceConnect(Times.Never()); - - // Case 4: Load a bound solution - ConfigureSolutionBinding(boundProject); - // Act - activeSolutionTracker.SimulateActiveSolutionChanged(isSolutionOpen: true); - - // Assert - testSubject.CurrentConfiguration.Mode.Should().Be(SonarLintMode.Connected, "Bound respond to solution change event and report bound"); - eventCounter.PreSolutionBindingChangedCount.Should().Be(4, "Solution change should trigger reanalysis"); - eventCounter.SolutionBindingChangedCount.Should().Be(4, "Solution change should trigger reanalysis"); - eventCounter.PreSolutionBindingUpdatedCount.Should().Be(0, "Bind should not trigger update event"); - eventCounter.SolutionBindingUpdatedCount.Should().Be(0, "Bind should not trigger update event"); - configScopeUpdaterMock.Verify(x => x.UpdateConfigScopeForCurrentSolution(It.IsAny()), Times.Exactly(4)); - VerifyAndResetBoundSolutionUIContextMock(isActive: true); - - // Loading a bound solution should call connect - VerifyServiceDisconnect(Times.Never()); - VerifyServiceConnect(Times.Once()); - - // Case 5: Close a bound solution - ConfigureSolutionBinding(null); - // Act - activeSolutionTracker.SimulateActiveSolutionChanged(isSolutionOpen: false); - - // Assert - eventCounter.PreSolutionBindingChangedCount.Should().Be(5, "Solution change should trigger reanalysis"); - eventCounter.SolutionBindingChangedCount.Should().Be(5, "Solution change should trigger reanalysis"); - eventCounter.PreSolutionBindingUpdatedCount.Should().Be(0, "Solution change should not trigger update event"); - eventCounter.SolutionBindingUpdatedCount.Should().Be(0, "Solution change should not trigger update event"); - configScopeUpdaterMock.Verify(x => x.UpdateConfigScopeForCurrentSolution(It.IsAny()), Times.Exactly(5)); - VerifyAndResetBoundSolutionUIContextMock(isActive: false); - - // SonarQubeService.Disconnect should be called since the WPF DisconnectCommand is not available - VerifyServiceDisconnect(Times.Once()); - VerifyServiceConnect(Times.Once()); - - // Case 6: Dispose and change - // Act - testSubject.Dispose(); - ConfigureSolutionBinding(boundProject); - testSubject.HandleBindingChange(true); - - // Assert - eventCounter.PreSolutionBindingChangedCount.Should().Be(5, "Once disposed should stop raising the event"); - eventCounter.SolutionBindingChangedCount.Should().Be(5, "Once disposed should stop raising the event"); - configScopeUpdaterMock.Verify(x => x.UpdateConfigScopeForCurrentSolution(It.IsAny()), Times.Exactly(5)); - // SonarQubeService.Disconnect should be called since the WPF DisconnectCommand is not available - VerifyServiceDisconnect(Times.Once()); - VerifyServiceConnect(Times.Once()); - } - } - - [TestMethod] - public void OnBindingStateChanged_NewConfiguration_EventsRaisedInCorrectOrder() - { - // Arrange - var initialProject = new BoundServerProject( - "solution", - "projectKey", - new ServerConnection.SonarCloud("myOrgKey")); - - // Set the current configuration used by the tracker - ConfigureSolutionBinding(initialProject); - using (var testSubject = CreateTestSubject(this.host, this.activeSolutionTracker, this.configProvider, loggerMock.Object)) - { - var eventCounter = new EventCounter(testSubject); + ConfigureSolutionBinding(boundSonarQubeProject); + + var testSubject = CreateTestSubject( + activeSolutionTracker, + configProvider, + loggerMock.Object, + configScopeUpdater: configScopeUpdaterMock.Object, + sonarQubeService: sonarQubeServiceMock.Object); + var eventCounter = new EventCounter(testSubject); + + // Sanity + testSubject.CurrentConfiguration.Mode.Should().Be(SonarLintMode.Connected, "Initially bound"); + eventCounter.PreSolutionBindingChangedCount.Should().Be(0, "no events raised during construction"); + eventCounter.SolutionBindingChangedCount.Should().Be(0, "no events raised during construction"); + eventCounter.PreSolutionBindingUpdatedCount.Should().Be(0, "no events raised during construction"); + eventCounter.SolutionBindingUpdatedCount.Should().Be(0, "no events raised during construction"); + VerifyAndResetBoundSolutionUiContextMock(isActive: true); + + // Case 1: Clear bound project + ConfigureSolutionBinding(null); - // Now configure the provider to return a different configuration - var newProject = new BoundServerProject( - "solution", - "projectKey", - new ServerConnection.SonarCloud("myOrgKey_DIFFERENT")); - ConfigureSolutionBinding(newProject); - - // Act - simulate the binding state changing in the Team explorer section. - // The project configuration hasn't changed (it doesn't matter what properties - // we pass here; they aren't used when raising the event.) - host.VisualStateManager.SetBoundProject(new Uri("http://junk"), "any", "any"); - - // Assert - // Different config so event should be raised - eventCounter.PreSolutionBindingChangedCount.Should().Be(1); - eventCounter.PreSolutionBindingUpdatedCount.Should().Be(0); - eventCounter.SolutionBindingChangedCount.Should().Be(1); - eventCounter.SolutionBindingUpdatedCount.Should().Be(0); - - eventCounter.RaisedEventNames.Should().HaveCount(2); - eventCounter.RaisedEventNames[0].Should().Be(nameof(testSubject.PreSolutionBindingChanged)); - eventCounter.RaisedEventNames[1].Should().Be(nameof(testSubject.SolutionBindingChanged)); - } - } - - [TestMethod] - public void HandleBindingChange_NewConfiguration_EventsRaisedInCorrectOrder() - { - // Arrange - var initialProject = new BoundServerProject( - "solution", - "projectKey", - new ServerConnection.SonarCloud("myOrgKey")); - - // Set the current configuration used by the tracker - ConfigureSolutionBinding(initialProject); - using (var testSubject = CreateTestSubject(this.host, this.activeSolutionTracker, this.configProvider, loggerMock.Object)) - { - var eventCounter = new EventCounter(testSubject); + // Act + testSubject.HandleBindingChange(true); + + // Assert + testSubject.CurrentConfiguration.Mode.Should().Be(SonarLintMode.Standalone, "Unbound solution should report false activation"); + eventCounter.PreSolutionBindingChangedCount.Should().Be(1, "Unbind should trigger reanalysis"); + eventCounter.SolutionBindingChangedCount.Should().Be(1, "Unbind should trigger reanalysis"); + eventCounter.PreSolutionBindingUpdatedCount.Should().Be(0, "Unbind should not trigger change"); + eventCounter.SolutionBindingUpdatedCount.Should().Be(0, "Unbind should not trigger change"); + configScopeUpdaterMock.Verify(x => x.UpdateConfigScopeForCurrentSolution(It.IsAny()), Times.Exactly(1)); + VerifyAndResetBoundSolutionUiContextMock(isActive: false); + + VerifyServiceDisconnect(Times.Never()); + VerifyServiceConnect(Times.Never()); + + // Case 2: Set bound project + ConfigureSolutionBinding(boundSonarQubeProject); + // Act + testSubject.HandleBindingChange(false); + + // Assert + testSubject.CurrentConfiguration.Mode.Should().Be(SonarLintMode.Connected, "Bound solution should report true activation"); + eventCounter.PreSolutionBindingChangedCount.Should().Be(2, "Bind should trigger reanalysis"); + eventCounter.SolutionBindingChangedCount.Should().Be(2, "Bind should trigger reanalysis"); + eventCounter.PreSolutionBindingUpdatedCount.Should().Be(0, "Bind should not trigger update event"); + eventCounter.SolutionBindingUpdatedCount.Should().Be(0, "Bind should not trigger update event"); + configScopeUpdaterMock.Verify(x => x.UpdateConfigScopeForCurrentSolution(It.IsAny()), Times.Exactly(2)); + VerifyAndResetBoundSolutionUiContextMock(isActive: true); + + // Notifications from the Team Explorer should not trigger connect/disconnect + VerifyServiceDisconnect(Times.Never()); + VerifyServiceConnect(Times.Never()); + + // Case 3: Bound solution unloaded -> disconnect + ConfigureSolutionBinding(null); + // Act + activeSolutionTracker.SimulateActiveSolutionChanged(isSolutionOpen: false); + + // Assert + testSubject.CurrentConfiguration.Mode.Should().Be(SonarLintMode.Standalone, "Should respond to solution change event and report unbound"); + eventCounter.PreSolutionBindingChangedCount.Should().Be(3, "Solution change should trigger reanalysis"); + eventCounter.SolutionBindingChangedCount.Should().Be(3, "Solution change should trigger reanalysis"); + eventCounter.PreSolutionBindingUpdatedCount.Should().Be(0, "Solution change should not trigger update event"); + eventCounter.SolutionBindingUpdatedCount.Should().Be(0, "Solution change should not trigger update event"); + configScopeUpdaterMock.Verify(x => x.UpdateConfigScopeForCurrentSolution(It.IsAny()), Times.Exactly(3)); + VerifyAndResetBoundSolutionUiContextMock(isActive: false); + + // Closing an unbound solution should not call disconnect/connect + VerifyServiceDisconnect(Times.Never()); + VerifyServiceConnect(Times.Never()); + + // Case 4: Load a bound solution + ConfigureSolutionBinding(boundSonarQubeProject); + // Act + activeSolutionTracker.SimulateActiveSolutionChanged(isSolutionOpen: true); + + // Assert + testSubject.CurrentConfiguration.Mode.Should().Be(SonarLintMode.Connected, "Bound respond to solution change event and report bound"); + eventCounter.PreSolutionBindingChangedCount.Should().Be(4, "Solution change should trigger reanalysis"); + eventCounter.SolutionBindingChangedCount.Should().Be(4, "Solution change should trigger reanalysis"); + eventCounter.PreSolutionBindingUpdatedCount.Should().Be(0, "Bind should not trigger update event"); + eventCounter.SolutionBindingUpdatedCount.Should().Be(0, "Bind should not trigger update event"); + configScopeUpdaterMock.Verify(x => x.UpdateConfigScopeForCurrentSolution(It.IsAny()), Times.Exactly(4)); + VerifyAndResetBoundSolutionUiContextMock(isActive: true); + + // Loading a bound solution should call connect + VerifyServiceDisconnect(Times.Never()); + VerifyServiceConnect(Times.Once()); + + // Case 5: Close a bound solution + ConfigureSolutionBinding(null); + // Act + activeSolutionTracker.SimulateActiveSolutionChanged(isSolutionOpen: false); - // Now configure the provider to return a different configuration - var newProject = new BoundServerProject( - "solution", - "projectKey", - new ServerConnection.SonarCloud("myOrgKey_DIFFERENT")); - ConfigureSolutionBinding(newProject); + // Assert + eventCounter.PreSolutionBindingChangedCount.Should().Be(5, "Solution change should trigger reanalysis"); + eventCounter.SolutionBindingChangedCount.Should().Be(5, "Solution change should trigger reanalysis"); + eventCounter.PreSolutionBindingUpdatedCount.Should().Be(0, "Solution change should not trigger update event"); + eventCounter.SolutionBindingUpdatedCount.Should().Be(0, "Solution change should not trigger update event"); + configScopeUpdaterMock.Verify(x => x.UpdateConfigScopeForCurrentSolution(It.IsAny()), Times.Exactly(5)); + VerifyAndResetBoundSolutionUiContextMock(isActive: false); - // Act - simulate the binding state changing in the Team explorer section. - // The project configuration hasn't changed (it doesn't matter what properties - // we pass here; they aren't used when raising the event.) - testSubject.HandleBindingChange(false); + // SonarQubeService.Disconnect should be called since the WPF DisconnectCommand is not available + VerifyServiceDisconnect(Times.Once()); + VerifyServiceConnect(Times.Once()); - // Assert - // Different config so event should be raised - eventCounter.PreSolutionBindingChangedCount.Should().Be(1); - eventCounter.PreSolutionBindingUpdatedCount.Should().Be(0); - eventCounter.SolutionBindingChangedCount.Should().Be(1); - eventCounter.SolutionBindingUpdatedCount.Should().Be(0); - - eventCounter.RaisedEventNames.Should().HaveCount(2); - eventCounter.RaisedEventNames[0].Should().Be(nameof(testSubject.PreSolutionBindingChanged)); - eventCounter.RaisedEventNames[1].Should().Be(nameof(testSubject.SolutionBindingChanged)); - } + // Case 6: Dispose and change + // Act + testSubject.Dispose(); + ConfigureSolutionBinding(boundSonarQubeProject); + testSubject.HandleBindingChange(true); + + // Assert + eventCounter.PreSolutionBindingChangedCount.Should().Be(5, "Once disposed should stop raising the event"); + eventCounter.SolutionBindingChangedCount.Should().Be(5, "Once disposed should stop raising the event"); + configScopeUpdaterMock.Verify(x => x.UpdateConfigScopeForCurrentSolution(It.IsAny()), Times.Exactly(5)); + // SonarQubeService.Disconnect should be called since the WPF DisconnectCommand is not available + VerifyServiceDisconnect(Times.Once()); + VerifyServiceConnect(Times.Once()); } [TestMethod] public void UpdateConnection_WasDisconnected_NewSolutionIsUnbound_NoConnectOrDisconnectCalls() { // Arrange - using (CreateTestSubject(this.host, this.activeSolutionTracker, this.configProvider, loggerMock.Object)) + ConfigureService(isConnected: false); + ConfigureSolutionBinding(null); + + using (CreateTestSubject(activeSolutionTracker, configProvider, loggerMock.Object, sonarQubeService: sonarQubeServiceMock.Object)) { - ConfigureService(isConnected: false); - ConfigureSolutionBinding(null); - // Act activeSolutionTracker.SimulateActiveSolutionChanged(isSolutionOpen: true); @@ -422,11 +310,11 @@ public void UpdateConnection_WasDisconnected_NewSolutionIsUnbound_NoConnectOrDis public void UpdateConnection_WasDisconnected_NewSolutionIsBound_ConnectCalled() { // Arrange - using (CreateTestSubject(this.host, this.activeSolutionTracker, this.configProvider, loggerMock.Object)) + ConfigureService(isConnected: false); + ConfigureSolutionBinding(new BoundServerProject("solution", "projectKey", new ServerConnection.SonarQube(new Uri("http://foo")))); + + using (CreateTestSubject(activeSolutionTracker, configProvider, loggerMock.Object, sonarQubeService: sonarQubeServiceMock.Object)) { - ConfigureService(isConnected: false); - ConfigureSolutionBinding(new BoundServerProject("solution", "projectKey", new ServerConnection.SonarQube(new Uri("http://foo")))); - // Act activeSolutionTracker.SimulateActiveSolutionChanged(isSolutionOpen: true); @@ -441,11 +329,11 @@ public void UpdateConnection_WasDisconnected_NewSolutionIsBound_ConnectCalled() public void UpdateConnection_WasConnected_NewSolutionIsUnbound_DisconnectedCalled() { // Arrange - using (CreateTestSubject(this.host, this.activeSolutionTracker, this.configProvider, loggerMock.Object)) + ConfigureService(isConnected: true); + ConfigureSolutionBinding(null); + + using (CreateTestSubject(activeSolutionTracker, configProvider, loggerMock.Object, sonarQubeService: sonarQubeServiceMock.Object)) { - ConfigureService(isConnected: true); - ConfigureSolutionBinding(null); - // Act activeSolutionTracker.SimulateActiveSolutionChanged(isSolutionOpen: true); @@ -465,11 +353,11 @@ public void UpdateConnection_WasConnected_NewSolutionBound_ConnectedCalled() // just in case. // Arrange - using (CreateTestSubject(this.host, this.activeSolutionTracker, this.configProvider, loggerMock.Object)) + ConfigureService(isConnected: true); + ConfigureSolutionBinding(new BoundServerProject("solution", "projectKey", new ServerConnection.SonarQube(new Uri("http://foo")))); + + using (CreateTestSubject(activeSolutionTracker, configProvider, loggerMock.Object, sonarQubeService: sonarQubeServiceMock.Object)) { - ConfigureService(isConnected: true); - ConfigureSolutionBinding(new BoundServerProject("solution", "projectKey", new ServerConnection.SonarQube(new Uri("http://foo")))); - // Act activeSolutionTracker.SimulateActiveSolutionChanged(isSolutionOpen: true); @@ -481,84 +369,20 @@ public void UpdateConnection_WasConnected_NewSolutionBound_ConnectedCalled() } [TestMethod] - public void UpdateConnection_Disconnect_WpfCommandIsAvailableButNotExecutable_ServiceDisconnectedIsCalled() + public void UpdateConnection_Disconnect_ServiceDisconnectedIsCalled() { - // If the WPF command is available then it should be used to disconnect, - // rather than calling service.Disconnect() directly. - // Here, the command exists but is not executable. - + ConfigureService(isConnected: true); + ConfigureSolutionBinding(null); + // Arrange - using (CreateTestSubject(this.host, this.activeSolutionTracker, this.configProvider, loggerMock.Object)) + using (CreateTestSubject(activeSolutionTracker, configProvider, loggerMock.Object, sonarQubeService: sonarQubeServiceMock.Object)) { - - int commandCallCount = 0; - int commandCanExecuteCallCount = 0; - var teSection = ConfigurableSectionController.CreateDefault(); - teSection.DisconnectCommand = new Integration.WPF.RelayCommand( - () => commandCallCount++, - () => { commandCanExecuteCallCount++; return false; }); - host.SetActiveSection(teSection); - - ConfigureService(isConnected: true); - ConfigureSolutionBinding(null); - // Act activeSolutionTracker.SimulateActiveSolutionChanged(isSolutionOpen: false); // Assert VerifyServiceConnect(Times.Never()); VerifyServiceDisconnect(Times.Once()); - commandCanExecuteCallCount.Should().Be(1); - commandCallCount.Should().Be(0); - } - } - - [TestMethod] - public void UpdateConnection_Disconnect_WpfCommandIsAvailableAndExecutable_ServiceDisconnectedIsNotCalled() - { - // Wpf command is available and can be executed -> should be called instead of service.Disconnect() - - // Arrange - using (CreateTestSubject(this.host, this.activeSolutionTracker, this.configProvider, loggerMock.Object)) - { - int commandCallCount = 0; - int commandCanExecuteCallCount = 0; - var teSection = ConfigurableSectionController.CreateDefault(); - teSection.DisconnectCommand = new Integration.WPF.RelayCommand( - () => { commandCallCount++; isMockServiceConnected = false; }, - () => { commandCanExecuteCallCount++; return true; }); - host.SetActiveSection(teSection); - - ConfigureService(isConnected: true); - ConfigureSolutionBinding(null); - - // Act - activeSolutionTracker.SimulateActiveSolutionChanged(isSolutionOpen: false); - - // Assert - VerifyServiceConnect(Times.Never()); - VerifyServiceDisconnect(Times.Never()); - commandCanExecuteCallCount.Should().Be(1); - commandCallCount.Should().Be(1); - } - } - - [TestMethod] - public void SolutionBindingUpdated_WhenClearBoundProject_NotRaised() - { - // Arrange - using (var testSubject = CreateTestSubject(this.host, this.activeSolutionTracker, this.configProvider, loggerMock.Object)) - { - var eventCounter = new EventCounter(testSubject); - - // Act - host.VisualStateManager.ClearBoundProject(); - - // Assert - eventCounter.PreSolutionBindingUpdatedCount.Should().Be(0); - eventCounter.SolutionBindingUpdatedCount.Should().Be(0); - eventCounter.PreSolutionBindingChangedCount.Should().Be(0); - eventCounter.SolutionBindingChangedCount.Should().Be(0); } } @@ -566,65 +390,38 @@ public void SolutionBindingUpdated_WhenClearBoundProject_NotRaised() public void HandleBindingChange_WhenClearBoundProject_NotRaised() { // Arrange - using (var testSubject = CreateTestSubject(this.host, this.activeSolutionTracker, this.configProvider, loggerMock.Object)) - { - var eventCounter = new EventCounter(testSubject); + using var testSubject = CreateTestSubject(activeSolutionTracker, configProvider, loggerMock.Object); + var eventCounter = new EventCounter(testSubject); - // Act - testSubject.HandleBindingChange(true); - - // Assert - eventCounter.PreSolutionBindingUpdatedCount.Should().Be(0); - eventCounter.SolutionBindingUpdatedCount.Should().Be(0); - eventCounter.PreSolutionBindingChangedCount.Should().Be(0); - eventCounter.SolutionBindingChangedCount.Should().Be(0); - } - } - - [TestMethod] - public void SolutionBindingUpdated_WhenSetBoundProject_EventsRaisedInExpectedOrder() - { - // Arrange - using (var testSubject = CreateTestSubject(this.host, this.activeSolutionTracker, this.configProvider, loggerMock.Object)) - { - var eventCounter = new EventCounter(testSubject); - - // Act - host.VisualStateManager.SetBoundProject(new Uri("http://localhost"), null, "project123"); + // Act + testSubject.HandleBindingChange(true); - // Assert - eventCounter.PreSolutionBindingUpdatedCount.Should().Be(1); - eventCounter.SolutionBindingUpdatedCount.Should().Be(1); - eventCounter.PreSolutionBindingChangedCount.Should().Be(0); - eventCounter.SolutionBindingChangedCount.Should().Be(0); - - eventCounter.RaisedEventNames.Should().HaveCount(2); - eventCounter.RaisedEventNames[0].Should().Be(nameof(testSubject.PreSolutionBindingUpdated)); - eventCounter.RaisedEventNames[1].Should().Be(nameof(testSubject.SolutionBindingUpdated)); - } + // Assert + eventCounter.PreSolutionBindingUpdatedCount.Should().Be(0); + eventCounter.SolutionBindingUpdatedCount.Should().Be(0); + eventCounter.PreSolutionBindingChangedCount.Should().Be(0); + eventCounter.SolutionBindingChangedCount.Should().Be(0); } [TestMethod] public void HandleBindingChange_WhenSetBoundProject_EventsRaisedInExpectedOrder() { // Arrange - using (var testSubject = CreateTestSubject(this.host, this.activeSolutionTracker, this.configProvider, loggerMock.Object)) - { - var eventCounter = new EventCounter(testSubject); + using var testSubject = CreateTestSubject(activeSolutionTracker, configProvider, loggerMock.Object); + var eventCounter = new EventCounter(testSubject); - // Act - testSubject.HandleBindingChange(false); + // Act + testSubject.HandleBindingChange(false); - // Assert - eventCounter.PreSolutionBindingUpdatedCount.Should().Be(1); - eventCounter.SolutionBindingUpdatedCount.Should().Be(1); - eventCounter.PreSolutionBindingChangedCount.Should().Be(0); - eventCounter.SolutionBindingChangedCount.Should().Be(0); - - eventCounter.RaisedEventNames.Should().HaveCount(2); - eventCounter.RaisedEventNames[0].Should().Be(nameof(testSubject.PreSolutionBindingUpdated)); - eventCounter.RaisedEventNames[1].Should().Be(nameof(testSubject.SolutionBindingUpdated)); - } + // Assert + eventCounter.PreSolutionBindingUpdatedCount.Should().Be(1); + eventCounter.SolutionBindingUpdatedCount.Should().Be(1); + eventCounter.PreSolutionBindingChangedCount.Should().Be(0); + eventCounter.SolutionBindingChangedCount.Should().Be(0); + + eventCounter.RaisedEventNames.Should().HaveCount(2); + eventCounter.RaisedEventNames[0].Should().Be(nameof(testSubject.PreSolutionBindingUpdated)); + eventCounter.RaisedEventNames[1].Should().Be(nameof(testSubject.SolutionBindingUpdated)); } [TestMethod] @@ -633,78 +430,70 @@ public void GitRepoUpdated_SolutionBindingUpdatedEventsRaised() var gitEventsMonitor = new Mock(); // Arrange - using (var testSubject = CreateTestSubject(this.host, this.activeSolutionTracker, this.configProvider, loggerMock.Object, gitEventsMonitor.Object)) - { - var eventCounter = new EventCounter(testSubject); - - // Act - ConfigureSolutionBinding(new BoundServerProject("solution", "projectKey", new ServerConnection.SonarQube(new Uri("http://foo")))); - gitEventsMonitor.Raise(x => x.HeadChanged += null, null, null); + using var testSubject = CreateTestSubject(activeSolutionTracker, configProvider, loggerMock.Object, gitEventsMonitor.Object); + var eventCounter = new EventCounter(testSubject); - // Assert - eventCounter.PreSolutionBindingUpdatedCount.Should().Be(1); - eventCounter.SolutionBindingUpdatedCount.Should().Be(1); - eventCounter.PreSolutionBindingChangedCount.Should().Be(0); - eventCounter.SolutionBindingChangedCount.Should().Be(0); - - eventCounter.RaisedEventNames.Should().HaveCount(2); - eventCounter.RaisedEventNames[0].Should().Be(nameof(testSubject.PreSolutionBindingUpdated)); - eventCounter.RaisedEventNames[1].Should().Be(nameof(testSubject.SolutionBindingUpdated)); - } + // Act + ConfigureSolutionBinding(new BoundServerProject("solution", "projectKey", new ServerConnection.SonarQube(new Uri("http://foo")))); + gitEventsMonitor.Raise(x => x.HeadChanged += null, null, null); + + // Assert + eventCounter.PreSolutionBindingUpdatedCount.Should().Be(1); + eventCounter.SolutionBindingUpdatedCount.Should().Be(1); + eventCounter.PreSolutionBindingChangedCount.Should().Be(0); + eventCounter.SolutionBindingChangedCount.Should().Be(0); + + eventCounter.RaisedEventNames.Should().HaveCount(2); + eventCounter.RaisedEventNames[0].Should().Be(nameof(testSubject.PreSolutionBindingUpdated)); + eventCounter.RaisedEventNames[1].Should().Be(nameof(testSubject.SolutionBindingUpdated)); } [TestMethod] public void GitRepoUpdated_UnBoundProject_SolutionBingingUpdatedNotInvoked() { - var gitEventsMonitor = new Mock(); - // Arrange - using (var testSubject = CreateTestSubject(this.host, this.activeSolutionTracker, this.configProvider, loggerMock.Object, gitEventsMonitor.Object)) - { - var eventCounter = new EventCounter(testSubject); + var gitEventsMonitor = new Mock(); + using var testSubject = CreateTestSubject(activeSolutionTracker, configProvider, loggerMock.Object, gitEventsMonitor.Object); + var eventCounter = new EventCounter(testSubject); - // Act - ConfigureSolutionBinding(null); - gitEventsMonitor.Raise(x => x.HeadChanged += null, null, null); + // Act + ConfigureSolutionBinding(null); + gitEventsMonitor.Raise(x => x.HeadChanged += null, null, null); - // Assert - eventCounter.RaisedEventNames.Should().HaveCount(0); - } + // Assert + eventCounter.RaisedEventNames.Should().HaveCount(0); } [TestMethod] public void ActiveSolutionChanged_GitEventsMonitorRefreshInvoked() { var gitEventsMonitor = new Mock(); + ConfigureService(true); + ConfigureSolutionBinding(new BoundServerProject("solution", "projectKey", new ServerConnection.SonarQube(new Uri("http://foo")))); // Arrange - using (var testSubject = CreateTestSubject(this.host, this.activeSolutionTracker, this.configProvider, loggerMock.Object, gitEventsMonitor.Object)) - { - var eventCounter = new EventCounter(testSubject); + using var testSubject = CreateTestSubject(activeSolutionTracker, configProvider, loggerMock.Object, gitEventsMonitor.Object, sonarQubeService: sonarQubeServiceMock.Object); - // Act - ConfigureService(true); - ConfigureSolutionBinding(new BoundServerProject("solution", "projectKey", new ServerConnection.SonarQube(new Uri("http://foo")))); - this.activeSolutionTracker.SimulateActiveSolutionChanged(true); + // Act + activeSolutionTracker.SimulateActiveSolutionChanged(true); - // Assert - gitEventsMonitor.Verify(x => x.Refresh(), Times.Once); - } + // Assert + gitEventsMonitor.Verify(x => x.Refresh(), Times.Once); } - private ActiveSolutionBoundTracker CreateTestSubject(IHost host, + private ActiveSolutionBoundTracker CreateTestSubject( IActiveSolutionTracker solutionTracker, IConfigurationProvider configurationProvider, ILogger logger = null, IBoundSolutionGitMonitor gitEvents = null, - IServiceProvider serviceProvider = null, - IConfigScopeUpdater configScopeUpdater = null) + IConfigScopeUpdater configScopeUpdater = null, + ISonarQubeService sonarQubeService = null) { configScopeUpdater ??= Mock.Of(); logger ??= new TestLogger(logToConsole: true); gitEvents ??= Mock.Of(); - serviceProvider ??= this.serviceProvider; - return new ActiveSolutionBoundTracker(serviceProvider, host, solutionTracker, configScopeUpdater, logger, gitEvents, configurationProvider); + sonarQubeService ??= Mock.Of(); + return new ActiveSolutionBoundTracker(serviceProvider, solutionTracker, configScopeUpdater, logger, gitEvents, configurationProvider, sonarQubeService); } private void ConfigureService(bool isConnected) @@ -720,8 +509,6 @@ private void ConfigureService(bool isConnected) .Callback(() => isMockServiceConnected = true ) .Returns(Task.Delay(0)) .Verifiable(); - - this.host.SonarQubeService = sonarQubeServiceMock.Object; } private void ConfigureSolutionBinding(BoundServerProject boundProject) @@ -741,17 +528,17 @@ private void VerifyServiceDisconnect(Times expected) sonarQubeServiceMock.Verify(disconnectMethod, expected); } - private void VerifyAndResetBoundSolutionUIContextMock(bool isActive) + private void VerifyAndResetBoundSolutionUiContextMock(bool isActive) { var isActiveInt = isActive ? 1 : 0; - vsMonitorMock.Verify(x => x.SetCmdUIContext(boundSolutionUIContextCookie, isActiveInt), Times.Once); + vsMonitorMock.Verify(x => x.SetCmdUIContext(boundSolutionUiContextCookie, isActiveInt), Times.Once); vsMonitorMock.Verify(x => x.SetCmdUIContext(It.IsAny(), It.IsAny()), Times.Once); vsMonitorMock.Invocations.Clear(); } private class EventCounter { - private readonly List raisedEventNames = new List(); + private readonly List raisedEventNames = []; public int PreSolutionBindingChangedCount { get; private set; } public int SolutionBindingChangedCount { get; private set; } diff --git a/src/Integration.UnitTests/MefServices/ConnectedModeWindowEventBasedSchedulerTests.cs b/src/Integration.UnitTests/MefServices/ConnectedModeWindowEventBasedSchedulerTests.cs deleted file mode 100644 index fdb586275f..0000000000 --- a/src/Integration.UnitTests/MefServices/ConnectedModeWindowEventBasedSchedulerTests.cs +++ /dev/null @@ -1,130 +0,0 @@ -/* - * 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 FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using SonarLint.VisualStudio.Integration.MefServices; -using SonarLint.VisualStudio.TestInfrastructure; - -namespace SonarLint.VisualStudio.Integration.UnitTests.MefServices; - -[TestClass] -public class ConnectedModeWindowEventBasedSchedulerTests -{ - [TestMethod] - public void MefCtor_CheckExports() - { - MefTestHelpers.CheckTypeCanBeImported(); - MefTestHelpers.CheckTypeCanBeImported(); - } - - [TestMethod] - public void MefCtor_CheckMultipleExportsReturnSameInstance() - { - MefTestHelpers.CheckMultipleExportsReturnSameInstance(); - } - - [TestMethod] - public void SubscribeToConnectedModeWindowEvents_SetsHostEventListener() - { - var hostMock = new Mock(); - var testSubject = CreateTestSubject(); - - testSubject.SubscribeToConnectedModeWindowEvents(hostMock.Object); - - hostMock.VerifyAdd(x => x.ActiveSectionChanged += It.IsAny()); - } - - [TestMethod] - public void SubscribeToConnectedModeWindowEvents_AlreadySubscribed_Throws() - { - var hostMock = new Mock(); - var testSubject = CreateTestSubject(); - testSubject.SubscribeToConnectedModeWindowEvents(hostMock.Object); - - Action act = () => testSubject.SubscribeToConnectedModeWindowEvents(hostMock.Object); - - act.Should().Throw(); - } - - [TestMethod] - public void Dispose_UnsubscribesFromEvents() - { - var hostMock = new Mock(); - var testSubject = CreateTestSubject(); - testSubject.SubscribeToConnectedModeWindowEvents(hostMock.Object); - - testSubject.Dispose(); - testSubject.Dispose(); - testSubject.Dispose(); - - hostMock.VerifyRemove(x => x.ActiveSectionChanged -= It.IsAny(), Times.Once); - } - - [TestMethod] - public void NoEvent_DoesNothing() - { - var acted = false; - var testSubject = CreateTestSubject(); - testSubject.ScheduleActionOnNextEvent(() => { acted = true;}); - - acted.Should().BeFalse(); - } - - [TestMethod] - public void OnEvent_CallsLatestAction() - { - var acted1 = false; - var acted2 = false; - var testSubject = CreateTestSubject(); - testSubject.ScheduleActionOnNextEvent(() => { acted1 = true;}); - testSubject.ScheduleActionOnNextEvent(() => { acted2 = true;}); - - testSubject.ActiveSectionChangedListener(null, null); - - acted1.Should().BeFalse(); - acted2.Should().BeTrue(); - } - - [TestMethod] - public void OnEvent_CallsActionOnlyOnce() - { - var calls = 0; - var testSubject = CreateTestSubject(); - testSubject.ScheduleActionOnNextEvent(() => { calls++;}); - - testSubject.ActiveSectionChangedListener(null, null); - testSubject.ActiveSectionChangedListener(null, null); - testSubject.ActiveSectionChangedListener(null, null); - testSubject.ActiveSectionChangedListener(null, null); - testSubject.ActiveSectionChangedListener(null, null); - - calls.Should().Be(1); - } - - private ConnectedModeWindowEventBasedScheduler CreateTestSubject() - { - return new ConnectedModeWindowEventBasedScheduler(); - } -} diff --git a/src/Integration.UnitTests/MefServices/VsSessionHostTests.cs b/src/Integration.UnitTests/MefServices/VsSessionHostTests.cs deleted file mode 100644 index 173acfe740..0000000000 --- a/src/Integration.UnitTests/MefServices/VsSessionHostTests.cs +++ /dev/null @@ -1,385 +0,0 @@ -/* - * 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 Moq; -using SonarLint.VisualStudio.ConnectedMode.Binding; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Core.Binding; -using SonarLint.VisualStudio.Integration.MefServices; -using SonarLint.VisualStudio.Integration.TeamExplorer; -using SonarLint.VisualStudio.Integration.WPF; -using SonarLint.VisualStudio.TestInfrastructure; -using SonarQube.Client; -using SonarQube.Client.Models; - -namespace SonarLint.VisualStudio.Integration.UnitTests.TeamExplorer -{ - [TestClass] - public class VsSessionHostTests - { - private Mock sonarQubeServiceMock; - private ConfigurableStateManager stateManager; - private ConfigurableProgressStepRunner stepRunner; - private ConfigurableConfigurationProvider configProvider; - private Mock sharedBindingSuggestionService; - private Mock connectedModeWindowEventListener; - - public TestContext TestContext { get; set; } - - [TestInitialize] - public void TestInitialize() - { - this.sonarQubeServiceMock = new Mock(); - this.stepRunner = new ConfigurableProgressStepRunner(); - this.configProvider = new ConfigurableConfigurationProvider(); - sharedBindingSuggestionService = new Mock(); - connectedModeWindowEventListener = new Mock(); - } - - #region Tests - - [TestMethod] - public void MefCtor_CheckIsExported() - { - MefTestHelpers.CheckTypeCanBeImported( - MefTestHelpers.CreateExport(), - MefTestHelpers.CreateExport(), - MefTestHelpers.CreateExport(), - MefTestHelpers.CreateExport(), - MefTestHelpers.CreateExport()); - } - - [TestMethod] - public void Ctor_SubscribesToActiveSolutionChanged() - { - var trackerMock = new Mock(); - - var testSubject = CreateTestSubject(trackerMock.Object); - - trackerMock.VerifyAdd(x => x.ActiveSolutionChanged += It.IsAny>(), Times.Once); - connectedModeWindowEventListener.Verify(x => x.SubscribeToConnectedModeWindowEvents(testSubject), Times.Once); - } - - [TestMethod] - public void Dispose_UnsubscribesFromActiveSolutionChanged() - { - var trackerMock = new Mock(); - var testSubject = CreateTestSubject(trackerMock.Object); - - testSubject.Dispose(); - - trackerMock.VerifyRemove(x => x.ActiveSolutionChanged -= It.IsAny>(), Times.Once); - connectedModeWindowEventListener.Verify(x => x.Dispose(), Times.Once); - } - - [TestMethod] - public void VsSessionHost_SetActiveSection() - { - // Arrange - VsSessionHost testSubject = this.CreateTestSubject(null); - - // Case 1: Invalid args - Exceptions.Expect(() => testSubject.SetActiveSection(null)); - - // Case 2: Valid args - var section1 = ConfigurableSectionController.CreateDefault(); - var section2 = ConfigurableSectionController.CreateDefault(); - bool refresh1Called = false; - section1.RefreshCommand = new RelayCommand(c => refresh1Called = true); - bool refresh2Called = false; - section2.RefreshCommand = new RelayCommand(c => refresh2Called = true); - - // Act (set section1) - testSubject.SetActiveSection(section1); - refresh1Called.Should().BeFalse(); - refresh2Called.Should().BeFalse(); - - // Assert - testSubject.ActiveSection.Should().Be(section1); - - // Act (set section2) - testSubject.ClearActiveSection(); - testSubject.SetActiveSection(section2); - - // Assert - testSubject.ActiveSection.Should().Be(section2); - refresh1Called.Should().BeFalse(); - refresh2Called.Should().BeFalse(); - } - - [TestMethod] - public void VsSessionHost_SetActiveSection_TransferState() - { - // Arrange - VsSessionHost testSubject = this.CreateTestSubject(null); - ISectionController section = ConfigurableSectionController.CreateDefault(); - - // Act - testSubject.SetActiveSection(section); - - // Assert - testSubject.ActiveSection.ViewModel.State.Should().Be(stateManager.ManagedState); - } - - [TestMethod] - public void VsSessionHost_SetActiveSection_ChangeHost() - { - // Arrange - VsSessionHost testSubject = this.CreateTestSubject(null); - ISectionController section = ConfigurableSectionController.CreateDefault(); - - // Sanity - this.stepRunner.CurrentHost.Should().BeNull(); - - // Act - testSubject.SetActiveSection(section); - - // Assert - this.stepRunner.CurrentHost.Should().Be(section.ProgressHost); - } - - [TestMethod] - public void VsSessionHost_ClearActiveSection_ClearState() - { - // Arrange - VsSessionHost testSubject = this.CreateTestSubject(null); - ISectionController section = ConfigurableSectionController.CreateDefault(); - testSubject.SetActiveSection(section); - - // Act - testSubject.ClearActiveSection(); - - // Assert - testSubject.ActiveSection.Should().BeNull(); - section.ViewModel.State.Should().BeNull(); - } - - [TestMethod] - public void VsSessionHost_ActiveSectionChangedEvent() - { - // Arrange - VsSessionHost testSubject = this.CreateTestSubject(null); - ISectionController section = ConfigurableSectionController.CreateDefault(); - ISectionController otherSection = ConfigurableSectionController.CreateDefault(); - int changed = 0; - testSubject.ActiveSectionChanged += (o, e) => changed++; - - // Act (1st set) - testSubject.SetActiveSection(section); - - // Assert - changed.Should().Be(1, "ActiveSectionChanged event was expected to fire"); - - // Act (clear) - testSubject.ClearActiveSection(); - - // Assert - changed.Should().Be(2, "ActiveSectionChanged event was expected to fire"); - - // Act (2nd set) - testSubject.SetActiveSection(otherSection); - - // Assert - changed.Should().Be(3, "ActiveSectionChanged event was expected to fire"); - - // Act (clear) - testSubject.ClearActiveSection(); - - // Assert - changed.Should().Be(4, "ActiveSectionChanged event was expected to fire"); - - // Act (clear again) - testSubject.ClearActiveSection(); - - // Assert - changed.Should().Be(4, "ActiveSectionChanged event was not expected to fire, since already cleared"); - } - - [TestMethod] - public void VsSessionHost_SyncCommandFromActiveSectionDuringActiveSectionChanges() - { - // Arrange - VsSessionHost testSubject = this.CreateTestSubject(null); - ISectionController section = ConfigurableSectionController.CreateDefault(); - int syncCalled = 0; - this.stateManager.SyncCommandFromActiveSectionAction = () => syncCalled++; - - // Case 1: SetActiveSection - this.stateManager.ExpectActiveSection = true; - - // Act - testSubject.SetActiveSection(section); - - // Assert - syncCalled.Should().Be(1, "SyncCommandFromActiveSection wasn't called during section activation"); - - // Case 2: ClearActiveSection section - this.stateManager.ExpectActiveSection = false; - - // Act - testSubject.ClearActiveSection(); - - // Assert - syncCalled.Should().Be(2, "SyncCommandFromActiveSection wasn't called during section deactivation"); - } - - [TestMethod] - public void VsSessionHost_ResetBinding_NoOpenSolutionScenario() - { - // Arrange - var tracker = new ConfigurableActiveSolutionTracker(); - this.CreateTestSubject(tracker); - - this.stateManager.BoundProjectKey = "bla"; - this.stateManager.SetBoundProject(new Uri("http://localhost"), null, "bla"); - - // Sanity - this.stateManager.AssignedProjectKey.Should().Be("bla"); - this.stepRunner.AbortAllNumberOfCalls.Should().Be(0); - - // Act - tracker.SimulateActiveSolutionChanged(isSolutionOpen: false); - - // Assert - this.stepRunner.AbortAllNumberOfCalls.Should().Be(1); - this.stateManager.AssignedProjectKey.Should().BeNull(); - this.stateManager.BoundProjectKey.Should().BeNull("Expecting the key to be reset to null"); - } - - [TestMethod] - public void VsSessionHost_ResetBinding_BoundSolutionWithNoActiveSectionScenario() - { - // Arrange - var tracker = new ConfigurableActiveSolutionTracker(); - var testSubject = this.CreateTestSubject(tracker); - SetConfiguration(new BoundServerProject("solution", "bla", new ServerConnection.SonarQube(new Uri("http://bound"))), SonarLintMode.LegacyConnected); - this.stateManager.SetBoundProject(new Uri("http://bound"), null, "bla"); - - // Sanity - this.stateManager.AssignedProjectKey.Should().Be("bla"); - this.stepRunner.AbortAllNumberOfCalls.Should().Be(0); - - // Act (simulate solution opened event) - tracker.SimulateActiveSolutionChanged(isSolutionOpen: true); - - // Assert that nothing has changed (should defer all the work to when the section is connected) - this.stepRunner.AbortAllNumberOfCalls.Should().Be(1); - this.stateManager.AssignedProjectKey.Should().Be("bla"); - this.stateManager.BoundProjectKey.Should().BeNull("The key should only be set when there's active section to allow marking it once fetched all the projects"); - - // Act (set active section) - var section = ConfigurableSectionController.CreateDefault(); - bool refreshCalled = false; - section.RefreshCommand = new RelayCommand(c => refreshCalled = true); - testSubject.SetActiveSection(section); - - // Assert (section has refreshed, no further aborts were required) - this.stepRunner.AbortAllNumberOfCalls.Should().Be(1); - this.stateManager.BoundProjectKey.Should().Be("bla", "Key was not set, will not be able to mark project as bound after refresh"); - this.stateManager.AssignedProjectKey.Should().Be("bla"); - refreshCalled.Should().BeTrue("Expected the refresh command to be called"); - } - - [TestMethod] - public void VsSessionHost_ResetBinding_BoundSolutionWithActiveSectionScenario() - { - // Arrange - var tracker = new ConfigurableActiveSolutionTracker(); - var testSubject = this.CreateTestSubject(tracker); - this.stateManager.SetBoundProject(new Uri("http://bound"), "org1", "bla"); - SetConfiguration(new BoundServerProject("solution", "bla", new ServerConnection.SonarQube(new Uri("http://bound"))), SonarLintMode.LegacyConnected); - var section = ConfigurableSectionController.CreateDefault(); - bool refreshCalled = false; - section.RefreshCommand = new RelayCommand(c => refreshCalled = true); - testSubject.SetActiveSection(section); - - // Sanity - this.stateManager.AssignedProjectKey.Should().Be("bla"); - this.stepRunner.AbortAllNumberOfCalls.Should().Be(0); - - // Act (simulate solution opened event) - tracker.SimulateActiveSolutionChanged(isSolutionOpen: true); - - // Assert - this.stepRunner.AbortAllNumberOfCalls.Should().Be(1); - this.stateManager.BoundProjectKey.Should().Be("bla", "Key was not set, will not be able to mark project as bound after refresh"); - this.stateManager.AssignedProjectKey.Should().Be("bla"); - refreshCalled.Should().BeTrue("Expected the refresh command to be called"); - } - - [TestMethod] - public void VsSessionHost_ResetBinding_ErrorInReadingBinding() - { - // Arrange - var tracker = new ConfigurableActiveSolutionTracker(); - var testSubject = this.CreateTestSubject(tracker); - - this.stateManager.SetBoundProject(new Uri("http://bound"), null, "bla"); - SetConfiguration(new BoundServerProject("solution", "bla", new ServerConnection.SonarQube(new Uri("http://bound"))), SonarLintMode.LegacyConnected); - var section = ConfigurableSectionController.CreateDefault(); - testSubject.SetActiveSection(section); - - // Sanity - this.stateManager.AssignedProjectKey.Should().Be("bla"); - this.stepRunner.AbortAllNumberOfCalls.Should().Be(0); - - // Introduce an error - this.configProvider.GetConfigurationAction = () => { throw new Exception("boom"); }; - - // Act (i.e. simulate loading a different solution) - using (new AssertIgnoreScope()) // Ignore exception assert - { - tracker.SimulateActiveSolutionChanged(isSolutionOpen: true); - } - - // Assert - this.stateManager.AssignedProjectKey.Should().BeNull(); - } - - #endregion Tests - - #region Helpers - - private VsSessionHost CreateTestSubject(IActiveSolutionTracker tracker = null) - { - this.stateManager = new ConfigurableStateManager(); - var host = new VsSessionHost(stateManager, - this.stepRunner, - this.sonarQubeServiceMock.Object, - tracker ?? new ConfigurableActiveSolutionTracker(), - this.configProvider, - connectedModeWindowEventListener.Object, - Mock.Of()); - - this.stateManager.Host = host; - - return host; - } - - private void SetConfiguration(BoundServerProject project, SonarLintMode mode) - { - this.configProvider.ProjectToReturn = project; - this.configProvider.ModeToReturn = mode; - this.configProvider.FolderPathToReturn = "c:\\test\\"; - } - - #endregion Helpers - } -} diff --git a/src/Integration.UnitTests/Progress/ProgressNotificationListenerTests.cs b/src/Integration.UnitTests/Progress/ProgressNotificationListenerTests.cs deleted file mode 100644 index 375b837916..0000000000 --- a/src/Integration.UnitTests/Progress/ProgressNotificationListenerTests.cs +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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 Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Integration.Progress; -using SonarLint.VisualStudio.TestInfrastructure; - -namespace SonarLint.VisualStudio.Integration.UnitTests -{ - [TestClass] - public class ProgressNotificationListenerTests - { - [TestMethod] - public void ProgressNotificationListener_ArgChecks() - { - Exceptions.Expect(() => new ProgressNotificationListener(null, new Mock().Object)); - Exceptions.Expect(() => new ProgressNotificationListener(new ConfigurableProgressEvents(), null)); - } - - [TestMethod] - public void ProgressNotificationListener_RespondToStepExecutionChangedEvent() - { - // Arrange - var logger = new TestLogger(); - - var progressEvents = new ConfigurableProgressEvents(); - var testSubject = new ProgressNotificationListener(progressEvents, logger); - string message1 = "Hello world"; - string formattedMessage2 = "Bye bye"; - - // Step 1: no formatting - // Act - progressEvents.SimulateStepExecutionChanged(message1, 0); - - // Assert - logger.AssertOutputStrings(message1); - - // Step 2: same message as before (ignore) - // Act - progressEvents.SimulateStepExecutionChanged(message1, 0); - - // Assert - logger.AssertOutputStrings(message1); - - // Step 3: whitespace message - // Act - progressEvents.SimulateStepExecutionChanged(" \t", 0); - - // Assert - logger.AssertOutputStrings(message1); - - // Step 4: formatting - testSubject.MessageFormat = "XXX{0}YYY"; - // Act - progressEvents.SimulateStepExecutionChanged(formattedMessage2, 0); - - // Assert - logger.AssertOutputStrings(message1, "XXX" + formattedMessage2 + "YYY"); - - // Step 5: different message than the previous one - testSubject.MessageFormat = null; - // Act - progressEvents.SimulateStepExecutionChanged(message1, 0); - - // Assert - logger.AssertOutputStrings(message1, "XXX" + formattedMessage2 + "YYY", message1); - - // Step 6: dispose - testSubject.Dispose(); - // Act - progressEvents.SimulateStepExecutionChanged("123", 0); - - // Assert - logger.AssertOutputStrings(message1, "XXX" + formattedMessage2 + "YYY", message1); - } - } -} diff --git a/src/Integration.UnitTests/State/StateManagerTests.cs b/src/Integration.UnitTests/State/StateManagerTests.cs deleted file mode 100644 index bfc259f650..0000000000 --- a/src/Integration.UnitTests/State/StateManagerTests.cs +++ /dev/null @@ -1,650 +0,0 @@ -/* - * 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.Windows.Input; -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SonarLint.VisualStudio.Integration.Connection; -using SonarLint.VisualStudio.Integration.Resources; -using SonarLint.VisualStudio.Integration.State; -using SonarLint.VisualStudio.Integration.TeamExplorer; -using SonarLint.VisualStudio.Integration.WPF; -using SonarLint.VisualStudio.TestInfrastructure; -using SonarQube.Client.Models; - -namespace SonarLint.VisualStudio.Integration.UnitTests.State -{ - [TestClass] - public class StateManagerTests - { - #region Tests - - [TestMethod] - public void StateManager_ArgsCheck() - { - Exceptions.Expect(() => new StateManager(null, new TransferableVisualState())); - Exceptions.Expect(() => new StateManager(new ConfigurableHost(), null)); - } - - [TestMethod] - public void StateManager_SetProjectsUIThread_With_BoundProjectKey() - { - // Arrange - var section = ConfigurableSectionController.CreateDefault(); - ConfigurableUserNotification notifications = (ConfigurableUserNotification)section.UserNotifications; - ConfigurableHost host = new ConfigurableHost(); - StateManager testSubject = this.CreateTestSubject(host); - host.VisualStateManager = testSubject; - section.ViewModel.State = testSubject.ManagedState; - host.SetActiveSection(section); - - var connection1 = new ConnectionInformation(new Uri("http://127.0.0.1")); - var projects = new[] { new SonarQubeProject("project1", ""), new SonarQubeProject("project2", "") }; - - // Case 1 - projects does not contain BoundProjectKey - testSubject.BoundProjectKey = "missing_project"; - Action act = () => testSubject.SetProjects(connection1, projects); - - // Assert - act.Should().Throw(); - var message = notifications.AssertNotification(NotificationIds.FailedToFindBoundProjectKeyId); - message.Should().MatchRegex("\\[.+\\]\\(\\)"); // Contains the hyperlink syntax [text]() - notifications.AssertNotification(NotificationIds.FailedToFindBoundProjectKeyId, section.ReconnectCommand); - - // Case 2 - projects contains BoundProjectKey - testSubject.BoundProjectKey = "project1"; - testSubject.SetProjects(connection1, projects); - - // Assert - notifications.AssertNoNotification(NotificationIds.FailedToFindBoundProjectKeyId); - } - - [TestMethod] - public void StateManager_SetProjectsUIThread() - { - // Arrange - var section = ConfigurableSectionController.CreateDefault(); - ConfigurableUserNotification notifications = (ConfigurableUserNotification)section.UserNotifications; - ConfigurableHost host = new ConfigurableHost(); - StateManager testSubject = this.CreateTestSubject(host); - host.VisualStateManager = testSubject; - section.ViewModel.State = testSubject.ManagedState; - var connection1 = new ConnectionInformation(new Uri("http://127.0.0.1")); - var connection2 = new ConnectionInformation(new Uri("http://127.0.0.2")); - var projects = new[] { new SonarQubeProject("projectKey1", ""), new SonarQubeProject("projectKey2", "") }; - host.SetActiveSection(section); - ServerViewModel serverVM; - - // Act + Assert - // Case 1 - not connected to server (indicated by null) - section.UserNotifications.ShowNotificationError("message", NotificationIds.FailedToFindBoundProjectKeyId, null); - testSubject.SetProjects(connection1, null); - - notifications.AssertNoNotification(NotificationIds.FailedToFindBoundProjectKeyId); - VerifyConnectSectionViewModelIsNotConnected(section.ViewModel, connection1); - VerifyConnectSectionViewModelIsNotConnected(section.ViewModel, connection2); - VerifyConnectSectionViewModelHasNoBoundProjects(section.ViewModel); - - // Case 2 - connection1, empty project collection - section.UserNotifications.ShowNotificationError("message", NotificationIds.FailedToFindBoundProjectKeyId, null); - testSubject.SetProjects(connection1, new SonarQubeProject[0]); - - notifications.AssertNoNotification(NotificationIds.FailedToFindBoundProjectKeyId); - serverVM = VerifyConnectSectionViewModelIsConnectedAndHasNoProjects(section.ViewModel, connection1); - serverVM.ShowAllProjects.Should().BeTrue("Expected show all projects"); - VerifySectionCommands(section, serverVM); - VerifyConnectSectionViewModelIsNotConnected(section.ViewModel, connection2); - VerifyConnectSectionViewModelHasNoBoundProjects(section.ViewModel); - - // Case 3 - connection1, non-empty project collection - section.UserNotifications.ShowNotificationError("message", NotificationIds.FailedToFindBoundProjectKeyId, null); - testSubject.SetProjects(connection1, projects); - - notifications.AssertNoNotification(NotificationIds.FailedToFindBoundProjectKeyId); - serverVM = VerifyConnectSectionViewModelIsConnectedAndHasProjects(section.ViewModel, connection1, projects); - VerifySectionCommands(section, serverVM); - VerifyConnectSectionViewModelIsNotConnected(section.ViewModel, connection2); - VerifyConnectSectionViewModelHasNoBoundProjects(section.ViewModel); - serverVM.ShowAllProjects.Should().BeTrue("Expected show all projects to be true when adding new SonarQubeProjects"); - - // Case 4 - connection2, change projects - testSubject.SetProjects(connection1, projects); - section.UserNotifications.ShowNotificationError("message", NotificationIds.FailedToFindBoundProjectKeyId, null); - testSubject.SetProjects(connection2, projects); - - notifications.AssertNoNotification(NotificationIds.FailedToFindBoundProjectKeyId); - serverVM = VerifyConnectSectionViewModelIsConnectedAndHasProjects(section.ViewModel, connection1, projects); - VerifySectionCommands(section, serverVM); - serverVM = VerifyConnectSectionViewModelIsConnectedAndHasProjects(section.ViewModel, connection2, projects); - VerifySectionCommands(section, serverVM); - VerifyConnectSectionViewModelHasNoBoundProjects(section.ViewModel); - serverVM.ShowAllProjects.Should().BeTrue("Expected show all projects to be true when changing projects"); - - // Case 5 - connection1 & connection2, once detached (connected or not), are reset, changes still being tracked - host.ClearActiveSection(); - testSubject.SetProjects(connection1, projects); - testSubject.SetProjects(connection2, projects); - // Act - section.UserNotifications.ShowNotificationError("message", NotificationIds.FailedToFindBoundProjectKeyId, null); - host.SetActiveSection(section); - // Assert - notifications.AssertNotification(NotificationIds.FailedToFindBoundProjectKeyId); - serverVM = VerifyConnectSectionViewModelIsConnectedAndHasProjects(section.ViewModel, connection1, projects); - VerifySectionCommands(section, serverVM); - serverVM = VerifyConnectSectionViewModelIsConnectedAndHasProjects(section.ViewModel, connection2, projects); - VerifySectionCommands(section, serverVM); - VerifyConnectSectionViewModelHasNoBoundProjects(section.ViewModel); - } - - [TestMethod] - public void StateManager_SyncCommandFromActiveSection() - { - // Arrange - var section = ConfigurableSectionController.CreateDefault(); - ConfigurableHost host = new ConfigurableHost(); - StateManager testSubject = this.CreateTestSubject(host, section); - var connection1 = new ConnectionInformation(new Uri("http://127.0.0.1")); - var projects = new SonarQubeProject[] { new SonarQubeProject("projectKey1", ""), new SonarQubeProject("projectKey2", "") }; - testSubject.SetProjects(connection1, projects); - ServerViewModel serverVM = testSubject.ManagedState.ConnectedServers.Single(); - - // Case 1: has active section - host.SetActiveSection(section); - - // Act - testSubject.SyncCommandFromActiveSection(); - VerifySectionCommands(section, serverVM); - - // Case 2: has no active section - host.ClearActiveSection(); - - // Act - testSubject.SyncCommandFromActiveSection(); - VerifyNoCommands(serverVM); - - // Case 3: re-active - host.SetActiveSection(section); - - // Act - testSubject.SyncCommandFromActiveSection(); - VerifySectionCommands(section, serverVM); - } - - [TestMethod] - public void StateManager_ToggleShowAllProjectsCommand_DynamicText() - { - // Arrange - var section = ConfigurableSectionController.CreateDefault(); - ConfigurableHost host = new ConfigurableHost(); - StateManager testSubject = this.CreateTestSubject(host, section); - var connection1 = new ConnectionInformation(new Uri("http://127.0.0.1")); - var projects = new SonarQubeProject[] { new SonarQubeProject("projectKey1", ""), new SonarQubeProject("projectKey2", "") }; - testSubject.SetProjects(connection1, projects); - ServerViewModel serverVM = testSubject.ManagedState.ConnectedServers.Single(); - host.SetActiveSection(section); - testSubject.SyncCommandFromActiveSection(); - ContextualCommandViewModel toggleContextCmd = serverVM.Commands.First(x => x.InternalRealCommand.Equals(section.ToggleShowAllProjectsCommand)); - - // Case 1: No bound projects - serverVM.ShowAllProjects = true; - // Act + Assert - toggleContextCmd.DisplayText.Should().Be(Strings.HideUnboundProjectsCommandText, "Unexpected disabled context command text"); - - // Case 2: has bound projects - serverVM.ShowAllProjects = false; - - // Act + Assert - toggleContextCmd.DisplayText.Should().Be(Strings.ShowAllProjectsCommandText, "Unexpected context command text"); - } - - [TestMethod] - public void StateManager_BindCommand_DynamicText() - { - // Arrange - var section = ConfigurableSectionController.CreateDefault(); - ConfigurableHost host = new ConfigurableHost(); - StateManager testSubject = this.CreateTestSubject(host, section); - var connection1 = new ConnectionInformation(new Uri("http://127.0.0.1")); - var projects = new SonarQubeProject[] { new SonarQubeProject("projectKey", "") }; - testSubject.SetProjects(connection1, projects); - ProjectViewModel projectVM = testSubject.ManagedState.ConnectedServers.Single().Projects.Single(); - host.SetActiveSection(section); - testSubject.SyncCommandFromActiveSection(); - ContextualCommandViewModel bindCmd = projectVM.Commands.First(x => x.InternalRealCommand.Equals(section.BindCommand)); - - // Case 1: Bound - projectVM.IsBound = true; - // Act + Assert - bindCmd.DisplayText.Should().Be(Strings.SyncButtonText, "Unexpected disabled context command text"); - - // Case 2: Not bound - projectVM.IsBound = false; - - // Act + Assert - bindCmd.DisplayText.Should().Be(Strings.BindButtonText, "Unexpected context command text"); - } - - [TestMethod] - public void StateManager_ClearBoundProject() - { - // Arrange - var section = ConfigurableSectionController.CreateDefault(); - ConfigurableUserNotification notifications = (ConfigurableUserNotification)section.UserNotifications; - ConfigurableHost host = new ConfigurableHost(); - host.SetActiveSection(section); - StateManager testSubject = this.CreateTestSubject(host); - - section.UserNotifications.ShowNotificationError("message", NotificationIds.FailedToFindBoundProjectKeyId, null); - testSubject.ManagedState.ConnectedServers.Add(new ServerViewModel(new ConnectionInformation(new Uri("http://zzz1")))); - testSubject.ManagedState.ConnectedServers.Add(new ServerViewModel(new ConnectionInformation(new Uri("http://zzz2")))); - testSubject.ManagedState.ConnectedServers.ToList().ForEach(s => s.Projects.Add(new ProjectViewModel(s, new SonarQubeProject(Guid.NewGuid().ToString(), "")))); - var allProjects = testSubject.ManagedState.ConnectedServers.SelectMany(s => s.Projects).ToList(); - testSubject.SetBoundProject(new Uri("http://zzz1"), null, allProjects.First().Project.Key); - - // Sanity - testSubject.ManagedState.HasBoundProject.Should().BeTrue(); - - // Act - testSubject.ClearBoundProject(); - - // Assert - testSubject.ManagedState.HasBoundProject.Should().BeFalse(); - notifications.AssertNoNotification(NotificationIds.FailedToFindBoundProjectKeyId); - } - - [TestMethod] - public void StateManager_SetBoundProject() - { - // Arrange - var section = ConfigurableSectionController.CreateDefault(); - ConfigurableUserNotification notifications = (ConfigurableUserNotification)section.UserNotifications; - ConfigurableHost host = new ConfigurableHost(); - host.SetActiveSection(section); - StateManager testSubject = this.CreateTestSubject(host); - - section.UserNotifications.ShowNotificationError("message", NotificationIds.FailedToFindBoundProjectKeyId, null); - var conn = new ConnectionInformation(new Uri("http://xyz")) - { - Organization = new SonarQubeOrganization("org1", "org1 name") - }; - var projects = new[] { new SonarQubeProject("111", ""), new SonarQubeProject("222", "") }; - testSubject.SetProjects(conn, projects); - TransferableVisualState state = testSubject.ManagedState; - bool hasBoundProjectChanged = false; - state.PropertyChanged += (o, e) => - { - e.PropertyName.Should().Be(nameof(state.HasBoundProject)); - hasBoundProjectChanged = true; - }; - - // Act - testSubject.SetBoundProject(conn.ServerUri, "org1", "222"); - - // Assert - notifications.AssertNoNotification(NotificationIds.FailedToFindBoundProjectKeyId); - var serverVM = state.ConnectedServers.Single(); - var project0VM = serverVM.Projects.Single(p => p.Project == projects[0]); - var project1VM = serverVM.Projects.Single(p => p.Project == projects[1]); - project1VM.IsBound.Should().BeTrue(); - project0VM.IsBound.Should().BeFalse(); - testSubject.ManagedState.HasBoundProject.Should().BeTrue("Expected a bound project"); - hasBoundProjectChanged.Should().BeTrue("HasBoundProject expected to change"); - } - - [TestMethod] - public void StateManager_IsBusyChanged() - { - // Arrange - var section = ConfigurableSectionController.CreateDefault(); - ConfigurableHost host = new ConfigurableHost(); - StateManager testSubject = this.CreateTestSubject(host); - testSubject.IsBusyChanged += (s, isBusy) => section.ViewModel.IsBusy = isBusy; - - // Case 1: no active section -> no-op (i.e. no exception) - testSubject.IsBusy = true; - testSubject.ManagedState.IsBusy.Should().BeTrue(); - testSubject.IsBusy = false; - testSubject.ManagedState.IsBusy.Should().BeFalse(); - - // Case 2: has active section - host.SetActiveSection(section); - - // Sanity (!IsBusy) - section.ViewModel.IsBusy.Should().BeFalse(); - - // Act (IsBusy -> IsBusy) - testSubject.IsBusy = true; - - // Assert - section.ViewModel.IsBusy.Should().BeTrue(); - testSubject.ManagedState.IsBusy.Should().BeTrue(); - - // Act (!IsBusy -> !IsBusy) - testSubject.IsBusy = false; - - // Assert - section.ViewModel.IsBusy.Should().BeFalse(); - testSubject.ManagedState.IsBusy.Should().BeFalse(); - - // Dispose (should stop updated the view model) - testSubject.Dispose(); - testSubject.IsBusy = true; - - // Assert - section.ViewModel.IsBusy.Should().BeFalse(); - testSubject.ManagedState.IsBusy.Should().BeTrue(); - } - - [TestMethod] - public void StateManager_BindingStateChanged() - { - // Arrange - ConfigurableHost host = new ConfigurableHost(); - StateManager testSubject = this.CreateTestSubject(host); - var countOnBindingStateChangeFired = 0; - testSubject.BindingStateChanged += (sender, e) => countOnBindingStateChangeFired++; - - testSubject.ManagedState.ConnectedServers.Add(new ServerViewModel(new ConnectionInformation(new Uri("http://zzz1")))); - testSubject.ManagedState.ConnectedServers.ToList().ForEach(s => s.Projects.Add(new ProjectViewModel(s, new SonarQubeProject(Guid.NewGuid().ToString(), "")))); - var allProjects = testSubject.ManagedState.ConnectedServers.SelectMany(s => s.Projects).ToList(); - - // Sanity - countOnBindingStateChangeFired.Should().Be(0); - - // Act - // Note: creating new project and connection objects with the same uri/key as above - testSubject.SetBoundProject(new Uri("http://zzz1"), null, allProjects.First().Project.Key); - - // Assert - countOnBindingStateChangeFired.Should().Be(1); - - // Act - testSubject.ClearBoundProject(); - - // Assert - countOnBindingStateChangeFired.Should().Be(2); - } - - [TestMethod] - public void ClearBoundProject_RaisesBindingStateChangedWithExpectedArgs() - { - // Arrange - ConfigurableHost host = new ConfigurableHost(); - var testSubject = this.CreateTestSubject(host); - BindingStateEventArgs actualArgs = null; - testSubject.BindingStateChanged += (sender, e) => actualArgs = e; - - // Act - testSubject.ClearBoundProject(); - - // Assert - actualArgs.Should().NotBeNull(); - actualArgs.IsBindingCleared.Should().BeTrue(); - } - - [TestMethod] - public void SetBoundProject_RaisesBindingStateChangedWithExpectedArgs() - { - // Arrange - ConfigurableHost host = new ConfigurableHost(); - var testSubject = this.CreateTestSubject(host); - BindingStateEventArgs actualArgs = null; - testSubject.BindingStateChanged += (sender, e) => actualArgs = e; - - testSubject.ManagedState.ConnectedServers.Add(new ServerViewModel(new ConnectionInformation(new Uri("http://zzz1")))); - testSubject.ManagedState.ConnectedServers.ToList().ForEach(s => s.Projects.Add(new ProjectViewModel(s, new SonarQubeProject(Guid.NewGuid().ToString(), "")))); - var allProjects = testSubject.ManagedState.ConnectedServers.SelectMany(s => s.Projects).ToList(); - - // Act - testSubject.SetBoundProject(new Uri("http://zzz1"), null, allProjects.First().Project.Key); - - // Assert - actualArgs.Should().NotBeNull(); - actualArgs.IsBindingCleared.Should().BeFalse(); - } - - [TestMethod] - public void StateManager_IsConnected() - { - // Arrange - ConfigurableHost host = new ConfigurableHost(); - StateManager testSubject = this.CreateTestSubject(host); - - // Sanity - testSubject.IsConnected.Should().BeFalse(); - - // Act (connect) - testSubject.SetProjects(new ConnectionInformation(new Uri("http://qwerty")), new SonarQubeProject[0]); - - // Assert - testSubject.IsConnected.Should().BeTrue(); - - // Act (disconnect) - testSubject.SetProjects(new ConnectionInformation(new Uri("http://qwerty")), null); - - // Assert - testSubject.IsConnected.Should().BeFalse(); - } - - [TestMethod] - public void StateManager_GetConnectedServers() - { - // Arrange - ConfigurableHost host = new ConfigurableHost(); - StateManager testSubject = this.CreateTestSubject(host); - var connection1 = new ConnectionInformation(new Uri("http://conn1")); - var connection2 = new ConnectionInformation(new Uri("http://conn2")); - - // Sanity - testSubject.GetConnectedServers().Should().BeEmpty(); - - // Act (connect) - testSubject.SetProjects(connection1, new SonarQubeProject[0]); - - // Assert - CollectionAssert.AreEquivalent(new[] { connection1 } , testSubject.GetConnectedServers().ToArray()); - - // Act (connect another one) - testSubject.SetProjects(connection2, new SonarQubeProject[0]); - - // Assert - CollectionAssert.AreEquivalent(new[] { connection1, connection2 }, testSubject.GetConnectedServers().ToArray()); - - // Act (disconnect) - testSubject.SetProjects(connection1, null); - testSubject.SetProjects(connection2, null); - - // Assert - testSubject.GetConnectedServers().Should().BeEmpty(); - connection1.IsDisposed.Should().BeTrue("Leaking connections?"); - connection2.IsDisposed.Should().BeTrue("Leaking connections?"); - } - - [TestMethod] - public void StateManager_Dispose() - { - // Arrange - ConfigurableHost host = new ConfigurableHost(); - StateManager testSubject = this.CreateTestSubject(host); - var connection1 = new ConnectionInformation(new Uri("http://conn1")); - testSubject.SetProjects(connection1, new SonarQubeProject[0]); - - // Act - testSubject.Dispose(); - - // Assert - connection1.IsDisposed.Should().BeTrue("Leaking connections?"); - } - - [TestMethod] - public void HasSharedBinding_ProxyForManagedState() - { - var host = new ConfigurableHost(); - var testSubject = CreateTestSubject(host); - - testSubject.HasSharedBinding = true; - testSubject.HasSharedBinding.Should().BeTrue(); - testSubject.ManagedState.HasSharedBinding.Should().BeTrue(); - - testSubject.HasSharedBinding = false; - testSubject.HasSharedBinding.Should().BeFalse(); - testSubject.ManagedState.HasSharedBinding.Should().BeFalse(); - - testSubject.ManagedState.HasSharedBinding = true; - testSubject.HasSharedBinding.Should().BeTrue(); - } - - [TestMethod] - public void ResetConnectionConfiguration_CreatesNewConfiguration() - { - var host = new ConfigurableHost(); - var testSubject = CreateTestSubject(host); - - var initialConfiguration = testSubject.ManagedState.ConnectConfiguration; - testSubject.ResetConnectionConfiguration(); - - testSubject.ManagedState.ConnectConfiguration.Should().NotBeSameAs(initialConfiguration); - testSubject.ManagedState.ConnectConfiguration.Should().BeEquivalentTo(new ConnectConfiguration()); - } - - #endregion Tests - - #region Helpers - - private StateManager CreateTestSubject(ConfigurableHost host, ConfigurableSectionController section = null) - { - var threadHandling = new NoOpThreadHandler(); - var testSubject = new StateManager(host, new TransferableVisualState(threadHandling), threadHandling); - - if (section != null) - { - section.ViewModel.State = testSubject.ManagedState; - } - - return testSubject; - } - - private static void VerifySectionCommands(ISectionController section, ServerViewModel serverVM) - { - AssertExpectedNumberOfCommands(serverVM.Commands, 3); - VerifyServerViewModelCommand(serverVM, section.RefreshCommand, fixedContext: serverVM, hasIcon: true); - VerifyServerViewModelCommand(serverVM, section.BrowseToUrlCommand, fixedContext: serverVM.ConnectionInformation.ServerUri.ToString(), hasIcon: true); - VerifyServerViewModelCommand(serverVM, section.ToggleShowAllProjectsCommand, fixedContext: serverVM, hasIcon: false); - - foreach (ProjectViewModel project in serverVM.Projects) - { - AssertExpectedNumberOfCommands(project.Commands, 2); - VerifyProjectViewModelCommand(project, section.BindCommand, fixedContext: project, hasIcon: true); - VerifyProjectViewModelCommand(project, section.BrowseToProjectDashboardCommand, fixedContext: project, hasIcon: true); - } - } - - private static void VerifyNoCommands(ServerViewModel serverVM) - { - AssertExpectedNumberOfCommands(serverVM.Commands, 0); - serverVM.Projects.Sum(p => p.Commands.Count).Should().Be(0, "Not expecting any project commands"); - } - - private static void VerifyServerViewModelCommand(ServerViewModel serverVM, ICommand internalCommand, object fixedContext, bool hasIcon) - { - ContextualCommandViewModel commandVM = AssertCommandExists(serverVM.Commands, internalCommand); - commandVM.DisplayText.Should().NotBeNull("DisplayText expected"); - commandVM.InternalFixedContext.Should().Be(fixedContext, "The fixed context is incorrect"); - if (hasIcon) - { - commandVM.Icon.Should().NotBeNull("Icon expected"); - commandVM.Icon.Moniker.Should().NotBeNull("Icon moniker expected"); - } - else - { - commandVM.Icon.Should().BeNull("Icon not expected"); - } - } - - private static void VerifyProjectViewModelCommand(ProjectViewModel projectVM, ICommand internalCommand, object fixedContext, bool hasIcon) - { - ContextualCommandViewModel commandVM = AssertCommandExists(projectVM.Commands, internalCommand); - commandVM.DisplayText.Should().NotBeNull("DisplayText expected"); - commandVM.InternalFixedContext.Should().Be(fixedContext, "The fixed context is incorrect"); - commandVM.InternalRealCommand.Should().Be(internalCommand, "Unexpected command"); - if (hasIcon) - { - commandVM.Icon.Should().NotBeNull("Icon expected"); - commandVM.Icon.Moniker.Should().NotBeNull("Icon moniker expected"); - } - else - { - commandVM.Icon.Should().BeNull("Icon not expected"); - } - } - - private static ContextualCommandViewModel AssertCommandExists(ContextualCommandsCollection commands, ICommand realCommand) - { - ContextualCommandViewModel[] commandsArr = commands.Where(c => c.InternalRealCommand.Equals(realCommand)).ToArray(); - AssertExpectedNumberOfCommands(commandsArr, 1); - return commandsArr[0]; - } - - private static void AssertExpectedNumberOfCommands(IEnumerable commands, int expectedCount) - { - commands.Should().HaveCount(expectedCount, "Unexpected commands. All command: {0}", string.Join(", ", commands.Select(c => c.DisplayText))); - } - - private static ServerViewModel VerifyConnectSectionViewModelIsConnectedAndHasProjects(IConnectSectionViewModel vm, ConnectionInformation connection, SonarQubeProject[] projects) - { - ServerViewModel serverVM = VerifyConnectSectionViewModelIsConnected(vm, connection); - CollectionAssert.AreEquivalent(projects, serverVM.Projects.Select(p => p.Project).ToArray(), "Unexpected projects for server {0}", connection.ServerUri); - - return serverVM; - } - - private static ServerViewModel VerifyConnectSectionViewModelIsConnectedAndHasNoProjects(IConnectSectionViewModel vm, ConnectionInformation connection) - { - ServerViewModel serverVM = VerifyConnectSectionViewModelIsConnected(vm, connection); - serverVM.Projects.Should().BeEmpty("Unexpected number of projects"); - - return serverVM; - } - - private static void VerifyConnectSectionViewModelIsNotConnected(IConnectSectionViewModel vm, ConnectionInformation connection) - { - ServerViewModel serverVM = vm.State?.ConnectedServers?.SingleOrDefault(s => s.Url == connection.ServerUri); - serverVM.Should().BeNull("Should not find server view model for {0}", connection.ServerUri); - } - - private static ServerViewModel VerifyConnectSectionViewModelIsConnected(IConnectSectionViewModel vm, ConnectionInformation connection) - { - ServerViewModel serverVM = vm.State?.ConnectedServers?.SingleOrDefault(s => s.Url == connection.ServerUri); - serverVM.Should().NotBeNull("Could not find server view model for {0}", connection.ServerUri); - - return serverVM; - } - - private static void VerifyConnectSectionViewModelHasNoBoundProjects(IConnectSectionViewModel vm) - { - vm.State.HasBoundProject.Should().BeFalse("View model should not have any bound projects"); - } - - #endregion Helpers - } -} diff --git a/src/Integration.UnitTests/State/TransferableVisualStateTests.cs b/src/Integration.UnitTests/State/TransferableVisualStateTests.cs deleted file mode 100644 index dfe0e7b620..0000000000 --- a/src/Integration.UnitTests/State/TransferableVisualStateTests.cs +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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 FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SonarLint.VisualStudio.Integration.Connection; -using SonarLint.VisualStudio.Integration.State; -using SonarLint.VisualStudio.Integration.TeamExplorer; -using SonarLint.VisualStudio.TestInfrastructure; -using SonarQube.Client.Models; - -namespace SonarLint.VisualStudio.Integration.UnitTests.State -{ - [TestClass] - public class TransferableVisualStateTests - { - [TestInitialize] - public void TestInitialize() - { - ThreadHelper.SetCurrentThreadAsUIThread(); - } - - [TestMethod] - public void TransferableVisualState_DefaultState() - { - // Arrange - var testSubject = new TransferableVisualState(); - - // Assert - testSubject.HasBoundProject.Should().BeFalse(); - testSubject.IsBusy.Should().BeFalse(); - testSubject.ConnectedServers.Should().NotBeNull(); - testSubject.ConnectedServers.Should().BeEmpty(); - testSubject.ConnectConfiguration.Should().BeEquivalentTo(new ConnectConfiguration()); - testSubject.HasSharedBinding.Should().BeFalse(); - } - - [TestMethod] - public void TransferableVisualState_BoundProjectManagement() - { - // Arrange - var testSubject = new TransferableVisualState(); - var server = new ServerViewModel(new ConnectionInformation(new System.Uri("http://server"))); - var project1 = new ProjectViewModel(server, new SonarQubeProject("", "")); - var project2 = new ProjectViewModel(server, new SonarQubeProject("", "")); - - // Act (bind to something) - testSubject.SetBoundProject(project1); - - // Assert - testSubject.HasBoundProject.Should().BeTrue(); - project1.IsBound.Should().BeTrue(); - project2.IsBound.Should().BeFalse(); - server.ShowAllProjects.Should().BeFalse(); - - // Act (bind to something else) - testSubject.SetBoundProject(project2); - - // Assert - testSubject.HasBoundProject.Should().BeTrue(); - project1.IsBound.Should().BeFalse(); - project2.IsBound.Should().BeTrue(); - server.ShowAllProjects.Should().BeFalse(); - - // Act(clear binding) - testSubject.ClearBoundProject(); - - // Assert - testSubject.HasBoundProject.Should().BeFalse(); - project1.IsBound.Should().BeFalse(); - project2.IsBound.Should().BeFalse(); - server.ShowAllProjects.Should().BeTrue(); - } - } -} diff --git a/src/Integration.UnitTests/TeamExplorer/ProjectViewModelTests.cs b/src/Integration.UnitTests/TeamExplorer/ProjectViewModelTests.cs deleted file mode 100644 index 69bbbcd73c..0000000000 --- a/src/Integration.UnitTests/TeamExplorer/ProjectViewModelTests.cs +++ /dev/null @@ -1,124 +0,0 @@ -/* - * 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.Globalization; -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SonarLint.VisualStudio.Integration.Resources; -using SonarLint.VisualStudio.Integration.TeamExplorer; -using SonarQube.Client.Models; -using SonarLint.VisualStudio.TestInfrastructure; - -namespace SonarLint.VisualStudio.Integration.UnitTests.TeamExplorer -{ - [TestClass] - public class ProjectViewModelTests - { - [TestMethod] - public void ProjectViewModel_Ctor_NullArgumentChecks() - { - Exceptions.Expect(() => - { - new ProjectViewModel(null, new SonarQubeProject("", "")); - }); - - Exceptions.Expect(() => - { - new ProjectViewModel(new ServerViewModel(new ConnectionInformation(new Uri("http://www.com"))), null); - }); - } - - [TestMethod] - public void ProjectViewModel_Ctor() - { - // Arrange - var projectInfo = new SonarQubeProject("P1", "Project1"); - var serverVM = CreateServerViewModel(); - - // Act - var viewModel = new ProjectViewModel(serverVM, projectInfo); - - // Assert - viewModel.IsBound.Should().BeFalse(); - viewModel.Key.Should().Be(projectInfo.Key); - viewModel.ProjectName.Should().Be(projectInfo.Name); - viewModel.Project.Should().Be(projectInfo); - viewModel.Owner.Should().Be(serverVM); - } - - [TestMethod] - public void ProjectViewModel_ToolTipProjectName_RespectsIsBound() - { - // Arrange - var projectInfo = new SonarQubeProject("P1", "Project1"); -#pragma warning disable IDE0017 // Simplify object initialization - var viewModel = new ProjectViewModel(CreateServerViewModel(), projectInfo); -#pragma warning restore IDE0017 // Simplify object initialization - - // Test Case 1: When project is bound, should show message with 'bound' marker - // Act - viewModel.IsBound = true; - - // Assert - StringAssert.Contains(viewModel.ToolTipProjectName, viewModel.ProjectName, "ToolTip message should include the project name"); - viewModel.ToolTipProjectName.Should().NotBe(viewModel.ProjectName, "ToolTip message should also indicate that the project is 'bound'"); - - // Test Case 2: When project is NOT bound, should show project name only - // Act - viewModel.IsBound = false; - - // Assert - viewModel.ToolTipProjectName.Should().Be(viewModel.ProjectName, "ToolTip message should be exactly the same as the project name"); - } - - [TestMethod] - public void ProjectViewModel_AutomationName() - { - // Arrange - var projectInfo = new SonarQubeProject("P1", "Project1"); - var testSubject = new ProjectViewModel(CreateServerViewModel(), projectInfo); - - var expectedNotBound = projectInfo.Name; - var expectedBound = string.Format(CultureInfo.CurrentCulture, Strings.AutomationProjectBoundDescription, projectInfo.Name); - - // Test case 1: bound - // Act - testSubject.IsBound = true; - var actualBound = testSubject.AutomationName; - - // Assert - actualBound.Should().Be(expectedBound, "Unexpected bound SonarQube project description"); - - // Test case 2: not bound - // Act - testSubject.IsBound = false; - var actualNotBound = testSubject.AutomationName; - - // Assert - actualNotBound.Should().Be(expectedNotBound, "Unexpected unbound SonarQube project description"); - } - - private static ServerViewModel CreateServerViewModel() - { - return new ServerViewModel(new ConnectionInformation(new Uri("http://123"))); - } - } -} diff --git a/src/Integration.UnitTests/TeamExplorer/ServerViewModelTests.cs b/src/Integration.UnitTests/TeamExplorer/ServerViewModelTests.cs deleted file mode 100644 index d626b46083..0000000000 --- a/src/Integration.UnitTests/TeamExplorer/ServerViewModelTests.cs +++ /dev/null @@ -1,156 +0,0 @@ -/* - * 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.Globalization; -using System.Linq; -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SonarLint.VisualStudio.Integration.Resources; -using SonarLint.VisualStudio.Integration.TeamExplorer; -using SonarQube.Client.Models; -using SonarLint.VisualStudio.TestInfrastructure; - -namespace SonarLint.VisualStudio.Integration.UnitTests.TeamExplorer -{ - [TestClass] - public class ServerViewModelTests - { - [TestMethod] - public void ServerViewModel_Ctor_NullArgumentChecks() - { - var connInfo = new ConnectionInformation(new Uri("http://localhost")); - - Exceptions.Expect(() => - { - new ServerViewModel(null); - }); - } - - [TestMethod] - public void ServerViewModel_Ctor() - { - // Arrange - var connInfo = new ConnectionInformation(new Uri("https://myawesomeserver:1234/")); - IEnumerable projects = new[] - { - new SonarQubeProject("1", "Project1"), - new SonarQubeProject("2", "Project2"), - new SonarQubeProject("3", "Project3"), - new SonarQubeProject("4", "Project4") - }; - string[] projectKeys = projects.Select(x => x.Key).ToArray(); - - // Case 0: default constructed state - // Act - var emptyViewModel = new ServerViewModel(connInfo); - - // Assert - emptyViewModel.IsExpanded.Should().BeTrue(); - emptyViewModel.ShowAllProjects.Should().BeFalse(); - - // Case 1, projects with default IsExpanded value - // Act - var viewModel = new ServerViewModel(connInfo); - viewModel.SetProjects(projects); - - // Assert - string[] vmProjectKeys = viewModel.Projects.Select(x => x.Key).ToArray(); - - viewModel.ShowAllProjects.Should().BeTrue(); - viewModel.IsExpanded.Should().BeTrue(); - viewModel.Url.Should().Be(connInfo.ServerUri); - CollectionAssert.AreEqual( - expected: projectKeys, - actual: vmProjectKeys, - message: $"VM projects [{string.Join(", ", vmProjectKeys)}] do not match input projects [{string.Join(", ", projectKeys)}]" - ); - - // Case 2, null projects with non default IsExpanded value - // Act - var viewModel2 = new ServerViewModel(connInfo, isExpanded: false); - - // Assert - viewModel2.Projects.Should().BeEmpty("Not expecting projects"); - viewModel2.IsExpanded.Should().BeFalse(); - } - - [TestMethod] - public void ServerViewModel_SetProjects() - { - // Arrange - var connInfo = new ConnectionInformation(new Uri("https://myawesomeserver:1234/")); - var viewModel = new ServerViewModel(connInfo); - IEnumerable projects = new[] - { - new SonarQubeProject("1", "Project3"), - new SonarQubeProject("2", "Project2"), - new SonarQubeProject("3", "project1"), - }; - string[] expectedOrderedProjectNames = projects.Select(p => p.Name).OrderBy(n => n, StringComparer.CurrentCulture).ToArray(); - - // Act - viewModel.SetProjects(projects); - - // Assert - string[] actualProjectNames = viewModel.Projects.Select(p => p.Project.Name).OrderBy(n => n, StringComparer.CurrentCulture).ToArray(); - CollectionAssert.AreEqual( - expectedOrderedProjectNames, - actualProjectNames, - message: $"VM projects [{string.Join(", ", actualProjectNames)}] do not match the expected projects [{string.Join(", ", expectedOrderedProjectNames)}]" - ); - - // Act again - var newProject = new SonarQubeProject("", ""); - viewModel.SetProjects(new[] { newProject }); - - // Assert that the collection was replaced with the new one - viewModel.Projects.SingleOrDefault()?.Project.Should().Be(newProject, "Expected a single project to be present"); - } - - [TestMethod] - public void ServerViewModel_AutomationName() - { - // Arrange - var connInfo = new ConnectionInformation(new Uri("https://myawesomeserver:1234/")); - var testSubject = new ServerViewModel(connInfo); - var projects = new[] { new SonarQubeProject("P", "A Project") }; - - var expectedProjects = string.Format(CultureInfo.CurrentCulture, Strings.AutomationServerDescription, connInfo.ServerUri); - var expectedNoProjects = string.Format(CultureInfo.CurrentCulture, Strings.AutomationServerNoProjectsDescription, connInfo.ServerUri); - - // Test case 1: no projects - // Act - var actualNoProjects = testSubject.AutomationName; - - // Assert - actualNoProjects.Should().Be(expectedNoProjects, "Unexpected description of SonarQube server without projects"); - - // Test case 2: projects - // Act - testSubject.SetProjects(projects); - var actualProjects = testSubject.AutomationName; - - // Assert - actualProjects.Should().Be(expectedProjects, "Unexpected description of SonarQube server with projects"); - } - } -} diff --git a/src/Integration.UnitTests/WPF/ContextualCommandViewModelTests.cs b/src/Integration.UnitTests/WPF/ContextualCommandViewModelTests.cs deleted file mode 100644 index 662cfa007d..0000000000 --- a/src/Integration.UnitTests/WPF/ContextualCommandViewModelTests.cs +++ /dev/null @@ -1,214 +0,0 @@ -/* - * 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.ComponentModel; -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SonarLint.VisualStudio.Integration.WPF; -using SonarLint.VisualStudio.TestInfrastructure; - -namespace SonarLint.VisualStudio.Integration.UnitTests.WPF -{ - [TestClass] - public class ContextualCommandViewModelTests - { - [TestMethod] - public void ContextualCommandViewModel_Ctor_NullArgChecks() - { - var command = new RelayCommand(() => { }); - ContextualCommandViewModel suppressAnalysisWarning; - Exceptions.Expect(() => - { - suppressAnalysisWarning = new ContextualCommandViewModel(null, command); - }); - } - - [TestMethod] - public void ContextualCommandViewModel_CommandInvocation() - { - // Arrange - bool canExecute = false; - bool executed = false; - var realCommand = new RelayCommand( - (state) => - { - state.Should().Be(this); - executed = true; - }, - (state) => - { - state.Should().Be(this); - return canExecute; - }); - var testSubject = new ContextualCommandViewModel(this, realCommand); - - // Sanity - testSubject.Command.Should().NotBeNull(); - testSubject.InternalRealCommand.Should().Be(realCommand); - - // Case 1: Can't execute - canExecute = false; - // Act - testSubject.Command.CanExecute(null).Should().BeFalse("CanExecute wasn't called as expected"); - - // Case 2: Can execute - canExecute = true; - - // Act - testSubject.Command.CanExecute(null).Should().BeTrue("CanExecute wasn't called as expected"); - - // Case 3: Execute - // Act - testSubject.Command.Execute(null); - executed.Should().BeTrue("Execute wasn't called as expected"); - } - - [TestMethod] - public void ContextualCommandViewModel_DisplayText() - { - // Arrange - var context = new object(); - var command = new RelayCommand(() => { }); - var testSubject = new ContextualCommandViewModel(context, command); - - using (var tracker = new PropertyChangedTracker(testSubject)) - { - // Case 1: null - // Act + Assert - testSubject.DisplayText.Should().BeNull("Expected display text to return null when not set"); - - // Case 2: static - testSubject.DisplayText = "foobar9000"; - // Act + Assert - testSubject.DisplayText.Should().Be("foobar9000", "Unexpected static display text"); - tracker.AssertPropertyChangedRaised(nameof(testSubject.DisplayText), 1); - - // Case 3: dynamic - var funcInvoked = false; - Func func = x => - { - funcInvoked = true; - return "1234"; - }; - testSubject.SetDynamicDisplayText(func); - // Act + Assert - testSubject.DisplayText.Should().Be("1234", "Unexpected dynamic display text"); - funcInvoked.Should().BeTrue("Dynamic display text function was not invoked"); - tracker.AssertPropertyChangedRaised(nameof(testSubject.DisplayText), 2); - } - - // Case 4: dynamic - null exception - Exceptions.Expect(() => testSubject.SetDynamicDisplayText(null)); - } - - [TestMethod] - public void ContextualCommandViewModel_Icon() - { - // Arrange - var context = new object(); - var command = new RelayCommand(() => { }); - var testSubject = new ContextualCommandViewModel(context, command); - - using (var tracker = new PropertyChangedTracker(testSubject)) - { - // Case 1: null - // Act + Assert - testSubject.Icon.Should().BeNull("Expected icon to return null when not set"); - - // Case 2: static - var staticIcon = new IconViewModel(null); - testSubject.Icon = staticIcon; - // Act + Assert - testSubject.Icon.Should().Be(staticIcon, "Unexpected static icon"); - tracker.AssertPropertyChangedRaised(nameof(testSubject.Icon), 1); - - // Case 3: dynamic - var dynamicIcon = new IconViewModel(null); - var funcInvoked = false; - Func func = x => - { - funcInvoked = true; - return dynamicIcon; - }; - testSubject.SetDynamicIcon(func); - // Act + Assert - testSubject.Icon.Should().Be(dynamicIcon, "Unexpected dynamic icon"); - funcInvoked.Should().BeTrue("Dynamic icon function was not invoked"); - tracker.AssertPropertyChangedRaised(nameof(testSubject.Icon), 2); - } - - // Case 4: dynamic - null exception - Exceptions.Expect(() => testSubject.SetDynamicIcon(null)); - } - - private sealed class PropertyChangedTracker : IDisposable - { - private readonly INotifyPropertyChanged subject; - private readonly IDictionary trackingDictionary = new Dictionary(); - - public PropertyChangedTracker(INotifyPropertyChanged subject) - { - this.subject = subject; - this.subject.PropertyChanged += this.OnPropertyChanged; - } - - public void AssertPropertyChangedRaised(string propertyName, int count) - { - (count > 0 || this.trackingDictionary.ContainsKey(propertyName)).Should().BeTrue($"PropertyChanged was not raised for '{propertyName}'"); - this.trackingDictionary[propertyName].Should().Be(count, "Unexpected number of PropertyChanged events raised"); - } - - private void OnPropertyChanged(object sender, PropertyChangedEventArgs e) - { - if (!this.trackingDictionary.ContainsKey(e.PropertyName)) - { - this.trackingDictionary[e.PropertyName] = 0; - } - this.trackingDictionary[e.PropertyName]++; - } - - #region IDisposable - - private bool isDisposed; - - private void Dispose(bool disposing) - { - if (!isDisposed) - { - if (disposing) - { - this.subject.PropertyChanged -= this.OnPropertyChanged; - } - - isDisposed = true; - } - } - - public void Dispose() - { - Dispose(true); - } - - #endregion IDisposable - } - } -} diff --git a/src/Integration.UnitTests/WPF/ContextualCommandsCollectionTests.cs b/src/Integration.UnitTests/WPF/ContextualCommandsCollectionTests.cs deleted file mode 100644 index 21bdd3be91..0000000000 --- a/src/Integration.UnitTests/WPF/ContextualCommandsCollectionTests.cs +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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.ComponentModel; -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SonarLint.VisualStudio.Integration.WPF; - -namespace SonarLint.VisualStudio.Integration.UnitTests.WPF -{ - [TestClass] - public class ContextualCommandsCollectionTests - { - [TestMethod] - public void ContextualCommandsCollection_HasCommands() - { - // Arrange - var testSubject = new ContextualCommandsCollection(); - - // Case 1: no commands - // Act + Assert - testSubject.HasCommands.Should().BeFalse(); - - // Case 2: has commands - testSubject.Add(new ContextualCommandViewModel(this, new RelayCommand(()=> { }))); - // Act + Assert - testSubject.HasCommands.Should().BeTrue(); - } - - [TestMethod] - public void ContextualCommandsCollection_HasCommands_ChangedOnCollectionChange() - { - // Arrange - var testSubject = new ContextualCommandsCollection(); - int hasCommandsChangedCounter = 0; - ((INotifyPropertyChanged)testSubject).PropertyChanged += (o, e) => - { - if (e.PropertyName == "HasCommands") - { - hasCommandsChangedCounter++; - } - }; - - // Case 1: Add command - var cmd1 = new ContextualCommandViewModel(this, new RelayCommand(() => { })); - var cmd2 = new ContextualCommandViewModel(this, new RelayCommand(() => { })); - // Act - testSubject.Add(cmd1); - testSubject.Add(cmd2); - - // Assert - hasCommandsChangedCounter.Should().Be(2, "Adding a command should update HasCommands"); - - // Case 2: Remove command - // Act - testSubject.Remove(cmd1); - // Assert - hasCommandsChangedCounter.Should().Be(3, "Adding a command should update HasCommands"); - - // Case 3: Update command - // Act - testSubject[0] = cmd1; - // Assert - hasCommandsChangedCounter.Should().Be(4, "Adding a command should update HasCommands"); - } - } -} diff --git a/src/Integration.UnitTests/WPF/IsValidOrganizationKeyConverterTests.cs b/src/Integration.UnitTests/WPF/IsValidOrganizationKeyConverterTests.cs deleted file mode 100644 index bd5037b5f8..0000000000 --- a/src/Integration.UnitTests/WPF/IsValidOrganizationKeyConverterTests.cs +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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.Globalization; -using System.Windows; -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SonarLint.VisualStudio.Integration.Connection.UI; -using SonarLint.VisualStudio.Integration.WPF; - -namespace SonarLint.VisualStudio.Integration.UnitTests.WPF -{ - [TestClass] - public class IsValidOrganizationKeyConverterTests - { - [TestMethod] - public void Convert_NonBoolTargetType_ThrowsArgumentException() - { - var converter = new IsValidOrganisationKeyConverter(); - - Action act = () => converter.Convert("valid input", typeof(object), null, CultureInfo.CurrentCulture); - - act.Should().ThrowExactly().And.ParamName.Should().Be("targetType"); - } - - [TestMethod] - public void Convert_NonTextInput_ThrowsArgumentException() - { - var converter = new IsValidOrganisationKeyConverter(); - - var notAString = new object(); - Action act = () => converter.Convert(notAString, typeof(bool), null, CultureInfo.CurrentCulture); - - act.Should().ThrowExactly().And.ParamName.Should().Be("value"); - } - - [TestMethod] - public void Convert_NullInputString_ReturnsFalse() - { - var converter = new IsValidOrganisationKeyConverter(); - - var result = converter.Convert((string)null, typeof(bool), null, CultureInfo.CurrentUICulture); - - result.Should().Be(false); - } - - [TestMethod] - public void Convert_WhitespaceInput_ReturnsFalse() - { - var converter = new IsValidOrganisationKeyConverter(); - - var result = converter.Convert("\t\r\n ", typeof(bool), null, CultureInfo.CurrentUICulture); - - result.Should().Be(false); - } - - [TestMethod] - public void Convert_ValidOrgKey_ReturnsTrue() - { - var converter = new IsValidOrganisationKeyConverter(); - - var result = converter.Convert(" is valid key ", typeof(bool), null, null); - - result.Should().Be(true); - } - - [TestMethod] - public void ConvertBack_NotImplemented() - { - var converter = new IsValidOrganisationKeyConverter(); - Action act = () => converter.ConvertBack(null, null, null, null); - - act.Should().ThrowExactly(); - } - - [TestMethod] - public void GetTrimmedKey_ReturnsExpectedValues() - { - IsValidOrganisationKeyConverter.GetTrimmedKey(null).Should().Be(null); - IsValidOrganisationKeyConverter.GetTrimmedKey("\r\t\n").Should().Be(""); - IsValidOrganisationKeyConverter.GetTrimmedKey(" is a valid key\r\n").Should().Be("is a valid key"); - } - } -} diff --git a/src/Integration.UnitTests/WPF/ProjectViewModelToBindingArgsConverterTests.cs b/src/Integration.UnitTests/WPF/ProjectViewModelToBindingArgsConverterTests.cs deleted file mode 100644 index ae92d4d868..0000000000 --- a/src/Integration.UnitTests/WPF/ProjectViewModelToBindingArgsConverterTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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. - */ - -// todo remove https://sonarsource.atlassian.net/browse/SLVS-1408 -// using System; -// using System.Globalization; -// using System.Security; -// using FluentAssertions; -// using Microsoft.VisualStudio.TestTools.UnitTesting; -// using SonarLint.VisualStudio.ConnectedMode.Binding; -// using SonarLint.VisualStudio.Integration.TeamExplorer; -// using SonarLint.VisualStudio.Integration.WPF; -// using SonarQube.Client.Models; -// -// namespace SonarLint.VisualStudio.Integration.UnitTests.WPF -// { -// [TestClass] -// public class ProjectViewModelToBindingArgsConverterTests -// { -// [TestMethod] -// public void Convert_NotProjectViewModel_ReturnsNull() -// { -// // Arrange -// var converter = new ProjectViewModelToBindingArgsConverter(); -// -// // Act && Assert -// converter.Convert(null, null, null, null).Should().BeNull(); -// converter.Convert("a string", typeof(object), null, CultureInfo.CurrentCulture).Should().BeNull(); -// } -// -// [TestMethod] -// public void Convert_ValidProjecModel_ReturnsBindingArgs() -// { -// // Arrange -// var expectedUri = new Uri("http://localhost:9000"); -// var expectedPassword = new SecureString(); -// expectedPassword.AppendChar('x'); -// var serverViewModel = new ServerViewModel(new ConnectionInformation(expectedUri, "user1", expectedPassword)); -// var project = new SonarQubeProject("key1", "name1"); -// var projectViewModel = new ProjectViewModel(serverViewModel, project); -// -// var converter = new ProjectViewModelToBindingArgsConverter(); -// -// // Act -// var convertedObj = converter.Convert(projectViewModel, null, null, null); -// convertedObj.Should().NotBeNull(); -// convertedObj.Should().BeOfType(); -// -// var bindCommandArgs = (BindCommandArgs)convertedObj; -// bindCommandArgs.ProjectKey.Should().Be("key1"); -// bindCommandArgs.ProjectName.Should().Be("name1"); -// bindCommandArgs.Connection.Should().NotBeNull(); -// bindCommandArgs.Connection.ServerUri.Should().BeSameAs(expectedUri); -// bindCommandArgs.Connection.UserName.Should().Be("user1"); -// bindCommandArgs.Connection.Password.Length.Should().Be(1); -// } -// } -// } diff --git a/src/Integration.UnitTests/WPF/ProjectViewModelVisibilityConverterTests.cs b/src/Integration.UnitTests/WPF/ProjectViewModelVisibilityConverterTests.cs deleted file mode 100644 index d1dac3db74..0000000000 --- a/src/Integration.UnitTests/WPF/ProjectViewModelVisibilityConverterTests.cs +++ /dev/null @@ -1,231 +0,0 @@ -/* - * 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.Windows; -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SonarLint.VisualStudio.Integration.WPF; - -namespace SonarLint.VisualStudio.Integration.UnitTests.WPF -{ - [TestClass] - public class ProjectViewModelVisibilityConverterTests - { - [ExpectedException(typeof(NotSupportedException))] - [TestMethod] - public void ProjectViewModelVisibilityConverter_ConvertBack_ThrowsNotSupportedException() - { - // Arrange - var converter = new ProjectViewModelVisibilityConverter(); - - // Act & Assert - converter.ConvertBack(null, null, null, null); - } - - [TestMethod] - public void ProjectViewModelVisibilityConverter_Convert_WhenGivenANullValue_ExpectsDependencyProperyUnsetValue() - { - // Arrange - var converter = new ProjectViewModelVisibilityConverter(); - - // Act - var result = converter.Convert(null, null, null, null); - - // Assert - DependencyProperty.UnsetValue.Should().Be(result); - } - - [TestMethod] - public void ProjectViewModelVisibilityConverter_Convert_WhenNotGivenFourValues_ExpectsDependencyProperyUnsetValue() - { - // Arrange - var converter = new ProjectViewModelVisibilityConverter(); - - // Act - var result0 = converter.Convert(new object[0], null, null, null); - var result1 = converter.Convert(new object[1], null, null, null); - var result2 = converter.Convert(new object[2], null, null, null); - var result3 = converter.Convert(new object[3], null, null, null); - var result5 = converter.Convert(new object[5], null, null, null); - - // Assert - result0.Should().Be(DependencyProperty.UnsetValue); - result1.Should().Be(DependencyProperty.UnsetValue); - result2.Should().Be(DependencyProperty.UnsetValue); - result3.Should().Be(DependencyProperty.UnsetValue); - result5.Should().Be(DependencyProperty.UnsetValue); - } - - [TestMethod] - public void ProjectViewModelVisibilityConverter_Convert_WhenGivenFourValuesButFirstValueIsNotStringOrIsNull_ExpectsDependencyProperyUnsetValue() - { - // Arrange - var converter = new ProjectViewModelVisibilityConverter(); - var values = new object[4]; - - // Act - values[0] = 5; - var result1 = converter.Convert(values, null, null, null); - values[0] = null; - var result2 = converter.Convert(values, null, null, null); - - // Assert - result1.Should().Be(DependencyProperty.UnsetValue); - result2.Should().Be(DependencyProperty.UnsetValue); - } - - [TestMethod] - public void ProjectViewModelVisibilityConverter_Convert_WhenGivenFourValuesButSecondValueIsNotBoolOrIsNull_ExpectsDependencyProperyUnsetValue() - { - // Arrange - var converter = new ProjectViewModelVisibilityConverter(); - var values = new object[4]; - values[0] = "foo"; - - // Act - values[1] = 5; - var result1 = converter.Convert(values, null, null, null); - values[1] = null; - var result2 = converter.Convert(values, null, null, null); - - // Assert - result1.Should().Be(DependencyProperty.UnsetValue); - result2.Should().Be(DependencyProperty.UnsetValue); - } - - [TestMethod] - public void ProjectViewModelVisibilityConverter_Convert_WhenGivenFourValuesButThirdValueIsNotBoolOrIsNull_ExpectsDependencyProperyUnsetValue() - { - // Arrange - var converter = new ProjectViewModelVisibilityConverter(); - var values = new object[4]; - values[0] = "foo"; - values[1] = true; - - // Act - values[2] = 5; - var result1 = converter.Convert(values, null, null, null); - values[2] = null; - var result2 = converter.Convert(values, null, null, null); - - // Assert - result1.Should().Be(DependencyProperty.UnsetValue); - result2.Should().Be(DependencyProperty.UnsetValue); - } - - [TestMethod] - public void ProjectViewModelVisibilityConverter_Convert_WhenGivenFourValuesButFourthValueIsNotStringOrIsNull_ExpectsDependencyProperyUnsetValue() - { - // Arrange - var converter = new ProjectViewModelVisibilityConverter(); - var values = new object[4]; - values[0] = "foo"; - values[1] = true; - values[2] = "bar"; - - // Act - values[3] = 5; - var result1 = converter.Convert(values, null, null, null); - values[3] = null; - var result2 = converter.Convert(values, null, null, null); - - // Assert - result1.Should().Be(DependencyProperty.UnsetValue); - result2.Should().Be(DependencyProperty.UnsetValue); - } - - [TestMethod] - public void ProjectViewModelVisibilityConverter_Convert_WhenShowAllProjectsIsTrueAndProjectNameContainsFilterText_ReturnsVisible() - { - // Arrange - var converter = new ProjectViewModelVisibilityConverter(); - var projectName = "foobar"; - var isShowingAllProjects = true; - var isBound = false; - var filterText = "bar"; - var values = new object[] { projectName, isShowingAllProjects, isBound, filterText }; - - // Act - var result = converter.Convert(values, null, null, null); - - // Assert - result.Should().BeAssignableTo(); - Visibility.Visible.Should().Be(result); - } - - [TestMethod] - public void ProjectViewModelVisibilityConverter_Convert_WhenShowAllProjectsIsTrueAndProjectNameDoesNotContainFilterText_ReturnsCollapsed() - { - // Arrange - var converter = new ProjectViewModelVisibilityConverter(); - var projectName = "foobar"; - var isShowingAllProjects = true; - var isBound = false; - var filterText = "test"; - var values = new object[] { projectName, isShowingAllProjects, isBound, filterText }; - - // Act - var result = converter.Convert(values, null, null, null); - - // Assert - result.Should().BeAssignableTo(); - Visibility.Collapsed.Should().Be(result); - } - - [TestMethod] - public void ProjectViewModelVisibilityConverter_Convert_WhenShowAllProjectsIsFalseAndIsBoundIsTrue_ReturnsVisible() - { - // Arrange - var converter = new ProjectViewModelVisibilityConverter(); - var projectName = ""; - var isShowingAllProjects = false; - var isBound = true; - var filterText = ""; - var values = new object[] { projectName, isShowingAllProjects, isBound, filterText }; - - // Act - var result = converter.Convert(values, null, null, null); - - // Assert - result.Should().BeAssignableTo(); - Visibility.Visible.Should().Be(result); - } - - [TestMethod] - public void ProjectViewModelVisibilityConverter_Convert_WhenShowAllProjectsIsFalseAndIsBoundIsFalse_ReturnsCollapsed() - { - // Arrange - var converter = new ProjectViewModelVisibilityConverter(); - var projectName = ""; - var isShowingAllProjects = false; - var isBound = false; - var filterText = ""; - var values = new object[] { projectName, isShowingAllProjects, isBound, filterText }; - - // Act - var result = converter.Convert(values, null, null, null); - - // Assert - result.Should().BeAssignableTo(); - Visibility.Collapsed.Should().Be(result); - } - } -} \ No newline at end of file diff --git a/src/Integration.UnitTests/app.config b/src/Integration.UnitTests/app.config index 2fd81811fc..f26a137624 100644 --- a/src/Integration.UnitTests/app.config +++ b/src/Integration.UnitTests/app.config @@ -14,10 +14,6 @@ - - - - diff --git a/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithStrongNames.txt b/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithStrongNames.txt index ba1cdc9b84..30a4ec33ba 100644 --- a/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithStrongNames.txt +++ b/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithStrongNames.txt @@ -256,9 +256,7 @@ Assembly: 'SonarLint.VisualStudio.Integration, Version=8.6.0.0, Culture=neutral, Relative path: 'SonarLint.VisualStudio.Integration.dll' Referenced assemblies: -- 'Microsoft.Alm.Authentication, Version=4.0.0.0, Culture=neutral, PublicKeyToken=c5b62af9de6d7244' - 'Microsoft.VisualStudio.ComponentModelHost, Version=17.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' -- 'Microsoft.VisualStudio.ImageCatalog, Version=17.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' - 'Microsoft.VisualStudio.Imaging, Version=17.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' - 'Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' - 'Microsoft.VisualStudio.Interop, Version=17.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' @@ -274,7 +272,6 @@ Referenced assemblies: - 'SonarLint.VisualStudio.Core, Version=8.6.0.0, Culture=neutral, PublicKeyToken=c5b62af9de6d7244' - 'SonarLint.VisualStudio.Infrastructure.VS, Version=8.6.0.0, Culture=neutral, PublicKeyToken=c5b62af9de6d7244' - 'SonarLint.VisualStudio.Progress, Version=8.6.0.0, Culture=neutral, PublicKeyToken=c5b62af9de6d7244' -- 'SonarLint.VisualStudio.ProgressVS, Version=8.6.0.0, Culture=neutral, PublicKeyToken=c5b62af9de6d7244' - 'SonarLint.VisualStudio.SLCore, Version=8.6.0.0, Culture=neutral, PublicKeyToken=c5b62af9de6d7244' - 'SonarQube.Client, Version=8.6.0.0, Culture=neutral, PublicKeyToken=c5b62af9de6d7244' - 'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' @@ -286,29 +283,23 @@ Referenced assemblies: - 'System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'WindowsBase, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' -# Number of references: 30 +# Number of references: 27 --- Assembly: 'SonarLint.VisualStudio.Integration.TeamExplorer, Version=8.6.0.0, Culture=neutral, PublicKeyToken=c5b62af9de6d7244' Relative path: 'SonarLint.VisualStudio.Integration.TeamExplorer.dll' Referenced assemblies: -- 'Microsoft.TeamFoundation.Client, Version=16.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' - 'Microsoft.TeamFoundation.Controls, Version=17.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' -- 'Microsoft.VisualStudio.Interop, Version=17.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' -- 'Microsoft.VisualStudio.Shell.Framework, Version=17.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' - 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'PresentationCore, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' - 'PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' -- 'SonarLint.VisualStudio.ConnectedMode, Version=8.6.0.0, Culture=neutral, PublicKeyToken=c5b62af9de6d7244' - 'SonarLint.VisualStudio.Infrastructure.VS, Version=8.6.0.0, Culture=neutral, PublicKeyToken=c5b62af9de6d7244' - 'SonarLint.VisualStudio.Integration, Version=8.6.0.0, Culture=neutral, PublicKeyToken=c5b62af9de6d7244' -- 'SonarQube.Client, Version=8.6.0.0, Culture=neutral, PublicKeyToken=c5b62af9de6d7244' - 'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.ComponentModel.Composition, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' -- 'System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.Xaml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' -# Number of references: 15 +# Number of references: 9 --- Assembly: 'SonarLint.VisualStudio.IssueVisualization, Version=8.6.0.0, Culture=neutral, PublicKeyToken=c5b62af9de6d7244' diff --git a/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithoutStrongNames.txt b/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithoutStrongNames.txt index 239c6619d9..f69d013bd5 100644 --- a/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithoutStrongNames.txt +++ b/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithoutStrongNames.txt @@ -256,9 +256,7 @@ Assembly: 'SonarLint.VisualStudio.Integration, Version=8.6.0.0, Culture=neutral, Relative path: 'SonarLint.VisualStudio.Integration.dll' Referenced assemblies: -- 'Microsoft.Alm.Authentication, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null' - 'Microsoft.VisualStudio.ComponentModelHost, Version=17.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' -- 'Microsoft.VisualStudio.ImageCatalog, Version=17.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' - 'Microsoft.VisualStudio.Imaging, Version=17.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' - 'Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' - 'Microsoft.VisualStudio.Interop, Version=17.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' @@ -274,7 +272,6 @@ Referenced assemblies: - 'SonarLint.VisualStudio.Core, Version=8.6.0.0, Culture=neutral, PublicKeyToken=null' - 'SonarLint.VisualStudio.Infrastructure.VS, Version=8.6.0.0, Culture=neutral, PublicKeyToken=null' - 'SonarLint.VisualStudio.Progress, Version=8.6.0.0, Culture=neutral, PublicKeyToken=null' -- 'SonarLint.VisualStudio.ProgressVS, Version=8.6.0.0, Culture=neutral, PublicKeyToken=null' - 'SonarLint.VisualStudio.SLCore, Version=8.6.0.0, Culture=neutral, PublicKeyToken=null' - 'SonarQube.Client, Version=8.6.0.0, Culture=neutral, PublicKeyToken=null' - 'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' @@ -286,29 +283,23 @@ Referenced assemblies: - 'System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'WindowsBase, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' -# Number of references: 30 +# Number of references: 27 --- Assembly: 'SonarLint.VisualStudio.Integration.TeamExplorer, Version=8.6.0.0, Culture=neutral, PublicKeyToken=null' Relative path: 'SonarLint.VisualStudio.Integration.TeamExplorer.dll' Referenced assemblies: -- 'Microsoft.TeamFoundation.Client, Version=16.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' - 'Microsoft.TeamFoundation.Controls, Version=17.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' -- 'Microsoft.VisualStudio.Interop, Version=17.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' -- 'Microsoft.VisualStudio.Shell.Framework, Version=17.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' - 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'PresentationCore, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' - 'PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' -- 'SonarLint.VisualStudio.ConnectedMode, Version=8.6.0.0, Culture=neutral, PublicKeyToken=null' - 'SonarLint.VisualStudio.Infrastructure.VS, Version=8.6.0.0, Culture=neutral, PublicKeyToken=null' - 'SonarLint.VisualStudio.Integration, Version=8.6.0.0, Culture=neutral, PublicKeyToken=null' -- 'SonarQube.Client, Version=8.6.0.0, Culture=neutral, PublicKeyToken=null' - 'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.ComponentModel.Composition, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' -- 'System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.Xaml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' -# Number of references: 15 +# Number of references: 9 --- Assembly: 'SonarLint.VisualStudio.IssueVisualization, Version=8.6.0.0, Culture=neutral, PublicKeyToken=null' diff --git a/src/Integration/Binding/AutoBindTrigger.cs b/src/Integration/Binding/AutoBindTrigger.cs deleted file mode 100644 index 05b84dbc9c..0000000000 --- a/src/Integration/Binding/AutoBindTrigger.cs +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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.ComponentModel.Composition; -using SonarLint.VisualStudio.ConnectedMode.Binding; -using SonarLint.VisualStudio.ConnectedMode.Persistence; -using SonarLint.VisualStudio.Core.Binding; -using SonarLint.VisualStudio.Progress.Controller; -using SonarQube.Client.Models; - -namespace SonarLint.VisualStudio.Integration.Binding -{ - internal interface IAutoBindTrigger - { - void TriggerAfterSuccessfulWorkflow(IProgressEvents workflowProgress, - string autoBindProjectKey, ConnectionInformation connectionInformation); - } - - [Export(typeof(IAutoBindTrigger))] - [PartCreationPolicy(CreationPolicy.Shared)] - internal class AutoBindTrigger : IAutoBindTrigger - { - private readonly IHost host; - - [ImportingConstructor] - public AutoBindTrigger(IHost host) - { - this.host = host; - } - - public void TriggerAfterSuccessfulWorkflow(IProgressEvents workflowProgress, - string autoBindProjectKey, ConnectionInformation connectionInformation) - { - workflowProgress.RunOnFinished(result => - { - AutobindIfPossible(result, autoBindProjectKey, connectionInformation); - }); - } - - internal /* for testing */ void AutobindIfPossible(ProgressControllerResult result, - string autoBindProjectKey, - ConnectionInformation connectionInformation) - { - if (result == ProgressControllerResult.Succeeded && !string.IsNullOrEmpty(autoBindProjectKey)) - { - host.ActiveSection.BindCommand.Execute( - new BindCommandArgs(new BoundServerProject("placeholder", autoBindProjectKey, connectionInformation.ToServerConnection()))); // todo https://sonarsource.atlassian.net/browse/SLVS-1408 - } - } - } -} diff --git a/src/Integration/Binding/BindingController.cs b/src/Integration/Binding/BindingController.cs deleted file mode 100644 index ea32ae50da..0000000000 --- a/src/Integration/Binding/BindingController.cs +++ /dev/null @@ -1,189 +0,0 @@ -/* - * 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.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using Microsoft.VisualStudio.OLE.Interop; -using SonarLint.VisualStudio.ConnectedMode.Binding; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Core.Binding; -using SonarLint.VisualStudio.Integration.Progress; -using SonarLint.VisualStudio.Integration.Resources; -using SonarLint.VisualStudio.Integration.TeamExplorer; -using SonarLint.VisualStudio.Integration.WPF; -using SonarLint.VisualStudio.Progress.Controller; - -namespace SonarLint.VisualStudio.Integration.Binding -{ - /// - /// A dedicated controller for the - /// - [ExcludeFromCodeCoverage] // todo https://sonarsource.atlassian.net/browse/SLVS-1408 - internal class BindingController : HostedCommandControllerBase, IBindingWorkflowExecutor - { - private readonly System.IServiceProvider serviceProvider; - private readonly IHost host; - private readonly IBindingWorkflowExecutor workflowExecutor; - private readonly IProjectSystemHelper projectSystemHelper; - private readonly IFolderWorkspaceService folderWorkspaceService; - private readonly IBindingProcessFactory bindingProcessFactory; - private readonly IKnownUIContexts knownUIContexts; - - public BindingController(System.IServiceProvider serviceProvider, IHost host) - : this(serviceProvider, host, null, new KnownUIContextsWrapper()) - { - } - - internal /*for testing purposes*/ BindingController(System.IServiceProvider serviceProvider, IHost host, IBindingWorkflowExecutor workflowExecutor, IKnownUIContexts knownUIContexts) - : base(serviceProvider) - { - this.serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); - this.host = host ?? throw new ArgumentNullException(nameof(host)); - - this.BindCommand = new RelayCommand(this.OnBind, this.OnBindStatus); - this.workflowExecutor = workflowExecutor ?? this; - this.knownUIContexts = knownUIContexts; - - projectSystemHelper = serviceProvider.GetMefService(); - folderWorkspaceService = serviceProvider.GetMefService(); - bindingProcessFactory = serviceProvider.GetMefService(); - } - - #region Commands - - public RelayCommand BindCommand { get; } - - internal /*for testing purposes*/ bool IsBindingInProgress - { - get - { - return this.host.VisualStateManager.IsBusy; - } - private set - { - if (this.host.VisualStateManager.IsBusy != value) - { - this.host.VisualStateManager.IsBusy = value; - this.BindCommand.RequeryCanExecute(); - } - } - } - - protected override int OnQueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText) - { - // Using just as a means that indicates that the status was invalidated and it needs to be recalculate - // in response to IVsUIShell.UpdateCommandUI which is triggered for the various UI context changes - this.BindCommand.RequeryCanExecute(); - - return base.OnQueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText); - } - - private bool OnBindStatus(BindCommandArgs args) - { - return args != null - && args.ProjectToBind != null - && host.VisualStateManager.IsConnected - && !host.VisualStateManager.IsBusy - && (folderWorkspaceService.IsFolderWorkspace() - || (knownUIContexts.SolutionExistsAndFullyLoadedContext.IsActive - && knownUIContexts.SolutionExistsAndNotBuildingAndNotDebuggingContext.IsActive - && (projectSystemHelper.GetSolutionProjects()?.Any() ?? false))); - } - - private void OnBind(BindCommandArgs args) - { - Debug.Assert(this.OnBindStatus(args)); - - this.workflowExecutor.BindProject(args); - } - - #endregion - - #region IBindingWorkflowExecutor - - void IBindingWorkflowExecutor.BindProject(BindCommandArgs bindingArgs) - { - var bindingProcess = CreateBindingProcess(bindingArgs, bindingProcessFactory, host.Logger); - var workflow = new BindingWorkflow(serviceProvider, host, bindingProcess); - - IProgressEvents progressEvents = workflow.Run(); - Debug.Assert(progressEvents != null, "BindingWorkflow.Run returned null"); - this.SetBindingInProgress(progressEvents, bindingArgs); - } - - internal static /* for testing purposes */ IBindingProcess CreateBindingProcess(BindCommandArgs bindingArgs, IBindingProcessFactory bindingProcessFactory, ILogger logger) - { - logger.WriteLine(Strings.Bind_UpdatingNewStyleBinding); - - var bindingProcess = bindingProcessFactory.Create(bindingArgs); - return bindingProcess; - } - - internal /*for testing purposes*/ void SetBindingInProgress(IProgressEvents progressEvents, BindCommandArgs bindingArgs) - { - this.OnBindingStarted(); - - ProgressNotificationListener progressListener = new ProgressNotificationListener(progressEvents, this.host.Logger); - progressListener.MessageFormat = Strings.BindingSolutionPrefixMessageFormat; - - progressEvents.RunOnFinished(result => - { - progressListener.Dispose(); - - this.OnBindingFinished(bindingArgs, result == ProgressControllerResult.Succeeded); - }); - } - - private void OnBindingStarted() - { - this.IsBindingInProgress = true; - this.host.ActiveSection?.UserNotifications?.HideNotification(NotificationIds.FailedToBindId); - } - - private void OnBindingFinished(BindCommandArgs bindingArgs, bool isFinishedSuccessfully) - { - this.IsBindingInProgress = false; - this.host.VisualStateManager.ClearBoundProject(); - - if (isFinishedSuccessfully) - { - this.host.VisualStateManager.SetBoundProject(bindingArgs.ProjectToBind.ServerConnection.ServerUri, (bindingArgs.ProjectToBind.ServerConnection as ServerConnection.SonarCloud)?.OrganizationKey, bindingArgs.ProjectToBind.ServerProjectKey); - - VsShellUtils.ActivateSolutionExplorer(serviceProvider); - } - else - { - IUserNotification notifications = this.host.ActiveSection?.UserNotifications; - if (notifications != null) - { - // Create a command with a fixed argument with the help of ContextualCommandViewModel that creates proxy command for the contextual (fixed) instance and the passed in ICommand that expects it - var rebindCommandVM = new ContextualCommandViewModel( - bindingArgs, - new RelayCommand(this.OnBind, this.OnBindStatus)); - notifications.ShowNotificationError(Strings.FailedToToBindSolution, NotificationIds.FailedToBindId, rebindCommandVM.Command); - } - } - } - - #endregion - } -} diff --git a/src/Integration/Binding/BindingWorkflow.cs b/src/Integration/Binding/BindingWorkflow.cs deleted file mode 100644 index 6f97a2ec3a..0000000000 --- a/src/Integration/Binding/BindingWorkflow.cs +++ /dev/null @@ -1,160 +0,0 @@ -/* - * 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.Diagnostics; -using System.Threading; -using SonarLint.VisualStudio.ConnectedMode.Binding; -using SonarLint.VisualStudio.Integration.Progress; -using SonarLint.VisualStudio.Integration.Resources; -using SonarLint.VisualStudio.Progress.Controller; - -namespace SonarLint.VisualStudio.Integration.Binding -{ - // Legacy connected mode: - // Handles binding a solution for legacy connected mode i.e. writes the - // solution-level files and adds rulesets to every applicable project. - - /// - /// Workflow execution for the bind command - /// - internal class BindingWorkflow : IBindingWorkflow - { - private readonly IServiceProvider serviceProvider; - private readonly IHost host; - private readonly IBindingProcess bindingProcess; - - public BindingWorkflow(IServiceProvider serviceProvider, - IHost host, - IBindingProcess bindingProcess) - { - this.serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); - this.bindingProcess = bindingProcess ?? throw new ArgumentNullException(nameof(bindingProcess)); - this.host = host ?? throw new ArgumentNullException(nameof(host)); - } - - #region Workflow startup - - public IProgressEvents Run() - { - Debug.Assert(this.host.ActiveSection != null, "Expect the section to be attached at least until this method returns"); - - IProgressEvents progress = ProgressStepRunner.StartAsync(serviceProvider, - this.host.ActiveSection.ProgressHost, - controller => this.CreateWorkflowSteps(controller)); - -#if DEBUG - progress.RunOnFinished(r => this.host.Logger.WriteLine("DEBUGONLY: Binding workflow finished, Execution result: {0}", r)); -#endif - return progress; - } - - private ProgressStepDefinition[] CreateWorkflowSteps(IProgressController controller) - { - const StepAttributes HiddenNonImpactingBackgroundStep = StepAttributes.BackgroundThread | StepAttributes.Hidden | StepAttributes.NoProgressImpact; - - return new ProgressStepDefinition[] - { - // Some of the steps are broken into multiple sub-steps, either - // because the work needs to be done on a different thread or - // to report progress separate. - - //***************************************************************** - // Initialization - //***************************************************************** - // Show an initial message - new ProgressStepDefinition(null, HiddenNonImpactingBackgroundStep, - (token, notifications) => notifications.ProgressChanged(Strings.StartedSolutionBindingWorkflow)), - - //***************************************************************** - // Preparation - //***************************************************************** - // Fetch data from Sonar server and write shared ruleset file(s) to temporary location on disk - new ProgressStepDefinition(Strings.BindingProjectsDisplayMessage, StepAttributes.BackgroundThread, - (token, notifications) => this.DownloadQualityProfileAsync(controller, notifications, token).GetAwaiter().GetResult()), - - //***************************************************************** - // Solution update phase - //***************************************************************** - // * write config files to non-scc location - // Most of the work is delegated to SolutionBindingOperation - - new ProgressStepDefinition(Strings.BindingProjectsDisplayMessage, StepAttributes.BackgroundThread | StepAttributes.Indeterminate, - (token, notifications) => this.SaveServerExclusionsAsync(controller, notifications, token).GetAwaiter().GetResult()), - - //***************************************************************** - // Finalization - //***************************************************************** - // Show final message - new ProgressStepDefinition(null, HiddenNonImpactingBackgroundStep, - (token, notifications) => this.EmitBindingCompleteMessage(notifications)) - }; - } - - #endregion - - #region Workflow steps - - internal /*for testing purposes*/ async System.Threading.Tasks.Task DownloadQualityProfileAsync( - IProgressController controller, IProgressStepExecutionEvents notificationEvents, - CancellationToken cancellationToken) - { - Debug.Assert(controller != null); - Debug.Assert(notificationEvents != null); - - var progressAdapter = new FixedStepsProgressAdapter(notificationEvents); - if (!await bindingProcess.DownloadQualityProfileAsync(progressAdapter, cancellationToken).ConfigureAwait(false)) - { - this.AbortWorkflow(controller, cancellationToken); - } - } - - internal /*for testing purposes*/ async System.Threading.Tasks.Task SaveServerExclusionsAsync( - IProgressController controller, IProgressStepExecutionEvents notificationEvents, - CancellationToken cancellationToken) - { - Debug.Assert(controller != null); - Debug.Assert(notificationEvents != null); - - notificationEvents.ProgressChanged(Strings.SaveServerExclusionsMessage); - if (!await bindingProcess.SaveServerExclusionsAsync(cancellationToken).ConfigureAwait(false)) - { - this.AbortWorkflow(controller, cancellationToken); - } - } - - internal /*for testing purposes*/ void EmitBindingCompleteMessage(IProgressStepExecutionEvents notifications) - { - notifications.ProgressChanged(Strings.FinishedSolutionBindingWorkflowSuccessful); - } - - #endregion - - #region Helpers - - private void AbortWorkflow(IProgressController controller, CancellationToken token) - { - bool aborted = controller.TryAbort(); - Debug.Assert(aborted || token.IsCancellationRequested, "Failed to abort the workflow"); - } - - #endregion - } -} diff --git a/src/Integration/Binding/IBindingWorkflow.cs b/src/Integration/Binding/IBindingWorkflow.cs deleted file mode 100644 index c474ec0219..0000000000 --- a/src/Integration/Binding/IBindingWorkflow.cs +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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.Progress.Controller; - -namespace SonarLint.VisualStudio.Integration.Binding -{ - /// - /// Workflow for the bind command - /// - internal interface IBindingWorkflow - { - IProgressEvents Run(); - } -} diff --git a/src/Integration/Binding/IBindingWorkflowExecutor.cs b/src/Integration/Binding/IBindingWorkflowExecutor.cs deleted file mode 100644 index 30cda0e63b..0000000000 --- a/src/Integration/Binding/IBindingWorkflowExecutor.cs +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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.ConnectedMode.Binding; - -namespace SonarLint.VisualStudio.Integration.Binding -{ - // Test only interface - internal interface IBindingWorkflowExecutor - { - void BindProject(BindCommandArgs bindingArgs); - } -} diff --git a/src/Integration/Connection/BasicAuthenticationCredentialsValidator.cs b/src/Integration/Connection/BasicAuthenticationCredentialsValidator.cs deleted file mode 100644 index 7f79d67b27..0000000000 --- a/src/Integration/Connection/BasicAuthenticationCredentialsValidator.cs +++ /dev/null @@ -1,126 +0,0 @@ -/* - * 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.Globalization; -using System.Security; -using System.Text; -using System.Text.RegularExpressions; -using SonarLint.VisualStudio.Core; -using SonarQube.Client.Helpers; - -namespace SonarLint.VisualStudio.Integration.Connection -{ - internal class BasicAuthenticationCredentialsValidator - { - private readonly static Regex InvalidCharacters = new Regex("[:]", - RegexOptions.None, - RegexConstants.DefaultTimeout); // colon - - private string user; - private SecureString pwd; - - public bool IsUsernameValid - { - get { return this.InvalidUsernameErrorMessage == null; } - } - - /// - /// True if username () is valid. - /// - public bool IsValid - { - get { return this.IsUsernameValid; } - } - - /// - /// Error message summary for the Username, or null if valid. - /// - public string InvalidUsernameErrorMessage - { - get - { - var errorMessage = new StringBuilder(); - - // Valid options: - // 1. No user name or password -> anonymous access - // 2. User name and password - // 3. User name only, which is treated as a user token: http://docs.sonarqube.org/display/SONAR/User+Token. - - // Required? - bool usernameRequired = !this.pwd.IsNullOrEmpty() && string.IsNullOrEmpty(this.user); - if (usernameRequired) - { - AppendError(errorMessage, Resources.Strings.UsernameRequired); - } - - // Invalid characters? - if (this.user != null && InvalidCharacters.IsMatch(this.user)) - { - AppendError(errorMessage, Resources.Strings.InvalidCharacterColon); - } - - return CreateMessage(errorMessage); - } - } - - public void Update(string username, SecureString password) - { - this.UpdateUsername(username); - this.UpdatePassword(password); - } - - internal /* testing purposes */ void UpdateUsername(string username) - { - this.user = username; - } - - internal /* testing purposes */ void UpdatePassword(SecureString password) - { - this.pwd = password; - } - - public void Reset() - { - this.user = null; - this.pwd = null; - } - - #region Error message helpers - - private static void AppendError(StringBuilder builder, string error, params object[] args) - { - IFormatProvider formatProvider = CultureInfo.CurrentCulture; - - if (builder.Length > 0) - { - builder.AppendLine(); - } - builder.AppendFormat(formatProvider, error, args); - } - - private string CreateMessage(StringBuilder builder) - { - return builder.Length == 0 ? null : builder.ToString(); - } - - #endregion - } -} diff --git a/src/Integration/Connection/ConnectConfiguration.cs b/src/Integration/Connection/ConnectConfiguration.cs deleted file mode 100644 index 1379941f71..0000000000 --- a/src/Integration/Connection/ConnectConfiguration.cs +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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.Integration.Connection -{ - internal sealed class ConnectConfiguration - { - public bool UseSharedBinding { get; set; } = false; - } -} diff --git a/src/Integration/Connection/ConnectionController.cs b/src/Integration/Connection/ConnectionController.cs deleted file mode 100644 index 07d389d5c4..0000000000 --- a/src/Integration/Connection/ConnectionController.cs +++ /dev/null @@ -1,236 +0,0 @@ -/* - * 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.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using SonarLint.VisualStudio.ConnectedMode.Binding; -using SonarLint.VisualStudio.ConnectedMode.Shared; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Integration.Binding; -using SonarLint.VisualStudio.Integration.Progress; -using SonarLint.VisualStudio.Integration.Resources; -using SonarLint.VisualStudio.Integration.WPF; -using SonarLint.VisualStudio.Progress.Controller; -using SonarQube.Client.Helpers; -using SonarQube.Client.Models; - -namespace SonarLint.VisualStudio.Integration.Connection -{ - /// - /// Connection related controller. - /// Provides the following commands: - /// - /// - /// - internal sealed class ConnectionController : HostedCommandControllerBase, IConnectionInformationProvider, - IConnectionWorkflowExecutor - { - private readonly IAutoBindTrigger autoBindTrigger; - private readonly IHost host; - private readonly ISharedBindingConfigProvider sharedBindingConfigProvider; - private readonly ICredentialStoreService credentialStoreService; - private readonly IConnectionInformationProvider connectionProvider; - private readonly ISolutionInfoProvider solutionInfoProvider; - - public ConnectionController(IServiceProvider serviceProvider, IHost host, IAutoBindTrigger autoBindTrigger, ISharedBindingConfigProvider sharedBindingConfigProvider, ICredentialStoreService credentialStoreService) - : this(serviceProvider, host, autoBindTrigger, null, null, sharedBindingConfigProvider, credentialStoreService) - { - if (autoBindTrigger == null) - { - throw new ArgumentNullException(nameof(autoBindTrigger)); - } - } - - internal /*for testing purposes*/ ConnectionController(IServiceProvider serviceProvider, - IHost host, - IAutoBindTrigger autoBindTrigger, - IConnectionInformationProvider connectionProvider, - IConnectionWorkflowExecutor workflowExecutor, - ISharedBindingConfigProvider sharedBindingConfigProvider, - ICredentialStoreService credentialStoreService) - : base(serviceProvider) - { - this.host = host ?? throw new ArgumentNullException(nameof(host)); - this.sharedBindingConfigProvider = sharedBindingConfigProvider; - this.credentialStoreService = credentialStoreService; - this.autoBindTrigger = workflowExecutor == null ? autoBindTrigger : null; - this.WorkflowExecutor = workflowExecutor ?? this; - this.connectionProvider = connectionProvider ?? this; - - this.solutionInfoProvider = serviceProvider.GetMefService(); - this.ConnectCommand = new RelayCommand(this.OnConnect, this.CanConnect); - this.RefreshCommand = new RelayCommand(this.OnRefresh, this.CanRefresh); - } - - #region Properties - - public RelayCommand ConnectCommand { get; } - - public RelayCommand RefreshCommand { get; } - - internal /*for testing purposes*/ IConnectionWorkflowExecutor WorkflowExecutor { get; } - - internal ConnectionInformation LastAttemptedConnection { get; private set; } - - internal bool IsConnectionInProgress - { - get { return this.host.VisualStateManager.IsBusy; } - set - { - if (this.host.VisualStateManager.IsBusy != value) - { - this.host.VisualStateManager.IsBusy = value; - this.ConnectCommand.RequeryCanExecute(); - this.RefreshCommand.RequeryCanExecute(); - } - } - } - - #endregion - - #region Connect Command - - private bool CanConnect(ConnectConfiguration configuration) - { - return solutionInfoProvider.IsSolutionFullyOpened() - && !this.host.VisualStateManager.IsConnected - && !this.host.VisualStateManager.IsBusy; - } - - private void OnConnect(ConnectConfiguration configuration) - { - Debug.Assert(this.CanConnect(configuration)); - Debug.Assert(!this.host.VisualStateManager.IsBusy, "Service is in a connecting state"); - - ConnectionInformation connectionInfo; - - var sharedConfig = sharedBindingConfigProvider.GetSharedBinding(); - var credentials = sharedConfig == null ? null : credentialStoreService.ReadCredentials(sharedConfig.Uri); - var useSharedConfig = configuration?.UseSharedBinding ?? false; - - if (useSharedConfig && sharedConfig != null) - { - connectionInfo = new ConnectionInformation(sharedConfig.Uri, - credentials?.Username, - credentials?.Password?.ToSecureString()) - { - Organization = new SonarQubeOrganization(sharedConfig.Organization, string.Empty) - }; - - if (credentials == null) - { - connectionInfo = this.connectionProvider.GetConnectionInformation(connectionInfo); - } - - if (connectionInfo != null) - { - this.EstablishConnection(connectionInfo, sharedConfig.ProjectKey); - } - } - else - { - connectionInfo = this.connectionProvider.GetConnectionInformation(this.LastAttemptedConnection); - - if (connectionInfo != null) - { - this.EstablishConnection(connectionInfo); - } - } - } - - #endregion - - #region Refresh Command - - private bool CanRefresh(ConnectionInformation useConnection) - { - return !this.host.VisualStateManager.IsBusy - && (useConnection != null || this.host.VisualStateManager.IsConnected); - } - - private void OnRefresh(ConnectionInformation useConnection) - { - Debug.Assert(this.CanRefresh(useConnection)); - - // We're currently only connected to one server. when this will change we will need to refresh all the connected servers - ConnectionInformation connectionToRefresh = useConnection - ?? this.host.VisualStateManager.GetConnectedServers() - .FirstOrDefault(); - Debug.Assert(connectionToRefresh != null, - "Expecting either to be connected to get a connection to connect to"); - - // Any existing connection will be disposed, so create a copy and use it to connect - this.EstablishConnection(connectionToRefresh.Clone()); - } - - #endregion - - #region IConnectionInformationProvider - - ConnectionInformation IConnectionInformationProvider.GetConnectionInformation( - ConnectionInformation currentConnection) - { - var dialog = new ConnectionInformationDialog(); - return dialog.ShowDialog(currentConnection); - } - - #endregion - - #region IConnectionWorkflowExecutor - - private void EstablishConnection(ConnectionInformation connectionInfo, string autoBindProjectKey = null) - { - Debug.Assert(connectionInfo != null); - - this.LastAttemptedConnection = connectionInfo; - - this.WorkflowExecutor.EstablishConnection(connectionInfo, autoBindProjectKey); - } - - [ExcludeFromCodeCoverage] // the workflow is impossible to test as it's directly requests services from service provider, not mock-able - void IConnectionWorkflowExecutor.EstablishConnection(ConnectionInformation information, - string autoBindProjectKey) - { - ConnectionWorkflow workflow = new ConnectionWorkflow(this.ServiceProvider, this.host, autoBindProjectKey, this.ConnectCommand); - IProgressEvents progressEvents = workflow.Run(information); - SetConnectionInProgress(progressEvents); - autoBindTrigger.TriggerAfterSuccessfulWorkflow(progressEvents, autoBindProjectKey, information); - } - - internal /*for testing purposes*/ void SetConnectionInProgress(IProgressEvents progressEvents) - { - this.IsConnectionInProgress = true; - - ProgressNotificationListener progressListener = - new ProgressNotificationListener(progressEvents, this.host.Logger); - progressListener.MessageFormat = Strings.ConnectingToSonarQubePrefixMessageFormat; - - progressEvents.RunOnFinished(result => - { - progressListener.Dispose(); - this.IsConnectionInProgress = false; - }); - } - - #endregion - } -} diff --git a/src/Integration/Connection/ConnectionInformationDialog.cs b/src/Integration/Connection/ConnectionInformationDialog.cs deleted file mode 100644 index 6ea72ad7c3..0000000000 --- a/src/Integration/Connection/ConnectionInformationDialog.cs +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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.Diagnostics; -using System.Security; -using System.Windows; -using SonarLint.VisualStudio.Integration.Connection.UI; -using SonarQube.Client.Models; - -namespace SonarLint.VisualStudio.Integration.Connection -{ - internal class ConnectionInformationDialog - { - #region Static helpers - - private static ConnectionInfoDialogView CreateView() - { - return new ConnectionInfoDialogView(); - } - - internal /*for testing purposes*/ static ConnectionInfoDialogViewModel CreateViewModel(ConnectionInformation currentConnection) - { - var vm = new ConnectionInfoDialogViewModel(); - if (currentConnection != null) - { - vm.ServerUrlRaw = currentConnection.ServerUri.AbsoluteUri; - // Security: don't populate the user name field, as this might be a token - // See https://github.com/SonarSource/sonarlint-visualstudio/issues/1081 - } - - return vm; - } - - internal /* testing purposes */ static ConnectionInformation CreateConnectionInformation(ConnectionInfoDialogViewModel viewModel, SecureString password) - { - if (viewModel == null) - { - throw new ArgumentNullException(nameof(viewModel)); - } - - if (password == null) - { - throw new ArgumentNullException(nameof(password)); - } - - ConnectionInformation info = null; - - Debug.Assert(viewModel.IsValid, "View model should be valid when creating connection information"); - if (viewModel.IsValid) - { - Uri serverUri = viewModel.ServerUrl; - string username = viewModel.Username; - - info = new ConnectionInformation(serverUri, username, password); - } - - return info; - } - - #endregion - - /// - /// Opens a window and returns only when the dialog is closed. - /// - /// Optional, the current connection information to show in the dialog - /// Captured connection information if closed successfully, null otherwise. - public ConnectionInformation ShowDialog(ConnectionInformation currentConnection) - { - ConnectionInfoDialogViewModel vm = CreateViewModel(currentConnection); - ConnectionInfoDialogView dialog = CreateView(); - dialog.ViewModel = vm; - dialog.Owner = Application.Current.MainWindow; - - bool? result = dialog.ShowDialog(); - - if (result.GetValueOrDefault()) - { - return CreateConnectionInformation(vm, dialog.Password); - } - - return null; - } - } -} diff --git a/src/Integration/Connection/ConnectionWorkflow.cs b/src/Integration/Connection/ConnectionWorkflow.cs deleted file mode 100644 index 67aebd58ef..0000000000 --- a/src/Integration/Connection/ConnectionWorkflow.cs +++ /dev/null @@ -1,293 +0,0 @@ -/* - * 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.Diagnostics; -using System.Globalization; -using System.Linq; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Input; -using Microsoft.Alm.Authentication; -using SonarLint.VisualStudio.ConnectedMode.Binding; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Infrastructure.VS; -using SonarLint.VisualStudio.Integration.Connection.UI; -using SonarLint.VisualStudio.Integration.Progress; -using SonarLint.VisualStudio.Integration.Resources; -using SonarLint.VisualStudio.Integration.TeamExplorer; -using SonarLint.VisualStudio.Progress.Controller; -using SonarQube.Client.Helpers; -using SonarQube.Client.Models; - -namespace SonarLint.VisualStudio.Integration.Connection -{ - /// - /// Workflow execution for the connect command. - /// - internal class ConnectionWorkflow - { - private readonly IServiceProvider serviceProvider; - private readonly IHost host; - private readonly string autoBindProjectKey; - private readonly ICommand parentCommand; - private readonly IThreadHandling threadHandling; - private readonly ICredentialStoreService credentialStore; - - public ConnectionWorkflow(IServiceProvider serviceProvider, IHost host, string autoBindProjectKey, ICommand parentCommand) - : this(serviceProvider, host, autoBindProjectKey, parentCommand, ThreadHandling.Instance) - { } - - internal /* for testing */ ConnectionWorkflow(IServiceProvider serviceProvider, - IHost host, - string autoBindProjectKey, - ICommand parentCommand, - IThreadHandling threadHandling) - { - this.serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); - this.host = host ?? throw new ArgumentNullException(nameof(host)); - this.autoBindProjectKey = autoBindProjectKey; - this.parentCommand = parentCommand ?? throw new ArgumentNullException(nameof(parentCommand)); - this.threadHandling = threadHandling; - - credentialStore = serviceProvider.GetMefService(); - } - - internal /*for testing purposes*/ ConnectionInformation ConnectedServer - { - get; - set; - } - - #region Start the workflow - public IProgressEvents Run(ConnectionInformation information) - { - Debug.Assert(this.host.ActiveSection != null, "Expect the section to be attached at least until this method returns"); - - this.OnProjectsChanged(information, null); - IProgressEvents progress = ProgressStepRunner.StartAsync(serviceProvider, this.host.ActiveSection.ProgressHost, (controller) => this.CreateConnectionSteps(controller, information)); - -#if DEBUG - progress.RunOnFinished(r => this.host.Logger.WriteLine("DEBUGONLY: Connect workflow finished, Execution result: {0}", r)); -#endif - - return progress; - } - - private ProgressStepDefinition[] CreateConnectionSteps(IProgressController controller, ConnectionInformation connection) - { - string connectStepDisplayText = string.Format(CultureInfo.CurrentCulture, Strings.ConnectingToSever, - connection.ServerUri); - return new[] - { - new ProgressStepDefinition(connectStepDisplayText, StepAttributes.Indeterminate | StepAttributes.BackgroundThread, - (cancellationToken, notifications) => - this.ConnectionStepAsync(connection, controller, notifications, cancellationToken).GetAwaiter().GetResult()) - }; - } - - #endregion - - #region Workflow events - - private void OnProjectsChanged(ConnectionInformation connection, IEnumerable projects) - { - this.host.VisualStateManager.SetProjects(connection, projects); - } - - #endregion - - #region Workflow steps - - internal /* for testing purposes */ async Task ConnectionStepAsync(ConnectionInformation connection, - IProgressController controller, IProgressStepExecutionEvents notifications, CancellationToken cancellationToken) - { - this.host.ActiveSection?.UserNotifications?.HideNotification(NotificationIds.FailedToConnectId); - this.host.ActiveSection?.UserNotifications?.HideNotification(NotificationIds.BadSonarQubePluginId); - - notifications.ProgressChanged(connection.ServerUri.ToString()); - - try - { - notifications.ProgressChanged(Strings.ConnectionStepValidatinCredentials); - if (!this.host.SonarQubeService.IsConnected) - { - await this.host.SonarQubeService.ConnectAsync(connection, cancellationToken); - } - - await HandleOrganizationSelectionAsync(connection, controller, notifications, cancellationToken); - - PersistCredentials(connection); - - this.ConnectedServer = connection; - - notifications.ProgressChanged(Strings.ConnectionStepRetrievingProjects); - var projects = await this.host.SonarQubeService.GetAllProjectsAsync(connection.Organization?.Key, - cancellationToken); - - projects = HandleTooManyProjects(projects); - - this.OnProjectsChanged(connection, projects); - notifications.ProgressChanged(Strings.ConnectionResultSuccess); - - if (!string.IsNullOrEmpty(autoBindProjectKey) && !projects.Any(project => SonarQubeProject.KeyComparer.Equals(project.Key, autoBindProjectKey))) - { - ShowAutobindProjectNotFoundNotificationAndThrow(); - } - } - catch (HttpRequestException e) - { - // For some errors we will get an inner exception which will have a more specific information - // that we would like to show i.e.when the host could not be resolved - var innerException = e.InnerException as System.Net.WebException; - this.host.Logger.WriteLine(CoreStrings.SonarQubeRequestFailed, e.Message, innerException?.Message); - AbortWithMessageAndThrow(notifications, controller, cancellationToken); - } - catch (TaskCanceledException) - { - // Canceled or timeout - this.host.Logger.WriteLine(CoreStrings.SonarQubeRequestTimeoutOrCancelled); - AbortWithMessageAndThrow(notifications, controller, cancellationToken); - } - catch (BindingAbortedException) - { - throw; // needed to set ProgressControllerResult to Failed, not possible without an exception (or a refactoring of the progress framework) - } - catch (Exception ex) - { - this.host.Logger.WriteLine(CoreStrings.SonarQubeRequestFailed, ex.Message, null); - AbortWithMessageAndThrow(notifications, controller, cancellationToken); - } - } - - private async Task HandleOrganizationSelectionAsync(ConnectionInformation connection, IProgressController controller, - IProgressStepExecutionEvents notifications, CancellationToken cancellationToken) - { - if (connection.Organization == null) - { - var hasOrgs = await this.host.SonarQubeService.HasOrganizations(cancellationToken); - if (hasOrgs) - { - notifications.ProgressChanged(Strings.ConnectionStepRetrievingOrganizations); - var organizations = - await this.host.SonarQubeService.GetAllOrganizationsAsync(cancellationToken); - - connection.Organization = AskUserToSelectOrganizationOnUIThread(organizations); - if (connection.Organization == null) // User clicked cancel - { - AbortWithMessageAndThrow(notifications, controller, cancellationToken); - } - } - } - } - - private void PersistCredentials(ConnectionInformation connection) - { - // Persist the credentials on successful connection to SonarQube, unless - // the connection is anonymous - if (!string.IsNullOrEmpty(connection.UserName) && - !string.IsNullOrEmpty(connection.Password.ToUnsecureString())) - { - this.credentialStore.WriteCredentials( - connection.ServerUri, - new Credential(connection.UserName, connection.Password.ToUnsecureString())); - } - } - - private IList HandleTooManyProjects(IList projects) - { - // The SonarQube client will limit the number of returned projects to 10K (hard limit on SonarQube side) - // but will no longer fail when trying to retrieve more. In the case where the project we want to bind to - // is not in the list and the binding was already done and the key is not null then we manually - // forge and add a new project to the list. - if (!string.IsNullOrWhiteSpace(this.host.VisualStateManager.BoundProjectKey) && - projects.Count == 10000 && - !projects.Any(p => p.Key == this.host.VisualStateManager.BoundProjectKey)) - { - this.host.Logger.WriteLine( - $"The project with key '{this.host.VisualStateManager.BoundProjectKey}' is not part of the first ten thousand projects. The binding process will continue assuming it was found."); - this.host.Logger.WriteLine( - "Note that if the project key does not actually exist on the server the binding will fail at a later stage."); - - // We have to create a new list because the collection returned by the service as a fixed size - projects = new List(projects); - // Let's put the new item first in the collection to ease finding it. - projects.Insert(0, new SonarQubeProject(this.host.VisualStateManager.BoundProjectKey, - this.host.VisualStateManager.BoundProjectName ?? this.host.VisualStateManager.BoundProjectKey)); - } - - return projects; - } - - private void ShowAutobindProjectNotFoundNotificationAndThrow() - { - this.host.ActiveSection?.UserNotifications?.ShowNotificationError( - string.Format(CultureInfo.CurrentCulture, Strings.BoundProjectNotFound, autoBindProjectKey), - NotificationIds.FailedToFindBoundProjectKeyId, - host.ActiveSection?.DisconnectCommand); - - throw new BindingAbortedException($"Connection succeeded, but {nameof(autoBindProjectKey)} not found"); - } - - private void AbortWithMessageAndThrow(IProgressStepExecutionEvents notifications, IProgressController controller, - CancellationToken cancellationToken) - { - notifications.ProgressChanged(cancellationToken.IsCancellationRequested - ? Strings.ConnectionResultCancellation - : Strings.ConnectionResultFailure); - this.host.ActiveSection?.UserNotifications?.ShowNotificationError(Strings.ConnectionFailed, - NotificationIds.FailedToConnectId, this.parentCommand); - - AbortWorkflowAndThrow(controller, cancellationToken); - } - - private SonarQubeOrganization AskUserToSelectOrganizationOnUIThread(IEnumerable organizations) - { - SonarQubeOrganization organization = null; - - threadHandling.RunOnUIThread(() => - { - var organizationDialog = new OrganizationSelectionWindow(organizations) { Owner = Application.Current.MainWindow }; - var hasUserClickedOk = organizationDialog.ShowDialog().GetValueOrDefault(); - - organization = hasUserClickedOk? organizationDialog.Organization : null; - }); - return organization; - } - - #endregion - - #region Helpers - - private void AbortWorkflowAndThrow(IProgressController controller, CancellationToken token) - { - bool aborted = controller.TryAbort(); - Debug.Assert(aborted || token.IsCancellationRequested, "Failed to abort the workflow"); - - this.host.SonarQubeService.Disconnect(); - throw new BindingAbortedException("Connection failed"); // needed to set ProgressControllerResult to Failed, not possible without an exception (or a refactoring of the progress framework) - } - - #endregion - } -} diff --git a/src/Integration/Connection/IConnectionInformationProvider.cs b/src/Integration/Connection/IConnectionInformationProvider.cs deleted file mode 100644 index 146bf92cbe..0000000000 --- a/src/Integration/Connection/IConnectionInformationProvider.cs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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 SonarQube.Client.Models; - -namespace SonarLint.VisualStudio.Integration.Connection -{ - // Test only interface - internal interface IConnectionInformationProvider - { - /// - /// Returns the connection information - /// - /// Optional, the current connection information (which might be invalid) - /// - ConnectionInformation GetConnectionInformation(ConnectionInformation currentConnection); - } -} diff --git a/src/Integration/Connection/IConnectionWorkflowExecutor.cs b/src/Integration/Connection/IConnectionWorkflowExecutor.cs deleted file mode 100644 index b547df4dc9..0000000000 --- a/src/Integration/Connection/IConnectionWorkflowExecutor.cs +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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 SonarQube.Client.Models; - -namespace SonarLint.VisualStudio.Integration.Connection -{ - public interface IConnectionWorkflowExecutor - { - void EstablishConnection(ConnectionInformation information, string autoBindProjectKey); - } -} diff --git a/src/Integration/Connection/UI/ConnectionInfoDialogView.xaml b/src/Integration/Connection/UI/ConnectionInfoDialogView.xaml deleted file mode 100644 index 26753d2943..0000000000 --- a/src/Integration/Connection/UI/ConnectionInfoDialogView.xaml +++ /dev/null @@ -1,216 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Integration/Connection/UI/OrganizationSelectionWindow.xaml.cs b/src/Integration/Connection/UI/OrganizationSelectionWindow.xaml.cs deleted file mode 100644 index 9b28fbf4bc..0000000000 --- a/src/Integration/Connection/UI/OrganizationSelectionWindow.xaml.cs +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Windows; -using Microsoft.VisualStudio.PlatformUI; -using SonarQube.Client.Models; - -namespace SonarLint.VisualStudio.Integration.Connection.UI -{ - /// - /// Interaction logic for OrganizationSelectionWindow.xaml - /// - [ExcludeFromCodeCoverage] // Rely on Visual Studio elements that cannot be rendered under test - public partial class OrganizationSelectionWindow : DialogWindow - { - public SonarQubeOrganization Organization { get; private set; } - - internal OrganizationSelectionWindow(IEnumerable organizations) - { - InitializeComponent(); - - var sortedOrganizations = organizations.OrderBy(x => x.Name).ToList(); - OrganizationComboBox.ItemsSource = sortedOrganizations; - - // Pre-select the first organization, if there is one - OrganizationComboBox.SelectedItem = sortedOrganizations?.FirstOrDefault(); - } - - private void OnOwnOkButtonClick(object sender, RoutedEventArgs e) - { - Organization = OrganizationComboBox?.SelectedItem as SonarQubeOrganization; - Debug.Assert(Organization != null, - "Not expecting organization to be null: user should not have been able to click ok with an invalid selection"); - - // Close dialog in the affirmative - this.DialogResult = true; - } - - private void OnOtherOrgOkButtonClick(object sender, RoutedEventArgs e) - { - var orgKey = IsValidOrganisationKeyConverter.GetTrimmedKey(OrganizationKeyTextBox.Text); - - Debug.Assert(!string.IsNullOrWhiteSpace(orgKey), - "Not expecting orgKey to be null: user should not have been able to click ok with an invalid orgKey"); - - // We don't know the name of the organization at this point so we'll just use the key. - // It will be displayed in our UI in the Team Explorer, but that's ok for now. - Organization = new SonarQubeOrganization(orgKey, orgKey); - - // Close dialog in the affirmative - this.DialogResult = true; - } - } -} diff --git a/src/Integration/Connection/UriValidator.cs b/src/Integration/Connection/UriValidator.cs deleted file mode 100644 index dfb4e24fe4..0000000000 --- a/src/Integration/Connection/UriValidator.cs +++ /dev/null @@ -1,169 +0,0 @@ -/* - * 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; - -namespace SonarLint.VisualStudio.Integration.Connection -{ - internal class UriValidator - { - private static readonly ISet DefaultSupportedSchemes = new HashSet(new[] { "http", "https" }, StringComparer.OrdinalIgnoreCase); - - private static readonly ISet DefaultInsecureSchemes = new HashSet(new[] { "http" }, StringComparer.OrdinalIgnoreCase); - - /// - /// Supported URI schemes. - /// - private readonly ISet supportedSchemes = new HashSet(StringComparer.OrdinalIgnoreCase); - - /// - /// Schemes which are considered to be insecure. - /// - private readonly ISet insecureSchemes = new HashSet(StringComparer.OrdinalIgnoreCase); - - public UriValidator() - { - this.supportedSchemes.UnionWith(DefaultSupportedSchemes); - this.insecureSchemes.UnionWith(DefaultInsecureSchemes); - } - - public UriValidator(ISet supportedSchemes) - { - if (supportedSchemes == null) - { - throw new ArgumentNullException(nameof(supportedSchemes)); - } - - this.supportedSchemes.Clear(); - this.insecureSchemes.Clear(); - - this.supportedSchemes.UnionWith(supportedSchemes); - } - - public UriValidator(ISet supportedSchemes, ISet insecureSchemes) - : this(supportedSchemes) - { - if (insecureSchemes == null) - { - throw new ArgumentNullException(nameof(supportedSchemes)); - } - - this.supportedSchemes.Clear(); - this.insecureSchemes.Clear(); - - this.supportedSchemes.UnionWith(supportedSchemes); - this.insecureSchemes.UnionWith(insecureSchemes); - - if (!this.insecureSchemes.IsSubsetOf(this.supportedSchemes)) - { - throw new ArgumentException(Resources.Strings.ExceptionInsecureSchemesIsNotSubset, nameof(insecureSchemes)); - } - } - - /// - /// True if 's scheme is one of , false otherwise. - /// - /// - /// to check, must not be null. - public bool IsSupportedScheme(Uri uri) - { - if (uri == null) - { - throw new ArgumentNullException(nameof(uri)); - } - - return this.supportedSchemes?.Contains(uri.Scheme) ?? false; - } - - /// - /// True if 's scheme is one of , false otherwise. - /// - /// - /// to check, must not be null. - public bool IsInsecureScheme(Uri uri) - { - if (uri == null) - { - throw new ArgumentNullException(nameof(uri)); - } - - return this.insecureSchemes?.Contains(uri.Scheme) ?? false; - } - - /// - /// Whether or not is considered to be a valid URI. - /// - /// URI to check - /// - /// Valid URIs cannot be null, must have a scheme listed in , - /// and be absolute. - /// - public virtual bool IsValidUri(string uriString) - { - // non empty - if (string.IsNullOrWhiteSpace(uriString)) - { - return false; - } - - Uri uri; - - // creatable - if (!Uri.TryCreate(uriString, UriKind.Absolute, out uri)) - { - return false; - } - - return this.IsValidUri(uri); - } - - /// - /// Whether or not is considered to be a valid URI. - /// - /// - /// to check, must not be null. - /// - /// Valid URIs must have a scheme listed in - /// and be absolute. - /// - public virtual bool IsValidUri(Uri uri) - { - if (uri == null) - { - throw new ArgumentNullException(nameof(uri)); - } - - // absolute - if (!uri.IsAbsoluteUri) - { - return false; - } - - // supported - if (!this.IsSupportedScheme(uri)) - { - return false; - } - - return true; - } - } -} diff --git a/src/Integration/HostedCommandControllerBase.cs b/src/Integration/HostedCommandControllerBase.cs deleted file mode 100644 index 258105da9a..0000000000 --- a/src/Integration/HostedCommandControllerBase.cs +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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 Microsoft.VisualStudio.OLE.Interop; - -namespace SonarLint.VisualStudio.Integration -{ - internal class HostedCommandControllerBase : IOleCommandTarget - { - private const int CommandNotHandled = (int)Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_UNKNOWNGROUP; - - protected HostedCommandControllerBase(System.IServiceProvider serviceProvider) - { - if (serviceProvider == null) - { - throw new ArgumentNullException(nameof(serviceProvider)); - } - - this.ServiceProvider = serviceProvider; - } - - public System.IServiceProvider ServiceProvider { get; } - - #region IOleCommandTarget - int IOleCommandTarget.QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText) - { - return this.OnQueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText); - } - - int IOleCommandTarget.Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) - { - return this.OnExec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); - } - #endregion - - #region Ole command extensibility - /// - /// Redirected call from - /// - protected virtual int OnQueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText) - { - return CommandNotHandled; - } - - /// - /// Redirected call from - /// - protected virtual int OnExec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) - { - return CommandNotHandled; - } - #endregion - } -} diff --git a/src/Integration/Integration.csproj b/src/Integration/Integration.csproj index 552499a722..ec75551970 100644 --- a/src/Integration/Integration.csproj +++ b/src/Integration/Integration.csproj @@ -55,14 +55,9 @@ - - - - - diff --git a/src/Integration/MefServices/ActiveSolutionBoundTracker.cs b/src/Integration/MefServices/ActiveSolutionBoundTracker.cs index 4c0f4fa5f1..d994d0b4a9 100644 --- a/src/Integration/MefServices/ActiveSolutionBoundTracker.cs +++ b/src/Integration/MefServices/ActiveSolutionBoundTracker.cs @@ -18,16 +18,13 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using System; using System.ComponentModel.Composition; -using System.Diagnostics; -using System.Threading; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using SonarLint.VisualStudio.Core; using SonarLint.VisualStudio.Core.Binding; -using SonarLint.VisualStudio.Integration.State; using SonarQube.Client; +using ErrorHandler = Microsoft.VisualStudio.ErrorHandler; using Task = System.Threading.Tasks.Task; namespace SonarLint.VisualStudio.Integration @@ -45,9 +42,9 @@ namespace SonarLint.VisualStudio.Integration [PartCreationPolicy(CreationPolicy.Shared)] internal sealed class ActiveSolutionBoundTracker : IActiveSolutionBoundTracker, IActiveSolutionChangedHandler, IDisposable, IPartImportsSatisfiedNotification { - private readonly IHost extensionHost; private readonly IActiveSolutionTracker solutionTracker; private readonly IConfigurationProvider configurationProvider; + private readonly ISonarQubeService sonarQubeService; private readonly IVsMonitorSelection vsMonitorSelection; private readonly IBoundSolutionGitMonitor gitEventsMonitor; private readonly IConfigScopeUpdater configScopeUpdater; @@ -64,14 +61,13 @@ internal sealed class ActiveSolutionBoundTracker : IActiveSolutionBoundTracker, [ImportingConstructor] public ActiveSolutionBoundTracker([Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider, - IHost host, IActiveSolutionTracker activeSolutionTracker, IConfigScopeUpdater configScopeUpdater, ILogger logger, IBoundSolutionGitMonitor gitEventsMonitor, - IConfigurationProvider configurationProvider) + IConfigurationProvider configurationProvider, + ISonarQubeService sonarQubeService) { - extensionHost = host; solutionTracker = activeSolutionTracker; this.gitEventsMonitor = gitEventsMonitor; this.logger = logger; @@ -82,11 +78,9 @@ public ActiveSolutionBoundTracker([Import(typeof(SVsServiceProvider))] IServiceP vsMonitorSelection.GetCmdUIContextCookie(ref BoundSolutionUIContext.Guid, out boundSolutionContextCookie); this.configurationProvider = configurationProvider; + this.sonarQubeService = sonarQubeService; this.configScopeUpdater = configScopeUpdater; - // The user changed the binding through the Team Explorer - extensionHost.VisualStateManager.BindingStateChanged += OnBindingStateChanged; - // The solution changed inside the IDE solutionTracker.ActiveSolutionChanged += OnActiveSolutionChanged; @@ -106,11 +100,6 @@ public void HandleBindingChange(bool isBindingCleared) this.RaiseAnalyzersChangedIfBindingChanged(GetBindingConfiguration(), isBindingCleared); } - - private void OnBindingStateChanged(object sender, BindingStateEventArgs e) - { - HandleBindingChange(e.IsBindingCleared); - } private BindingConfiguration GetBindingConfiguration() { @@ -141,7 +130,7 @@ private async void OnActiveSolutionChanged(object sender, ActiveSolutionChangedE this.RaiseAnalyzersChangedIfBindingChanged(newBindingConfiguration); } - catch (Exception ex) when (!Microsoft.VisualStudio.ErrorHandler.IsCriticalException(ex)) + catch (Exception ex) when (!ErrorHandler.IsCriticalException(ex)) { logger.WriteLine($"Error handling solution change: {ex.Message}"); } @@ -149,18 +138,9 @@ private async void OnActiveSolutionChanged(object sender, ActiveSolutionChangedE private async Task UpdateConnectionAsync(BindingConfiguration bindingConfiguration) { - ISonarQubeService sonarQubeService = this.extensionHost.SonarQubeService; - if (sonarQubeService.IsConnected) { - if (this.extensionHost.ActiveSection?.DisconnectCommand.CanExecute(null) == true) - { - this.extensionHost.ActiveSection.DisconnectCommand.Execute(null); - } - else - { - sonarQubeService.Disconnect(); - } + sonarQubeService.Disconnect(); } Debug.Assert(!sonarQubeService.IsConnected, @@ -171,7 +151,7 @@ private async Task UpdateConnectionAsync(BindingConfiguration bindingConfigurati if (boundProject != null) { var connectionInformation = boundProject.CreateConnectionInformation(); - await Core.WebServiceHelper.SafeServiceCallAsync(() => sonarQubeService.ConnectAsync(connectionInformation, + await WebServiceHelper.SafeServiceCallAsync(() => sonarQubeService.ConnectAsync(connectionInformation, CancellationToken.None), this.logger); } } @@ -220,7 +200,6 @@ private void Dispose(bool disposing) { this.disposed = true; this.solutionTracker.ActiveSolutionChanged -= this.OnActiveSolutionChanged; - this.extensionHost.VisualStateManager.BindingStateChanged -= this.OnBindingStateChanged; this.gitEventsMonitor.HeadChanged -= GitEventsMonitor_HeadChanged; } } diff --git a/src/Integration/MefServices/ConnectedModeWindowEventBasedScheduler.cs b/src/Integration/MefServices/ConnectedModeWindowEventBasedScheduler.cs deleted file mode 100644 index bbb96b86ef..0000000000 --- a/src/Integration/MefServices/ConnectedModeWindowEventBasedScheduler.cs +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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.ComponentModel.Composition; - -namespace SonarLint.VisualStudio.Integration.MefServices -{ - internal interface IConnectedModeWindowEventListener: IDisposable - { - void SubscribeToConnectedModeWindowEvents(IHost connectedModeWindowHost); - } - - internal interface IConnectedModeWindowEventBasedScheduler - { - void ScheduleActionOnNextEvent(Action action); - } - - [Export(typeof(IConnectedModeWindowEventBasedScheduler))] - [Export(typeof(IConnectedModeWindowEventListener))] - [PartCreationPolicy(CreationPolicy.Shared)] - internal sealed class ConnectedModeWindowEventBasedScheduler : IConnectedModeWindowEventBasedScheduler, IConnectedModeWindowEventListener - { - private bool disposed = false; - - private IHost host; - private Action nextScheduledAction; - - public void SubscribeToConnectedModeWindowEvents(IHost connectedModeWindowHost) - { - if (host != null) - { - throw new ArgumentException(nameof(host)); - } - - host = connectedModeWindowHost; - host.ActiveSectionChanged += ActiveSectionChangedListener; - } - - public void ScheduleActionOnNextEvent(Action action) - { - nextScheduledAction = action; - } - - internal /* for testing */ void ActiveSectionChangedListener(object sender, EventArgs args) - { - if (nextScheduledAction == null) - { - return; - } - - HandlePostponedAutobind(); - } - - private void HandlePostponedAutobind() - { - nextScheduledAction(); - nextScheduledAction = null; - } - - public void Dispose() - { - if (disposed) - { - return; - } - - disposed = true; - host.ActiveSectionChanged -= ActiveSectionChangedListener; - } - } -} diff --git a/src/Integration/MefServices/IHost.cs b/src/Integration/MefServices/IHost.cs deleted file mode 100644 index 3e9c5a3fe2..0000000000 --- a/src/Integration/MefServices/IHost.cs +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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 Microsoft.Alm.Authentication; -using SonarLint.VisualStudio.ConnectedMode.Shared; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Integration.State; -using SonarLint.VisualStudio.Integration.TeamExplorer; -using SonarQube.Client; - -namespace SonarLint.VisualStudio.Integration -{ - internal interface IHost - { - /// - /// . Not null. - /// - ISonarQubeService SonarQubeService { get; } - - /// - /// The visual state manager. Not null. - /// - IStateManager VisualStateManager { get; } - - /// - /// The currently active section. Null when no active section. - /// - ISectionController ActiveSection { get; } - - /// - /// Sets the with the specified - /// - /// Required - void SetActiveSection(ISectionController section); - - /// - /// Change event when the changed - /// - event EventHandler ActiveSectionChanged; - - /// - /// Clears the - /// - void ClearActiveSection(); - - ILogger Logger { get; } - } -} diff --git a/src/Integration/MefServices/VsSessionHost.cs b/src/Integration/MefServices/VsSessionHost.cs deleted file mode 100644 index d561053e7f..0000000000 --- a/src/Integration/MefServices/VsSessionHost.cs +++ /dev/null @@ -1,278 +0,0 @@ -/* - * 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.ComponentModel.Composition; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Core.Binding; -using SonarLint.VisualStudio.Integration.MefServices; -using SonarLint.VisualStudio.Integration.Progress; -using SonarLint.VisualStudio.Integration.State; -using SonarLint.VisualStudio.Integration.TeamExplorer; -using SonarQube.Client; -using ErrorHandler = Microsoft.VisualStudio.ErrorHandler; - -namespace SonarLint.VisualStudio.Integration -{ - [Export(typeof(IHost))] - [PartCreationPolicy(CreationPolicy.Shared)] - internal sealed class VsSessionHost : IHost, IProgressStepRunnerWrapper, IDisposable - { - private readonly IActiveSolutionTracker solutionTracker; - private readonly IConfigurationProvider configurationProvider; - private readonly IConnectedModeWindowEventListener connectedModeWindowEventListener; - private readonly IProgressStepRunnerWrapper progressStepRunner; - - private bool isDisposed; - private bool resetBindingWhenAttaching = true; - - [ImportingConstructor] - public VsSessionHost(ISonarQubeService sonarQubeService, - IActiveSolutionTracker solutionTracker, - IConfigurationProvider configurationProvider, - IConnectedModeWindowEventListener connectedModeWindowEventListener, - ILogger logger) - : this( - null, - null, - sonarQubeService, - solutionTracker, - configurationProvider, - connectedModeWindowEventListener, - logger) - { - } - - internal /*for test purposes*/ VsSessionHost(IStateManager state, - IProgressStepRunnerWrapper progressStepRunner, - ISonarQubeService sonarQubeService, - IActiveSolutionTracker solutionTracker, - IConfigurationProvider configurationProvider, - IConnectedModeWindowEventListener connectedModeWindowEventListener, - ILogger logger) - { - this.VisualStateManager = state ?? new StateManager(this, new TransferableVisualState()); - this.progressStepRunner = progressStepRunner ?? this; - this.SonarQubeService = sonarQubeService ?? throw new ArgumentNullException(nameof(sonarQubeService)); - this.solutionTracker = solutionTracker ?? throw new ArgumentNullException(nameof(solutionTracker)); - this.configurationProvider = configurationProvider ?? throw new ArgumentNullException(nameof(configurationProvider)); - this.connectedModeWindowEventListener = connectedModeWindowEventListener; - this.solutionTracker.ActiveSolutionChanged += this.OnActiveSolutionChanged; - this.Logger = logger ?? throw new ArgumentNullException(nameof(logger)); - connectedModeWindowEventListener.SubscribeToConnectedModeWindowEvents(this); - } - - #region IProgressStepRunnerWrapper - void IProgressStepRunnerWrapper.AbortAll() - { - ProgressStepRunner.AbortAll(); - } - - void IProgressStepRunnerWrapper.ChangeHost(IProgressControlHost host) - { - ProgressStepRunner.ChangeHost(host); - } - #endregion - - #region IHost - public event EventHandler ActiveSectionChanged; - - public IStateManager VisualStateManager { get; } - - public ISonarQubeService SonarQubeService { get; } - - public ISectionController ActiveSection { get; private set; } - - public ILogger Logger { get; } - - public void SetActiveSection(ISectionController section) - { - if (section == null) - { - throw new ArgumentNullException(nameof(section)); - } - - Debug.Assert(this.ActiveSection == null, "Already attached. Detach first"); - this.ActiveSection = section; - this.VisualStateManager.SyncCommandFromActiveSection(); - - this.TransferState(); - - if (this.resetBindingWhenAttaching) - { - this.resetBindingWhenAttaching = false; - - // The connect section activated after the solution is opened, - // so reset the binding if applicable. No reason to abort since - // this is the first time after the solution was opened so that - // we switched to the connect section. - this.ResetBinding(abortCurrentlyRunningWorklows: false, clearCurrentBinding: false); - } - - this.OnActiveSectionChanged(); - } - - public void ClearActiveSection() - { - if (this.ActiveSection == null) // Can be called multiple times - { - return; - } - - this.ActiveSection.ViewModel.State = null; - this.ActiveSection = null; - this.VisualStateManager.SyncCommandFromActiveSection(); - - this.OnActiveSectionChanged(); - } - - private void TransferState() - { - Debug.Assert(this.ActiveSection != null, "Not attached to any section attached"); - - if (this.ActiveSection != null) - { - this.ActiveSection.ViewModel.State = this.VisualStateManager.ManagedState; - - IProgressControlHost progressHost = this.ActiveSection.ProgressHost; - Debug.Assert(progressHost != null, "IProgressControlHost is expected"); - this.progressStepRunner.ChangeHost(progressHost); - } - } - - private void OnActiveSectionChanged() - { - this.ActiveSectionChanged?.Invoke(this, EventArgs.Empty); - } - #endregion - - #region Active solution changed event handler - private void OnActiveSolutionChanged(object sender, ActiveSolutionChangedEventArgs args) - { - // TODO: simplifying the eventing model. - // Both this class and the ActiveSolutionBoundTracker both listen for solution closing events. - // The ASBT can set the current configuration to Standalone, and this class reads the - // configuration. However, we can't control the order they receive the "solution closing" - // notification, so this class might try to read the configuration before it has been - // updated by the ASBT. - // To work round this, we have specifically check whether there is an open solution - // so we do the right thing here. - - // Reset, and abort workflows - this.ResetBinding(abortCurrentlyRunningWorklows: true, clearCurrentBinding: !args.IsSolutionOpen); - } - - private void ResetBinding(bool abortCurrentlyRunningWorklows, bool clearCurrentBinding) - { - if (abortCurrentlyRunningWorklows) - { - // We may have running workflows, abort them before proceeding - this.progressStepRunner.AbortAll(); - } - - var bindingConfig = this.SafeGetBindingConfig(); - if (clearCurrentBinding || bindingConfig == null || bindingConfig.Mode == SonarLintMode.Standalone) - { - this.ClearCurrentBinding(); - } - else - { - Debug.Assert(bindingConfig.Project != null, "Project should not be null unless in standalone mode"); - if (this.ActiveSection == null) - { - // In case the connect section is not active, make it so that next time it activates - // it will reset the binding then. - this.resetBindingWhenAttaching = true; - } - else - { - this.ApplyBindingInformation(bindingConfig); - } - } - } - - private void ClearCurrentBinding() - { - this.VisualStateManager.BoundProjectKey = null; - this.VisualStateManager.BoundProjectName = null; - - this.VisualStateManager.ClearBoundProject(); - } - - private void ApplyBindingInformation(BindingConfiguration bindingConfig) - { - // Set the project key that should become bound once the connection workflow has completed - this.VisualStateManager.BoundProjectKey = bindingConfig.Project.ServerProjectKey; - this.VisualStateManager.BoundProjectName = bindingConfig.Project.ServerProjectKey; - - Debug.Assert(this.ActiveSection != null, "Expected ActiveSection to be set"); - Debug.Assert(this.ActiveSection?.RefreshCommand != null, "Refresh command is not set"); - // Run the refresh workflow, passing the connection information - var refreshCmd = this.ActiveSection.RefreshCommand; - - var connection = bindingConfig.Project.CreateConnectionInformation(); - if (refreshCmd.CanExecute(connection)) - { - refreshCmd.Execute(connection); // start the workflow - } - } - - private BindingConfiguration SafeGetBindingConfig() - { - BindingConfiguration bindingConfig = null; - try - { - bindingConfig = configurationProvider.GetConfiguration(); - } - catch (Exception ex) - { - if (ErrorHandler.IsCriticalException(ex)) - { - throw; - } - - Debug.Fail("Unexpected exception: " + ex.ToString()); - } - - return bindingConfig; - } - #endregion - - #region IDisposable Support - private void Dispose(bool disposing) - { - if (!this.isDisposed) - { - if (disposing) - { - this.solutionTracker.ActiveSolutionChanged -= this.OnActiveSolutionChanged; - connectedModeWindowEventListener.Dispose(); - } - - this.isDisposed = true; - } - } - - public void Dispose() - { - Dispose(true); - } - #endregion - } -} diff --git a/src/Integration/Progress/IProgressControlHost.cs b/src/Integration/Progress/IProgressControlHost.cs deleted file mode 100644 index 1de92cfb52..0000000000 --- a/src/Integration/Progress/IProgressControlHost.cs +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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.Integration.Progress -{ - internal interface IProgressControlHost - { - /// - /// Request to host the for display purposes - /// - /// Required - void Host(ProgressControl progressControl); - } -} diff --git a/src/Integration/Progress/IProgressStepRunnerWrapper.cs b/src/Integration/Progress/IProgressStepRunnerWrapper.cs deleted file mode 100644 index c01766cc11..0000000000 --- a/src/Integration/Progress/IProgressStepRunnerWrapper.cs +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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.Integration.Progress -{ - // Test only interface to hide the implementation of ProgressStepRunner - internal interface IProgressStepRunnerWrapper - { - void ChangeHost(IProgressControlHost host); - - void AbortAll(); - } -} diff --git a/src/Integration/Progress/ProgressNotificationListener.cs b/src/Integration/Progress/ProgressNotificationListener.cs deleted file mode 100644 index 8776ab0d40..0000000000 --- a/src/Integration/Progress/ProgressNotificationListener.cs +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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 SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Progress.Controller; - -namespace SonarLint.VisualStudio.Integration.Progress -{ - /// - /// The listener will forward the progress notifications to the output window. - /// The listener will ignore empty and duplicate messages (duplicate with the previous one notification progress message) - /// - public sealed class ProgressNotificationListener : IDisposable - { - private readonly IProgressEvents progressEvents; - private readonly ILogger logger; - private string previousProgressDetail; - - public ProgressNotificationListener(IProgressEvents progressEvents, ILogger logger) - { - if (progressEvents == null) - { - throw new ArgumentNullException(nameof(progressEvents)); - } - - if (logger == null) - { - throw new ArgumentNullException(nameof(logger)); - } - - this.progressEvents = progressEvents; - this.logger = logger; - - this.progressEvents.StepExecutionChanged += this.OnStepExecutionChanged; - } - - public string MessageFormat - { - get; - set; - } - - private void OnStepExecutionChanged(object sender, StepExecutionChangedEventArgs e) - { - if (!string.IsNullOrWhiteSpace(e.ProgressDetailText) && !StringComparer.CurrentCulture.Equals(previousProgressDetail, e.ProgressDetailText)) - { - previousProgressDetail = e.ProgressDetailText; - string format = string.IsNullOrWhiteSpace(this.MessageFormat) ? "{0}" : this.MessageFormat; - this.logger.WriteLine(format, e.ProgressDetailText); - } - } - - #region IDisposable Support - private bool disposedValue; - - private void Dispose(bool disposing) - { - if (!disposedValue) - { - if (disposing) - { - this.progressEvents.StepExecutionChanged -= this.OnStepExecutionChanged; - } - - disposedValue = true; - } - } - - public void Dispose() - { - // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - Dispose(true); - } - #endregion - } -} diff --git a/src/Integration/Progress/ProgressStepRunner.cs b/src/Integration/Progress/ProgressStepRunner.cs deleted file mode 100644 index 66b2262928..0000000000 --- a/src/Integration/Progress/ProgressStepRunner.cs +++ /dev/null @@ -1,180 +0,0 @@ -/* - * 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.Diagnostics; -using System.Linq; -using Microsoft.VisualStudio.Shell; -using Microsoft.VisualStudio.Shell.Interop; -using SonarLint.VisualStudio.Integration.Resources; -using SonarLint.VisualStudio.Progress.Controller; -using SonarLint.VisualStudio.Progress.Controller.ErrorNotification; -using SonarLint.VisualStudio.Progress.Observation; -using SonarLint.VisualStudio.Progress.Observation.ViewModels; - -namespace SonarLint.VisualStudio.Integration.Progress -{ - internal static class ProgressStepRunner - { - private readonly static Dictionary observedControllersMap = new Dictionary(); - - internal static IReadOnlyDictionary ObservedControllers - { - get - { - return observedControllersMap; - } - } - - internal /*for testing purposes*/ static void Reset() - { - observedControllersMap.Clear(); - } - - public static IProgressEvents StartAsync(IServiceProvider sp, IProgressControlHost host, Func stepFactory) - { - if (sp == null) - { - throw new ArgumentNullException(nameof(sp)); - } - - if (host == null) - { - throw new ArgumentNullException(nameof(host)); - } - - if (stepFactory == null) - { - throw new ArgumentNullException(nameof(stepFactory)); - } - - Debug.Assert(ThreadHelper.CheckAccess(), "Expected to be called on the UI thread"); - - // Initialize a controller and an observer - var controller = new SequentialProgressController(sp); - controller.Initialize(stepFactory(controller)); - - IVsOutputWindowPane sonarLintPane = VsShellUtils.GetOrCreateSonarLintOutputPane(sp); - - bool logFullMessage; -#if DEBUG - logFullMessage = true; -#else - logFullMessage = false; -#endif - var notifier = new VsOutputWindowPaneNotifier(sp, - sonarLintPane, - ensureOutputVisible: true, - messageFormat: Strings.UnexpectedWorkflowError, - logFullException: logFullMessage); - controller.ErrorNotificationManager.AddNotifier(notifier); - - Observe(controller, host); - controller.RunOnFinished(r => observedControllersMap.Remove(controller)); -#pragma warning disable 4014 // We do want to start and forget. All the errors will be forwarded via the error notification manager - controller.StartAsync(); -#pragma warning restore 4014 - - return controller; - } - - /// - /// Will use the specified to visualize the progress of - /// - public static ProgressObserver Observe(IProgressController controller, IProgressControlHost host) - { - if (controller == null) - { - throw new ArgumentNullException(nameof(controller)); - } - - if (host == null) - { - throw new ArgumentNullException(nameof(host)); - } - - Debug.Assert(ThreadHelper.CheckAccess(), "Expected to be called on the UI thread"); - - ProgressControl visualizer = VisualizeInHost(host); - return Observe(controller, visualizer); - } - - /// - /// Re-hosts all the current observers into the specified - /// - public static void ChangeHost(IProgressControlHost host) - { - if (host == null) - { - throw new ArgumentNullException(nameof(host)); - } - - Debug.Assert(ThreadHelper.CheckAccess(), "Expected to be called on the UI thread"); - - Lazy visualizer = new Lazy(() => VisualizeInHost(host)); - observedControllersMap.ToList().ForEach(kv => - { - if (!kv.Value.IsFinished) - { - ProgressControllerViewModel state = kv.Value.State; - kv.Value.Dispose(); // Dispose previous observer - observedControllersMap[kv.Key] = CreateObserver(kv.Key, visualizer.Value, state); - } - }); - } - - /// - /// Aborts all the currently executing controllers - /// - public static void AbortAll() - { - Debug.Assert(ThreadHelper.CheckAccess(), "Expected to be called on the UI thread"); - - observedControllersMap.ToList().ForEach(kv => - { - kv.Key.TryAbort(); // Try to abort (could already be aborted) - kv.Value.Dispose(); // Clean up the observer - observedControllersMap.Remove(kv.Key); - }); - - } - - private static ProgressControl VisualizeInHost(IProgressControlHost host) - { - Debug.Assert(ThreadHelper.CheckAccess(), "Expected to be called on the UI thread"); - var progressControl = new ProgressControl(); - host.Host(progressControl); - return progressControl; - } - - private static ProgressObserver Observe(IProgressController controller, IProgressVisualizer visualizer) - { - ProgressObserver observer = CreateObserver(controller, visualizer, null); - observedControllersMap.Add(controller, observer); - return observer; - } - - private static ProgressObserver CreateObserver(IProgressController controller, IProgressVisualizer visualizer, ProgressControllerViewModel state) - { - return ProgressObserver.StartObserving(controller, visualizer, state); - } - } -} diff --git a/src/Integration/Resources/Strings.Designer.cs b/src/Integration/Resources/Strings.Designer.cs index 8e2f29ba7a..3569f50c96 100644 --- a/src/Integration/Resources/Strings.Designer.cs +++ b/src/Integration/Resources/Strings.Designer.cs @@ -60,143 +60,7 @@ internal Strings() { } /// - /// Looks up a localized string similar to Bound project: {0}. - /// - public static string AutomationProjectBoundDescription { - get { - return ResourceManager.GetString("AutomationProjectBoundDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to SonarQube server: {0}. - /// - public static string AutomationServerDescription { - get { - return ResourceManager.GetString("AutomationServerDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to SonarQube server: {0}. This server has no projects.. - /// - public static string AutomationServerNoProjectsDescription { - get { - return ResourceManager.GetString("AutomationServerNoProjectsDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to save configuration. - /// - public static string Bind_FailedToSaveConfiguration { - get { - return ResourceManager.GetString("Bind_FailedToSaveConfiguration", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to INFO: SonarLint connected mode no longer installs the analyzers NuGet packages. The analyzers embedded in the VSIX are used instead.. - /// - public static string Bind_NuGetAnalyzersNoLongerInstalled { - get { - return ResourceManager.GetString("Bind_NuGetAnalyzersNoLongerInstalled", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Project {0} RuleSet declarations: - /// {1}. - /// - public static string Bind_ProjectRulesetDeclarations { - get { - return ResourceManager.GetString("Bind_ProjectRulesetDeclarations", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Saving configuration.... - /// - public static string Bind_SavingBindingConfiguration { - get { - return ResourceManager.GetString("Bind_SavingBindingConfiguration", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Updating binding.... - /// - public static string Bind_UpdatingNewStyleBinding { - get { - return ResourceManager.GetString("Bind_UpdatingNewStyleBinding", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Bind. - /// - public static string BindButtonText { - get { - return ResourceManager.GetString("BindButtonText", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Binding projects. - /// - public static string BindingProjectsDisplayMessage { - get { - return ResourceManager.GetString("BindingProjectsDisplayMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Binding solution to SonarQube project: {0}. - /// - public static string BindingSolutionPrefixMessageFormat { - get { - return ResourceManager.GetString("BindingSolutionPrefixMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to [Binding check] Error checking bound project settings file: {0}. - /// - public static string BindingUpdateFailedToCheckSettings { - get { - return ResourceManager.GetString("BindingUpdateFailedToCheckSettings", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Bound project with key '{0}' was not found on the connected server. Select a different SonarQube project, or use [a different login]() that has access to the desired project.. - /// - public static string BoundProjectNotFound { - get { - return ResourceManager.GetString("BoundProjectNotFound", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Browse the server in your web browser.. - /// - public static string BrowserServerMenuItemTooltip { - get { - return ResourceManager.GetString("BrowserServerMenuItemTooltip", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Browse. - /// - public static string BrowseServerMenuItemDisplayText { - get { - return ResourceManager.GetString("BrowseServerMenuItemDisplayText", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Ca_ncel. + /// Looks up a localized string similar to Cancel. /// public static string CancelButtonText { get { @@ -204,15 +68,6 @@ public static string CancelButtonText { } } - /// - /// Looks up a localized string similar to Cancel. - /// - public static string CancelLinkText { - get { - return ResourceManager.GetString("CancelLinkText", resourceCulture); - } - } - /// /// Looks up a localized string similar to A SonarQube server plugin has a malformed version and cannot be compared. Version was '{0}'.. /// @@ -222,115 +77,6 @@ public static string CannotCompareVersionStrings { } } - /// - /// Looks up a localized string similar to _Connect. - /// - public static string ConnectButtonText { - get { - return ResourceManager.GetString("ConnectButtonText", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Connect to a SonarQube Server. - /// - public static string ConnectDialogTitle { - get { - return ResourceManager.GetString("ConnectDialogTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Connecting to {0}. - /// - public static string ConnectingToSever { - get { - return ResourceManager.GetString("ConnectingToSever", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Connecting to SonarQube server: {0}. - /// - public static string ConnectingToSonarQubePrefixMessageFormat { - get { - return ResourceManager.GetString("ConnectingToSonarQubePrefixMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cannot connect to the SonarQube server. Make sure you provided the correct connection information, including your sign-in credentials, and [try again](). - ///See more information in the output window (SonarLint).. - /// - public static string ConnectionFailed { - get { - return ResourceManager.GetString("ConnectionFailed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Canceled. - /// - public static string ConnectionResultCancellation { - get { - return ResourceManager.GetString("ConnectionResultCancellation", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed. - /// - public static string ConnectionResultFailure { - get { - return ResourceManager.GetString("ConnectionResultFailure", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Succeeded. - /// - public static string ConnectionResultSuccess { - get { - return ResourceManager.GetString("ConnectionResultSuccess", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Retrieving organizations. - /// - public static string ConnectionStepRetrievingOrganizations { - get { - return ResourceManager.GetString("ConnectionStepRetrievingOrganizations", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Retrieving projects. - /// - public static string ConnectionStepRetrievingProjects { - get { - return ResourceManager.GetString("ConnectionStepRetrievingProjects", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Validating credentials. - /// - public static string ConnectionStepValidatinCredentials { - get { - return ResourceManager.GetString("ConnectionStepValidatinCredentials", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Connect.... - /// - public static string ConnectLinkText { - get { - return ResourceManager.GetString("ConnectLinkText", resourceCulture); - } - } - /// /// Looks up a localized string similar to Connections. /// @@ -340,24 +86,6 @@ public static string ConnectSectionTitle { } } - /// - /// Looks up a localized string similar to Warning: could not find CodeAnalysisRuleSet for project '{0}'. - /// - public static string CouldNotFindCodeAnalysisRuleSetPropertyOnProject { - get { - return ResourceManager.GetString("CouldNotFindCodeAnalysisRuleSetPropertyOnProject", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Insecure schemes must be a subset of supported schemes. - /// - public static string ExceptionInsecureSchemesIsNotSubset { - get { - return ResourceManager.GetString("ExceptionInsecureSchemesIsNotSubset", resourceCulture); - } - } - /// /// Looks up a localized string similar to No file was found under '{0}'. /// @@ -376,106 +104,6 @@ public static string ExclusionGetError { } } - /// - /// Looks up a localized string similar to The target URI must be an absolute URI. - /// - public static string ExpectedAbsoluteUris { - get { - return ResourceManager.GetString("ExpectedAbsoluteUris", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to install NuGet package '{0}' for project '{1}'. Message: {2}. - /// - public static string FailedDuringNuGetPackageInstall { - get { - return ResourceManager.GetString("FailedDuringNuGetPackageInstall", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Error identifying project '{0}' as test project: {1}. - /// - public static string FailedToCheckIfTestProject { - get { - return ResourceManager.GetString("FailedToCheckIfTestProject", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to delete credentials for {0}.. - /// - public static string FailedToDeleteCredentials { - get { - return ResourceManager.GetString("FailedToDeleteCredentials", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to bind the solution to SonarQube project, [try again](). - ///See more information in the output window (SonarLint).. - /// - public static string FailedToToBindSolution { - get { - return ResourceManager.GetString("FailedToToBindSolution", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to unpack additional files required for analyzers. - /// - public static string FailedToUnpackAdditionalFiles { - get { - return ResourceManager.GetString("FailedToUnpackAdditionalFiles", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Completed successfully. - /// - public static string FinishedSolutionBindingWorkflowSuccessful { - get { - return ResourceManager.GetString("FinishedSolutionBindingWorkflowSuccessful", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Hide Unbound Projects. - /// - public static string HideUnboundProjectsCommandText { - get { - return ResourceManager.GetString("HideUnboundProjectsCommandText", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The URL you entered is not secure. Use HTTPS connections to protect sensitive information.. - /// - public static string InsecureProtocolWarning { - get { - return ResourceManager.GetString("InsecureProtocolWarning", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Colon (":") is not a permitted character.. - /// - public static string InvalidCharacterColon { - get { - return ResourceManager.GetString("InvalidCharacterColon", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to '{0}' is not a valid URL. - /// - public static string InvalidServerUriFormat { - get { - return ResourceManager.GetString("InvalidServerUriFormat", resourceCulture); - } - } - /// /// Looks up a localized string similar to Test project regular expression pattern '{0}' is invalid. The default will be used instead. Please check your server settings.. /// @@ -485,15 +113,6 @@ public static string InvalidTestProjectRegexPattern { } } - /// - /// Looks up a localized string similar to More info.... - /// - public static string MoreInfoLinkText { - get { - return ResourceManager.GetString("MoreInfoLinkText", resourceCulture); - } - } - /// /// Looks up a localized string similar to The issue is valid but will not be fixed now. It represents accepted technical debt.. /// @@ -603,36 +222,9 @@ public static string MuteWindow_WontFixTitle { } /// - /// Looks up a localized string similar to None (for selected SonarQube project's quality profile). + /// Looks up a localized string similar to Failed to fetch notifications: {0}. /// - public static string NoProjectsApplicableForBinding { - get { - return ResourceManager.GetString("NoProjectsApplicableForBinding", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to None (for selected SonarQube project's quality profile). - /// - public static string NoProjectsExcludedFromBinding { - get { - return ResourceManager.GetString("NoProjectsExcludedFromBinding", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Field can not be empty. - /// - public static string NotEmptyValidatorRequiredField { - get { - return ResourceManager.GetString("NotEmptyValidatorRequiredField", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to fetch notifications: {0}. - /// - public static string Notifications_ERROR_Fetching { + public static string Notifications_ERROR_Fetching { get { return ResourceManager.GetString("Notifications_ERROR_Fetching", resourceCulture); } @@ -647,258 +239,6 @@ public static string Notifications_NotSupported { } } - /// - /// Looks up a localized string similar to Password:. - /// - public static string PasswordLabel { - get { - return ResourceManager.GetString("PasswordLabel", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to SonarSource and Microsoft. - /// - public static string ProductAuthors { - get { - return ResourceManager.GetString("ProductAuthors", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Could not get the IVsHierarchy/IVsBuildPropertyStorage for the given project is IVsBuildPropertyStorage for the given EnvDTE.Project. - /// - public static string ProjectFilterDteProjectFailedToGetIVs { - get { - return ResourceManager.GetString("ProjectFilterDteProjectFailedToGetIVs", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Key: {0}. - /// - public static string ProjectToolTipKeyFormat { - get { - return ResourceManager.GetString("ProjectToolTipKeyFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to {0} (Bound). - /// - public static string ProjectToolTipProjectNameFormat { - get { - return ResourceManager.GetString("ProjectToolTipProjectNameFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to download quality profile. Name: '{0}', Key: '{1}', Language: '{2}'. - /// - public static string QualityProfileDownloadFailedMessageFormat { - get { - return ResourceManager.GetString("QualityProfileDownloadFailedMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Refresh. - /// - public static string RefreshCommandDisplayText { - get { - return ResourceManager.GetString("RefreshCommandDisplayText", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Will refetch the SonarQube projects from the server using the existing credentials. - /// - public static string RefreshCommandTooltip { - get { - return ResourceManager.GetString("RefreshCommandTooltip", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Rule set generated from SonarQube. - /// - public static string RuleSetDescription { - get { - return ResourceManager.GetString("RuleSetDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Ruleset could not be loaded. File: {0}, Error: {1}. - /// - public static string RulesetSerializer_FailedToLoadRuleset { - get { - return ResourceManager.GetString("RulesetSerializer_FailedToLoadRuleset", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Ruleset file does not exist: {0}. - /// - public static string RulesetSerializer_RulesetDoesNotExist { - get { - return ResourceManager.GetString("RulesetSerializer_RulesetDoesNotExist", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Downloading Server Exclusions. - /// - public static string SaveServerExclusionsMessage { - get { - return ResourceManager.GetString("SaveServerExclusionsMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to check out files for editing. Error flags: {0}. - /// - public static string SCCFS_FailedToCheckOutFilesForEditing { - get { - return ResourceManager.GetString("SCCFS_FailedToCheckOutFilesForEditing", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to check out files for saving. Error code: {0}. - /// - public static string SCCFS_FailedToCheckOutFilesForSave { - get { - return ResourceManager.GetString("SCCFS_FailedToCheckOutFilesForSave", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to (no projects). - /// - public static string ServerNoProjectsInlineText { - get { - return ResourceManager.GetString("ServerNoProjectsInlineText", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to This server has no available projects with which to bind to. Please create at least one project and then refresh this connection.. - /// - public static string ServerNoProjectsToolTipText { - get { - return ResourceManager.GetString("ServerNoProjectsToolTipText", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Show All Projects. - /// - public static string ShowAllProjectsCommandText { - get { - return ResourceManager.GetString("ShowAllProjectsCommandText", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The operation requires an open solution.. - /// - public static string SolutionIsClosed { - get { - return ResourceManager.GetString("SolutionIsClosed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Checking for unbound projects.. - /// - public static string SonarLintCheckingForUnboundProjects { - get { - return ResourceManager.GetString("SonarLintCheckingForUnboundProjects", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to attach info bar to error list tool window.. - /// - public static string SonarLintFailedToAttachInfoBarToErrorList { - get { - return ResourceManager.GetString("SonarLintFailedToAttachInfoBarToErrorList", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Some binding configuration is missing. Please rebind the solution.. - /// - public static string SonarLintFoundUnboundSolution { - get { - return ResourceManager.GetString("SonarLintFoundUnboundSolution", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to SonarLint can now tell you when the quality profile in SonarQube project is updated. Press Update to enable this feature now.. - /// - public static string SonarLintInfoBarOldBindingFile { - get { - return ResourceManager.GetString("SonarLintInfoBarOldBindingFile", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to SonarLint: a binding update is required. See output window (SonarLint) for more information.. - /// - public static string SonarLintInfoBarUnboundProjectsMessage { - get { - return ResourceManager.GetString("SonarLintInfoBarUnboundProjectsMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The current solution is not bound to the expected SonarQube project, aborting.. - /// - public static string SonarLintInfoBarUpdateCommandInvalidSolutionBindings { - get { - return ResourceManager.GetString("SonarLintInfoBarUpdateCommandInvalidSolutionBindings", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Could not update the binding because the command is not currently available. Please try again and ensure that the solution is not building or currently being debugged.. - /// - public static string SonarLintInfoBarUpdateCommandIsBusyRetry { - get { - return ResourceManager.GetString("SonarLintInfoBarUpdateCommandIsBusyRetry", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The SonarQube Team Explorer page was closed and we cannot proceed with the update. Please try again.. - /// - public static string SonarLintInfoBarUpdateCommandRetryNoActiveSection { - get { - return ResourceManager.GetString("SonarLintInfoBarUpdateCommandRetryNoActiveSection", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Update. - /// - public static string SonarLintInfoBarUpdateCommandText { - get { - return ResourceManager.GetString("SonarLintInfoBarUpdateCommandText", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to No unbound projects were found.. - /// - public static string SonarLintNoUnboundProjectWereFound { - get { - return ResourceManager.GetString("SonarLintNoUnboundProjectWereFound", resourceCulture); - } - } - /// /// Looks up a localized string similar to SonarLint. /// @@ -908,186 +248,6 @@ public static string SonarLintOutputPaneTitle { } } - /// - /// Looks up a localized string similar to Checking if SonarQube Quality profile has changed.. - /// - public static string SonarLintProfileCheck { - get { - return ResourceManager.GetString("SonarLintProfileCheck", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The SonarQube project is using a different Quality Profile. Update is required.. - /// - public static string SonarLintProfileCheckDifferentProfile { - get { - return ResourceManager.GetString("SonarLintProfileCheckDifferentProfile", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Unexpected failure getting quality profile information. Automatic quality profile update will not be performed.. - /// - public static string SonarLintProfileCheckFailed { - get { - return ResourceManager.GetString("SonarLintProfileCheckFailed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The binding information is out of date. Update is required.. - /// - public static string SonarLintProfileCheckNoProfiles { - get { - return ResourceManager.GetString("SonarLintProfileCheckNoProfiles", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The Quality Profile for the SonarQube project has changed. Update is required.. - /// - public static string SonarLintProfileCheckProfileUpdated { - get { - return ResourceManager.GetString("SonarLintProfileCheckProfileUpdated", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Quality Profile is up-to-date.. - /// - public static string SonarLintProfileCheckQualityProfileIsUpToDate { - get { - return ResourceManager.GetString("SonarLintProfileCheckQualityProfileIsUpToDate", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The solution has changed and now requires additional Quality Profiles to be included. Update is required.. - /// - public static string SonarLintProfileCheckSolutionRequiresMoreProfiles { - get { - return ResourceManager.GetString("SonarLintProfileCheckSolutionRequiresMoreProfiles", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to SonarQube is an open source platform to manage code quality. Connect your solution to an existing SonarQube Server to get the same issues in Visual Studio and in your SonarQube server.. - /// - public static string SonarQubeDescription { - get { - return ResourceManager.GetString("SonarQubeDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to SonarQube. - /// - public static string SonarQubeName { - get { - return ResourceManager.GetString("SonarQubeName", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Warning failed calling '{0}' (http error code={1}). Some of the functionality will be reduced.. - /// - public static string SonarQubeOptionalServiceFailed { - get { - return ResourceManager.GetString("SonarQubeOptionalServiceFailed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to {0}profiles/show?key={1}. - /// - public static string SonarQubeQualityProfilePageUrlFormat { - get { - return ResourceManager.GetString("SonarQubeQualityProfilePageUrlFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to SonarQube - {0} {1}. - /// - public static string SonarQubeRuleSetNameFormat { - get { - return ResourceManager.GetString("SonarQubeRuleSetNameFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to SonarQube server:. - /// - public static string SonarQubeServerLabel { - get { - return ResourceManager.GetString("SonarQubeServerLabel", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Started. - /// - public static string StartedSolutionBindingWorkflow { - get { - return ResourceManager.GetString("StartedSolutionBindingWorkflow", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to {0}. - /// - public static string SubTextPaddingFormat { - get { - return ResourceManager.GetString("SubTextPaddingFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Checking for suppressions.... - /// - public static string Suppression_Checking { - get { - return ResourceManager.GetString("Suppression_Checking", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Number of suppressions found: {0}. - /// - public static string Suppression_FinishedChecking { - get { - return ResourceManager.GetString("Suppression_FinishedChecking", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to fetch suppressions: {0}. - /// - public static string Suppressions_ERROR_Fetching { - get { - return ResourceManager.GetString("Suppressions_ERROR_Fetching", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cannot fetch suppressions - not connected to a SonarQube server. - /// - public static string Suppressions_NotConnected { - get { - return ResourceManager.GetString("Suppressions_NotConnected", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Update. - /// - public static string SyncButtonText { - get { - return ResourceManager.GetString("SyncButtonText", resourceCulture); - } - } - /// /// Looks up a localized string similar to SonarQube. /// @@ -1096,117 +256,5 @@ public static string TeamExplorerPageTitle { return ResourceManager.GetString("TeamExplorerPageTitle", resourceCulture); } } - - /// - /// Looks up a localized string similar to Telemetry: error occurred when recording: {0}. - /// - public static string Telemetry_ERROR_Recording { - get { - return ResourceManager.GetString("Telemetry_ERROR_Recording", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Telemetry: error occurred when sending: {0}. - /// - public static string Telemetry_ERROR_SendingTelemetry { - get { - return ResourceManager.GetString("Telemetry_ERROR_SendingTelemetry", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Toggle visibility of unbound projects for this server.. - /// - public static string ToggleShowAllProjectsCommandTooltip { - get { - return ResourceManager.GetString("ToggleShowAllProjectsCommandTooltip", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to {0}: Unexpected error: {1}. - /// - ///You can help us improve by reporting this bug at {2}. - /// - ///. - /// - public static string UnexpectedErrorMessageFormat { - get { - return ResourceManager.GetString("UnexpectedErrorMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Unexpected token type encountered. - /// - public static string UnexpectedTokenType { - get { - return ResourceManager.GetString("UnexpectedTokenType", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Unexpected error during workflow execution: {0}.. - /// - public static string UnexpectedWorkflowError { - get { - return ResourceManager.GetString("UnexpectedWorkflowError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Unpacking additional file: {0}. - /// - public static string UnpackingAdditionalFile { - get { - return ResourceManager.GetString("UnpackingAdditionalFile", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Username/Token:. - /// - public static string UsernameLabel { - get { - return ResourceManager.GetString("UsernameLabel", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to If a password is specified, a username must also be specified.. - /// - public static string UsernameRequired { - get { - return ResourceManager.GetString("UsernameRequired", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to View in SonarQube. - /// - public static string ViewInSonarQubeMenuItemDisplayText { - get { - return ResourceManager.GetString("ViewInSonarQubeMenuItemDisplayText", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Open the dashboard for this project in SonarQube using your web browser.. - /// - public static string ViewInSonarQubeMenuItemTooltip { - get { - return ResourceManager.GetString("ViewInSonarQubeMenuItemTooltip", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Warnings. - /// - public static string WarningsSettingsCategory { - get { - return ResourceManager.GetString("WarningsSettingsCategory", resourceCulture); - } - } } } diff --git a/src/Integration/Resources/Strings.resx b/src/Integration/Resources/Strings.resx index e8f96fcabe..062c985b4c 100644 --- a/src/Integration/Resources/Strings.resx +++ b/src/Integration/Resources/Strings.resx @@ -117,463 +117,43 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - Connecting to {0} - Progress message when connecting to server. {0} is the server uri. - - - Connect... - Link text for 'connect' command to initiate a connection with a SonarQube server - Connections Title for the SonarQube connection section of the TE SonarQube page - Ca_ncel - Button text (with access key) for canceling an action – access key 'n' - - - _Connect - Button text for 'Connect' in SonarQube connect dialog - - - Connect to a SonarQube Server - Connect dialog title - - - The URL you entered is not secure. Use HTTPS connections to protect sensitive information. - Message warning of consequences of connecting over an insecure channel - - - Field can not be empty - Not empty validator validation failed message - required field - - - Password: - Input label for password when connecting to a SonarQube server - - - '{0}' is not a valid URL - Server URI validator error message, formatted. {0} - invalid input string - - - SonarQube server: - Input label for SonarQube server URL - - - Username/Token: - Input label for username/token when connecting to a SonarQube server - - - Insecure schemes must be a subset of supported schemes - Exception message stating that the specified set of insecure schemes was not a subset of all supported schemes - - - Cannot connect to the SonarQube server. Make sure you provided the correct connection information, including your sign-in credentials, and [try again](). -See more information in the output window (SonarLint). - The error message the user sees when the connection has failed. The message will appear in team explorer. -Localization note: '[try again]()' is a special markup used to translate the text 'try again' into a hyperlink. Make sure to keep it localized content in with that format to avoid reducing functionality in the localized version of the product - - - Bind - Button text for binding a SonarQube project to a VS solution - - Cancel - Link text for canceling an action - - - If a password is specified, a username must also be specified. - Error message indicating that the username field is required if the password field is not empty - - - Colon (":") is not a permitted character. - Error message indicating that the string must not contain an a colon (":") as is it an invalid character - - - {0} (Bound) - SonarQube project tooltip for project name (with 'bound' marker indicating the project is bound to the current solution). {0} - project name - - - Key: {0} - SonarQube project tooltip for project key. {0} - project key - - - Binding projects - Display message - - - Failed to download quality profile. Name: '{0}', Key: '{1}', Language: '{2}' - Output window message indicating the quality profile file failed to download. {0} quality profile name, {1} quality profile key, {2} language name. - - - Rule set generated from SonarQube - Generated rule set description. - - - Unexpected error during workflow execution: {0}. - Error message in case of an unexpected exception during workflow execution. {0} exception message. - - - Refresh - Refresh command display text - - - Will refetch the SonarQube projects from the server using the existing credentials - Refresh command tooltip - - - (no projects) - Text to be displayed inline next to the connected SonarQube server in the Team Explorer tree view when there are no SonarQube Projects available under the given server - - - This server has no available projects with which to bind to. Please create at least one project and then refresh this connection. - Tool tip text to be displayed for the connected SonarQube server in the Team Explorer tree view when there are no SonarQube Projects available under the given server - - - Failed to install NuGet package '{0}' for project '{1}'. Message: {2} - Output window message in case of failure - {0} nuget package id, {1} project name, {2} message - - - Failed to bind the solution to SonarQube project, [try again](). -See more information in the output window (SonarLint). - Team explorer notification in case of a failure to bind a solution to SonarQube project -ocalization note: '[try again]()' is a special markup used to translate the text 'try again' into a hyperlink. Make sure to keep it localized content in with that format to avoid reducing functionality in the localized version of the product - - - Binding solution to SonarQube project: {0} - Output window message prefix that will be common to all the steps (more than one) in the binding workflow. {0} the step specific message in that workflow. - - - Completed successfully - A progress notification message - - - Started - A progress notification message - - - Update - Button text for syncing an already bound SonarQube project to a VS solution - - - Show All Projects - Server context menu command display text to show all projects (inc. unbound projects) - - - Toggle visibility of unbound projects for this server. - Server context menu command tool tip text to show all projects - - - The target URI must be an absolute URI - Exception message - - - Failed to delete credentials for {0}. - Exception message. {0} - the name of the thing we failed to delete. - - - Unexpected token type encountered - Exception message - - - Bound project with key '{0}' was not found on the connected server. Select a different SonarQube project, or use [a different login]() that has access to the desired project. - Team explorer error message when a persisted or shared bound project is not found. {0} - SonarQube project key. - - - Connecting to SonarQube server: {0} - Output window message format. {0} the messages associated with the connection will all have this prefix and {0} will be the actual message. - - - Canceled - The place holder value in ConnectingToSonarQubePrefixMessageFormat - - - Failed - The place holder value in ConnectingToSonarQubePrefixMessageFormat - - - Succeeded - The place holder value in ConnectingToSonarQubePrefixMessageFormat - - - Hide Unbound Projects - Server context menu command display text to show all projects (inc. unbound projects) - - - SonarQube server: {0} - Narrator description of a SonarQube server node in the Team Explorer. {0} server URL - - - Bound project: {0} - Narrator description of a bound SonarQube project node in the Team Explorer. {0} project name - - - SonarQube server: {0}. This server has no projects. - Narrator description of an empty SonarQube server node in the Team Explorer. {0} server URL + Button text (with access key) for canceling an action – access key 'n' SonarQube Title for the SonarQube Team Explorer page - - Warnings - Warnings settings category for Security options page - A SonarQube server plugin has a malformed version and cannot be compared. Version was '{0}'. Exception message indicating that the provided version string is malformed and cannot be compared. {0} version string - - Failed to unpack additional files required for analyzers - Output window message indicating failure when unpacking additional files for analyzers - - - Unpacking additional file: {0} - Progress message indicating an additional files is being unpacked. {0} additional file name - - - More info... - Link text for getting more information about SonarQube - - - SonarSource and Microsoft - Authors of the product - - - SonarQube is an open source platform to manage code quality. Connect your solution to an existing SonarQube Server to get the same issues in Visual Studio and in your SonarQube server. - Short descriptive text about SonarQube shown on the Team Explorer invitation card. - - - SonarQube - Name of SonarQube server product - - - {0}: Unexpected error: {1}. - -You can help us improve by reporting this bug at {2}. - - - {0} - the class name, {1} - error message, {2} url. -This is the general format to use when writing errors to output window or when there might not be enough context for the user to understand where is the error coming from. - - - Warning: could not find CodeAnalysisRuleSet for project '{0}' - Warning message. '{0}' project unique name - - - None (for selected SonarQube project's quality profile) - Output window message indicating the current solution does not contain any projects that are applicable to the currently selected SonarQube project's quality profile. - Test project regular expression pattern '{0}' is invalid. The default will be used instead. Please check your server settings. Warning output message indicating that the test project name regex was invalid. {0} invalid regex pattern - - Could not get the IVsHierarchy/IVsBuildPropertyStorage for the given project is IVsBuildPropertyStorage for the given EnvDTE.Project - Exception message indicating was unable to get the equivalent IVs objects for the passed EnvDTE.Project. - - - View in SonarQube - Menu item display text for the contextual command to view the SonarQube dashboard for the current item in the user's browser. - - - Open the dashboard for this project in SonarQube using your web browser. - Tooltip text for view project in SonarQube context menu item - - - Browse the server in your web browser. - Tooltip text for browser server context menu item - - - Browse - Context menu item text for opening the server in the users's browser - - - The operation requires an open solution. - - - Could not update the binding because the command is not currently available. Please try again and ensure that the solution is not building or currently being debugged. - Output window message - - - SonarLint: a binding update is required. See output window (SonarLint) for more information. - Info bar message - - - Update - Info bar command text - - - Checking for unbound projects. - Output window message - - - Failed to attach info bar to error list tool window. - Output window message - - - No unbound projects were found. - Output window message - - - The SonarQube Team Explorer page was closed and we cannot proceed with the update. Please try again. - Output window message - - - The current solution is not bound to the expected SonarQube project, aborting. - Output window message - SonarLint Output pane title - - Warning failed calling '{0}' (http error code={1}). Some of the functionality will be reduced. - Output window message. -{0} - service -{1} - http error code - - - Checking if SonarQube Quality profile has changed. - Output window message - - - The SonarQube project is using a different Quality Profile. Update is required. - Output window message - - - Unexpected failure getting quality profile information. Automatic quality profile update will not be performed. - Output window message - - - The binding information is out of date. Update is required. - Output window message - - - The Quality Profile for the SonarQube project has changed. Update is required. - Output window message - - - Quality Profile is up-to-date. - Output window message - - - The solution has changed and now requires additional Quality Profiles to be included. Update is required. - Output window message - - - SonarLint can now tell you when the quality profile in SonarQube project is updated. Press Update to enable this feature now. - Info bar message - - - None (for selected SonarQube project's quality profile) - Output window message indicating the current solution does not contain any projects filtered out from the currently selected SonarQube project's quality profile. - - - {0} - Output window message format. Used whenever the message should be displayed as a sub-message from a step message so the user can clearly identify the grouping. {0} the actual message to display. - - - SonarQube - {0} {1} - Format used to build the project specific ruleset file name. {0} SonarQube project name. {1} SonarQube quality profile name. - - - {0}profiles/show?key={1} - Used to create the Quality Profile URL. This will be integrated as part of the ruleset description. {0} the server URL. {1} the quality profile key. - - - Retrieving organizations - - - Retrieving projects - - - Validating credentials - - - Saving configuration... - Message for save step in new binding mode - - - Failed to save configuration - Message for save error in new binding workflow - - - Updating binding... - - - INFO: SonarLint connected mode no longer installs the analyzers NuGet packages. The analyzers embedded in the VSIX are used instead. - Notifications are not supported on this version of SonarQube Output window message - - Telemetry: error occurred when recording: {0} - Output window message - - - Telemetry: error occurred when sending: {0} - Output window message - Failed to fetch notifications: {0} - - Failed to fetch suppressions: {0} - - - Cannot fetch suppressions - not connected to a SonarQube server - Warning in output window - - - Checking for suppressions... - - - Number of suppressions found: {0} - - - Failed to check out files for editing. Error flags: {0} - Output window message showing the tagVSQueryEditResultFlags enum value(s) - - - Failed to check out files for saving. Error code: {0} - Output window message showing the tagVSQuerySaveResult enum value - - - Error identifying project '{0}' as test project: {1} - Error message indicating that an error occured while checking if a project is a test project - - - Project {0} RuleSet declarations: - {1} - - - Ruleset could not be loaded. File: {0}, Error: {1} - Output window message for unloadable ruleset - - - Ruleset file does not exist: {0} - Output window message for a ruleset that does not exist on disc - No file was found under '{0}' Error loading server settings. Analysis settings from the server (e.g. inclusions/exclusions) will not be applied in the IDE. Error: {0} - - Downloading Server Exclusions - - - Some binding configuration is missing. Please rebind the solution. - Output window message - - - [Binding check] Error checking bound project settings file: {0} - The issue is valid but will not be fixed now. It represents accepted technical debt. diff --git a/src/Integration/State/IStateManager.cs b/src/Integration/State/IStateManager.cs deleted file mode 100644 index 08da9f7c45..0000000000 --- a/src/Integration/State/IStateManager.cs +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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 SonarQube.Client.Models; - -namespace SonarLint.VisualStudio.Integration.State -{ - /// - /// Manages the view model state (also encapsulates it) - /// - internal interface IStateManager - { - /// - /// The underlying managed visual state - /// - /// The state should not be manipulated directly, it exposed only for data binding purposes - TransferableVisualState ManagedState { get; } - - /// - /// Event fired when is changed. The arguments will include the new value. - /// - event EventHandler IsBusyChanged; - - /// - /// Event fired when the SonarQube project binding of the solution changes. - /// - event EventHandler BindingStateChanged; - - bool IsBusy { get; set; } - - bool HasSharedBinding { get; set; } - - bool HasBoundProject { get; } - - bool IsConnected { get; } - - string BoundProjectKey { get; set; } - string BoundProjectName { get; set; } - - void ResetConnectionConfiguration(); - - IEnumerable GetConnectedServers(); - - void SetProjects(ConnectionInformation connection, IEnumerable projects); - - void SetBoundProject(Uri serverUri, string organizationKey, string projectKey); - - void ClearBoundProject(); - - void SyncCommandFromActiveSection(); - } -} diff --git a/src/Integration/State/StateManager.cs b/src/Integration/State/StateManager.cs deleted file mode 100644 index 22045ce6d8..0000000000 --- a/src/Integration/State/StateManager.cs +++ /dev/null @@ -1,377 +0,0 @@ -/* - * 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.Diagnostics; -using System.Globalization; -using System.Linq; -using Microsoft.VisualStudio.Imaging; -using SonarLint.VisualStudio.ConnectedMode.Binding; -using SonarLint.VisualStudio.ConnectedMode.Persistence; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Core.Binding; -using SonarLint.VisualStudio.Infrastructure.VS; -using SonarLint.VisualStudio.Integration.Connection; -using SonarLint.VisualStudio.Integration.Resources; -using SonarLint.VisualStudio.Integration.TeamExplorer; -using SonarLint.VisualStudio.Integration.WPF; -using SonarQube.Client.Models; - -namespace SonarLint.VisualStudio.Integration.State -{ - /// - /// Implementation of - /// - internal sealed class StateManager : IStateManager, IDisposable - { - private readonly IThreadHandling threadHandling; - private bool isDisposed; - - public StateManager(IHost host, TransferableVisualState state) - : this(host, state, ThreadHandling.Instance) - { } - - internal /* for testing */ StateManager(IHost host, TransferableVisualState state, IThreadHandling threadHandling) - { - this.Host = host ?? throw new ArgumentNullException(nameof(host)); - this.ManagedState = state ?? throw new ArgumentNullException(nameof(state)); - this.ManagedState.PropertyChanged += this.OnStatePropertyChanged; - - this.threadHandling = threadHandling; - } - - #region IStateManager - public event EventHandler IsBusyChanged; - - public event EventHandler BindingStateChanged; - - public TransferableVisualState ManagedState - { - get; - } - - public IHost Host - { - get; - } - - public bool IsBusy - { - get - { - return this.ManagedState.IsBusy; - } - set - { - this.ManagedState.IsBusy = value; - } - } - - public bool HasSharedBinding - { - get => ManagedState.HasSharedBinding; - set => ManagedState.HasSharedBinding = value; - } - - public bool HasBoundProject - { - get - { - return this.ManagedState.HasBoundProject; - } - } - - public bool IsConnected - { - get - { - return this.GetConnectedServers().Any(); - } - } - - public void ResetConnectionConfiguration() - { - ManagedState.ConnectConfiguration = new ConnectConfiguration(); - } - - public IEnumerable GetConnectedServers() - { - return this.ManagedState.ConnectedServers.Select(s => s.ConnectionInformation); - } - - public string BoundProjectKey { get; set; } - public string BoundProjectName { get; set; } - - public void SetProjects(ConnectionInformation connection, IEnumerable projects) - { - threadHandling.RunOnUIThread(() => SetProjectsUIThread(connection, projects)); - } - - public void SetBoundProject(Uri serverUri, string organizationKey, string projectKey) - { - this.ClearBindingErrorNotifications(); - - var serverViewModel = this.ManagedState.ConnectedServers.FirstOrDefault(s => s.Url == serverUri && s.ConnectionInformation?.Organization?.Key == organizationKey); - Debug.Assert(serverViewModel != null, "Expecting the connection to map to a single server"); - - var projectViewModel = serverViewModel?.Projects?.FirstOrDefault(p => SonarQubeProject.KeyComparer.Equals(p.Project.Key, projectKey)); - Debug.Assert(projectViewModel != null, "Expecting a single project mapped to project information"); - - DoSetBoundProject(projectViewModel); - } - - public void ClearBoundProject() - { - this.ClearBindingErrorNotifications(); - this.ManagedState.ClearBoundProject(); - Debug.Assert(!this.HasBoundProject, "Expected not to have a bound project"); - - this.OnBindingStateChanged(isCleared: true); - } - - public void SyncCommandFromActiveSection() - { - foreach (ServerViewModel serverVM in this.ManagedState.ConnectedServers) - { - this.SetServerVMCommands(serverVM); - this.SetServerProjectsVMCommands(serverVM); - } - } - #endregion - - #region Non public API - private void OnIsBusyChanged(bool isBusy) - { - this.IsBusyChanged?.Invoke(this, isBusy); - } - - private void OnBindingStateChanged(bool isCleared) - { - this.BindingStateChanged?.Invoke(this, new BindingStateEventArgs(isCleared)); - } - - private void OnStatePropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) - { - if (e.PropertyName == nameof(this.ManagedState.IsBusy)) - { - this.OnIsBusyChanged(this.IsBusy); - } - } - - private void SetProjectsUIThread(ConnectionInformation connection, IEnumerable projects) - { - threadHandling.ThrowIfNotOnUIThread(); - Debug.Assert(connection != null); - this.ClearBindingErrorNotifications(); - - // !!! Avoid using the service to detect disconnects since it's not thread safe !!! - if (projects == null) - { - // Disconnected, clear all - this.ClearBoundProject(); - this.DisposeConnections(); - this.ManagedState.ConnectedServers.Clear(); - } - else - { - var matchingServers = this.ManagedState.ConnectedServers.Where(serverVM => serverVM.Url == connection.ServerUri) - .ToList(); - - ServerViewModel serverViewModel; - if (matchingServers.Count > 1) - { - Debug.Fail($"Not expecting to find multiple connected servers with url '{connection.ServerUri}'"); - return; - } - else if (matchingServers.Count == 0) - { - // Add new server - serverViewModel = new ServerViewModel(connection); - this.SetServerVMCommands(serverViewModel); - this.ManagedState.ConnectedServers.Add(serverViewModel); - } - else - { - // Update existing server - serverViewModel = matchingServers[0]; - } - - serverViewModel.SetProjects(projects); - Debug.Assert(serverViewModel.ShowAllProjects, "ShowAllProjects should have been set"); - this.SetServerProjectsVMCommands(serverViewModel); - this.RestoreBoundProject(serverViewModel); - } - } - - private void DisposeConnections() - { - this.ManagedState.ConnectedServers - .Select(s => s.ConnectionInformation) - .ToList() - .ForEach(c => c.Dispose()); - } - - private void ClearBindingErrorNotifications() - { - this.Host.ActiveSection?.UserNotifications?.HideNotification(NotificationIds.FailedToFindBoundProjectKeyId); - } - - private void RestoreBoundProject(ServerViewModel serverViewModel) - { - if (this.BoundProjectKey == null) - { - // Nothing to restore - return; - } - - var projectVm = serverViewModel.Projects.FirstOrDefault(pvm => SonarQubeProject.KeyComparer.Equals(pvm.Key, this.BoundProjectKey)); - if (projectVm?.Project == null) - { - // Defensive coding: invoked asynchronous and it's safer to assume that value could be null - // and just not do anything since if they are null it means that there's no solution open. - this.Host.ActiveSection?.UserNotifications?.ShowNotificationError( - string.Format(CultureInfo.CurrentCulture, Strings.BoundProjectNotFound, this.BoundProjectKey), - NotificationIds.FailedToFindBoundProjectKeyId, - Host.ActiveSection?.ReconnectCommand); - throw new BindingAbortedException("Can't find selected project"); - } - else - { - this.DoSetBoundProject(projectVm); - } - } - - private void DoSetBoundProject(ProjectViewModel projectViewModel) - { - if (projectViewModel != null) - { - this.ManagedState.SetBoundProject(projectViewModel); - Debug.Assert(this.HasBoundProject, "Expected to have a bound project"); - - this.OnBindingStateChanged(isCleared: false); - } - } - - private void SetServerVMCommands(ServerViewModel serverVM) - { - serverVM.Commands.Clear(); - if (this.Host.ActiveSection == null) - { - // Don't add command (which will be disabled). - return; - } - - - var refreshContextualCommand = new ContextualCommandViewModel(serverVM, this.Host.ActiveSection.RefreshCommand) - { - DisplayText = Strings.RefreshCommandDisplayText, - Tooltip = Strings.RefreshCommandTooltip, - Icon = new IconViewModel(KnownMonikers.Refresh) - }; - - var browseServerContextualCommand = new ContextualCommandViewModel(serverVM.Url.ToString(), this.Host.ActiveSection.BrowseToUrlCommand) - { - DisplayText = Strings.BrowseServerMenuItemDisplayText, - Tooltip = Strings.BrowserServerMenuItemTooltip, - Icon = new IconViewModel(KnownMonikers.OpenWebSite) - }; - - var toggleShowAllProjectsCommand = new ContextualCommandViewModel(serverVM, this.Host.ActiveSection.ToggleShowAllProjectsCommand) - { - Tooltip = Strings.ToggleShowAllProjectsCommandTooltip - }; - toggleShowAllProjectsCommand.SetDynamicDisplayText(x => - { - ServerViewModel ctx = x as ServerViewModel; - Debug.Assert(ctx != null, "Unexpected fixed context for ToggleShowAllProjects context command"); - return ctx?.ShowAllProjects ?? false ? Strings.HideUnboundProjectsCommandText : Strings.ShowAllProjectsCommandText; - }); - - // Note: the Disconnect command is not on the context menu, although it is - // called directly from code e.g. when the solution unloads - serverVM.Commands.Add(refreshContextualCommand); - serverVM.Commands.Add(browseServerContextualCommand); - serverVM.Commands.Add(toggleShowAllProjectsCommand); - } - - private void SetServerProjectsVMCommands(ServerViewModel serverVM) - { - foreach (ProjectViewModel projectVM in serverVM.Projects) - { - projectVM.Commands.Clear(); - - if (this.Host.ActiveSection == null) - { - // Don't add command (which will be disabled). - continue; - } - - var bindContextCommand = new ContextualCommandViewModel(projectVM, - this.Host.ActiveSection.BindCommand, - new BindCommandArgs(new BoundServerProject("placeholder", projectVM.Key, serverVM.ConnectionInformation.ToServerConnection()))); - bindContextCommand.SetDynamicDisplayText(x => - { - var ctx = x as ProjectViewModel; - Debug.Assert(ctx != null, "Unexpected fixed context for bind context command"); - return ctx?.IsBound ?? false ? Strings.SyncButtonText : Strings.BindButtonText; - }); - bindContextCommand.SetDynamicIcon(x => - { - var ctx = x as ProjectViewModel; - Debug.Assert(ctx != null, "Unexpected fixed context for bind context command"); - return new IconViewModel(ctx?.IsBound ?? false ? KnownMonikers.Sync : KnownMonikers.Link); - }); - - var openProjectDashboardCommand = new ContextualCommandViewModel(projectVM, this.Host.ActiveSection.BrowseToProjectDashboardCommand) - { - DisplayText = Strings.ViewInSonarQubeMenuItemDisplayText, - Tooltip = Strings.ViewInSonarQubeMenuItemTooltip, - Icon = new IconViewModel(KnownMonikers.OpenWebSite) - }; - - projectVM.Commands.Add(bindContextCommand); - projectVM.Commands.Add(openProjectDashboardCommand); - } - } - #endregion - - #region IDisposable Support - private void Dispose(bool disposing) - { - if (!this.isDisposed) - { - if (disposing) - { - this.ManagedState.PropertyChanged -= this.OnStatePropertyChanged; - this.DisposeConnections(); - } - - this.isDisposed = true; - } - } - - public void Dispose() - { - Dispose(true); - } - #endregion - } -} diff --git a/src/Integration/State/TransferableVisualState.cs b/src/Integration/State/TransferableVisualState.cs deleted file mode 100644 index 389564ff3a..0000000000 --- a/src/Integration/State/TransferableVisualState.cs +++ /dev/null @@ -1,143 +0,0 @@ -/* - * 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.ObjectModel; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Core.WPF; -using SonarLint.VisualStudio.Infrastructure.VS; -using SonarLint.VisualStudio.Integration.Connection; -using SonarLint.VisualStudio.Integration.TeamExplorer; - -namespace SonarLint.VisualStudio.Integration.State -{ - internal class TransferableVisualState : ViewModelBase - { - private readonly ObservableCollection connectedServers = new ObservableCollection(); - private readonly IThreadHandling threadHandling; - private ProjectViewModel boundProject; - private bool isBusy; - private ConnectConfiguration connectConfiguration = new ConnectConfiguration(); - private bool hasSharedBinding; - - public TransferableVisualState() - : this(ThreadHandling.Instance) - { } - - internal /* for testing */ TransferableVisualState(IThreadHandling threadHandling) - { - this.threadHandling = threadHandling; - } - - public ObservableCollection ConnectedServers - { - get - { - Debug.Assert(threadHandling.CheckAccess(), $"{nameof(ConnectedServers)} should only be accessed from the UI thread"); - return this.connectedServers; - } - } - - public ConnectConfiguration ConnectConfiguration - { - get - { - Debug.Assert(threadHandling.CheckAccess(), $"{nameof(ConnectConfiguration)} should only be accessed from the UI thread"); - return connectConfiguration; - } - set - { - Debug.Assert(threadHandling.CheckAccess(), $"{nameof(ConnectConfiguration)} should only be set from the UI thread"); - SetAndRaisePropertyChanged(ref connectConfiguration, value); - } - } - - public bool HasSharedBinding - { - get - { - Debug.Assert(threadHandling.CheckAccess(), $"{nameof(HasSharedBinding)} should only be accessed from the UI thread"); - return hasSharedBinding; - } - set - { - Debug.Assert(threadHandling.CheckAccess(), $"{nameof(HasSharedBinding)} should only be set from the UI thread"); - SetAndRaisePropertyChanged(ref hasSharedBinding, value); - } - } - - public bool HasBoundProject - { - get - { - Debug.Assert(threadHandling.CheckAccess(), $"{nameof(HasBoundProject)} should only be accessed from the UI thread"); - return this.boundProject != null; - } - } - - public bool IsBusy - { - get - { - Debug.Assert(threadHandling.CheckAccess(), $"{nameof(IsBusy)} should only be accessed from the UI thread"); - return this.isBusy; - } - set - { - Debug.Assert(threadHandling.CheckAccess(), $"{nameof(IsBusy)} should only be set from the UI thread"); - this.SetAndRaisePropertyChanged(ref this.isBusy, value); - } - } - - public void SetBoundProject(ProjectViewModel project) - { - Debug.Assert(threadHandling.CheckAccess(), $"{nameof(SetBoundProject)} should only be accessed from the UI thread"); - this.ClearBoundProject(); - - this.boundProject = project; - this.boundProject.IsBound = true; - this.boundProject.Owner.ShowAllProjects = false; - - this.OnHasBoundProjectChanged(); - } - - public void ClearBoundProject() - { - Debug.Assert(threadHandling.CheckAccess(), $"{nameof(ClearBoundProject)} should only be accessed from the UI thread"); - if (this.boundProject != null) - { - this.boundProject.IsBound = false; - this.boundProject.Owner.ShowAllProjects = true; - this.boundProject = null; - - this.OnHasBoundProjectChanged(); - } - } - - [System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", - "S3236:Methods with caller info attributes should not be invoked with explicit arguments", - Justification = "We actually want to specify a different property to change", - Scope = "member", - Target = "~M:SonarLint.VisualStudio.Integration.State.TransferableVisualState.OnHasBoundProjectChanged()")] - private void OnHasBoundProjectChanged() - { - this.RaisePropertyChanged(nameof(HasBoundProject)); - } - } -} diff --git a/src/Integration/TeamExplorer/IConnectSectionView.cs b/src/Integration/TeamExplorer/IConnectSectionView.cs deleted file mode 100644 index 7202a2c915..0000000000 --- a/src/Integration/TeamExplorer/IConnectSectionView.cs +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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.Integration.TeamExplorer -{ - internal interface IConnectSectionView - { - } -} diff --git a/src/Integration/TeamExplorer/ISectionController.cs b/src/Integration/TeamExplorer/ISectionController.cs deleted file mode 100644 index 70d0d697cb..0000000000 --- a/src/Integration/TeamExplorer/ISectionController.cs +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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.Windows.Input; -using SonarLint.VisualStudio.ConnectedMode.Binding; -using SonarLint.VisualStudio.Integration.Connection; -using SonarLint.VisualStudio.Integration.Progress; -using SonarLint.VisualStudio.Integration.WPF; -using SonarQube.Client.Models; - -namespace SonarLint.VisualStudio.Integration.TeamExplorer -{ - /// - /// Representation of the connect section - /// - internal interface ISectionController - { - /// - /// The progress host - /// - /// return when the view specific host is the one to use - IProgressControlHost ProgressHost { get; } - - /// - /// - /// - IConnectSectionView View { get; } - - /// - /// - /// - IConnectSectionViewModel ViewModel { get; } - - /// - /// The notifications service to use - /// - ///return when the view model specific implementation is the one to use - IUserNotification UserNotifications { get; } - - ICommand ConnectCommand { get; } - - ICommand ReconnectCommand { get; } - - ICommand BindCommand { get; } - - ICommand BrowseToUrlCommand { get; } - - ICommand BrowseToProjectDashboardCommand { get; } - - ICommand RefreshCommand { get; } - - ICommand DisconnectCommand { get; } - - ICommand ToggleShowAllProjectsCommand { get; } - } -} diff --git a/src/Integration/TeamExplorer/IUserNotification.cs b/src/Integration/TeamExplorer/IUserNotification.cs deleted file mode 100644 index b5e2c5fabb..0000000000 --- a/src/Integration/TeamExplorer/IUserNotification.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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.Windows.Input; - -namespace SonarLint.VisualStudio.Integration.TeamExplorer -{ - /// - /// Show notifications to the user - /// - internal interface IUserNotification - { - /// - /// - /// - bool HideNotification(Guid id); - - void ShowNotificationError(string message, Guid notificationId, ICommand associatedCommand); - - void ShowNotificationWarning(string message, Guid notificationId, ICommand associatedCommand); - } -} diff --git a/src/Integration/TeamExplorer/NotificationIds.cs b/src/Integration/TeamExplorer/NotificationIds.cs deleted file mode 100644 index ad240aff30..0000000000 --- a/src/Integration/TeamExplorer/NotificationIds.cs +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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; - -namespace SonarLint.VisualStudio.Integration.TeamExplorer -{ - internal static class NotificationIds - { - public static readonly Guid FailedToConnectId = new Guid("39A75D18-6CF4-44CE-996C-CD692977668F"); - public static readonly Guid FailedToBindId = new Guid("5A38773F-89F6-49ED-8B67-1A82A8182589"); - public static readonly Guid FailedToFindBoundProjectKeyId = new Guid("4A92944A-2585-442D-8821-DE235DA9E478"); - public static readonly Guid WarnServerTrustId = new Guid("{F9A383D5-47ED-439E-A1DB-7A1083062CCD}"); - public static readonly Guid BadSonarQubePluginId = new Guid("{F89A8FAB-6EF1-4EB5-A1F7-A197AEF9DC8C}"); - public static readonly Guid RuleSetConflictsId = new Guid("{D9DBFF58-B6D1-43ED-BDFB-083D4A5ECFF5}"); - } -} diff --git a/src/Integration/TeamExplorer/ProjectViewModel.cs b/src/Integration/TeamExplorer/ProjectViewModel.cs deleted file mode 100644 index 3488168cb9..0000000000 --- a/src/Integration/TeamExplorer/ProjectViewModel.cs +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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.Globalization; -using SonarLint.VisualStudio.Core.WPF; -using SonarLint.VisualStudio.Integration.Resources; -using SonarLint.VisualStudio.Integration.WPF; -using SonarQube.Client.Models; - -namespace SonarLint.VisualStudio.Integration.TeamExplorer -{ - public class ProjectViewModel : ViewModelBase - { - private readonly ContextualCommandsCollection commands = new ContextualCommandsCollection(); - private bool isBound; - - public ProjectViewModel(ServerViewModel owner, SonarQubeProject projectInformation) - { - if (owner == null) - { - throw new ArgumentNullException(nameof(owner)); - } - - if (projectInformation == null) - { - throw new ArgumentNullException(nameof(projectInformation)); - } - - this.Owner = owner; - this.Project = projectInformation; - } - - #region Properties - public ServerViewModel Owner - { - get; - } - - public SonarQubeProject Project - { - get; - } - - public string Key - { - get { return this.Project.Key; } - } - - public string ProjectName - { - get { return this.Project.Name; } - } - - public bool IsBound - { - get { return this.isBound; } - set { this.SetAndRaisePropertyChanged(ref this.isBound, value); } - } - - public string ToolTipProjectName - { - get - { - return this.IsBound - ? string.Format(CultureInfo.CurrentCulture, Strings.ProjectToolTipProjectNameFormat, this.ProjectName) - : this.ProjectName; - } - } - - public string ToolTipKey - { - get - { - return string.Format(CultureInfo.CurrentCulture, Strings.ProjectToolTipKeyFormat, this.Key); - } - } - - public string AutomationName - { - get - { - return this.IsBound - ? string.Format(CultureInfo.CurrentCulture, Strings.AutomationProjectBoundDescription, this.ProjectName) - : this.ProjectName; - } - } - - #endregion - - #region Commands - public ContextualCommandsCollection Commands - { - get { return this.commands; } - } - #endregion - - } -} diff --git a/src/Integration/TeamExplorer/ServerViewModel.cs b/src/Integration/TeamExplorer/ServerViewModel.cs deleted file mode 100644 index 587f863701..0000000000 --- a/src/Integration/TeamExplorer/ServerViewModel.cs +++ /dev/null @@ -1,125 +0,0 @@ -/* - * 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.ObjectModel; -using System.Globalization; -using SonarLint.VisualStudio.Core.WPF; -using SonarLint.VisualStudio.Integration.Resources; -using SonarLint.VisualStudio.Integration.WPF; -using SonarQube.Client.Models; - -namespace SonarLint.VisualStudio.Integration.TeamExplorer -{ - public class ServerViewModel : ViewModelBase - { - private readonly ConnectionInformation connectionInformation; - private readonly ObservableCollection projects = new ObservableCollection(); - private readonly ContextualCommandsCollection commands = new ContextualCommandsCollection(); - private bool showAllProjects; - private bool isExpanded; - - public ServerViewModel(ConnectionInformation connectionInformation, bool isExpanded = true) - { - if (connectionInformation == null) - { - throw new ArgumentNullException(nameof(connectionInformation)); - } - - this.connectionInformation = connectionInformation; - this.IsExpanded = isExpanded; - } - - /// - /// Will clear any existing project view models and will replace them with the specified ones. - /// The project view models will be alphabetically sorted by for the - /// - public void SetProjects(IEnumerable projectsToSet) - { - this.Projects.Clear(); - if (projectsToSet == null) - { - return; // all done - } - - IEnumerable projectViewModels = projectsToSet - .OrderBy(p => p.Name, StringComparer.CurrentCulture) - .Select(project => new ProjectViewModel(this, project)); - - foreach (var projectVM in projectViewModels) - { - this.Projects.Add(projectVM); - } - - this.ShowAllProjects = true; - } - - #region Properties - - public ConnectionInformation ConnectionInformation - { - get { return this.connectionInformation; } - } - - public bool ShowAllProjects - { - get { return this.showAllProjects; } - set { this.SetAndRaisePropertyChanged(ref this.showAllProjects, value); } - } - - public Uri Url - { - get { return this.connectionInformation.ServerUri; } - } - - public string OrganizationName => this.connectionInformation.Organization?.Name; - - public ObservableCollection Projects - { - get { return this.projects; } - } - - public bool IsExpanded - { - get { return this.isExpanded; } - set { SetAndRaisePropertyChanged(ref this.isExpanded, value); } - } - - public string AutomationName - { - get - { - return this.Projects.Any() - ? string.Format(CultureInfo.CurrentCulture, Strings.AutomationServerDescription, this.Url) - : string.Format(CultureInfo.CurrentCulture, Strings.AutomationServerNoProjectsDescription, this.Url); - } - } - - #endregion - - #region Commands - - public ContextualCommandsCollection Commands - { - get { return this.commands; } - } - - #endregion - } -} diff --git a/src/Integration/WPF/ContextualCommandViewModel.cs b/src/Integration/WPF/ContextualCommandViewModel.cs deleted file mode 100644 index 987dbba112..0000000000 --- a/src/Integration/WPF/ContextualCommandViewModel.cs +++ /dev/null @@ -1,165 +0,0 @@ -/* - * 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.Windows.Input; -using SonarLint.VisualStudio.Core.WPF; - -namespace SonarLint.VisualStudio.Integration.WPF -{ - /// - /// View model for a command with fixed context that is required during command execution - /// - /// Command argument. - public class ContextualCommandViewModel : ViewModelBase - { - private readonly object fixedContext; - private readonly object commandArgs; - private readonly RelayCommand proxyCommand; - - private ICommand command; - private Func displayTextFunc; - private string tooltip; - private Func iconFunc; - - /// - /// Creates an instance of contextual command view model - /// - /// Required context - /// Optional real command to trigger and pass the fixed context to - public ContextualCommandViewModel(object fixedContext, ICommand command) - : this(fixedContext, command, commandArgs: null) - { - } - - /// - /// Creates an instance of contextual command view model - /// - /// Required context - /// Optional real command to trigger and pass the fixed context to - /// Optional arguments to pass to the command. If null then will be passed. - public ContextualCommandViewModel(object fixedContext, ICommand command, object commandArgs) - { - if (fixedContext == null) - { - throw new ArgumentNullException(nameof(fixedContext)); - } - - this.fixedContext = fixedContext; - this.commandArgs = commandArgs ?? fixedContext; - this.proxyCommand = new RelayCommand(this.Execute, this.CanExecute); - this.SetCommand(command); - } - - [System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", - "S3236:Methods with caller info attributes should not be invoked with explicit arguments", - Justification = "We want to change a different property than the 'caller' which is a method", - Scope = "member", - Target = "~M:SonarLint.VisualStudio.Integration.WPF.ContextualCommandViewModel.SetDynamicDisplayText(System.Func{System.Object,System.String})")] - public void SetDynamicDisplayText(Func getDisplayText) - { - if (getDisplayText == null) - { - throw new ArgumentNullException(nameof(getDisplayText)); - } - - this.displayTextFunc = getDisplayText; - this.RaisePropertyChanged(nameof(this.DisplayText)); - } - - [System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", - "S3236:Methods with caller info attributes should not be invoked with explicit arguments", - Justification = "We want to change a different property than the 'caller' which is a method", - Scope = "member", - Target = "~M:SonarLint.VisualStudio.Integration.WPF.ContextualCommandViewModel.SetDynamicIcon(System.Func{System.Object,SonarLint.VisualStudio.Integration.WPF.IconViewModel})")] - public void SetDynamicIcon(Func getIconFunc) - { - if (getIconFunc == null) - { - throw new ArgumentNullException(nameof(getIconFunc)); - } - - this.iconFunc = getIconFunc; - this.RaisePropertyChanged(nameof(this.Icon)); - } - - public void SetCommand(ICommand realCommand) - { - this.command = realCommand; - this.proxyCommand.RequeryCanExecute(); - } - - public string DisplayText - { - get - { - return this.displayTextFunc?.Invoke(this.fixedContext); - } - set - { - this.displayTextFunc = x => value; - this.RaisePropertyChanged(); - } - } - - public string Tooltip - { - get { return this.tooltip; } - set { this.SetAndRaisePropertyChanged(ref this.tooltip, value); } - } - - public IconViewModel Icon - { - get - { - return this.iconFunc?.Invoke(this.fixedContext); - } - set - { - this.iconFunc = x => value; - this.RaisePropertyChanged(); - } - } - - public ICommand Command - { - get { return this.proxyCommand; } - } - - internal /*for testing purposes*/ ICommand InternalRealCommand - { - get { return this.command; } - } - - internal /*for testing purposes*/ object InternalFixedContext - { - get { return this.fixedContext; } - } - - private void Execute() - { - this.command.Execute(this.commandArgs); - } - - private bool CanExecute() - { - return this.command != null && this.command.CanExecute(this.commandArgs); - } - } -} diff --git a/src/Integration/WPF/ContextualCommandsCollection.cs b/src/Integration/WPF/ContextualCommandsCollection.cs deleted file mode 100644 index e306b525ff..0000000000 --- a/src/Integration/WPF/ContextualCommandsCollection.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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.ObjectModel; -using System.Collections.Specialized; -using System.ComponentModel; - -namespace SonarLint.VisualStudio.Integration.WPF -{ - public class ContextualCommandsCollection : ObservableCollection - { - public bool HasCommands - { - get - { - return this.Count > 0; - } - } - - protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) - { - base.OnCollectionChanged(e); - this.OnPropertyChanged(new PropertyChangedEventArgs(nameof(HasCommands))); - } - } -} diff --git a/src/Integration/WPF/ProjectViewModelToBindingArgsConverter.cs b/src/Integration/WPF/ProjectViewModelToBindingArgsConverter.cs deleted file mode 100644 index e7c0717096..0000000000 --- a/src/Integration/WPF/ProjectViewModelToBindingArgsConverter.cs +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Windows.Data; -using SonarLint.VisualStudio.ConnectedMode.Binding; -using SonarLint.VisualStudio.ConnectedMode.Persistence; -using SonarLint.VisualStudio.Core.Binding; -using SonarLint.VisualStudio.Integration.TeamExplorer; - -namespace SonarLint.VisualStudio.Integration.WPF -{ - [ExcludeFromCodeCoverage] // todo https://sonarsource.atlassian.net/browse/SLVS-1408 - [ValueConversion(typeof(ProjectViewModel), typeof(BindCommandArgs))] - public class ProjectViewModelToBindingArgsConverter : IValueConverter - { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - var projectViewModel = value as ProjectViewModel; - if (projectViewModel == null) - { - return null; - } - - return new BindCommandArgs(new BoundServerProject("placeholder", projectViewModel.Key, projectViewModel.Owner.ConnectionInformation.ToServerConnection())); // todo https://sonarsource.atlassian.net/browse/SLVS-1408 - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - throw new NotSupportedException(); - } - } -} diff --git a/src/Integration/WPF/ProjectViewModelVisibilityConverter.cs b/src/Integration/WPF/ProjectViewModelVisibilityConverter.cs deleted file mode 100644 index 8f6824f7c5..0000000000 --- a/src/Integration/WPF/ProjectViewModelVisibilityConverter.cs +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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.Globalization; -using System.Windows; -using System.Windows.Data; - -namespace SonarLint.VisualStudio.Integration.WPF -{ - public sealed class ProjectViewModelVisibilityConverter : IMultiValueConverter - { - public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) - { - if (values == null || values.Length != 4) - { - return DependencyProperty.UnsetValue; - } - - var projectName = values[0] as string; - var showAllProjects = values[1] as bool?; - var isBound = values[2] as bool?; - var filterText = values[3] as string; - - if (projectName == null || showAllProjects == null || isBound == null || filterText == null) - { - return DependencyProperty.UnsetValue; - } - - if (showAllProjects.Value) - { - return projectName.IndexOf(filterText, StringComparison.OrdinalIgnoreCase) != -1 - ? Visibility.Visible - : Visibility.Collapsed; - } - else - { - return isBound.Value ? Visibility.Visible : Visibility.Collapsed; - } - } - - public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) - { - throw new NotSupportedException($"{nameof(ProjectViewModelVisibilityConverter)} does not support ConvertBack method."); - } - } -} diff --git a/src/SonarLint.VSSpecificAssemblies.props b/src/SonarLint.VSSpecificAssemblies.props index 1978e46f7f..4071e3a1c4 100644 --- a/src/SonarLint.VSSpecificAssemblies.props +++ b/src/SonarLint.VSSpecificAssemblies.props @@ -49,10 +49,6 @@ - - $(ThirdPartyPath)\Microsoft.TeamFoundation.Client.dll - $(CopyVSAssembliesToOutput) - $(ThirdPartyPath)\Microsoft.TeamFoundation.Controls.dll $(CopyVSAssembliesToOutput) diff --git a/src/TestInfrastructure/Framework/ConfigurableConnectSectionViewModel.cs b/src/TestInfrastructure/Framework/ConfigurableConnectSectionViewModel.cs deleted file mode 100644 index b2c24c608f..0000000000 --- a/src/TestInfrastructure/Framework/ConfigurableConnectSectionViewModel.cs +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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.Windows.Input; -using SonarLint.VisualStudio.ConnectedMode.Binding; -using SonarLint.VisualStudio.Integration.Connection; -using SonarLint.VisualStudio.Integration.State; -using SonarLint.VisualStudio.Integration.TeamExplorer; -using SonarLint.VisualStudio.Integration.WPF; - -namespace SonarLint.VisualStudio.TestInfrastructure -{ - internal class ConfigurableConnectSectionViewModel : IConnectSectionViewModel - { - public ICommand BindCommand { get; set; } - public ICommand BrowseToUrlCommand { get; set; } - public ICommand ConnectCommand { get; set; } - public bool IsBusy { get; set; } - public TransferableVisualState State { get; set; } - } -} diff --git a/src/TestInfrastructure/Framework/ConfigurableConnectionInformationProvider.cs b/src/TestInfrastructure/Framework/ConfigurableConnectionInformationProvider.cs deleted file mode 100644 index 6142c76701..0000000000 --- a/src/TestInfrastructure/Framework/ConfigurableConnectionInformationProvider.cs +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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 FluentAssertions; -using SonarLint.VisualStudio.Integration.Connection; -using SonarQube.Client.Models; - -namespace SonarLint.VisualStudio.TestInfrastructure -{ - internal class ConfigurableConnectionInformationProvider : IConnectionInformationProvider - { - #region IConnectionInformationProvider - - ConnectionInformation IConnectionInformationProvider.GetConnectionInformation(ConnectionInformation currentConnection) - { - if (this.ExpectExistingConnection) - { - currentConnection.Should().NotBeNull("No existing connection provided"); - } - return this.ConnectionInformationToReturn; - } - - #endregion IConnectionInformationProvider - - #region Test helpers - - public bool ExpectExistingConnection - { - get; set; - } - - public ConnectionInformation ConnectionInformationToReturn - { - get; - set; - } - - #endregion Test helpers - } -} \ No newline at end of file diff --git a/src/TestInfrastructure/Framework/ConfigurableHost.cs b/src/TestInfrastructure/Framework/ConfigurableHost.cs deleted file mode 100644 index d52d512fde..0000000000 --- a/src/TestInfrastructure/Framework/ConfigurableHost.cs +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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 FluentAssertions; -using Microsoft.Alm.Authentication; -using SonarLint.VisualStudio.ConnectedMode.Shared; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Integration; -using SonarLint.VisualStudio.Integration.State; -using SonarLint.VisualStudio.Integration.TeamExplorer; -using SonarQube.Client; - -namespace SonarLint.VisualStudio.TestInfrastructure -{ - internal class ConfigurableHost : IHost - { - public ConfigurableHost() - { - this.VisualStateManager = new ConfigurableStateManager { Host = this }; - Logger = new TestLogger(); - } - - #region IHost - - public event EventHandler ActiveSectionChanged; - - public ISectionController ActiveSection - { - get; - private set; - } - - public ISonarQubeService SonarQubeService - { - get; - set; - } - - public IStateManager VisualStateManager - { - get; - set; - } - - public ILogger Logger { get; set; } - - public void ClearActiveSection() - { - this.ActiveSection = null; - - // Simulate product code - this.VisualStateManager.SyncCommandFromActiveSection(); - } - - public void SetActiveSection(ISectionController section) - { - section.Should().NotBeNull(); - - this.ActiveSection = section; - - // Simulate product code - this.VisualStateManager.SyncCommandFromActiveSection(); - } - - #endregion IHost - - #region Test helpers - - public void SimulateActiveSectionChanged() - { - this.ActiveSectionChanged?.Invoke(this, EventArgs.Empty); - } - - public ConfigurableStateManager TestStateManager - { - get { return (ConfigurableStateManager)this.VisualStateManager; } - } - - #endregion Test helpers - } -} diff --git a/src/TestInfrastructure/Framework/ConfigurableProgressControlHost.cs b/src/TestInfrastructure/Framework/ConfigurableProgressControlHost.cs deleted file mode 100644 index ea9074437a..0000000000 --- a/src/TestInfrastructure/Framework/ConfigurableProgressControlHost.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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 FluentAssertions; -using SonarLint.VisualStudio.Integration.Progress; - -namespace SonarLint.VisualStudio.TestInfrastructure -{ - internal class ConfigurableProgressControlHost : IProgressControlHost - { - internal ProgressControl ProgressControl { get; private set; } = null; - - #region IProgressControlHost - - void IProgressControlHost.Host(ProgressControl progressControl) - { - progressControl.Should().NotBeNull(); - this.ProgressControl = progressControl; - } - - #endregion IProgressControlHost - } -} \ No newline at end of file diff --git a/src/TestInfrastructure/Framework/ConfigurableProgressStepRunner.cs b/src/TestInfrastructure/Framework/ConfigurableProgressStepRunner.cs deleted file mode 100644 index ac5d44b96d..0000000000 --- a/src/TestInfrastructure/Framework/ConfigurableProgressStepRunner.cs +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 FluentAssertions; -using SonarLint.VisualStudio.Integration.Progress; - -namespace SonarLint.VisualStudio.TestInfrastructure -{ - internal class ConfigurableProgressStepRunner : IProgressStepRunnerWrapper - { - internal int AbortAllNumberOfCalls { get; private set; } - internal IProgressControlHost CurrentHost { get; private set; } - - #region IProgressStepRunnerWrapper - - void IProgressStepRunnerWrapper.AbortAll() - { - this.AbortAllNumberOfCalls++; - } - - void IProgressStepRunnerWrapper.ChangeHost(IProgressControlHost host) - { - host.Should().NotBeNull(); - - this.CurrentHost = host; - } - - #endregion IProgressStepRunnerWrapper - } -} \ No newline at end of file diff --git a/src/TestInfrastructure/Framework/ConfigurableSectionController.cs b/src/TestInfrastructure/Framework/ConfigurableSectionController.cs deleted file mode 100644 index 87cf81428d..0000000000 --- a/src/TestInfrastructure/Framework/ConfigurableSectionController.cs +++ /dev/null @@ -1,134 +0,0 @@ -/* - * 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.Windows.Input; -using Moq; -using SonarLint.VisualStudio.ConnectedMode.Binding; -using SonarLint.VisualStudio.Integration.Connection; -using SonarLint.VisualStudio.Integration.Progress; -using SonarLint.VisualStudio.Integration.TeamExplorer; -using SonarLint.VisualStudio.Integration.WPF; -using SonarQube.Client.Models; - -namespace SonarLint.VisualStudio.TestInfrastructure -{ - internal class ConfigurableSectionController : ISectionController - { - #region ISectionController - - public ICommand BindCommand - { - get; - set; - } - - public ICommand ConnectCommand - { - get; - set; - } - - public ICommand DisconnectCommand - { - get; - set; - } - - public ICommand ReconnectCommand - { - get; - set; - } - - public IProgressControlHost ProgressHost - { - get; - set; - } - - public ICommand RefreshCommand - { - get; - set; - } - - public ICommand ToggleShowAllProjectsCommand - { - get; - set; - } - - public IUserNotification UserNotifications - { - get; - set; - } - - public IConnectSectionViewModel ViewModel - { - get; - set; - } - - public IConnectSectionView View - { - get; - set; - } - - public ICommand BrowseToUrlCommand - { - get; - set; - } - - public ICommand BrowseToProjectDashboardCommand - { - get; - set; - } - - #endregion ISectionController - - #region Test helpers - - public static ConfigurableSectionController CreateDefault() - { - var section = new ConfigurableSectionController - { - ViewModel = new ConfigurableConnectSectionViewModel(), - View = Mock.Of(), - ProgressHost = new ConfigurableProgressControlHost(), - UserNotifications = new ConfigurableUserNotification(), - BindCommand = new RelayCommand(args => { }), - ConnectCommand = new RelayCommand(_ => { }), - DisconnectCommand = new RelayCommand(() => { }), - ReconnectCommand = new RelayCommand(() => { }), - RefreshCommand = new RelayCommand(c => { }), - BrowseToUrlCommand = new RelayCommand(url => { }), - BrowseToProjectDashboardCommand = new RelayCommand(vm => { }), - ToggleShowAllProjectsCommand = new RelayCommand(vm => { }) - }; - return section; - } - - #endregion Test helpers - } -} diff --git a/src/TestInfrastructure/Framework/ConfigurableStateManager.cs b/src/TestInfrastructure/Framework/ConfigurableStateManager.cs deleted file mode 100644 index 281b974fc5..0000000000 --- a/src/TestInfrastructure/Framework/ConfigurableStateManager.cs +++ /dev/null @@ -1,174 +0,0 @@ -/* - * 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 FluentAssertions; -using SonarLint.VisualStudio.Integration; -using SonarLint.VisualStudio.Integration.Connection; -using SonarLint.VisualStudio.Integration.State; -using SonarQube.Client.Models; - -namespace SonarLint.VisualStudio.TestInfrastructure -{ - internal class ConfigurableStateManager : IStateManager - { - public Uri AssignedServerUri { get; private set; } - public string AssignedOrganizationKey { get; private set; } - public string AssignedProjectKey { get; private set; } - - public ConfigurableStateManager() - { - this.ManagedState = new TransferableVisualState(); - } - - #region IStateManager - - public event EventHandler IsBusyChanged; - - public event EventHandler BindingStateChanged; - - public string BoundProjectKey - { - get; - set; - } - - public string BoundProjectName - { - get; - set; - } - - public bool IsBusy - { - get; - set; - } - - public bool HasSharedBinding { get; set; } - - public bool HasBoundProject - { - get - { - return this.AssignedProjectKey != null; - } - } - - public void ClearBoundProject() - { - this.VerifyActiveSection(); - - this.AssignedServerUri = null; - this.AssignedOrganizationKey = null; - this.AssignedProjectKey = null; - - this.BindingStateChanged?.Invoke(this, new BindingStateEventArgs(true)); - } - - public void SetBoundProject(Uri serverUri, string organizationKey, string projectKey) - { - serverUri.Should().NotBeNull(); - projectKey.Should().NotBeNull(); - - this.VerifyActiveSection(); - - this.AssignedServerUri = serverUri; - this.AssignedOrganizationKey = organizationKey; - this.AssignedProjectKey = projectKey; - - this.BindingStateChanged?.Invoke(this, new BindingStateEventArgs(false)); - } - - public void SetProjects(ConnectionInformation connection, IEnumerable projects) - { - this.VerifyActiveSection(); - this.SetProjectsAction?.Invoke(connection, projects); - } - - public void SyncCommandFromActiveSection() - { - this.VerifyActiveSection(); - this.SyncCommandFromActiveSectionAction?.Invoke(); - } - - public bool IsConnected { get; set; } - - public void ResetConnectionConfiguration() - { - ResetConnectionConfigCalled++; - } - - public IEnumerable GetConnectedServers() - { - return this.ConnectedServers; - } - - #endregion IStateManager - - #region Test helpers - - public IHost Host { get; set; } - - public HashSet ConnectedServers { get; } = new HashSet(); - - public Dictionary ProjectServerMap { get; } = new Dictionary(); - - public TransferableVisualState ManagedState { get; set; } - - public int SyncCommandFromActiveSectionCalled { get; private set; } - - public bool? ExpectActiveSection { get; set; } - - public Action> SetProjectsAction { get; set; } - - public Action SyncCommandFromActiveSectionAction { get; set; } - - private void VerifyActiveSection() - { - if (!this.ExpectActiveSection.HasValue) - { - return; - } - - this.Host.Should().NotBeNull("Test setup issue: the Host needs to be set"); - - if (this.ExpectActiveSection.Value) - { - this.Host.ActiveSection.Should().NotBeNull("ActiveSection is null"); - } - else - { - this.Host.ActiveSection.Should().BeNull("ActiveSection is not null"); - } - } - - public void SetAndInvokeBusyChanged(bool value) - { - this.IsBusy = value; - this.IsBusyChanged?.Invoke(this, value); - } - - public int ResetConnectionConfigCalled { get; set; } - - #endregion Test helpers - } -} diff --git a/src/TestInfrastructure/Framework/ConfigurableUriValidator.cs b/src/TestInfrastructure/Framework/ConfigurableUriValidator.cs deleted file mode 100644 index 35a9359ef3..0000000000 --- a/src/TestInfrastructure/Framework/ConfigurableUriValidator.cs +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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 SonarLint.VisualStudio.Integration.Connection; - -namespace SonarLint.VisualStudio.TestInfrastructure -{ - internal class ConfigurableUriValidator : UriValidator - { - #region Configurable properties - - public bool? IsValidUriOverride { get; set; } - - #endregion Configurable properties - - public ConfigurableUriValidator() - { - } - - public ConfigurableUriValidator(bool? isValidUriOverride) - { - this.IsValidUriOverride = isValidUriOverride; - } - - public ConfigurableUriValidator(ISet supportedSchemes) - : base(supportedSchemes) - { - } - - public ConfigurableUriValidator(ISet supportedSchemes, ISet insecureSchemes) - : base(supportedSchemes, insecureSchemes) - { - } - - public override bool IsValidUri(string uriString) - { - if (this.IsValidUriOverride.HasValue) - { - return this.IsValidUriOverride.Value; - } - return base.IsValidUri(uriString); - } - - public override bool IsValidUri(Uri uri) - { - if (this.IsValidUriOverride.HasValue) - { - return this.IsValidUriOverride.Value; - } - return base.IsValidUri(uri); - } - } -} \ No newline at end of file diff --git a/src/TestInfrastructure/Framework/ConfigurableUserNotification.cs b/src/TestInfrastructure/Framework/ConfigurableUserNotification.cs deleted file mode 100644 index fd88f734b0..0000000000 --- a/src/TestInfrastructure/Framework/ConfigurableUserNotification.cs +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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.Windows.Input; -using FluentAssertions; -using SonarLint.VisualStudio.Integration.TeamExplorer; - -namespace SonarLint.VisualStudio.TestInfrastructure -{ - internal class ConfigurableUserNotification : IUserNotification - { - private enum NotificationType - { - Information = 0, - Warning = 1, - Error = 2 - } - - private class Notification - { - public NotificationType Type { get; } - - public string Message { get; } - - public ICommand AssociatedCommand { get; } - - public Notification(NotificationType type, string message, ICommand associatedCommand) - { - this.Type = type; - this.Message = message; - this.AssociatedCommand = associatedCommand; - } - } - - private readonly List showErrorRequests = new List(); - private readonly IDictionary notifications = new Dictionary(); - - #region IUserNotification - - bool IUserNotification.HideNotification(Guid id) - { - return this.notifications.Remove(id); - } - - void IUserNotification.ShowNotificationError(string message, Guid notificationId, ICommand associatedCommand) - { - this.notifications[notificationId] = new Notification(NotificationType.Error, message, associatedCommand); - } - - void IUserNotification.ShowNotificationWarning(string message, Guid notificationId, ICommand associatedCommand) - { - this.notifications[notificationId] = new Notification(NotificationType.Warning, message, associatedCommand); - } - - #endregion IUserNotification - - #region Test helpers - - public void AssertNoShowErrorMessages() - { - this.showErrorRequests.Should().BeEmpty("Unexpected messages: {0}", string.Join(", ", this.showErrorRequests)); - } - - public void AssertSingleErrorMessage(string expected) - { - this.showErrorRequests.Should().HaveCount(1, "Unexpected messages: {0}", string.Join(", ", this.showErrorRequests)); - this.showErrorRequests[0].Should().Be(expected, "Unexpected message"); - } - - public void AssertNotification(Guid notificationId, string expected) - { - string message = this.AssertNotification(notificationId); - message.Should().Be(expected, "Unexpected message"); - } - - public void AssertNotification(Guid notificationId, ICommand expectedCommand) - { - Notification notification; - this.notifications.TryGetValue(notificationId, out notification).Should().BeTrue("Unexpected notificationId: {0}", notificationId); - notification.AssociatedCommand.Should().Be(expectedCommand, "Unexpected message"); - } - - public string AssertNotification(Guid notificationId) - { - Notification notification; - this.notifications.TryGetValue(notificationId, out notification).Should().BeTrue("Unexpected notificationId: {0}", notificationId); - return notification.Message; - } - - public void AssertNoNotification(Guid notificationId) - { - Notification notification; - this.notifications.TryGetValue(notificationId, out notification).Should().BeFalse("Unexpected notification: {0}", notification?.Message); - } - - #endregion Test helpers - } -}