public async Task RunAsync() { string runFileName = RunFileName; if (!Path.IsPathRooted(runFileName)) { runFileName = _tests.DebuggerTestPath + runFileName; } string breakFileName = BreakFileName; if (breakFileName != null && !Path.IsPathRooted(breakFileName)) { breakFileName = _tests.DebuggerTestPath + breakFileName; } foreach (var bp in Breakpoints) { var fileName = bp.FileName ?? breakFileName ?? runFileName; if (fileName.EndsWith(".py")) { Assert.IsTrue(bp is Breakpoint); } else { Assert.IsTrue(bp is DjangoBreakpoint); } } var bps = new Dictionary <PythonBreakpoint, BreakpointBase>(); var unboundBps = new HashSet <Breakpoint>(); var breakpointsToBeBound = Breakpoints.Count; var debugger = new PythonDebugger(); PythonThread thread = null; // Used to signal exceptions from debugger event handlers that run on a background thread. var backgroundException = new TaskCompletionSource <bool>(); var processLoaded = new TaskCompletionSource <bool>(); var process = _tests.DebugProcess( debugger, runFileName, cwd: WorkingDirectory, arguments: Arguments, resumeOnProcessLoaded: false, onLoaded: async(newproc, newthread) => { try { foreach (var bp in Breakpoints) { var fileName = bp.FileName ?? breakFileName ?? runFileName; PythonBreakpoint breakpoint; var pyBP = bp as Breakpoint; if (pyBP != null) { breakpoint = newproc.AddBreakpoint(fileName, pyBP.LineNumber, pyBP.ConditionKind, pyBP.Condition, pyBP.PassCountKind, pyBP.PassCount); unboundBps.Add(pyBP); } else { var djangoBP = bp as DjangoBreakpoint; if (djangoBP != null) { breakpoint = newproc.AddDjangoBreakpoint(fileName, djangoBP.LineNumber); // Django breakpoints are never bound. --breakpointsToBeBound; } else { Assert.Fail("Unknown breakpoint type."); return; } } // Bind failed and succeeded events expect to find the breakpoint // in the dictionary, so update it before sending the add request. bps.Add(breakpoint, bp); await breakpoint.AddAsync(TimeoutToken()); } OnProcessLoaded?.Invoke(newproc); thread = newthread; processLoaded.SetResult(true); } catch (Exception ex) { backgroundException.TrySetException(ex); } }, interpreterOptions: InterpreterOptions ); int breakpointsBound = 0; int breakpointsNotBound = 0; int nextExpectedHit = 0; var allBreakpointsHit = new TaskCompletionSource <bool>(); var allBreakpointBindResults = new TaskCompletionSource <bool>(); if (breakpointsToBeBound == 0) { allBreakpointBindResults.SetResult(true); } try { process.BreakpointBindFailed += (sender, args) => { try { var bp = (Breakpoint)bps[args.Breakpoint]; if (bp != null && !(bp.IsBindFailureExpected ?? IsBindFailureExpected)) { Assert.Fail("Breakpoint at {0}:{1} failed to bind.", bp.FileName ?? breakFileName ?? runFileName, bp.LineNumber); } ++breakpointsNotBound; if (breakpointsBound + breakpointsNotBound == breakpointsToBeBound) { allBreakpointBindResults.SetResult(true); } } catch (Exception ex) { backgroundException.TrySetException(ex); } }; process.BreakpointBindSucceeded += (sender, args) => { try { var bp = (Breakpoint)bps[args.Breakpoint]; Assert.AreEqual(bp.FileName ?? breakFileName ?? runFileName, args.Breakpoint.Filename); Assert.IsTrue(unboundBps.Remove(bp)); ++breakpointsBound; if (breakpointsBound + breakpointsNotBound == breakpointsToBeBound) { allBreakpointBindResults.SetResult(true); } } catch (Exception ex) { backgroundException.TrySetException(ex); } }; process.BreakpointHit += async(sender, args) => { try { if (nextExpectedHit < ExpectedHits.Count) { var bp = Breakpoints[ExpectedHits[nextExpectedHit]]; Trace.TraceInformation("Hit {0}:{1}", args.Breakpoint.Filename, args.Breakpoint.LineNo); Assert.AreSame(bp, bps[args.Breakpoint]); if (bp.RemoveWhenHit) { await args.Breakpoint.RemoveAsync(TimeoutToken()); } if (bp.ExpectHitOnMainThread ?? ExpectHitOnMainThread) { Assert.AreSame(thread, args.Thread); } bp.OnHit?.Invoke(args); if (++nextExpectedHit == ExpectedHits.Count) { allBreakpointsHit.SetResult(true); } } try { await process.ResumeAsync(TimeoutToken()); } catch (TaskCanceledException) { // If we don't wait for exit, the Terminate() call // will cause ResumeAsync to be canceled. if (WaitForExit) { throw; } } } catch (Exception ex) { backgroundException.TrySetException(ex); } }; await process.StartAsync(); Assert.IsTrue(WaitForAny(10000, processLoaded.Task, backgroundException.Task), "Timed out waiting for process load"); await process.AutoResumeThread(thread.Id, TimeoutToken()); if (breakpointsToBeBound > 0) { Assert.IsTrue(WaitForAny(10000, allBreakpointBindResults.Task, backgroundException.Task), "Timed out waiting for breakpoints to bind"); } } finally { if (WaitForExit) { _tests.WaitForExit(process); } else { Assert.IsTrue(WaitForAny(20000, allBreakpointsHit.Task, backgroundException.Task), "Timed out waiting for breakpoints to hit"); process.Terminate(); } } if (backgroundException.Task.IsFaulted) { backgroundException.Task.GetAwaiter().GetResult(); } Assert.AreEqual(ExpectedHits.Count, nextExpectedHit); Assert.IsTrue(unboundBps.All(bp => bp.IsBindFailureExpected ?? IsBindFailureExpected)); }
public void Run() { string runFileName = RunFileName; if (!Path.IsPathRooted(runFileName)) { runFileName = _tests.DebuggerTestPath + runFileName; } string breakFileName = BreakFileName; if (breakFileName != null && !Path.IsPathRooted(breakFileName)) { breakFileName = _tests.DebuggerTestPath + breakFileName; } foreach (var bp in Breakpoints) { var fileName = bp.FileName ?? breakFileName ?? runFileName; if (fileName.EndsWith(".py")) { Assert.IsTrue(bp is Breakpoint); } else { Assert.IsTrue(bp is DjangoBreakpoint); } } var bps = new Dictionary <PythonBreakpoint, BreakpointBase>(); var unboundBps = new HashSet <Breakpoint>(); var breakpointsToBeBound = Breakpoints.Count; var debugger = new PythonDebugger(); PythonThread thread = null; // Used to signal exceptions from debugger event handlers that run on a background thread. var backgroundException = new TaskCompletionSource <bool>(); var processLoaded = new TaskCompletionSource <bool>(); var process = _tests.DebugProcess( debugger, runFileName, cwd: WorkingDirectory, arguments: Arguments, resumeOnProcessLoaded: false, onLoaded: (newproc, newthread) => { try { foreach (var bp in Breakpoints) { var fileName = bp.FileName ?? breakFileName ?? runFileName; PythonBreakpoint breakpoint; var pyBP = bp as Breakpoint; if (pyBP != null) { breakpoint = newproc.AddBreakPoint(fileName, pyBP.LineNumber, pyBP.ConditionKind, pyBP.Condition, pyBP.PassCountKind, pyBP.PassCount); unboundBps.Add(pyBP); } else { var djangoBP = bp as DjangoBreakpoint; if (djangoBP != null) { breakpoint = newproc.AddDjangoBreakPoint(fileName, djangoBP.LineNumber); // Django breakpoints are never bound. --breakpointsToBeBound; } else { Assert.Fail("Unknown breakpoint type."); return; } } breakpoint.Add(); bps.Add(breakpoint, bp); } if (OnProcessLoaded != null) { OnProcessLoaded(newproc); } thread = newthread; processLoaded.SetResult(true); } catch (Exception ex) { backgroundException.SetException(ex); } }, interpreterOptions: InterpreterOptions ); int breakpointsBound = 0; int breakpointsNotBound = 0; int nextExpectedHit = 0; var allBreakpointsHit = new TaskCompletionSource <bool>(); var allBreakpointBindResults = new TaskCompletionSource <bool>(); if (breakpointsToBeBound == 0) { allBreakpointBindResults.SetResult(true); } try { process.BreakpointBindFailed += (sender, args) => { try { var bp = (Breakpoint)bps[args.Breakpoint]; if (bp != null && !(bp.IsBindFailureExpected ?? IsBindFailureExpected)) { Assert.Fail("Breakpoint at {0}:{1} failed to bind.", bp.FileName ?? breakFileName ?? runFileName, bp.LineNumber); } ++breakpointsNotBound; if (breakpointsBound + breakpointsNotBound == breakpointsToBeBound) { allBreakpointBindResults.SetResult(true); } } catch (Exception ex) { backgroundException.SetException(ex); } }; process.BreakpointBindSucceeded += (sender, args) => { try { var bp = (Breakpoint)bps[args.Breakpoint]; Assert.AreEqual(bp.FileName ?? breakFileName ?? runFileName, args.Breakpoint.Filename); Assert.IsTrue(unboundBps.Remove(bp)); ++breakpointsBound; if (breakpointsBound + breakpointsNotBound == breakpointsToBeBound) { allBreakpointBindResults.SetResult(true); } } catch (Exception ex) { backgroundException.SetException(ex); } }; process.BreakpointHit += (sender, args) => { try { if (nextExpectedHit < ExpectedHits.Count) { var bp = Breakpoints[ExpectedHits[nextExpectedHit]]; Assert.AreSame(bp, bps[args.Breakpoint]); if (bp.RemoveWhenHit) { args.Breakpoint.Remove(); } if (bp.ExpectHitOnMainThread ?? ExpectHitOnMainThread) { Assert.AreSame(thread, args.Thread); } if (bp.OnHit != null) { bp.OnHit(args); } if (++nextExpectedHit == ExpectedHits.Count) { allBreakpointsHit.SetResult(true); } } process.Continue(); } catch (Exception ex) { backgroundException.SetException(ex); } }; process.Start(); WaitForAny(10000, processLoaded.Task, backgroundException.Task); process.AutoResumeThread(thread.Id); if (breakpointsToBeBound > 0) { WaitForAny(10000, allBreakpointBindResults.Task, backgroundException.Task); } } finally { if (WaitForExit) { _tests.WaitForExit(process); } else { WaitForAny(10000, allBreakpointsHit.Task, backgroundException.Task); process.Terminate(); } } if (backgroundException.Task.IsFaulted) { backgroundException.Task.GetAwaiter().GetResult(); } Assert.AreEqual(ExpectedHits.Count, nextExpectedHit); Assert.IsTrue(unboundBps.All(bp => bp.IsBindFailureExpected ?? IsBindFailureExpected)); }