public void Run() { if (!File.Exists(_settings.InterpreterPath)) { Error(Strings.Test_InterpreterDoesNotExist.FormatUI(_settings.InterpreterPath)); return; } try { ExecutorService.DetachFromSillyManagedProcess(_app, _debugMode); 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 = TestUtils.CreateTestListFile(GetTestCases().Select(pair => pair.Key)); } var arguments = GetArguments(testList); //////////////////////////////////////////////////////////// // Do the test run _connected.Reset(); using (var proc = ProcessOutput.Run( _settings.InterpreterPath, arguments, _settings.WorkingDirectory, _env, _showConsole, null )) { bool killed = false; Info("cd " + _settings.WorkingDirectory); Info("set " + pythonPath.Key + "=" + pythonPath.Value); Info(proc.Arguments); // If there's an error in the launcher script, // it will terminate without connecting back. WaitHandle.WaitAny(new WaitHandle[] { _connected, proc.WaitHandle }); bool processConnected = _connected.WaitOne(1); if (!processConnected && 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); } } foreach (var test in GetTestCases()) { _frameworkHandle.RecordStart(test.Value); _frameworkHandle.RecordResult(new TestResult(test.Value) { Outcome = TestOutcome.Skipped, ErrorMessage = Strings.Test_NotRun }); } killed = true; } if (!killed && _debugMode != PythonDebugMode.None) { try { ExecutorService.AttachDebugger(_app, proc, _debugMode, _debugSecret, _debugPort); } 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()); } }