private static void AttachIfDebugging(Process targetVs) { if (!Debugger.IsAttached) { return; } // We are debugging tests, so attach the debugger to VS var selfId = Process.GetCurrentProcess().Id; foreach (var p in Process.GetProcessesByName("devenv")) { if (p.Id == targetVs.Id) { continue; } using (VisualStudioApp vs = VisualStudioApp.FromProcessId(p.Id)) { EnvDTE.DTE dte; try { dte = vs.GetDTE(); } catch (InvalidOperationException) { // DTE is not available, which means VS has not been running continue; } if (dte.Debugger.CurrentMode == EnvDTE.dbgDebugMode.dbgDesignMode) { // Not the correct VS continue; } foreach (EnvDTE.Process dp in dte.Debugger.DebuggedProcesses) { if (dp.ProcessID == selfId) { // This is the correct VS, so attach and return. vs.AttachToProcess(targetVs, null); return; } } } } }
public static IInterpreterOptionsService GetService(VisualStudioApp app) { var provider = new MockExportProvider(); var container = new CompositionContainer( new AssemblyCatalog(typeof(IInterpreterOptionsService).Assembly), provider ); if (app != null) { var sp = new ServiceProvider(app.GetDTE() as IOleServiceProvider); provider.SetExport(typeof(SVsServiceProvider), () => sp); } return(container.GetExportedValue <IInterpreterOptionsService>()); }
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( 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, _interpreterService); } 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) {
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) {
public void StartOrRestart( string devenvExe, string devenvArguments, string testDataRoot, string tempRoot ) { lock (_lock) { var settings = $"{devenvExe ?? ""};{devenvArguments ?? ""};{testDataRoot ?? ""};{tempRoot ?? ""}"; if (_vs != null && _app != null) { if (_currentSettings == settings) { return; } Console.WriteLine("Restarting VS because settings have changed"); } _currentSettings = settings; CloseCurrentInstance(); var psi = new ProcessStartInfo { FileName = devenvExe, Arguments = devenvArguments, ErrorDialog = false, UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true }; psi.Environment["_PTVS_UI_TEST"] = "1"; if (!string.IsNullOrEmpty(testDataRoot)) { psi.Environment["_TESTDATA_ROOT_PATH"] = testDataRoot; } if (!string.IsNullOrEmpty(tempRoot)) { psi.Environment["_TESTDATA_TEMP_PATH"] = tempRoot; } _vs = Process.Start(psi); if (!NativeMethods.AssignProcessToJobObject(_jobObject, _vs.Handle)) { try { _vs.Kill(); } catch (Exception) { } _vs.Dispose(); throw new InvalidOperationException("Failed to add VS to our job object"); } // Forward console output to our own output, which will // be captured by the test runner. _vs.OutputDataReceived += (s, e) => { if (e.Data != null) { Console.WriteLine(e.Data); } }; _vs.ErrorDataReceived += (s, e) => { if (e.Data != null) { Console.Error.WriteLine(e.Data); } }; _vs.BeginOutputReadLine(); _vs.BeginErrorReadLine(); // Always allow at least five seconds to start Thread.Sleep(5000); if (_vs.HasExited) { throw new InvalidOperationException("Failed to start VS"); } _app = VisualStudioApp.FromProcessId(_vs.Id); var stopAt = DateTime.Now.AddSeconds(60); EnvDTE.DTE dte = null; while (DateTime.Now < stopAt && dte == null) { try { dte = _app.GetDTE(); } catch (InvalidOperationException) { Thread.Sleep(1000); } } if (dte == null) { throw new InvalidOperationException("Failed to start VS"); } AttachIfDebugging(_vs); } }
public bool RunTest(string container, string name, TimeSpan timeout, object[] arguments, bool allowRetry) { if (_isDisposed) { throw new ObjectDisposedException(GetType().Name); } var dte = _app.GetDTE(); bool timedOut = false; CancellationTokenSource cts = null; var startTime = DateTime.UtcNow; if (!Debugger.IsAttached && timeout < TimeSpan.MaxValue) { cts = new CancellationTokenSource(); Task.Delay(timeout, cts.Token).ContinueWith(t => { timedOut = true; Console.WriteLine($"Terminating {container}.{name}() after {DateTime.UtcNow - startTime}"); // Terminate VS to unblock the Execute() call below CloseCurrentInstance(hard: true); }, TaskContinuationOptions.OnlyOnRanToCompletion); } try { var r = dte.GetObject(container).Execute(name, arguments); if (!r.IsSuccess) { if (r.ExceptionType == "Microsoft.VisualStudio.TestTools.UnitTesting.AssertInconclusiveException") { throw new AssertInconclusiveException(r.ExceptionMessage); } throw new TestFailedException( r.ExceptionType, r.ExceptionMessage, r.ExceptionTraceback ); } return(true); } catch (InvalidComObjectException ex) { Console.WriteLine(ex); CloseCurrentInstance(); if (!allowRetry) { ExceptionDispatchInfo.Capture(ex).Throw(); } } catch (COMException ex) { Console.WriteLine(ex); CloseCurrentInstance(); if (timedOut) { throw new TimeoutException($"Terminating {container}.{name}() after {DateTime.UtcNow - startTime}", ex); } if (!allowRetry) { ExceptionDispatchInfo.Capture(ex).Throw(); } } catch (ThreadAbortException ex) { Console.WriteLine(ex); CloseCurrentInstance(hard: true); ExceptionDispatchInfo.Capture(ex).Throw(); } catch (Exception ex) { CloseCurrentInstance(); ExceptionDispatchInfo.Capture(ex).Throw(); } finally { if (cts != null) { cts.Cancel(); cts.Dispose(); } } return(false); }