From 203c33ec06fed9f791095705648a0625023b8c9f Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Thu, 10 Feb 2022 16:22:21 -0800 Subject: [PATCH 1/7] Port AbstractUpdateProjectTest and derived to the new integration test project Closes #44301 --- .../CSharpUpdateProjectToAllowUnsafe.cs | 111 ----------------- .../AbstractUpgradeProjectTest.cs} | 22 ++-- .../CSharpUpdateProjectToAllowUnsafe.cs | 114 ++++++++++++++++++ .../CSharp/CSharpUpgradeProject.cs | 95 ++++++++------- .../InProcess/SolutionExplorerInProcess.cs | 43 ++++++- .../InProcess/SolutionExplorer_InProc.cs | 12 -- .../SolutionExplorer_OutOfProc.cs | 6 - 7 files changed, 210 insertions(+), 193 deletions(-) delete mode 100644 src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpUpdateProjectToAllowUnsafe.cs rename src/VisualStudio/IntegrationTest/{IntegrationTests/AbstractUpdateProjectTest.cs => New.IntegrationTests/AbstractUpgradeProjectTest.cs} (67%) create mode 100644 src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpUpdateProjectToAllowUnsafe.cs rename src/VisualStudio/IntegrationTest/{IntegrationTests => New.IntegrationTests}/CSharp/CSharpUpgradeProject.cs (51%) diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpUpdateProjectToAllowUnsafe.cs b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpUpdateProjectToAllowUnsafe.cs deleted file mode 100644 index 7856319c5d6f9..0000000000000 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpUpdateProjectToAllowUnsafe.cs +++ /dev/null @@ -1,111 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#nullable disable - -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Test.Utilities; -using Microsoft.VisualStudio.IntegrationTest.Utilities; -using Roslyn.Test.Utilities; -using Xunit; -using Xunit.Abstractions; -using ProjectUtils = Microsoft.VisualStudio.IntegrationTest.Utilities.Common.ProjectUtils; - -namespace Roslyn.VisualStudio.IntegrationTests.CSharp -{ - [Collection(nameof(SharedIntegrationHostFixture))] - public class CSharpUpdateProjectToAllowUnsafe : AbstractUpdateProjectTest - { - public CSharpUpdateProjectToAllowUnsafe(VisualStudioInstanceFactory instanceFactory) - : base(instanceFactory) - { - } - - private void InvokeFix() - { - VisualStudio.Editor.SetText(@" -unsafe class C -{ -}"); - VisualStudio.Editor.Activate(); - - VisualStudio.Editor.PlaceCaret("C"); - VisualStudio.Editor.InvokeCodeActionList(); - VisualStudio.Editor.Verify.CodeAction("Allow unsafe code in this project", applyFix: true); - } - - [WpfFact(Skip = "https://github.com/dotnet/roslyn/issues/44301"), Trait(Traits.Feature, Traits.Features.CodeActionsUpdateProjectToAllowUnsafe)] - public void CPSProject_GeneralPropertyGroupUpdated() - { - var project = new ProjectUtils.Project(ProjectName); - - VisualStudio.SolutionExplorer.CreateSolution(SolutionName); - VisualStudio.SolutionExplorer.AddProject(project, WellKnownProjectTemplates.CSharpNetStandardClassLibrary, LanguageNames.CSharp); - VisualStudio.SolutionExplorer.RestoreNuGetPackages(project); - - InvokeFix(); - VerifyPropertyOutsideConfiguration(GetProjectFileElement(project), "AllowUnsafeBlocks", "true"); - } - - [WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsUpdateProjectToAllowUnsafe)] - public void LegacyProject_AllConfigurationsUpdated() - { - var project = new ProjectUtils.Project(ProjectName); - - VisualStudio.SolutionExplorer.CreateSolution(SolutionName); - VisualStudio.SolutionExplorer.AddProject(project, WellKnownProjectTemplates.ClassLibrary, LanguageNames.CSharp); - - InvokeFix(); - VerifyPropertyInEachConfiguration(GetProjectFileElement(project), "AllowUnsafeBlocks", "true"); - } - - [WorkItem(23342, "https://github.com/dotnet/roslyn/issues/23342")] - [WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsUpdateProjectToAllowUnsafe)] - public void LegacyProject_MultiplePlatforms_AllConfigurationsUpdated() - { - var project = new ProjectUtils.Project(ProjectName); - - VisualStudio.SolutionExplorer.CreateSolution(SolutionName); - VisualStudio.SolutionExplorer.AddCustomProject(project, ".csproj", $@" - - - - Debug - x64 - {{F4233BA4-A4CB-498B-BBC1-65A42206B1BA}} - Library - {ProjectName} - {ProjectName} - v4.6 - - - bin\x86\Debug\ - x86 - - - bin\x86\Release\ - x86 - true - - - bin\x64\Debug\ - x64 - false - - - bin\x64\Release\ - x64 - - - - -"); - - VisualStudio.SolutionExplorer.AddFile(project, "C.cs", open: true); - - InvokeFix(); - VerifyPropertyInEachConfiguration(GetProjectFileElement(project), "AllowUnsafeBlocks", "true"); - } - } -} diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/AbstractUpdateProjectTest.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/AbstractUpgradeProjectTest.cs similarity index 67% rename from src/VisualStudio/IntegrationTest/IntegrationTests/AbstractUpdateProjectTest.cs rename to src/VisualStudio/IntegrationTest/New.IntegrationTests/AbstractUpgradeProjectTest.cs index d91c9891241ae..abd8a1dcc6177 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/AbstractUpdateProjectTest.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/AbstractUpgradeProjectTest.cs @@ -2,31 +2,23 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.IO; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using System.Xml.Linq; -using Microsoft.VisualStudio.IntegrationTest.Utilities; using Xunit; -using Xunit.Abstractions; -using ProjectUtils = Microsoft.VisualStudio.IntegrationTest.Utilities.Common.ProjectUtils; namespace Roslyn.VisualStudio.IntegrationTests { - public abstract class AbstractUpdateProjectTest : AbstractIntegrationTest + public abstract class AbstractUpgradeProjectTest : AbstractIntegrationTest { - protected AbstractUpdateProjectTest(VisualStudioInstanceFactory instanceFactory) - : base(instanceFactory) - { - } - - protected XElement GetProjectFileElement(ProjectUtils.Project project) + protected async Task GetProjectFileElementAsync(string projectName, CancellationToken cancellationToken) { // Save the project file. - VisualStudio.SolutionExplorer.SaveAll(); + await TestServices.SolutionExplorer.SaveAllAsync(cancellationToken); - var projectFileContent = VisualStudio.SolutionExplorer.GetFileContents(project, Path.GetFileName(project.RelativePath)); + var projectFileContent = await TestServices.SolutionExplorer.GetFileContentsAsync(projectName, $"{ProjectName}.csproj", cancellationToken); return XElement.Parse(projectFileContent); } @@ -50,7 +42,7 @@ static bool IsConditionalPropertyGroup(XElement element) => element.Name.LocalName == "PropertyGroup" && element.Attributes().Any(a => a.Name.LocalName == "Condition"); } - private static string GetPropertyValue(XElement propertyGroup, string propertyName) + private static string? GetPropertyValue(XElement propertyGroup, string propertyName) => propertyGroup.Elements().SingleOrDefault(e => e.Name.LocalName == propertyName)?.Value; } } diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpUpdateProjectToAllowUnsafe.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpUpdateProjectToAllowUnsafe.cs new file mode 100644 index 0000000000000..c297804d640d5 --- /dev/null +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpUpdateProjectToAllowUnsafe.cs @@ -0,0 +1,114 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.VisualStudio.IntegrationTest.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Roslyn.VisualStudio.IntegrationTests.CSharp +{ + [Trait(Traits.Feature, Traits.Features.CodeActionsUpdateProjectToAllowUnsafe)] + public class CSharpUpdateProjectToAllowUnsafe : AbstractUpgradeProjectTest + { + private async Task InvokeFixAsync(CancellationToken cancellationToken) + { + await TestServices.Editor.SetTextAsync(@" +unsafe class C +{ +}", cancellationToken); + await TestServices.Editor.ActivateAsync(cancellationToken); + + await TestServices.Editor.PlaceCaretAsync("C", charsOffset: 0, cancellationToken); + + // Suspend file change notification during code action application, since spurious file change notifications + // can cause silent failure to apply the code action if they occur within this block. + await using (var fileChangeRestorer = await TestServices.Shell.PauseFileChangesAsync(HangMitigatingCancellationToken)) + { + await TestServices.Editor.InvokeCodeActionListAsync(cancellationToken); + await TestServices.EditorVerifier.CodeActionAsync("Allow unsafe code in this project", applyFix: true, cancellationToken: cancellationToken); + } + } + + [IdeFact] + public async Task CPSProject_GeneralPropertyGroupUpdated() + { + var project = ProjectName; + + await TestServices.SolutionExplorer.CreateSolutionAsync(SolutionName, HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.AddProjectAsync(project, WellKnownProjectTemplates.CSharpNetStandardClassLibrary, LanguageNames.CSharp, HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.RestoreNuGetPackagesAsync(project, HangMitigatingCancellationToken); + + await InvokeFixAsync(HangMitigatingCancellationToken); + VerifyPropertyOutsideConfiguration(await GetProjectFileElementAsync(project, HangMitigatingCancellationToken), "AllowUnsafeBlocks", "true"); + } + + [IdeFact] + public async Task LegacyProject_AllConfigurationsUpdated() + { + var project = ProjectName; + + await TestServices.SolutionExplorer.CreateSolutionAsync(SolutionName, HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.AddProjectAsync(project, WellKnownProjectTemplates.ClassLibrary, LanguageNames.CSharp, HangMitigatingCancellationToken); + + await InvokeFixAsync(HangMitigatingCancellationToken); + VerifyPropertyInEachConfiguration(await GetProjectFileElementAsync(project, HangMitigatingCancellationToken), "AllowUnsafeBlocks", "true"); + } + + [IdeFact] + [WorkItem(23342, "https://github.com/dotnet/roslyn/issues/23342")] + public async Task LegacyProject_MultiplePlatforms_AllConfigurationsUpdated() + { + var project = ProjectName; + + await TestServices.SolutionExplorer.CreateSolutionAsync(SolutionName, HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.AddCustomProjectAsync( + project, + ".csproj", + $@" + + + + Debug + x64 + {{F4233BA4-A4CB-498B-BBC1-65A42206B1BA}} + Library + {ProjectName} + {ProjectName} + v4.6 + + + bin\x86\Debug\ + x86 + + + bin\x86\Release\ + x86 + true + + + bin\x64\Debug\ + x64 + false + + + bin\x64\Release\ + x64 + + + + +", + HangMitigatingCancellationToken); + + await TestServices.SolutionExplorer.AddFileAsync(project, "C.cs", open: true, cancellationToken: HangMitigatingCancellationToken); + + await InvokeFixAsync(HangMitigatingCancellationToken); + VerifyPropertyInEachConfiguration(await GetProjectFileElementAsync(project, HangMitigatingCancellationToken), "AllowUnsafeBlocks", "true"); + } + } +} diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpUpgradeProject.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpUpgradeProject.cs similarity index 51% rename from src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpUpgradeProject.cs rename to src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpUpgradeProject.cs index c604b8acee107..0f336ef86fa7e 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpUpgradeProject.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpUpgradeProject.cs @@ -2,58 +2,60 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - +using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.VisualStudio.IntegrationTest.Utilities; using Roslyn.Test.Utilities; using Xunit; -using Xunit.Abstractions; -using ProjectUtils = Microsoft.VisualStudio.IntegrationTest.Utilities.Common.ProjectUtils; namespace Roslyn.VisualStudio.IntegrationTests.CSharp { - [Collection(nameof(SharedIntegrationHostFixture))] - public class CSharpUpgradeProject : AbstractUpdateProjectTest + [Trait(Traits.Feature, Traits.Features.CodeActionsUpgradeProject)] + public class CSharpUpgradeProject : AbstractUpgradeProjectTest { - public CSharpUpgradeProject(VisualStudioInstanceFactory instanceFactory) - : base(instanceFactory) + private async Task InvokeFixAsync(string version, CancellationToken cancellationToken) { - } - - private void InvokeFix(string version = "latest") - { - VisualStudio.Editor.SetText(@$" + await TestServices.Editor.SetTextAsync(@$" #error version:{version} -"); - VisualStudio.Editor.Activate(); +", cancellationToken); + await TestServices.Editor.ActivateAsync(cancellationToken); + + await TestServices.Editor.PlaceCaretAsync($"version:{version}", charsOffset: 0, cancellationToken); - VisualStudio.Editor.PlaceCaret($"version:{version}"); - VisualStudio.Editor.InvokeCodeActionList(); - VisualStudio.Editor.Verify.CodeAction($"Upgrade this project to C# language version '{version}'", applyFix: true); + // Suspend file change notification during code action application, since spurious file change notifications + // can cause silent failure to apply the code action if they occur within this block. + await using (var fileChangeRestorer = await TestServices.Shell.PauseFileChangesAsync(HangMitigatingCancellationToken)) + { + await TestServices.Editor.InvokeCodeActionListAsync(cancellationToken); + await TestServices.EditorVerifier.CodeActionAsync($"Upgrade this project to C# language version '{version}'", applyFix: true, cancellationToken: cancellationToken); + } } - [WpfFact(Skip = "https://github.com/dotnet/roslyn/issues/38301"), Trait(Traits.Feature, Traits.Features.CodeActionsUpgradeProject)] - public void CPSProject_GeneralPropertyGroupUpdated() + [IdeFact(Skip = "https://github.com/dotnet/roslyn/issues/38301")] + public async Task CPSProject_GeneralPropertyGroupUpdated() { - var project = new ProjectUtils.Project(ProjectName); + var project = ProjectName; - VisualStudio.SolutionExplorer.CreateSolution(SolutionName); - VisualStudio.SolutionExplorer.AddProject(project, WellKnownProjectTemplates.CSharpNetStandardClassLibrary, LanguageNames.CSharp); - VisualStudio.SolutionExplorer.RestoreNuGetPackages(project); + await TestServices.SolutionExplorer.CreateSolutionAsync(SolutionName, HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.AddProjectAsync(project, WellKnownProjectTemplates.CSharpNetStandardClassLibrary, LanguageNames.CSharp, HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.RestoreNuGetPackagesAsync(project, HangMitigatingCancellationToken); - InvokeFix(); - VerifyPropertyOutsideConfiguration(GetProjectFileElement(project), "LangVersion", "latest"); + await InvokeFixAsync(version: "latest", HangMitigatingCancellationToken); + VerifyPropertyOutsideConfiguration(await GetProjectFileElementAsync(project, HangMitigatingCancellationToken), "LangVersion", "latest"); } - [WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsUpgradeProject)] - public void LegacyProject_AllConfigurationsUpdated() + [IdeFact] + public async Task LegacyProject_AllConfigurationsUpdated() { - var project = new ProjectUtils.Project(ProjectName); + var project = ProjectName; - VisualStudio.SolutionExplorer.CreateSolution(SolutionName); - VisualStudio.SolutionExplorer.AddCustomProject(project, ".csproj", $@" + await TestServices.SolutionExplorer.CreateSolutionAsync(SolutionName, HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.AddCustomProjectAsync( + project, + ".csproj", + $@" @@ -85,21 +87,25 @@ public void LegacyProject_AllConfigurationsUpdated() -"); - VisualStudio.SolutionExplorer.AddFile(project, "C.cs", open: true); +", + HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.AddFileAsync(project, "C.cs", open: true, cancellationToken: HangMitigatingCancellationToken); - InvokeFix(version: "7.3"); - VerifyPropertyInEachConfiguration(GetProjectFileElement(project), "LangVersion", "7.3"); + await InvokeFixAsync(version: "7.3", HangMitigatingCancellationToken); + VerifyPropertyInEachConfiguration(await GetProjectFileElementAsync(project, HangMitigatingCancellationToken), "LangVersion", "7.3"); } + [IdeFact] [WorkItem(23342, "https://github.com/dotnet/roslyn/issues/23342")] - [WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsUpgradeProject)] - public void LegacyProject_MultiplePlatforms_AllConfigurationsUpdated() + public async Task LegacyProject_MultiplePlatforms_AllConfigurationsUpdated() { - var project = new ProjectUtils.Project(ProjectName); + var project = ProjectName; - VisualStudio.SolutionExplorer.CreateSolution(SolutionName); - VisualStudio.SolutionExplorer.AddCustomProject(project, ".csproj", $@" + await TestServices.SolutionExplorer.CreateSolutionAsync(SolutionName, HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.AddCustomProjectAsync( + project, + ".csproj", + $@" @@ -134,12 +140,13 @@ public void LegacyProject_MultiplePlatforms_AllConfigurationsUpdated() -"); +", + HangMitigatingCancellationToken); - VisualStudio.SolutionExplorer.AddFile(project, "C.cs", open: true); + await TestServices.SolutionExplorer.AddFileAsync(project, "C.cs", open: true, cancellationToken: HangMitigatingCancellationToken); - InvokeFix(version: "7.3"); - VerifyPropertyInEachConfiguration(GetProjectFileElement(project), "LangVersion", "7.3"); + await InvokeFixAsync(version: "7.3", HangMitigatingCancellationToken); + VerifyPropertyInEachConfiguration(await GetProjectFileElementAsync(project, HangMitigatingCancellationToken), "LangVersion", "7.3"); } } } diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/SolutionExplorerInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/SolutionExplorerInProcess.cs index 38aaff963ecbf..dee04f7fdc0d3 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/SolutionExplorerInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/SolutionExplorerInProcess.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis; using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.IntegrationTest.Utilities; +using Microsoft.VisualStudio.OLE.Interop; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Text; @@ -151,6 +152,20 @@ public async Task AddProjectAsync(string projectName, string projectTemplate, st ErrorHandler.ThrowOnFailure(solution.AddNewProjectFromTemplate(projectTemplatePath, null, null, projectPath, projectName, null, out _)); } + public async Task AddCustomProjectAsync(string projectName, string projectFileExtension, string projectFileContent, CancellationToken cancellationToken) + { + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + var projectPath = Path.Combine(await GetDirectoryNameAsync(cancellationToken), projectName); + Directory.CreateDirectory(projectPath); + + var projectFilePath = Path.Combine(projectPath, projectName + projectFileExtension); + File.WriteAllText(projectFilePath, projectFileContent); + + var solution = await GetRequiredGlobalServiceAsync(cancellationToken); + ErrorHandler.ThrowOnFailure(solution.AddExistingProject(projectFilePath, pParent: null, out _)); + } + public async Task RestoreNuGetPackagesAsync(CancellationToken cancellationToken) { await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); @@ -189,6 +204,14 @@ await Helper.RetryAsync( cancellationToken); } + public async Task SaveAllAsync(CancellationToken cancellationToken) + { + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + var dispatcher = await GetRequiredGlobalServiceAsync(cancellationToken); + ErrorHandler.ThrowOnFailure(dispatcher.Exec(typeof(VSConstants.VSStd97CmdID).GUID, (uint)VSConstants.VSStd97CmdID.SaveSolution, (uint)OLECMDEXECOPT.OLECMDEXECOPT_DODEFAULT, IntPtr.Zero, IntPtr.Zero)); + } + public async Task OpenFileAsync(string projectName, string relativeFilePath, CancellationToken cancellationToken) { await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); @@ -340,6 +363,19 @@ public async Task GetFileContentsAsync(string projectName, string relati return File.ReadAllText(filePath); } + public async Task<(string solutionDirectory, string solutionFileFullPath, string userOptionsFile)> GetSolutionInfoAsync(CancellationToken cancellationToken) + { + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + if (!await IsSolutionOpenAsync(cancellationToken)) + throw new InvalidOperationException("No solution is open."); + + var solution = await GetRequiredGlobalServiceAsync(cancellationToken); + ErrorHandler.ThrowOnFailure(solution.GetSolutionInfo(out var solutionDirectory, out var solutionFileFullPath, out var userOptionsFile)); + + return (solutionDirectory, solutionFileFullPath, userOptionsFile); + } + private static string ConvertLanguageName(string languageName) { return languageName switch @@ -365,16 +401,13 @@ private async Task GetAbsolutePathForProjectRelativeFilePathAsync(string private async Task GetDirectoryNameAsync(CancellationToken cancellationToken) { - await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - - var solution = await GetRequiredGlobalServiceAsync(cancellationToken); - ErrorHandler.ThrowOnFailure(solution.GetSolutionInfo(out _, out var solutionFileFullPath, out _)); + var (solutionDirectory, solutionFileFullPath, _) = await GetSolutionInfoAsync(cancellationToken); if (string.IsNullOrEmpty(solutionFileFullPath)) { throw new InvalidOperationException(); } - return Path.GetDirectoryName(solutionFileFullPath); + return solutionDirectory; } private async Task GetProjectTemplatePathAsync(string projectTemplate, string languageName, CancellationToken cancellationToken) diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/SolutionExplorer_InProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/SolutionExplorer_InProc.cs index 55cd499e9725d..8c62995cbdbe1 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/SolutionExplorer_InProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/SolutionExplorer_InProc.cs @@ -332,18 +332,6 @@ public void AddProject(string projectName, string projectTemplate, string langua _solution.AddFromTemplate(projectTemplatePath, projectPath, projectName, Exclusive: false); } - public void AddCustomProject(string projectName, string projectFileExtension, string projectFileContent) - { - var projectPath = Path.Combine(DirectoryName, projectName); - Directory.CreateDirectory(projectPath); - - var projectFilePath = Path.Combine(projectPath, projectName + projectFileExtension); - File.WriteAllText(projectFilePath, projectFileContent); - - Contract.ThrowIfNull(_solution); - _solution.AddFromFile(projectFilePath); - } - // TODO: Adjust language name based on whether we are using a web template private string GetProjectTemplatePath(string projectTemplate, string languageName) { diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/SolutionExplorer_OutOfProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/SolutionExplorer_OutOfProc.cs index 7ed4ac8ef86fc..fb16e4f4fd34f 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/SolutionExplorer_OutOfProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/SolutionExplorer_OutOfProc.cs @@ -47,12 +47,6 @@ public void AddProject(ProjectUtils.Project projectName, string projectTemplate, _instance.Workspace.WaitForAsyncOperations(Helper.HangMitigatingTimeout, FeatureAttribute.Workspace); } - public void AddCustomProject(ProjectUtils.Project projectName, string projectFileExtension, string projectFileContent) - { - _inProc.AddCustomProject(projectName.Name, projectFileExtension, projectFileContent); - _instance.Workspace.WaitForAsyncOperations(Helper.HangMitigatingTimeout, FeatureAttribute.Workspace); - } - public void AddProjectReference(ProjectUtils.Project fromProjectName, ProjectUtils.ProjectReference toProjectName) { _inProc.AddProjectReference(fromProjectName.Name, toProjectName.Name); From 8950d604b3f67c1cb479740b40e591083cde31c5 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Thu, 10 Feb 2022 23:50:23 -0800 Subject: [PATCH 2/7] Port CSharpGoToBase to the new integration test project --- .../IntegrationTests/CSharp/CSharpGoToBase.cs | 51 ------------------- .../CSharp/CSharpGoToBase.cs | 44 ++++++++++++++++ .../InProcess/EditorInProcess.cs | 13 +++++ .../TestUtilities/InProcess/Editor_InProc.cs | 3 -- .../OutOfProcess/Editor_OutOfProc.cs | 8 --- 5 files changed, 57 insertions(+), 62 deletions(-) delete mode 100644 src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpGoToBase.cs create mode 100644 src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpGoToBase.cs diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpGoToBase.cs b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpGoToBase.cs deleted file mode 100644 index dd22ee5c6dc46..0000000000000 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpGoToBase.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#nullable disable - -using System; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Test.Utilities; -using Microsoft.VisualStudio.IntegrationTest.Utilities; -using Microsoft.VisualStudio.Shell; -using Roslyn.Test.Utilities; -using Xunit; -using Xunit.Abstractions; -using ProjectUtils = Microsoft.VisualStudio.IntegrationTest.Utilities.Common.ProjectUtils; - -namespace Roslyn.VisualStudio.IntegrationTests.CSharp -{ - [Collection(nameof(SharedIntegrationHostFixture))] - public class CSharpGoToBase : AbstractEditorTest - { - protected override string LanguageName => LanguageNames.CSharp; - - public CSharpGoToBase(VisualStudioInstanceFactory instanceFactory) - : base(instanceFactory, nameof(CSharpGoToBase)) - { - } - - [WpfFact, Trait(Traits.Feature, Traits.Features.GoToBase)] - public void GoToBaseFromMetadataAsSource() - { - var project = new ProjectUtils.Project(ProjectName); - VisualStudio.SolutionExplorer.AddFile(project, "C.cs"); - VisualStudio.SolutionExplorer.OpenFile(project, "C.cs"); - VisualStudio.Editor.SetText( -@"using System; - -class C -{ - public override string ToString() - { - return ""C""; - } -}"); - VisualStudio.Editor.PlaceCaret("ToString", charsOffset: -1); - VisualStudio.Editor.GoToBase("Object [from metadata]"); - VisualStudio.Editor.Verify.TextContains(@"public virtual string ToString$$()", assertCaretPosition: true); - } - } -} diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpGoToBase.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpGoToBase.cs new file mode 100644 index 0000000000000..207ee11393f96 --- /dev/null +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpGoToBase.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; + +namespace Roslyn.VisualStudio.IntegrationTests.CSharp +{ + [Trait(Traits.Feature, Traits.Features.GoToBase)] + public class CSharpGoToBase : AbstractEditorTest + { + protected override string LanguageName => LanguageNames.CSharp; + + public CSharpGoToBase() + : base(nameof(CSharpGoToBase)) + { + } + + [IdeFact] + public async Task GoToBaseFromMetadataAsSource() + { + await TestServices.SolutionExplorer.AddFileAsync(ProjectName, "C.cs", cancellationToken: HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.OpenFileAsync(ProjectName, "C.cs", HangMitigatingCancellationToken); + await TestServices.Editor.SetTextAsync( +@"using System; + +class C +{ + public override string ToString() + { + return ""C""; + } +}", HangMitigatingCancellationToken); + await TestServices.Editor.PlaceCaretAsync("ToString", charsOffset: -1, HangMitigatingCancellationToken); + await TestServices.Editor.GoToBaseAsync(HangMitigatingCancellationToken); + Assert.Equal("Object [from metadata]", await TestServices.Shell.GetActiveWindowCaptionAsync(HangMitigatingCancellationToken)); + + await TestServices.EditorVerifier.TextContainsAsync(@"public virtual string ToString$$()", assertCaretPosition: true); + } + } +} diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs index 92ece3a575b8d..632870b89dab2 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs @@ -846,6 +846,19 @@ public async Task GetCaretPositionAsync(CancellationToken cancellationToken return bufferPosition.Position; } + public async Task GoToBaseAsync(CancellationToken cancellationToken) + { + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + var dispatcher = await GetRequiredGlobalServiceAsync(cancellationToken); + ErrorHandler.ThrowOnFailure(dispatcher.Exec(EditorConstants.EditorCommandSet, (uint)EditorConstants.EditorCommandID.GoToBase, (uint)OLECMDEXECOPT.OLECMDEXECOPT_DODEFAULT, IntPtr.Zero, IntPtr.Zero)); + + await TestServices.Workspace.WaitForAsyncOperationsAsync(FeatureAttribute.Workspace, cancellationToken); + + await TestServices.Workspace.WaitForAsyncOperationsAsync(FeatureAttribute.GoToBase, cancellationToken); + await TestServices.Editor.WaitForEditorOperationsAsync(cancellationToken); + } + private async Task WaitForCompletionSetAsync(CancellationToken cancellationToken) { await TestServices.Workspace.WaitForAsyncOperationsAsync(FeatureAttribute.CompletionSet, cancellationToken); diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs index 4cfe91ffe213b..22c4a7376e484 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs @@ -775,9 +775,6 @@ public void GoToDefinition() public void GoToImplementation() => ExecuteCommand(WellKnownCommandNames.Edit_GoToImplementation); - public void GoToBase() - => ExecuteCommand(WellKnownCommandNames.Edit_GoToBase); - /// /// Gets the spans where a particular tag appears in the active text view. /// diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs index e7df5ae363793..e491862fda2b8 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs @@ -345,14 +345,6 @@ public void GoToImplementation(string? expectedNavigateWindowName) } } - public void GoToBase(string expectedNavigateWindowName) - { - _instance.Workspace.WaitForAsyncOperations(Helper.HangMitigatingTimeout, FeatureAttribute.Workspace); - _editorInProc.GoToBase(); - _instance.Workspace.WaitForAsyncOperations(Helper.HangMitigatingTimeout, FeatureAttribute.GoToBase); - _editorInProc.WaitForActiveWindow(expectedNavigateWindowName); - } - public void SendExplicitFocus() => _editorInProc.SendExplicitFocus(); From fba2d342c385cb4946eebbe709f8fbbc4c3274dc Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Fri, 11 Feb 2022 08:43:11 -0800 Subject: [PATCH 3/7] Port CSharpF1Help to the new integration tests --- .../VisualBasic/BasicF1Help.cs | 60 ------------------- .../CSharp/CSharpF1Help.cs | 33 +++++----- .../InProcess/EditorInProcess.cs | 36 +++++++++++ .../VisualBasic/BasicF1Help.cs | 59 ++++++++++++++++++ .../TestUtilities/InProcess/Editor_InProc.cs | 33 ---------- .../OutOfProcess/Editor_OutOfProc.cs | 3 - 6 files changed, 110 insertions(+), 114 deletions(-) delete mode 100644 src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicF1Help.cs rename src/VisualStudio/IntegrationTest/{IntegrationTests => New.IntegrationTests}/CSharp/CSharpF1Help.cs (59%) create mode 100644 src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicF1Help.cs diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicF1Help.cs b/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicF1Help.cs deleted file mode 100644 index 95819182d9168..0000000000000 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicF1Help.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Test.Utilities; -using Microsoft.VisualStudio.IntegrationTest.Utilities; -using Roslyn.Test.Utilities; -using Xunit; -using Xunit.Abstractions; - -namespace Roslyn.VisualStudio.IntegrationTests.VisualBasic -{ - [Collection(nameof(SharedIntegrationHostFixture))] - public class BasicF1Help : AbstractEditorTest - { - protected override string LanguageName => LanguageNames.VisualBasic; - - public BasicF1Help(VisualStudioInstanceFactory instanceFactory) - : base(instanceFactory, nameof(BasicF1Help)) - { - } - - [WpfFact, Trait(Traits.Feature, Traits.Features.F1Help)] - private void F1Help() - { - var text = @" -Imports System -Imports System.Collections.Generic -Imports System.Linq - -Module Program$$ - Sub Main(args As String()) - Dim query = From arg In args - Select args.Any(Function(a) a.Length > 5) - Dim x = 0 - x += 1 - End Sub - Public Function F() As Object - Return Nothing - End Function -End Module"; - - SetUpEditor(text); - Verify("Linq", "System.Linq"); - Verify("String", "vb.String"); - Verify("Any", "System.Linq.Enumerable.Any"); - Verify("From", "vb.QueryFrom"); - Verify("+=", "vb.+="); - Verify("Nothing", "vb.Nothing"); - - } - - private void Verify(string word, string expectedKeyword) - { - VisualStudio.Editor.PlaceCaret(word, charsOffset: -1); - Assert.Contains(expectedKeyword, VisualStudio.Editor.GetF1Keyword()); - } - } -} diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpF1Help.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpF1Help.cs similarity index 59% rename from src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpF1Help.cs rename to src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpF1Help.cs index 45a8bcd352d05..bb85cde539413 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpF1Help.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpF1Help.cs @@ -2,29 +2,26 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - +using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Test.Utilities; -using Microsoft.VisualStudio.IntegrationTest.Utilities; -using Roslyn.Test.Utilities; using Xunit; -using Xunit.Abstractions; namespace Roslyn.VisualStudio.IntegrationTests.CSharp { - [Collection(nameof(SharedIntegrationHostFixture))] + [Trait(Traits.Feature, Traits.Features.F1Help)] public class CSharpF1Help : AbstractEditorTest { protected override string LanguageName => LanguageNames.CSharp; - public CSharpF1Help(VisualStudioInstanceFactory instanceFactory) - : base(instanceFactory, nameof(CSharpF1Help)) + public CSharpF1Help() + : base(nameof(CSharpF1Help)) { } - [WpfFact, Trait(Traits.Feature, Traits.Features.F1Help)] - private void F1Help() + [IdeFact] + private async Task F1Help() { var text = @" using System; @@ -69,17 +66,17 @@ group n by n % 5 into g #endregion TaoRegion }"; - SetUpEditor(text); - Verify("abstract", "abstract_CSharpKeyword"); - Verify("ascending", "ascending_CSharpKeyword"); - Verify("from", "from_CSharpKeyword"); - Verify("First();", "System.Linq.Enumerable.First``1"); + await SetUpEditorAsync(text, HangMitigatingCancellationToken); + await VerifyAsync("abstract", "abstract_CSharpKeyword", HangMitigatingCancellationToken); + await VerifyAsync("ascending", "ascending_CSharpKeyword", HangMitigatingCancellationToken); + await VerifyAsync("from", "from_CSharpKeyword", HangMitigatingCancellationToken); + await VerifyAsync("First();", "System.Linq.Enumerable.First``1", HangMitigatingCancellationToken); } - private void Verify(string word, string expectedKeyword) + private async Task VerifyAsync(string word, string expectedKeyword, CancellationToken cancellationToken) { - VisualStudio.Editor.PlaceCaret(word, charsOffset: -1); - Assert.Contains(expectedKeyword, VisualStudio.Editor.GetF1Keyword()); + await TestServices.Editor.PlaceCaretAsync(word, charsOffset: -1, cancellationToken); + Assert.Contains(expectedKeyword, await TestServices.Editor.GetF1KeywordsAsync(cancellationToken)); } } } diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs index 632870b89dab2..792663a008b42 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs @@ -291,6 +291,42 @@ public async Task SetUseSuggestionModeAsync(bool forDebuggerTextView, bool value private static bool IsDebuggerTextView(ITextView textView) => textView.Roles.Contains("DEBUGVIEW"); + public async Task> GetF1KeywordsAsync(CancellationToken cancellationToken) + { + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + var vsView = await GetActiveVsTextViewAsync(cancellationToken); + ErrorHandler.ThrowOnFailure(vsView.GetBuffer(out var textLines)); + ErrorHandler.ThrowOnFailure(textLines.GetLanguageServiceID(out var languageServiceGuid)); + + var languageService = await ((AsyncServiceProvider)AsyncServiceProvider.GlobalProvider).QueryServiceAsync(languageServiceGuid).WithCancellation(cancellationToken); + Assumes.Present(languageService); + + var languageContextProvider = (IVsLanguageContextProvider)languageService; + var monitorUserContext = await GetRequiredGlobalServiceAsync(cancellationToken); + ErrorHandler.ThrowOnFailure(monitorUserContext.CreateEmptyContext(out var emptyUserContext)); + ErrorHandler.ThrowOnFailure(vsView.GetCaretPos(out var line, out var column)); + + var span = new TextManager.Interop.TextSpan() + { + iStartLine = line, + iStartIndex = column, + iEndLine = line, + iEndIndex = column, + }; + + ErrorHandler.ThrowOnFailure(languageContextProvider.UpdateLanguageContext(dwHint: 0, textLines, new[] { span }, emptyUserContext)); + ErrorHandler.ThrowOnFailure(emptyUserContext.CountAttributes("keyword", fIncludeChildren: Convert.ToInt32(true), out var count)); + var results = ImmutableArray.CreateBuilder(count); + for (var i = 0; i < count; i++) + { + emptyUserContext.GetAttribute(i, "keyword", fIncludeChildren: Convert.ToInt32(true), pbstrName: out _, out var value); + results.Add(value); + } + + return results.MoveToImmutable(); + } + #region Navigation bars public async Task ExpandNavigationBarAsync(NavigationBarDropdownKind index, CancellationToken cancellationToken) diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicF1Help.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicF1Help.cs new file mode 100644 index 0000000000000..b90e54250fbe2 --- /dev/null +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicF1Help.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; + +namespace Roslyn.VisualStudio.IntegrationTests.VisualBasic +{ + [Trait(Traits.Feature, Traits.Features.F1Help)] + public class BasicF1Help : AbstractEditorTest + { + protected override string LanguageName => LanguageNames.VisualBasic; + + public BasicF1Help() + : base(nameof(BasicF1Help)) + { + } + + [IdeFact] + private async Task F1Help() + { + var text = @" +Imports System +Imports System.Collections.Generic +Imports System.Linq + +Module Program$$ + Sub Main(args As String()) + Dim query = From arg In args + Select args.Any(Function(a) a.Length > 5) + Dim x = 0 + x += 1 + End Sub + Public Function F() As Object + Return Nothing + End Function +End Module"; + + await SetUpEditorAsync(text, HangMitigatingCancellationToken); + await VerifyAsync("Linq", "System.Linq", HangMitigatingCancellationToken); + await VerifyAsync("String", "vb.String", HangMitigatingCancellationToken); + await VerifyAsync("Any", "System.Linq.Enumerable.Any", HangMitigatingCancellationToken); + await VerifyAsync("From", "vb.QueryFrom", HangMitigatingCancellationToken); + await VerifyAsync("+=", "vb.+=", HangMitigatingCancellationToken); + await VerifyAsync("Nothing", "vb.Nothing", HangMitigatingCancellationToken); + + } + + private async Task VerifyAsync(string word, string expectedKeyword, CancellationToken cancellationToken) + { + await TestServices.Editor.PlaceCaretAsync(word, charsOffset: -1, cancellationToken); + Assert.Contains(expectedKeyword, await TestServices.Editor.GetF1KeywordsAsync(cancellationToken)); + } + } +} diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs index 22c4a7376e484..9be898d6dc390 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs @@ -736,39 +736,6 @@ public string[] GetOutliningSpans() }); } - public List GetF1Keywords() - { - return InvokeOnUIThread(cancellationToken => - { - var results = new List(); - GetActiveVsTextView().GetBuffer(out var textLines); - Marshal.ThrowExceptionForHR(textLines.GetLanguageServiceID(out var languageServiceGuid)); - Marshal.ThrowExceptionForHR(Microsoft.VisualStudio.Shell.ServiceProvider.GlobalProvider.QueryService(languageServiceGuid, out var languageService)); - var languageContextProvider = (IVsLanguageContextProvider)languageService; - - var monitorUserContext = GetGlobalService(); - Marshal.ThrowExceptionForHR(monitorUserContext.CreateEmptyContext(out var emptyUserContext)); - Marshal.ThrowExceptionForHR(GetActiveVsTextView().GetCaretPos(out var line, out var column)); - var span = new TextManager.Interop.TextSpan() - { - iStartLine = line, - iStartIndex = column, - iEndLine = line, - iEndIndex = column - }; - - Marshal.ThrowExceptionForHR(languageContextProvider.UpdateLanguageContext(0, textLines, new[] { span }, emptyUserContext)); - Marshal.ThrowExceptionForHR(emptyUserContext.CountAttributes("keyword", VSConstants.S_FALSE, out var count)); - for (var i = 0; i < count; i++) - { - emptyUserContext.GetAttribute(i, "keyword", VSConstants.S_FALSE, out var key, out var value); - results.Add(value); - } - - return results; - }); - } - public void GoToDefinition() => ExecuteCommand(WellKnownCommandNames.Edit_GoToDefinition); diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs index e491862fda2b8..3929d76969eb9 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs @@ -239,9 +239,6 @@ public void WaitForActiveView(string viewName) public string[] GetErrorTags() => _editorInProc.GetErrorTags(); - public List GetF1Keyword() - => _editorInProc.GetF1Keywords(); - public void ExpandTypeNavBar() { _instance.Workspace.WaitForAsyncOperations(Helper.HangMitigatingTimeout, FeatureAttribute.NavigationBar); From ed66950dde1b26b3ef50a4a099369593b36bda6c Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Fri, 11 Feb 2022 11:00:16 -0800 Subject: [PATCH 4/7] Simplify command execution --- .../InProcess/EditorInProcess.cs | 8 +-- .../InProcess/ShellInProcess.cs | 51 +++++++++++++++++++ .../InProcess/SolutionExplorerInProcess.cs | 5 +- 3 files changed, 54 insertions(+), 10 deletions(-) diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs index 792663a008b42..bdbf90e0f1669 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs @@ -37,7 +37,6 @@ using Roslyn.VisualStudio.IntegrationTests.InProcess; using Xunit; using IObjectWithSite = Microsoft.VisualStudio.OLE.Interop.IObjectWithSite; -using IOleCommandTarget = Microsoft.VisualStudio.OLE.Interop.IOleCommandTarget; using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider; using OLECMDEXECOPT = Microsoft.VisualStudio.OLE.Interop.OLECMDEXECOPT; using TextSpan = Microsoft.CodeAnalysis.Text.TextSpan; @@ -278,9 +277,7 @@ public async Task SetUseSuggestionModeAsync(bool forDebuggerTextView, bool value { if (await IsUseSuggestionModeOnAsync(forDebuggerTextView, cancellationToken) != value) { - var dispatcher = await GetRequiredGlobalServiceAsync(cancellationToken); - ErrorHandler.ThrowOnFailure(dispatcher.Exec(typeof(VSConstants.VSStd2KCmdID).GUID, (uint)VSConstants.VSStd2KCmdID.ToggleConsumeFirstCompletionMode, (uint)OLECMDEXECOPT.OLECMDEXECOPT_DODEFAULT, IntPtr.Zero, IntPtr.Zero)); - + await TestServices.Shell.ExecuteCommandAsync(VSConstants.VSStd2KCmdID.ToggleConsumeFirstCompletionMode, cancellationToken); if (await IsUseSuggestionModeOnAsync(forDebuggerTextView, cancellationToken) != value) { throw new InvalidOperationException($"{WellKnownCommandNames.Edit_ToggleCompletionMode} did not leave the editor in the expected state."); @@ -886,8 +883,7 @@ public async Task GoToBaseAsync(CancellationToken cancellationToken) { await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - var dispatcher = await GetRequiredGlobalServiceAsync(cancellationToken); - ErrorHandler.ThrowOnFailure(dispatcher.Exec(EditorConstants.EditorCommandSet, (uint)EditorConstants.EditorCommandID.GoToBase, (uint)OLECMDEXECOPT.OLECMDEXECOPT_DODEFAULT, IntPtr.Zero, IntPtr.Zero)); + await TestServices.Shell.ExecuteCommandAsync(EditorConstants.EditorCommandID.GoToBase, cancellationToken); await TestServices.Workspace.WaitForAsyncOperationsAsync(FeatureAttribute.Workspace, cancellationToken); diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/ShellInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/ShellInProcess.cs index 4d53d48932271..2ff1364699281 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/ShellInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/ShellInProcess.cs @@ -2,20 +2,71 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.UnitTests; +using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Threading; using IAsyncDisposable = System.IAsyncDisposable; +using IOleCommandTarget = Microsoft.VisualStudio.OLE.Interop.IOleCommandTarget; +using OLECMDEXECOPT = Microsoft.VisualStudio.OLE.Interop.OLECMDEXECOPT; namespace Microsoft.VisualStudio.Extensibility.Testing { internal partial class ShellInProcess { + public async Task<(Guid commandGroup, uint commandId)> PrepareCommandAsync(string command, CancellationToken cancellationToken) + { + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + var commandWindow = await GetRequiredGlobalServiceAsync(cancellationToken); + var result = new PREPARECOMMANDRESULT[1]; + ErrorHandler.ThrowOnFailure(commandWindow.PrepareCommand(command, out var commandGroup, out var commandId, out var cmdArg, result)); + + if (cmdArg != IntPtr.Zero) + { + throw new NotSupportedException("Unable to create a disposable wrapper for command arguments (VARIANT)."); + } + + return (commandGroup, commandId); + } + + public async Task ExecuteCommandAsync(string command, CancellationToken cancellationToken) + { + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + var (commandGuid, commandId) = await PrepareCommandAsync(command, cancellationToken); + await ExecuteCommandAsync(commandGuid, commandId, cancellationToken); + } + + public Task ExecuteCommandAsync(TEnum command, CancellationToken cancellationToken) + where TEnum : struct, Enum + { + var commandGuid = command switch + { + EditorConstants.EditorCommandID => EditorConstants.EditorCommandSet, + _ => typeof(TEnum).GUID, + }; + + return ExecuteCommandAsync(commandGuid, Convert.ToUInt32(command), cancellationToken); + } + + public Task ExecuteCommandAsync((Guid commandGuid, uint commandId) command, CancellationToken cancellationToken) + => ExecuteCommandAsync(command.commandGuid, command.commandId, cancellationToken); + + public async Task ExecuteCommandAsync(Guid commandGuid, uint commandId, CancellationToken cancellationToken) + { + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + var dispatcher = await TestServices.Shell.GetRequiredGlobalServiceAsync(cancellationToken); + ErrorHandler.ThrowOnFailure(dispatcher.Exec(commandGuid, commandId, (uint)OLECMDEXECOPT.OLECMDEXECOPT_DODEFAULT, IntPtr.Zero, IntPtr.Zero)); + } + public async Task PauseFileChangesAsync(CancellationToken cancellationToken) { await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/SolutionExplorerInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/SolutionExplorerInProcess.cs index dee04f7fdc0d3..b490f15a66def 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/SolutionExplorerInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/SolutionExplorerInProcess.cs @@ -206,10 +206,7 @@ await Helper.RetryAsync( public async Task SaveAllAsync(CancellationToken cancellationToken) { - await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - - var dispatcher = await GetRequiredGlobalServiceAsync(cancellationToken); - ErrorHandler.ThrowOnFailure(dispatcher.Exec(typeof(VSConstants.VSStd97CmdID).GUID, (uint)VSConstants.VSStd97CmdID.SaveSolution, (uint)OLECMDEXECOPT.OLECMDEXECOPT_DODEFAULT, IntPtr.Zero, IntPtr.Zero)); + await TestServices.Shell.ExecuteCommandAsync(VSConstants.VSStd97CmdID.SaveSolution, cancellationToken); } public async Task OpenFileAsync(string projectName, string relativeFilePath, CancellationToken cancellationToken) From 8e72ebe3e4e2b89a929eae2307620fae3446062a Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Fri, 11 Feb 2022 11:05:53 -0800 Subject: [PATCH 5/7] Port CSharpOrganizing to the new integration test project --- .../CSharp/CSharpOrganizing.cs | 27 ++++++++----------- .../New.IntegrationTests/WellKnownCommands.cs | 17 ++++++++++++ 2 files changed, 28 insertions(+), 16 deletions(-) rename src/VisualStudio/IntegrationTest/{IntegrationTests => New.IntegrationTests}/CSharp/CSharpOrganizing.cs (55%) create mode 100644 src/VisualStudio/IntegrationTest/New.IntegrationTests/WellKnownCommands.cs diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpOrganizing.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpOrganizing.cs similarity index 55% rename from src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpOrganizing.cs rename to src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpOrganizing.cs index d6c96fabf184b..5901459adab23 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpOrganizing.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpOrganizing.cs @@ -2,31 +2,27 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - +using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Test.Utilities; -using Microsoft.VisualStudio.IntegrationTest.Utilities; -using Roslyn.Test.Utilities; using Xunit; -using Xunit.Abstractions; namespace Roslyn.VisualStudio.IntegrationTests.CSharp { - [Collection(nameof(SharedIntegrationHostFixture))] + [Trait(Traits.Feature, Traits.Features.Organizing)] public class CSharpOrganizing : AbstractEditorTest { protected override string LanguageName => LanguageNames.CSharp; - public CSharpOrganizing(VisualStudioInstanceFactory instanceFactory) - : base(instanceFactory, nameof(CSharpOrganizing)) + public CSharpOrganizing() + : base(nameof(CSharpOrganizing)) { } - [WpfFact, Trait(Traits.Feature, Traits.Features.Organizing)] - public void RemoveAndSort() + [IdeFact] + public async Task RemoveAndSort() { - SetUpEditor(@"$$ + await SetUpEditorAsync(@"$$ using C; using B; using A; @@ -38,9 +34,9 @@ class Test } namespace A { public class CA { } } namespace B { public class CB { } } -namespace C { public class CC { } }"); - VisualStudio.ExecuteCommand("Edit.RemoveAndSort"); - VisualStudio.Editor.Verify.TextContains(@" +namespace C { public class CC { } }", HangMitigatingCancellationToken); + await TestServices.Shell.ExecuteCommandAsync(WellKnownCommands.Edit.RemoveAndSort, HangMitigatingCancellationToken); + await TestServices.EditorVerifier.TextContainsAsync(@" using A; using C; @@ -51,8 +47,7 @@ class Test } namespace A { public class CA { } } namespace B { public class CB { } } -namespace C { public class CC { } }"); - +namespace C { public class CC { } }", cancellationToken: HangMitigatingCancellationToken); } } } diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/WellKnownCommands.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/WellKnownCommands.cs new file mode 100644 index 0000000000000..45e7742dc685f --- /dev/null +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/WellKnownCommands.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Microsoft.VisualStudio; + +namespace Roslyn.VisualStudio.IntegrationTests +{ + internal static class WellKnownCommands + { + public static class Edit + { + public static readonly (Guid commandGroup, uint commandId) RemoveAndSort = (VSConstants.CMDSETID.CSharpGroup_guid, 6419); + } + } +} From 4da854c246ae5f4979837131e4b3d1cc8f209726 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Wed, 16 Feb 2022 13:12:48 -0800 Subject: [PATCH 6/7] Update to vs-extension-testing 0.1.127-beta --- eng/Versions.props | 2 +- .../InProcess/ShellInProcess.cs | 51 ------------------- .../New.IntegrationTests/WellKnownCommands.cs | 4 +- 3 files changed, 3 insertions(+), 54 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index bb7a9a0e7202f..efa3d1be8afc3 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -22,7 +22,7 @@ 3.3.3-beta1.21105.3 6.0.0-rc1.21366.2 1.1.1-beta1.22081.4 - 0.1.124-beta + 0.1.127-beta 4.0.1 16.10.230 diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/ShellInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/ShellInProcess.cs index 2ff1364699281..4d53d48932271 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/ShellInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/ShellInProcess.cs @@ -2,71 +2,20 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.UnitTests; -using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Threading; using IAsyncDisposable = System.IAsyncDisposable; -using IOleCommandTarget = Microsoft.VisualStudio.OLE.Interop.IOleCommandTarget; -using OLECMDEXECOPT = Microsoft.VisualStudio.OLE.Interop.OLECMDEXECOPT; namespace Microsoft.VisualStudio.Extensibility.Testing { internal partial class ShellInProcess { - public async Task<(Guid commandGroup, uint commandId)> PrepareCommandAsync(string command, CancellationToken cancellationToken) - { - await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - - var commandWindow = await GetRequiredGlobalServiceAsync(cancellationToken); - var result = new PREPARECOMMANDRESULT[1]; - ErrorHandler.ThrowOnFailure(commandWindow.PrepareCommand(command, out var commandGroup, out var commandId, out var cmdArg, result)); - - if (cmdArg != IntPtr.Zero) - { - throw new NotSupportedException("Unable to create a disposable wrapper for command arguments (VARIANT)."); - } - - return (commandGroup, commandId); - } - - public async Task ExecuteCommandAsync(string command, CancellationToken cancellationToken) - { - await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - - var (commandGuid, commandId) = await PrepareCommandAsync(command, cancellationToken); - await ExecuteCommandAsync(commandGuid, commandId, cancellationToken); - } - - public Task ExecuteCommandAsync(TEnum command, CancellationToken cancellationToken) - where TEnum : struct, Enum - { - var commandGuid = command switch - { - EditorConstants.EditorCommandID => EditorConstants.EditorCommandSet, - _ => typeof(TEnum).GUID, - }; - - return ExecuteCommandAsync(commandGuid, Convert.ToUInt32(command), cancellationToken); - } - - public Task ExecuteCommandAsync((Guid commandGuid, uint commandId) command, CancellationToken cancellationToken) - => ExecuteCommandAsync(command.commandGuid, command.commandId, cancellationToken); - - public async Task ExecuteCommandAsync(Guid commandGuid, uint commandId, CancellationToken cancellationToken) - { - await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - - var dispatcher = await TestServices.Shell.GetRequiredGlobalServiceAsync(cancellationToken); - ErrorHandler.ThrowOnFailure(dispatcher.Exec(commandGuid, commandId, (uint)OLECMDEXECOPT.OLECMDEXECOPT_DODEFAULT, IntPtr.Zero, IntPtr.Zero)); - } - public async Task PauseFileChangesAsync(CancellationToken cancellationToken) { await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/WellKnownCommands.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/WellKnownCommands.cs index 45e7742dc685f..ba104e0ac500f 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/WellKnownCommands.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/WellKnownCommands.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; +using System.ComponentModel.Design; using Microsoft.VisualStudio; namespace Roslyn.VisualStudio.IntegrationTests @@ -11,7 +11,7 @@ internal static class WellKnownCommands { public static class Edit { - public static readonly (Guid commandGroup, uint commandId) RemoveAndSort = (VSConstants.CMDSETID.CSharpGroup_guid, 6419); + public static readonly CommandID RemoveAndSort = new(VSConstants.CMDSETID.CSharpGroup_guid, 6419); } } } From 6f94f3dbfde7ba42e37532eb0713f4b67a3616b7 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Wed, 16 Feb 2022 13:15:48 -0800 Subject: [PATCH 7/7] Log execution time for tests --- src/Tools/Source/RunTests/ProcessTestExecutor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tools/Source/RunTests/ProcessTestExecutor.cs b/src/Tools/Source/RunTests/ProcessTestExecutor.cs index e8dc9282c45af..9f34c63733534 100644 --- a/src/Tools/Source/RunTests/ProcessTestExecutor.cs +++ b/src/Tools/Source/RunTests/ProcessTestExecutor.cs @@ -196,7 +196,7 @@ private async Task RunTestAsyncInternal(AssemblyInfo assemblyInfo, b } } - Logger.Log($"Command line {assemblyInfo.DisplayName}: {Options.DotnetFilePath} {commandLineArguments}"); + Logger.Log($"Command line {assemblyInfo.DisplayName} completed in {span.TotalSeconds} seconds: {Options.DotnetFilePath} {commandLineArguments}"); var standardOutput = string.Join(Environment.NewLine, xunitProcessResult.OutputLines) ?? ""; var errorOutput = string.Join(Environment.NewLine, xunitProcessResult.ErrorLines) ?? "";