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 void Run() { if (!File.Exists(_settings.InterpreterPath)) { Error(Strings.Test_InterpreterDoesNotExist.FormatUI(_settings.InterpreterPath)); return; } try { DetachFromSillyManagedProcess(); var pythonPath = InitializeEnvironment(); string testList = null; // For a small set of tests, we'll pass them on the command // line. Once we exceed a certain (arbitrary) number, create // a test list on disk so that we do not overflow the // 32K argument limit. if (_tests.Length > 5) { testList = CreateTestList(); } var arguments = GetArguments(testList); //////////////////////////////////////////////////////////// // Do the test run using (var proc = ProcessOutput.Run( _settings.InterpreterPath, arguments, _settings.WorkingDirectory, _env, _showConsole, null )) { bool killed = false; DebugInfo("cd " + _settings.WorkingDirectory); DebugInfo("set " + pythonPath.Key + "=" + pythonPath.Value); DebugInfo(proc.Arguments); _connected.WaitOne(); if (proc.ExitCode.HasValue) { // Process has already exited proc.Wait(); Error(Strings.Test_FailedToStartExited); if (proc.StandardErrorLines.Any()) { foreach (var line in proc.StandardErrorLines) { Error(line); } } } if (_debugMode != PythonDebugMode.None) { try { 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 { var engines = new[] { PythonDebugEngineGuid, NativeDebugEngineGuid }; while (!_app.AttachToProcess(proc, engines)) { if (proc.Wait(TimeSpan.FromMilliseconds(500))) { break; } } } } catch (COMException ex) { Error(Strings.Test_ErrorConnecting); DebugError(ex.ToString()); try { proc.Kill(); } catch (InvalidOperationException) { // Process has already exited } killed = true; } } // https://pytools.codeplex.com/workitem/2290 // Check that proc.WaitHandle was not null to avoid crashing if // a test fails to start running. We will report failure and // send the error message from stdout/stderr. var handles = new WaitHandle[] { _cancelRequested, proc.WaitHandle, _done }; if (handles[1] == null) { killed = true; } if (!killed) { switch (WaitHandle.WaitAny(handles)) { case 0: // We've been cancelled try { proc.Kill(); } catch (InvalidOperationException) { // Process has already exited } killed = true; break; case 1: // The process has exited, give a chance for our comm channel // to be flushed... handles = new WaitHandle[] { _cancelRequested, _done }; if (WaitHandle.WaitAny(handles, 10000) != 1) { Warning(Strings.Test_NoTestFinishedNotification); } break; case 2: // We received the done event break; } } } if (File.Exists(testList)) { try { File.Delete(testList); } catch (IOException) { } } } catch (Exception e) { Error(e.ToString()); } }
private void RunTestCase( IFrameworkHandle frameworkHandle, IRunContext runContext, TestCase test, Dictionary <string, PythonProjectSettings> sourceToSettings ) { var testResult = new TestResult(test); frameworkHandle.RecordStart(test); testResult.StartTime = DateTimeOffset.Now; PythonProjectSettings 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; } var debugMode = PythonDebugMode.None; if (runContext.IsBeingDebugged && _app != null) { debugMode = settings.EnableNativeCodeDebugging ? PythonDebugMode.PythonAndNative : PythonDebugMode.PythonOnly; } var testCase = new PythonTestCase(settings, test, debugMode); var dte = _app != null?_app.GetDTE() : null; if (dte != null && debugMode != PythonDebugMode.None) { dte.Debugger.DetachAll(); } if (!File.Exists(settings.Factory.Configuration.InterpreterPath)) { frameworkHandle.SendMessage(TestMessageLevel.Error, "Interpreter path does not exist: " + settings.Factory.Configuration.InterpreterPath); return; } var env = new Dictionary <string, string>(); var pythonPathVar = settings.Factory.Configuration.PathEnvironmentVariable; var pythonPath = testCase.SearchPaths; if (!string.IsNullOrWhiteSpace(pythonPathVar)) { if (_app != null) { var settingsManager = SettingsManagerCreator.GetSettingsManager(dte); if (settingsManager != null) { var store = settingsManager.GetReadOnlySettingsStore(SettingsScope.UserSettings); if (store != null && store.CollectionExists(@"PythonTools\Options\General")) { var settingStr = store.GetString(@"PythonTools\Options\General", "ClearGlobalPythonPath", "True"); bool settingBool; if (bool.TryParse(settingStr, out settingBool) && !settingBool) { pythonPath += ";" + Environment.GetEnvironmentVariable(pythonPathVar); } } } } env[pythonPathVar] = pythonPath; } foreach (var envVar in testCase.Environment) { env[envVar.Key] = envVar.Value; } using (var proc = ProcessOutput.Run( !settings.IsWindowsApplication ? settings.Factory.Configuration.InterpreterPath : settings.Factory.Configuration.WindowsInterpreterPath, testCase.Arguments, testCase.WorkingDirectory, env, false, null )) { bool killed = false; #if DEBUG frameworkHandle.SendMessage(TestMessageLevel.Informational, "cd " + testCase.WorkingDirectory); frameworkHandle.SendMessage(TestMessageLevel.Informational, "set " + (pythonPathVar ?? "") + "=" + (pythonPath ?? "")); frameworkHandle.SendMessage(TestMessageLevel.Informational, proc.Arguments); #endif proc.Wait(TimeSpan.FromMilliseconds(500)); if (debugMode != PythonDebugMode.None) { if (proc.ExitCode.HasValue) { // Process has already exited frameworkHandle.SendMessage(TestMessageLevel.Error, "Failed to attach debugger because the process has already exited."); if (proc.StandardErrorLines.Any()) { frameworkHandle.SendMessage(TestMessageLevel.Error, "Standard error from Python:"); foreach (var line in proc.StandardErrorLines) { frameworkHandle.SendMessage(TestMessageLevel.Error, line); } } } try { if (debugMode == PythonDebugMode.PythonOnly) { string qualifierUri = string.Format("tcp://{0}@localhost:{1}", testCase.DebugSecret, testCase.DebugPort); while (!_app.AttachToProcess(proc, PythonRemoteDebugPortSupplierUnsecuredId, qualifierUri)) { if (proc.Wait(TimeSpan.FromMilliseconds(500))) { break; } } } else { var engines = new[] { PythonDebugEngineGuid, VSConstants.DebugEnginesGuids.NativeOnly_guid }; while (!_app.AttachToProcess(proc, engines)) { if (proc.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()); try { proc.Kill(); } catch (InvalidOperationException) { // Process has already exited } killed = true; } #else } catch (COMException) {