Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(beforeAll): fix issue 190 when parent before_all throws #192

Merged
merged 27 commits into from
May 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
606c88e
test(formatterStub): keep track of written context/example as-is when…
GiuseppePiscopo May 11, 2017
7f78c72
test(beforeAll): tests should fail for formatter when before_all throws
GiuseppePiscopo May 11, 2017
bce5410
test(beforeAll): tests body should not run when before_all throws
GiuseppePiscopo May 11, 2017
1716cb3
chore(examples): revert temporary changes to try sample spec classes
GiuseppePiscopo May 11, 2017
65da991
fix(beforeAll): formatter reports failed test when before_all throws
GiuseppePiscopo May 11, 2017
b86c2f9
chore(tools): add settings for VS Code on MacOS
May 12, 2017
45e730b
test(beforeAll): show failing dummy spec instances
May 16, 2017
33252a4
test(afterAll): example should fail when afterAll/after fail
May 16, 2017
91d363c
test(before): example should fail when before fails
May 16, 2017
2566d61
test(xUnitFormatter): don't fail for EOL differences among OSes
May 16, 2017
58e671f
feat(beforeAll): fail example when parent beforeAll throws
May 16, 2017
d82ec8c
test(afterAll): example should fail when context afterAll/after fail
May 16, 2017
d6c63ac
test(beforeAll): example should fail when context beforeAll/before fail
May 16, 2017
d9b0bdd
test(act): example should fail when context act fails
May 16, 2017
03f004d
test(pending): example should not fail when previous hook throws
May 16, 2017
29fb598
fix(beforeAll): fail example also when current context beforeAll throws
May 17, 2017
e26f263
test(after): exception thrown in afterAll/after should not override t…
May 17, 2017
6cc7e1f
test(act): example body should run when act throws
May 17, 2017
25360c4
test(after): preserve example exception when afterAll/after throws
May 17, 2017
809b265
test(asyncBefore): example should not run and fail when before throws
May 17, 2017
c9d68d1
test(asyncAfter): example should run but fail when after throws
May 17, 2017
a10fdac
feat(exceptions): don't run example when before throws, preserve exam…
May 17, 2017
18db97d
test(act): correct test case names when exception is unexpected
May 17, 2017
b5d53b0
resolve merge on failFast test
GiuseppePiscopo May 19, 2017
a55d406
Merge branch 'master' into fix/issue-190-before-all-throw
GiuseppePiscopo May 19, 2017
f25d78b
Merge branch 'master' into fix/issue-190-before-all-throw
GiuseppePiscopo May 19, 2017
ee19874
refactor(formatters): set `alreadyWritten` flag in one place
May 19, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion sln/src/NSpec/Api/Execution/ExampleRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,16 @@ void RunContext(Context context)
var instance = context.GetInstance();

context.Run(
formatter: this,
failFast: false,
instance: instance,
recurse: false);

context.AssignExceptions(
recurse: false);

context.Write(
formatter: this,
recurse: false);
}

DiscoveredExample MapToDiscovered(ExampleBase example, string binaryPath)
Expand Down
4 changes: 2 additions & 2 deletions sln/src/NSpec/Domain/ClassContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ void AddFailingExample(Exception targetEx)
this.AddExample(failingExample);
}

public override void Run(Formatters.ILiveFormatter formatter, bool failFast, nspec instance = null, bool recurse = true)
public override void Run(bool failFast, nspec instance = null, bool recurse = true)
{
if (cantCreateInstance)
{
Expand All @@ -165,7 +165,7 @@ public override void Run(Formatters.ILiveFormatter formatter, bool failFast, nsp
}
else
{
base.Run(formatter, failFast, instance, recurse);
base.Run(failFast, instance, recurse);
}
}

Expand Down
118 changes: 84 additions & 34 deletions sln/src/NSpec/Domain/Context.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Diagnostics;

namespace NSpec.Domain
{
Expand Down Expand Up @@ -226,25 +227,28 @@ public void AddContext(Context child)
}

/// <summary>
/// Test execution happens in two phases: this is the first phase.
/// Test execution happens in three phases: this is the first phase.
/// </summary>
/// <remarks>
/// Here all contexts and all their examples are run, collecting distinct exceptions
/// from context itself (befores/ acts/ it/ afters), beforeAll, afterAll.
/// </remarks>
public virtual void Run(ILiveFormatter formatter, bool failFast, nspec instance = null, bool recurse = true)
public virtual void Run(bool failFast, nspec instance = null, bool recurse = true)
{
if (failFast && Parent.HasAnyFailures()) return;

var nspec = savedInstance ?? instance;

bool runBeforeAfterAll = AnyUnfilteredExampleInSubTree(nspec);
bool runBeforeAfterAll = !AnyBeforeAllThrew() && AnyUnfilteredExampleInSubTree(nspec);

using (new ConsoleCatcher(output => this.CapturedOutput = output))
{
if (runBeforeAfterAll) RunAndHandleException(RunBeforeAll, nspec, ref ExceptionBeforeAll);
}

// evaluate again, after running this context `beforeAll`
bool anyBeforeAllThrew = AnyBeforeAllThrew();

// intentionally using for loop to prevent collection was modified error in sample specs
for (int i = 0; i < Examples.Count; i++)
{
Expand All @@ -254,28 +258,21 @@ public virtual void Run(ILiveFormatter formatter, bool failFast, nspec instance

using (new ConsoleCatcher(output => example.CapturedOutput = output))
{
Exercise(example, nspec);
}

if (example.HasRun && !alreadyWritten)
{
WriteAncestors(formatter);
alreadyWritten = true;
Exercise(example, nspec, anyBeforeAllThrew);
}

if (example.HasRun) formatter.Write(example, Level);
}

if (recurse)
{
Contexts.Do(c => c.Run(formatter, failFast, nspec));
Contexts.Do(c => c.Run(failFast, nspec, recurse));
}

// TODO wrap this as well in a ConsoleCatcher, not before adding tests about it
if (runBeforeAfterAll) RunAndHandleException(RunAfterAll, nspec, ref ExceptionAfterAll);
}

/// <summary>
/// Test execution happens in two phases: this is the second phase.
/// Test execution happens in three phases: this is the second phase.
/// </summary>
/// <remarks>
/// Here all contexts and all their examples are traversed again to set proper exception
Expand All @@ -292,18 +289,19 @@ protected virtual void AssignExceptions(Exception inheritedBeforeAllException, E
inheritedBeforeAllException = inheritedBeforeAllException ?? ExceptionBeforeAll;
inheritedAfterAllException = ExceptionAfterAll ?? inheritedAfterAllException;

// if thrown exception was correctly expected, ignore this context Exception
Exception unexpectedException = ClearExpectedException ? null : Exception;
// if an exception was thrown before the example (either `before` or `act`) but was expected, ignore it
Exception unexpectedException = ClearExpectedException ? null : ExceptionBeforeAct;

Exception contextException = (inheritedBeforeAllException ?? unexpectedException) ?? inheritedAfterAllException;
Exception previousException = inheritedBeforeAllException ?? unexpectedException;
Exception followingException = ExceptionAfter ?? inheritedAfterAllException;

for (int i = 0; i < Examples.Count; i++)
{
var example = Examples[i];

if (!example.Pending)
{
example.AssignProperException(contextException);
example.AssignProperException(previousException, followingException);
}
}

Expand All @@ -313,6 +311,33 @@ protected virtual void AssignExceptions(Exception inheritedBeforeAllException, E
}
}

/// <summary>
/// Test execution happens in three phases: this is the third phase.
/// </summary>
/// <remarks>
/// Here all examples are written out to formatter, together with their contexts,
/// befores, acts, afters, beforeAll, afterAll.
/// </remarks>
public virtual void Write(ILiveFormatter formatter, bool recurse = true)
{
for (int i = 0; i < Examples.Count; i++)
{
var example = Examples[i];

if (example.HasRun && !alreadyWritten)
{
WriteAncestors(formatter);
}

if (example.HasRun) formatter.Write(example, Level);
}

if (recurse)
{
Contexts.Do(c => c.Write(formatter, recurse));
}
}

public virtual void Build(nspec instance = null)
{
instance.Context = this;
Expand All @@ -327,7 +352,7 @@ public string FullContext()
return Parent != null ? Parent.FullContext() + ". " + Name : Name;
}

public bool RunAndHandleException(Action<nspec> action, nspec nspec, ref Exception exceptionToSet)
static bool RunAndHandleException(Action<nspec> action, nspec nspec, ref Exception exceptionToSet)
{
bool hasThrown = false;

Expand All @@ -351,7 +376,7 @@ public bool RunAndHandleException(Action<nspec> action, nspec nspec, ref Excepti
return hasThrown;
}

public void Exercise(ExampleBase example, nspec nspec)
public void Exercise(ExampleBase example, nspec nspec, bool anyBeforeAllThrew)
{
if (example.ShouldSkip(nspec.tagsFilter))
{
Expand All @@ -369,20 +394,26 @@ public void Exercise(ExampleBase example, nspec nspec)

var stopWatch = example.StartTiming();

RunAndHandleException(RunBefores, nspec, ref Exception);
if (!anyBeforeAllThrew)
{
bool exceptionThrownInBefores = RunAndHandleException(RunBefores, nspec, ref ExceptionBeforeAct);

RunAndHandleException(RunActs, nspec, ref Exception);
if (!exceptionThrownInBefores)
{
RunAndHandleException(RunActs, nspec, ref ExceptionBeforeAct);

RunAndHandleException(example.Run, nspec, ref example.Exception);
RunAndHandleException(example.Run, nspec, ref example.Exception);
}

bool exceptionThrownInAfters = RunAndHandleException(RunAfters, nspec, ref Exception);
bool exceptionThrownInAfters = RunAndHandleException(RunAfters, nspec, ref ExceptionAfter);

example.StopTiming(stopWatch);
// when an expected exception is thrown and is set to be cleared by 'expect<>',
// a subsequent exception thrown in 'after' hooks would go unnoticed, so do not clear in this case

// when an expected exception is thrown and is set to be cleared by 'expect<>',
// a subsequent exception thrown in 'after' hooks would go unnoticed, so do not clear in this case
if (exceptionThrownInAfters) ClearExpectedException = false;
}

if (exceptionThrownInAfters) ClearExpectedException = false;
example.StopTiming(stopWatch);
}

public virtual bool IsSub(Type baseType)
Expand Down Expand Up @@ -424,20 +455,35 @@ public void TrimSkippedDescendants()
Contexts.Do(c => c.TrimSkippedDescendants());
}

bool AnyUnfilteredExampleInSubTree(nspec nspec)
bool AnyUnfilteredExampleInSubTree(nspec instance)
{
Func<ExampleBase, bool> shouldNotSkip = e => e.ShouldNotSkip(nspec.tagsFilter);
Func<ExampleBase, bool> shouldNotSkip = e => e.ShouldNotSkip(instance.tagsFilter);

bool anyExampleOrSubExample = Examples.Any(shouldNotSkip) || Contexts.Examples().Any(shouldNotSkip);

return anyExampleOrSubExample;
}

bool AnyBeforeAllThrew()
{
return
ExceptionBeforeAll != null ||
(Parent != null && Parent.AnyBeforeAllThrew());
}

public override string ToString()
{
string exceptionText = (Exception != null ? ", " + Exception.GetType().Name : String.Empty);
string levelText = $"L{Level}";
string exampleText = $"{Examples.Count} exm";
string contextText = $"{Contexts.Count} exm";

return String.Format("{0}, L{1}, {2} exm, {3} ctx{4}", Name, Level, Examples.Count, Contexts.Count, exceptionText);
var exception = ExceptionBeforeAct ?? ExceptionAfter;
string exceptionText = exception?.GetType().Name ?? String.Empty;

return String.Join(",", new []
{
Name, levelText, exampleText, contextText, exceptionText,
});
}

void RecurseAncestors(Action<Context> ancestorAction)
Expand All @@ -447,7 +493,11 @@ void RecurseAncestors(Action<Context> ancestorAction)

void WriteAncestors(ILiveFormatter formatter)
{
if (Parent == null) return;
if (Parent == null)
{
alreadyWritten = true;
return;
}

Parent.WriteAncestors(formatter);

Expand Down Expand Up @@ -475,7 +525,7 @@ public Context(string name = "", string tags = null, bool isPending = false)
public Func<Task> BeforeAsync, ActAsync, AfterAsync, BeforeAllAsync, AfterAllAsync;
public Action<nspec> BeforeInstanceAsync, ActInstanceAsync, AfterInstanceAsync, BeforeAllInstanceAsync, AfterAllInstanceAsync;
public Context Parent;
public Exception ExceptionBeforeAll, Exception, ExceptionAfterAll;
public Exception ExceptionBeforeAll, ExceptionBeforeAct, ExceptionAfter, ExceptionAfterAll;
public bool ClearExpectedException;
public string CapturedOutput;

Expand Down
4 changes: 3 additions & 1 deletion sln/src/NSpec/Domain/ContextCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,11 @@ public void Run(bool failFast = false)

public void Run(ILiveFormatter formatter, bool failFast)
{
this.Do(c => c.Run(formatter, failFast: failFast));
this.Do(c => c.Run(failFast: failFast));

this.Do(c => c.AssignExceptions());

this.Do(c => c.Write(formatter));
}

public void TrimSkippedContexts()
Expand Down
42 changes: 35 additions & 7 deletions sln/src/NSpec/Domain/ExampleBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,43 @@ public void StopTiming(Stopwatch stopWatch)
Duration = stopWatch.Elapsed;
}

public void AssignProperException(Exception contextException)
public void AssignProperException(Exception previousException, Exception followingException)
{
if (contextException == null) return; //stick with whatever Exception may or may not be set on this Example

if (Exception != null && Exception.GetType() != typeof(ExceptionNotThrown))
Exception = new ExampleFailureException("Context Failure: " + contextException.Message + ", Example Failure: " + Exception.Message, contextException);
if (previousException == null && followingException == null)
{
// stick with whatever exception may or may not be set on this example
return;
}

if (Exception == null)
Exception = new ExampleFailureException("Context Failure: " + contextException.Message, contextException);
if (previousException != null)
{
var contextException = previousException;

if (Exception != null && Exception.GetType() != typeof(ExceptionNotThrown))
{
Exception = new ExampleFailureException(
"Context Failure: " + contextException.Message + ", Example Failure: " + Exception.Message,
contextException);
}

if (Exception == null)
{
Exception = new ExampleFailureException(
"Context Failure: " + contextException.Message,
contextException);
}
}
else
{
var contextException = followingException;

if (Exception == null)
{
Exception = new ExampleFailureException(
"Context Failure: " + contextException.Message,
contextException);
}
}
}

public bool ShouldSkip(Tags tagsFilter)
Expand Down
Loading