From a8bef89392920dd0ef5d74ef3563d97083246414 Mon Sep 17 00:00:00 2001 From: Bernhard Urban Date: Tue, 14 Nov 2017 12:27:54 +0100 Subject: [PATCH] make FinallyDelegate more resilient regarding unhandled exceptions (#5) --- .../src/framework/FinallyDelegate.cs | 27 ++++++++++++++++--- .../framework/Internal/WorkItems/WorkItem.cs | 9 +++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/NUnitLite-1.0.0/src/framework/FinallyDelegate.cs b/NUnitLite-1.0.0/src/framework/FinallyDelegate.cs index 313c1f9..1a3bb97 100644 --- a/NUnitLite-1.0.0/src/framework/FinallyDelegate.cs +++ b/NUnitLite-1.0.0/src/framework/FinallyDelegate.cs @@ -29,9 +29,18 @@ using System.Threading; using NUnit.Framework.Api; using System.Collections.Generic; +using System.Runtime.Remoting.Messaging; namespace NUnit.Framework.Internal { + [Serializable] + class Container : ILogicalThreadAffinative { + public string testName; + public Container(string testName) { + this.testName = testName; + } + } + public class FinallyDelegate { // If our test spawns a thread that throws, we will bubble @@ -49,19 +58,31 @@ public class FinallyDelegate // so we need a stack of finally delegate continuations Stack> testStack; + Dictionary lookupTable; + + private static readonly string CONTEXT_KEY = "TestResultName"; + public FinallyDelegate () { this.testStack = new Stack>(); + this.lookupTable = new Dictionary(); } public void Set (TestExecutionContext context, long startTicks, TestResult result) { var frame = new Tuple(context, startTicks, result); + + /* keep name in LogicalCallContext, because this will be inherited by + * Threads spawned by the test case */ + CallContext.SetData(CONTEXT_KEY, new Container(result.Test.FullName)); + + this.lookupTable.Add(result.Test.FullName, result); this.testStack.Push(frame); } public void HandleUnhandledExc (Exception ex) { - TestExecutionContext context = this.testStack.Peek().Item1; - context.CurrentResult.RecordException(ex); - context.CurrentResult.ThreadCrashFail = true; + Container c = (Container) CallContext.GetData(CONTEXT_KEY); + TestResult result = this.lookupTable [c.testName]; + result.RecordException(ex); + result.ThreadCrashFail = true; } public void Complete () { diff --git a/NUnitLite-1.0.0/src/framework/Internal/WorkItems/WorkItem.cs b/NUnitLite-1.0.0/src/framework/Internal/WorkItems/WorkItem.cs index be4558f..9c3ed2d 100644 --- a/NUnitLite-1.0.0/src/framework/Internal/WorkItems/WorkItem.cs +++ b/NUnitLite-1.0.0/src/framework/Internal/WorkItems/WorkItem.cs @@ -180,6 +180,15 @@ private void RunTestWithTimeout(int timeout) #endif private void RunTest() + { + /* using a separate ExecutionContext for every test case, + * guarantees us to have a dedicated "namespace" for the + * LogicalCallContext per testcase */ + ExecutionContext ec = ExecutionContext.Capture(); + ExecutionContext.Run(ec, DispatchWork, null); + } + + private void DispatchWork(object o) { _context.CurrentTest = this.Test; _context.CurrentResult = this.Result;