async Task<StepExecutionState> IProgressStepOperation.Run(CancellationToken cancellationToken, IProgressStepExecutionEvents progressCallback) { if (this.ExecutionState != StepExecutionState.NotStarted) { throw new InvalidOperationException(ProgressResources.StepOperationWasAlreadyExecuted); } if (this.Cancellable && cancellationToken.IsCancellationRequested) { return this.ExecutionState = StepExecutionState.Cancelled; } VsTaskRunContext context = GetContext(this.Execution); StepExecutionState stepState = await VsThreadingHelper.RunTask<StepExecutionState>(this.controller, context, () => { DoStatefulExecution(progressCallback, cancellationToken); return this.ExecutionState; }, cancellationToken); return stepState; }
Task<StepExecutionState> IProgressStepOperation.Run(CancellationToken cancellationToken, IProgressStepExecutionEvents executionNotify) { Assert.IsNotNull(cancellationToken, "cancellationToken is not expected to be null"); Assert.IsNotNull(executionNotify, "executionNotify is not expected to be null"); return Task.Factory.StartNew(() => { this.ExecutionState = StepExecutionState.Executing; this.operation(cancellationToken, executionNotify); this.executed = true; return this.ExecutionState = this.ExecutionResult; }); }
/// <summary> /// Creates an instance of <see cref="DeterminateStepProgressNotifier"/> /// </summary> /// <param name="executionEvents">Required <see cref="IProgressStepExecutionEvents"/></param> /// <param name="numberOfIncrements">The number of predefined increments to the progress, at least one is expected.</param> public DeterminateStepProgressNotifier(IProgressStepExecutionEvents executionEvents, int numberOfIncrements) { if (executionEvents == null) { throw new ArgumentNullException(nameof(executionEvents)); } if (numberOfIncrements < 1) { throw new ArgumentOutOfRangeException(nameof(numberOfIncrements)); } this.executionEvents = executionEvents; this.maxNumberOfIncrements = numberOfIncrements; }
protected virtual void DoStatefulExecution(IProgressStepExecutionEvents progressCallback, CancellationToken cancellationToken) { try { Debug.Assert(this.ExecutionState == StepExecutionState.NotStarted, "Unexpected stated"); if (this.Execution == StepExecution.ForegroundThread) { ThreadHelper.ThrowIfNotOnUIThread(); } else { Debug.Assert(this.Execution == StepExecution.BackgroundThread, "Unexpected enum value: " + this.Execution); ThreadHelper.ThrowIfOnUIThread(); } this.ExecutionState = StepExecutionState.Executing; this.ExecuteOperation(cancellationToken, progressCallback); this.ExecutionState = StepExecutionState.Succeeded; } catch (OperationCanceledException) { this.ExecutionState = StepExecutionState.Cancelled; } catch (Exception ex) { Debug.WriteLine(ex.ToString(), "DoStatefulExecution-Failed"); if (ErrorHandler.IsCriticalException(ex)) { throw; } this.ExecutionState = StepExecutionState.Failed; Debug.Assert(this.controller.ErrorNotificationManager != null, "Expecting valid ErrorNotifier"); if (this.controller.ErrorNotificationManager != null) { this.controller.ErrorNotificationManager.Notify(ex); } } }
internal /*for testing purposes*/ void EmitBindingCompleteMessage(IProgressStepExecutionEvents notifications) { var message = this.AllNuGetPackagesInstalled ? Strings.FinishedSolutionBindingWorkflowSuccessful : Strings.FinishedSolutionBindingWorkflowNotAllPackagesInstalled; notifications.ProgressChanged(message); }
/// <summary> /// Step operation that will throw an exception and cause the step to fail /// </summary> /// <param name="token">Cancellation token</param> /// <param name="notifier">Progress notifier</param> private void Fail(CancellationToken token, IProgressStepExecutionEvents notifier) { AssertOperationArgumentsAreNotNull(token, notifier); throw new Exception(); }
internal /*for testing purposes*/ void DiscoverProjects(IProgressController controller, IProgressStepExecutionEvents notifications) { Debug.Assert(ThreadHelper.CheckAccess(), "Expected step to be run on the UI thread"); notifications.ProgressChanged(Strings.DiscoveringSolutionProjectsProgressMessage); this.BindingProjects.UnionWith(this.projectSystem.GetFilteredSolutionProjects()); this.InformAboutFilteredOutProjects(); if (!this.BindingProjects.Any()) { AbortWorkflow(controller, CancellationToken.None); } }
private void InitializeSolutionBindingOnUIThread(IProgressStepExecutionEvents notificationEvents) { Debug.Assert(System.Windows.Application.Current?.Dispatcher.CheckAccess() ?? false, "Expected to run on UI thread"); notificationEvents.ProgressChanged(Strings.RuleSetGenerationProgressMessage); this.solutionBindingOperation.RegisterKnownRuleSets(this.Rulesets); this.solutionBindingOperation.Initialize(this.BindingProjects, this.QualityProfiles); }
public Task <StepExecutionState> RunAsync(System.Threading.CancellationToken cancellationToken, IProgressStepExecutionEvents executionNotify) { throw new NotImplementedException(); }
internal /*for testing purposes*/ void DiscoverProjects(IProgressController controller, IProgressStepExecutionEvents notifications) { Debug.Assert(ThreadHelper.CheckAccess(), "Expected step to be run on the UI thread"); notifications.ProgressChanged(Strings.DiscoveringSolutionProjectsProgressMessage); var patternFilteredProjects = this.projectSystem.GetFilteredSolutionProjects(); var pluginAndPatternFilteredProjects = patternFilteredProjects.Where(p => this.host.SupportedPluginLanguages.Contains(Language.ForProject(p))); this.BindingProjects.UnionWith(pluginAndPatternFilteredProjects); this.InformAboutFilteredOutProjects(); if (!this.BindingProjects.Any()) { AbortWorkflow(controller, CancellationToken.None); } }
internal /*for testing purposes*/ void DownloadServiceParameters(IProgressController controller, CancellationToken token, IProgressStepExecutionEvents notifications) { Debug.Assert(this.ConnectedServer != null); // Should never realistically take more than 1 second to match against a project name var timeout = TimeSpan.FromSeconds(1); var defaultRegex = new Regex(ServerProperty.TestProjectRegexDefaultValue, RegexOptions.IgnoreCase, timeout); notifications.ProgressChanged(Strings.DownloadingServerSettingsProgessMessage); ServerProperty[] properties; if (!this.host.SonarQubeService.TryGetProperties(this.ConnectedServer, token, out properties) || token.IsCancellationRequested) { AbortWorkflow(controller, token); return; } var testProjRegexProperty = properties.FirstOrDefault(x => StringComparer.Ordinal.Equals(x.Key, ServerProperty.TestProjectRegexKey)); // Using this older API, if the property hasn't been explicitly set no default value is returned. // http://jira.sonarsource.com/browse/SONAR-5891 var testProjRegexPattern = testProjRegexProperty?.Value ?? ServerProperty.TestProjectRegexDefaultValue; Regex regex = null; if (testProjRegexPattern != null) { // Try and create regex from provided server pattern. // No way to determine a valid pattern other than attempting to construct // the Regex object. try { regex = new Regex(testProjRegexPattern, RegexOptions.IgnoreCase, timeout); } catch (ArgumentException) { VsShellUtils.WriteToSonarLintOutputPane(this.host, Strings.InvalidTestProjectRegexPattern, testProjRegexPattern); } } var projectFilter = this.host.GetService<IProjectSystemFilter>(); projectFilter.AssertLocalServiceIsNotNull(); projectFilter.SetTestRegex(regex ?? defaultRegex); }
private void ExecuteNonCancellable(CancellationToken token, IProgressStepExecutionEvents notifier) { VerificationHelper.CheckState(this.testSubject, StepExecutionState.Executing); Assert.IsFalse(this.testController.IsCurrentStepCancellable, "Not expected to be cancellable"); }
internal /*for testing purposes*/ void DownloadQualityProfile(IProgressController controller, CancellationToken cancellationToken, IProgressStepExecutionEvents notificationEvents, IEnumerable <Language> languages) { Debug.Assert(controller != null); Debug.Assert(notificationEvents != null); bool failed = false; var rulesets = new Dictionary <Language, RuleSet>(); var languageList = languages as IList <Language> ?? languages.ToList(); DeterminateStepProgressNotifier notifier = new DeterminateStepProgressNotifier(notificationEvents, languageList.Count); notifier.NotifyCurrentProgress(Strings.DownloadingQualityProfileProgressMessage); foreach (var language in languageList) { QualityProfile qualityProfileInfo; if (!host.SonarQubeService.TryGetQualityProfile(this.connectionInformation, this.project, language, cancellationToken, out qualityProfileInfo)) { failed = true; InformAboutQualityProfileToDownload(qualityProfileInfo.Name, qualityProfileInfo.Key, language.Name, true); break; } this.QualityProfiles[language] = qualityProfileInfo; RoslynExportProfile export; if (!this.host.SonarQubeService.TryGetExportProfile(this.connectionInformation, qualityProfileInfo, language, cancellationToken, out export)) { failed = true; InformAboutQualityProfileToDownload(qualityProfileInfo.Name, qualityProfileInfo.Key, language.Name, true); break; } this.NuGetPackages.Add(language, export.Deployment.NuGetPackages); var tempRuleSetFilePath = Path.GetTempFileName(); File.WriteAllText(tempRuleSetFilePath, export.Configuration.RuleSet.OuterXml); RuleSet ruleSet = RuleSet.LoadFromFile(tempRuleSetFilePath); // Remove/Move/Refactor code when XML ruleset file is no longer downloaded but the proper API is used to retrieve rules UpdateDownloadedSonarQubeQualityProfile(ruleSet, qualityProfileInfo); rulesets[language] = ruleSet; notifier.NotifyIncrementedProgress(string.Empty); if (rulesets[language] == null) { failed = true; InformAboutQualityProfileToDownload(qualityProfileInfo.Name, qualityProfileInfo.Key, language.Name, true); break; } InformAboutQualityProfileToDownload(qualityProfileInfo.Name, qualityProfileInfo.Key, language.Name, false); } if (failed) { this.AbortWorkflow(controller, cancellationToken); } else { // Set the rule set which should be available for the following steps foreach (var keyValue in rulesets) { this.Rulesets[keyValue.Key] = keyValue.Value; } } }
/// <summary> /// Step operation that will notify the progress /// </summary> /// <param name="token">Cancellation token</param> /// <param name="notifier">Progress notifier</param> private void NotifyProgress(CancellationToken token, IProgressStepExecutionEvents notifier) { AssertOperationArgumentsAreNotNull(token, notifier); this.notifyProgressSequence.ForEach(t => notifier.ProgressChanged(t.Item1, t.Item2)); }
public FixedStepsProgressAdapter(IProgressStepExecutionEvents executionEvents) { this.executionEvents = executionEvents ?? throw new ArgumentNullException(nameof(executionEvents)); }
Task <StepExecutionState> IProgressStepOperation.RunAsync(CancellationToken cancellationToken, IProgressStepExecutionEvents executionNotify) { cancellationToken.Should().NotBeNull("cancellationToken is not expected to be null"); executionNotify.Should().NotBeNull("executionNotify is not expected to be null"); return(Task.Factory.StartNew(() => { this.ExecutionState = StepExecutionState.Executing; this.operation(cancellationToken, executionNotify); this.IsExecuted = true; return this.ExecutionState = this.ExecutionResult; })); }
private void ExecuteAndVerify(CancellationToken token, IProgressStepExecutionEvents notifier) { VerificationHelper.CheckState(this.testSubject, StepExecutionState.Executing); }
private void ExecuteAndFail(CancellationToken token, IProgressStepExecutionEvents notifier) { VerificationHelper.CheckState(this.testSubject, StepExecutionState.Executing); throw new Exception("Boom"); }
/// <summary> /// Will install the NuGet packages for the current managed projects. /// The packages that will be installed will be based on the information from <see cref="Analyzer.GetRequiredNuGetPackages"/> /// and is specific to the <see cref="RuleSet"/>. /// </summary> internal /*for testing purposes*/ void InstallPackages(IProgressController controller, CancellationToken token, IProgressStepExecutionEvents notificationEvents) { if (!this.NuGetPackages.Any() || !this.settings.AllowNuGetPackageInstall) { return; } Debug.Assert(this.NuGetPackages.Count == this.NuGetPackages.Distinct().Count(), "Duplicate NuGet packages specified"); if (!this.BindingProjects.Any()) { Debug.Fail("Not expected to be called when there are no projects"); return; } DeterminateStepProgressNotifier progressNotifier = new DeterminateStepProgressNotifier(notificationEvents, this.BindingProjects.Count * this.NuGetPackages.Count); foreach (var bindingProject in this.BindingProjects) { List <NuGetPackageInfo> nugetPackages; if (!this.NuGetPackages.TryGetValue(Language.ForProject(bindingProject), out nugetPackages)) { var message = string.Format(Strings.BindingProjectLanguageNotMatchingAnyQualityProfileLanguage, bindingProject.Name); VsShellUtils.WriteToSonarLintOutputPane(this.host, Strings.SubTextPaddingFormat, message); continue; } foreach (var packageInfo in nugetPackages) { if (token.IsCancellationRequested) { break; } string message = string.Format(CultureInfo.CurrentCulture, Strings.EnsuringNugetPackagesProgressMessage, packageInfo.Id, bindingProject.Name); VsShellUtils.WriteToSonarLintOutputPane(this.host, Strings.SubTextPaddingFormat, message); var isNugetInstallSuccessful = NuGetHelper.TryInstallPackage(this.host, bindingProject, packageInfo.Id, packageInfo.Version); if (isNugetInstallSuccessful) // NuGetHelper.TryInstallPackage already displayed the error message so only take care of the success message { message = string.Format(CultureInfo.CurrentCulture, Strings.SuccessfullyInstalledNugetPackageForProject, packageInfo.Id, bindingProject.Name); VsShellUtils.WriteToSonarLintOutputPane(this.host, Strings.SubTextPaddingFormat, message); } // TODO: SVS-33 (https://jira.sonarsource.com/browse/SVS-33) Trigger a Team Explorer warning notification to investigate the partial binding in the output window. this.AllNuGetPackagesInstalled &= isNugetInstallSuccessful; progressNotifier.NotifyIncrementedProgress(string.Empty); } } }
/// <summary> /// Will install the NuGet packages for the current managed projects. /// The packages that will be installed will be based on the information from <see cref="Analyzer.GetRequiredNuGetPackages"/> /// and is specific to the <see cref="RuleSet"/>. /// </summary> public bool InstallPackages(ISet <Project> projectsToBind, IProgressController controller, IProgressStepExecutionEvents notificationEvents, CancellationToken token) { // Nothing to do - just return success this.logger.WriteLine(Strings.Bind_NuGetAnalyzersNoLongerInstalled); return(true); }
/// <summary> /// Step operation that doesn't do anything /// </summary> /// <param name="token">Cancellation token</param> /// <param name="notifier">Progress notifier</param> private void DoNothing(CancellationToken token, IProgressStepExecutionEvents notifier) { AssertOperationArgumentsAreNotNull(token, notifier); }
async Task <StepExecutionState> IProgressStepOperation.Run(CancellationToken cancellationToken, IProgressStepExecutionEvents progressCallback) { if (this.ExecutionState != StepExecutionState.NotStarted) { throw new InvalidOperationException(ProgressResources.StepOperationWasAlreadyExecuted); } if (this.Cancellable && cancellationToken.IsCancellationRequested) { return(this.ExecutionState = StepExecutionState.Cancelled); } VsTaskRunContext context = GetContext(this.Execution); StepExecutionState stepState = await VsThreadingHelper.RunTask <StepExecutionState>(this.controller, context, () => { DoStatefulExecution(progressCallback, cancellationToken); return(this.ExecutionState); }, cancellationToken); return(stepState); }
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); } 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 { AbortWithMessage(notifications, controller, cancellationToken); // TODO: Might be worth throwing return; } } } // 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())); } var isCompatible = await this.AreSolutionProjectsAndSonarQubePluginsCompatibleAsync(controller, notifications, cancellationToken); if (!isCompatible) { return; // Message is already displayed by the method } this.ConnectedServer = connection; notifications.ProgressChanged(Strings.ConnectionStepRetrievingProjects); var projects = await this.host.SonarQubeService.GetAllProjectsAsync(connection.Organization?.Key, cancellationToken); // 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 <SonarQubeProject>(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)); } this.OnProjectsChanged(connection, projects); notifications.ProgressChanged(Strings.ConnectionResultSuccess); } 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); AbortWithMessage(notifications, controller, cancellationToken); } catch (TaskCanceledException) { // Canceled or timeout this.host.Logger.WriteLine(CoreStrings.SonarQubeRequestTimeoutOrCancelled); AbortWithMessage(notifications, controller, cancellationToken); } catch (Exception ex) { this.host.Logger.WriteLine(CoreStrings.SonarQubeRequestFailed, ex.Message, null); AbortWithMessage(notifications, controller, cancellationToken); } }
private async Task <bool> AreSolutionProjectsAndSonarQubePluginsCompatibleAsync(IProgressController controller, IProgressStepExecutionEvents notifications, CancellationToken cancellationToken) { notifications.ProgressChanged(Strings.DetectingSonarQubePlugins); var plugins = await this.host.SonarQubeService.GetAllPluginsAsync(cancellationToken); var csharpOrVbNetProjects = new HashSet <EnvDTE.Project>(this.projectSystem.GetSolutionProjects()); var supportedPluginsLanguages = MinimumSupportedSonarQubePlugin.All .Where(supportedPlugin => IsSonarQubePluginSupported(plugins, supportedPlugin, host.Logger)) .SelectMany(lang => lang.Languages); this.host.SupportedPluginLanguages.UnionWith(new HashSet <Language>(supportedPluginsLanguages)); // If any of the projects can be bound then return success if (csharpOrVbNetProjects.Select(ProjectToLanguageMapper.GetLanguageForProject) .Any(this.host.SupportedPluginLanguages.Contains)) { return(true); } string errorMessage = GetPluginProjectMismatchErrorMessage(csharpOrVbNetProjects); this.host.ActiveSection?.UserNotifications?.ShowNotificationError(errorMessage, NotificationIds.BadSonarQubePluginId, null); this.host.Logger.WriteLine(Strings.SubTextPaddingFormat, errorMessage); notifications.ProgressChanged(Strings.ConnectionResultFailure); AbortWorkflow(controller, cancellationToken); return(false); }
internal /*for testing purposes*/ async System.Threading.Tasks.Task DownloadQualityProfileAsync( IProgressController controller, IProgressStepExecutionEvents notificationEvents, IEnumerable <Language> languages, CancellationToken cancellationToken) { Debug.Assert(controller != null); Debug.Assert(notificationEvents != null); var rulesets = new Dictionary <Language, RuleSet>(); var languageList = languages as IList <Language> ?? languages.ToList(); DeterminateStepProgressNotifier notifier = new DeterminateStepProgressNotifier(notificationEvents, languageList.Count); notifier.NotifyCurrentProgress(Strings.DownloadingQualityProfileProgressMessage); foreach (var language in languageList) { var serverLanguage = language.ToServerLanguage(); var qualityProfileInfo = await WebServiceHelper.SafeServiceCall(() => this.host.SonarQubeService.GetQualityProfileAsync( this.bindingArgs.ProjectKey, this.bindingArgs.Connection.Organization?.Key, serverLanguage, cancellationToken), this.host.Logger); if (qualityProfileInfo == null) { this.host.Logger.WriteLine(string.Format(Strings.SubTextPaddingFormat, string.Format(Strings.CannotDownloadQualityProfileForLanguage, language.Name))); this.AbortWorkflow(controller, cancellationToken); return; } this.QualityProfiles[language] = qualityProfileInfo; var roslynProfileExporter = await WebServiceHelper.SafeServiceCall(() => this.host.SonarQubeService.GetRoslynExportProfileAsync(qualityProfileInfo.Name, this.bindingArgs.Connection.Organization?.Key, serverLanguage, cancellationToken), this.host.Logger); if (roslynProfileExporter == null) { this.host.Logger.WriteLine(string.Format(Strings.SubTextPaddingFormat, string.Format(Strings.QualityProfileDownloadFailedMessageFormat, qualityProfileInfo.Name, qualityProfileInfo.Key, language.Name))); this.AbortWorkflow(controller, cancellationToken); return; } var tempRuleSetFilePath = Path.GetTempFileName(); File.WriteAllText(tempRuleSetFilePath, roslynProfileExporter.Configuration.RuleSet.OuterXml); RuleSet ruleSet = RuleSet.LoadFromFile(tempRuleSetFilePath); if (ruleSet == null || ruleSet.Rules.Count == 0 || ruleSet.Rules.All(rule => rule.Action == RuleAction.None)) { this.host.Logger.WriteLine(string.Format(Strings.SubTextPaddingFormat, string.Format(Strings.NoSonarAnalyzerActiveRulesForQualityProfile, qualityProfileInfo.Name, language.Name))); this.AbortWorkflow(controller, cancellationToken); return; } if (!this.NuGetBindingOperation.ProcessExport(language, roslynProfileExporter)) { this.AbortWorkflow(controller, cancellationToken); return; } // Remove/Move/Refactor code when XML ruleset file is no longer downloaded but the proper API is used to retrieve rules UpdateDownloadedSonarQubeQualityProfile(ruleSet, qualityProfileInfo); rulesets[language] = ruleSet; notifier.NotifyIncrementedProgress(string.Empty); this.host.Logger.WriteLine(string.Format(Strings.SubTextPaddingFormat, string.Format(Strings.QualityProfileDownloadSuccessfulMessageFormat, qualityProfileInfo.Name, qualityProfileInfo.Key, language.Name))); } // Set the rule set which should be available for the following steps foreach (var keyValue in rulesets) { this.Rulesets[keyValue.Key] = keyValue.Value; } }
internal /*for testing purposes*/ void DiscoverProjects(IProgressController controller, IProgressStepExecutionEvents notifications) { Debug.Assert(ThreadHelper.CheckAccess(), "Expected step to be run on the UI thread"); notifications.ProgressChanged(Strings.DiscoveringSolutionProjectsProgressMessage, double.NaN); this.BindingProjects.UnionWith(this.projectSystem.GetFilteredSolutionProjects()); this.InformAboutFilteredOutProjects(this.projectSystem.GetSolutionProjects().Except(this.BindingProjects)); if (!this.BindingProjects.Any()) { VsShellUtils.WriteToSonarLintOutputPane(this.host, Strings.NoProjectsApplicableForBinding); AbortWorkflow(controller, CancellationToken.None); } }
internal /*for testing purposes*/ void DownloadQualityProfile(IProgressController controller, CancellationToken cancellationToken, IProgressStepExecutionEvents notificationEvents, IEnumerable<Language> languages) { Debug.Assert(controller != null); Debug.Assert(notificationEvents != null); bool failed = false; var rulesets = new Dictionary<Language, RuleSet>(); var languageList = languages as IList<Language> ?? languages.ToList(); DeterminateStepProgressNotifier notifier = new DeterminateStepProgressNotifier(notificationEvents, languageList.Count); notifier.NotifyCurrentProgress(Strings.DownloadingQualityProfileProgressMessage); foreach (var language in languageList) { QualityProfile qualityProfileInfo; if (!host.SonarQubeService.TryGetQualityProfile(this.connectionInformation, this.project, language, cancellationToken, out qualityProfileInfo)) { failed = true; InformAboutQualityProfileToDownload(qualityProfileInfo.Name, qualityProfileInfo.Key, language.Name, true); break; } this.QualityProfiles[language] = qualityProfileInfo; RoslynExportProfile export; if (!this.host.SonarQubeService.TryGetExportProfile(this.connectionInformation, qualityProfileInfo, language, cancellationToken, out export)) { failed = true; InformAboutQualityProfileToDownload(qualityProfileInfo.Name, qualityProfileInfo.Key, language.Name, true); break; } this.NuGetPackages.Add(language, export.Deployment.NuGetPackages); var tempRuleSetFilePath = Path.GetTempFileName(); File.WriteAllText(tempRuleSetFilePath, export.Configuration.RuleSet.OuterXml); RuleSet ruleSet = RuleSet.LoadFromFile(tempRuleSetFilePath); // Remove/Move/Refactor code when XML ruleset file is no longer downloaded but the proper API is used to retrieve rules UpdateDownloadedSonarQubeQualityProfile(ruleSet, qualityProfileInfo); rulesets[language] = ruleSet; notifier.NotifyIncrementedProgress(string.Empty); if (rulesets[language] == null) { failed = true; InformAboutQualityProfileToDownload(qualityProfileInfo.Name, qualityProfileInfo.Key, language.Name, true); break; } InformAboutQualityProfileToDownload(qualityProfileInfo.Name, qualityProfileInfo.Key, language.Name, false); } if (failed) { this.AbortWorkflow(controller, cancellationToken); } else { // Set the rule set which should be available for the following steps foreach (var keyValue in rulesets) { this.Rulesets[keyValue.Key] = keyValue.Value; } } }
internal /*for testing purposes*/ void DownloadQualityProfile(IProgressController controller, CancellationToken cancellationToken, IProgressStepExecutionEvents notificationEvents, IEnumerable<Language> languages) { Debug.Assert(controller != null); Debug.Assert(notificationEvents != null); bool failed = false; var rulesets = new Dictionary<Language, RuleSet>(); DeterminateStepProgressNotifier notifier = new DeterminateStepProgressNotifier(notificationEvents, languages.Count()); foreach (var language in languages) { notifier.NotifyCurrentProgress(string.Format(CultureInfo.CurrentCulture, Strings.DownloadingQualityProfileProgressMessage, language.Name)); QualityProfile qualityProfileInfo; if (!host.SonarQubeService.TryGetQualityProfile(this.connectionInformation, this.project, language, cancellationToken, out qualityProfileInfo)) { failed = true; break; } this.QualityProfiles[language] = qualityProfileInfo; RoslynExportProfile export; if (!this.host.SonarQubeService.TryGetExportProfile(this.connectionInformation, qualityProfileInfo, language, cancellationToken, out export)) { failed = true; break; } this.NuGetPackages.AddRange(export.Deployment.NuGetPackages); var tempRuleSetFilePath = Path.GetTempFileName(); File.WriteAllText(tempRuleSetFilePath, export.Configuration.RuleSet.OuterXml); RuleSet ruleSet = RuleSet.LoadFromFile(tempRuleSetFilePath); rulesets[language] = ruleSet; notifier.NotifyIncrementedProgress(string.Empty); if (rulesets[language] == null) { failed = true; break; } } if (failed) { VsShellUtils.WriteToSonarLintOutputPane(this.host, Strings.QualityProfileDownloadFailedMessage); this.AbortWorkflow(controller, cancellationToken); } else { // Set the rule set which should be available for the following steps foreach (var keyValue in rulesets) { this.Rulesets[keyValue.Key] = keyValue.Value; } notifier.NotifyCurrentProgress(Strings.QualityProfileDownloadedSuccessfulMessage); } }
/// <summary> /// Will install the NuGet packages for the current managed projects. /// The packages that will be installed will be based on the information from <see cref="Analyzer.GetRequiredNuGetPackages"/> /// and is specific to the <see cref="RuleSet"/>. /// </summary> internal /*for testing purposes*/ void InstallPackages(IProgressController controller, CancellationToken token, IProgressStepExecutionEvents notificationEvents) { if (!this.NuGetPackages.Any()) { return; } Debug.Assert(this.NuGetPackages.Count == this.NuGetPackages.Distinct().Count(), "Duplicate NuGet packages specified"); if (!this.BindingProjects.Any()) { Debug.Fail("Not expected to be called when there are no projects"); return; } DeterminateStepProgressNotifier progressNotifier = new DeterminateStepProgressNotifier(notificationEvents, this.BindingProjects.Count * this.NuGetPackages.Count); foreach (var bindingProject in this.BindingProjects) { List<NuGetPackageInfo> nugetPackages; if (!this.NuGetPackages.TryGetValue(Language.ForProject(bindingProject), out nugetPackages)) { var message = string.Format(Strings.BindingProjectLanguageNotMatchingAnyQualityProfileLanguage, bindingProject.Name); VsShellUtils.WriteToSonarLintOutputPane(this.host, Strings.SubTextPaddingFormat, message); continue; } foreach (var packageInfo in nugetPackages) { if (token.IsCancellationRequested) { break; } string message = string.Format(CultureInfo.CurrentCulture, Strings.EnsuringNugetPackagesProgressMessage, packageInfo.Id, bindingProject.Name); VsShellUtils.WriteToSonarLintOutputPane(this.host, Strings.SubTextPaddingFormat, message); var isNugetInstallSuccessful = NuGetHelper.TryInstallPackage(this.host, bindingProject, packageInfo.Id, packageInfo.Version); if (isNugetInstallSuccessful) // NuGetHelper.TryInstallPackage already displayed the error message so only take care of the success message { message = string.Format(CultureInfo.CurrentCulture, Strings.SuccessfullyInstalledNugetPackageForProject, packageInfo.Id, bindingProject.Name); VsShellUtils.WriteToSonarLintOutputPane(this.host, Strings.SubTextPaddingFormat, message); } // TODO: SVS-33 (https://jira.sonarsource.com/browse/SVS-33) Trigger a Team Explorer warning notification to investigate the partial binding in the output window. this.AllNuGetPackagesInstalled &= isNugetInstallSuccessful; progressNotifier.NotifyIncrementedProgress(string.Empty); } } }
/// <summary> /// Will install the NuGet packages for the current managed projects. /// The packages that will be installed will be based on the information from <see cref="Analyzer.GetRequiredNuGetPackages"/> /// and is specific to the <see cref="RuleSet"/>. /// </summary> internal /*for testing purposes*/ void InstallPackages(IProgressController controller, CancellationToken token, IProgressStepExecutionEvents notificationEvents) { if (!this.NuGetPackages.Any()) { return; } Debug.Assert(this.NuGetPackages.Count == this.NuGetPackages.Distinct().Count(), "Duplicate NuGet packages specified"); if (!this.BindingProjects.Any()) { Debug.Fail("Not expected to be called when there are no projects"); return; } DeterminateStepProgressNotifier progressNotifier = new DeterminateStepProgressNotifier(notificationEvents, this.BindingProjects.Count * this.NuGetPackages.Count); foreach (var project in this.BindingProjects) { foreach (var packageInfo in this.NuGetPackages) { if (token.IsCancellationRequested) { break; } string message = string.Format(CultureInfo.CurrentCulture, Strings.EnsuringNugetPackagesProgressMessage, packageInfo.Id, project.Name); progressNotifier.NotifyCurrentProgress(message); // TODO: SVS-33 (https://jira.sonarsource.com/browse/SVS-33) Trigger a Team Explorer warning notification to investigate the partial binding in the output window. this.AllNuGetPackagesInstalled &= NuGetHelper.TryInstallPackage(this.host, project, packageInfo.Id, packageInfo.Version); progressNotifier.NotifyIncrementedProgress(string.Empty); } } }
private static void AssertOperationArgumentsAreNotNull(CancellationToken token, IProgressStepExecutionEvents callback) { Assert.IsNotNull(token, "CancellationToken is expected not to be null"); Assert.IsNotNull(callback, "IProgressStepExecutionEvents is expected not to be null"); }
private bool VerifyCSharpPlugin(IProgressController controller, CancellationToken cancellationToken, ConnectionInformation connection, IProgressStepExecutionEvents notifications) { ServerPlugin[] plugins; if (!this.host.SonarQubeService.TryGetPlugins(connection, cancellationToken, out plugins)) { notifications.ProgressChanged(cancellationToken.IsCancellationRequested ? Strings.ConnectionResultCancellation : Strings.ConnectionResultFailure, double.NaN); this.host.ActiveSection?.UserNotifications?.ShowNotificationError(Strings.ConnectionFailed, NotificationIds.FailedToConnectId, this.parentCommand); AbortWorkflow(controller, cancellationToken); return false; } var csPlugin = plugins.FirstOrDefault(x => StringComparer.Ordinal.Equals(x.Key, ServerPlugin.CSharpPluginKey)); if (string.IsNullOrWhiteSpace(csPlugin?.Version) || VersionHelper.Compare(csPlugin.Version, ServerPlugin.CSharpPluginMinimumVersion) < 0) { string errorMessage = string.Format(CultureInfo.CurrentCulture, Strings.ServerDoesNotHaveCorrectVersionOfCSharpPlugin, ServerPlugin.CSharpPluginMinimumVersion); this.host.ActiveSection?.UserNotifications?.ShowNotificationError(errorMessage, NotificationIds.BadServerPluginId, null); notifications.ProgressChanged(errorMessage, double.NaN); notifications.ProgressChanged(Strings.ConnectionResultFailure, double.NaN); AbortWorkflow(controller, cancellationToken); return false; } return true; }
/// <summary> /// Step operation that verifies that the controller is started but not finished /// </summary> /// <param name="token">Cancellation token</param> /// <param name="notifier">Progress notifier</param> private void VerifyControllerExecuting(CancellationToken token, IProgressStepExecutionEvents notifier) { AssertOperationArgumentsAreNotNull(token, notifier); Assert.IsTrue(this.testSubject.IsStarted, "Expected to be started"); Assert.IsFalse(this.testSubject.IsFinished, "Not expected to be finished"); }
private void ExecuteAndNotify(CancellationToken token, IProgressStepExecutionEvents notifier) { VerificationHelper.CheckState(this.testSubject, StepExecutionState.Executing); for (int i = 0; i < DeterminateLoops; i++) { notifier.ProgressChanged(i.ToString(), (double)(i + 1) / (double)DeterminateLoops); } }
/// <summary> /// Step operation that will abort and throw cancellation exception /// </summary> /// <param name="token">Cancellation token</param> /// <param name="notifier">Progress notifier</param> private void Cancel(CancellationToken token, IProgressStepExecutionEvents notifier) { AssertOperationArgumentsAreNotNull(token, notifier); Assert.IsTrue(this.testSubject.TryAbort(), "Expected to abort"); token.ThrowIfCancellationRequested(); }
private void ExecuteAndCancell(CancellationToken token, IProgressStepExecutionEvents notifier) { VerificationHelper.CheckState(this.testSubject, StepExecutionState.Executing); Assert.IsTrue(this.testController.IsCurrentStepCancellable, "Expected to be cancellable"); this.testController.Cancel(); token.ThrowIfCancellationRequested(); }
/// <summary> /// Step operation that will request to cancel in a cancellable step /// </summary> /// <remarks>Simulates an attempt to cancel during cancellable step execution</remarks> /// <param name="token">Cancellation token</param> /// <param name="notifier">Progress notifier</param> private void RequestToCancelAccepted(CancellationToken token, IProgressStepExecutionEvents notifier) { AssertOperationArgumentsAreNotNull(token, notifier); Assert.IsTrue(this.testSubject.CanAbort, "Should be able to abort"); Assert.IsTrue(this.testSubject.TryAbort(), "Aborting should succeed"); }
/// <summary> /// Executes the operation /// </summary> /// <param name="cancellationToken">Cancellation token</param> /// <param name="progressCallback">The callback instance to use when executing the operation</param> protected void ExecuteOperation(CancellationToken cancellationToken, IProgressStepExecutionEvents progressCallback) { this.definition.Operation(cancellationToken, progressCallback); }
/// <summary> /// Step operation that will request to cancel but the step is cannot be canceled /// </summary> /// <remarks>Simulates an attempt to cancel during non-cancellable step execution</remarks> /// <param name="token">Cancellation token</param> /// <param name="notifier">Progress notifier</param> private void RequestToCancelIgnored(CancellationToken token, IProgressStepExecutionEvents notifier) { AssertOperationArgumentsAreNotNull(token, notifier); Assert.IsFalse(this.testSubject.CanAbort, "Should not be able to abort"); Assert.IsFalse(this.testSubject.TryAbort(), "Aborting should fail"); }
internal /* for testing purposes */ void ConnectionStep(IProgressController controller, CancellationToken cancellationToken, ConnectionInformation connection, IProgressStepExecutionEvents notifications) { this.host.ActiveSection?.UserNotifications?.HideNotification(NotificationIds.FailedToConnectId); this.host.ActiveSection?.UserNotifications?.HideNotification(NotificationIds.BadServerPluginId); notifications.ProgressChanged(connection.ServerUri.ToString()); if (!this.VerifyDotNetPlugins(controller, cancellationToken, connection, notifications)) { return; } this.ConnectedServer = connection; ProjectInformation[] projects; if (!this.host.SonarQubeService.TryGetProjects(connection, cancellationToken, out projects)) { notifications.ProgressChanged(cancellationToken.IsCancellationRequested ? Strings.ConnectionResultCancellation : Strings.ConnectionResultFailure); this.host.ActiveSection?.UserNotifications?.ShowNotificationError(Strings.ConnectionFailed, NotificationIds.FailedToConnectId, this.parentCommand); AbortWorkflow(controller, cancellationToken); return; } this.OnProjectsChanged(connection, projects); notifications.ProgressChanged(Strings.ConnectionResultSuccess); }
private bool VerifyDotNetPlugins(IProgressController controller, CancellationToken cancellationToken, ConnectionInformation connection, IProgressStepExecutionEvents notifications) { notifications.ProgressChanged(Strings.DetectingServerPlugins); ServerPlugin[] plugins; if (!this.host.SonarQubeService.TryGetPlugins(connection, cancellationToken, out plugins)) { notifications.ProgressChanged(cancellationToken.IsCancellationRequested ? Strings.ConnectionResultCancellation : Strings.ConnectionResultFailure); this.host.ActiveSection?.UserNotifications?.ShowNotificationError(Strings.ConnectionFailed, NotificationIds.FailedToConnectId, this.parentCommand); AbortWorkflow(controller, cancellationToken); return false; } IsCSharpPluginSupported = VerifyPluginSupport(plugins, MinimumSupportedServerPlugin.CSharp); IsVBNetPluginSupported = VerifyPluginSupport(plugins, MinimumSupportedServerPlugin.VbNet); var projects = this.projectSystem.GetSolutionProjects().ToList(); var anyCSharpProject = projects.Any(p => MinimumSupportedServerPlugin.CSharp.ISupported(p)); var anyVbNetProject = projects.Any(p => MinimumSupportedServerPlugin.VbNet.ISupported(p)); string errorMessage; if ((IsCSharpPluginSupported && anyCSharpProject) || (IsVBNetPluginSupported && anyVbNetProject)) { return true; } else if (!IsCSharpPluginSupported && !IsVBNetPluginSupported) { errorMessage = Strings.ServerHasNoSupportedPluginVersion; } else if (projects.Count == 0) { errorMessage = Strings.SolutionContainsNoSupportedProject; } else if (IsCSharpPluginSupported && !anyCSharpProject) { errorMessage = string.Format(Strings.OnlySupportedPluginHasNoProjectInSolution, Language.CSharp.Name); } else { errorMessage = string.Format(Strings.OnlySupportedPluginHasNoProjectInSolution, Language.VBNET.Name); } this.host.ActiveSection?.UserNotifications?.ShowNotificationError(errorMessage, NotificationIds.BadServerPluginId, null); VsShellUtils.WriteToSonarLintOutputPane(this.host, Strings.SubTextPaddingFormat, errorMessage); notifications.ProgressChanged(Strings.ConnectionResultFailure); AbortWorkflow(controller, cancellationToken); return false; }
private static void AssertOperationArgumentsAreNotNull(CancellationToken token, IProgressStepExecutionEvents callback) { token.Should().NotBeNull("CancellationToken is expected not to be null"); callback.Should().NotBeNull("IProgressStepExecutionEvents is expected not to be null"); }