diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpCodeActions.cs b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpCodeActions.cs index fbaf16e52fe3dcefeaed0e8b6659dc9a6aff05f8..002d8d5ad3e1b8f7271c4b6f7e7cf7c3f20ce305 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpCodeActions.cs +++ b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpCodeActions.cs @@ -197,6 +197,12 @@ class C MarkupTestFile.GetSpans(markup, out var text, out ImmutableArray spans); SetUpEditor(markup); VisualStudio.WaitForApplicationIdle(CancellationToken.None); + VisualStudio.Workspace.WaitForAllAsyncOperations( + Helper.HangMitigatingTimeout, + FeatureAttribute.Workspace, + FeatureAttribute.SolutionCrawler, + FeatureAttribute.DiagnosticService, + FeatureAttribute.ErrorSquiggles); VisualStudio.Editor.Verify.CodeActionsNotShowing(); var editorConfig = @"root = true @@ -205,7 +211,15 @@ class C csharp_style_expression_bodied_properties = true:warning "; - VisualStudio.SolutionExplorer.AddFile(new ProjectUtils.Project(ProjectName), ".editorconfig", editorConfig, open: false); + VisualStudio.SolutionExplorer.BeginWatchForCodingConventionsChange(new ProjectUtils.Project(ProjectName), "Class1.cs"); + try + { + VisualStudio.SolutionExplorer.AddFile(new ProjectUtils.Project(ProjectName), ".editorconfig", editorConfig, open: false); + } + finally + { + VisualStudio.SolutionExplorer.EndWaitForCodingConventionsChange(Helper.HangMitigatingTimeout); + } // Wait for CodingConventions library events to propagate to the workspace VisualStudio.WaitForApplicationIdle(CancellationToken.None); @@ -213,7 +227,8 @@ class C Helper.HangMitigatingTimeout, FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawler, - FeatureAttribute.DiagnosticService); + FeatureAttribute.DiagnosticService, + FeatureAttribute.ErrorSquiggles); VisualStudio.Editor.InvokeCodeActionList(); VisualStudio.Editor.Verify.CodeAction( "Use expression body for properties", @@ -229,7 +244,15 @@ class C * outcome for the modified .editorconfig style. */ - VisualStudio.SolutionExplorer.SetFileContents(new ProjectUtils.Project(ProjectName), ".editorconfig", editorConfig.Replace("true:warning", "false:warning")); + VisualStudio.SolutionExplorer.BeginWatchForCodingConventionsChange(new ProjectUtils.Project(ProjectName), "Class1.cs"); + try + { + VisualStudio.SolutionExplorer.SetFileContents(new ProjectUtils.Project(ProjectName), ".editorconfig", editorConfig.Replace("true:warning", "false:warning")); + } + finally + { + VisualStudio.SolutionExplorer.EndWaitForCodingConventionsChange(Helper.HangMitigatingTimeout); + } // Wait for CodingConventions library events to propagate to the workspace VisualStudio.WaitForApplicationIdle(CancellationToken.None); @@ -237,7 +260,8 @@ class C Helper.HangMitigatingTimeout, FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawler, - FeatureAttribute.DiagnosticService); + FeatureAttribute.DiagnosticService, + FeatureAttribute.ErrorSquiggles); VisualStudio.Editor.InvokeCodeActionList(); VisualStudio.Editor.Verify.CodeAction( "Use block body for properties", diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpFormatting.cs b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpFormatting.cs index 6c7f1779aa86b4cab8bd92854bb78cffe53c317b..b8b4e7e710c29fd13bd8154f0a83b70e6230726b 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpFormatting.cs +++ b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpFormatting.cs @@ -317,7 +317,8 @@ public int X1 Helper.HangMitigatingTimeout, FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawler, - FeatureAttribute.DiagnosticService); + FeatureAttribute.DiagnosticService, + FeatureAttribute.ErrorSquiggles); VisualStudio.Editor.FormatDocumentViaCommand(); Assert.Equal(expectedTextFourSpaceIndent, VisualStudio.Editor.GetText()); @@ -333,7 +334,15 @@ public int X1 indent_size = 2 "; - VisualStudio.SolutionExplorer.AddFile(new ProjectUtils.Project(ProjectName), ".editorconfig", editorConfig, open: false); + VisualStudio.SolutionExplorer.BeginWatchForCodingConventionsChange(new ProjectUtils.Project(ProjectName), "Class1.cs"); + try + { + VisualStudio.SolutionExplorer.AddFile(new ProjectUtils.Project(ProjectName), ".editorconfig", editorConfig, open: false); + } + finally + { + VisualStudio.SolutionExplorer.EndWaitForCodingConventionsChange(Helper.HangMitigatingTimeout); + } // Wait for CodingConventions library events to propagate to the workspace VisualStudio.WaitForApplicationIdle(CancellationToken.None); @@ -341,7 +350,8 @@ public int X1 Helper.HangMitigatingTimeout, FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawler, - FeatureAttribute.DiagnosticService); + FeatureAttribute.DiagnosticService, + FeatureAttribute.ErrorSquiggles); VisualStudio.Editor.FormatDocumentViaCommand(); Assert.Equal(expectedTextTwoSpaceIndent, VisualStudio.Editor.GetText()); @@ -351,7 +361,15 @@ public int X1 * and verifies that the next Format Document operation adheres to the updated formatting. */ - VisualStudio.SolutionExplorer.SetFileContents(new ProjectUtils.Project(ProjectName), ".editorconfig", editorConfig.Replace("2", "4")); + VisualStudio.SolutionExplorer.BeginWatchForCodingConventionsChange(new ProjectUtils.Project(ProjectName), "Class1.cs"); + try + { + VisualStudio.SolutionExplorer.SetFileContents(new ProjectUtils.Project(ProjectName), ".editorconfig", editorConfig.Replace("2", "4")); + } + finally + { + VisualStudio.SolutionExplorer.EndWaitForCodingConventionsChange(Helper.HangMitigatingTimeout); + } // Wait for CodingConventions library events to propagate to the workspace VisualStudio.WaitForApplicationIdle(CancellationToken.None); @@ -359,7 +377,8 @@ public int X1 Helper.HangMitigatingTimeout, FeatureAttribute.Workspace, FeatureAttribute.SolutionCrawler, - FeatureAttribute.DiagnosticService); + FeatureAttribute.DiagnosticService, + FeatureAttribute.ErrorSquiggles); VisualStudio.Editor.FormatDocumentViaCommand(); Assert.Equal(expectedTextFourSpaceIndent, VisualStudio.Editor.GetText()); diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/SolutionExplorer_InProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/SolutionExplorer_InProc.cs index 3c16e967a7bfb2b407e250e700ff8e12051b9928..92014bb050ad85538b89c65422b99995a7ddb337 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/SolutionExplorer_InProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/SolutionExplorer_InProc.cs @@ -7,11 +7,13 @@ using System.Linq; using System.Runtime.InteropServices; using System.Threading; +using System.Threading.Tasks; using System.Xml.Linq; using EnvDTE80; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.EditAndContinue; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.VisualStudio.CodingConventions; using Microsoft.VisualStudio.IntegrationTest.Utilities.Input; using Microsoft.VisualStudio.ProjectSystem.Properties; using Microsoft.VisualStudio.Shell; @@ -21,6 +23,7 @@ using NuGet.SolutionRestoreManager; using Roslyn.Hosting.Diagnostics.Waiters; using VSLangProj; +using Task = System.Threading.Tasks.Task; namespace Microsoft.VisualStudio.IntegrationTest.Utilities.InProcess { @@ -1113,5 +1116,43 @@ public string[] GetChildrenOfItemAtPath(params string[] path) return null; } + + private CodingConventionsChangedWatcher _codingConventionsChangedWatcher; + + public void BeginWatchForCodingConventionsChange(string projectName, string relativeFilePath) + { + var filePath = GetAbsolutePathForProjectRelativeFilePath(projectName, relativeFilePath); + _codingConventionsChangedWatcher = new CodingConventionsChangedWatcher(filePath); + } + + public void EndWaitForCodingConventionsChange(TimeSpan timeout) + { + var watcher = Interlocked.Exchange(ref _codingConventionsChangedWatcher, null); + if (watcher is null) + { + throw new InvalidOperationException(); + } + + watcher.Changed.Wait(timeout); + } + + private class CodingConventionsChangedWatcher + { + private readonly TaskCompletionSource _taskCompletionSource = new TaskCompletionSource(); + private readonly ICodingConventionContext _codingConventionContext; + + public CodingConventionsChangedWatcher(string filePath) + { + var codingConventionsManager = GetComponentModelService(); + _codingConventionContext = codingConventionsManager.GetConventionContextAsync(filePath, CancellationToken.None).Result; + _codingConventionContext.CodingConventionsChangedAsync += (sender, e) => + { + _taskCompletionSource.SetResult(null); + return Task.CompletedTask; + }; + } + + public Task Changed => _taskCompletionSource.Task; + } } } diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/Microsoft.VisualStudio.IntegrationTest.Utilities.csproj b/src/VisualStudio/IntegrationTest/TestUtilities/Microsoft.VisualStudio.IntegrationTest.Utilities.csproj index f1fe7b27279ee5452816dc6314cfbf3f14ccfcdf..c0246e2362573a01f127017e3666c7c3aba5f3fd 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/Microsoft.VisualStudio.IntegrationTest.Utilities.csproj +++ b/src/VisualStudio/IntegrationTest/TestUtilities/Microsoft.VisualStudio.IntegrationTest.Utilities.csproj @@ -39,6 +39,7 @@ + diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/SolutionExplorer_OutOfProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/SolutionExplorer_OutOfProc.cs index 95f0ac2c12d86ea48343ef5c1fba454873333846..f5d7eaf228b65be46a5ff816193fbe43ed52378a 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/SolutionExplorer_OutOfProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/SolutionExplorer_OutOfProc.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Xml.Linq; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.IntegrationTest.Utilities.InProcess; @@ -199,5 +200,11 @@ public void EditProjectFile(ProjectUtils.Project project) public void AddStandaloneFile(string fileName) => _inProc.AddStandaloneFile(fileName); + + public void BeginWatchForCodingConventionsChange(ProjectUtils.Project project, string fileName) + => _inProc.BeginWatchForCodingConventionsChange(project.Name, fileName); + + public void EndWaitForCodingConventionsChange(TimeSpan timeout) + => _inProc.EndWaitForCodingConventionsChange(timeout); } }