From 0fe720c50bc6a997810366c3582e3bedf02d667c Mon Sep 17 00:00:00 2001 From: dino Date: Tue, 12 May 2015 16:24:54 -0700 Subject: [PATCH] Improve mock tests add mock testing setting file for running mock tests add support for deleting via solution explorer send delete command to hierarchy node translate into proper interface calls add start of some DTE mocks Start adding some abstractions for different dialog types, add dialog message loop imporve running document table mocks Implement more missing IVisualStudioInstance functionality Update DragDropCopyCutPaste tests to use interface calls instead of using keyboard/UI directly --- Build/default.Mock.testsettings | 23 ++ .../MockVsTests/CommandTargetExtensions.cs | 5 + Common/Tests/MockVsTests/MockDTE.cs | 319 ++++++++++++++++++ Common/Tests/MockVsTests/MockDTEProperties.cs | 63 ++++ Common/Tests/MockVsTests/MockDTEProperty.cs | 94 ++++++ Common/Tests/MockVsTests/MockDTESolution.cs | 178 ++++++++++ Common/Tests/MockVsTests/MockDialog.cs | 50 +++ Common/Tests/MockVsTests/MockMessageBox.cs | 23 ++ Common/Tests/MockVsTests/MockTreeNode.cs | 173 +++++++++- Common/Tests/MockVsTests/MockVs.cs | 112 ++++-- .../MockVsTests/MockVsRunningDocumentTable.cs | 172 +++++++++- Common/Tests/MockVsTests/MockVsTests.csproj | 12 + .../MockVsTrackProjectDocuments.cs | 10 +- Common/Tests/MockVsTests/MockVsUIShell.cs | 56 ++- .../DragDropCopyCutPaste.cs | 80 ++--- .../Utilities.UI/UI/VisualStudioInstance.cs | 4 + .../Tests/Utilities/IVisualStudioInstance.cs | 1 + 17 files changed, 1280 insertions(+), 95 deletions(-) create mode 100644 Build/default.Mock.testsettings create mode 100644 Common/Tests/MockVsTests/MockDTE.cs create mode 100644 Common/Tests/MockVsTests/MockDTEProperties.cs create mode 100644 Common/Tests/MockVsTests/MockDTEProperty.cs create mode 100644 Common/Tests/MockVsTests/MockDTESolution.cs create mode 100644 Common/Tests/MockVsTests/MockDialog.cs create mode 100644 Common/Tests/MockVsTests/MockMessageBox.cs diff --git a/Build/default.Mock.testsettings b/Build/default.Mock.testsettings new file mode 100644 index 0000000000..89950fe78b --- /dev/null +++ b/Build/default.Mock.testsettings @@ -0,0 +1,23 @@ + + + Local test runs with the VS 2013 Experimental Hive + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Common/Tests/MockVsTests/CommandTargetExtensions.cs b/Common/Tests/MockVsTests/CommandTargetExtensions.cs index abf7c763d2..f816b47bca 100644 --- a/Common/Tests/MockVsTests/CommandTargetExtensions.cs +++ b/Common/Tests/MockVsTests/CommandTargetExtensions.cs @@ -55,6 +55,11 @@ public static void Tab(this IOleCommandTarget target) { target.Exec(ref guid, (int)VSConstants.VSStd2KCmdID.TAB, 0, IntPtr.Zero, IntPtr.Zero); } + public static void Delete(this IOleCommandTarget target) { + var guid = VSConstants.VSStd2K; + target.Exec(ref guid, (int)VSConstants.VSStd2KCmdID.DELETE, 0, IntPtr.Zero, IntPtr.Zero); + } + public static void Backspace(this IOleCommandTarget target) { var guid = VSConstants.VSStd2K; target.Exec(ref guid, (int)VSConstants.VSStd2KCmdID.BACKSPACE, 0, IntPtr.Zero, IntPtr.Zero); diff --git a/Common/Tests/MockVsTests/MockDTE.cs b/Common/Tests/MockVsTests/MockDTE.cs new file mode 100644 index 0000000000..78c5a3e2c3 --- /dev/null +++ b/Common/Tests/MockVsTests/MockDTE.cs @@ -0,0 +1,319 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using EnvDTE; + +namespace Microsoft.VisualStudioTools.MockVsTests { + internal class MockDTE : EnvDTE.DTE { + internal readonly MockVs _vs; + + public MockDTE(MockVs vs) { + _vs = vs; + } + + public Document ActiveDocument { + get { + throw new NotImplementedException(); + } + } + + public object ActiveSolutionProjects { + get { + throw new NotImplementedException(); + } + } + + public Window ActiveWindow { + get { + throw new NotImplementedException(); + } + } + + public AddIns AddIns { + get { + throw new NotImplementedException(); + } + } + + public DTE Application { + get { + throw new NotImplementedException(); + } + } + + public object CommandBars { + get { + throw new NotImplementedException(); + } + } + + public string CommandLineArguments { + get { + throw new NotImplementedException(); + } + } + + public Commands Commands { + get { + throw new NotImplementedException(); + } + } + + public ContextAttributes ContextAttributes { + get { + throw new NotImplementedException(); + } + } + + public Debugger Debugger { + get { + throw new NotImplementedException(); + } + } + + public vsDisplay DisplayMode { + get { + throw new NotImplementedException(); + } + + set { + throw new NotImplementedException(); + } + } + + public Documents Documents { + get { + throw new NotImplementedException(); + } + } + + public DTE DTE { + get { + return this; + } + } + + public string Edition { + get { + throw new NotImplementedException(); + } + } + + public Events Events { + get { + throw new NotImplementedException(); + } + } + + public string FileName { + get { + throw new NotImplementedException(); + } + } + + public Find Find { + get { + throw new NotImplementedException(); + } + } + + public string FullName { + get { + throw new NotImplementedException(); + } + } + + public Globals Globals { + get { + throw new NotImplementedException(); + } + } + + public ItemOperations ItemOperations { + get { + throw new NotImplementedException(); + } + } + + public int LocaleID { + get { + throw new NotImplementedException(); + } + } + + public Macros Macros { + get { + throw new NotImplementedException(); + } + } + + public DTE MacrosIDE { + get { + throw new NotImplementedException(); + } + } + + public Window MainWindow { + get { + throw new NotImplementedException(); + } + } + + public vsIDEMode Mode { + get { + throw new NotImplementedException(); + } + } + + public string Name { + get { + throw new NotImplementedException(); + } + } + + public ObjectExtenders ObjectExtenders { + get { + throw new NotImplementedException(); + } + } + + public string RegistryRoot { + get { + throw new NotImplementedException(); + } + } + + public SelectedItems SelectedItems { + get { + throw new NotImplementedException(); + } + } + + public Solution Solution { + get { + return new MockDTESolution(this); + } + } + + public SourceControl SourceControl { + get { + throw new NotImplementedException(); + } + } + + public StatusBar StatusBar { + get { + throw new NotImplementedException(); + } + } + + public bool SuppressUI { + get { + throw new NotImplementedException(); + } + + set { + throw new NotImplementedException(); + } + } + + public UndoContext UndoContext { + get { + throw new NotImplementedException(); + } + } + + public bool UserControl { + get { + throw new NotImplementedException(); + } + + set { + throw new NotImplementedException(); + } + } + + public string Version { + get { + throw new NotImplementedException(); + } + } + + public WindowConfigurations WindowConfigurations { + get { + throw new NotImplementedException(); + } + } + + public Windows Windows { + get { + throw new NotImplementedException(); + } + } + + public void ExecuteCommand(string CommandName, string CommandArgs = "") { + throw new NotImplementedException(); + } + + public object GetObject(string Name) { + throw new NotImplementedException(); + } + + public bool get_IsOpenFile(string ViewKind, string FileName) { + throw new NotImplementedException(); + } + + public Properties get_Properties(string Category, string Page) { + Properties res; + Dictionary pages; + if (_properties.TryGetValue(Category, out pages) && + pages.TryGetValue(Page, out res)) { + return res; + } + return null; + } + + public wizardResult LaunchWizard(string VSZFile, ref object[] ContextParams) { + throw new NotImplementedException(); + } + + public Window OpenFile(string ViewKind, string FileName) { + throw new NotImplementedException(); + } + + public void Quit() { + throw new NotImplementedException(); + } + + public string SatelliteDllPath(string Path, string Name) { + throw new NotImplementedException(); + } + + Dictionary> _properties = new Dictionary>() { + { + "Environment", + new Dictionary() { + { + "ProjectsAndSolution", + new MockDTEProperties() { + { "MSBuildOutputVerbosity", 2 } + } + } + } + } + }; + } +} \ No newline at end of file diff --git a/Common/Tests/MockVsTests/MockDTEProperties.cs b/Common/Tests/MockVsTests/MockDTEProperties.cs new file mode 100644 index 0000000000..2620941554 --- /dev/null +++ b/Common/Tests/MockVsTests/MockDTEProperties.cs @@ -0,0 +1,63 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + + using System; +using System.Collections; +using System.Collections.Generic; +using EnvDTE; + +namespace Microsoft.VisualStudioTools.MockVsTests { + internal class MockDTEProperties : EnvDTE.Properties { + private readonly Dictionary _properties = new Dictionary(); + + public MockDTEProperties() { + } + + public object Application { + get { + throw new NotImplementedException(); + } + } + + public int Count { + get { + return _properties.Count; + } + } + + public DTE DTE { + get { + throw new NotImplementedException(); + } + } + + public object Parent { + get { + throw new NotImplementedException(); + } + } + + public IEnumerator GetEnumerator() { + throw new NotImplementedException(); + } + + public Property Item(object index) { + return _properties[(string)index]; + } + + public void Add(string name, object value) { + _properties.Add(name, new MockDTEProperty(value)); + } + } +} \ No newline at end of file diff --git a/Common/Tests/MockVsTests/MockDTEProperty.cs b/Common/Tests/MockVsTests/MockDTEProperty.cs new file mode 100644 index 0000000000..b4bea02137 --- /dev/null +++ b/Common/Tests/MockVsTests/MockDTEProperty.cs @@ -0,0 +1,94 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using EnvDTE; + +namespace Microsoft.VisualStudioTools.MockVsTests { + internal class MockDTEProperty : Property { + private object _value; + + public MockDTEProperty(object value) { + _value = value; + } + + public object Application { + get { + throw new NotImplementedException(); + } + } + + public Properties Collection { + get { + throw new NotImplementedException(); + } + } + + public DTE DTE { + get { + throw new NotImplementedException(); + } + } + + public string Name { + get { + throw new NotImplementedException(); + } + } + + public short NumIndices { + get { + throw new NotImplementedException(); + } + } + + public object Object { + get { + throw new NotImplementedException(); + } + + set { + throw new NotImplementedException(); + } + } + + public Properties Parent { + get { + throw new NotImplementedException(); + } + } + + public object Value { + get { + return _value; + } + + set { + _value = value; + } + } + + public object get_IndexedValue(object Index1, object Index2, object Index3, object Index4) { + throw new NotImplementedException(); + } + + public void let_Value(object lppvReturn) { + throw new NotImplementedException(); + } + + public void set_IndexedValue(object Index1, object Index2, object Index3, object Index4, object Val) { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Common/Tests/MockVsTests/MockDTESolution.cs b/Common/Tests/MockVsTests/MockDTESolution.cs new file mode 100644 index 0000000000..3b01d85e9b --- /dev/null +++ b/Common/Tests/MockVsTests/MockDTESolution.cs @@ -0,0 +1,178 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections; +using EnvDTE; +using Microsoft.VisualStudio; + +namespace Microsoft.VisualStudioTools.MockVsTests { + internal class MockDTESolution : Solution { + private readonly MockDTE _dte; + + public MockDTESolution(MockDTE dte) { + _dte = dte; + } + + public AddIns AddIns { + get { + throw new NotImplementedException(); + } + } + + public int Count { + get { + throw new NotImplementedException(); + } + } + + public DTE DTE { + get { + return _dte; + } + } + + public string ExtenderCATID { + get { + throw new NotImplementedException(); + } + } + + public object ExtenderNames { + get { + throw new NotImplementedException(); + } + } + + public string FileName { + get { + throw new NotImplementedException(); + } + } + + public string FullName { + get { + throw new NotImplementedException(); + } + } + + public Globals Globals { + get { + throw new NotImplementedException(); + } + } + + public bool IsDirty { + get { + throw new NotImplementedException(); + } + + set { + throw new NotImplementedException(); + } + } + + public bool IsOpen { + get { + throw new NotImplementedException(); + } + } + + public DTE Parent { + get { + throw new NotImplementedException(); + } + } + + public Projects Projects { + get { + throw new NotImplementedException(); + } + } + + public Properties Properties { + get { + throw new NotImplementedException(); + } + } + + public bool Saved { + get { + throw new NotImplementedException(); + } + + set { + throw new NotImplementedException(); + } + } + + public SolutionBuild SolutionBuild { + get { + throw new NotImplementedException(); + } + } + + public Project AddFromFile(string FileName, bool Exclusive = false) { + throw new NotImplementedException(); + } + + public Project AddFromTemplate(string FileName, string Destination, string ProjectName, bool Exclusive = false) { + throw new NotImplementedException(); + } + + public void Close(bool SaveFirst = false) { + ErrorHandler.ThrowOnFailure(_dte._vs.Solution.Close()); + } + + public void Create(string Destination, string Name) { + throw new NotImplementedException(); + } + + public ProjectItem FindProjectItem(string FileName) { + throw new NotImplementedException(); + } + + public IEnumerator GetEnumerator() { + throw new NotImplementedException(); + } + + public object get_Extender(string ExtenderName) { + throw new NotImplementedException(); + } + + public string get_TemplatePath(string ProjectType) { + throw new NotImplementedException(); + } + + public Project Item(object index) { + throw new NotImplementedException(); + } + + public void Open(string FileName) { + throw new NotImplementedException(); + } + + public string ProjectItemsTemplatePath(string ProjectKind) { + throw new NotImplementedException(); + } + + public void Remove(Project proj) { + throw new NotImplementedException(); + } + + public void SaveAs(string FileName) { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Common/Tests/MockVsTests/MockDialog.cs b/Common/Tests/MockVsTests/MockDialog.cs new file mode 100644 index 0000000000..5670196558 --- /dev/null +++ b/Common/Tests/MockVsTests/MockDialog.cs @@ -0,0 +1,50 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Threading; +using TestUtilities; + +namespace Microsoft.VisualStudioTools.MockVsTests { + class MockDialog { + public readonly string Title; + public readonly MockVs Vs; + public int DialogResult = 0; + private AutoResetEvent _dismiss = new AutoResetEvent(false); + + public MockDialog(MockVs vs, string title) { + Title = title; + Vs = vs; + } + + public virtual void Type(string text) { + switch (text) { + case "\r": + Close((int)MessageBoxButton.Ok); + break; + default: + throw new NotImplementedException("Unhandled dialog text: " + text); + } + } + + public virtual void Run() { + Vs.RunMessageLoop(_dismiss); + } + + public virtual void Close(int result) { + DialogResult = result; + _dismiss.Set(); + } + } +} diff --git a/Common/Tests/MockVsTests/MockMessageBox.cs b/Common/Tests/MockVsTests/MockMessageBox.cs new file mode 100644 index 0000000000..5a4a2f3eb5 --- /dev/null +++ b/Common/Tests/MockVsTests/MockMessageBox.cs @@ -0,0 +1,23 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +namespace Microsoft.VisualStudioTools.MockVsTests { + class MockMessageBox : MockDialog { + public readonly string Text; + + public MockMessageBox(MockVs vs, string title, string text) : base(vs, title) { + Text = text; + } + } +} diff --git a/Common/Tests/MockVsTests/MockTreeNode.cs b/Common/Tests/MockVsTests/MockTreeNode.cs index 74611a2fd7..37bc7c0c6e 100644 --- a/Common/Tests/MockVsTests/MockTreeNode.cs +++ b/Common/Tests/MockVsTests/MockTreeNode.cs @@ -16,6 +16,7 @@ using System.IO; using System.Runtime.InteropServices; using System.Windows.Input; +using Microsoft.Internal.VisualStudio.PlatformUI; using Microsoft.VisualStudio; using Microsoft.VisualStudio.OLE.Interop; using Microsoft.VisualStudio.Shell.Interop; @@ -52,7 +53,7 @@ public void LostFocus() { public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) { if (pguidCmdGroup == VSConstants.GUID_VSStandardCommandSet97) { - switch((VSConstants.VSStd97CmdID)nCmdID) { + switch ((VSConstants.VSStd97CmdID)nCmdID) { case VSConstants.VSStd97CmdID.Rename: if ((_editLabel = _item.EditLabel) != null) { _selectionLength = 0; @@ -62,7 +63,7 @@ public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pv break; } } else if (pguidCmdGroup == VSConstants.VSStd2K) { - switch((VSConstants.VSStd2KCmdID)nCmdID) { + switch ((VSConstants.VSStd2KCmdID)nCmdID) { case VSConstants.VSStd2KCmdID.TYPECHAR: if (_editLabel != null) { if (_selectionLength != 0) { @@ -81,6 +82,9 @@ public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pv return VSConstants.S_OK; } break; + case VSConstants.VSStd2KCmdID.DELETE: + DeleteItem(); + break; } } @@ -106,6 +110,92 @@ public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pv return VSConstants.E_FAIL; } + private void DeleteItem() { + var deleteHandler = _item.Hierarchy as IVsHierarchyDeleteHandler; + int canRemoveItem = 0, canDeleteItem = 0; + if (deleteHandler != null && + ErrorHandler.Succeeded(deleteHandler.QueryDeleteItem((uint)__VSDELETEITEMOPERATION.DELITEMOP_RemoveFromProject, _item.ItemId, out canRemoveItem))) { + deleteHandler.QueryDeleteItem((uint)__VSDELETEITEMOPERATION.DELITEMOP_DeleteFromStorage, _item.ItemId, out canDeleteItem); + } + bool showSpecificMsg = ShouldShowSpecificMessage(canRemoveItem, canDeleteItem); + if (canRemoveItem != 0) { + if (canDeleteItem != 0) { + // show delete or remove dialog... + } else { + // show remove dialog... + PrmoptAndDelete( + deleteHandler, + __VSDELETEITEMOPERATION.DELITEMOP_RemoveFromProject, + "" + ); + } + + } else if (canDeleteItem != 0) { + object name; + ErrorHandler.ThrowOnFailure(_item.Hierarchy.GetProperty(_item.ItemId, (int)__VSHPROPID.VSHPROPID_Name, out name)); + string message = string.Format("'{0}' will be deleted permanently.", name); + PrmoptAndDelete( + deleteHandler, + __VSDELETEITEMOPERATION.DELITEMOP_DeleteFromStorage, + message + ); + } + } + + private void PrmoptAndDelete(IVsHierarchyDeleteHandler deleteHandler, __VSDELETEITEMOPERATION deleteType,string message) { + Guid unused = Guid.Empty; + int result; + // show delete dialog... + if (ErrorHandler.Succeeded( + _mockVs.UIShell.ShowMessageBox( + 0, + ref unused, + null, + message, + null, + 0, + OLEMSGBUTTON.OLEMSGBUTTON_OKCANCEL, + OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST, + OLEMSGICON.OLEMSGICON_WARNING, + 0, + out result + )) && result == DialogResult.OK) { + int hr = deleteHandler.DeleteItem( + (uint)deleteType, + _item.ItemId + ); + + if (ErrorHandler.Failed(hr) && hr != VSConstants.OLE_E_PROMPTSAVECANCELLED) { + _mockVs.UIShell.ReportErrorInfo(hr); + } + } + } + + private bool ShouldShowSpecificMessage(int canRemoveItem, int canDeleteItem) { + __VSDELETEITEMOPERATION op = 0; + if (canRemoveItem != 0) { + op |= __VSDELETEITEMOPERATION.DELITEMOP_RemoveFromProject; + } + if (canDeleteItem != 0) { + op |= __VSDELETEITEMOPERATION.DELITEMOP_DeleteFromStorage; + } + + IVsHierarchyDeleteHandler2 deleteHandler = _item.Hierarchy as IVsHierarchyDeleteHandler2; + if (deleteHandler != null) { + int dwShowStandardMessage; + uint pdwDelItemOp; + deleteHandler.ShowSpecificDeleteRemoveMessage( + (uint)op, + 1, + new[] { _item.ItemId }, + out dwShowStandardMessage, + out pdwDelItemOp + ); + return dwShowStandardMessage != 0; + } + return false; + } + public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText) { IOleCommandTarget target = _item.Hierarchy as IOleCommandTarget; if (target != null) { @@ -134,7 +224,86 @@ public void DragOntoThis(params ITreeNode[] source) { } public void DragOntoThis(Key modifier, params ITreeNode[] source) { + _mockVs.Invoke(() => DragOntoThisUIThread(modifier, source)); + } + + private void DragOntoThisUIThread(Key modifier, ITreeNode[] source) { + var target = _item.Hierarchy as IVsHierarchyDropDataTarget; + if (target != null) { + uint effect = 0; + uint keyState = GetKeyState(modifier); + + source[0].Select(); + for (int i = 1; i < source.Length; i++) { + source[i].AddToSelection(); + } + + MockTreeNode sourceNode = (MockTreeNode)source[0]; + var dropDataSource = (IVsHierarchyDropDataSource2)sourceNode._item.Hierarchy; + uint okEffects; + IDataObject data; + IDropSource dropSource; + ErrorHandler.ThrowOnFailure(dropDataSource.GetDropInfo(out okEffects, out data, out dropSource)); + + int hr = hr = target.DragEnter( + data, + keyState, + _item.ItemId, + ref effect + ); + + if (ErrorHandler.Succeeded(hr)) { + if (effect == 0) { + return; + } + + hr = target.DragOver(keyState, _item.ItemId, ref effect); + + if (ErrorHandler.Succeeded(hr)) { + int cancel; + ErrorHandler.ThrowOnFailure( + dropDataSource.OnBeforeDropNotify( + data, + effect, + out cancel + ) + ); + + if (cancel == 0) { + hr = target.Drop( + data, + keyState, + _item.ItemId, + ref effect + ); + } + + int dropped = 0; + if (cancel == 0 && ErrorHandler.Succeeded(hr)) { + dropped = 1; + } + ErrorHandler.ThrowOnFailure(dropDataSource.OnDropNotify(dropped, effect)); + } + } + return; + } throw new NotImplementedException(); } + + private uint GetKeyState(Key modifier) { + switch (modifier) { + case Key.LeftShift: + return MK_SHIFT; + case Key.LeftCtrl: + return MK_CONTROL; + case Key.None: + return 0; + default: + throw new NotImplementedException(); + } + } + + public const uint MK_CONTROL = 0x0008; //winuser.h + public const uint MK_SHIFT = 0x0004; } } diff --git a/Common/Tests/MockVsTests/MockVs.cs b/Common/Tests/MockVsTests/MockVs.cs index 53f515ec5b..ec92285e81 100644 --- a/Common/Tests/MockVsTests/MockVs.cs +++ b/Common/Tests/MockVsTests/MockVs.cs @@ -18,8 +18,11 @@ using System.ComponentModel.Composition.Hosting; using System.ComponentModel.Composition.Primitives; using System.ComponentModel.Design; +using System.Diagnostics; using System.IO; +using System.Linq; using System.Reflection; +using System.Runtime.ExceptionServices; using System.Runtime.InteropServices; using System.Threading; using System.Windows.Input; @@ -31,6 +34,7 @@ using Microsoft.VisualStudio.OLE.Interop; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.TextManager.Interop; using Microsoft.VisualStudio.Utilities; @@ -38,9 +42,6 @@ using TestUtilities.Mocks; namespace Microsoft.VisualStudioTools.MockVsTests { - using System.Diagnostics; - using System.Runtime.ExceptionServices; - using Microsoft.VisualStudio.Text; using Thread = System.Threading.Thread; public sealed class MockVs : IComponentModel, IDisposable, IVisualStudioInstance { @@ -71,6 +72,7 @@ public sealed class MockVs : IComponentModel, IDisposable, IVisualStudioInstance private readonly MockVsUIShellOpenDocument _shellOpenDoc = new MockVsUIShellOpenDocument(); private readonly MockVsSolutionBuildManager _slnBuildMgr = new MockVsSolutionBuildManager(); private readonly MockVsExtensibility _extensibility = new MockVsExtensibility(); + private readonly MockDTE _dte; internal IFocusable _focused; private bool _shutdown; private AutoResetEvent _uiEvent = new AutoResetEvent(false); @@ -88,6 +90,7 @@ public MockVs() { _monSel = new MockVsMonitorSelection(this); _uiHierarchy = new MockVsUIHierarchyWindow(); _rdt = new MockVsRunningDocumentTable(this); + _dte = new MockDTE(this); _serviceProvider.AddService(typeof(SVsTextManager), TextManager); _serviceProvider.AddService(typeof(SVsActivityLog), ActivityLog); _serviceProvider.AddService(typeof(SVsSettingsManager), SettingsManager); @@ -111,6 +114,7 @@ public MockVs() { _serviceProvider.AddService(typeof(SVsUIShellOpenDocument), _shellOpenDoc); _serviceProvider.AddService(typeof(SVsSolutionBuildManager), _slnBuildMgr); _serviceProvider.AddService(typeof(EnvDTE.IVsExtensibility), _extensibility); + _serviceProvider.AddService(typeof(EnvDTE.DTE), _dte); UIShell.AddToolWindow(new Guid(ToolWindowGuids80.SolutionExplorer), new MockToolWindow(new MockVsUIHierarchyWindow())); @@ -150,21 +154,10 @@ private void UIThreadWorker(object evt) { packages.Add(package); package.Initialize(); } + ((AutoResetEvent)evt).Set(); - while (!_shutdown) { - _uiEvent.WaitOne(); - Action[] events; - do { - lock (_uiEvents) { - events = _uiEvents.ToArray(); - _uiEvents.Clear(); - } - foreach (var action in events) { - action(); - } - } while (events.Length > 0); - } + RunMessageLoop(); foreach (var package in packages) { package.Dispose(); @@ -175,6 +168,32 @@ private void UIThreadWorker(object evt) { } } + internal void RunMessageLoop(AutoResetEvent dialogEvent = null) { + WaitHandle[] handles; + if (dialogEvent == null) { + handles = new[] { _uiEvent }; + } else { + handles = new[] { _uiEvent, dialogEvent }; + } + + while (!_shutdown) { + if (WaitHandle.WaitAny(handles) == 1) { + // dialog is closing... + break; + } + Action[] events; + do { + lock (_uiEvents) { + events = _uiEvents.ToArray(); + _uiEvents.Clear(); + } + foreach (var action in events) { + action(); + } + } while (events.Length > 0); + } + } + private void ThrowPendingException(bool checkThread = true) { if (!checkThread || _throwExceptionsOn == Thread.CurrentThread) { var edi = Interlocked.Exchange(ref _edi, null); @@ -397,7 +416,15 @@ private static CachedVsInfo CreateCachedVsInfo() { #endregion public ITreeNode WaitForItemRemoved(params string[] path) { - throw new NotImplementedException(); + ITreeNode item = null; + for (int i = 0; i < 400; i++) { + item = FindItem(path); + if (item == null) { + break; + } + System.Threading.Thread.Sleep(25); + } + return item; } ITreeNode IVisualStudioInstance.WaitForItem(params string[] items) { @@ -409,7 +436,11 @@ ITreeNode IVisualStudioInstance.WaitForItem(params string[] items) { } public ITreeNode FindItem(params string[] items) { - throw new NotImplementedException(); + var res = WaitForItem(items); + if (res.IsNull) { + return null; + } + return new MockTreeNode(this, res); } /// @@ -511,7 +542,7 @@ out cookie } internal void SetFocus(IFocusable res) { - Invoke(() =>SetFocusWorker(res)); + Invoke(() => SetFocusWorker(res)); } private void SetFocusWorker(IFocusable res) { @@ -565,6 +596,7 @@ private void TypeWorker(Key key) { case Key.F2: cmdTarget.Rename(); break; case Key.Enter: cmdTarget.Enter(); break; case Key.Tab: cmdTarget.Tab(); break; + case Key.Delete: cmdTarget.Delete(); break; default: throw new InvalidOperationException("Unmapped key " + key); } @@ -597,7 +629,16 @@ public void Type(string p) { Invoke(() => TypeWorker(p)); } + public void PressAndRelease(Key key, params Key[] modifier) { + throw new NotImplementedException(); + } + private void TypeWorker(string p) { + if (UIShell.Dialogs.Count != 0) { + UIShell.Dialogs.Last().Type(p); + return; + } + var cmdTarget = _focused as IOleCommandTarget; if (cmdTarget != null) { cmdTarget.Type(p); @@ -644,31 +685,47 @@ public string SolutionDirectory { } public IntPtr WaitForDialog() { - throw new NotImplementedException(); + return UIShell.WaitForDialog(); } public void WaitForDialogDismissed() { - throw new NotImplementedException(); + UIShell.WaitForDialogDismissed(); } public void AssertFileExists(params string[] path) { - throw new NotImplementedException(); + Assert.IsNotNull(FindItem(path)); + + var basePath = Path.Combine(new[] { SolutionDirectory }.Concat(path).ToArray()); + Assert.IsTrue(File.Exists(basePath), "File doesn't exist: " + basePath); } public void AssertFileDoesntExist(params string[] path) { - throw new NotImplementedException(); + Assert.IsNull(FindItem(path)); + + var basePath = Path.Combine(new[] { SolutionDirectory }.Concat(path).ToArray()); + Assert.IsFalse(File.Exists(basePath), "File exists: " + basePath); } public void AssertFolderExists(params string[] path) { - throw new NotImplementedException(); + Assert.IsNotNull(FindItem(path)); + + var basePath = Path.Combine(new[] { SolutionDirectory }.Concat(path).ToArray()); + Assert.IsTrue(Directory.Exists(basePath), "Folder doesn't exist: " + basePath); } public void AssertFolderDoesntExist(params string[] path) { - throw new NotImplementedException(); + Assert.IsNull(FindItem(path)); + + var basePath = Path.Combine(new[] { SolutionDirectory }.Concat(path).ToArray()); + Assert.IsFalse(Directory.Exists(basePath), "Folder exists: " + basePath); } public void AssertFileExistsWithContent(string content, params string[] path) { - throw new NotImplementedException(); + Assert.IsNotNull(FindItem(path)); + + var basePath = Path.Combine(new[] { SolutionDirectory }.Concat(path).ToArray()); + Assert.IsTrue(File.Exists(basePath), "File doesn't exist: " + basePath); + Assert.AreEqual(File.ReadAllText(basePath), content); } public void CloseActiveWindow(vsSaveChanges save) { @@ -705,7 +762,6 @@ public IOverwriteFile WaitForOverwriteFileDialog() { throw new NotImplementedException(); } - public IAddNewItem AddNewItem() { throw new NotImplementedException(); } @@ -720,7 +776,7 @@ public List WaitForErrorListItems(int expectedCount) { public DTE Dte { - get { throw new NotImplementedException(); } + get { return _dte; } } public void OnDispose(Action action) { diff --git a/Common/Tests/MockVsTests/MockVsRunningDocumentTable.cs b/Common/Tests/MockVsTests/MockVsRunningDocumentTable.cs index 06b673b9b8..0cb68fc67f 100644 --- a/Common/Tests/MockVsTests/MockVsRunningDocumentTable.cs +++ b/Common/Tests/MockVsTests/MockVsRunningDocumentTable.cs @@ -19,7 +19,11 @@ using Microsoft.VisualStudio.Shell.Interop; namespace Microsoft.VisualStudioTools.MockVsTests { - class MockVsRunningDocumentTable : IVsRunningDocumentTable { + class MockVsRunningDocumentTable : IVsRunningDocumentTable +#if DEV12_OR_LATER + , IVsRunningDocumentTable4 +#endif + { private readonly MockVs _vs; private readonly Dictionary _table = new Dictionary(); private readonly Dictionary _ids = new Dictionary(StringComparer.OrdinalIgnoreCase); @@ -36,7 +40,8 @@ class DocInfo { public uint ItemId; public IntPtr DocData; public uint Cookie; - public int LockCount = 1; + public int ReadLockCount = 1; + public int EditLockCount = 0; public DocInfo(_VSRDTFLAGS _VSRDTFLAGS, string pszMkDocument, IVsHierarchy pHier, uint itemid, IntPtr punkDocData, uint p) { Flags = _VSRDTFLAGS; @@ -66,8 +71,11 @@ public int FindAndLockDocument(uint dwRDTLockType, string pszMkDocument, out IVs ppunkDocData = docInfo.DocData; pdwCookie = id; docInfo.Flags = (_VSRDTFLAGS)dwRDTLockType; - if (lockType.HasFlag(_VSRDTFLAGS.RDT_ReadLock) || lockType.HasFlag(_VSRDTFLAGS.RDT_EditLock)) { - docInfo.LockCount++; + if (lockType.HasFlag(_VSRDTFLAGS.RDT_ReadLock)) { + docInfo.ReadLockCount++; + } + if (lockType.HasFlag(_VSRDTFLAGS.RDT_EditLock)) { + docInfo.EditLockCount++; } return VSConstants.S_OK; } @@ -79,17 +87,85 @@ public int FindAndLockDocument(uint dwRDTLockType, string pszMkDocument, out IVs } public int GetDocumentInfo(uint docCookie, out uint pgrfRDTFlags, out uint pdwReadLocks, out uint pdwEditLocks, out string pbstrMkDocument, out IVsHierarchy ppHier, out uint pitemid, out IntPtr ppunkDocData) { - throw new NotImplementedException(); + DocInfo docInfo; + pgrfRDTFlags = 0; + pdwReadLocks = 0; + pdwEditLocks = 0; + pbstrMkDocument = null; + ppHier = null; + pitemid = 0; + ppunkDocData = IntPtr.Zero; + if (_table.TryGetValue(docCookie, out docInfo)) { + pgrfRDTFlags = (uint)docInfo.Flags; + pdwReadLocks = (uint)docInfo.ReadLockCount; + pdwEditLocks = (uint)docInfo.EditLockCount; + pbstrMkDocument = docInfo.Document; + ppHier = docInfo.Hierarchy; + pitemid = docInfo.ItemId; + ppunkDocData = docInfo.DocData; + if (ppunkDocData != IntPtr.Zero) { + Marshal.AddRef(ppunkDocData); + } + return VSConstants.S_OK; + } + return VSConstants.E_FAIL; } public int GetRunningDocumentsEnum(out IEnumRunningDocuments ppenum) { - throw new NotImplementedException(); + ppenum = new RunningDocumentsEnum(this); + return VSConstants.S_OK; + } + + class RunningDocumentsEnum : IEnumRunningDocuments { + private readonly MockVsRunningDocumentTable _docTable; + private IEnumerator _enum; + + public RunningDocumentsEnum(MockVsRunningDocumentTable docTable) { + _docTable = docTable; + _enum = _docTable._table.Values.GetEnumerator(); + } + + public int Clone(out IEnumRunningDocuments ppenum) { + ppenum = new RunningDocumentsEnum(_docTable); + return VSConstants.S_OK; + } + + public int Next(uint celt, uint[] rgelt, out uint pceltFetched) { + pceltFetched = 0; + for (int i = 0; i < celt; i++) { + if (_enum.MoveNext()) { + rgelt[i] = _enum.Current.Cookie; + pceltFetched++; + } + if (i == celt - 1) { + return VSConstants.S_OK; + } + } + return VSConstants.S_FALSE; + } + + public int Reset() { + _enum = _docTable._table.Values.GetEnumerator(); + return VSConstants.S_OK; + } + + public int Skip(uint celt) { + for (int i = 0; i < celt && _enum.MoveNext(); i++) { + } + return VSConstants.S_OK; + } } public int LockDocument(uint grfRDTLockType, uint dwCookie) { DocInfo docInfo; if (_table.TryGetValue(dwCookie, out docInfo)) { - docInfo.LockCount++; + var lockType = (_VSRDTFLAGS)grfRDTLockType; + if (lockType.HasFlag(_VSRDTFLAGS.RDT_ReadLock)) { + docInfo.ReadLockCount++; + } + if (lockType.HasFlag(_VSRDTFLAGS.RDT_EditLock)) { + docInfo.EditLockCount++; + } return VSConstants.S_OK; } return VSConstants.E_FAIL; @@ -163,8 +239,14 @@ public int UnadviseRunningDocTableEvents(uint dwCookie) { public int UnlockDocument(uint grfRDTLockType, uint dwCookie) { DocInfo docInfo; if (_table.TryGetValue(dwCookie, out docInfo)) { - docInfo.LockCount--; - if (docInfo.LockCount == 0) { + var lockType = (_VSRDTFLAGS)grfRDTLockType; + if (lockType.HasFlag(_VSRDTFLAGS.RDT_ReadLock)) { + docInfo.ReadLockCount--; + } + if (lockType.HasFlag(_VSRDTFLAGS.RDT_EditLock)) { + docInfo.EditLockCount--; + } + if (docInfo.ReadLockCount + docInfo.EditLockCount == 0) { _ids.Remove(docInfo.Document); _table.Remove(dwCookie); ErrorHandler.ThrowOnFailure(((IVsPersistDocData)Marshal.GetObjectForIUnknown(docInfo.DocData)).Close()); @@ -178,5 +260,77 @@ public int UnlockDocument(uint grfRDTLockType, uint dwCookie) { public int UnregisterDocumentLockHolder(uint dwLHCookie) { throw new NotImplementedException(); } + +#if DEV12_OR_LATER + public uint GetRelatedSaveTreeItems(uint cookie, uint grfSave, uint celt, VSSAVETREEITEM[] rgSaveTreeItems) { + throw new NotImplementedException(); + } + + public void NotifyDocumentChangedEx(uint cookie, uint attributes) { + throw new NotImplementedException(); + } + + public bool IsDocumentDirty(uint cookie) { + throw new NotImplementedException(); + } + + public bool IsDocumentReadOnly(uint cookie) { + throw new NotImplementedException(); + } + + public void UpdateDirtyState(uint cookie) { + throw new NotImplementedException(); + } + + public void UpdateReadOnlyState(uint cookie) { + throw new NotImplementedException(); + } + + public bool IsMonikerValid(string moniker) { + return _ids.ContainsKey(moniker); + } + + public bool IsCookieValid(uint cookie) { + return _table.ContainsKey(cookie); + } + + public uint GetDocumentCookie(string moniker) { + return _ids[moniker]; + } + + public uint GetDocumentFlags(uint cookie) { + throw new NotImplementedException(); + } + + public uint GetDocumentReadLockCount(uint cookie) { + throw new NotImplementedException(); + } + + public uint GetDocumentEditLockCount(uint cookie) { + throw new NotImplementedException(); + } + + public string GetDocumentMoniker(uint cookie) { + return _table[cookie].Document; + } + + public void GetDocumentHierarchyItem(uint cookie, out IVsHierarchy hierarchy, out uint itemID) { + DocInfo docInfo; + hierarchy = null; + itemID = (uint)VSConstants.VSITEMID.Nil; + if (_table.TryGetValue(cookie, out docInfo)) { + hierarchy = docInfo.Hierarchy; + itemID = docInfo.ItemId; + } + } + + public dynamic GetDocumentData(uint cookie) { + throw new NotImplementedException(); + } + + public Guid GetDocumentProjectGuid(uint cookie) { + throw new NotImplementedException(); + } +#endif } } diff --git a/Common/Tests/MockVsTests/MockVsTests.csproj b/Common/Tests/MockVsTests/MockVsTests.csproj index 51095bfb8a..213dd4b60d 100644 --- a/Common/Tests/MockVsTests/MockVsTests.csproj +++ b/Common/Tests/MockVsTests/MockVsTests.csproj @@ -48,6 +48,12 @@ False + + True + + + True + @@ -86,6 +92,8 @@ + + @@ -97,6 +105,9 @@ + + + @@ -105,6 +116,7 @@ + diff --git a/Common/Tests/MockVsTests/MockVsTrackProjectDocuments.cs b/Common/Tests/MockVsTests/MockVsTrackProjectDocuments.cs index 9030518eef..b95ea35f88 100644 --- a/Common/Tests/MockVsTests/MockVsTrackProjectDocuments.cs +++ b/Common/Tests/MockVsTests/MockVsTrackProjectDocuments.cs @@ -93,7 +93,15 @@ public int OnQueryRemoveDirectories(IVsProject pProject, int cDirectories, strin } public int OnQueryRemoveFiles(IVsProject pProject, int cFiles, string[] rgpszMkDocuments, VSQUERYREMOVEFILEFLAGS[] rgFlags, VSQUERYREMOVEFILERESULTS[] pSummaryResult, VSQUERYREMOVEFILERESULTS[] rgResults) { - throw new NotImplementedException(); + if (pSummaryResult != null) { + pSummaryResult[0] = VSQUERYREMOVEFILERESULTS.VSQUERYREMOVEFILERESULTS_RemoveOK; + } + if (rgResults != null) { + for (int i = 0; i < cFiles; i++) { + rgResults[i] = VSQUERYREMOVEFILERESULTS.VSQUERYREMOVEFILERESULTS_RemoveOK; + } + } + return VSConstants.S_OK; } public int OnQueryRenameDirectories(IVsProject pProject, int cDirs, string[] rgszMkOldNames, string[] rgszMkNewNames, VSQUERYRENAMEDIRECTORYFLAGS[] rgFlags, VSQUERYRENAMEDIRECTORYRESULTS[] pSummaryResult, VSQUERYRENAMEDIRECTORYRESULTS[] rgResults) { diff --git a/Common/Tests/MockVsTests/MockVsUIShell.cs b/Common/Tests/MockVsTests/MockVsUIShell.cs index 0eaba433ab..017dde1ee7 100644 --- a/Common/Tests/MockVsTests/MockVsUIShell.cs +++ b/Common/Tests/MockVsTests/MockVsUIShell.cs @@ -14,6 +14,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading; using Microsoft.VisualStudio; using Microsoft.VisualStudio.Shell.Interop; @@ -22,9 +23,7 @@ namespace Microsoft.VisualStudioTools.MockVsTests { class MockVsUIShell : IVsUIShell { private readonly MockVs _instance; - private string _title, _text; - private AutoResetEvent _dismiss = new AutoResetEvent(false); - private MessageBoxButton _buttonPressed; + internal List Dialogs = new List(); private Dictionary _toolWindows = new Dictionary(); public MockVsUIShell(MockVs instance) { @@ -156,7 +155,7 @@ public int PostSetFocusMenuCommand(ref Guid pguidCmdGroup, uint nCmdID) { } public int RefreshPropertyBrowser(int dispid) { - throw new NotImplementedException(); + return VSConstants.S_OK; } public int RemoveAdjacentBFNavigationItem(RemoveBFDirection rdDir) { @@ -210,13 +209,15 @@ public int ShowContextMenu(uint dwCompRole, ref Guid rclsidActive, int nMenuId, public int ShowMessageBox(uint dwCompRole, ref Guid rclsidComp, string pszTitle, string pszText, string pszHelpFile, uint dwHelpContextID, OLEMSGBUTTON msgbtn, OLEMSGDEFBUTTON msgdefbtn, OLEMSGICON msgicon, int fSysAlert, out int pnResult) { pnResult = (int)_instance.Invoke( () => { - _title = pszTitle; - _text = pszText; - _dismiss.WaitOne(); - _title = null; - _text = null; - _dismiss = null; - return _buttonPressed; + MockDialog dialog = new MockMessageBox(_instance, pszTitle, pszText); + lock (Dialogs) { + Dialogs.Add(dialog); + } + dialog.Run(); + lock (Dialogs) { + Dialogs.RemoveAt(Dialogs.Count - 1); + } + return dialog.DialogResult; } ); return VSConstants.S_OK; @@ -235,17 +236,38 @@ public int UpdateDocDataIsDirtyFeedback(uint docCookie, int fDirty) { } internal void CheckMessageBox(MessageBoxButton button, string[] text) { - string dlgText; - while ((dlgText = _text) == null) { - System.Threading.Thread.Sleep(10); + MockMessageBox msgBox; + while ((msgBox = LastDialog()) == null) { + Thread.Sleep(10); + } + AssertUtil.Contains(msgBox.Text, text); + msgBox.Close((int)button); + } + + private T LastDialog() where T : MockDialog { + lock (Dialogs) { + if (Dialogs.Count == 0) { + return null; + } + return Dialogs.Last() as T; } - _buttonPressed = button; - AssertUtil.Contains(dlgText, text); - _dismiss.Set(); } public void AddToolWindow(Guid id, MockToolWindow toolWindow) { _toolWindows[id] = toolWindow; } + + internal void WaitForDialogDismissed() { + while (Dialogs.Count != 0) { + Thread.Sleep(10); + } + } + + internal IntPtr WaitForDialog() { + while (Dialogs.Count == 0) { + Thread.Sleep(10); + } + return new IntPtr(IntPtr.Size * Dialogs.Count); + } } } diff --git a/Common/Tests/SharedProjectTests/DragDropCopyCutPaste.cs b/Common/Tests/SharedProjectTests/DragDropCopyCutPaste.cs index 30bae35283..f3bdb04186 100644 --- a/Common/Tests/SharedProjectTests/DragDropCopyCutPaste.cs +++ b/Common/Tests/SharedProjectTests/DragDropCopyCutPaste.cs @@ -156,15 +156,15 @@ public void CutRenamePaste() { solution.ControlX(); AutomationWrapper.Select(file); - Keyboard.Type(Key.F2); - Keyboard.Type("CutRenamePasteNewName"); - Keyboard.Type(Key.Enter); + solution.Type(Key.F2); + solution.Type("CutRenamePasteNewName"); + solution.Type(Key.Enter); System.Threading.Thread.Sleep(1000); AutomationWrapper.Select(project); solution.ControlV(); - VisualStudioApp.CheckMessageBox("The source URL 'CutRenamePaste" + projectType.CodeExtension + "' could not be found."); + solution.CheckMessageBox("The source URL 'CutRenamePaste" + projectType.CodeExtension + "' could not be found."); } } } @@ -196,7 +196,7 @@ public void CutDeletePaste() { AutomationWrapper.Select(project); solution.ControlV(); - VisualStudioApp.CheckMessageBox("The item 'CutDeletePaste" + projectType.CodeExtension + "' does not exist in the project directory. It may have been moved, renamed or deleted."); + solution.CheckMessageBox("The item 'CutDeletePaste" + projectType.CodeExtension + "' does not exist in the project directory. It may have been moved, renamed or deleted."); Assert.IsNotNull(solution.FindItem("DragDropCopyCutPaste", "CutDeletePaste", "CutDeletePaste" + projectType.CodeExtension)); } @@ -232,19 +232,19 @@ private void CopyFileToFolderTooLong(MoveDelegate copier) { var projectNode = solution.WaitForItem("LFN"); AutomationWrapper.Select(projectNode); - Keyboard.PressAndRelease(Key.F10, Key.LeftCtrl, Key.LeftShift); - Keyboard.PressAndRelease(Key.D); - Keyboard.PressAndRelease(Key.Right); - Keyboard.PressAndRelease(Key.D); - Keyboard.Type("01234567891"); - Keyboard.PressAndRelease(Key.Enter); + solution.PressAndRelease(Key.F10, Key.LeftCtrl, Key.LeftShift); + solution.PressAndRelease(Key.D); + solution.PressAndRelease(Key.Right); + solution.PressAndRelease(Key.D); + solution.Type("01234567891"); + solution.PressAndRelease(Key.Enter); var folderNode = solution.WaitForItem("LFN", "01234567891"); Assert.IsNotNull(folderNode); var serverNode = solution.WaitForItem("LFN", "server" + projectType.CodeExtension); AutomationWrapper.Select(serverNode); - Keyboard.ControlC(); + solution.ControlC(); solution.ControlV(); var serverCopy = solution.WaitForItem("LFN", "server - Copy" + projectType.CodeExtension); @@ -255,7 +255,7 @@ private void CopyFileToFolderTooLong(MoveDelegate copier) { // Depending on VS version/update, the message may be: // "The filename is too long." // "The filename or extension is too long." - VisualStudioApp.CheckMessageBox(" filename ", " is too long."); + solution.CheckMessageBox(" filename ", " is too long."); } } } @@ -289,19 +289,19 @@ private void CutFileToFolderTooLong(MoveDelegate mover) { var projectNode = solution.WaitForItem("LFN"); AutomationWrapper.Select(projectNode); - Keyboard.PressAndRelease(Key.F10, Key.LeftCtrl, Key.LeftShift); - Keyboard.PressAndRelease(Key.D); - Keyboard.PressAndRelease(Key.Right); - Keyboard.PressAndRelease(Key.D); - Keyboard.Type("01234567891"); - Keyboard.PressAndRelease(Key.Enter); + solution.PressAndRelease(Key.F10, Key.LeftCtrl, Key.LeftShift); + solution.PressAndRelease(Key.D); + solution.PressAndRelease(Key.Right); + solution.PressAndRelease(Key.D); + solution.Type("01234567891"); + solution.PressAndRelease(Key.Enter); var folderNode = solution.WaitForItem("LFN", "01234567891"); Assert.IsNotNull(folderNode); var serverNode = solution.FindItem("LFN", "server" + projectType.CodeExtension); AutomationWrapper.Select(serverNode); - Keyboard.ControlC(); + solution.ControlC(); solution.ControlV(); var serverCopy = solution.WaitForItem("LFN", "server - Copy" + projectType.CodeExtension); @@ -312,7 +312,7 @@ private void CutFileToFolderTooLong(MoveDelegate mover) { // Depending on VS version/update, the message may be: // "The filename is too long." // "The filename or extension is too long." - VisualStudioApp.CheckMessageBox(" filename ", " is too long."); + solution.CheckMessageBox(" filename ", " is too long."); } } } @@ -338,15 +338,15 @@ public void CutRenamePasteFolder() { AutomationWrapper.Select(file); solution.ControlX(); - Keyboard.Type(Key.F2); - Keyboard.Type("CutRenamePasteFolderNewName"); - Keyboard.Type(Key.Enter); + solution.Type(Key.F2); + solution.Type("CutRenamePasteFolderNewName"); + solution.Type(Key.Enter); System.Threading.Thread.Sleep(1000); AutomationWrapper.Select(project); solution.ControlV(); - VisualStudioApp.CheckMessageBox("The source URL 'CutRenamePasteFolder' could not be found."); + solution.CheckMessageBox("The source URL 'CutRenamePasteFolder' could not be found."); } } } @@ -379,7 +379,7 @@ public void CopiedBeforeDragPastedAfterDrop() { Assert.AreNotEqual(null, dragFolder); AutomationWrapper.Select(file); - Keyboard.ControlC(); + solution.ControlC(); MoveByMouse( solution, @@ -467,7 +467,7 @@ public void CutFolderPasteOnSelf() { solution.WaitForItem("DragDropCopyCutPaste", "CutFolderPasteOnSelf") ); - VisualStudioApp.CheckMessageBox("Cannot move 'CutFolderPasteOnSelf'. The destination folder is the same as the source folder."); + solution.CheckMessageBox("Cannot move 'CutFolderPasteOnSelf'. The destination folder is the same as the source folder."); solution.AssertFolderExists("DragDropCopyCutPaste", "CutFolderPasteOnSelf"); solution.AssertFolderDoesntExist("DragDropCopyCutPaste", "CutFolderPasteOnSelf - Copy"); @@ -526,7 +526,7 @@ public void DragFolderOntoChild() { solution.WaitForItem("DragDropCopyCutPaste", "ParentFolder") ); - VisualStudioApp.CheckMessageBox("Cannot move 'ParentFolder'. The destination folder is a subfolder of the source folder."); + solution.CheckMessageBox("Cannot move 'ParentFolder'. The destination folder is a subfolder of the source folder."); solution.WaitForDialogDismissed(); var draggedFolder = solution.FindItem("DragDropCopyCutPaste", "ParentFolder"); @@ -627,7 +627,7 @@ public void CutFilePasteSameLocation() { solution.WaitForItem("DragDropCopyCutPaste", "CutFilePasteSameLocation" + projectType.CodeExtension) ); - VisualStudioApp.CheckMessageBox("Cannot move 'CutFilePasteSameLocation" + projectType.CodeExtension + "'. The destination folder is the same as the source folder."); + solution.CheckMessageBox("Cannot move 'CutFilePasteSameLocation" + projectType.CodeExtension + "'. The destination folder is the same as the source folder."); solution.AssertFileExists("DragDropCopyCutPaste", "CutFilePasteSameLocation" + projectType.CodeExtension); solution.AssertFileDoesntExist("DragDropCopyCutPaste", "CutFilePasteSameLocation - Copy" + projectType.CodeExtension); @@ -660,7 +660,7 @@ public void DragFolderAndFileOntoSelf() { solution.WaitForItem("DragDropCopyCutPaste", "DragFolderAndFileOntoSelf", "File" + projectType.CodeExtension) ); - VisualStudioApp.CheckMessageBox("Cannot move 'DragFolderAndFileOntoSelf'. The destination folder is the same as the source folder."); + solution.CheckMessageBox("Cannot move 'DragFolderAndFileOntoSelf'. The destination folder is the same as the source folder."); } } } @@ -725,18 +725,22 @@ public void CopyDeletePaste() { var project = solution.WaitForItem("DragDropCopyCutPaste"); AutomationWrapper.Select(file); - Keyboard.ControlC(); + solution.ControlC(); AutomationWrapper.Select(file); - Keyboard.Type(Key.Delete); + solution.Type(Key.Delete); solution.WaitForDialog(); - Keyboard.Type("\r"); + solution.Type("\r"); + + solution.WaitForDialogDismissed(); + + solution.WaitForItemRemoved("DragDropCopyCutPaste", "CopyDeletePaste", "CopyDeletePaste" + projectType.CodeExtension); AutomationWrapper.Select(project); solution.ControlV(); - VisualStudioApp.CheckMessageBox("The source URL 'CopyDeletePaste" + projectType.CodeExtension + "' could not be found."); + solution.CheckMessageBox("The source URL 'CopyDeletePaste" + projectType.CodeExtension + "' could not be found."); } } } @@ -821,7 +825,7 @@ private void MoveDuplicateFolderName(MoveDelegate mover) { solution.WaitForItem("DragDropCopyCutPaste", "DuplicateFolderName") ); - VisualStudioApp.CheckMessageBox("Cannot move the folder 'DuplicateFolderName'. A folder with that name already exists in the destination directory."); + solution.CheckMessageBox("Cannot move the folder 'DuplicateFolderName'. A folder with that name already exists in the destination directory."); } } } @@ -1438,7 +1442,7 @@ public void CopyFolderMissingItem() { ); // make sure no dialogs pop up - VisualStudioApp.CheckMessageBox("The item 'missing" + projectType.CodeExtension + "' does not exist in the project directory. It may have been moved, renamed or deleted."); + solution.CheckMessageBox("The item 'missing" + projectType.CodeExtension + "' does not exist in the project directory. It may have been moved, renamed or deleted."); solution.AssertFolderExists("DragDropCopyCutPaste", "CopyFolderMissingItem"); solution.AssertFolderDoesntExist("DragDropCopyCutPaste", "PasteFolder", "CopyFolderMissingItem"); @@ -1471,7 +1475,7 @@ public void CopyPasteMissingFile() { solution.WaitForItem("DragDropCopyCutPaste", "MissingFile" + projectType.CodeExtension) ); - VisualStudioApp.CheckMessageBox("The item 'MissingFile" + projectType.CodeExtension + "' does not exist in the project directory. It may have been moved, renamed or deleted."); + solution.CheckMessageBox("The item 'MissingFile" + projectType.CodeExtension + "' does not exist in the project directory. It may have been moved, renamed or deleted."); } } } @@ -1501,7 +1505,7 @@ public void MoveFolderExistingFile() { solution.FindItem("DragDropCopyCutPaste", "FolderCollision") ); - VisualStudioApp.CheckMessageBox("Unable to add 'FolderCollision'. A file with that name already exists."); + solution.CheckMessageBox("Unable to add 'FolderCollision'. A file with that name already exists."); } } } diff --git a/Common/Tests/Utilities.UI/UI/VisualStudioInstance.cs b/Common/Tests/Utilities.UI/UI/VisualStudioInstance.cs index 7bef211044..fb16a66355 100644 --- a/Common/Tests/Utilities.UI/UI/VisualStudioInstance.cs +++ b/Common/Tests/Utilities.UI/UI/VisualStudioInstance.cs @@ -263,6 +263,10 @@ public void ControlV() { Keyboard.ControlV(); } + public void PressAndRelease(Key key, params Key[] modifier) { + Keyboard.PressAndRelease(key, modifier); + } + public void CheckMessageBox(params string[] text) { VisualStudioApp.CheckMessageBox(text); } diff --git a/Common/Tests/Utilities/IVisualStudioInstance.cs b/Common/Tests/Utilities/IVisualStudioInstance.cs index 472265314f..9e72113eb9 100644 --- a/Common/Tests/Utilities/IVisualStudioInstance.cs +++ b/Common/Tests/Utilities/IVisualStudioInstance.cs @@ -89,5 +89,6 @@ public interface IVisualStudioInstance : IDisposable { DTE Dte { get; } void OnDispose(Action action); + void PressAndRelease(Key key, params Key[] modifier); } }