/Release/Tests/DebuggerUITests/AttachTest.cs

http://pytools.codeplex.com · C# · 386 lines · 250 code · 74 blank · 62 comment · 18 complexity · c614a21a5337fbc33c5377bf59e85570 MD5 · raw file

  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Apache License, Version 2.0, please send an email to
  8. * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Apache License, Version 2.0.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. * ***************************************************************************/
  14. using System;
  15. using System.Linq;
  16. using System.Reflection;
  17. using System.Threading;
  18. using System.Windows.Input;
  19. using EnvDTE;
  20. using EnvDTE80;
  21. using EnvDTE90;
  22. using Microsoft.PythonTools;
  23. using Microsoft.PythonTools.Interpreter;
  24. using Microsoft.TC.TestHostAdapters;
  25. using Microsoft.TestSccPackage;
  26. using Microsoft.VisualStudio.ComponentModelHost;
  27. using Microsoft.VisualStudio.Shell;
  28. using Microsoft.VisualStudio.Shell.Interop;
  29. using Microsoft.VisualStudio.TestTools.UnitTesting;
  30. using TestUtilities;
  31. using TestUtilities.UI;
  32. using Keyboard = TestUtilities.UI.Keyboard;
  33. using Mouse = TestUtilities.UI.Mouse;
  34. using Path = System.IO.Path;
  35. using SD = System.Diagnostics;
  36. namespace DebuggerUITests {
  37. /// <summary>
  38. /// Summary description for AttachTest
  39. /// </summary>
  40. [TestClass]
  41. public class AttachTest {
  42. [ClassInitialize]
  43. public static void DoDeployment(TestContext context) {
  44. TestData.Deploy();
  45. }
  46. public AttachTest() {
  47. //
  48. // TODO: Add constructor logic here
  49. //
  50. }
  51. private TestContext testContextInstance;
  52. /// <summary>
  53. ///Gets or sets the test context which provides
  54. ///information about and functionality for the current test run.
  55. ///</summary>
  56. public TestContext TestContext {
  57. get {
  58. return testContextInstance;
  59. }
  60. set {
  61. testContextInstance = value;
  62. }
  63. }
  64. #region Additional test attributes
  65. //
  66. // You can use the following additional attributes as you write your tests:
  67. //
  68. // Use ClassInitialize to run code before running the first test in the class
  69. // [ClassInitialize()]
  70. // public static void MyClassInitialize(TestContext testContext) { }
  71. //
  72. // Use ClassCleanup to run code after all tests in a class have run
  73. // [ClassCleanup()]
  74. // public static void MyClassCleanup() { }
  75. //
  76. // Use TestInitialize to run code before running each test
  77. // [TestInitialize()]
  78. // public void MyTestInitialize() { }
  79. [TestCleanup()]
  80. public void MyTestCleanup() {
  81. VsIdeTestHostContext.Dte.Solution.Close(false);
  82. }
  83. #endregion
  84. #region Tests
  85. [TestMethod, Priority(0), TestCategory("Core")]
  86. [HostType("TC Dynamic"), DynamicHostType(typeof(VsIdeHostAdapter))]
  87. public void TestAttachBasic() {
  88. string debugSolution = TestData.GetPath(@"TestData\DebugAttach\DebugAttach.sln");
  89. string startFile = "Simple.py";
  90. Debugger2 dbg2 = (Debugger2)VsIdeTestHostContext.Dte.Debugger;
  91. SD.Process processToAttach = OpenSolutionAndLaunchFile(debugSolution, startFile, "", "");
  92. try {
  93. AttachAndWaitForMode(processToAttach, "Python Debugging", dbgDebugMode.dbgRunMode);
  94. } finally {
  95. dbg2.DetachAll();
  96. DebugProject.WaitForMode(dbgDebugMode.dbgDesignMode);
  97. if (!processToAttach.HasExited) processToAttach.Kill();
  98. }
  99. return;
  100. }
  101. [TestMethod, Priority(0), TestCategory("Core")]
  102. [HostType("TC Dynamic"), DynamicHostType(typeof(VsIdeHostAdapter))]
  103. public void TestAttachBreakImmediately() {
  104. string debugSolution = TestData.GetPath(@"TestData\DebugAttach\DebugAttach.sln");
  105. string startFile = "Simple.py";
  106. int breakLine = 22;
  107. Debugger2 dbg2 = (Debugger2)VsIdeTestHostContext.Dte.Debugger;
  108. SD.Process processToAttach = OpenSolutionAndLaunchFile(debugSolution, startFile, "", "");
  109. VsIdeTestHostContext.Dte.Debugger.Breakpoints.Add(File: startFile, Line: breakLine);
  110. try {
  111. AttachAndWaitForMode(processToAttach, "Python Debugging", dbgDebugMode.dbgBreakMode);
  112. } finally {
  113. dbg2.DetachAll();
  114. DebugProject.WaitForMode(dbgDebugMode.dbgDesignMode);
  115. if (!processToAttach.HasExited) processToAttach.Kill();
  116. }
  117. return;
  118. }
  119. [TestMethod, Priority(0), TestCategory("Core")]
  120. [HostType("TC Dynamic"), DynamicHostType(typeof(VsIdeHostAdapter))]
  121. public void TestAttachUserSetsBreakpoint() {
  122. string debugSolution = TestData.GetPath(@"TestData\DebugAttach\DebugAttach.sln");
  123. string startFile = "Simple.py";
  124. int breakLine = 22;
  125. Debugger2 dbg2 = (Debugger2)VsIdeTestHostContext.Dte.Debugger;
  126. SD.Process processToAttach = OpenSolutionAndLaunchFile(debugSolution, startFile, "", "");
  127. try {
  128. AttachAndWaitForMode(processToAttach, "Python Debugging", dbgDebugMode.dbgRunMode);
  129. dbg2.Breakpoints.Add(File: startFile, Line: breakLine);
  130. DebugProject.WaitForMode(dbgDebugMode.dbgBreakMode);
  131. } finally {
  132. dbg2.DetachAll();
  133. DebugProject.WaitForMode(dbgDebugMode.dbgDesignMode);
  134. if (!processToAttach.HasExited) processToAttach.Kill();
  135. }
  136. }
  137. [TestMethod, Priority(0), TestCategory("Core")]
  138. [HostType("TC Dynamic"), DynamicHostType(typeof(VsIdeHostAdapter))]
  139. public void TestAttachThreadsBreakAllAndSetExitFlag() {
  140. string debugSolution = TestData.GetPath(@"TestData\DebugAttach\DebugAttach.sln");
  141. string startFile = "fg.py";
  142. Debugger2 dbg2 = (Debugger2)VsIdeTestHostContext.Dte.Debugger;
  143. SD.Process processToAttach = OpenSolutionAndLaunchFile(debugSolution, startFile, "", "");
  144. try {
  145. Process2 proc = AttachAndWaitForMode(processToAttach, "Python Debugging", dbgDebugMode.dbgRunMode);
  146. dbg2.Break(WaitForBreakMode: false);
  147. DebugProject.WaitForMode(dbgDebugMode.dbgBreakMode);
  148. var x = proc.Threads.Cast<Thread2>()
  149. .SelectMany<Thread2, StackFrame>(t => t.StackFrames.Cast<StackFrame>())
  150. .SelectMany<StackFrame, Expression>(f => f.Locals.Cast<Expression>())
  151. .Where(e => e.Name == "exit_flag")
  152. .First();
  153. Assert.IsNotNull(x, "Couldn't find a frame with 'exit_flag' defined!");
  154. x.Value = "True";
  155. dbg2.Go(WaitForBreakOrEnd: false);
  156. DebugProject.WaitForMode(dbgDebugMode.dbgDesignMode);
  157. } finally {
  158. if (!processToAttach.HasExited) processToAttach.Kill();
  159. }
  160. }
  161. [TestMethod, Priority(0), TestCategory("Core")]
  162. [HostType("TC Dynamic"), DynamicHostType(typeof(VsIdeHostAdapter))]
  163. public void TestAttachThreadsBreakOneAndSetExitFlag() {
  164. string debugSolution = TestData.GetPath(@"TestData\DebugAttach\DebugAttach.sln");
  165. string startFile = "fg.py";
  166. int breakLine = 8;
  167. Debugger2 dbg2 = (Debugger2)VsIdeTestHostContext.Dte.Debugger;
  168. SD.Process processToAttach = OpenSolutionAndLaunchFile(debugSolution, startFile, "", "");
  169. try {
  170. Process2 proc = AttachAndWaitForMode(processToAttach, "Python Debugging", dbgDebugMode.dbgRunMode);
  171. dbg2.Breakpoints.Add(File: startFile, Line: breakLine);
  172. DebugProject.WaitForMode(dbgDebugMode.dbgBreakMode);
  173. dbg2.BreakpointLastHit.Delete();
  174. var x = proc.Threads.Cast<Thread2>()
  175. .SelectMany<Thread2, StackFrame>(t => t.StackFrames.Cast<StackFrame>())
  176. .SelectMany<StackFrame, Expression>(f => f.Locals.Cast<Expression>())
  177. .Where(e => e.Name == "exit_flag")
  178. .First();
  179. Assert.IsNotNull(x, "Couldn't find a frame with 'exit_flag' defined!");
  180. x.Value = "True";
  181. dbg2.Go(WaitForBreakOrEnd: false);
  182. DebugProject.WaitForMode(dbgDebugMode.dbgDesignMode);
  183. } finally {
  184. if (!processToAttach.HasExited) processToAttach.Kill();
  185. }
  186. }
  187. [TestMethod, Priority(0), TestCategory("Core")]
  188. [HostType("TC Dynamic"), DynamicHostType(typeof(VsIdeHostAdapter))]
  189. public void TestAttachLotsOfThreads() {
  190. string debugSolution = TestData.GetPath(@"TestData\DebugAttach\DebugAttach.sln");
  191. string startFile = "LotsOfThreads.py";
  192. Debugger2 dbg2 = (Debugger2)VsIdeTestHostContext.Dte.Debugger;
  193. SD.Process processToAttach = OpenSolutionAndLaunchFile(debugSolution, startFile, "", "");
  194. System.Threading.Thread.Sleep(2000);
  195. try {
  196. Process2 proc = AttachAndWaitForMode(processToAttach, "Python Debugging", dbgDebugMode.dbgRunMode);
  197. } finally {
  198. if (!processToAttach.HasExited) processToAttach.Kill();
  199. }
  200. }
  201. ///TODO: TestAttachThreadsMakingProgress
  202. /// <summary>
  203. /// See workitem http://pytools.codeplex.com/workitem/456
  204. /// </summary>
  205. /// <param name="debugSolution"></param>
  206. /// <param name="startFile"></param>
  207. /// <param name="interpreterArgs"></param>
  208. /// <param name="programArgs"></param>
  209. /// <returns></returns>
  210. #endregion
  211. #region Helper methods
  212. private static SD.Process OpenSolutionAndLaunchFile(string debugSolution, string startFile, string interpreterArgs, string programArgs) {
  213. var project = DebugProject.OpenProject(debugSolution, startFile);
  214. return LaunchFileFromProject(project, startFile, interpreterArgs, programArgs);
  215. }
  216. private static Process2 AttachAndWaitForMode(SD.Process processToAttach, object debugEngines, dbgDebugMode expectedMode) {
  217. Debugger2 dbg2 = (Debugger2)VsIdeTestHostContext.Dte.Debugger;
  218. System.Threading.Thread.Sleep(1000);
  219. Process2 result = null;
  220. Transport t = dbg2.Transports.Item("Default");
  221. bool foundit = false;
  222. foreach (Process2 p in dbg2.LocalProcesses) {
  223. if (p.ProcessID == processToAttach.Id) {
  224. foundit = true;
  225. p.Attach2(debugEngines);
  226. result = p;
  227. break;
  228. }
  229. }
  230. Assert.IsTrue(foundit, "The process to attach [{0}] could not be found in LocalProcesses (did it exit immediately?)", processToAttach.Id);
  231. DebugProject.WaitForMode(expectedMode);
  232. return result;
  233. }
  234. public static SD.Process LaunchFileFromProject(EnvDTE.Project project, string filename, string interpreterArgs, string programArgs) {
  235. var item = project.ProjectItems.Item(filename);
  236. var window = item.Open();
  237. window.Activate();
  238. var doc = item.Document;
  239. var docFN = doc.FullName;
  240. string fullFilename = Path.GetFullPath(docFN);
  241. string cmdlineArgs = String.Format("{0} \"{1}\" {2}", interpreterArgs, fullFilename, programArgs);
  242. string projectInterpreter = GetProjectInterpreterOrDefault(project);
  243. SD.Process p = SD.Process.Start(projectInterpreter, cmdlineArgs);
  244. Assert.IsNotNull(p, "Failure to start process {0} {1} {2} {3}", projectInterpreter, interpreterArgs, fullFilename, programArgs);
  245. return p;
  246. }
  247. public static string GetProjectInterpreterOrDefault(EnvDTE.Project project) {
  248. var interpreterId = (string)project.Properties.Item("InterpreterId").Value;
  249. var interpreterVersion = (string)project.Properties.Item("InterpreterVersion").Value;
  250. var args = (string)project.Properties.Item("CommandLineArguments").Value;
  251. var interpreterPath = (string)project.Properties.Item("InterpreterPath").Value;
  252. var searchPath = (string)project.Properties.Item("SearchPath").Value;
  253. string interpreter;
  254. Guid intGuid;
  255. Version intVersion;
  256. ProcessorArchitecture arch;
  257. string searchPathEnvVarName;
  258. // use the project's custom interpreter path if defined
  259. if (!String.IsNullOrWhiteSpace(interpreterPath)) {
  260. return Path.GetFullPath(interpreterPath);
  261. }
  262. // use the project's interpreter if we can find it
  263. if (Guid.TryParse(interpreterId, out intGuid) &&
  264. Version.TryParse(interpreterVersion, out intVersion) &&
  265. TryGetInterpreter(intVersion, intGuid, out interpreter, out searchPathEnvVarName, out arch)) {
  266. return Path.GetFullPath(interpreter);
  267. }
  268. // use the VS instance's default interpreter if there is one
  269. if (TryGetInterpreter(PythonToolsPackage.Instance.InterpreterOptionsPage.DefaultInterpreterVersionValue,
  270. PythonToolsPackage.Instance.InterpreterOptionsPage.DefaultInterpreterValue,
  271. out interpreter, out searchPathEnvVarName, out arch)) {
  272. return Path.GetFullPath(interpreter);
  273. }
  274. // fail
  275. Assert.Fail("There were no available interpreters. Could not launch project.");
  276. return null;
  277. }
  278. private static bool TryGetInterpreter(Version selectedVersion, Guid interpreterId, out string interpreter, out string searchPathEnv, out ProcessorArchitecture architecture) {
  279. interpreter = null;
  280. architecture = ProcessorArchitecture.None;
  281. searchPathEnv = null;
  282. var service = (IComponentModel)VsIdeTestHostContext.ServiceProvider.GetService(typeof(SComponentModel));
  283. var factories = service.GetAllPythonInterpreterFactories();
  284. foreach (var fact in factories) {
  285. if (fact.Id == interpreterId &&
  286. fact.Configuration.Version == selectedVersion) {
  287. interpreter = fact.Configuration.InterpreterPath;
  288. architecture = fact.Configuration.Architecture;
  289. searchPathEnv = fact.Configuration.PathEnvironmentVariable;
  290. break;
  291. }
  292. }
  293. if (interpreter == null) {
  294. return false;
  295. }
  296. return true;
  297. }
  298. #endregion
  299. }
  300. }
  301. ////EnvDTE80.Debugger2
  302. //var atp = app.OpenDebugAttach();
  303. //var sctpd = atp.SelectCodeTypeForDebugging();
  304. //sctpd.SetDebugSpecificCodeTypes();
  305. //foreach (var codeType in sctpd.AvailableCodeTypes.Items) {
  306. // if (codeType.Name == "Python Debugging") codeType.SetSelected();
  307. // else codeType.SetUnselected();
  308. //}
  309. //sctpd.ClickOk();
  310. //atp.SelectProcessForDebuggingByName("python.exe");
  311. //atp.ClickAttach();
  312. //DebugProject.WaitForMode(dbgDebugMode.dbgRunMode);