internal static void AttachDebugger(VisualStudioProxy app, ProcessOutput proc, PythonDebugMode debugMode, string debugSecret, int debugPort) { if (debugMode == PythonDebugMode.PythonOnly) { string qualifierUri = string.Format("tcp://{0}@localhost:{1}", debugSecret, debugPort); while (!app.AttachToProcess(proc, PythonRemoteDebugPortSupplierUnsecuredId, qualifierUri)) { if (proc.Wait(TimeSpan.FromMilliseconds(500))) { break; } } } else if (debugMode == PythonDebugMode.PythonAndNative) { var engines = new[] { PythonDebugEngineGuid, NativeDebugEngineGuid }; while (!app.AttachToProcess(proc, engines)) { if (proc.Wait(TimeSpan.FromMilliseconds(500))) { break; } } } }
public bool AttachToProcess(ProcessOutput proc, Guid portSupplier, string transportQualifierUri) { var debugger3 = (EnvDTE90.Debugger3)DTE.Debugger; var transports = debugger3.Transports; EnvDTE80.Transport transport = null; for (int i = 1; i <= transports.Count; ++i) { var t = transports.Item(i); if (Guid.Parse(t.ID) == portSupplier) { transport = t; break; } } if (transport == null) { return(false); } var processes = debugger3.GetProcesses(transport, transportQualifierUri); if (processes.Count < 1) { return(false); } // Retry the attach itself 3 times before displaying a Retry/Cancel // dialog to the user. DTE.SuppressUI = true; try { try { processes.Item(1).Attach(); return(true); } catch (COMException) { if (proc.Wait(TimeSpan.FromMilliseconds(500))) { // Process exited while we were trying return(false); } } } finally { DTE.SuppressUI = false; } // Another attempt, but display UI. processes.Item(1).Attach(); return(true); }
public bool AttachToProcess(ProcessOutput processOutput, EnvDTE.Process process, Guid[] engines = null) { // Retry the attach itself 3 times before displaying a Retry/Cancel // dialog to the user. var dte = GetDTE(); dte.SuppressUI = true; try { try { if (engines == null) { process.Attach(); } else { var process3 = process as EnvDTE90.Process3; if (process3 == null) { return(false); } process3.Attach2(engines.Select(engine => engine.ToString("B")).ToArray()); } return(true); } catch (COMException) { if (processOutput.Wait(TimeSpan.FromMilliseconds(500))) { // Process exited while we were trying return(false); } } } finally { dte.SuppressUI = false; } // Another attempt, but display UI. process.Attach(); return(true); }
private void RunTestCases(IEnumerable <TestCase> tests, IRunContext runContext, IFrameworkHandle frameworkHandle, NodejsProjectSettings settings) { // May be null, but this is handled by RunTestCase if it matters. // No VS instance just means no debugging, but everything else is // okay. if (tests.Count() == 0) { return; } using (var app = VisualStudioApp.FromEnvironmentVariable(NodejsConstants.NodeToolsProcessIdEnvironmentVariable)) { var port = 0; var nodeArgs = new List <string>(); // .njsproj file path -> project settings var sourceToSettings = new Dictionary <string, NodejsProjectSettings>(); var testObjects = new List <TestCaseObject>(); if (!File.Exists(settings.NodeExePath)) { frameworkHandle.SendMessage(TestMessageLevel.Error, "Interpreter path does not exist: " + settings.NodeExePath); return; } // All tests being run are for the same test file, so just use the first test listed to get the working dir var testInfo = new NodejsTestInfo(tests.First().FullyQualifiedName); var workingDir = Path.GetDirectoryName(CommonUtils.GetAbsoluteFilePath(settings.WorkingDir, testInfo.ModulePath)); var nodeVersion = Nodejs.GetNodeVersion(settings.NodeExePath); // We can only log telemetry when we're running in VS. // Since the required assemblies are not on disk if we're not running in VS, we have to reference them in a separate method // this way the .NET framework only tries to load the assemblies when we actually need them. if (app != null) { LogTelemetry(tests.Count(), nodeVersion, runContext.IsBeingDebugged); } foreach (var test in tests) { if (_cancelRequested.WaitOne(0)) { break; } if (settings == null) { frameworkHandle.SendMessage( TestMessageLevel.Error, $"Unable to determine interpreter to use for {test.Source}."); frameworkHandle.RecordEnd(test, TestOutcome.Failed); } var args = new List <string>(); args.AddRange(GetInterpreterArgs(test, workingDir, settings.ProjectRootDir)); // Fetch the run_tests argument for starting node.exe if not specified yet if (nodeArgs.Count == 0 && args.Count > 0) { nodeArgs.Add(args[0]); } testObjects.Add(new TestCaseObject(args[1], args[2], args[3], args[4], args[5])); } if (runContext.IsBeingDebugged && app != null) { app.GetDTE().Debugger.DetachAll(); // Ensure that --debug-brk is the first argument nodeArgs.InsertRange(0, GetDebugArgs(out port)); } _nodeProcess = ProcessOutput.Run( settings.NodeExePath, nodeArgs, settings.WorkingDir, /* env */ null, /* visible */ false, /* redirector */ new TestExecutionRedirector(this.ProcessTestRunnerEmit), /* quote args */ false); if (runContext.IsBeingDebugged && app != null) { try { //the '#ping=0' is a special flag to tell VS node debugger not to connect to the port, //because a connection carries the consequence of setting off --debug-brk, and breakpoints will be missed. var qualifierUri = string.Format("tcp://localhost:{0}#ping=0", port); while (!app.AttachToProcess(_nodeProcess, NodejsRemoteDebugPortSupplierUnsecuredId, qualifierUri)) { if (_nodeProcess.Wait(TimeSpan.FromMilliseconds(500))) { break; } } #if DEBUG } catch (COMException ex) { frameworkHandle.SendMessage(TestMessageLevel.Error, "Error occurred connecting to debuggee."); frameworkHandle.SendMessage(TestMessageLevel.Error, ex.ToString()); KillNodeProcess(); } #else } catch (COMException) {
private void RunTestCase(VisualStudioApp app, IFrameworkHandle frameworkHandle, IRunContext runContext, TestCase test, Dictionary <string, NodejsProjectSettings> sourceToSettings) { var testResult = new TestResult(test); frameworkHandle.RecordStart(test); testResult.StartTime = DateTimeOffset.Now; NodejsProjectSettings settings; if (!sourceToSettings.TryGetValue(test.Source, out settings)) { sourceToSettings[test.Source] = settings = LoadProjectSettings(test.Source); } if (settings == null) { frameworkHandle.SendMessage( TestMessageLevel.Error, "Unable to determine interpreter to use for " + test.Source); RecordEnd( frameworkHandle, test, testResult, null, "Unable to determine interpreter to use for " + test.Source, TestOutcome.Failed); return; } NodejsTestInfo testInfo = new NodejsTestInfo(test.FullyQualifiedName); List <string> args = new List <string>(); int port = 0; if (runContext.IsBeingDebugged && app != null) { app.GetDTE().Debugger.DetachAll(); args.AddRange(GetDebugArgs(settings, out port)); } var workingDir = Path.GetDirectoryName(CommonUtils.GetAbsoluteFilePath(settings.WorkingDir, testInfo.ModulePath)); args.AddRange(GetInterpreterArgs(test, workingDir, settings.ProjectRootDir)); //Debug.Fail("attach debugger"); if (!File.Exists(settings.NodeExePath)) { frameworkHandle.SendMessage(TestMessageLevel.Error, "Interpreter path does not exist: " + settings.NodeExePath); return; } lock (_syncObject) { _nodeProcess = ProcessOutput.Run( settings.NodeExePath, args, workingDir, null, false, null, false); #if DEBUG frameworkHandle.SendMessage(TestMessageLevel.Informational, "cd " + workingDir); frameworkHandle.SendMessage(TestMessageLevel.Informational, _nodeProcess.Arguments); #endif _nodeProcess.Wait(TimeSpan.FromMilliseconds(500)); if (runContext.IsBeingDebugged && app != null) { try { //the '#ping=0' is a special flag to tell VS node debugger not to connect to the port, //because a connection carries the consequence of setting off --debug-brk, and breakpoints will be missed. string qualifierUri = string.Format("tcp://localhost:{0}#ping=0", port); while (!app.AttachToProcess(_nodeProcess, NodejsRemoteDebugPortSupplierUnsecuredId, qualifierUri)) { if (_nodeProcess.Wait(TimeSpan.FromMilliseconds(500))) { break; } } #if DEBUG } catch (COMException ex) { frameworkHandle.SendMessage(TestMessageLevel.Error, "Error occurred connecting to debuggee."); frameworkHandle.SendMessage(TestMessageLevel.Error, ex.ToString()); KillNodeProcess(); } #else } catch (COMException) {
private void RunTestCase(VisualStudioApp app, IFrameworkHandle frameworkHandle, IRunContext runContext, TestCase test, Dictionary <string, NodejsProjectSettings> sourceToSettings) { var testResult = new TestResult(test); frameworkHandle.RecordStart(test); testResult.StartTime = DateTimeOffset.Now; NodejsProjectSettings settings; if (!sourceToSettings.TryGetValue(test.Source, out settings)) { sourceToSettings[test.Source] = settings = LoadProjectSettings(test.Source); } if (settings == null) { frameworkHandle.SendMessage( TestMessageLevel.Error, "Unable to determine interpreter to use for " + test.Source); RecordEnd( frameworkHandle, test, testResult, null, "Unable to determine interpreter to use for " + test.Source, TestOutcome.Failed); return; } #if DEV15 // VS 2017 doesn't install some assemblies to the GAC that are needed to work with the // debugger, and as the tests don't execute in the devenv.exe process, those assemblies // fail to load - so load them manually from PublicAssemblies. // Use the executable name, as this is only needed for the out of proc test execution // that may interact with the debugger (vstest.executionengine.x86.exe). string currentProc = Process.GetCurrentProcess().MainModule.FileName; if (Path.GetFileName(currentProc).ToLowerInvariant().Equals("vstest.executionengine.x86.exe")) { string baseDir = Path.GetDirectoryName(currentProc); string publicAssemblies = Path.Combine(baseDir, "..\\..\\..\\PublicAssemblies"); Assembly.LoadFrom(Path.Combine(publicAssemblies, "Microsoft.VisualStudio.OLE.Interop.dll")); Assembly.LoadFrom(Path.Combine(publicAssemblies, "envdte90.dll")); Assembly.LoadFrom(Path.Combine(publicAssemblies, "envdte80.dll")); Assembly.LoadFrom(Path.Combine(publicAssemblies, "envdte.dll")); } #endif NodejsTestInfo testInfo = new NodejsTestInfo(test.FullyQualifiedName); List <string> args = new List <string>(); int port = 0; if (runContext.IsBeingDebugged && app != null) { app.GetDTE().Debugger.DetachAll(); args.AddRange(GetDebugArgs(settings, out port)); } var workingDir = Path.GetDirectoryName(CommonUtils.GetAbsoluteFilePath(settings.WorkingDir, testInfo.ModulePath)); args.AddRange(GetInterpreterArgs(test, workingDir, settings.ProjectRootDir)); //Debug.Fail("attach debugger"); if (!File.Exists(settings.NodeExePath)) { frameworkHandle.SendMessage(TestMessageLevel.Error, "Interpreter path does not exist: " + settings.NodeExePath); return; } lock (_syncObject) { _nodeProcess = ProcessOutput.Run( settings.NodeExePath, args, workingDir, null, false, null, false); #if DEBUG frameworkHandle.SendMessage(TestMessageLevel.Informational, "cd " + workingDir); frameworkHandle.SendMessage(TestMessageLevel.Informational, _nodeProcess.Arguments); #endif _nodeProcess.Wait(TimeSpan.FromMilliseconds(500)); if (runContext.IsBeingDebugged && app != null) { try { //the '#ping=0' is a special flag to tell VS node debugger not to connect to the port, //because a connection carries the consequence of setting off --debug-brk, and breakpoints will be missed. string qualifierUri = string.Format(CultureInfo.InvariantCulture, "tcp://localhost:{0}#ping=0", port); while (!app.AttachToProcess(_nodeProcess, NodejsRemoteDebugPortSupplierUnsecuredId, qualifierUri)) { if (_nodeProcess.Wait(TimeSpan.FromMilliseconds(500))) { break; } } #if DEBUG } catch (COMException ex) { frameworkHandle.SendMessage(TestMessageLevel.Error, "Error occurred connecting to debuggee."); frameworkHandle.SendMessage(TestMessageLevel.Error, ex.ToString()); KillNodeProcess(); } #else } catch (COMException) {