Skip to content

Commit

Permalink
Add integration test support for LSP based editor
Browse files Browse the repository at this point in the history
  • Loading branch information
dibarbet committed Jan 5, 2021
1 parent a4d4605 commit 97c54a2
Show file tree
Hide file tree
Showing 14 changed files with 109 additions and 59 deletions.
9 changes: 9 additions & 0 deletions eng/build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ param (
[switch]$warnAsError = $false,
[switch]$sourceBuild = $false,
[switch]$oop64bit = $true,
[switch]$lspEditor = $false,

# official build settings
[string]$officialBuildId = "",
Expand Down Expand Up @@ -365,6 +366,13 @@ function TestUsingRunTests() {
$args += " --sequential"
$args += " --include '\.IntegrationTests'"
$args += " --include 'Microsoft.CodeAnalysis.Workspaces.MSBuild.UnitTests'"

if ($lspEditor) {
$args += " --testfilter FullyQualifiedName~Roslyn.VisualStudio.IntegrationTests.LanguageServerProtocol|Editor=LanguageServerProtocol"
}
else {
$args += " --testfilter FullyQualifiedName!~Roslyn.VisualStudio.IntegrationTests.LanguageServerProtocol"
}
}

if (-not $ci -and -not $testVsi) {
Expand Down Expand Up @@ -538,6 +546,7 @@ function Setup-IntegrationTestRun() {
}

$env:ROSLYN_OOP64BIT = "$oop64bit"
$env:ROSLYN_LSPEDITOR = "$lspEditor"
}

function Prepare-TempDir() {
Expand Down
1 change: 1 addition & 0 deletions src/Compilers/Test/Core/Traits/Traits.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public static class Editors
public const string KeyProcessors = nameof(KeyProcessors);
public const string KeyProcessorProviders = nameof(KeyProcessorProviders);
public const string Preview = nameof(Preview);
public const string LanguageServerProtocol = nameof(LanguageServerProtocol);
}

public const string Feature = nameof(Feature);
Expand Down
8 changes: 3 additions & 5 deletions src/Tools/Source/RunTests/ITestExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,16 @@ internal readonly struct TestExecutionOptions
internal string DotnetFilePath { get; }
internal ProcDumpInfo? ProcDumpInfo { get; }
internal string TestResultsDirectory { get; }
internal string? Trait { get; }
internal string? NoTrait { get; }
internal string? TestFilter { get; }
internal bool IncludeHtml { get; }
internal bool Retry { get; }

internal TestExecutionOptions(string dotnetFilePath, ProcDumpInfo? procDumpInfo, string testResultsDirectory, string? trait, string? noTrait, bool includeHtml, bool retry)
internal TestExecutionOptions(string dotnetFilePath, ProcDumpInfo? procDumpInfo, string testResultsDirectory, string? testFilter, bool includeHtml, bool retry)
{
DotnetFilePath = dotnetFilePath;
ProcDumpInfo = procDumpInfo;
TestResultsDirectory = testResultsDirectory;
Trait = trait;
NoTrait = noTrait;
TestFilter = testFilter;
IncludeHtml = includeHtml;
Retry = retry;
}
Expand Down
18 changes: 5 additions & 13 deletions src/Tools/Source/RunTests/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,9 @@ internal class Options
public Display Display { get; set; }

/// <summary>
/// Trait string to pass to xunit.
/// Filter string to pass to xunit.
/// </summary>
public string? Trait { get; set; }

/// <summary>
/// The no-trait string to pass to xunit.
/// </summary>
public string? NoTrait { get; set; }
public string? TestFilter { get; set; }

public string Configuration { get; set; }

Expand Down Expand Up @@ -125,8 +120,7 @@ public Options(
var excludeFilter = new List<string>();
var sequential = false;
var retry = false;
string? traits = null;
string? noTraits = null;
string? testFilter = null;
int? timeout = null;
string? resultFileDirectory = null;
string? logFileDirectory = null;
Expand All @@ -144,8 +138,7 @@ public Options(
{ "platform=", "Platform to test: x86 or x64", (string s) => platform = s },
{ "html", "Include HTML file output", o => includeHtml = o is object },
{ "sequential", "Run tests sequentially", o => sequential = o is object },
{ "traits=", "xUnit traits to include (semicolon delimited)", (string s) => traits = s },
{ "notraits=", "xUnit traits to exclude (semicolon delimited)", (string s) => noTraits = s },
{ "testfilter=", "xUnit string to pass to --filter, e.g. FullyQualifiedName~TestClass1|Category=CategoryA", (string s) => testFilter = s },
{ "timeout=", "Minute timeout to limit the tests to", (int i) => timeout = i },
{ "out=", "Test result file directory", (string s) => resultFileDirectory = s },
{ "logs=", "Log file directory", (string s) => logFileDirectory = s },
Expand Down Expand Up @@ -225,8 +218,7 @@ public Options(
CollectDumps = collectDumps,
Sequential = sequential,
IncludeHtml = includeHtml,
Trait = traits,
NoTrait = noTraits,
TestFilter = testFilter,
Timeout = timeout is { } t ? TimeSpan.FromMinutes(t) : null,
Retry = retry,
};
Expand Down
20 changes: 4 additions & 16 deletions src/Tools/Source/RunTests/ProcessTestExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public string GetCommandLineArguments(AssemblyInfo assemblyInfo)
builder.Append($@"test");
builder.Append($@" ""{assemblyInfo.AssemblyPath}""");
var typeInfoList = assemblyInfo.PartitionInfo.TypeInfoList;
if (typeInfoList.Length > 0 || !string.IsNullOrWhiteSpace(Options.Trait) || !string.IsNullOrWhiteSpace(Options.NoTrait))
if (typeInfoList.Length > 0 || !string.IsNullOrWhiteSpace(Options.TestFilter))
{
builder.Append(" --filter ");
var any = false;
Expand All @@ -44,22 +44,10 @@ public string GetCommandLineArguments(AssemblyInfo assemblyInfo)
builder.Append(typeInfo.FullName);
}

if (Options.Trait is object)
if (Options.TestFilter is object)
{
foreach (var trait in Options.Trait.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
{
MaybeAddSeparator();
builder.Append($"Trait={trait}");
}
}

if (Options.NoTrait is object)
{
foreach (var trait in Options.NoTrait.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
{
MaybeAddSeparator('&');
builder.Append($"Trait!~{trait}");
}
MaybeAddSeparator();
builder.Append(Options.TestFilter);
}

void MaybeAddSeparator(char separator = '|')
Expand Down
4 changes: 1 addition & 3 deletions src/Tools/Source/RunTests/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ internal static async Task<int> Main(string[] args)
{
Logger.Log("RunTest command line");
Logger.Log(string.Join(" ", args));

var options = Options.Parse(args);
if (options == null)
{
Expand Down Expand Up @@ -364,8 +363,7 @@ private static ProcessTestExecutor CreateTestExecutor(Options options)
dotnetFilePath: options.DotnetFilePath,
procDumpInfo: options.CollectDumps ? GetProcDumpInfo(options) : null,
testResultsDirectory: options.TestResultsDirectory,
trait: options.Trait,
noTrait: options.NoTrait,
testFilter: options.TestFilter,
includeHtml: options.IncludeHtml,
retry: options.Retry);
return new ProcessTestExecutor(testExecutionOptions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public CSharpGoToDefinition(VisualStudioInstanceFactory instanceFactory)
{
}

[WpfFact, Trait(Traits.Feature, Traits.Features.GoToDefinition)]
[WpfFact, Trait(Traits.Feature, Traits.Features.GoToDefinition), Trait(Traits.Editor, Traits.Editors.LanguageServerProtocol)]
public void GoToClassDeclaration()
{
var project = new ProjectUtils.Project(ProjectName);
Expand All @@ -44,12 +44,12 @@ public void GoToClassDeclaration()
SomeClass sc;
}");
VisualStudio.Editor.PlaceCaret("SomeClass");
VisualStudio.Editor.GoToDefinition();
VisualStudio.Editor.GoToDefinition("FileDef.cs");
VisualStudio.Editor.Verify.TextContains(@"class SomeClass$$", assertCaretPosition: true);
Assert.False(VisualStudio.Shell.IsActiveTabProvisional());
}

[WpfFact, Trait(Traits.Feature, Traits.Features.GoToDefinition)]
[WpfFact, Trait(Traits.Feature, Traits.Features.GoToDefinition), Trait(Traits.Editor, Traits.Editors.LanguageServerProtocol)]
public void GoToDefinitionOpensProvisionalTabIfDocumentNotAlreadyOpen()
{
var project = new ProjectUtils.Project(ProjectName);
Expand All @@ -69,7 +69,7 @@ public void GoToDefinitionOpensProvisionalTabIfDocumentNotAlreadyOpen()
SomeClass sc;
}");
VisualStudio.Editor.PlaceCaret("SomeClass");
VisualStudio.Editor.GoToDefinition();
VisualStudio.Editor.GoToDefinition("FileDef.cs");
VisualStudio.Editor.Verify.TextContains(@"class SomeClass$$", assertCaretPosition: true);
Assert.True(VisualStudio.Shell.IsActiveTabProvisional());
}
Expand All @@ -82,7 +82,7 @@ public void GoToDefinitionWithMultipleResults()
partial class PartialClass { int i = 0; }");

VisualStudio.Editor.GoToDefinition();
VisualStudio.Editor.GoToDefinition("Class1.cs");

const string programReferencesCaption = "'PartialClass' declarations";
var results = VisualStudio.FindReferencesWindow.GetContents(programReferencesCaption);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public CSharpGoToImplementation(VisualStudioInstanceFactory instanceFactory)
{
}

[WpfFact, Trait(Traits.Feature, Traits.Features.GoToImplementation)]
[WpfFact, Trait(Traits.Feature, Traits.Features.GoToImplementation), Trait(Traits.Editor, Traits.Editors.LanguageServerProtocol)]
public void SimpleGoToImplementation()
{
var project = new ProjectUtils.Project(ProjectName);
Expand All @@ -41,12 +41,12 @@ public void SimpleGoToImplementation()
{
}");
VisualStudio.Editor.PlaceCaret("interface IGoo");
VisualStudio.Editor.GoToImplementation();
VisualStudio.Editor.GoToImplementation("FileImplementation.cs");
VisualStudio.Editor.Verify.TextContains(@"class Implementation$$", assertCaretPosition: true);
Assert.False(VisualStudio.Shell.IsActiveTabProvisional());
}

[WpfFact, Trait(Traits.Feature, Traits.Features.GoToImplementation)]
[WpfFact, Trait(Traits.Feature, Traits.Features.GoToImplementation), Trait(Traits.Editor, Traits.Editors.LanguageServerProtocol)]
public void GoToImplementationOpensProvisionalTabIfDocumentNotOpen()
{
var project = new ProjectUtils.Project(ProjectName);
Expand All @@ -65,13 +65,13 @@ public void GoToImplementationOpensProvisionalTabIfDocumentNotOpen()
{
}");
VisualStudio.Editor.PlaceCaret("interface IBar");
VisualStudio.Editor.GoToImplementation();
VisualStudio.Editor.GoToImplementation("FileImplementation.cs");
VisualStudio.Editor.Verify.TextContains(@"class Implementation$$", assertCaretPosition: true);
Assert.True(VisualStudio.Shell.IsActiveTabProvisional());
}

// TODO: Enable this once the GoToDefinition tests are merged
[WpfFact, Trait(Traits.Feature, Traits.Features.GoToImplementation)]
[WpfFact, Trait(Traits.Feature, Traits.Features.GoToImplementation), Trait(Traits.Editor, Traits.Editors.LanguageServerProtocol)]
public void GoToImplementationFromMetadataAsSource()
{
var project = new ProjectUtils.Project(ProjectName);
Expand All @@ -88,8 +88,8 @@ public void SomeMethod()
}
}");
VisualStudio.Editor.PlaceCaret("IDisposable d", charsOffset: -1);
VisualStudio.Editor.GoToDefinition();
VisualStudio.Editor.GoToImplementation();
VisualStudio.Editor.GoToDefinition("IDisposable.cs");
VisualStudio.Editor.GoToImplementation("FileImplementation.cs");
VisualStudio.Editor.Verify.TextContains(@"class Implementation$$ : IDisposable", assertCaretPosition: true);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public static void Main()
}");

VisualStudio.Editor.PlaceCaret(HelloWorldGenerator.GeneratedEnglishClassName);
VisualStudio.Editor.GoToDefinition();
VisualStudio.Editor.GoToDefinition($"{HelloWorldGenerator.GeneratedEnglishClassName}.cs");
Assert.Equal($"{HelloWorldGenerator.GeneratedEnglishClassName}.cs {ServicesVSResources.generated_suffix}", VisualStudio.Shell.GetActiveWindowCaption());
Assert.Equal(HelloWorldGenerator.GeneratedEnglishClassName, VisualStudio.Editor.GetSelectedText());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// 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 Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.VisualStudio.IntegrationTest.Utilities;
using Microsoft.VisualStudio.IntegrationTest.Utilities.Common;
using Roslyn.Test.Utilities;
using Xunit;
using Xunit.Abstractions;
using ProjectUtils = Microsoft.VisualStudio.IntegrationTest.Utilities.Common.ProjectUtils;

namespace Roslyn.VisualStudio.IntegrationTests.LanguageServerProtocol
{
[Collection(nameof(SharedIntegrationHostFixture))]
public class LspGoToDefinition : AbstractEditorTest
{
protected override string LanguageName => LanguageNames.CSharp;

public LspGoToDefinition(VisualStudioInstanceFactory instanceFactory)
: base(instanceFactory, nameof(LspGoToDefinition))
{
}

[WpfFact, Trait(Traits.Feature, Traits.Features.GoToDefinition), Trait(Traits.Editor, Traits.Editors.LanguageServerProtocol)]
public void GoToDefinitionLSP()
{
var project = new ProjectUtils.Project(ProjectName);
VisualStudio.SolutionExplorer.AddFile(project, "FileDef.cs");
VisualStudio.SolutionExplorer.OpenFile(project, "FileDef.cs");
VisualStudio.Editor.SetText(
@"class SomeClass
{
}
");
VisualStudio.SolutionExplorer.CloseCodeFile(project, "FileDef.cs", saveFile: true);
VisualStudio.SolutionExplorer.AddFile(project, "FileConsumer.cs");
VisualStudio.SolutionExplorer.OpenFile(project, "FileConsumer.cs");
VisualStudio.Editor.SetText(
@"class SomeOtherClass
{
SomeClass sc;
}");
VisualStudio.Editor.PlaceCaret("SomeClass");
VisualStudio.Editor.GoToDefinition("FileDef.cs");
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
VisualStudio.Editor.Verify.TextContains(@"class SomeClass$$", assertCaretPosition: true);
Assert.True(VisualStudio.Shell.IsActiveTabProvisional());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public void GoToClassDeclaration()
Dim gibberish As SomeClass
End Class");
VisualStudio.Editor.PlaceCaret("SomeClass");
VisualStudio.Editor.GoToDefinition();
VisualStudio.Editor.GoToDefinition("FileDef.vb");
VisualStudio.Editor.Verify.TextContains(@"Class SomeClass$$", assertCaretPosition: true);
Assert.False(VisualStudio.Shell.IsActiveTabProvisional());
}
Expand All @@ -52,13 +52,13 @@ Dim i As Integer$$
End Class");
VisualStudio.Workspace.SetFeatureOption(feature: "VisualStudioNavigationOptions", optionName: "NavigateToObjectBrowser", language: LanguageName, valueString: "True");

VisualStudio.Editor.GoToDefinition();
VisualStudio.Editor.GoToDefinition("Class1.vb");
Assert.Equal("Object Browser", VisualStudio.Shell.GetActiveWindowCaption());

VisualStudio.Workspace.SetFeatureOption(feature: "VisualStudioNavigationOptions", optionName: "NavigateToObjectBrowser", language: LanguageName, valueString: "False");

VisualStudio.SolutionExplorer.OpenFile(new ProjectUtils.Project(ProjectName), "Class1.vb");
VisualStudio.Editor.GoToDefinition();
VisualStudio.Editor.GoToDefinition("Int32.vb");
VisualStudio.Editor.Verify.TextContains("Public Structure Int32");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public BasicGoToImplementation(VisualStudioInstanceFactory instanceFactory)
{
}

[WpfFact, Trait(Traits.Feature, Traits.Features.GoToImplementation)]
[WpfFact, Trait(Traits.Feature, Traits.Features.GoToImplementation), Trait(Traits.Editor, Traits.Editors.LanguageServerProtocol)]
public void SimpleGoToImplementation()
{
var project = new ProjectUtils.Project(ProjectName);
Expand All @@ -38,7 +38,7 @@ Implements IGoo
@"Interface IGoo
End Interface");
VisualStudio.Editor.PlaceCaret("Interface IGoo");
VisualStudio.Editor.GoToImplementation();
VisualStudio.Editor.GoToImplementation("FileImplementation.vb");
VisualStudio.Editor.Verify.TextContains(@"Class Implementation$$", assertCaretPosition: true);
Assert.False(VisualStudio.Shell.IsActiveTabProvisional());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -362,11 +362,17 @@ private TextSpan[] Deserialize(string[] v)
}).ToArray();
}

public void GoToDefinition()
=> _editorInProc.GoToDefinition();
public void GoToDefinition(string viewName)
{
_editorInProc.GoToDefinition();
_editorInProc.WaitForActiveView(viewName);
}

public void GoToImplementation()
=> _editorInProc.GoToImplementation();
public void GoToImplementation(string viewName)
{
_editorInProc.GoToImplementation();
_editorInProc.WaitForActiveView(viewName);
}

public void SendExplicitFocus()
=> _editorInProc.SendExplicitFocus();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,9 @@ private static Process StartNewVisualStudioProcess(string installationPath, int
// Disable background download UI to avoid toasts
Process.Start(CreateSilentStartInfo(vsRegEditExeFile, $"set \"{installationPath}\" {Settings.Default.VsRootSuffix} HKCU \"FeatureFlags\\Setup\\BackgroundDownload\" Value dword 0")).WaitForExit();

var lspRegistryValue = string.Equals(Environment.GetEnvironmentVariable("ROSLYN_LSPEDITOR"), "true", StringComparison.OrdinalIgnoreCase) ? "1" : "0";
Process.Start(CreateSilentStartInfo(vsRegEditExeFile, $"set \"{installationPath}\" {Settings.Default.VsRootSuffix} HKCU \"FeatureFlags\\Roslyn\\LSP\\Editor\" Value dword {lspRegistryValue}")).WaitForExit();

// Remove legacy experiment setting for controlling async completion to ensure it does not interfere.
// We no longer set this value, but it could be in place from an earlier test run on the same machine.
var disabledFlights = Registry.GetValue(@"HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\ABExp\LocalTest", "DisabledFlights", Array.Empty<string>()) as string[] ?? Array.Empty<string>();
Expand Down

0 comments on commit 97c54a2

Please sign in to comment.