From 7c5039453c6af7dd78d47fb372da402a083f73bb Mon Sep 17 00:00:00 2001 From: Ivan Basov Date: Wed, 10 May 2017 13:43:49 -0700 Subject: [PATCH] Migrate BasicEditAndContinue integration tests from Tao to .NET (#19381) Tests only. No ask mode approval is required. --- .../VisualBasic/BasicEditAndContinue.cs | 326 ++++++++++++++++++ .../VisualStudioIntegrationTests.csproj | 1 + .../TestUtilities/Common/Expression.cs | 23 ++ .../InProcess/Debugger_InProc.cs | 37 ++ .../TestUtilities/InProcess/Editor_InProc.cs | 39 +++ .../InProcess/LocalsWindow_InProc.cs | 30 ++ .../InProcess/VisualStudio_InProc.cs | 4 +- .../TestUtilities/IntegrationHelper.cs | 10 +- .../OutOfProcess/Debugger_OutOfProc.cs | 51 +++ .../OutOfProcess/Dialog_OutOfProc.cs | 30 ++ .../OutOfProcess/Editor_OutOfProc.Verifier.cs | 5 + .../OutOfProcess/Editor_OutOfProc.cs | 10 + .../ErrorList_OutOfProc.Verifier.cs | 5 + .../LocalsWindow_OutOfProc.Verifier.cs | 26 ++ .../OutOfProcess/LocalsWindow_OutOfProc.cs | 19 + .../TestUtilities/VisualStudioInstance.cs | 13 +- ...isualStudioIntegrationTestUtilities.csproj | 7 + 17 files changed, 629 insertions(+), 7 deletions(-) create mode 100644 src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicEditAndContinue.cs create mode 100644 src/VisualStudio/IntegrationTest/TestUtilities/Common/Expression.cs create mode 100644 src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Debugger_InProc.cs create mode 100644 src/VisualStudio/IntegrationTest/TestUtilities/InProcess/LocalsWindow_InProc.cs create mode 100644 src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Debugger_OutOfProc.cs create mode 100644 src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Dialog_OutOfProc.cs create mode 100644 src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/LocalsWindow_OutOfProc.Verifier.cs create mode 100644 src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/LocalsWindow_OutOfProc.cs diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicEditAndContinue.cs b/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicEditAndContinue.cs new file mode 100644 index 0000000000000..7643daa7d20bd --- /dev/null +++ b/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicEditAndContinue.cs @@ -0,0 +1,326 @@ +// 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 Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.VisualStudio.IntegrationTest.Utilities; +using Microsoft.VisualStudio.IntegrationTest.Utilities.Input; +using Xunit; +using ProjectUtils = Microsoft.VisualStudio.IntegrationTest.Utilities.Common.ProjectUtils; + +namespace Roslyn.VisualStudio.IntegrationTests.VisualBasic +{ + [Collection(nameof(SharedIntegrationHostFixture))] + public class BasicEditAndContinue : AbstractEditorTest + { + private const string module1FileName = "Module1.vb"; + + public BasicEditAndContinue(VisualStudioInstanceFactory instanceFactory) : base(instanceFactory) + { + VisualStudio.SolutionExplorer.CreateSolution(nameof(BasicBuild)); + var testProj = new ProjectUtils.Project("TestProj"); + VisualStudio.SolutionExplorer.AddProject(testProj, WellKnownProjectTemplates.ConsoleApplication, LanguageNames.VisualBasic); + } + + protected override string LanguageName => LanguageNames.VisualBasic; + + [Fact] + public void UpdateActiveStatementLeafNode() + { + VisualStudio.Editor.SetText(@" +Imports System +Imports System.Collections.Generic +Imports System.Linq + +Module Module1 + Sub Main() + Dim names(2) As String + names(0) = ""foo"" + names(1) = ""bar"" + + For index = 0 To names.GetUpperBound(0) + Console.WriteLine(names(index)) + Next + End Sub +End Module +"); + + VisualStudio.Workspace.WaitForAsyncOperations(FeatureAttribute.Workspace); + VisualStudio.Debugger.SetBreakPoint(module1FileName, "names(0)"); + VisualStudio.Debugger.Go(waitForBreakMode: true); + VisualStudio.Editor.ReplaceText("names(0)", "names(1)"); + VisualStudio.Debugger.StepOver(waitForBreakOrEnd: true); + VisualStudio.Debugger.CheckExpression("names(1)", "String", "\"foo\""); + VisualStudio.Debugger.StepOver(waitForBreakOrEnd: true); + VisualStudio.Debugger.CheckExpression("names(1)", "String", "\"bar\""); + } + + [Fact] + public void AddTryCatchAroundActiveStatement() + { + VisualStudio.Editor.SetText(@" +Imports System +Module Module1 + Sub Main() + Foo() + End Sub + + Private Sub Foo() + Console.WriteLine(1) + End Sub +End Module"); + + VisualStudio.Workspace.WaitForAsyncOperations(FeatureAttribute.Workspace); + VisualStudio.Debugger.SetBreakPoint(module1FileName, "Console.WriteLine(1)"); + VisualStudio.Debugger.Go(waitForBreakMode: true); + VisualStudio.Editor.ReplaceText("Console.WriteLine(1)", + @"Try +Console.WriteLine(1) +Catch ex As Exception +End Try"); + VisualStudio.Workspace.WaitForAsyncOperations(FeatureAttribute.Workspace); + VisualStudio.Debugger.StepOver(waitForBreakOrEnd: true); + VisualStudio.Editor.Verify.CurrentLineText("End Try"); + } + + [Fact] + public void EditLambdaExpression() + { + VisualStudio.Editor.SetText(@" +Imports System +Module Module1 + Private Delegate Function del(i As Integer) As Integer + + Sub Main() + Dim myDel As del = Function(x) x * x + Dim j As Integer = myDel(5) + End Sub +End Module"); + + VisualStudio.Workspace.WaitForAsyncOperations(FeatureAttribute.Workspace); + VisualStudio.Debugger.SetBreakPoint(module1FileName, "x * x", charsOffset: -1); + + VisualStudio.Debugger.Go(waitForBreakMode: true); + VisualStudio.Editor.ReplaceText("x * x", "x * 2"); + + VisualStudio.Debugger.StepOver(waitForBreakOrEnd: false); + VisualStudio.Debugger.Stop(waitForDesignMode: true); + VisualStudio.ErrorList.Verify.NoBuildErrors(); + + VisualStudio.Debugger.Go(waitForBreakMode: true); + VisualStudio.Editor.ReplaceText("x * 2", "x * x"); + VisualStudio.Debugger.StepOver(waitForBreakOrEnd: true); + VisualStudio.Debugger.Stop(waitForDesignMode: true); + VisualStudio.ErrorList.Verify.NoBuildErrors(); + } + + [Fact] + public void EnCWhileDebuggingFromImmediateWindow() + { + VisualStudio.Editor.SetText(@" +Imports System + +Module Module1 + Sub Main() + Dim x = 4 + Console.WriteLine(x) + End Sub +End Module"); + + VisualStudio.Workspace.WaitForAsyncOperations(FeatureAttribute.Workspace); + VisualStudio.Debugger.Go(waitForBreakMode: true); + VisualStudio.Debugger.SetBreakPoint(module1FileName, "Dim x", charsOffset: 1); + VisualStudio.Debugger.ExecuteStatement("Module1.Main()"); + VisualStudio.Editor.ReplaceText("x = 4", "x = 42"); + VisualStudio.Debugger.StepOver(waitForBreakOrEnd: true); + VisualStudio.Debugger.CheckExpression("x", "Integer", "42"); + VisualStudio.Debugger.ExecuteStatement("Module1.Main()"); + } + + private void SetupMultiProjectSolution() + { + var basicLibrary = new ProjectUtils.Project("BasicLibrary1"); + VisualStudio.SolutionExplorer.AddProject(basicLibrary, WellKnownProjectTemplates.ClassLibrary, LanguageNames.VisualBasic); + + var cSharpLibrary = new ProjectUtils.Project("CSharpLibrary1"); + VisualStudio.SolutionExplorer.AddProject(cSharpLibrary, WellKnownProjectTemplates.ClassLibrary, LanguageNames.CSharp); + VisualStudio.SolutionExplorer.AddFile(cSharpLibrary, "File1.cs"); + + VisualStudio.SolutionExplorer.OpenFile(basicLibrary, "Class1.vb"); + VisualStudio.Editor.SetText(@" +Imports System +Public Class Class1 + Public Sub New() + End Sub + + Public Sub PrintX(x As Integer) + Console.WriteLine(x) + End Sub +End Class +"); + + var project = new ProjectUtils.Project(ProjectName); + VisualStudio.SolutionExplorer.AddProjectReference(project, new ProjectUtils.ProjectReference("BasicLibrary1")); + VisualStudio.SolutionExplorer.OpenFile(project, module1FileName); + + VisualStudio.Editor.SetText(@" +Imports System +Imports BasicLibrary1 + +Module Module1 + Sub Main() + Dim c As New Class1() + c.PrintX(5) + End Sub +End Module +"); + + VisualStudio.Workspace.WaitForAsyncOperations(FeatureAttribute.Workspace); + } + + [Fact] + public void MultiProjectDebuggingWhereNotAllModulesAreLoaded() + { + SetupMultiProjectSolution(); + VisualStudio.Debugger.SetBreakPoint(module1FileName, "PrintX", charsOffset: 1); + VisualStudio.Debugger.Go(waitForBreakMode: true); + VisualStudio.Editor.ReplaceText("5", "42"); + VisualStudio.Debugger.StepOver(waitForBreakOrEnd: false); + VisualStudio.ErrorList.Verify.NoErrors(); + } + + [Fact] + public void DocumentStateTrackingReadonlyInRunMode() + { + SetupMultiProjectSolution(); + var project = new ProjectUtils.Project(ProjectName); + var basicLibrary = new ProjectUtils.Project("BasicLibrary1"); + var cSharpLibrary = new ProjectUtils.Project("CSharpLibrary1"); + + VisualStudio.Editor.SetText(@" +Imports System +Imports BasicLibrary1 +Module Module1 + Sub Main() + Console.Read() + End Sub +End Module +"); + VisualStudio.Workspace.WaitForAsyncOperations(FeatureAttribute.Workspace); + VisualStudio.Debugger.Go(waitForBreakMode: false); + VisualStudio.ActivateMainWindow(skipAttachingThreads: true); + VisualStudio.SolutionExplorer.OpenFile(project, module1FileName); + + VisualStudio.SendKeys.Send(VirtualKey.T); + string editAndContinueDialogName = "Edit and Continue"; + VisualStudio.Dialog.VerifyOpen(editAndContinueDialogName); + VisualStudio.Dialog.Click(editAndContinueDialogName, "OK"); + VisualStudio.Dialog.VerifyClosed(editAndContinueDialogName); + VisualStudio.Editor.Verify.IsProjectItemDirty(expectedValue: false); + + // This module is referred by the loaded module, but not used. So this will not be loaded + VisualStudio.SolutionExplorer.OpenFile(basicLibrary, "Class1.vb"); + VisualStudio.Workspace.WaitForAsyncOperations(FeatureAttribute.Workspace); + VisualStudio.SendKeys.Send(VirtualKey.T); + VisualStudio.Dialog.VerifyOpen(editAndContinueDialogName); + VisualStudio.Dialog.Click(editAndContinueDialogName, "OK"); + VisualStudio.Dialog.VerifyClosed(editAndContinueDialogName); + VisualStudio.Editor.Verify.IsProjectItemDirty(expectedValue: false); + + // This module is not referred by the loaded module. this will not be loaded + VisualStudio.SolutionExplorer.OpenFile(cSharpLibrary, "File1.cs"); + VisualStudio.Workspace.WaitForAsyncOperations(FeatureAttribute.Workspace); + VisualStudio.SendKeys.Send(VirtualKey.T); + + string microsoftVisualStudionDialogName = "Microsoft Visual Studio"; + VisualStudio.Dialog.VerifyOpen(microsoftVisualStudionDialogName); + VisualStudio.Dialog.Click(microsoftVisualStudionDialogName, "OK"); + VisualStudio.Dialog.VerifyClosed(microsoftVisualStudionDialogName); + VisualStudio.Editor.Verify.IsProjectItemDirty(expectedValue: false); + } + + [Fact] + public void LocalsWindowUpdatesAfterLocalGetsItsTypeUpdatedDuringEnC() + { + VisualStudio.Editor.SetText(@" +Imports System +Module Module1 + Sub Main() + Dim foo As String = ""abc"" + Console.WriteLine(foo) + End Sub +End Module +"); + VisualStudio.Workspace.WaitForAsyncOperations(FeatureAttribute.Workspace); + VisualStudio.Debugger.SetBreakPoint(module1FileName, "End Sub"); + VisualStudio.Debugger.Go(waitForBreakMode: true); + VisualStudio.Editor.ReplaceText("Dim foo As String = \"abc\"", "Dim foo As Single = 10"); + VisualStudio.Editor.SelectTextInCurrentDocument("Sub Main()"); + VisualStudio.Debugger.SetNextStatement(); + VisualStudio.Debugger.Go(waitForBreakMode: true); + + VisualStudio.LocalsWindow.Verify.CheckEntry("foo", "Single", "10"); + } + + [Fact] + public void LocalsWindowUpdatesCorrectlyDuringEnC() + { + VisualStudio.Editor.SetText(@" +Imports System + +Module Module1 + Sub Main() + bar(5) + End Sub + + Function bar(ByVal moo As Long) As Decimal + Dim iInt As Integer = 0 + Dim lLng As Long = 5 + + iInt += 30 + Return 4 + End Function +End Module +"); + VisualStudio.Workspace.WaitForAsyncOperations(FeatureAttribute.Workspace); + VisualStudio.Debugger.SetBreakPoint(module1FileName, "Function bar(ByVal moo As Long) As Decimal"); + VisualStudio.Debugger.Go(waitForBreakMode: true); + VisualStudio.Editor.ReplaceText("Dim lLng As Long = 5", "Dim lLng As Long = 444"); + VisualStudio.Debugger.SetBreakPoint(module1FileName, "Return 4"); + VisualStudio.Debugger.Go(waitForBreakMode: true); + + VisualStudio.LocalsWindow.Verify.CheckEntry("bar", "Decimal", "0"); + VisualStudio.LocalsWindow.Verify.CheckEntry("moo", "Long", "5"); + VisualStudio.LocalsWindow.Verify.CheckEntry("iInt", "Integer", "30"); + VisualStudio.LocalsWindow.Verify.CheckEntry("lLng", "Long", "444"); + } + + [Fact] + public void WatchWindowUpdatesCorrectlyDuringEnC() + { + VisualStudio.Editor.SetText(@" +Imports System + +Module Module1 + Sub Main() + Dim iInt As Integer = 0 + System.Diagnostics.Debugger.Break() + End Sub +End Module +"); + + VisualStudio.Workspace.WaitForAsyncOperations(FeatureAttribute.Workspace); + VisualStudio.Debugger.Go(waitForBreakMode: true); + + VisualStudio.Debugger.CheckExpression("iInt", "Integer", "0"); + + VisualStudio.Editor.ReplaceText("System.Diagnostics.Debugger.Break()", @"iInt = 5 +System.Diagnostics.Debugger.Break()"); + + VisualStudio.Editor.SelectTextInCurrentDocument("iInt = 5"); + VisualStudio.Debugger.SetNextStatement(); + VisualStudio.Debugger.Go(waitForBreakMode: true); + + VisualStudio.Debugger.CheckExpression("iInt", "Integer", "5"); + } + } +} \ No newline at end of file diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/VisualStudioIntegrationTests.csproj b/src/VisualStudio/IntegrationTest/IntegrationTests/VisualStudioIntegrationTests.csproj index 94fe76840afc8..0529e228bc986 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/VisualStudioIntegrationTests.csproj +++ b/src/VisualStudio/IntegrationTest/IntegrationTests/VisualStudioIntegrationTests.csproj @@ -64,6 +64,7 @@ + diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/Common/Expression.cs b/src/VisualStudio/IntegrationTest/TestUtilities/Common/Expression.cs new file mode 100644 index 0000000000000..30e6235a3872a --- /dev/null +++ b/src/VisualStudio/IntegrationTest/TestUtilities/Common/Expression.cs @@ -0,0 +1,23 @@ +// 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; + +namespace Microsoft.VisualStudio.IntegrationTest.Utilities.Common +{ + [Serializable] + public class Expression + { + public string Name { get; set; } + + public string Type { get; set; } + + public string Value { get; set; } + + public Expression(EnvDTE.Expression input) + { + Name = input.Name; + Type = input.Type; + Value = input.Value; + } + } +} \ No newline at end of file diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Debugger_InProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Debugger_InProc.cs new file mode 100644 index 0000000000000..ccbda1f1747a1 --- /dev/null +++ b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Debugger_InProc.cs @@ -0,0 +1,37 @@ +// 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 EnvDTE; + +namespace Microsoft.VisualStudio.IntegrationTest.Utilities.InProcess +{ + internal class Debugger_InProc : InProcComponent + { + private readonly Debugger _debugger; + + private Debugger_InProc() + { + _debugger = GetDTE().Debugger; + } + + public static Debugger_InProc Create() + => new Debugger_InProc(); + + public void SetBreakPoint(string fileName, int lineNumber, int columnIndex) + { + // Need to increment the line number because editor line numbers starts from 0 but the debugger ones starts from 1. + _debugger.Breakpoints.Add(File: fileName, Line: lineNumber + 1, Column: columnIndex); + } + + public void Go(bool waitForBreakMode) => _debugger.Go(waitForBreakMode); + + public void StepOver(bool waitForBreakOrEnd) => _debugger.StepOver(waitForBreakOrEnd); + + public void Stop(bool waitForDesignMode) => _debugger.Stop(WaitForDesignMode: waitForDesignMode); + + public void SetNextStatement() => _debugger.SetNextStatement(); + + public void ExecuteStatement(string statement) => _debugger.ExecuteStatement(statement); + + public Common.Expression GetExpression(string expressionText) => new Common.Expression(_debugger.GetExpression(expressionText)); + } +} \ No newline at end of file diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs index a9337b07b26ba..8b57ac5cd43fd 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs @@ -78,6 +78,9 @@ public void WaitForActiveView(string expectedView) public void Activate() => GetDTE().ActiveDocument.Activate(); + public bool IsProjectItemDirty() + => GetDTE().ActiveDocument.ProjectItem.IsDirty; + public string GetText() => ExecuteOnActiveView(view => view.TextSnapshot.GetText()); @@ -89,6 +92,21 @@ public void SetText(string text) view.TextBuffer.Replace(replacementSpan, text); }); + public void SelectText(string text) + { + PlaceCaret(text, charsOffset: -1, occurrence: 0, extendSelection: false, selectBlock: false); + PlaceCaret(text, charsOffset: 0, occurrence: 0, extendSelection: true, selectBlock: false); + } + + public void ReplaceText(string oldText, string newText) + => ExecuteOnActiveView(view => + { + var textSnapshot = view.TextSnapshot; + SelectText(oldText); + var replacementSpan = new SnapshotSpan(textSnapshot, view.Selection.Start.Position, view.Selection.End.Position - view.Selection.Start.Position); + view.TextBuffer.Replace(replacementSpan, newText); + }); + public string GetCurrentLineText() => ExecuteOnActiveView(view => { @@ -99,6 +117,20 @@ public string GetCurrentLineText() return line.GetText(); }); + public int GetLine() + => ExecuteOnActiveView(view => + { + view.Caret.Position.BufferPosition.GetLineAndColumn(out int lineNumber, out int columnIndex); + return lineNumber; + }); + + public int GetColumn() + => ExecuteOnActiveView(view => + { + view.Caret.Position.BufferPosition.GetLineAndColumn(out int lineNumber, out int columnIndex); + return columnIndex; + }); + public string GetLineTextBeforeCaret() => ExecuteOnActiveView(view => { @@ -670,5 +702,12 @@ public int[] GetTagSpans(string tagId) return matchingTags.Select(t => t.Span.GetSpans(view.TextBuffer).Single().Span.ToTextSpan()).SelectMany(t => new List { t.Start, t.Length }).ToArray(); }); + + public void SendExplicitFocus() + => InvokeOnUIThread(() => + { + var view = GetActiveVsTextView(); + view.SendExplicitFocus(); + }); } } diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/LocalsWindow_InProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/LocalsWindow_InProc.cs new file mode 100644 index 0000000000000..dc952b1ede9f5 --- /dev/null +++ b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/LocalsWindow_InProc.cs @@ -0,0 +1,30 @@ +// 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 EnvDTE80; + +namespace Microsoft.VisualStudio.IntegrationTest.Utilities.InProcess +{ + internal class LocalsWindow_InProc : InProcComponent + { + public static LocalsWindow_InProc Create() => new LocalsWindow_InProc(); + + public Common.Expression GetEntry(string entryName) + { + var dte = ((DTE2)GetDTE()); + if (dte.Debugger.CurrentStackFrame != null) // Ensure that debugger is running + { + EnvDTE.Expressions locals = dte.Debugger.CurrentStackFrame.Locals; + foreach (EnvDTE.Expression local in locals) + { + if (local.Name == entryName) + { + return new Common.Expression(local); + } + } + } + + throw new Exception($"Could not find the local named {entryName}."); + } + } +} \ No newline at end of file diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/VisualStudio_InProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/VisualStudio_InProc.cs index 58d6202387d01..3066e17b38ca9 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/VisualStudio_InProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/VisualStudio_InProc.cs @@ -47,7 +47,7 @@ public string[] GetAvailableCommands() return result.ToArray(); } - public void ActivateMainWindow() + public void ActivateMainWindow(bool skipAttachingThreads = false) => InvokeOnUIThread(() => { var dte = GetDTE(); @@ -60,7 +60,7 @@ public void ActivateMainWindow() Debug.WriteLine($"DTE.MainWindow.HWnd = {activeVisualStudioWindow}"); } - IntegrationHelper.SetForegroundWindow(activeVisualStudioWindow); + IntegrationHelper.SetForegroundWindow(activeVisualStudioWindow, skipAttachingThreads); }); public int GetErrorListErrorCount() diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/IntegrationHelper.cs b/src/VisualStudio/IntegrationTest/TestUtilities/IntegrationHelper.cs index 5aeb931844402..30aca200a733d 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/IntegrationHelper.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/IntegrationHelper.cs @@ -217,7 +217,7 @@ public static void KillProcess(string processName) } } - public static void SetForegroundWindow(IntPtr window) + public static void SetForegroundWindow(IntPtr window, bool skipAttachingThread = false) { var foregroundWindow = GetForegroundWindow(); @@ -233,8 +233,12 @@ public static void SetForegroundWindow(IntPtr window) try { - // Attach the thread inputs so that 'SetActiveWindow' and 'SetFocus' work - threadInputsAttached = AttachThreadInput(currentThreadId, activeThreadId); + // No need to re-attach threads in case when VS initializaed an UI thread for a debugged application. + if (!skipAttachingThread) + { + // Attach the thread inputs so that 'SetActiveWindow' and 'SetFocus' work + threadInputsAttached = AttachThreadInput(currentThreadId, activeThreadId); + } // Make the window a top-most window so it will appear above any existing top-most windows NativeMethods.SetWindowPos(window, (IntPtr)NativeMethods.HWND_TOPMOST, 0, 0, 0, 0, (NativeMethods.SWP_NOSIZE | NativeMethods.SWP_NOMOVE)); diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Debugger_OutOfProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Debugger_OutOfProc.cs new file mode 100644 index 0000000000000..91987fb488043 --- /dev/null +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Debugger_OutOfProc.cs @@ -0,0 +1,51 @@ +// 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 Microsoft.VisualStudio.IntegrationTest.Utilities.InProcess; +using Xunit; + +namespace Microsoft.VisualStudio.IntegrationTest.Utilities.OutOfProcess +{ + /// + /// Provides a means of interacting with the Visual Studio debugger by remoting calls into Visual Studio. + /// + public partial class Debugger_OutOfProc : OutOfProcComponent + { + private readonly Debugger_InProc _debuggerInProc; + private readonly VisualStudioInstance _instance; + + public Debugger_OutOfProc(VisualStudioInstance visualStudioInstance) : base(visualStudioInstance) + { + _instance = visualStudioInstance; + _debuggerInProc = CreateInProcComponent(visualStudioInstance); + } + + public void SetBreakPoint(string fileName, int lineNumber, int columnIndex) => + _debuggerInProc.SetBreakPoint(fileName, lineNumber, columnIndex); + + public void SetBreakPoint(string fileName, string text, int charsOffset = 0) + { + _instance.Editor.SelectTextInCurrentDocument(text); + int lineNumber = _instance.Editor.GetLine(); + int columnIndex = _instance.Editor.GetColumn(); + + SetBreakPoint(fileName, lineNumber, columnIndex + charsOffset); + } + + public void Go(bool waitForBreakMode) => _debuggerInProc.Go(waitForBreakMode); + + public void StepOver(bool waitForBreakOrEnd) => _debuggerInProc.StepOver(waitForBreakOrEnd); + + public void Stop(bool waitForDesignMode) => _debuggerInProc.Stop(waitForDesignMode); + + public void SetNextStatement() => _debuggerInProc.SetNextStatement(); + + public void ExecuteStatement(string statement) => _debuggerInProc.ExecuteStatement(statement); + + public void CheckExpression(string expressionText, string expectedType, string expectedValue) + { + var entry = _debuggerInProc.GetExpression(expressionText); + Assert.Equal(expectedType, entry.Type); + Assert.Equal(expectedValue, entry.Value); + } + } +} \ No newline at end of file diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Dialog_OutOfProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Dialog_OutOfProc.cs new file mode 100644 index 0000000000000..bf3c528a6c01e --- /dev/null +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Dialog_OutOfProc.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.VisualStudio.IntegrationTest.Utilities.OutOfProcess +{ + public class Dialog_OutOfProc : OutOfProcComponent + { + public Dialog_OutOfProc(VisualStudioInstance visualStudioInstance) + : base(visualStudioInstance) + { + } + + public void VerifyOpen(string dialogName) + { + // FindDialog will wait until the dialog is open, so the return value is unused. + DialogHelpers.FindDialogByName(GetMainWindowHWnd(), dialogName, isOpen: true); + } + + public void VerifyClosed(string dialogName) + { + // FindDialog will wait until the dialog is closed, so the return value is unused. + DialogHelpers.FindDialogByName(GetMainWindowHWnd(), dialogName, isOpen: false); + } + + public void Click(string dialogName, string buttonName) + => DialogHelpers.PressButtonWithNameFromDialogWithName(GetMainWindowHWnd(), dialogName, buttonName); + + private int GetMainWindowHWnd() + => VisualStudioInstance.Shell.GetHWnd(); + } +} \ No newline at end of file diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.Verifier.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.Verifier.cs index 72bc88f457edd..3f98699bb715a 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.Verifier.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.Verifier.cs @@ -194,6 +194,11 @@ public void ErrorTags(params string[] expectedTags) var actualTags = _textViewWindow.GetErrorTags(); Assert.Equal(expectedTags, actualTags); } + + public void IsProjectItemDirty(bool expectedValue) + { + Assert.Equal(expectedValue, _textViewWindow._editorInProc.IsProjectItemDirty()); + } } } } diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs index 0b68f0c090d8c..a96117770d89f 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs @@ -127,12 +127,19 @@ public void SelectTextInCurrentDocument(string text) PlaceCaret(text, charsOffset: 0, occurrence: 0, extendSelection: true, selectBlock: false); } + public int GetLine() => _editorInProc.GetLine(); + + public int GetColumn() => _editorInProc.GetColumn(); + public void DeleteText(string text) { SelectTextInCurrentDocument(text); SendKeys(VirtualKey.Delete); } + public void ReplaceText(string oldText, string newText) + => _editorInProc.ReplaceText(oldText, newText); + public bool IsCaretOnScreen() => _editorInProc.IsCaretOnScreen(); @@ -324,5 +331,8 @@ public void GoToDefinition() public void GoToImplementation() => _editorInProc.GoToImplementation(); + + public void SendExplicitFocus() + => _editorInProc.SendExplicitFocus(); } } \ No newline at end of file diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/ErrorList_OutOfProc.Verifier.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/ErrorList_OutOfProc.Verifier.cs index 6159b9d754672..b3598f2cc61bf 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/ErrorList_OutOfProc.Verifier.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/ErrorList_OutOfProc.Verifier.cs @@ -21,6 +21,11 @@ public Verifier(ErrorList_OutOfProc errorList, VisualStudioInstance instance) public void NoBuildErrors() { _instance.SolutionExplorer.BuildSolution(waitForBuildToFinish: true); + NoErrors(); + } + + public void NoErrors() + { Assert.Equal(0, _errorList.GetErrorListErrorCount()); } } diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/LocalsWindow_OutOfProc.Verifier.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/LocalsWindow_OutOfProc.Verifier.cs new file mode 100644 index 0000000000000..43b3512651eff --- /dev/null +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/LocalsWindow_OutOfProc.Verifier.cs @@ -0,0 +1,26 @@ +// 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 Xunit; + +namespace Microsoft.VisualStudio.IntegrationTest.Utilities.OutOfProcess +{ + public partial class LocalsWindow_OutOfProc + { + public class Verifier + { + private readonly LocalsWindow_OutOfProc _localsWindow; + + public Verifier(LocalsWindow_OutOfProc localsWindow) + { + _localsWindow = localsWindow; + } + + public void CheckEntry(string entryName, string expectedType, string expectedValue) + { + var entry =_localsWindow._localsWindowInProc.GetEntry(entryName); + Assert.Equal(expectedType, entry.Type); + Assert.Equal(expectedValue, entry.Value); + } + } + } +} \ No newline at end of file diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/LocalsWindow_OutOfProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/LocalsWindow_OutOfProc.cs new file mode 100644 index 0000000000000..f9bf5ded7f801 --- /dev/null +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/LocalsWindow_OutOfProc.cs @@ -0,0 +1,19 @@ +// 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 Microsoft.VisualStudio.IntegrationTest.Utilities.InProcess; + +namespace Microsoft.VisualStudio.IntegrationTest.Utilities.OutOfProcess +{ + public partial class LocalsWindow_OutOfProc : OutOfProcComponent + { + public Verifier Verify { get; } + + private readonly LocalsWindow_InProc _localsWindowInProc; + + public LocalsWindow_OutOfProc(VisualStudioInstance visualStudioInstance) : base(visualStudioInstance) + { + _localsWindowInProc = CreateInProcComponent(visualStudioInstance); + Verify = new Verifier(this); + } + } +} diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/VisualStudioInstance.cs b/src/VisualStudio/IntegrationTest/TestUtilities/VisualStudioInstance.cs index 3c0ca3f7d88c5..c06bb58536ecf 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/VisualStudioInstance.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/VisualStudioInstance.cs @@ -25,6 +25,10 @@ public class VisualStudioInstance public CSharpInteractiveWindow_OutOfProc InteractiveWindow { get; } + public Debugger_OutOfProc Debugger { get; } + + public Dialog_OutOfProc Dialog { get; } + public Editor_OutOfProc Editor { get; } public EncapsulateField_OutOfProc EncapsulateField { get; } @@ -39,6 +43,8 @@ public class VisualStudioInstance public InlineRenameDialog_OutOfProc InlineRenameDialog { get; set; } + public LocalsWindow_OutOfProc LocalsWindow { get; set; } + public PreviewChangesDialog_OutOfProc PreviewChangesDialog { get; } public SendKeys SendKeys { get; } @@ -91,6 +97,8 @@ public VisualStudioInstance(Process hostProcess, DTE dte, ImmutableHashSet(Type type, string methodName) return (T)Activator.GetObject(typeof(T), $"{_integrationService.BaseUri}/{objectUri}"); } - public void ActivateMainWindow() - => _inProc.ActivateMainWindow(); + public void ActivateMainWindow(bool skipAttachingThreads = false) + => _inProc.ActivateMainWindow(skipAttachingThreads); public void WaitForApplicationIdle() => _inProc.WaitForApplicationIdle(); diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/VisualStudioIntegrationTestUtilities.csproj b/src/VisualStudio/IntegrationTest/TestUtilities/VisualStudioIntegrationTestUtilities.csproj index 14769b8476d50..d621c7c85d0ac 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/VisualStudioIntegrationTestUtilities.csproj +++ b/src/VisualStudio/IntegrationTest/TestUtilities/VisualStudioIntegrationTestUtilities.csproj @@ -28,15 +28,18 @@ + + + @@ -54,11 +57,15 @@ + + + +