From 26863fed2f8cfc51cfae5c2e7dd032489703610a Mon Sep 17 00:00:00 2001 From: Giuseppe Piscopo Date: Fri, 19 May 2017 11:20:50 +0200 Subject: [PATCH 01/26] refactor(context): use dedicated classes to build and run class/context hooks --- sln/src/NSpec/Domain/ActChain.cs | 79 ++++++ sln/src/NSpec/Domain/AfterAllChain.cs | 72 +++++ sln/src/NSpec/Domain/AfterChain.cs | 83 ++++++ sln/src/NSpec/Domain/BeforeAllChain.cs | 68 +++++ sln/src/NSpec/Domain/BeforeChain.cs | 80 ++++++ sln/src/NSpec/Domain/ChainUtils.cs | 19 ++ sln/src/NSpec/Domain/ClassContext.cs | 106 +------ sln/src/NSpec/Domain/Context.cs | 334 ++++++++++------------- sln/src/NSpec/nspec.cs | 56 ++-- sln/test/NSpec.Tests/describe_Context.cs | 4 +- 10 files changed, 582 insertions(+), 319 deletions(-) create mode 100644 sln/src/NSpec/Domain/ActChain.cs create mode 100644 sln/src/NSpec/Domain/AfterAllChain.cs create mode 100644 sln/src/NSpec/Domain/AfterChain.cs create mode 100644 sln/src/NSpec/Domain/BeforeAllChain.cs create mode 100644 sln/src/NSpec/Domain/BeforeChain.cs create mode 100644 sln/src/NSpec/Domain/ChainUtils.cs diff --git a/sln/src/NSpec/Domain/ActChain.cs b/sln/src/NSpec/Domain/ActChain.cs new file mode 100644 index 00000000..010de16b --- /dev/null +++ b/sln/src/NSpec/Domain/ActChain.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using NSpec.Domain.Extensions; + +namespace NSpec.Domain +{ + public class ActChain + { + public void BuildMethodLevel(Conventions conventions, List classHierarchy) + { + var methods = ChainUtils.GetMethodsFromHierarchy( + classHierarchy, conventions.GetMethodLevelAct); + + if (methods.Count > 0) + { + ClassHook = instance => methods.Do(m => m.Invoke(instance, null)); + } + + var asyncMethods = ChainUtils.GetMethodsFromHierarchy( + classHierarchy, conventions.GetAsyncMethodLevelAct); + + if (asyncMethods.Count > 0) + { + AsyncClassHook = instance => asyncMethods.Do(m => new AsyncMethodLevelAct(m).Run(instance)); + } + } + + public void Run(nspec instance) + { + // parent chain + + context.RecurseAncestors(c => c.ActChain.Run(instance)); + + // class (method-level) + + if (ClassHook != null && AsyncClassHook != null) + { + throw new AsyncMismatchException( + "A spec class with all its ancestors cannot set both sync and async class-level 'act_each' hooks, they should either be all sync or all async"); + } + + ClassHook.SafeInvoke(instance); + + AsyncClassHook.SafeInvoke(instance); + + // context-level + + if (Hook != null && AsyncHook != null) + { + throw new AsyncMismatchException( + "A single context cannot set both an 'act' and an 'actAsync', please pick one of the two"); + } + + if (Hook != null && Hook.IsAsync()) + { + throw new AsyncMismatchException( + "'act' cannot be set to an async delegate, please use 'actAsync' instead"); + } + + Hook.SafeInvoke(); + + AsyncHook.SafeInvoke(); + } + + public ActChain(Context context) + { + this.context = context; + } + + public Action Hook; + public Func AsyncHook; + + public Action ClassHook { get; private set; } + public Action AsyncClassHook { get; private set; } + + readonly Context context; + } +} diff --git a/sln/src/NSpec/Domain/AfterAllChain.cs b/sln/src/NSpec/Domain/AfterAllChain.cs new file mode 100644 index 00000000..db8189b4 --- /dev/null +++ b/sln/src/NSpec/Domain/AfterAllChain.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using NSpec.Domain.Extensions; + +namespace NSpec.Domain +{ + public class AfterAllChain + { + public void BuildMethodLevel(Conventions conventions, List classHierarchy) + { + var methods = ChainUtils.GetMethodsFromHierarchy( + classHierarchy, conventions.GetMethodLevelAfterAll); + + methods.Reverse(); + + if (methods.Count > 0) + { + ClassHook = instance => methods.Do(m => m.Invoke(instance, null)); + } + + var asyncMethods = ChainUtils.GetMethodsFromHierarchy( + classHierarchy, conventions.GetAsyncMethodLevelAfterAll); + + asyncMethods.Reverse(); + + if (asyncMethods.Count > 0) + { + AsyncClassHook = instance => asyncMethods.Do(m => new AsyncMethodLevelAfterAll(m).Run(instance)); + } + } + + public void Run(nspec instance) + { + // context-level + + if (Hook != null && AsyncHook != null) + { + throw new AsyncMismatchException( + "A single context cannot set both an 'afterAll' and an 'afterAllAsync', please pick one of the two"); + } + + if (Hook != null && Hook.IsAsync()) + { + throw new AsyncMismatchException( + "'afterAll' cannot be set to an async delegate, please use 'afterAllAsync' instead"); + } + + Hook.SafeInvoke(); + + AsyncHook.SafeInvoke(); + + // class (method-level) + + if (ClassHook != null && AsyncClassHook != null) + { + throw new AsyncMismatchException( + "A spec class with all its ancestors cannot set both sync and async class-level 'after_all' hooks, they should either be all sync or all async"); + } + + ClassHook.SafeInvoke(instance); + + AsyncClassHook.SafeInvoke(instance); + } + + public Action Hook; + public Func AsyncHook; + + public Action ClassHook { get; private set; } + public Action AsyncClassHook { get; private set; } + } +} diff --git a/sln/src/NSpec/Domain/AfterChain.cs b/sln/src/NSpec/Domain/AfterChain.cs new file mode 100644 index 00000000..c8e22857 --- /dev/null +++ b/sln/src/NSpec/Domain/AfterChain.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using NSpec.Domain.Extensions; + +namespace NSpec.Domain +{ + public class AfterChain + { + public void BuildMethodLevel(Conventions conventions, List classHierarchy) + { + var methods = ChainUtils.GetMethodsFromHierarchy( + classHierarchy, conventions.GetMethodLevelAfter); + + methods.Reverse(); + + if (methods.Count > 0) + { + ClassHook = instance => methods.Do(m => m.Invoke(instance, null)); + } + + var asyncMethods = ChainUtils.GetMethodsFromHierarchy( + classHierarchy, conventions.GetAsyncMethodLevelAfter); + + asyncMethods.Reverse(); + + if (asyncMethods.Count > 0) + { + AsyncClassHook = instance => asyncMethods.Do(m => new AsyncMethodLevelAfter(m).Run(instance)); + } + } + + public void Run(nspec instance) + { + // context-level + + if (Hook != null && AsyncHook != null) + { + throw new AsyncMismatchException( + "A single context cannot set both an 'after' and an 'afterAsync', please pick one of the two"); + } + + if (Hook != null && Hook.IsAsync()) + { + throw new AsyncMismatchException( + "'after' cannot be set to an async delegate, please use 'afterAsync' instead"); + } + + Hook.SafeInvoke(); + + AsyncHook.SafeInvoke(); + + // class (method-level) + + if (ClassHook != null && AsyncClassHook != null) + { + throw new AsyncMismatchException( + "A spec class with all its ancestors cannot set both sync and async class-level 'after_each' hooks, they should either be all sync or all async"); + } + + ClassHook.SafeInvoke(instance); + + AsyncClassHook.SafeInvoke(instance); + + // parent chain + + context.RecurseAncestors(c => c.AfterChain.Run(instance)); + } + + public AfterChain(Context context) + { + this.context = context; + } + + public Action Hook; + public Func AsyncHook; + + public Action ClassHook { get; private set; } + public Action AsyncClassHook { get; private set; } + + readonly Context context; + } +} diff --git a/sln/src/NSpec/Domain/BeforeAllChain.cs b/sln/src/NSpec/Domain/BeforeAllChain.cs new file mode 100644 index 00000000..5d537483 --- /dev/null +++ b/sln/src/NSpec/Domain/BeforeAllChain.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using NSpec.Domain.Extensions; + +namespace NSpec.Domain +{ + public class BeforeAllChain + { + public void BuildMethodLevel(Conventions conventions, List classHierarchy) + { + var methods = ChainUtils.GetMethodsFromHierarchy( + classHierarchy, conventions.GetMethodLevelBeforeAll); + + if (methods.Count > 0) + { + ClassHook = instance => methods.Do(m => m.Invoke(instance, null)); + } + + var asyncMethods = ChainUtils.GetMethodsFromHierarchy( + classHierarchy, conventions.GetAsyncMethodLevelBeforeAll); + + if (asyncMethods.Count > 0) + { + AsyncClassHook = instance => asyncMethods.Do(m => new AsyncMethodLevelBeforeAll(m).Run(instance)); + } + } + + public void Run(nspec instance) + { + // context-level + + if (Hook != null && AsyncHook != null) + { + throw new AsyncMismatchException( + "A single context cannot set both a 'beforeAll' and an 'beforeAllAsync', please pick one of the two"); + } + + if (Hook != null && Hook.IsAsync()) + { + throw new AsyncMismatchException( + "'beforeAll' cannot be set to an async delegate, please use 'beforeAllAsync' instead"); + } + + Hook.SafeInvoke(); + + AsyncHook.SafeInvoke(); + + // class (method-level) + + if (ClassHook != null && AsyncClassHook != null) + { + throw new AsyncMismatchException( + "A spec class with all its ancestors cannot set both sync and async class-level 'before_all' hooks, they should either be all sync or all async"); + } + + ClassHook.SafeInvoke(instance); + + AsyncClassHook.SafeInvoke(instance); + } + + public Action Hook; + public Func AsyncHook; + + public Action ClassHook { get; private set; } + public Action AsyncClassHook { get; private set; } + } +} diff --git a/sln/src/NSpec/Domain/BeforeChain.cs b/sln/src/NSpec/Domain/BeforeChain.cs new file mode 100644 index 00000000..6ad405de --- /dev/null +++ b/sln/src/NSpec/Domain/BeforeChain.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using NSpec.Domain.Extensions; + +namespace NSpec.Domain +{ + public class BeforeChain + { + public void BuildMethodLevel(Conventions conventions, List classHierarchy) + { + var methods = ChainUtils.GetMethodsFromHierarchy( + classHierarchy, conventions.GetMethodLevelBefore); + + if (methods.Count > 0) + { + ClassHook = instance => methods.Do(m => m.Invoke(instance, null)); + } + + var asyncMethods = ChainUtils.GetMethodsFromHierarchy( + classHierarchy, conventions.GetAsyncMethodLevelBefore); + + if (asyncMethods.Count > 0) + { + AsyncClassHook = instance => asyncMethods.Do(m => new AsyncMethodLevelBefore(m).Run(instance)); + } + } + + public void Run(nspec instance) + { + // parent chain + + context.RecurseAncestors(c => c.BeforeChain.Run(instance)); + + // class (method-level) + + if (ClassHook != null && AsyncClassHook != null) + { + throw new AsyncMismatchException( + "A spec class with all its ancestors cannot set both sync and async " + + "class-level 'before_each' hooks, they should either be all sync or all async"); + } + + ClassHook.SafeInvoke(instance); + + AsyncClassHook.SafeInvoke(instance); + + // context-level + + if (Hook != null && AsyncHook != null) + { + throw new AsyncMismatchException( + "A single context cannot set both a 'before' and an 'beforeAsync', please pick one of the two"); + } + + if (Hook != null && Hook.IsAsync()) + { + throw new AsyncMismatchException( + "'before' cannot be set to an async delegate, please use 'beforeAsync' instead"); + } + + Hook.SafeInvoke(); + + AsyncHook.SafeInvoke(); + } + + public BeforeChain(Context context) + { + this.context = context; + } + + public Action Hook; + public Func AsyncHook; + + public Action ClassHook { get; private set; } + public Action AsyncClassHook { get; private set; } + + readonly Context context; + } +} diff --git a/sln/src/NSpec/Domain/ChainUtils.cs b/sln/src/NSpec/Domain/ChainUtils.cs new file mode 100644 index 00000000..9e0fd69b --- /dev/null +++ b/sln/src/NSpec/Domain/ChainUtils.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace NSpec.Domain +{ + public static class ChainUtils + { + public static List GetMethodsFromHierarchy( + List classHierarchy, Func selectMethod) + { + return classHierarchy + .Select(selectMethod) + .Where(m => m != null) + .ToList(); + } + } +} diff --git a/sln/src/NSpec/Domain/ClassContext.cs b/sln/src/NSpec/Domain/ClassContext.cs index 055a8f16..775718c8 100644 --- a/sln/src/NSpec/Domain/ClassContext.cs +++ b/sln/src/NSpec/Domain/ClassContext.cs @@ -10,15 +10,15 @@ public class ClassContext : Context { public override void Build(nspec unused = null) { - BuildMethodLevelBefore(); + BeforeAllChain.BuildMethodLevel(conventions, classHierarchy); - BuildMethodLevelBeforeAll(); + BeforeChain.BuildMethodLevel(conventions, classHierarchy); - BuildMethodLevelAct(); + ActChain.BuildMethodLevel(conventions, classHierarchy); - BuildMethodLevelAfter(); + AfterChain.BuildMethodLevel(conventions, classHierarchy); - BuildMethodLevelAfterAll(); + AfterAllChain.BuildMethodLevel(conventions, classHierarchy); try { @@ -48,92 +48,7 @@ public override bool IsSub(Type baseType) IEnumerable GetMethodsFromHierarchy(Func methodAccessor) { - return classHierarchyToClass.Select(methodAccessor).Where(mi => mi != null); - } - - void BuildMethodLevelBefore() - { - var befores = GetMethodsFromHierarchy(conventions.GetMethodLevelBefore).ToList(); - - if (befores.Count > 0) - { - BeforeInstance = instance => befores.Do(b => b.Invoke(instance, null)); - } - - var asyncBefores = GetMethodsFromHierarchy(conventions.GetAsyncMethodLevelBefore).ToList(); - - if (asyncBefores.Count > 0) - { - BeforeInstanceAsync = instance => asyncBefores.Do(b => new AsyncMethodLevelBefore(b).Run(instance)); - } - } - - void BuildMethodLevelBeforeAll() - { - var beforeAlls = GetMethodsFromHierarchy(conventions.GetMethodLevelBeforeAll).ToList(); - - if (beforeAlls.Count > 0) - { - BeforeAllInstance = instance => beforeAlls.Do(a => a.Invoke(instance, null)); - } - - var asyncBeforeAlls = GetMethodsFromHierarchy(conventions.GetAsyncMethodLevelBeforeAll).ToList(); - - if (asyncBeforeAlls.Count > 0) - { - BeforeAllInstanceAsync = instance => asyncBeforeAlls.Do(b => new AsyncMethodLevelBeforeAll(b).Run(instance)); - } - } - - void BuildMethodLevelAct() - { - var acts = GetMethodsFromHierarchy(conventions.GetMethodLevelAct).ToList(); - - if (acts.Count > 0) - { - ActInstance = instance => acts.Do(a => a.Invoke(instance, null)); - } - - var asyncActs = GetMethodsFromHierarchy(conventions.GetAsyncMethodLevelAct).ToList(); - - if (asyncActs.Count > 0) - { - ActInstanceAsync = instance => asyncActs.Do(a => new AsyncMethodLevelAct(a).Run(instance)); - } - } - - void BuildMethodLevelAfter() - { - var afters = GetMethodsFromHierarchy(conventions.GetMethodLevelAfter).Reverse().ToList(); - - if (afters.Count > 0) - { - AfterInstance = instance => afters.Do(a => a.Invoke(instance, null)); - } - - var asyncAfters = GetMethodsFromHierarchy(conventions.GetAsyncMethodLevelAfter).Reverse().ToList(); - - if (asyncAfters.Count > 0) - { - AfterInstanceAsync = instance => asyncAfters.Do(a => new AsyncMethodLevelAfter(a).Run(instance)); - } - } - - void BuildMethodLevelAfterAll() - { - var afterAlls = GetMethodsFromHierarchy(conventions.GetMethodLevelAfterAll).Reverse().ToList(); - - if (afterAlls.Count > 0) - { - AfterAllInstance = instance => afterAlls.Do(a => a.Invoke(instance, null)); - } - - var asyncAfterAlls = GetMethodsFromHierarchy(conventions.GetAsyncMethodLevelAfterAll).Reverse().ToList(); - - if (asyncAfterAlls.Count > 0) - { - AfterAllInstanceAsync = instance => asyncAfterAlls.Do(a => new AsyncMethodLevelAfterAll(a).Run(instance)); - } + return classHierarchy.Select(methodAccessor).Where(mi => mi != null); } void AddFailingExample(Exception targetEx) @@ -178,16 +93,15 @@ public ClassContext(Type type, Conventions conventions = null, Tags tagsFilter = this.tagsFilter = tagsFilter; - if (type != typeof(nspec)) - { - classHierarchyToClass.AddRange(type.GetAbstractBaseClassChainWithClass()); - } + this.classHierarchy = (type == typeof(nspec)) + ? new List() + : new List(type.GetAbstractBaseClassChainWithClass()); } public Type SpecType; Tags tagsFilter; - List classHierarchyToClass = new List(); + List classHierarchy; Conventions conventions; bool cantCreateInstance = false; } diff --git a/sln/src/NSpec/Domain/Context.cs b/sln/src/NSpec/Domain/Context.cs index 3f858cb8..f48239bd 100644 --- a/sln/src/NSpec/Domain/Context.cs +++ b/sln/src/NSpec/Domain/Context.cs @@ -11,184 +11,6 @@ namespace NSpec.Domain { public class Context { - public void RunBefores(nspec instance) - { - // parent chain - - RecurseAncestors(c => c.RunBefores(instance)); - - // class (method-level) - - if (BeforeInstance != null && BeforeInstanceAsync != null) - { - throw new AsyncMismatchException( - "A spec class with all its ancestors cannot set both sync and async " + - "class-level 'before_each' hooks, they should either be all sync or all async"); - } - - BeforeInstance.SafeInvoke(instance); - - BeforeInstanceAsync.SafeInvoke(instance); - - // context-level - - if (Before != null && BeforeAsync != null) - { - throw new AsyncMismatchException( - "A single context cannot set both a 'before' and an 'beforeAsync', please pick one of the two"); - } - - if (Before != null && Before.IsAsync()) - { - throw new AsyncMismatchException( - "'before' cannot be set to an async delegate, please use 'beforeAsync' instead"); - } - - Before.SafeInvoke(); - - BeforeAsync.SafeInvoke(); - } - - void RunBeforeAll(nspec instance) - { - // context-level - - if (BeforeAll != null && BeforeAllAsync != null) - { - throw new AsyncMismatchException( - "A single context cannot set both a 'beforeAll' and an 'beforeAllAsync', please pick one of the two"); - } - - if (BeforeAll != null && BeforeAll.IsAsync()) - { - throw new AsyncMismatchException( - "'beforeAll' cannot be set to an async delegate, please use 'beforeAllAsync' instead"); - } - - BeforeAll.SafeInvoke(); - - BeforeAllAsync.SafeInvoke(); - - // class (method-level) - - if (BeforeAllInstance != null && BeforeAllInstanceAsync != null) - { - throw new AsyncMismatchException( - "A spec class with all its ancestors cannot set both sync and async class-level 'before_all' hooks, they should either be all sync or all async"); - } - - BeforeAllInstance.SafeInvoke(instance); - - BeforeAllInstanceAsync.SafeInvoke(instance); - } - - public void RunActs(nspec instance) - { - // parent chain - - RecurseAncestors(c => c.RunActs(instance)); - - // class (method-level) - - if (ActInstance != null && ActInstanceAsync != null) - { - throw new AsyncMismatchException( - "A spec class with all its ancestors cannot set both sync and async class-level 'act_each' hooks, they should either be all sync or all async"); - } - - ActInstance.SafeInvoke(instance); - - ActInstanceAsync.SafeInvoke(instance); - - // context-level - - if (Act != null && ActAsync != null) - { - throw new AsyncMismatchException( - "A single context cannot set both an 'act' and an 'actAsync', please pick one of the two"); - } - - if (Act != null && Act.IsAsync()) - { - throw new AsyncMismatchException( - "'act' cannot be set to an async delegate, please use 'actAsync' instead"); - } - - Act.SafeInvoke(); - - ActAsync.SafeInvoke(); - } - - public void RunAfters(nspec instance) - { - // context-level - - if (After != null && AfterAsync != null) - { - throw new AsyncMismatchException( - "A single context cannot set both an 'after' and an 'afterAsync', please pick one of the two"); - } - - if (After != null && After.IsAsync()) - { - throw new AsyncMismatchException( - "'after' cannot be set to an async delegate, please use 'afterAsync' instead"); - } - - After.SafeInvoke(); - - AfterAsync.SafeInvoke(); - - // class (method-level) - - if (AfterInstance != null && AfterInstanceAsync != null) - { - throw new AsyncMismatchException( - "A spec class with all its ancestors cannot set both sync and async class-level 'after_each' hooks, they should either be all sync or all async"); - } - - AfterInstance.SafeInvoke(instance); - - AfterInstanceAsync.SafeInvoke(instance); - - // parent chain - - RecurseAncestors(c => c.RunAfters(instance)); - } - - public void RunAfterAll(nspec instance) - { - // context-level - - if (AfterAll != null && AfterAllAsync != null) - { - throw new AsyncMismatchException( - "A single context cannot set both an 'afterAll' and an 'afterAllAsync', please pick one of the two"); - } - - if (AfterAll != null && AfterAll.IsAsync()) - { - throw new AsyncMismatchException( - "'afterAll' cannot be set to an async delegate, please use 'afterAllAsync' instead"); - } - - AfterAll.SafeInvoke(); - - AfterAllAsync.SafeInvoke(); - - // class (method-level) - - if (AfterAllInstance != null && AfterAllInstanceAsync != null) - { - throw new AsyncMismatchException( - "A spec class with all its ancestors cannot set both sync and async class-level 'after_all' hooks, they should either be all sync or all async"); - } - - AfterAllInstance.SafeInvoke(instance); - - AfterAllInstanceAsync.SafeInvoke(instance); - } - public void AddExample(ExampleBase example) { example.Context = this; @@ -243,7 +65,7 @@ public virtual void Run(bool failFast, nspec instance = null, bool recurse = tru using (new ConsoleCatcher(output => this.CapturedOutput = output)) { - if (runBeforeAfterAll) RunAndHandleException(RunBeforeAll, nspec, ref ExceptionBeforeAll); + if (runBeforeAfterAll) RunAndHandleException(BeforeAllChain.Run, nspec, ref ExceptionBeforeAll); } // evaluate again, after running this context `beforeAll` @@ -268,7 +90,7 @@ public virtual void Run(bool failFast, nspec instance = null, bool recurse = tru } // TODO wrap this as well in a ConsoleCatcher, not before adding tests about it - if (runBeforeAfterAll) RunAndHandleException(RunAfterAll, nspec, ref ExceptionAfterAll); + if (runBeforeAfterAll) RunAndHandleException(AfterAllChain.Run, nspec, ref ExceptionAfterAll); } /// @@ -396,16 +218,16 @@ public void Exercise(ExampleBase example, nspec nspec, bool anyBeforeAllThrew) if (!anyBeforeAllThrew) { - bool exceptionThrownInBefores = RunAndHandleException(RunBefores, nspec, ref ExceptionBeforeAct); + bool exceptionThrownInBefores = RunAndHandleException(BeforeChain.Run, nspec, ref ExceptionBeforeAct); if (!exceptionThrownInBefores) { - RunAndHandleException(RunActs, nspec, ref ExceptionBeforeAct); + RunAndHandleException(ActChain.Run, nspec, ref ExceptionBeforeAct); RunAndHandleException(example.Run, nspec, ref example.Exception); } - bool exceptionThrownInAfters = RunAndHandleException(RunAfters, nspec, ref ExceptionAfter); + bool exceptionThrownInAfters = RunAndHandleException(AfterChain.Run, nspec, ref ExceptionAfter); // 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 @@ -486,7 +308,7 @@ public override string ToString() }); } - void RecurseAncestors(Action ancestorAction) + public void RecurseAncestors(Action ancestorAction) { if (Parent != null) ancestorAction(Parent); } @@ -506,13 +328,138 @@ void WriteAncestors(ILiveFormatter formatter) alreadyWritten = true; } + // Context-level hook wrappers + + public Action BeforeAll + { + get { return BeforeAllChain.Hook; } + //set { BeforeAllChain.Hook = value; } + } + + public Action Before + { + get { return BeforeChain.Hook; } + //set { BeforeChain.Hook = value; } + } + + public Action Act + { + get { return ActChain.Hook; } + //set { ActChain.Hook = value; } + } + + public Action After + { + get { return AfterChain.Hook; } + //set { AfterChain.Hook = value; } + } + + public Action AfterAll + { + get { return AfterAllChain.Hook; } + //set { AfterAllChain.Hook = value; } + } + + // Class/method-level hook wrappers + + public Action BeforeAllInstance + { + get { return BeforeAllChain.ClassHook; } + } + + public Action BeforeInstance + { + get { return BeforeChain.ClassHook; } + } + + public Action ActInstance + { + get { return ActChain.ClassHook; } + } + + public Action AfterInstance + { + get { return AfterChain.ClassHook; } + } + + public Action AfterAllInstance + { + get { return AfterAllChain.ClassHook; } + } + + // Context-level async hook wrappers + + public Func BeforeAllAsync + { + get { return BeforeAllChain.AsyncHook; } + //set { BeforeAllChain.AsyncHook = value; } + } + + public Func BeforeAsync + { + get { return BeforeChain.AsyncHook; } + //set { BeforeChain.AsyncHook = value; } + } + + public Func ActAsync + { + get { return ActChain.AsyncHook; } + //set { ActChain.AsyncHook = value; } + } + + public Func AfterAsync + { + get { return AfterChain.AsyncHook; } + //set { AfterChain.AsyncHook = value; } + } + + public Func AfterAllAsync + { + get { return AfterChain.AsyncHook; } + //set { AfterChain.AsyncHook = value; } + } + + // Class/method-level async hook wrappers + + public Action BeforeAllInstanceAsync + { + get { return BeforeAllChain.AsyncClassHook; } + } + + public Action BeforeInstanceAsync + { + get { return BeforeChain.AsyncClassHook; } + } + + public Action ActInstanceAsync + { + get { return ActChain.AsyncClassHook; } + } + + public Action AfterInstanceAsync + { + get { return AfterChain.AsyncClassHook; } + } + + public Action AfterAllInstanceAsync + { + get { return AfterAllChain.AsyncClassHook; } + } + public Context(string name = "", string tags = null, bool isPending = false) { Name = name.Replace("_", " "); - Examples = new List(); - Contexts = new ContextCollection(); Tags = Domain.Tags.ParseTags(tags); this.isPending = isPending; + + Examples = new List(); + Contexts = new ContextCollection(); + + BeforeAllChain = new BeforeAllChain(); + BeforeChain = new BeforeChain(this); + ActChain = new ActChain(this); + AfterChain = new AfterChain(this); + AfterAllChain = new AfterAllChain(); } public string Name; @@ -520,16 +467,17 @@ public Context(string name = "", string tags = null, bool isPending = false) public List Tags; public List Examples; public ContextCollection Contexts; - public Action Before, Act, After, BeforeAll, AfterAll; - public Action BeforeInstance, ActInstance, AfterInstance, BeforeAllInstance, AfterAllInstance; - public Func BeforeAsync, ActAsync, AfterAsync, BeforeAllAsync, AfterAllAsync; - public Action BeforeInstanceAsync, ActInstanceAsync, AfterInstanceAsync, BeforeAllInstanceAsync, AfterAllInstanceAsync; - public Context Parent; + public BeforeAllChain BeforeAllChain; + public BeforeChain BeforeChain; + public ActChain ActChain; + public AfterChain AfterChain; + public AfterAllChain AfterAllChain; public Exception ExceptionBeforeAll, ExceptionBeforeAct, ExceptionAfter, ExceptionAfterAll; public bool ClearExpectedException; public string CapturedOutput; - + public Context Parent; + nspec savedInstance; bool alreadyWritten, isPending; } -} \ No newline at end of file +} diff --git a/sln/src/NSpec/nspec.cs b/sln/src/NSpec/nspec.cs index a87c7a17..7757e12f 100644 --- a/sln/src/NSpec/nspec.cs +++ b/sln/src/NSpec/nspec.cs @@ -65,8 +65,8 @@ public virtual Expression> xspecifyAsync { ... } /// public virtual Action before { - get { return Context.Before; } - set { Context.Before = value; } + get { return Context.BeforeChain.Hook; } + set { Context.BeforeChain.Hook = value; } } /// @@ -77,8 +77,8 @@ public virtual Action before /// public virtual Func beforeAsync { - get { return Context.BeforeAsync; } - set { Context.BeforeAsync = value; } + get { return Context.BeforeChain.AsyncHook; } + set { Context.BeforeChain.AsyncHook = value; } } /// @@ -89,8 +89,8 @@ public virtual Func beforeAsync /// public virtual Action beforeEach { - get { return Context.Before; } - set { Context.Before = value; } + get { return Context.BeforeChain.Hook; } + set { Context.BeforeChain.Hook = value; } } /// @@ -101,8 +101,8 @@ public virtual Action beforeEach /// public virtual Func beforeEachAsync { - get { return Context.BeforeAsync; } - set { Context.BeforeAsync = value; } + get { return Context.BeforeChain.AsyncHook; } + set { Context.BeforeChain.AsyncHook = value; } } /// @@ -113,8 +113,8 @@ public virtual Func beforeEachAsync /// public virtual Action beforeAll { - get { return Context.BeforeAll; } - set { Context.BeforeAll = value; } + get { return Context.BeforeAllChain.Hook; } + set { Context.BeforeAllChain.Hook = value; } } /// @@ -125,8 +125,8 @@ public virtual Action beforeAll /// public virtual Func beforeAllAsync { - get { return Context.BeforeAllAsync; } - set { Context.BeforeAllAsync = value; } + get { return Context.BeforeAllChain.AsyncHook; } + set { Context.BeforeAllChain.AsyncHook = value; } } /// @@ -137,8 +137,8 @@ public virtual Func beforeAllAsync /// public virtual Action after { - get { return Context.After; } - set { Context.After = value; } + get { return Context.AfterChain.Hook; } + set { Context.AfterChain.Hook = value; } } /// @@ -149,8 +149,8 @@ public virtual Action after /// public virtual Func afterAsync { - get { return Context.AfterAsync; } - set { Context.AfterAsync = value; } + get { return Context.AfterChain.AsyncHook; } + set { Context.AfterChain.AsyncHook = value; } } /// @@ -161,8 +161,8 @@ public virtual Func afterAsync /// public virtual Action afterEach { - get { return Context.After; } - set { Context.After = value; } + get { return Context.AfterChain.Hook; } + set { Context.AfterChain.Hook = value; } } /// @@ -173,8 +173,8 @@ public virtual Action afterEach /// public virtual Func afterEachAsync { - get { return Context.AfterAsync; } - set { Context.AfterAsync = value; } + get { return Context.AfterChain.AsyncHook; } + set { Context.AfterChain.AsyncHook = value; } } /// @@ -185,8 +185,8 @@ public virtual Func afterEachAsync /// public virtual Action afterAll { - get { return Context.AfterAll; } - set { Context.AfterAll = value; } + get { return Context.AfterAllChain.Hook; } + set { Context.AfterAllChain.Hook = value; } } /// @@ -197,8 +197,8 @@ public virtual Action afterAll /// public virtual Func afterAllAsync { - get { return Context.AfterAllAsync; } - set { Context.AfterAllAsync = value; } + get { return Context.AfterAllChain.AsyncHook; } + set { Context.AfterAllChain.AsyncHook = value; } } /// @@ -207,8 +207,8 @@ public virtual Func afterAllAsync /// public virtual Action act { - get { return Context.Act; } - set { Context.Act = value; } + get { return Context.ActChain.Hook; } + set { Context.ActChain.Hook = value; } } /// @@ -217,8 +217,8 @@ public virtual Action act /// public virtual Func actAsync { - get { return Context.ActAsync; } - set { Context.ActAsync = value; } + get { return Context.ActChain.AsyncHook; } + set { Context.ActChain.AsyncHook = value; } } /// diff --git a/sln/test/NSpec.Tests/describe_Context.cs b/sln/test/NSpec.Tests/describe_Context.cs index a38b148e..ba02d694 100644 --- a/sln/test/NSpec.Tests/describe_Context.cs +++ b/sln/test/NSpec.Tests/describe_Context.cs @@ -76,7 +76,7 @@ public void setup() [Test] public void should_run_the_acts_in_the_right_order() { - childContext.RunActs(instance); + childContext.ActChain.Run(instance); instance.actResult.Should().Be("parentchild"); } @@ -178,7 +178,7 @@ public void setup() [Test] public void should_run_the_befores_in_the_proper_order() { - childContext.RunBefores(instance); + childContext.BeforeChain.Run(instance); instance.beforeResult.Should().Be("parentchild"); } From 3a9a86442752b8afbd5a295550cfe89cb7e6767e Mon Sep 17 00:00:00 2001 From: Giuseppe Piscopo Date: Fri, 19 May 2017 11:38:44 +0200 Subject: [PATCH 02/26] fix(context): afterAll hook is incorrectly bound to after hook --- sln/src/NSpec/Domain/Context.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sln/src/NSpec/Domain/Context.cs b/sln/src/NSpec/Domain/Context.cs index f48239bd..cbc7f3ed 100644 --- a/sln/src/NSpec/Domain/Context.cs +++ b/sln/src/NSpec/Domain/Context.cs @@ -415,8 +415,8 @@ public Func AfterAsync public Func AfterAllAsync { - get { return AfterChain.AsyncHook; } - //set { AfterChain.AsyncHook = value; } + get { return AfterAllChain.AsyncHook; } + //set { AfterAllChain.AsyncHook = value; } } // Class/method-level async hook wrappers From 01a0bdc1c5e1b2bd780b5bffb6f8157d8b8424db Mon Sep 17 00:00:00 2001 From: Giuseppe Piscopo Date: Fri, 19 May 2017 11:45:01 +0200 Subject: [PATCH 03/26] refactor(context): expose wrappers to set hooks --- sln/src/NSpec/Domain/Context.cs | 20 ++++++------ sln/src/NSpec/nspec.cs | 56 ++++++++++++++++----------------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/sln/src/NSpec/Domain/Context.cs b/sln/src/NSpec/Domain/Context.cs index cbc7f3ed..b65ad42c 100644 --- a/sln/src/NSpec/Domain/Context.cs +++ b/sln/src/NSpec/Domain/Context.cs @@ -333,31 +333,31 @@ void WriteAncestors(ILiveFormatter formatter) public Action BeforeAll { get { return BeforeAllChain.Hook; } - //set { BeforeAllChain.Hook = value; } + set { BeforeAllChain.Hook = value; } } public Action Before { get { return BeforeChain.Hook; } - //set { BeforeChain.Hook = value; } + set { BeforeChain.Hook = value; } } public Action Act { get { return ActChain.Hook; } - //set { ActChain.Hook = value; } + set { ActChain.Hook = value; } } public Action After { get { return AfterChain.Hook; } - //set { AfterChain.Hook = value; } + set { AfterChain.Hook = value; } } public Action AfterAll { get { return AfterAllChain.Hook; } - //set { AfterAllChain.Hook = value; } + set { AfterAllChain.Hook = value; } } // Class/method-level hook wrappers @@ -392,31 +392,31 @@ public Action AfterAllInstance public Func BeforeAllAsync { get { return BeforeAllChain.AsyncHook; } - //set { BeforeAllChain.AsyncHook = value; } + set { BeforeAllChain.AsyncHook = value; } } public Func BeforeAsync { get { return BeforeChain.AsyncHook; } - //set { BeforeChain.AsyncHook = value; } + set { BeforeChain.AsyncHook = value; } } public Func ActAsync { get { return ActChain.AsyncHook; } - //set { ActChain.AsyncHook = value; } + set { ActChain.AsyncHook = value; } } public Func AfterAsync { get { return AfterChain.AsyncHook; } - //set { AfterChain.AsyncHook = value; } + set { AfterChain.AsyncHook = value; } } public Func AfterAllAsync { get { return AfterAllChain.AsyncHook; } - //set { AfterAllChain.AsyncHook = value; } + set { AfterAllChain.AsyncHook = value; } } // Class/method-level async hook wrappers diff --git a/sln/src/NSpec/nspec.cs b/sln/src/NSpec/nspec.cs index 7757e12f..a87c7a17 100644 --- a/sln/src/NSpec/nspec.cs +++ b/sln/src/NSpec/nspec.cs @@ -65,8 +65,8 @@ public virtual Expression> xspecifyAsync { ... } /// public virtual Action before { - get { return Context.BeforeChain.Hook; } - set { Context.BeforeChain.Hook = value; } + get { return Context.Before; } + set { Context.Before = value; } } /// @@ -77,8 +77,8 @@ public virtual Action before /// public virtual Func beforeAsync { - get { return Context.BeforeChain.AsyncHook; } - set { Context.BeforeChain.AsyncHook = value; } + get { return Context.BeforeAsync; } + set { Context.BeforeAsync = value; } } /// @@ -89,8 +89,8 @@ public virtual Func beforeAsync /// public virtual Action beforeEach { - get { return Context.BeforeChain.Hook; } - set { Context.BeforeChain.Hook = value; } + get { return Context.Before; } + set { Context.Before = value; } } /// @@ -101,8 +101,8 @@ public virtual Action beforeEach /// public virtual Func beforeEachAsync { - get { return Context.BeforeChain.AsyncHook; } - set { Context.BeforeChain.AsyncHook = value; } + get { return Context.BeforeAsync; } + set { Context.BeforeAsync = value; } } /// @@ -113,8 +113,8 @@ public virtual Func beforeEachAsync /// public virtual Action beforeAll { - get { return Context.BeforeAllChain.Hook; } - set { Context.BeforeAllChain.Hook = value; } + get { return Context.BeforeAll; } + set { Context.BeforeAll = value; } } /// @@ -125,8 +125,8 @@ public virtual Action beforeAll /// public virtual Func beforeAllAsync { - get { return Context.BeforeAllChain.AsyncHook; } - set { Context.BeforeAllChain.AsyncHook = value; } + get { return Context.BeforeAllAsync; } + set { Context.BeforeAllAsync = value; } } /// @@ -137,8 +137,8 @@ public virtual Func beforeAllAsync /// public virtual Action after { - get { return Context.AfterChain.Hook; } - set { Context.AfterChain.Hook = value; } + get { return Context.After; } + set { Context.After = value; } } /// @@ -149,8 +149,8 @@ public virtual Action after /// public virtual Func afterAsync { - get { return Context.AfterChain.AsyncHook; } - set { Context.AfterChain.AsyncHook = value; } + get { return Context.AfterAsync; } + set { Context.AfterAsync = value; } } /// @@ -161,8 +161,8 @@ public virtual Func afterAsync /// public virtual Action afterEach { - get { return Context.AfterChain.Hook; } - set { Context.AfterChain.Hook = value; } + get { return Context.After; } + set { Context.After = value; } } /// @@ -173,8 +173,8 @@ public virtual Action afterEach /// public virtual Func afterEachAsync { - get { return Context.AfterChain.AsyncHook; } - set { Context.AfterChain.AsyncHook = value; } + get { return Context.AfterAsync; } + set { Context.AfterAsync = value; } } /// @@ -185,8 +185,8 @@ public virtual Func afterEachAsync /// public virtual Action afterAll { - get { return Context.AfterAllChain.Hook; } - set { Context.AfterAllChain.Hook = value; } + get { return Context.AfterAll; } + set { Context.AfterAll = value; } } /// @@ -197,8 +197,8 @@ public virtual Action afterAll /// public virtual Func afterAllAsync { - get { return Context.AfterAllChain.AsyncHook; } - set { Context.AfterAllChain.AsyncHook = value; } + get { return Context.AfterAllAsync; } + set { Context.AfterAllAsync = value; } } /// @@ -207,8 +207,8 @@ public virtual Func afterAllAsync /// public virtual Action act { - get { return Context.ActChain.Hook; } - set { Context.ActChain.Hook = value; } + get { return Context.Act; } + set { Context.Act = value; } } /// @@ -217,8 +217,8 @@ public virtual Action act /// public virtual Func actAsync { - get { return Context.ActChain.AsyncHook; } - set { Context.ActChain.AsyncHook = value; } + get { return Context.ActAsync; } + set { Context.ActAsync = value; } } /// From 882b14105038276b9819fe891789d87a4a5fbd84 Mon Sep 17 00:00:00 2001 From: GiuseppePiscopo Date: Fri, 19 May 2017 19:50:21 +0200 Subject: [PATCH 04/26] refactor(context): move beforeAll/afterAll exceptions to dedicated classes --- sln/src/NSpec/Domain/AfterAllChain.cs | 34 +++++++++++++++++++ sln/src/NSpec/Domain/BeforeAllChain.cs | 34 +++++++++++++++++++ sln/src/NSpec/Domain/ChainUtils.cs | 24 +++++++++++++ sln/src/NSpec/Domain/Context.cs | 47 +++++++++++--------------- sln/test/NSpec.Tests/WrittenContext.cs | 8 ++--- 5 files changed, 116 insertions(+), 31 deletions(-) diff --git a/sln/src/NSpec/Domain/AfterAllChain.cs b/sln/src/NSpec/Domain/AfterAllChain.cs index db8189b4..b7c62fe3 100644 --- a/sln/src/NSpec/Domain/AfterAllChain.cs +++ b/sln/src/NSpec/Domain/AfterAllChain.cs @@ -31,6 +31,14 @@ public void BuildMethodLevel(Conventions conventions, List classHierarchy) } public void Run(nspec instance) + { + if (CanRun(instance)) + { + ChainUtils.RunAndHandleException(RunHooks, instance, ref Exception); + } + } + + void RunHooks(nspec instance) { // context-level @@ -63,10 +71,36 @@ public void Run(nspec instance) AsyncClassHook.SafeInvoke(instance); } + bool CanRun(nspec instance) + { + return AncestorBeforeAllsThrew() + ? false + : context.AnyUnfilteredExampleInSubTree(instance); + } + + bool AnyBeforeAllsThrew() + { + return (Exception != null || AncestorBeforeAllsThrew()); + } + + bool AncestorBeforeAllsThrew() + { + return (context.Parent?.AfterAllChain.AnyBeforeAllsThrew() ?? false); + } + + public AfterAllChain(Context context) + { + this.context = context; + } + public Action Hook; public Func AsyncHook; public Action ClassHook { get; private set; } public Action AsyncClassHook { get; private set; } + + public Exception Exception; + + readonly Context context; } } diff --git a/sln/src/NSpec/Domain/BeforeAllChain.cs b/sln/src/NSpec/Domain/BeforeAllChain.cs index 5d537483..b5a22d9d 100644 --- a/sln/src/NSpec/Domain/BeforeAllChain.cs +++ b/sln/src/NSpec/Domain/BeforeAllChain.cs @@ -27,6 +27,14 @@ public void BuildMethodLevel(Conventions conventions, List classHierarchy) } public void Run(nspec instance) + { + if (CanRun(instance)) + { + ChainUtils.RunAndHandleException(RunHooks, instance, ref Exception); + } + } + + void RunHooks(nspec instance) { // context-level @@ -59,10 +67,36 @@ public void Run(nspec instance) AsyncClassHook.SafeInvoke(instance); } + bool CanRun(nspec instance) + { + return AncestorBeforeAllsThrew() + ? false + : context.AnyUnfilteredExampleInSubTree(instance); + } + + public bool AnyBeforeAllsThrew() + { + return (Exception != null || AncestorBeforeAllsThrew()); + } + + bool AncestorBeforeAllsThrew() + { + return (context.Parent?.BeforeAllChain.AnyBeforeAllsThrew() ?? false); + } + + public BeforeAllChain(Context context) + { + this.context = context; + } + public Action Hook; public Func AsyncHook; public Action ClassHook { get; private set; } public Action AsyncClassHook { get; private set; } + + public Exception Exception; + + readonly Context context; } } diff --git a/sln/src/NSpec/Domain/ChainUtils.cs b/sln/src/NSpec/Domain/ChainUtils.cs index 9e0fd69b..f07ff1d7 100644 --- a/sln/src/NSpec/Domain/ChainUtils.cs +++ b/sln/src/NSpec/Domain/ChainUtils.cs @@ -15,5 +15,29 @@ public static List GetMethodsFromHierarchy( .Where(m => m != null) .ToList(); } + + public static bool RunAndHandleException(Action action, nspec instance, ref Exception exceptionToSet) + { + bool hasThrown = false; + + try + { + action(instance); + } + catch (TargetInvocationException invocationException) + { + if (exceptionToSet == null) exceptionToSet = instance.ExceptionToReturn(invocationException.InnerException); + + hasThrown = true; + } + catch (Exception exception) + { + if (exceptionToSet == null) exceptionToSet = instance.ExceptionToReturn(exception); + + hasThrown = true; + } + + return hasThrown; + } } } diff --git a/sln/src/NSpec/Domain/Context.cs b/sln/src/NSpec/Domain/Context.cs index b65ad42c..8803a183 100644 --- a/sln/src/NSpec/Domain/Context.cs +++ b/sln/src/NSpec/Domain/Context.cs @@ -59,17 +59,15 @@ public virtual void Run(bool failFast, nspec instance = null, bool recurse = tru { if (failFast && Parent.HasAnyFailures()) return; - var nspec = savedInstance ?? instance; - - bool runBeforeAfterAll = !AnyBeforeAllThrew() && AnyUnfilteredExampleInSubTree(nspec); + var nspec = builtInstance ?? instance; using (new ConsoleCatcher(output => this.CapturedOutput = output)) { - if (runBeforeAfterAll) RunAndHandleException(BeforeAllChain.Run, nspec, ref ExceptionBeforeAll); + BeforeAllChain.Run(nspec); } // evaluate again, after running this context `beforeAll` - bool anyBeforeAllThrew = AnyBeforeAllThrew(); + bool anyBeforeAllThrew = BeforeAllChain.AnyBeforeAllsThrew(); // intentionally using for loop to prevent collection was modified error in sample specs for (int i = 0; i < Examples.Count; i++) @@ -90,7 +88,7 @@ public virtual void Run(bool failFast, nspec instance = null, bool recurse = tru } // TODO wrap this as well in a ConsoleCatcher, not before adding tests about it - if (runBeforeAfterAll) RunAndHandleException(AfterAllChain.Run, nspec, ref ExceptionAfterAll); + AfterAllChain.Run(nspec); } /// @@ -108,8 +106,8 @@ public virtual void AssignExceptions(bool recurse = true) protected virtual void AssignExceptions(Exception inheritedBeforeAllException, Exception inheritedAfterAllException, bool recurse) { - inheritedBeforeAllException = inheritedBeforeAllException ?? ExceptionBeforeAll; - inheritedAfterAllException = ExceptionAfterAll ?? inheritedAfterAllException; + inheritedBeforeAllException = inheritedBeforeAllException ?? BeforeAllChain.Exception; + inheritedAfterAllException = AfterAllChain.Exception ?? inheritedAfterAllException; // if an exception was thrown before the example (either `before` or `act`) but was expected, ignore it Exception unexpectedException = ClearExpectedException ? null : ExceptionBeforeAct; @@ -164,7 +162,7 @@ public virtual void Build(nspec instance = null) { instance.Context = this; - savedInstance = instance; + builtInstance = instance; Contexts.Do(c => c.Build(instance)); } @@ -174,23 +172,23 @@ public string FullContext() return Parent != null ? Parent.FullContext() + ". " + Name : Name; } - static bool RunAndHandleException(Action action, nspec nspec, ref Exception exceptionToSet) + static bool RunAndHandleException(Action action, nspec instance, ref Exception exceptionToSet) { bool hasThrown = false; try { - action(nspec); + action(instance); } catch (TargetInvocationException invocationException) { - if (exceptionToSet == null) exceptionToSet = nspec.ExceptionToReturn(invocationException.InnerException); + if (exceptionToSet == null) exceptionToSet = instance.ExceptionToReturn(invocationException.InnerException); hasThrown = true; } catch (Exception exception) { - if (exceptionToSet == null) exceptionToSet = nspec.ExceptionToReturn(exception); + if (exceptionToSet == null) exceptionToSet = instance.ExceptionToReturn(exception); hasThrown = true; } @@ -245,7 +243,7 @@ public virtual bool IsSub(Type baseType) public nspec GetInstance() { - return savedInstance ?? Parent.GetInstance(); + return builtInstance ?? Parent.GetInstance(); } public IEnumerable AllContexts() @@ -277,22 +275,17 @@ public void TrimSkippedDescendants() Contexts.Do(c => c.TrimSkippedDescendants()); } - bool AnyUnfilteredExampleInSubTree(nspec instance) + public bool AnyUnfilteredExampleInSubTree(nspec instance) { Func shouldNotSkip = e => e.ShouldNotSkip(instance.tagsFilter); - bool anyExampleOrSubExample = Examples.Any(shouldNotSkip) || Contexts.Examples().Any(shouldNotSkip); + 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 levelText = $"L{Level}"; @@ -455,11 +448,11 @@ public Context(string name = "", string tags = null, bool isPending = false) Examples = new List(); Contexts = new ContextCollection(); - BeforeAllChain = new BeforeAllChain(); + BeforeAllChain = new BeforeAllChain(this); BeforeChain = new BeforeChain(this); ActChain = new ActChain(this); AfterChain = new AfterChain(this); - AfterAllChain = new AfterAllChain(); + AfterAllChain = new AfterAllChain(this); } public string Name; @@ -472,12 +465,12 @@ public Context(string name = "", string tags = null, bool isPending = false) public ActChain ActChain; public AfterChain AfterChain; public AfterAllChain AfterAllChain; - public Exception ExceptionBeforeAll, ExceptionBeforeAct, ExceptionAfter, ExceptionAfterAll; + public Exception ExceptionBeforeAct, ExceptionAfter; public bool ClearExpectedException; public string CapturedOutput; public Context Parent; - nspec savedInstance; + nspec builtInstance; bool alreadyWritten, isPending; } } diff --git a/sln/test/NSpec.Tests/WrittenContext.cs b/sln/test/NSpec.Tests/WrittenContext.cs index b00fd1fe..b59ad984 100644 --- a/sln/test/NSpec.Tests/WrittenContext.cs +++ b/sln/test/NSpec.Tests/WrittenContext.cs @@ -14,10 +14,10 @@ public WrittenContext(Context context) Name = context.Name; Level = context.Level; Tags = new List(context.Tags); - ExceptionBeforeAll = context.ExceptionBeforeAll; + BeforeAllException = context.BeforeAllChain.Exception; ExceptionBeforeAct = context.ExceptionBeforeAct; ExceptionAfter = context.ExceptionAfter; - ExceptionAfterAll = context.ExceptionAfterAll; + AfterAllException = context.AfterAllChain.Exception; ClearExpectedException = context.ClearExpectedException; CapturedOutput = context.CapturedOutput; IsPending = context.IsPending(); @@ -32,13 +32,13 @@ public WrittenContext(Context context) public List Tags { get; private set; } - public Exception ExceptionBeforeAll { get; private set; } + public Exception BeforeAllException { get; private set; } public Exception ExceptionBeforeAct { get; private set; } public Exception ExceptionAfter { get; private set; } - public Exception ExceptionAfterAll { get; private set; } + public Exception AfterAllException { get; private set; } public bool ClearExpectedException { get; private set; } From c2c622c691009f04f4cbcb79d26f0aaa15eee6b7 Mon Sep 17 00:00:00 2001 From: GiuseppePiscopo Date: Fri, 19 May 2017 20:43:17 +0200 Subject: [PATCH 05/26] refactor(context): reuse `RunAndHandleException` --- sln/src/NSpec/Domain/Context.cs | 34 +++++---------------------------- 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/sln/src/NSpec/Domain/Context.cs b/sln/src/NSpec/Domain/Context.cs index 8803a183..76517fb0 100644 --- a/sln/src/NSpec/Domain/Context.cs +++ b/sln/src/NSpec/Domain/Context.cs @@ -172,30 +172,6 @@ public string FullContext() return Parent != null ? Parent.FullContext() + ". " + Name : Name; } - static bool RunAndHandleException(Action action, nspec instance, ref Exception exceptionToSet) - { - bool hasThrown = false; - - try - { - action(instance); - } - catch (TargetInvocationException invocationException) - { - if (exceptionToSet == null) exceptionToSet = instance.ExceptionToReturn(invocationException.InnerException); - - hasThrown = true; - } - catch (Exception exception) - { - if (exceptionToSet == null) exceptionToSet = instance.ExceptionToReturn(exception); - - hasThrown = true; - } - - return hasThrown; - } - public void Exercise(ExampleBase example, nspec nspec, bool anyBeforeAllThrew) { if (example.ShouldSkip(nspec.tagsFilter)) @@ -207,7 +183,7 @@ public void Exercise(ExampleBase example, nspec nspec, bool anyBeforeAllThrew) if (example.Pending) { - RunAndHandleException(example.RunPending, nspec, ref example.Exception); + ChainUtils.RunAndHandleException(example.RunPending, nspec, ref example.Exception); return; } @@ -216,16 +192,16 @@ public void Exercise(ExampleBase example, nspec nspec, bool anyBeforeAllThrew) if (!anyBeforeAllThrew) { - bool exceptionThrownInBefores = RunAndHandleException(BeforeChain.Run, nspec, ref ExceptionBeforeAct); + bool exceptionThrownInBefores = ChainUtils.RunAndHandleException(BeforeChain.Run, nspec, ref ExceptionBeforeAct); if (!exceptionThrownInBefores) { - RunAndHandleException(ActChain.Run, nspec, ref ExceptionBeforeAct); + ChainUtils.RunAndHandleException(ActChain.Run, nspec, ref ExceptionBeforeAct); - RunAndHandleException(example.Run, nspec, ref example.Exception); + ChainUtils.RunAndHandleException(example.Run, nspec, ref example.Exception); } - bool exceptionThrownInAfters = RunAndHandleException(AfterChain.Run, nspec, ref ExceptionAfter); + bool exceptionThrownInAfters = ChainUtils.RunAndHandleException(AfterChain.Run, nspec, ref ExceptionAfter); // 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 From e99360b5ccb19bd385bff2a183a4a17b46a3b8e9 Mon Sep 17 00:00:00 2001 From: GiuseppePiscopo Date: Fri, 19 May 2017 20:46:36 +0200 Subject: [PATCH 06/26] refactor(afterAll): reuse checks from beforeAll chain --- sln/src/NSpec/Domain/AfterAllChain.cs | 12 +----------- sln/src/NSpec/Domain/BeforeAllChain.cs | 2 +- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/sln/src/NSpec/Domain/AfterAllChain.cs b/sln/src/NSpec/Domain/AfterAllChain.cs index b7c62fe3..9c1f4948 100644 --- a/sln/src/NSpec/Domain/AfterAllChain.cs +++ b/sln/src/NSpec/Domain/AfterAllChain.cs @@ -73,21 +73,11 @@ void RunHooks(nspec instance) bool CanRun(nspec instance) { - return AncestorBeforeAllsThrew() + return context.BeforeAllChain.AncestorBeforeAllsThrew() ? false : context.AnyUnfilteredExampleInSubTree(instance); } - bool AnyBeforeAllsThrew() - { - return (Exception != null || AncestorBeforeAllsThrew()); - } - - bool AncestorBeforeAllsThrew() - { - return (context.Parent?.AfterAllChain.AnyBeforeAllsThrew() ?? false); - } - public AfterAllChain(Context context) { this.context = context; diff --git a/sln/src/NSpec/Domain/BeforeAllChain.cs b/sln/src/NSpec/Domain/BeforeAllChain.cs index b5a22d9d..729f0f79 100644 --- a/sln/src/NSpec/Domain/BeforeAllChain.cs +++ b/sln/src/NSpec/Domain/BeforeAllChain.cs @@ -79,7 +79,7 @@ public bool AnyBeforeAllsThrew() return (Exception != null || AncestorBeforeAllsThrew()); } - bool AncestorBeforeAllsThrew() + public bool AncestorBeforeAllsThrew() { return (context.Parent?.BeforeAllChain.AnyBeforeAllsThrew() ?? false); } From f8d6eb7a27de2f725ada44c78b6490c56a72842c Mon Sep 17 00:00:00 2001 From: GiuseppePiscopo Date: Sat, 20 May 2017 12:40:25 +0200 Subject: [PATCH 07/26] refactor(capturing): reducing capturing area to ease adding debug logs --- sln/src/NSpec/Domain/Context.cs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/sln/src/NSpec/Domain/Context.cs b/sln/src/NSpec/Domain/Context.cs index 76517fb0..1fbbb8ec 100644 --- a/sln/src/NSpec/Domain/Context.cs +++ b/sln/src/NSpec/Domain/Context.cs @@ -76,10 +76,7 @@ public virtual void Run(bool failFast, nspec instance = null, bool recurse = tru if (failFast && example.Context.HasAnyFailures()) return; - using (new ConsoleCatcher(output => example.CapturedOutput = output)) - { - Exercise(example, nspec, anyBeforeAllThrew); - } + Exercise(example, nspec, anyBeforeAllThrew); } if (recurse) @@ -192,16 +189,22 @@ public void Exercise(ExampleBase example, nspec nspec, bool anyBeforeAllThrew) if (!anyBeforeAllThrew) { - bool exceptionThrownInBefores = ChainUtils.RunAndHandleException(BeforeChain.Run, nspec, ref ExceptionBeforeAct); + bool exceptionThrownInBefores; + bool exceptionThrownInAfters; - if (!exceptionThrownInBefores) + using (new ConsoleCatcher(output => example.CapturedOutput = output)) { - ChainUtils.RunAndHandleException(ActChain.Run, nspec, ref ExceptionBeforeAct); + exceptionThrownInBefores = ChainUtils.RunAndHandleException(BeforeChain.Run, nspec, ref ExceptionBeforeAct); - ChainUtils.RunAndHandleException(example.Run, nspec, ref example.Exception); - } + if (!exceptionThrownInBefores) + { + ChainUtils.RunAndHandleException(ActChain.Run, nspec, ref ExceptionBeforeAct); + + ChainUtils.RunAndHandleException(example.Run, nspec, ref example.Exception); + } - bool exceptionThrownInAfters = ChainUtils.RunAndHandleException(AfterChain.Run, nspec, ref ExceptionAfter); + exceptionThrownInAfters = ChainUtils.RunAndHandleException(AfterChain.Run, nspec, ref ExceptionAfter); + } // 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 From 4926a2af6c14b945db12648bf89bb1b3b7bfa2fe Mon Sep 17 00:00:00 2001 From: GiuseppePiscopo Date: Sat, 20 May 2017 16:41:12 +0200 Subject: [PATCH 08/26] refactor(context): move before/act/after exceptions to dedicated classes --- sln/src/NSpec/Domain/ActChain.cs | 19 +++++++++- sln/src/NSpec/Domain/AfterAllChain.cs | 2 ++ sln/src/NSpec/Domain/AfterChain.cs | 17 ++++++++- sln/src/NSpec/Domain/BeforeAllChain.cs | 2 ++ sln/src/NSpec/Domain/BeforeChain.cs | 17 ++++++++- sln/src/NSpec/Domain/Context.cs | 50 ++++++++++++-------------- sln/src/NSpec/nspec.cs | 8 ++--- sln/test/NSpec.Tests/WrittenContext.cs | 11 +++--- 8 files changed, 88 insertions(+), 38 deletions(-) diff --git a/sln/src/NSpec/Domain/ActChain.cs b/sln/src/NSpec/Domain/ActChain.cs index 010de16b..695d202d 100644 --- a/sln/src/NSpec/Domain/ActChain.cs +++ b/sln/src/NSpec/Domain/ActChain.cs @@ -27,10 +27,18 @@ public void BuildMethodLevel(Conventions conventions, List classHierarchy) } public void Run(nspec instance) + { + if (CanRun(instance)) + { + ChainUtils.RunAndHandleException(RunHooks, instance, ref Exception); + } + } + + void RunHooks(nspec instance) { // parent chain - context.RecurseAncestors(c => c.ActChain.Run(instance)); + context.RecurseAncestors(c => c.ActChain.RunHooks(instance)); // class (method-level) @@ -63,6 +71,13 @@ public void Run(nspec instance) AsyncHook.SafeInvoke(); } + bool CanRun(nspec instance) + { + return context.BeforeAllChain.AnyBeforeAllsThrew() + ? false + : (context.BeforeChain.Exception == null); + } + public ActChain(Context context) { this.context = context; @@ -74,6 +89,8 @@ public ActChain(Context context) public Action ClassHook { get; private set; } public Action AsyncClassHook { get; private set; } + public Exception Exception; + readonly Context context; } } diff --git a/sln/src/NSpec/Domain/AfterAllChain.cs b/sln/src/NSpec/Domain/AfterAllChain.cs index 9c1f4948..10e238a4 100644 --- a/sln/src/NSpec/Domain/AfterAllChain.cs +++ b/sln/src/NSpec/Domain/AfterAllChain.cs @@ -69,6 +69,8 @@ void RunHooks(nspec instance) ClassHook.SafeInvoke(instance); AsyncClassHook.SafeInvoke(instance); + + // do NOT traverse parent chain } bool CanRun(nspec instance) diff --git a/sln/src/NSpec/Domain/AfterChain.cs b/sln/src/NSpec/Domain/AfterChain.cs index c8e22857..10ef89b6 100644 --- a/sln/src/NSpec/Domain/AfterChain.cs +++ b/sln/src/NSpec/Domain/AfterChain.cs @@ -31,6 +31,14 @@ public void BuildMethodLevel(Conventions conventions, List classHierarchy) } public void Run(nspec instance) + { + if (CanRun(instance)) + { + ChainUtils.RunAndHandleException(RunHooks, instance, ref Exception); + } + } + + void RunHooks(nspec instance) { // context-level @@ -64,7 +72,12 @@ public void Run(nspec instance) // parent chain - context.RecurseAncestors(c => c.AfterChain.Run(instance)); + context.RecurseAncestors(c => c.AfterChain.RunHooks(instance)); + } + + bool CanRun(nspec instance) + { + return !context.BeforeAllChain.AnyBeforeAllsThrew(); } public AfterChain(Context context) @@ -78,6 +91,8 @@ public AfterChain(Context context) public Action ClassHook { get; private set; } public Action AsyncClassHook { get; private set; } + public Exception Exception; + readonly Context context; } } diff --git a/sln/src/NSpec/Domain/BeforeAllChain.cs b/sln/src/NSpec/Domain/BeforeAllChain.cs index 729f0f79..a23970ff 100644 --- a/sln/src/NSpec/Domain/BeforeAllChain.cs +++ b/sln/src/NSpec/Domain/BeforeAllChain.cs @@ -36,6 +36,8 @@ public void Run(nspec instance) void RunHooks(nspec instance) { + // do NOT traverse parent chain + // context-level if (Hook != null && AsyncHook != null) diff --git a/sln/src/NSpec/Domain/BeforeChain.cs b/sln/src/NSpec/Domain/BeforeChain.cs index 6ad405de..413dedbf 100644 --- a/sln/src/NSpec/Domain/BeforeChain.cs +++ b/sln/src/NSpec/Domain/BeforeChain.cs @@ -27,10 +27,18 @@ public void BuildMethodLevel(Conventions conventions, List classHierarchy) } public void Run(nspec instance) + { + if (CanRun(instance)) + { + ChainUtils.RunAndHandleException(RunHooks, instance, ref Exception); + } + } + + void RunHooks(nspec instance) { // parent chain - context.RecurseAncestors(c => c.BeforeChain.Run(instance)); + context.RecurseAncestors(c => c.BeforeChain.RunHooks(instance)); // class (method-level) @@ -64,6 +72,11 @@ public void Run(nspec instance) AsyncHook.SafeInvoke(); } + bool CanRun(nspec instance) + { + return !context.BeforeAllChain.AnyBeforeAllsThrew(); + } + public BeforeChain(Context context) { this.context = context; @@ -75,6 +88,8 @@ public BeforeChain(Context context) public Action ClassHook { get; private set; } public Action AsyncClassHook { get; private set; } + public Exception Exception; + readonly Context context; } } diff --git a/sln/src/NSpec/Domain/Context.cs b/sln/src/NSpec/Domain/Context.cs index 1fbbb8ec..a1f05759 100644 --- a/sln/src/NSpec/Domain/Context.cs +++ b/sln/src/NSpec/Domain/Context.cs @@ -66,9 +66,6 @@ public virtual void Run(bool failFast, nspec instance = null, bool recurse = tru BeforeAllChain.Run(nspec); } - // evaluate again, after running this context `beforeAll` - bool anyBeforeAllThrew = BeforeAllChain.AnyBeforeAllsThrew(); - // intentionally using for loop to prevent collection was modified error in sample specs for (int i = 0; i < Examples.Count; i++) { @@ -76,7 +73,7 @@ public virtual void Run(bool failFast, nspec instance = null, bool recurse = tru if (failFast && example.Context.HasAnyFailures()) return; - Exercise(example, nspec, anyBeforeAllThrew); + Exercise(example, nspec); } if (recurse) @@ -107,10 +104,10 @@ protected virtual void AssignExceptions(Exception inheritedBeforeAllException, E inheritedAfterAllException = AfterAllChain.Exception ?? inheritedAfterAllException; // if an exception was thrown before the example (either `before` or `act`) but was expected, ignore it - Exception unexpectedException = ClearExpectedException ? null : ExceptionBeforeAct; + Exception unexpectedException = ClearExpectedException ? null : BeforeChain.Exception ?? ActChain.Exception; Exception previousException = inheritedBeforeAllException ?? unexpectedException; - Exception followingException = ExceptionAfter ?? inheritedAfterAllException; + Exception followingException = AfterChain.Exception ?? inheritedAfterAllException; for (int i = 0; i < Examples.Count; i++) { @@ -169,7 +166,7 @@ public string FullContext() return Parent != null ? Parent.FullContext() + ". " + Name : Name; } - public void Exercise(ExampleBase example, nspec nspec, bool anyBeforeAllThrew) + public void Exercise(ExampleBase example, nspec nspec) { if (example.ShouldSkip(nspec.tagsFilter)) { @@ -187,34 +184,34 @@ public void Exercise(ExampleBase example, nspec nspec, bool anyBeforeAllThrew) var stopWatch = example.StartTiming(); - if (!anyBeforeAllThrew) + using (new ConsoleCatcher(output => example.CapturedOutput = output)) { - bool exceptionThrownInBefores; - bool exceptionThrownInAfters; - - using (new ConsoleCatcher(output => example.CapturedOutput = output)) - { - exceptionThrownInBefores = ChainUtils.RunAndHandleException(BeforeChain.Run, nspec, ref ExceptionBeforeAct); + BeforeChain.Run(nspec); - if (!exceptionThrownInBefores) - { - ChainUtils.RunAndHandleException(ActChain.Run, nspec, ref ExceptionBeforeAct); + ActChain.Run(nspec); - ChainUtils.RunAndHandleException(example.Run, nspec, ref example.Exception); - } + RunExample(example, nspec); - exceptionThrownInAfters = ChainUtils.RunAndHandleException(AfterChain.Run, nspec, ref ExceptionAfter); - } + AfterChain.Run(nspec); + } - // 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 (AfterChain.Exception != null) ClearExpectedException = false; example.StopTiming(stopWatch); } + void RunExample(ExampleBase example, nspec nspec) + { + if (BeforeAllChain.AnyBeforeAllsThrew()) return; + + if (BeforeChain.Exception != null) return; + + ChainUtils.RunAndHandleException(example.Run, nspec, ref example.Exception); + } + public virtual bool IsSub(Type baseType) { return false; @@ -271,7 +268,7 @@ public override string ToString() string exampleText = $"{Examples.Count} exm"; string contextText = $"{Contexts.Count} exm"; - var exception = ExceptionBeforeAct ?? ExceptionAfter; + var exception = BeforeChain.Exception ?? ActChain.Exception ?? AfterChain.Exception; string exceptionText = exception?.GetType().Name ?? String.Empty; return String.Join(",", new [] @@ -444,7 +441,6 @@ public Context(string name = "", string tags = null, bool isPending = false) public ActChain ActChain; public AfterChain AfterChain; public AfterAllChain AfterAllChain; - public Exception ExceptionBeforeAct, ExceptionAfter; public bool ClearExpectedException; public string CapturedOutput; public Context Parent; diff --git a/sln/src/NSpec/nspec.cs b/sln/src/NSpec/nspec.cs index a87c7a17..30c07999 100644 --- a/sln/src/NSpec/nspec.cs +++ b/sln/src/NSpec/nspec.cs @@ -296,8 +296,8 @@ public Action expectNoExceptions return () => { - if (specContext.ExceptionBeforeAct != null) - throw specContext.ExceptionBeforeAct; + if (specContext.ActChain.Exception != null) + throw specContext.ActChain.Exception; }; } } @@ -323,10 +323,10 @@ public virtual Action expect(string expectedMessage) where T : Exception return () => { - if (specContext.ExceptionBeforeAct == null) + if (specContext.ActChain.Exception == null) throw new ExceptionNotThrown(IncorrectType()); - AssertExpectedException(specContext.ExceptionBeforeAct, expectedMessage); + AssertExpectedException(specContext.ActChain.Exception, expectedMessage); // do not clear exception right now, during first phase, but leave a note for second phase specContext.ClearExpectedException = true; diff --git a/sln/test/NSpec.Tests/WrittenContext.cs b/sln/test/NSpec.Tests/WrittenContext.cs index b59ad984..ff107bb5 100644 --- a/sln/test/NSpec.Tests/WrittenContext.cs +++ b/sln/test/NSpec.Tests/WrittenContext.cs @@ -15,8 +15,9 @@ public WrittenContext(Context context) Level = context.Level; Tags = new List(context.Tags); BeforeAllException = context.BeforeAllChain.Exception; - ExceptionBeforeAct = context.ExceptionBeforeAct; - ExceptionAfter = context.ExceptionAfter; + BeforeException = context.BeforeChain.Exception; + ActException = context.ActChain.Exception; + AfterException = context.AfterChain.Exception; AfterAllException = context.AfterAllChain.Exception; ClearExpectedException = context.ClearExpectedException; CapturedOutput = context.CapturedOutput; @@ -34,9 +35,11 @@ public WrittenContext(Context context) public Exception BeforeAllException { get; private set; } - public Exception ExceptionBeforeAct { get; private set; } + public Exception BeforeException { get; private set; } - public Exception ExceptionAfter { get; private set; } + public Exception ActException { get; private set; } + + public Exception AfterException { get; private set; } public Exception AfterAllException { get; private set; } From 4e4bac684b995ad59158e19546b4e92e6f7b8a73 Mon Sep 17 00:00:00 2001 From: GiuseppePiscopo Date: Sat, 20 May 2017 17:06:06 +0200 Subject: [PATCH 09/26] refactor(hooks): extract parts common to all hooks --- sln/src/NSpec/Domain/ActChain.cs | 38 ++++---------- sln/src/NSpec/Domain/AfterAllChain.cs | 36 +++----------- sln/src/NSpec/Domain/AfterChain.cs | 38 ++++---------- sln/src/NSpec/Domain/BeforeAllChain.cs | 36 +++----------- sln/src/NSpec/Domain/BeforeChain.cs | 38 ++++---------- sln/src/NSpec/Domain/Context.cs | 9 +--- .../{ChainUtils.cs => HookChainBase.cs} | 49 ++++++++++++++++--- 7 files changed, 87 insertions(+), 157 deletions(-) rename sln/src/NSpec/Domain/{ChainUtils.cs => HookChainBase.cs} (50%) diff --git a/sln/src/NSpec/Domain/ActChain.cs b/sln/src/NSpec/Domain/ActChain.cs index 695d202d..4c8cb70d 100644 --- a/sln/src/NSpec/Domain/ActChain.cs +++ b/sln/src/NSpec/Domain/ActChain.cs @@ -5,11 +5,11 @@ namespace NSpec.Domain { - public class ActChain + public class ActChain : HookChainBase { - public void BuildMethodLevel(Conventions conventions, List classHierarchy) + public override void BuildMethodLevel(Conventions conventions, List classHierarchy) { - var methods = ChainUtils.GetMethodsFromHierarchy( + var methods = GetMethodsFromHierarchy( classHierarchy, conventions.GetMethodLevelAct); if (methods.Count > 0) @@ -17,7 +17,7 @@ public void BuildMethodLevel(Conventions conventions, List classHierarchy) ClassHook = instance => methods.Do(m => m.Invoke(instance, null)); } - var asyncMethods = ChainUtils.GetMethodsFromHierarchy( + var asyncMethods = GetMethodsFromHierarchy( classHierarchy, conventions.GetAsyncMethodLevelAct); if (asyncMethods.Count > 0) @@ -26,19 +26,11 @@ public void BuildMethodLevel(Conventions conventions, List classHierarchy) } } - public void Run(nspec instance) - { - if (CanRun(instance)) - { - ChainUtils.RunAndHandleException(RunHooks, instance, ref Exception); - } - } - - void RunHooks(nspec instance) + protected override void RunHooks(nspec instance) { // parent chain - context.RecurseAncestors(c => c.ActChain.RunHooks(instance)); + RecurseAncestors(c => c.ActChain.RunHooks(instance)); // class (method-level) @@ -71,26 +63,14 @@ void RunHooks(nspec instance) AsyncHook.SafeInvoke(); } - bool CanRun(nspec instance) + protected override bool CanRun(nspec instance) { return context.BeforeAllChain.AnyBeforeAllsThrew() ? false : (context.BeforeChain.Exception == null); } - public ActChain(Context context) - { - this.context = context; - } - - public Action Hook; - public Func AsyncHook; - - public Action ClassHook { get; private set; } - public Action AsyncClassHook { get; private set; } - - public Exception Exception; - - readonly Context context; + public ActChain(Context context) : base(context) + { } } } diff --git a/sln/src/NSpec/Domain/AfterAllChain.cs b/sln/src/NSpec/Domain/AfterAllChain.cs index 10e238a4..2e040671 100644 --- a/sln/src/NSpec/Domain/AfterAllChain.cs +++ b/sln/src/NSpec/Domain/AfterAllChain.cs @@ -5,11 +5,11 @@ namespace NSpec.Domain { - public class AfterAllChain + public class AfterAllChain : HookChainBase { - public void BuildMethodLevel(Conventions conventions, List classHierarchy) + public override void BuildMethodLevel(Conventions conventions, List classHierarchy) { - var methods = ChainUtils.GetMethodsFromHierarchy( + var methods = GetMethodsFromHierarchy( classHierarchy, conventions.GetMethodLevelAfterAll); methods.Reverse(); @@ -19,7 +19,7 @@ public void BuildMethodLevel(Conventions conventions, List classHierarchy) ClassHook = instance => methods.Do(m => m.Invoke(instance, null)); } - var asyncMethods = ChainUtils.GetMethodsFromHierarchy( + var asyncMethods = GetMethodsFromHierarchy( classHierarchy, conventions.GetAsyncMethodLevelAfterAll); asyncMethods.Reverse(); @@ -30,15 +30,7 @@ public void BuildMethodLevel(Conventions conventions, List classHierarchy) } } - public void Run(nspec instance) - { - if (CanRun(instance)) - { - ChainUtils.RunAndHandleException(RunHooks, instance, ref Exception); - } - } - - void RunHooks(nspec instance) + protected override void RunHooks(nspec instance) { // context-level @@ -73,26 +65,14 @@ void RunHooks(nspec instance) // do NOT traverse parent chain } - bool CanRun(nspec instance) + protected override bool CanRun(nspec instance) { return context.BeforeAllChain.AncestorBeforeAllsThrew() ? false : context.AnyUnfilteredExampleInSubTree(instance); } - public AfterAllChain(Context context) - { - this.context = context; - } - - public Action Hook; - public Func AsyncHook; - - public Action ClassHook { get; private set; } - public Action AsyncClassHook { get; private set; } - - public Exception Exception; - - readonly Context context; + public AfterAllChain(Context context) : base(context) + { } } } diff --git a/sln/src/NSpec/Domain/AfterChain.cs b/sln/src/NSpec/Domain/AfterChain.cs index 10ef89b6..3c16c6da 100644 --- a/sln/src/NSpec/Domain/AfterChain.cs +++ b/sln/src/NSpec/Domain/AfterChain.cs @@ -5,11 +5,11 @@ namespace NSpec.Domain { - public class AfterChain + public class AfterChain : HookChainBase { - public void BuildMethodLevel(Conventions conventions, List classHierarchy) + public override void BuildMethodLevel(Conventions conventions, List classHierarchy) { - var methods = ChainUtils.GetMethodsFromHierarchy( + var methods = GetMethodsFromHierarchy( classHierarchy, conventions.GetMethodLevelAfter); methods.Reverse(); @@ -19,7 +19,7 @@ public void BuildMethodLevel(Conventions conventions, List classHierarchy) ClassHook = instance => methods.Do(m => m.Invoke(instance, null)); } - var asyncMethods = ChainUtils.GetMethodsFromHierarchy( + var asyncMethods = GetMethodsFromHierarchy( classHierarchy, conventions.GetAsyncMethodLevelAfter); asyncMethods.Reverse(); @@ -30,15 +30,7 @@ public void BuildMethodLevel(Conventions conventions, List classHierarchy) } } - public void Run(nspec instance) - { - if (CanRun(instance)) - { - ChainUtils.RunAndHandleException(RunHooks, instance, ref Exception); - } - } - - void RunHooks(nspec instance) + protected override void RunHooks(nspec instance) { // context-level @@ -72,27 +64,15 @@ void RunHooks(nspec instance) // parent chain - context.RecurseAncestors(c => c.AfterChain.RunHooks(instance)); + RecurseAncestors(c => c.AfterChain.RunHooks(instance)); } - bool CanRun(nspec instance) + protected override bool CanRun(nspec instance) { return !context.BeforeAllChain.AnyBeforeAllsThrew(); } - public AfterChain(Context context) - { - this.context = context; - } - - public Action Hook; - public Func AsyncHook; - - public Action ClassHook { get; private set; } - public Action AsyncClassHook { get; private set; } - - public Exception Exception; - - readonly Context context; + public AfterChain(Context context) : base(context) + { } } } diff --git a/sln/src/NSpec/Domain/BeforeAllChain.cs b/sln/src/NSpec/Domain/BeforeAllChain.cs index a23970ff..6dcd974e 100644 --- a/sln/src/NSpec/Domain/BeforeAllChain.cs +++ b/sln/src/NSpec/Domain/BeforeAllChain.cs @@ -5,11 +5,11 @@ namespace NSpec.Domain { - public class BeforeAllChain + public class BeforeAllChain : HookChainBase { - public void BuildMethodLevel(Conventions conventions, List classHierarchy) + public override void BuildMethodLevel(Conventions conventions, List classHierarchy) { - var methods = ChainUtils.GetMethodsFromHierarchy( + var methods = GetMethodsFromHierarchy( classHierarchy, conventions.GetMethodLevelBeforeAll); if (methods.Count > 0) @@ -17,7 +17,7 @@ public void BuildMethodLevel(Conventions conventions, List classHierarchy) ClassHook = instance => methods.Do(m => m.Invoke(instance, null)); } - var asyncMethods = ChainUtils.GetMethodsFromHierarchy( + var asyncMethods = GetMethodsFromHierarchy( classHierarchy, conventions.GetAsyncMethodLevelBeforeAll); if (asyncMethods.Count > 0) @@ -26,15 +26,7 @@ public void BuildMethodLevel(Conventions conventions, List classHierarchy) } } - public void Run(nspec instance) - { - if (CanRun(instance)) - { - ChainUtils.RunAndHandleException(RunHooks, instance, ref Exception); - } - } - - void RunHooks(nspec instance) + protected override void RunHooks(nspec instance) { // do NOT traverse parent chain @@ -69,7 +61,7 @@ void RunHooks(nspec instance) AsyncClassHook.SafeInvoke(instance); } - bool CanRun(nspec instance) + protected override bool CanRun(nspec instance) { return AncestorBeforeAllsThrew() ? false @@ -86,19 +78,7 @@ public bool AncestorBeforeAllsThrew() return (context.Parent?.BeforeAllChain.AnyBeforeAllsThrew() ?? false); } - public BeforeAllChain(Context context) - { - this.context = context; - } - - public Action Hook; - public Func AsyncHook; - - public Action ClassHook { get; private set; } - public Action AsyncClassHook { get; private set; } - - public Exception Exception; - - readonly Context context; + public BeforeAllChain(Context context) : base(context) + { } } } diff --git a/sln/src/NSpec/Domain/BeforeChain.cs b/sln/src/NSpec/Domain/BeforeChain.cs index 413dedbf..daf5dc7c 100644 --- a/sln/src/NSpec/Domain/BeforeChain.cs +++ b/sln/src/NSpec/Domain/BeforeChain.cs @@ -5,11 +5,11 @@ namespace NSpec.Domain { - public class BeforeChain + public class BeforeChain : HookChainBase { - public void BuildMethodLevel(Conventions conventions, List classHierarchy) + public override void BuildMethodLevel(Conventions conventions, List classHierarchy) { - var methods = ChainUtils.GetMethodsFromHierarchy( + var methods = GetMethodsFromHierarchy( classHierarchy, conventions.GetMethodLevelBefore); if (methods.Count > 0) @@ -17,7 +17,7 @@ public void BuildMethodLevel(Conventions conventions, List classHierarchy) ClassHook = instance => methods.Do(m => m.Invoke(instance, null)); } - var asyncMethods = ChainUtils.GetMethodsFromHierarchy( + var asyncMethods = GetMethodsFromHierarchy( classHierarchy, conventions.GetAsyncMethodLevelBefore); if (asyncMethods.Count > 0) @@ -26,19 +26,11 @@ public void BuildMethodLevel(Conventions conventions, List classHierarchy) } } - public void Run(nspec instance) - { - if (CanRun(instance)) - { - ChainUtils.RunAndHandleException(RunHooks, instance, ref Exception); - } - } - - void RunHooks(nspec instance) + protected override void RunHooks(nspec instance) { // parent chain - context.RecurseAncestors(c => c.BeforeChain.RunHooks(instance)); + RecurseAncestors(c => c.BeforeChain.RunHooks(instance)); // class (method-level) @@ -72,24 +64,12 @@ void RunHooks(nspec instance) AsyncHook.SafeInvoke(); } - bool CanRun(nspec instance) + protected override bool CanRun(nspec instance) { return !context.BeforeAllChain.AnyBeforeAllsThrew(); } - public BeforeChain(Context context) - { - this.context = context; - } - - public Action Hook; - public Func AsyncHook; - - public Action ClassHook { get; private set; } - public Action AsyncClassHook { get; private set; } - - public Exception Exception; - - readonly Context context; + public BeforeChain(Context context) : base(context) + { } } } diff --git a/sln/src/NSpec/Domain/Context.cs b/sln/src/NSpec/Domain/Context.cs index a1f05759..1836d1f0 100644 --- a/sln/src/NSpec/Domain/Context.cs +++ b/sln/src/NSpec/Domain/Context.cs @@ -177,7 +177,7 @@ public void Exercise(ExampleBase example, nspec nspec) if (example.Pending) { - ChainUtils.RunAndHandleException(example.RunPending, nspec, ref example.Exception); + HookChainBase.RunAndHandleException(example.RunPending, nspec, ref example.Exception); return; } @@ -209,7 +209,7 @@ void RunExample(ExampleBase example, nspec nspec) if (BeforeChain.Exception != null) return; - ChainUtils.RunAndHandleException(example.Run, nspec, ref example.Exception); + HookChainBase.RunAndHandleException(example.Run, nspec, ref example.Exception); } public virtual bool IsSub(Type baseType) @@ -277,11 +277,6 @@ public override string ToString() }); } - public void RecurseAncestors(Action ancestorAction) - { - if (Parent != null) ancestorAction(Parent); - } - void WriteAncestors(ILiveFormatter formatter) { if (Parent == null) diff --git a/sln/src/NSpec/Domain/ChainUtils.cs b/sln/src/NSpec/Domain/HookChainBase.cs similarity index 50% rename from sln/src/NSpec/Domain/ChainUtils.cs rename to sln/src/NSpec/Domain/HookChainBase.cs index f07ff1d7..8260d974 100644 --- a/sln/src/NSpec/Domain/ChainUtils.cs +++ b/sln/src/NSpec/Domain/HookChainBase.cs @@ -2,18 +2,24 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Threading.Tasks; namespace NSpec.Domain { - public static class ChainUtils + public abstract class HookChainBase { - public static List GetMethodsFromHierarchy( - List classHierarchy, Func selectMethod) + public abstract void BuildMethodLevel(Conventions conventions, List classHierarchy); + + protected abstract bool CanRun(nspec instance); + + protected abstract void RunHooks(nspec instance); + + public void Run(nspec instance) { - return classHierarchy - .Select(selectMethod) - .Where(m => m != null) - .ToList(); + if (CanRun(instance)) + { + RunAndHandleException(RunHooks, instance, ref Exception); + } } public static bool RunAndHandleException(Action action, nspec instance, ref Exception exceptionToSet) @@ -39,5 +45,34 @@ public static bool RunAndHandleException(Action action, nspec instance, r return hasThrown; } + + protected void RecurseAncestors(Action ancestorAction) + { + if (context.Parent != null) ancestorAction(context.Parent); + } + + protected static List GetMethodsFromHierarchy( + List classHierarchy, Func selectMethod) + { + return classHierarchy + .Select(selectMethod) + .Where(m => m != null) + .ToList(); + } + + public HookChainBase(Context context) + { + this.context = context; + } + + public Action Hook; + public Func AsyncHook; + + public Action ClassHook { get; protected set; } + public Action AsyncClassHook { get; protected set; } + + public Exception Exception; + + protected readonly Context context; } } From 69a4b60dbbe46da20577e6100978a88ddeb60554 Mon Sep 17 00:00:00 2001 From: GiuseppePiscopo Date: Sat, 20 May 2017 17:08:40 +0200 Subject: [PATCH 10/26] chore(context): rename some nspec instances --- sln/src/NSpec/Domain/Context.cs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/sln/src/NSpec/Domain/Context.cs b/sln/src/NSpec/Domain/Context.cs index 1836d1f0..21568391 100644 --- a/sln/src/NSpec/Domain/Context.cs +++ b/sln/src/NSpec/Domain/Context.cs @@ -59,11 +59,11 @@ public virtual void Run(bool failFast, nspec instance = null, bool recurse = tru { if (failFast && Parent.HasAnyFailures()) return; - var nspec = builtInstance ?? instance; + var runningInstance = builtInstance ?? instance; using (new ConsoleCatcher(output => this.CapturedOutput = output)) { - BeforeAllChain.Run(nspec); + BeforeAllChain.Run(runningInstance); } // intentionally using for loop to prevent collection was modified error in sample specs @@ -73,16 +73,16 @@ public virtual void Run(bool failFast, nspec instance = null, bool recurse = tru if (failFast && example.Context.HasAnyFailures()) return; - Exercise(example, nspec); + Exercise(example, runningInstance); } if (recurse) { - Contexts.Do(c => c.Run(failFast, nspec, recurse)); + Contexts.Do(c => c.Run(failFast, runningInstance, recurse)); } // TODO wrap this as well in a ConsoleCatcher, not before adding tests about it - AfterAllChain.Run(nspec); + AfterAllChain.Run(runningInstance); } /// @@ -166,9 +166,9 @@ public string FullContext() return Parent != null ? Parent.FullContext() + ". " + Name : Name; } - public void Exercise(ExampleBase example, nspec nspec) + public void Exercise(ExampleBase example, nspec instance) { - if (example.ShouldSkip(nspec.tagsFilter)) + if (example.ShouldSkip(instance.tagsFilter)) { return; } @@ -177,7 +177,7 @@ public void Exercise(ExampleBase example, nspec nspec) if (example.Pending) { - HookChainBase.RunAndHandleException(example.RunPending, nspec, ref example.Exception); + HookChainBase.RunAndHandleException(example.RunPending, instance, ref example.Exception); return; } @@ -186,13 +186,13 @@ public void Exercise(ExampleBase example, nspec nspec) using (new ConsoleCatcher(output => example.CapturedOutput = output)) { - BeforeChain.Run(nspec); + BeforeChain.Run(instance); - ActChain.Run(nspec); + ActChain.Run(instance); - RunExample(example, nspec); + RunExample(example, instance); - AfterChain.Run(nspec); + AfterChain.Run(instance); } // when an expected exception is thrown and is set to be cleared by 'expect<>', @@ -203,13 +203,13 @@ public void Exercise(ExampleBase example, nspec nspec) example.StopTiming(stopWatch); } - void RunExample(ExampleBase example, nspec nspec) + void RunExample(ExampleBase example, nspec instance) { if (BeforeAllChain.AnyBeforeAllsThrew()) return; if (BeforeChain.Exception != null) return; - HookChainBase.RunAndHandleException(example.Run, nspec, ref example.Exception); + HookChainBase.RunAndHandleException(example.Run, instance, ref example.Exception); } public virtual bool IsSub(Type baseType) From ea40e43dc69b0c895b73024faa3b33300626dc1b Mon Sep 17 00:00:00 2001 From: GiuseppePiscopo Date: Sun, 21 May 2017 01:44:46 +0200 Subject: [PATCH 11/26] refactor(hooks): extract building and running class/context hooks --- sln/src/NSpec/Domain/ActChain.cs | 54 ++++------------ sln/src/NSpec/Domain/AfterAllChain.cs | 60 +++++------------- sln/src/NSpec/Domain/AfterChain.cs | 61 +++++------------- sln/src/NSpec/Domain/BeforeAllChain.cs | 55 ++++------------- sln/src/NSpec/Domain/BeforeChain.cs | 57 ++++------------- sln/src/NSpec/Domain/HookChainBase.cs | 85 +++++++++++++++++++++++++- 6 files changed, 148 insertions(+), 224 deletions(-) diff --git a/sln/src/NSpec/Domain/ActChain.cs b/sln/src/NSpec/Domain/ActChain.cs index 4c8cb70d..b782a6fa 100644 --- a/sln/src/NSpec/Domain/ActChain.cs +++ b/sln/src/NSpec/Domain/ActChain.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Reflection; using System.Threading.Tasks; using NSpec.Domain.Extensions; @@ -7,60 +8,26 @@ namespace NSpec.Domain { public class ActChain : HookChainBase { - public override void BuildMethodLevel(Conventions conventions, List classHierarchy) + protected override Func GetMethodSelector(Conventions conventions) { - var methods = GetMethodsFromHierarchy( - classHierarchy, conventions.GetMethodLevelAct); - - if (methods.Count > 0) - { - ClassHook = instance => methods.Do(m => m.Invoke(instance, null)); - } - - var asyncMethods = GetMethodsFromHierarchy( - classHierarchy, conventions.GetAsyncMethodLevelAct); + return conventions.GetMethodLevelAct; + } - if (asyncMethods.Count > 0) - { - AsyncClassHook = instance => asyncMethods.Do(m => new AsyncMethodLevelAct(m).Run(instance)); - } + protected override Func GetAsyncMethodSelector(Conventions conventions) + { + return conventions.GetAsyncMethodLevelAct; } protected override void RunHooks(nspec instance) { // parent chain - RecurseAncestors(c => c.ActChain.RunHooks(instance)); // class (method-level) - - if (ClassHook != null && AsyncClassHook != null) - { - throw new AsyncMismatchException( - "A spec class with all its ancestors cannot set both sync and async class-level 'act_each' hooks, they should either be all sync or all async"); - } - - ClassHook.SafeInvoke(instance); - - AsyncClassHook.SafeInvoke(instance); + RunClassHooks(instance); // context-level - - if (Hook != null && AsyncHook != null) - { - throw new AsyncMismatchException( - "A single context cannot set both an 'act' and an 'actAsync', please pick one of the two"); - } - - if (Hook != null && Hook.IsAsync()) - { - throw new AsyncMismatchException( - "'act' cannot be set to an async delegate, please use 'actAsync' instead"); - } - - Hook.SafeInvoke(); - - AsyncHook.SafeInvoke(); + RunContextHooks(); } protected override bool CanRun(nspec instance) @@ -70,7 +37,8 @@ protected override bool CanRun(nspec instance) : (context.BeforeChain.Exception == null); } - public ActChain(Context context) : base(context) + public ActChain(Context context) : base(context, + "act", "actAsync", "act_each") { } } } diff --git a/sln/src/NSpec/Domain/AfterAllChain.cs b/sln/src/NSpec/Domain/AfterAllChain.cs index 2e040671..9820fb8c 100644 --- a/sln/src/NSpec/Domain/AfterAllChain.cs +++ b/sln/src/NSpec/Domain/AfterAllChain.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Reflection; using System.Threading.Tasks; using NSpec.Domain.Extensions; @@ -7,60 +8,28 @@ namespace NSpec.Domain { public class AfterAllChain : HookChainBase { - public override void BuildMethodLevel(Conventions conventions, List classHierarchy) + protected override Func GetMethodSelector(Conventions conventions) { - var methods = GetMethodsFromHierarchy( - classHierarchy, conventions.GetMethodLevelAfterAll); - - methods.Reverse(); - - if (methods.Count > 0) - { - ClassHook = instance => methods.Do(m => m.Invoke(instance, null)); - } - - var asyncMethods = GetMethodsFromHierarchy( - classHierarchy, conventions.GetAsyncMethodLevelAfterAll); + return conventions.GetMethodLevelAfterAll; + } - asyncMethods.Reverse(); + protected override Func GetAsyncMethodSelector(Conventions conventions) + { + return conventions.GetAsyncMethodLevelAfterAll; + } - if (asyncMethods.Count > 0) - { - AsyncClassHook = instance => asyncMethods.Do(m => new AsyncMethodLevelAfterAll(m).Run(instance)); - } + protected override bool ReverseClassMethods() + { + return true; } protected override void RunHooks(nspec instance) { // context-level - - if (Hook != null && AsyncHook != null) - { - throw new AsyncMismatchException( - "A single context cannot set both an 'afterAll' and an 'afterAllAsync', please pick one of the two"); - } - - if (Hook != null && Hook.IsAsync()) - { - throw new AsyncMismatchException( - "'afterAll' cannot be set to an async delegate, please use 'afterAllAsync' instead"); - } - - Hook.SafeInvoke(); - - AsyncHook.SafeInvoke(); + RunContextHooks(); // class (method-level) - - if (ClassHook != null && AsyncClassHook != null) - { - throw new AsyncMismatchException( - "A spec class with all its ancestors cannot set both sync and async class-level 'after_all' hooks, they should either be all sync or all async"); - } - - ClassHook.SafeInvoke(instance); - - AsyncClassHook.SafeInvoke(instance); + RunClassHooks(instance); // do NOT traverse parent chain } @@ -72,7 +41,8 @@ protected override bool CanRun(nspec instance) : context.AnyUnfilteredExampleInSubTree(instance); } - public AfterAllChain(Context context) : base(context) + public AfterAllChain(Context context) : base(context, + "afterAll", "afterAllAsync", "after_all") { } } } diff --git a/sln/src/NSpec/Domain/AfterChain.cs b/sln/src/NSpec/Domain/AfterChain.cs index 3c16c6da..2d7e5b06 100644 --- a/sln/src/NSpec/Domain/AfterChain.cs +++ b/sln/src/NSpec/Domain/AfterChain.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Reflection; using System.Threading.Tasks; using NSpec.Domain.Extensions; @@ -7,63 +8,30 @@ namespace NSpec.Domain { public class AfterChain : HookChainBase { - public override void BuildMethodLevel(Conventions conventions, List classHierarchy) + protected override Func GetMethodSelector(Conventions conventions) { - var methods = GetMethodsFromHierarchy( - classHierarchy, conventions.GetMethodLevelAfter); - - methods.Reverse(); - - if (methods.Count > 0) - { - ClassHook = instance => methods.Do(m => m.Invoke(instance, null)); - } - - var asyncMethods = GetMethodsFromHierarchy( - classHierarchy, conventions.GetAsyncMethodLevelAfter); + return conventions.GetMethodLevelAfter; + } - asyncMethods.Reverse(); + protected override Func GetAsyncMethodSelector(Conventions conventions) + { + return conventions.GetAsyncMethodLevelAfter; + } - if (asyncMethods.Count > 0) - { - AsyncClassHook = instance => asyncMethods.Do(m => new AsyncMethodLevelAfter(m).Run(instance)); - } + protected override bool ReverseClassMethods() + { + return true; } protected override void RunHooks(nspec instance) { // context-level - - if (Hook != null && AsyncHook != null) - { - throw new AsyncMismatchException( - "A single context cannot set both an 'after' and an 'afterAsync', please pick one of the two"); - } - - if (Hook != null && Hook.IsAsync()) - { - throw new AsyncMismatchException( - "'after' cannot be set to an async delegate, please use 'afterAsync' instead"); - } - - Hook.SafeInvoke(); - - AsyncHook.SafeInvoke(); + RunContextHooks(); // class (method-level) - - if (ClassHook != null && AsyncClassHook != null) - { - throw new AsyncMismatchException( - "A spec class with all its ancestors cannot set both sync and async class-level 'after_each' hooks, they should either be all sync or all async"); - } - - ClassHook.SafeInvoke(instance); - - AsyncClassHook.SafeInvoke(instance); + RunClassHooks(instance); // parent chain - RecurseAncestors(c => c.AfterChain.RunHooks(instance)); } @@ -72,7 +40,8 @@ protected override bool CanRun(nspec instance) return !context.BeforeAllChain.AnyBeforeAllsThrew(); } - public AfterChain(Context context) : base(context) + public AfterChain(Context context) : base(context, + "after", "afterAsync", "after_each") { } } } diff --git a/sln/src/NSpec/Domain/BeforeAllChain.cs b/sln/src/NSpec/Domain/BeforeAllChain.cs index 6dcd974e..617e4018 100644 --- a/sln/src/NSpec/Domain/BeforeAllChain.cs +++ b/sln/src/NSpec/Domain/BeforeAllChain.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Reflection; using System.Threading.Tasks; using NSpec.Domain.Extensions; @@ -7,58 +8,25 @@ namespace NSpec.Domain { public class BeforeAllChain : HookChainBase { - public override void BuildMethodLevel(Conventions conventions, List classHierarchy) + protected override Func GetMethodSelector(Conventions conventions) { - var methods = GetMethodsFromHierarchy( - classHierarchy, conventions.GetMethodLevelBeforeAll); - - if (methods.Count > 0) - { - ClassHook = instance => methods.Do(m => m.Invoke(instance, null)); - } - - var asyncMethods = GetMethodsFromHierarchy( - classHierarchy, conventions.GetAsyncMethodLevelBeforeAll); + return conventions.GetMethodLevelBeforeAll; + } - if (asyncMethods.Count > 0) - { - AsyncClassHook = instance => asyncMethods.Do(m => new AsyncMethodLevelBeforeAll(m).Run(instance)); - } + protected override Func GetAsyncMethodSelector(Conventions conventions) + { + return conventions.GetAsyncMethodLevelBeforeAll; } protected override void RunHooks(nspec instance) { // do NOT traverse parent chain - // context-level - - if (Hook != null && AsyncHook != null) - { - throw new AsyncMismatchException( - "A single context cannot set both a 'beforeAll' and an 'beforeAllAsync', please pick one of the two"); - } - - if (Hook != null && Hook.IsAsync()) - { - throw new AsyncMismatchException( - "'beforeAll' cannot be set to an async delegate, please use 'beforeAllAsync' instead"); - } - - Hook.SafeInvoke(); - - AsyncHook.SafeInvoke(); - // class (method-level) + RunClassHooks(instance); - if (ClassHook != null && AsyncClassHook != null) - { - throw new AsyncMismatchException( - "A spec class with all its ancestors cannot set both sync and async class-level 'before_all' hooks, they should either be all sync or all async"); - } - - ClassHook.SafeInvoke(instance); - - AsyncClassHook.SafeInvoke(instance); + // context-level + RunContextHooks(); } protected override bool CanRun(nspec instance) @@ -78,7 +46,8 @@ public bool AncestorBeforeAllsThrew() return (context.Parent?.BeforeAllChain.AnyBeforeAllsThrew() ?? false); } - public BeforeAllChain(Context context) : base(context) + public BeforeAllChain(Context context) : base(context, + "beforeAll", "beforeAllAsync", "before_all") { } } } diff --git a/sln/src/NSpec/Domain/BeforeChain.cs b/sln/src/NSpec/Domain/BeforeChain.cs index daf5dc7c..6235afe7 100644 --- a/sln/src/NSpec/Domain/BeforeChain.cs +++ b/sln/src/NSpec/Domain/BeforeChain.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Reflection; using System.Threading.Tasks; using NSpec.Domain.Extensions; @@ -7,61 +8,26 @@ namespace NSpec.Domain { public class BeforeChain : HookChainBase { - public override void BuildMethodLevel(Conventions conventions, List classHierarchy) + protected override Func GetMethodSelector(Conventions conventions) { - var methods = GetMethodsFromHierarchy( - classHierarchy, conventions.GetMethodLevelBefore); - - if (methods.Count > 0) - { - ClassHook = instance => methods.Do(m => m.Invoke(instance, null)); - } - - var asyncMethods = GetMethodsFromHierarchy( - classHierarchy, conventions.GetAsyncMethodLevelBefore); + return conventions.GetMethodLevelBefore; + } - if (asyncMethods.Count > 0) - { - AsyncClassHook = instance => asyncMethods.Do(m => new AsyncMethodLevelBefore(m).Run(instance)); - } + protected override Func GetAsyncMethodSelector(Conventions conventions) + { + return conventions.GetAsyncMethodLevelBefore; } protected override void RunHooks(nspec instance) { // parent chain - RecurseAncestors(c => c.BeforeChain.RunHooks(instance)); - // class (method-level) - - if (ClassHook != null && AsyncClassHook != null) - { - throw new AsyncMismatchException( - "A spec class with all its ancestors cannot set both sync and async " + - "class-level 'before_each' hooks, they should either be all sync or all async"); - } - - ClassHook.SafeInvoke(instance); - - AsyncClassHook.SafeInvoke(instance); + // class (method-level) + RunContextHooks(); // context-level - - if (Hook != null && AsyncHook != null) - { - throw new AsyncMismatchException( - "A single context cannot set both a 'before' and an 'beforeAsync', please pick one of the two"); - } - - if (Hook != null && Hook.IsAsync()) - { - throw new AsyncMismatchException( - "'before' cannot be set to an async delegate, please use 'beforeAsync' instead"); - } - - Hook.SafeInvoke(); - - AsyncHook.SafeInvoke(); + RunClassHooks(instance); } protected override bool CanRun(nspec instance) @@ -69,7 +35,8 @@ protected override bool CanRun(nspec instance) return !context.BeforeAllChain.AnyBeforeAllsThrew(); } - public BeforeChain(Context context) : base(context) + public BeforeChain(Context context) : base(context, + "before", "beforeAsync", "before_each") { } } } diff --git a/sln/src/NSpec/Domain/HookChainBase.cs b/sln/src/NSpec/Domain/HookChainBase.cs index 8260d974..e313a672 100644 --- a/sln/src/NSpec/Domain/HookChainBase.cs +++ b/sln/src/NSpec/Domain/HookChainBase.cs @@ -3,12 +3,49 @@ using System.Linq; using System.Reflection; using System.Threading.Tasks; +using NSpec.Domain.Extensions; namespace NSpec.Domain { public abstract class HookChainBase { - public abstract void BuildMethodLevel(Conventions conventions, List classHierarchy); + public void BuildMethodLevel(Conventions conventions, List classHierarchy) + { + var selector = GetMethodSelector(conventions); + var methods = GetMethodsFromHierarchy(classHierarchy, selector); + + if (ReverseClassMethods()) + { + methods.Reverse(); + } + + if (methods.Count > 0) + { + ClassHook = instance => methods.Do(m => m.Invoke(instance, null)); + } + + var asyncSelector = GetAsyncMethodSelector(conventions); + var asyncMethods = GetMethodsFromHierarchy(classHierarchy, asyncSelector); + + if (ReverseClassMethods()) + { + asyncMethods.Reverse(); + } + + if (asyncMethods.Count > 0) + { + AsyncClassHook = instance => asyncMethods.Do(m => new AsyncMethodLevelBefore(m).Run(instance)); + } + } + + protected abstract Func GetMethodSelector(Conventions conventions); + + protected abstract Func GetAsyncMethodSelector(Conventions conventions); + + protected virtual bool ReverseClassMethods() + { + return false; + } protected abstract bool CanRun(nspec instance); @@ -46,6 +83,43 @@ public static bool RunAndHandleException(Action action, nspec instance, r return hasThrown; } + protected void RunClassHooks(nspec instance) + { + // class (method-level) + + if (ClassHook != null && AsyncClassHook != null) + { + throw new AsyncMismatchException( + $"A spec class with all its ancestors cannot set both sync and async " + + "class-level '{classHookName}' hooks, they should either be all sync or all async"); + } + + ClassHook.SafeInvoke(instance); + + AsyncClassHook.SafeInvoke(instance); + } + + protected void RunContextHooks() + { + // context-level + + if (Hook != null && AsyncHook != null) + { + throw new AsyncMismatchException( + $"A single context cannot set both a '{hookName}' and an '{asyncHookName}', please pick one of the two"); + } + + if (Hook != null && Hook.IsAsync()) + { + throw new AsyncMismatchException( + $"'{hookName}' cannot be set to an async delegate, please use '{asyncHookName}' instead"); + } + + Hook.SafeInvoke(); + + AsyncHook.SafeInvoke(); + } + protected void RecurseAncestors(Action ancestorAction) { if (context.Parent != null) ancestorAction(context.Parent); @@ -60,9 +134,12 @@ protected static List GetMethodsFromHierarchy( .ToList(); } - public HookChainBase(Context context) + public HookChainBase(Context context, string hookName, string asyncHookName, string classHookName) { this.context = context; + this.hookName = hookName; + this.asyncHookName = asyncHookName; + this.classHookName = classHookName; } public Action Hook; @@ -74,5 +151,9 @@ public HookChainBase(Context context) public Exception Exception; protected readonly Context context; + + protected readonly string hookName; + protected readonly string asyncHookName; + protected readonly string classHookName; } } From c185e97696efb1b18c1939a44af52e6efc0c94d5 Mon Sep 17 00:00:00 2001 From: GiuseppePiscopo Date: Sun, 21 May 2017 13:20:30 +0200 Subject: [PATCH 12/26] refactor(hooks): turn method selectors into fields --- sln/src/NSpec/Domain/ActChain.cs | 19 ++++++------------- sln/src/NSpec/Domain/AfterAllChain.cs | 19 ++++++------------- sln/src/NSpec/Domain/AfterChain.cs | 19 ++++++------------- sln/src/NSpec/Domain/BeforeAllChain.cs | 19 ++++++------------- sln/src/NSpec/Domain/BeforeChain.cs | 19 ++++++------------- sln/src/NSpec/Domain/ClassContext.cs | 15 ++++++--------- sln/src/NSpec/Domain/Context.cs | 14 ++++++++------ sln/src/NSpec/Domain/HookChainBase.cs | 19 ++++++++----------- 8 files changed, 52 insertions(+), 91 deletions(-) diff --git a/sln/src/NSpec/Domain/ActChain.cs b/sln/src/NSpec/Domain/ActChain.cs index b782a6fa..0bdf9ab6 100644 --- a/sln/src/NSpec/Domain/ActChain.cs +++ b/sln/src/NSpec/Domain/ActChain.cs @@ -8,16 +8,6 @@ namespace NSpec.Domain { public class ActChain : HookChainBase { - protected override Func GetMethodSelector(Conventions conventions) - { - return conventions.GetMethodLevelAct; - } - - protected override Func GetAsyncMethodSelector(Conventions conventions) - { - return conventions.GetAsyncMethodLevelAct; - } - protected override void RunHooks(nspec instance) { // parent chain @@ -37,8 +27,11 @@ protected override bool CanRun(nspec instance) : (context.BeforeChain.Exception == null); } - public ActChain(Context context) : base(context, - "act", "actAsync", "act_each") - { } + public ActChain(Context context, Conventions conventions) + : base(context, "act", "actAsync", "act_each") + { + methodSelector = conventions.GetMethodLevelAct; + asyncMethodSelector = conventions.GetAsyncMethodLevelAct; + } } } diff --git a/sln/src/NSpec/Domain/AfterAllChain.cs b/sln/src/NSpec/Domain/AfterAllChain.cs index 9820fb8c..dfe23f2b 100644 --- a/sln/src/NSpec/Domain/AfterAllChain.cs +++ b/sln/src/NSpec/Domain/AfterAllChain.cs @@ -8,16 +8,6 @@ namespace NSpec.Domain { public class AfterAllChain : HookChainBase { - protected override Func GetMethodSelector(Conventions conventions) - { - return conventions.GetMethodLevelAfterAll; - } - - protected override Func GetAsyncMethodSelector(Conventions conventions) - { - return conventions.GetAsyncMethodLevelAfterAll; - } - protected override bool ReverseClassMethods() { return true; @@ -41,8 +31,11 @@ protected override bool CanRun(nspec instance) : context.AnyUnfilteredExampleInSubTree(instance); } - public AfterAllChain(Context context) : base(context, - "afterAll", "afterAllAsync", "after_all") - { } + public AfterAllChain(Context context, Conventions conventions) + : base(context, "afterAll", "afterAllAsync", "after_all") + { + methodSelector = conventions.GetMethodLevelAfterAll; + asyncMethodSelector = conventions.GetAsyncMethodLevelAfterAll; + } } } diff --git a/sln/src/NSpec/Domain/AfterChain.cs b/sln/src/NSpec/Domain/AfterChain.cs index 2d7e5b06..35861bed 100644 --- a/sln/src/NSpec/Domain/AfterChain.cs +++ b/sln/src/NSpec/Domain/AfterChain.cs @@ -8,16 +8,6 @@ namespace NSpec.Domain { public class AfterChain : HookChainBase { - protected override Func GetMethodSelector(Conventions conventions) - { - return conventions.GetMethodLevelAfter; - } - - protected override Func GetAsyncMethodSelector(Conventions conventions) - { - return conventions.GetAsyncMethodLevelAfter; - } - protected override bool ReverseClassMethods() { return true; @@ -40,8 +30,11 @@ protected override bool CanRun(nspec instance) return !context.BeforeAllChain.AnyBeforeAllsThrew(); } - public AfterChain(Context context) : base(context, - "after", "afterAsync", "after_each") - { } + public AfterChain(Context context, Conventions conventions) + : base(context, "after", "afterAsync", "after_each") + { + methodSelector = conventions.GetMethodLevelAfter; + asyncMethodSelector = conventions.GetAsyncMethodLevelAfter; + } } } diff --git a/sln/src/NSpec/Domain/BeforeAllChain.cs b/sln/src/NSpec/Domain/BeforeAllChain.cs index 617e4018..f674a64b 100644 --- a/sln/src/NSpec/Domain/BeforeAllChain.cs +++ b/sln/src/NSpec/Domain/BeforeAllChain.cs @@ -8,16 +8,6 @@ namespace NSpec.Domain { public class BeforeAllChain : HookChainBase { - protected override Func GetMethodSelector(Conventions conventions) - { - return conventions.GetMethodLevelBeforeAll; - } - - protected override Func GetAsyncMethodSelector(Conventions conventions) - { - return conventions.GetAsyncMethodLevelBeforeAll; - } - protected override void RunHooks(nspec instance) { // do NOT traverse parent chain @@ -46,8 +36,11 @@ public bool AncestorBeforeAllsThrew() return (context.Parent?.BeforeAllChain.AnyBeforeAllsThrew() ?? false); } - public BeforeAllChain(Context context) : base(context, - "beforeAll", "beforeAllAsync", "before_all") - { } + public BeforeAllChain(Context context, Conventions conventions) + : base(context, "beforeAll", "beforeAllAsync", "before_all") + { + methodSelector = conventions.GetMethodLevelBeforeAll; + asyncMethodSelector = conventions.GetAsyncMethodLevelBeforeAll; + } } } diff --git a/sln/src/NSpec/Domain/BeforeChain.cs b/sln/src/NSpec/Domain/BeforeChain.cs index 6235afe7..e49ba46c 100644 --- a/sln/src/NSpec/Domain/BeforeChain.cs +++ b/sln/src/NSpec/Domain/BeforeChain.cs @@ -8,16 +8,6 @@ namespace NSpec.Domain { public class BeforeChain : HookChainBase { - protected override Func GetMethodSelector(Conventions conventions) - { - return conventions.GetMethodLevelBefore; - } - - protected override Func GetAsyncMethodSelector(Conventions conventions) - { - return conventions.GetAsyncMethodLevelBefore; - } - protected override void RunHooks(nspec instance) { // parent chain @@ -35,8 +25,11 @@ protected override bool CanRun(nspec instance) return !context.BeforeAllChain.AnyBeforeAllsThrew(); } - public BeforeChain(Context context) : base(context, - "before", "beforeAsync", "before_each") - { } + public BeforeChain(Context context, Conventions conventions) + : base(context, "before", "beforeAsync", "before_each") + { + methodSelector = conventions.GetMethodLevelBefore; + asyncMethodSelector = conventions.GetAsyncMethodLevelBefore; + } } } diff --git a/sln/src/NSpec/Domain/ClassContext.cs b/sln/src/NSpec/Domain/ClassContext.cs index 775718c8..8054d435 100644 --- a/sln/src/NSpec/Domain/ClassContext.cs +++ b/sln/src/NSpec/Domain/ClassContext.cs @@ -10,15 +10,15 @@ public class ClassContext : Context { public override void Build(nspec unused = null) { - BeforeAllChain.BuildMethodLevel(conventions, classHierarchy); + BeforeAllChain.BuildMethodLevel(classHierarchy); - BeforeChain.BuildMethodLevel(conventions, classHierarchy); + BeforeChain.BuildMethodLevel(classHierarchy); - ActChain.BuildMethodLevel(conventions, classHierarchy); + ActChain.BuildMethodLevel(classHierarchy); - AfterChain.BuildMethodLevel(conventions, classHierarchy); + AfterChain.BuildMethodLevel(classHierarchy); - AfterAllChain.BuildMethodLevel(conventions, classHierarchy); + AfterAllChain.BuildMethodLevel(classHierarchy); try { @@ -85,12 +85,10 @@ public override void Run(bool failFast, nspec instance = null, bool recurse = tr } public ClassContext(Type type, Conventions conventions = null, Tags tagsFilter = null, string tags = null) - : base(type.CleanName(), tags) + : base(type.CleanName(), tags, false, conventions) { this.SpecType = type; - this.conventions = conventions ?? new DefaultConventions().Initialize(); - this.tagsFilter = tagsFilter; this.classHierarchy = (type == typeof(nspec)) @@ -102,7 +100,6 @@ public ClassContext(Type type, Conventions conventions = null, Tags tagsFilter = Tags tagsFilter; List classHierarchy; - Conventions conventions; bool cantCreateInstance = false; } } diff --git a/sln/src/NSpec/Domain/Context.cs b/sln/src/NSpec/Domain/Context.cs index 21568391..e3f82bce 100644 --- a/sln/src/NSpec/Domain/Context.cs +++ b/sln/src/NSpec/Domain/Context.cs @@ -410,7 +410,7 @@ public Action AfterAllInstanceAsync get { return AfterAllChain.AsyncClassHook; } } - public Context(string name = "", string tags = null, bool isPending = false) + public Context(string name = "", string tags = null, bool isPending = false, Conventions conventions = null) { Name = name.Replace("_", " "); Tags = Domain.Tags.ParseTags(tags); @@ -419,11 +419,13 @@ public Context(string name = "", string tags = null, bool isPending = false) Examples = new List(); Contexts = new ContextCollection(); - BeforeAllChain = new BeforeAllChain(this); - BeforeChain = new BeforeChain(this); - ActChain = new ActChain(this); - AfterChain = new AfterChain(this); - AfterAllChain = new AfterAllChain(this); + if (conventions == null) conventions = new DefaultConventions().Initialize(); + + BeforeAllChain = new BeforeAllChain(this, conventions); + BeforeChain = new BeforeChain(this, conventions); + ActChain = new ActChain(this, conventions); + AfterChain = new AfterChain(this, conventions); + AfterAllChain = new AfterAllChain(this, conventions); } public string Name; diff --git a/sln/src/NSpec/Domain/HookChainBase.cs b/sln/src/NSpec/Domain/HookChainBase.cs index e313a672..f74a87e8 100644 --- a/sln/src/NSpec/Domain/HookChainBase.cs +++ b/sln/src/NSpec/Domain/HookChainBase.cs @@ -9,10 +9,9 @@ namespace NSpec.Domain { public abstract class HookChainBase { - public void BuildMethodLevel(Conventions conventions, List classHierarchy) + public void BuildMethodLevel(List classHierarchy) { - var selector = GetMethodSelector(conventions); - var methods = GetMethodsFromHierarchy(classHierarchy, selector); + var methods = GetMethodsFromHierarchy(classHierarchy, methodSelector); if (ReverseClassMethods()) { @@ -24,8 +23,7 @@ public void BuildMethodLevel(Conventions conventions, List classHierarchy) ClassHook = instance => methods.Do(m => m.Invoke(instance, null)); } - var asyncSelector = GetAsyncMethodSelector(conventions); - var asyncMethods = GetMethodsFromHierarchy(classHierarchy, asyncSelector); + var asyncMethods = GetMethodsFromHierarchy(classHierarchy, asyncMethodSelector); if (ReverseClassMethods()) { @@ -38,10 +36,6 @@ public void BuildMethodLevel(Conventions conventions, List classHierarchy) } } - protected abstract Func GetMethodSelector(Conventions conventions); - - protected abstract Func GetAsyncMethodSelector(Conventions conventions); - protected virtual bool ReverseClassMethods() { return false; @@ -134,7 +128,8 @@ protected static List GetMethodsFromHierarchy( .ToList(); } - public HookChainBase(Context context, string hookName, string asyncHookName, string classHookName) + public HookChainBase( + Context context, string hookName, string asyncHookName, string classHookName) { this.context = context; this.hookName = hookName; @@ -150,8 +145,10 @@ public HookChainBase(Context context, string hookName, string asyncHookName, str public Exception Exception; - protected readonly Context context; + protected Func methodSelector; + protected Func asyncMethodSelector; + protected readonly Context context; protected readonly string hookName; protected readonly string asyncHookName; protected readonly string classHookName; From 9d71a8969f5d6fb9485c696315141628f6d79b15 Mon Sep 17 00:00:00 2001 From: GiuseppePiscopo Date: Sun, 21 May 2017 14:54:30 +0200 Subject: [PATCH 13/26] refactor(hooks): generalize running all hooks --- sln/src/NSpec/Domain/ActChain.cs | 15 ++-------- sln/src/NSpec/Domain/AfterAllChain.cs | 19 ++---------- sln/src/NSpec/Domain/AfterChain.cs | 20 ++----------- sln/src/NSpec/Domain/BeforeAllChain.cs | 13 +-------- sln/src/NSpec/Domain/BeforeChain.cs | 15 ++-------- sln/src/NSpec/Domain/HookChainBase.cs | 40 +++++++++++++++++++------- 6 files changed, 39 insertions(+), 83 deletions(-) diff --git a/sln/src/NSpec/Domain/ActChain.cs b/sln/src/NSpec/Domain/ActChain.cs index 0bdf9ab6..2a6a03dd 100644 --- a/sln/src/NSpec/Domain/ActChain.cs +++ b/sln/src/NSpec/Domain/ActChain.cs @@ -8,18 +8,6 @@ namespace NSpec.Domain { public class ActChain : HookChainBase { - protected override void RunHooks(nspec instance) - { - // parent chain - RecurseAncestors(c => c.ActChain.RunHooks(instance)); - - // class (method-level) - RunClassHooks(instance); - - // context-level - RunContextHooks(); - } - protected override bool CanRun(nspec instance) { return context.BeforeAllChain.AnyBeforeAllsThrew() @@ -28,10 +16,11 @@ protected override bool CanRun(nspec instance) } public ActChain(Context context, Conventions conventions) - : base(context, "act", "actAsync", "act_each") + : base(context, true, false, "act", "actAsync", "act_each") { methodSelector = conventions.GetMethodLevelAct; asyncMethodSelector = conventions.GetAsyncMethodLevelAct; + chainSelector = c => c.ActChain; } } } diff --git a/sln/src/NSpec/Domain/AfterAllChain.cs b/sln/src/NSpec/Domain/AfterAllChain.cs index dfe23f2b..d0a07659 100644 --- a/sln/src/NSpec/Domain/AfterAllChain.cs +++ b/sln/src/NSpec/Domain/AfterAllChain.cs @@ -8,22 +8,6 @@ namespace NSpec.Domain { public class AfterAllChain : HookChainBase { - protected override bool ReverseClassMethods() - { - return true; - } - - protected override void RunHooks(nspec instance) - { - // context-level - RunContextHooks(); - - // class (method-level) - RunClassHooks(instance); - - // do NOT traverse parent chain - } - protected override bool CanRun(nspec instance) { return context.BeforeAllChain.AncestorBeforeAllsThrew() @@ -32,10 +16,11 @@ protected override bool CanRun(nspec instance) } public AfterAllChain(Context context, Conventions conventions) - : base(context, "afterAll", "afterAllAsync", "after_all") + : base(context, false, true, "afterAll", "afterAllAsync", "after_all") { methodSelector = conventions.GetMethodLevelAfterAll; asyncMethodSelector = conventions.GetAsyncMethodLevelAfterAll; + //chainSelector = c => c.AfterAllChain; } } } diff --git a/sln/src/NSpec/Domain/AfterChain.cs b/sln/src/NSpec/Domain/AfterChain.cs index 35861bed..b4878f80 100644 --- a/sln/src/NSpec/Domain/AfterChain.cs +++ b/sln/src/NSpec/Domain/AfterChain.cs @@ -8,33 +8,17 @@ namespace NSpec.Domain { public class AfterChain : HookChainBase { - protected override bool ReverseClassMethods() - { - return true; - } - - protected override void RunHooks(nspec instance) - { - // context-level - RunContextHooks(); - - // class (method-level) - RunClassHooks(instance); - - // parent chain - RecurseAncestors(c => c.AfterChain.RunHooks(instance)); - } - protected override bool CanRun(nspec instance) { return !context.BeforeAllChain.AnyBeforeAllsThrew(); } public AfterChain(Context context, Conventions conventions) - : base(context, "after", "afterAsync", "after_each") + : base(context, true, true, "after", "afterAsync", "after_each") { methodSelector = conventions.GetMethodLevelAfter; asyncMethodSelector = conventions.GetAsyncMethodLevelAfter; + chainSelector = c => c.AfterChain; } } } diff --git a/sln/src/NSpec/Domain/BeforeAllChain.cs b/sln/src/NSpec/Domain/BeforeAllChain.cs index f674a64b..3fb90562 100644 --- a/sln/src/NSpec/Domain/BeforeAllChain.cs +++ b/sln/src/NSpec/Domain/BeforeAllChain.cs @@ -8,17 +8,6 @@ namespace NSpec.Domain { public class BeforeAllChain : HookChainBase { - protected override void RunHooks(nspec instance) - { - // do NOT traverse parent chain - - // class (method-level) - RunClassHooks(instance); - - // context-level - RunContextHooks(); - } - protected override bool CanRun(nspec instance) { return AncestorBeforeAllsThrew() @@ -37,7 +26,7 @@ public bool AncestorBeforeAllsThrew() } public BeforeAllChain(Context context, Conventions conventions) - : base(context, "beforeAll", "beforeAllAsync", "before_all") + : base(context, false, false, "beforeAll", "beforeAllAsync", "before_all") { methodSelector = conventions.GetMethodLevelBeforeAll; asyncMethodSelector = conventions.GetAsyncMethodLevelBeforeAll; diff --git a/sln/src/NSpec/Domain/BeforeChain.cs b/sln/src/NSpec/Domain/BeforeChain.cs index e49ba46c..c73f21ba 100644 --- a/sln/src/NSpec/Domain/BeforeChain.cs +++ b/sln/src/NSpec/Domain/BeforeChain.cs @@ -8,28 +8,17 @@ namespace NSpec.Domain { public class BeforeChain : HookChainBase { - protected override void RunHooks(nspec instance) - { - // parent chain - RecurseAncestors(c => c.BeforeChain.RunHooks(instance)); - - - // class (method-level) - RunContextHooks(); - // context-level - RunClassHooks(instance); - } - protected override bool CanRun(nspec instance) { return !context.BeforeAllChain.AnyBeforeAllsThrew(); } public BeforeChain(Context context, Conventions conventions) - : base(context, "before", "beforeAsync", "before_each") + : base(context, true, false, "before", "beforeAsync", "before_each") { methodSelector = conventions.GetMethodLevelBefore; asyncMethodSelector = conventions.GetAsyncMethodLevelBefore; + chainSelector = c => c.BeforeChain; } } } diff --git a/sln/src/NSpec/Domain/HookChainBase.cs b/sln/src/NSpec/Domain/HookChainBase.cs index f74a87e8..cadf1168 100644 --- a/sln/src/NSpec/Domain/HookChainBase.cs +++ b/sln/src/NSpec/Domain/HookChainBase.cs @@ -13,7 +13,7 @@ public void BuildMethodLevel(List classHierarchy) { var methods = GetMethodsFromHierarchy(classHierarchy, methodSelector); - if (ReverseClassMethods()) + if (reversed) { methods.Reverse(); } @@ -25,7 +25,7 @@ public void BuildMethodLevel(List classHierarchy) var asyncMethods = GetMethodsFromHierarchy(classHierarchy, asyncMethodSelector); - if (ReverseClassMethods()) + if (reversed) { asyncMethods.Reverse(); } @@ -36,15 +36,8 @@ public void BuildMethodLevel(List classHierarchy) } } - protected virtual bool ReverseClassMethods() - { - return false; - } - protected abstract bool CanRun(nspec instance); - protected abstract void RunHooks(nspec instance); - public void Run(nspec instance) { if (CanRun(instance)) @@ -53,6 +46,27 @@ public void Run(nspec instance) } } + protected void RunHooks(nspec instance) + { + // parent chain + if (!reversed && traverse) + { + RecurseAncestors(c => chainSelector(c).RunHooks(instance)); + } + + // class (method-level) + RunClassHooks(instance); + + // context-level + RunContextHooks(); + + // parent chain, reverse order + if (reversed && traverse) + { + RecurseAncestors(c => chainSelector(c).RunHooks(instance)); + } + } + public static bool RunAndHandleException(Action action, nspec instance, ref Exception exceptionToSet) { bool hasThrown = false; @@ -129,9 +143,12 @@ protected static List GetMethodsFromHierarchy( } public HookChainBase( - Context context, string hookName, string asyncHookName, string classHookName) + Context context, bool traverse, bool reversed, + string hookName, string asyncHookName, string classHookName) { this.context = context; + this.traverse = traverse; + this.reversed = reversed; this.hookName = hookName; this.asyncHookName = asyncHookName; this.classHookName = classHookName; @@ -147,8 +164,11 @@ public HookChainBase( protected Func methodSelector; protected Func asyncMethodSelector; + protected Func chainSelector; protected readonly Context context; + protected readonly bool traverse; + protected readonly bool reversed; protected readonly string hookName; protected readonly string asyncHookName; protected readonly string classHookName; From cb4ba49d3efc34c34b1f2c37059112349e1ad1c9 Mon Sep 17 00:00:00 2001 From: GiuseppePiscopo Date: Sun, 21 May 2017 15:07:49 +0200 Subject: [PATCH 14/26] refactor(hooks): extract hooks traversing ancestors --- sln/src/NSpec/Domain/ActChain.cs | 4 +- sln/src/NSpec/Domain/AfterAllChain.cs | 3 +- sln/src/NSpec/Domain/AfterChain.cs | 4 +- sln/src/NSpec/Domain/BeforeAllChain.cs | 2 +- sln/src/NSpec/Domain/BeforeChain.cs | 4 +- sln/src/NSpec/Domain/HookChainBase.cs | 61 +++++++++++++++----------- 6 files changed, 44 insertions(+), 34 deletions(-) diff --git a/sln/src/NSpec/Domain/ActChain.cs b/sln/src/NSpec/Domain/ActChain.cs index 2a6a03dd..a1805afd 100644 --- a/sln/src/NSpec/Domain/ActChain.cs +++ b/sln/src/NSpec/Domain/ActChain.cs @@ -6,7 +6,7 @@ namespace NSpec.Domain { - public class ActChain : HookChainBase + public class ActChain : TraversingHookChain { protected override bool CanRun(nspec instance) { @@ -16,7 +16,7 @@ protected override bool CanRun(nspec instance) } public ActChain(Context context, Conventions conventions) - : base(context, true, false, "act", "actAsync", "act_each") + : base(context, "act", "actAsync", "act_each") { methodSelector = conventions.GetMethodLevelAct; asyncMethodSelector = conventions.GetAsyncMethodLevelAct; diff --git a/sln/src/NSpec/Domain/AfterAllChain.cs b/sln/src/NSpec/Domain/AfterAllChain.cs index d0a07659..2f684906 100644 --- a/sln/src/NSpec/Domain/AfterAllChain.cs +++ b/sln/src/NSpec/Domain/AfterAllChain.cs @@ -16,11 +16,10 @@ protected override bool CanRun(nspec instance) } public AfterAllChain(Context context, Conventions conventions) - : base(context, false, true, "afterAll", "afterAllAsync", "after_all") + : base(context, "afterAll", "afterAllAsync", "after_all", reversed: true) { methodSelector = conventions.GetMethodLevelAfterAll; asyncMethodSelector = conventions.GetAsyncMethodLevelAfterAll; - //chainSelector = c => c.AfterAllChain; } } } diff --git a/sln/src/NSpec/Domain/AfterChain.cs b/sln/src/NSpec/Domain/AfterChain.cs index b4878f80..74974bbd 100644 --- a/sln/src/NSpec/Domain/AfterChain.cs +++ b/sln/src/NSpec/Domain/AfterChain.cs @@ -6,7 +6,7 @@ namespace NSpec.Domain { - public class AfterChain : HookChainBase + public class AfterChain : TraversingHookChain { protected override bool CanRun(nspec instance) { @@ -14,7 +14,7 @@ protected override bool CanRun(nspec instance) } public AfterChain(Context context, Conventions conventions) - : base(context, true, true, "after", "afterAsync", "after_each") + : base(context, "after", "afterAsync", "after_each", reversed: true) { methodSelector = conventions.GetMethodLevelAfter; asyncMethodSelector = conventions.GetAsyncMethodLevelAfter; diff --git a/sln/src/NSpec/Domain/BeforeAllChain.cs b/sln/src/NSpec/Domain/BeforeAllChain.cs index 3fb90562..3897174c 100644 --- a/sln/src/NSpec/Domain/BeforeAllChain.cs +++ b/sln/src/NSpec/Domain/BeforeAllChain.cs @@ -26,7 +26,7 @@ public bool AncestorBeforeAllsThrew() } public BeforeAllChain(Context context, Conventions conventions) - : base(context, false, false, "beforeAll", "beforeAllAsync", "before_all") + : base(context, "beforeAll", "beforeAllAsync", "before_all") { methodSelector = conventions.GetMethodLevelBeforeAll; asyncMethodSelector = conventions.GetAsyncMethodLevelBeforeAll; diff --git a/sln/src/NSpec/Domain/BeforeChain.cs b/sln/src/NSpec/Domain/BeforeChain.cs index c73f21ba..8253c048 100644 --- a/sln/src/NSpec/Domain/BeforeChain.cs +++ b/sln/src/NSpec/Domain/BeforeChain.cs @@ -6,7 +6,7 @@ namespace NSpec.Domain { - public class BeforeChain : HookChainBase + public class BeforeChain : TraversingHookChain { protected override bool CanRun(nspec instance) { @@ -14,7 +14,7 @@ protected override bool CanRun(nspec instance) } public BeforeChain(Context context, Conventions conventions) - : base(context, true, false, "before", "beforeAsync", "before_each") + : base(context, "before", "beforeAsync", "before_each") { methodSelector = conventions.GetMethodLevelBefore; asyncMethodSelector = conventions.GetAsyncMethodLevelBefore; diff --git a/sln/src/NSpec/Domain/HookChainBase.cs b/sln/src/NSpec/Domain/HookChainBase.cs index cadf1168..bf03b81d 100644 --- a/sln/src/NSpec/Domain/HookChainBase.cs +++ b/sln/src/NSpec/Domain/HookChainBase.cs @@ -46,25 +46,13 @@ public void Run(nspec instance) } } - protected void RunHooks(nspec instance) + protected virtual void RunHooks(nspec instance) { - // parent chain - if (!reversed && traverse) - { - RecurseAncestors(c => chainSelector(c).RunHooks(instance)); - } - // class (method-level) RunClassHooks(instance); // context-level RunContextHooks(); - - // parent chain, reverse order - if (reversed && traverse) - { - RecurseAncestors(c => chainSelector(c).RunHooks(instance)); - } } public static bool RunAndHandleException(Action action, nspec instance, ref Exception exceptionToSet) @@ -128,11 +116,6 @@ protected void RunContextHooks() AsyncHook.SafeInvoke(); } - protected void RecurseAncestors(Action ancestorAction) - { - if (context.Parent != null) ancestorAction(context.Parent); - } - protected static List GetMethodsFromHierarchy( List classHierarchy, Func selectMethod) { @@ -142,16 +125,14 @@ protected static List GetMethodsFromHierarchy( .ToList(); } - public HookChainBase( - Context context, bool traverse, bool reversed, - string hookName, string asyncHookName, string classHookName) + public HookChainBase(Context context, + string hookName, string asyncHookName, string classHookName, bool reversed = false) { this.context = context; - this.traverse = traverse; - this.reversed = reversed; this.hookName = hookName; this.asyncHookName = asyncHookName; this.classHookName = classHookName; + this.reversed = reversed; } public Action Hook; @@ -164,13 +145,43 @@ public HookChainBase( protected Func methodSelector; protected Func asyncMethodSelector; - protected Func chainSelector; protected readonly Context context; - protected readonly bool traverse; protected readonly bool reversed; protected readonly string hookName; protected readonly string asyncHookName; protected readonly string classHookName; } + + public abstract class TraversingHookChain : HookChainBase + { + protected override void RunHooks(nspec instance) + { + // parent chain + if (!reversed) + { + RecurseAncestors(c => chainSelector(c).RunHooks(instance)); + } + + base.RunHooks(instance); + + // parent chain, reverse order + if (reversed) + { + RecurseAncestors(c => chainSelector(c).RunHooks(instance)); + } + } + + protected void RecurseAncestors(Action ancestorAction) + { + if (context.Parent != null) ancestorAction(context.Parent); + } + + public TraversingHookChain(Context context, + string hookName, string asyncHookName, string classHookName, bool reversed = false) + : base(context, hookName, asyncHookName, classHookName, reversed) + { } + + protected Func chainSelector; + } } From bfab7ff4edcd44ec54594dbaa5896e94e39b195c Mon Sep 17 00:00:00 2001 From: GiuseppePiscopo Date: Sun, 21 May 2017 15:16:36 +0200 Subject: [PATCH 15/26] refactor(context): example knows how to add itself to context --- sln/src/NSpec/Domain/ClassContext.cs | 5 ----- sln/src/NSpec/Domain/Context.cs | 8 ++------ sln/src/NSpec/Domain/ExampleBase.cs | 9 +++++++++ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/sln/src/NSpec/Domain/ClassContext.cs b/sln/src/NSpec/Domain/ClassContext.cs index 8054d435..406efa6d 100644 --- a/sln/src/NSpec/Domain/ClassContext.cs +++ b/sln/src/NSpec/Domain/ClassContext.cs @@ -46,11 +46,6 @@ public override bool IsSub(Type baseType) return baseType == SpecType; } - IEnumerable GetMethodsFromHierarchy(Func methodAccessor) - { - return classHierarchy.Select(methodAccessor).Where(mi => mi != null); - } - void AddFailingExample(Exception targetEx) { var reportedEx = (targetEx.InnerException != null) diff --git a/sln/src/NSpec/Domain/Context.cs b/sln/src/NSpec/Domain/Context.cs index e3f82bce..5f6c7332 100644 --- a/sln/src/NSpec/Domain/Context.cs +++ b/sln/src/NSpec/Domain/Context.cs @@ -13,13 +13,9 @@ public class Context { public void AddExample(ExampleBase example) { - example.Context = this; - - example.Tags.AddRange(Tags); + example.AddTo(this); Examples.Add(example); - - example.Pending |= IsPending(); } public IEnumerable AllExamples() @@ -43,7 +39,7 @@ public void AddContext(Context child) child.Parent = this; - child.Tags.AddRange(child.Parent.Tags); + child.Tags.AddRange(Tags); Contexts.Add(child); } diff --git a/sln/src/NSpec/Domain/ExampleBase.cs b/sln/src/NSpec/Domain/ExampleBase.cs index 4676bdb3..34f18780 100644 --- a/sln/src/NSpec/Domain/ExampleBase.cs +++ b/sln/src/NSpec/Domain/ExampleBase.cs @@ -36,6 +36,15 @@ public static string Parse(Expression exp) return Parse(exp.Body); } + public void AddTo(Context context) + { + Context = context; + + Tags.AddRange(context.Tags); + + Pending |= context.IsPending(); + } + public abstract void Run(nspec nspec); public abstract void RunPending(nspec nspec); From 4c8bc1c82206f26e46bf1ba82391e6bc70bd94f9 Mon Sep 17 00:00:00 2001 From: GiuseppePiscopo Date: Sun, 21 May 2017 15:32:35 +0200 Subject: [PATCH 16/26] refactor(context): let beforeAll/afterAll traverse exceptions --- sln/src/NSpec/Domain/ActChain.cs | 2 +- sln/src/NSpec/Domain/AfterAllChain.cs | 7 +++++++ sln/src/NSpec/Domain/AfterChain.cs | 2 +- sln/src/NSpec/Domain/BeforeAllChain.cs | 11 +++++++++-- sln/src/NSpec/Domain/BeforeChain.cs | 2 +- sln/src/NSpec/Domain/Context.cs | 21 ++++++++------------- 6 files changed, 27 insertions(+), 18 deletions(-) diff --git a/sln/src/NSpec/Domain/ActChain.cs b/sln/src/NSpec/Domain/ActChain.cs index a1805afd..6d8464e7 100644 --- a/sln/src/NSpec/Domain/ActChain.cs +++ b/sln/src/NSpec/Domain/ActChain.cs @@ -10,7 +10,7 @@ public class ActChain : TraversingHookChain { protected override bool CanRun(nspec instance) { - return context.BeforeAllChain.AnyBeforeAllsThrew() + return context.BeforeAllChain.AnyBeforeAllThrew() ? false : (context.BeforeChain.Exception == null); } diff --git a/sln/src/NSpec/Domain/AfterAllChain.cs b/sln/src/NSpec/Domain/AfterAllChain.cs index 2f684906..3b15aeeb 100644 --- a/sln/src/NSpec/Domain/AfterAllChain.cs +++ b/sln/src/NSpec/Domain/AfterAllChain.cs @@ -15,6 +15,13 @@ protected override bool CanRun(nspec instance) : context.AnyUnfilteredExampleInSubTree(instance); } + public Exception AnyAfterAllException() + { + // give precedence to Exception closer in the chain + + return Exception ?? context.Parent?.AfterAllChain.AnyAfterAllException(); + } + public AfterAllChain(Context context, Conventions conventions) : base(context, "afterAll", "afterAllAsync", "after_all", reversed: true) { diff --git a/sln/src/NSpec/Domain/AfterChain.cs b/sln/src/NSpec/Domain/AfterChain.cs index 74974bbd..238ccdee 100644 --- a/sln/src/NSpec/Domain/AfterChain.cs +++ b/sln/src/NSpec/Domain/AfterChain.cs @@ -10,7 +10,7 @@ public class AfterChain : TraversingHookChain { protected override bool CanRun(nspec instance) { - return !context.BeforeAllChain.AnyBeforeAllsThrew(); + return !context.BeforeAllChain.AnyBeforeAllThrew(); } public AfterChain(Context context, Conventions conventions) diff --git a/sln/src/NSpec/Domain/BeforeAllChain.cs b/sln/src/NSpec/Domain/BeforeAllChain.cs index 3897174c..3b7b3b22 100644 --- a/sln/src/NSpec/Domain/BeforeAllChain.cs +++ b/sln/src/NSpec/Domain/BeforeAllChain.cs @@ -15,14 +15,21 @@ protected override bool CanRun(nspec instance) : context.AnyUnfilteredExampleInSubTree(instance); } - public bool AnyBeforeAllsThrew() + public bool AnyBeforeAllThrew() { return (Exception != null || AncestorBeforeAllsThrew()); } public bool AncestorBeforeAllsThrew() { - return (context.Parent?.BeforeAllChain.AnyBeforeAllsThrew() ?? false); + return (context.Parent?.BeforeAllChain.AnyBeforeAllThrew() ?? false); + } + + public Exception AnyBeforeAllException() + { + // give precedence to Exception farther up in the chain + + return context.Parent?.BeforeAllChain.AnyBeforeAllException() ?? Exception; } public BeforeAllChain(Context context, Conventions conventions) diff --git a/sln/src/NSpec/Domain/BeforeChain.cs b/sln/src/NSpec/Domain/BeforeChain.cs index 8253c048..23677b1b 100644 --- a/sln/src/NSpec/Domain/BeforeChain.cs +++ b/sln/src/NSpec/Domain/BeforeChain.cs @@ -10,7 +10,7 @@ public class BeforeChain : TraversingHookChain { protected override bool CanRun(nspec instance) { - return !context.BeforeAllChain.AnyBeforeAllsThrew(); + return !context.BeforeAllChain.AnyBeforeAllThrew(); } public BeforeChain(Context context, Conventions conventions) diff --git a/sln/src/NSpec/Domain/Context.cs b/sln/src/NSpec/Domain/Context.cs index 5f6c7332..57d8c6f9 100644 --- a/sln/src/NSpec/Domain/Context.cs +++ b/sln/src/NSpec/Domain/Context.cs @@ -91,19 +91,14 @@ public virtual void Run(bool failFast, nspec instance = null, bool recurse = tru /// public virtual void AssignExceptions(bool recurse = true) { - AssignExceptions(null, null, recurse); - } - - protected virtual void AssignExceptions(Exception inheritedBeforeAllException, Exception inheritedAfterAllException, bool recurse) - { - inheritedBeforeAllException = inheritedBeforeAllException ?? BeforeAllChain.Exception; - inheritedAfterAllException = AfterAllChain.Exception ?? inheritedAfterAllException; + var anyBeforeAllException = BeforeAllChain.AnyBeforeAllException(); + var anyAfterAllException = AfterAllChain.AnyAfterAllException(); - // if an exception was thrown before the example (either `before` or `act`) but was expected, ignore it - Exception unexpectedException = ClearExpectedException ? null : BeforeChain.Exception ?? ActChain.Exception; + // if an exception was thrown before the example (only in `act`) but was expected, ignore it + Exception unexpectedException = ClearExpectedException ? null : ActChain.Exception; - Exception previousException = inheritedBeforeAllException ?? unexpectedException; - Exception followingException = AfterChain.Exception ?? inheritedAfterAllException; + Exception previousException = anyBeforeAllException ?? BeforeChain.Exception ?? unexpectedException; + Exception followingException = AfterChain.Exception ?? anyAfterAllException; for (int i = 0; i < Examples.Count; i++) { @@ -117,7 +112,7 @@ protected virtual void AssignExceptions(Exception inheritedBeforeAllException, E if (recurse) { - Contexts.Do(c => c.AssignExceptions(inheritedBeforeAllException, inheritedAfterAllException, recurse)); + Contexts.Do(c => c.AssignExceptions(recurse)); } } @@ -201,7 +196,7 @@ public void Exercise(ExampleBase example, nspec instance) void RunExample(ExampleBase example, nspec instance) { - if (BeforeAllChain.AnyBeforeAllsThrew()) return; + if (BeforeAllChain.AnyBeforeAllThrew()) return; if (BeforeChain.Exception != null) return; From 87905d66ddfc63589de72550745669e5f46a728c Mon Sep 17 00:00:00 2001 From: GiuseppePiscopo Date: Sun, 21 May 2017 15:53:24 +0200 Subject: [PATCH 17/26] refactor(hooks): move helper methods to dedicated class --- sln/src/NSpec/Domain/Context.cs | 4 +- sln/src/NSpec/Domain/ContextUtils.cs | 49 +++++++++++++ sln/src/NSpec/Domain/HookChainBase.cs | 81 ++------------------- sln/src/NSpec/Domain/TraversingHookChain.cs | 36 +++++++++ 4 files changed, 95 insertions(+), 75 deletions(-) create mode 100644 sln/src/NSpec/Domain/ContextUtils.cs create mode 100644 sln/src/NSpec/Domain/TraversingHookChain.cs diff --git a/sln/src/NSpec/Domain/Context.cs b/sln/src/NSpec/Domain/Context.cs index 57d8c6f9..b1efb480 100644 --- a/sln/src/NSpec/Domain/Context.cs +++ b/sln/src/NSpec/Domain/Context.cs @@ -168,7 +168,7 @@ public void Exercise(ExampleBase example, nspec instance) if (example.Pending) { - HookChainBase.RunAndHandleException(example.RunPending, instance, ref example.Exception); + ContextUtils.RunAndHandleException(example.RunPending, instance, ref example.Exception); return; } @@ -200,7 +200,7 @@ void RunExample(ExampleBase example, nspec instance) if (BeforeChain.Exception != null) return; - HookChainBase.RunAndHandleException(example.Run, instance, ref example.Exception); + ContextUtils.RunAndHandleException(example.Run, instance, ref example.Exception); } public virtual bool IsSub(Type baseType) diff --git a/sln/src/NSpec/Domain/ContextUtils.cs b/sln/src/NSpec/Domain/ContextUtils.cs new file mode 100644 index 00000000..68aa1e07 --- /dev/null +++ b/sln/src/NSpec/Domain/ContextUtils.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace NSpec.Domain +{ + public static class ContextUtils + { + public static List GetMethodsFromHierarchy( + List classHierarchy, Func selectMethod) + { + return classHierarchy + .Select(selectMethod) + .Where(m => m != null) + .ToList(); + } + + public static bool RunAndHandleException(Action action, nspec instance, ref Exception targetException) + { + bool hasThrown = false; + Exception exceptionToSet = null; + + try + { + action(instance); + } + catch (TargetInvocationException invocationException) + { + exceptionToSet = instance.ExceptionToReturn(invocationException.InnerException); + + hasThrown = true; + } + catch (Exception exception) + { + exceptionToSet = instance.ExceptionToReturn(exception); + + hasThrown = true; + } + + if (targetException == null && exceptionToSet != null) + { + targetException = exceptionToSet; + } + + return hasThrown; + } + } +} diff --git a/sln/src/NSpec/Domain/HookChainBase.cs b/sln/src/NSpec/Domain/HookChainBase.cs index bf03b81d..181b1c29 100644 --- a/sln/src/NSpec/Domain/HookChainBase.cs +++ b/sln/src/NSpec/Domain/HookChainBase.cs @@ -11,7 +11,7 @@ public abstract class HookChainBase { public void BuildMethodLevel(List classHierarchy) { - var methods = GetMethodsFromHierarchy(classHierarchy, methodSelector); + var methods = ContextUtils.GetMethodsFromHierarchy(classHierarchy, methodSelector); if (reversed) { @@ -23,7 +23,7 @@ public void BuildMethodLevel(List classHierarchy) ClassHook = instance => methods.Do(m => m.Invoke(instance, null)); } - var asyncMethods = GetMethodsFromHierarchy(classHierarchy, asyncMethodSelector); + var asyncMethods = ContextUtils.GetMethodsFromHierarchy(classHierarchy, asyncMethodSelector); if (reversed) { @@ -42,44 +42,20 @@ public void Run(nspec instance) { if (CanRun(instance)) { - RunAndHandleException(RunHooks, instance, ref Exception); + ContextUtils.RunAndHandleException(InvokeHooks, instance, ref Exception); } } - protected virtual void RunHooks(nspec instance) + protected virtual void InvokeHooks(nspec instance) { // class (method-level) - RunClassHooks(instance); + InvokeClassHooks(instance); // context-level - RunContextHooks(); + InvokeContextHooks(); } - public static bool RunAndHandleException(Action action, nspec instance, ref Exception exceptionToSet) - { - bool hasThrown = false; - - try - { - action(instance); - } - catch (TargetInvocationException invocationException) - { - if (exceptionToSet == null) exceptionToSet = instance.ExceptionToReturn(invocationException.InnerException); - - hasThrown = true; - } - catch (Exception exception) - { - if (exceptionToSet == null) exceptionToSet = instance.ExceptionToReturn(exception); - - hasThrown = true; - } - - return hasThrown; - } - - protected void RunClassHooks(nspec instance) + protected void InvokeClassHooks(nspec instance) { // class (method-level) @@ -95,7 +71,7 @@ protected void RunClassHooks(nspec instance) AsyncClassHook.SafeInvoke(instance); } - protected void RunContextHooks() + protected void InvokeContextHooks() { // context-level @@ -116,15 +92,6 @@ protected void RunContextHooks() AsyncHook.SafeInvoke(); } - protected static List GetMethodsFromHierarchy( - List classHierarchy, Func selectMethod) - { - return classHierarchy - .Select(selectMethod) - .Where(m => m != null) - .ToList(); - } - public HookChainBase(Context context, string hookName, string asyncHookName, string classHookName, bool reversed = false) { @@ -152,36 +119,4 @@ public HookChainBase(Context context, protected readonly string asyncHookName; protected readonly string classHookName; } - - public abstract class TraversingHookChain : HookChainBase - { - protected override void RunHooks(nspec instance) - { - // parent chain - if (!reversed) - { - RecurseAncestors(c => chainSelector(c).RunHooks(instance)); - } - - base.RunHooks(instance); - - // parent chain, reverse order - if (reversed) - { - RecurseAncestors(c => chainSelector(c).RunHooks(instance)); - } - } - - protected void RecurseAncestors(Action ancestorAction) - { - if (context.Parent != null) ancestorAction(context.Parent); - } - - public TraversingHookChain(Context context, - string hookName, string asyncHookName, string classHookName, bool reversed = false) - : base(context, hookName, asyncHookName, classHookName, reversed) - { } - - protected Func chainSelector; - } } diff --git a/sln/src/NSpec/Domain/TraversingHookChain.cs b/sln/src/NSpec/Domain/TraversingHookChain.cs new file mode 100644 index 00000000..8375372e --- /dev/null +++ b/sln/src/NSpec/Domain/TraversingHookChain.cs @@ -0,0 +1,36 @@ +using System; + +namespace NSpec.Domain +{ + public abstract class TraversingHookChain : HookChainBase + { + protected override void InvokeHooks(nspec instance) + { + // parent chain + if (!reversed) + { + RecurseAncestors(c => chainSelector(c).InvokeHooks(instance)); + } + + base.InvokeHooks(instance); + + // parent chain, reverse order + if (reversed) + { + RecurseAncestors(c => chainSelector(c).InvokeHooks(instance)); + } + } + + protected void RecurseAncestors(Action ancestorAction) + { + if (context.Parent != null) ancestorAction(context.Parent); + } + + public TraversingHookChain(Context context, + string hookName, string asyncHookName, string classHookName, bool reversed = false) + : base(context, hookName, asyncHookName, classHookName, reversed) + { } + + protected Func chainSelector; + } +} From fad71b69e0e6e94ff57c10a4feae697743bc31f0 Mon Sep 17 00:00:00 2001 From: GiuseppePiscopo Date: Sun, 21 May 2017 16:36:14 +0200 Subject: [PATCH 18/26] refactor(hooks): streamline access to exceptions --- sln/src/NSpec/Domain/ActChain.cs | 4 ++-- sln/src/NSpec/Domain/AfterAllChain.cs | 14 ++++++++++---- sln/src/NSpec/Domain/AfterChain.cs | 2 +- sln/src/NSpec/Domain/BeforeAllChain.cs | 17 +++++++++-------- sln/src/NSpec/Domain/BeforeChain.cs | 2 +- sln/src/NSpec/Domain/Context.cs | 17 +++++++---------- sln/src/NSpec/Domain/HookChainBase.cs | 13 ++++++++++--- sln/src/NSpec/Domain/TraversingHookChain.cs | 14 ++++++++++++++ sln/src/NSpec/nspec.cs | 12 +++++++----- sln/test/NSpec.Tests/WrittenContext.cs | 10 ++++++++++ 10 files changed, 71 insertions(+), 34 deletions(-) diff --git a/sln/src/NSpec/Domain/ActChain.cs b/sln/src/NSpec/Domain/ActChain.cs index 6d8464e7..ee65201c 100644 --- a/sln/src/NSpec/Domain/ActChain.cs +++ b/sln/src/NSpec/Domain/ActChain.cs @@ -10,9 +10,9 @@ public class ActChain : TraversingHookChain { protected override bool CanRun(nspec instance) { - return context.BeforeAllChain.AnyBeforeAllThrew() + return context.BeforeAllChain.AnyThrew() ? false - : (context.BeforeChain.Exception == null); + : !context.BeforeChain.AnyThrew(); } public ActChain(Context context, Conventions conventions) diff --git a/sln/src/NSpec/Domain/AfterAllChain.cs b/sln/src/NSpec/Domain/AfterAllChain.cs index 3b15aeeb..312a2dfb 100644 --- a/sln/src/NSpec/Domain/AfterAllChain.cs +++ b/sln/src/NSpec/Domain/AfterAllChain.cs @@ -10,16 +10,22 @@ public class AfterAllChain : HookChainBase { protected override bool CanRun(nspec instance) { - return context.BeforeAllChain.AncestorBeforeAllsThrew() + return context.BeforeAllChain.AncestorsThrew() ? false : context.AnyUnfilteredExampleInSubTree(instance); } - public Exception AnyAfterAllException() + public bool AnyThrew() { - // give precedence to Exception closer in the chain + return (AnyException() != null); + } - return Exception ?? context.Parent?.AfterAllChain.AnyAfterAllException(); + public override Exception AnyException() + { + // when hook chain is NOT traversed, build up exceptions along ancestor chain + + // give precedence to Exception closer in the chain + return Exception ?? context.Parent?.AfterAllChain.AnyException(); } public AfterAllChain(Context context, Conventions conventions) diff --git a/sln/src/NSpec/Domain/AfterChain.cs b/sln/src/NSpec/Domain/AfterChain.cs index 238ccdee..e10635f4 100644 --- a/sln/src/NSpec/Domain/AfterChain.cs +++ b/sln/src/NSpec/Domain/AfterChain.cs @@ -10,7 +10,7 @@ public class AfterChain : TraversingHookChain { protected override bool CanRun(nspec instance) { - return !context.BeforeAllChain.AnyBeforeAllThrew(); + return !context.BeforeAllChain.AnyThrew(); } public AfterChain(Context context, Conventions conventions) diff --git a/sln/src/NSpec/Domain/BeforeAllChain.cs b/sln/src/NSpec/Domain/BeforeAllChain.cs index 3b7b3b22..f1879809 100644 --- a/sln/src/NSpec/Domain/BeforeAllChain.cs +++ b/sln/src/NSpec/Domain/BeforeAllChain.cs @@ -10,26 +10,27 @@ public class BeforeAllChain : HookChainBase { protected override bool CanRun(nspec instance) { - return AncestorBeforeAllsThrew() + return AncestorsThrew() ? false : context.AnyUnfilteredExampleInSubTree(instance); } - public bool AnyBeforeAllThrew() + public bool AnyThrew() { - return (Exception != null || AncestorBeforeAllsThrew()); + return (Exception != null || AncestorsThrew()); } - public bool AncestorBeforeAllsThrew() + public bool AncestorsThrew() { - return (context.Parent?.BeforeAllChain.AnyBeforeAllThrew() ?? false); + return (context.Parent?.BeforeAllChain.AnyThrew() ?? false); } - public Exception AnyBeforeAllException() + public override Exception AnyException() { - // give precedence to Exception farther up in the chain + // when hook chain is NOT traversed, build up exceptions along ancestor chain - return context.Parent?.BeforeAllChain.AnyBeforeAllException() ?? Exception; + // give precedence to Exception farther up in the chain + return context.Parent?.BeforeAllChain.AnyException() ?? Exception; } public BeforeAllChain(Context context, Conventions conventions) diff --git a/sln/src/NSpec/Domain/BeforeChain.cs b/sln/src/NSpec/Domain/BeforeChain.cs index 23677b1b..6504c559 100644 --- a/sln/src/NSpec/Domain/BeforeChain.cs +++ b/sln/src/NSpec/Domain/BeforeChain.cs @@ -10,7 +10,7 @@ public class BeforeChain : TraversingHookChain { protected override bool CanRun(nspec instance) { - return !context.BeforeAllChain.AnyBeforeAllThrew(); + return !context.BeforeAllChain.AnyThrew(); } public BeforeChain(Context context, Conventions conventions) diff --git a/sln/src/NSpec/Domain/Context.cs b/sln/src/NSpec/Domain/Context.cs index b1efb480..28cff3f1 100644 --- a/sln/src/NSpec/Domain/Context.cs +++ b/sln/src/NSpec/Domain/Context.cs @@ -91,14 +91,11 @@ public virtual void Run(bool failFast, nspec instance = null, bool recurse = tru /// public virtual void AssignExceptions(bool recurse = true) { - var anyBeforeAllException = BeforeAllChain.AnyBeforeAllException(); - var anyAfterAllException = AfterAllChain.AnyAfterAllException(); - // if an exception was thrown before the example (only in `act`) but was expected, ignore it - Exception unexpectedException = ClearExpectedException ? null : ActChain.Exception; + Exception unexpectedException = ClearExpectedException ? null : ActChain.AnyException(); - Exception previousException = anyBeforeAllException ?? BeforeChain.Exception ?? unexpectedException; - Exception followingException = AfterChain.Exception ?? anyAfterAllException; + Exception previousException = BeforeAllChain.AnyException() ?? BeforeChain.AnyException() ?? unexpectedException; + Exception followingException = AfterChain.AnyException() ?? AfterAllChain.AnyException(); for (int i = 0; i < Examples.Count; i++) { @@ -189,16 +186,16 @@ public void Exercise(ExampleBase example, nspec instance) // 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 (AfterChain.Exception != null) ClearExpectedException = false; + if (AfterChain.AnyThrew()) ClearExpectedException = false; example.StopTiming(stopWatch); } void RunExample(ExampleBase example, nspec instance) { - if (BeforeAllChain.AnyBeforeAllThrew()) return; + if (BeforeAllChain.AnyThrew()) return; - if (BeforeChain.Exception != null) return; + if (BeforeChain.AnyThrew()) return; ContextUtils.RunAndHandleException(example.Run, instance, ref example.Exception); } @@ -259,7 +256,7 @@ public override string ToString() string exampleText = $"{Examples.Count} exm"; string contextText = $"{Contexts.Count} exm"; - var exception = BeforeChain.Exception ?? ActChain.Exception ?? AfterChain.Exception; + var exception = BeforeChain.AnyException() ?? ActChain.AnyException() ?? AfterChain.AnyException(); string exceptionText = exception?.GetType().Name ?? String.Empty; return String.Join(",", new [] diff --git a/sln/src/NSpec/Domain/HookChainBase.cs b/sln/src/NSpec/Domain/HookChainBase.cs index 181b1c29..fa712ae4 100644 --- a/sln/src/NSpec/Domain/HookChainBase.cs +++ b/sln/src/NSpec/Domain/HookChainBase.cs @@ -42,7 +42,7 @@ public void Run(nspec instance) { if (CanRun(instance)) { - ContextUtils.RunAndHandleException(InvokeHooks, instance, ref Exception); + ContextUtils.RunAndHandleException(InvokeHooks, instance, ref exception); } } @@ -92,6 +92,14 @@ protected void InvokeContextHooks() AsyncHook.SafeInvoke(); } + public Exception Exception + { + get { return exception; } + protected set { exception = value; } + } + + public abstract Exception AnyException(); + public HookChainBase(Context context, string hookName, string asyncHookName, string classHookName, bool reversed = false) { @@ -108,8 +116,7 @@ public HookChainBase(Context context, public Action ClassHook { get; protected set; } public Action AsyncClassHook { get; protected set; } - public Exception Exception; - + protected Exception exception; protected Func methodSelector; protected Func asyncMethodSelector; diff --git a/sln/src/NSpec/Domain/TraversingHookChain.cs b/sln/src/NSpec/Domain/TraversingHookChain.cs index 8375372e..45eb8375 100644 --- a/sln/src/NSpec/Domain/TraversingHookChain.cs +++ b/sln/src/NSpec/Domain/TraversingHookChain.cs @@ -26,6 +26,20 @@ protected void RecurseAncestors(Action ancestorAction) if (context.Parent != null) ancestorAction(context.Parent); } + public override Exception AnyException() + { + // when hook chain is traversed, this chain exception holds any ancestor exception + + return Exception; + } + + public bool AnyThrew() + { + // when hook chain is traversed, this chain exception holds any ancestor exception + + return (Exception != null); + } + public TraversingHookChain(Context context, string hookName, string asyncHookName, string classHookName, bool reversed = false) : base(context, hookName, asyncHookName, classHookName, reversed) diff --git a/sln/src/NSpec/nspec.cs b/sln/src/NSpec/nspec.cs index 30c07999..0f238fab 100644 --- a/sln/src/NSpec/nspec.cs +++ b/sln/src/NSpec/nspec.cs @@ -296,8 +296,9 @@ public Action expectNoExceptions return () => { - if (specContext.ActChain.Exception != null) - throw specContext.ActChain.Exception; + var actException = specContext.ActChain.AnyException(); + + if (actException != null) throw actException; }; } } @@ -323,10 +324,11 @@ public virtual Action expect(string expectedMessage) where T : Exception return () => { - if (specContext.ActChain.Exception == null) - throw new ExceptionNotThrown(IncorrectType()); + var actException = specContext.ActChain.AnyException(); + + if (actException == null) throw new ExceptionNotThrown(IncorrectType()); - AssertExpectedException(specContext.ActChain.Exception, expectedMessage); + AssertExpectedException(actException, expectedMessage); // do not clear exception right now, during first phase, but leave a note for second phase specContext.ClearExpectedException = true; diff --git a/sln/test/NSpec.Tests/WrittenContext.cs b/sln/test/NSpec.Tests/WrittenContext.cs index ff107bb5..dee02618 100644 --- a/sln/test/NSpec.Tests/WrittenContext.cs +++ b/sln/test/NSpec.Tests/WrittenContext.cs @@ -15,10 +15,15 @@ public WrittenContext(Context context) Level = context.Level; Tags = new List(context.Tags); BeforeAllException = context.BeforeAllChain.Exception; + AnyBeforeAllException = context.BeforeAllChain.AnyException(); BeforeException = context.BeforeChain.Exception; + AnyBeforeException = context.BeforeChain.AnyException(); ActException = context.ActChain.Exception; + AnyActException = context.ActChain.AnyException(); AfterException = context.AfterChain.Exception; + AnyAfterException = context.AfterChain.AnyException(); AfterAllException = context.AfterAllChain.Exception; + AnyAfterAllException = context.AfterAllChain.AnyException(); ClearExpectedException = context.ClearExpectedException; CapturedOutput = context.CapturedOutput; IsPending = context.IsPending(); @@ -34,14 +39,19 @@ public WrittenContext(Context context) public List Tags { get; private set; } public Exception BeforeAllException { get; private set; } + public Exception AnyBeforeAllException { get; private set; } public Exception BeforeException { get; private set; } + public Exception AnyBeforeException { get; private set; } public Exception ActException { get; private set; } + public Exception AnyActException { get; private set; } public Exception AfterException { get; private set; } + public Exception AnyAfterException { get; private set; } public Exception AfterAllException { get; private set; } + public Exception AnyAfterAllException { get; private set; } public bool ClearExpectedException { get; private set; } From 73d35740a64324c9eadeadd4788725da1555100f Mon Sep 17 00:00:00 2001 From: GiuseppePiscopo Date: Sun, 21 May 2017 16:45:24 +0200 Subject: [PATCH 19/26] refactor(example): protect access to Pending property --- sln/src/NSpec/Domain/ExampleBase.cs | 2 +- sln/test/NSpec.Tests/ExampleBaseWrap.cs | 9 ++------- sln/test/NSpec.Tests/describe_ContextCollection.cs | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/sln/src/NSpec/Domain/ExampleBase.cs b/sln/src/NSpec/Domain/ExampleBase.cs index 34f18780..34f0e5a9 100644 --- a/sln/src/NSpec/Domain/ExampleBase.cs +++ b/sln/src/NSpec/Domain/ExampleBase.cs @@ -146,7 +146,7 @@ public ExampleBase(string name = "", string tags = "", bool pending = false) Pending = pending; } - public bool Pending; + public bool Pending { get; protected set; } public bool HasRun; public string Spec; public List Tags; diff --git a/sln/test/NSpec.Tests/ExampleBaseWrap.cs b/sln/test/NSpec.Tests/ExampleBaseWrap.cs index b16e1d83..ced48bee 100644 --- a/sln/test/NSpec.Tests/ExampleBaseWrap.cs +++ b/sln/test/NSpec.Tests/ExampleBaseWrap.cs @@ -10,15 +10,10 @@ namespace NSpec.Tests /// class ExampleBaseWrap : ExampleBase { - public ExampleBaseWrap(string name) - : base(name) + public ExampleBaseWrap(string name = "", bool pending = false) + : base(name, pending: pending) { } - - public ExampleBaseWrap() - { - } - public override void Run(nspec nspec) { throw new NotImplementedException(); diff --git a/sln/test/NSpec.Tests/describe_ContextCollection.cs b/sln/test/NSpec.Tests/describe_ContextCollection.cs index cd2f21d9..45e39bb2 100644 --- a/sln/test/NSpec.Tests/describe_ContextCollection.cs +++ b/sln/test/NSpec.Tests/describe_ContextCollection.cs @@ -21,7 +21,7 @@ public void setup() context.AddExample(new ExampleBaseWrap()); - context.AddExample(new ExampleBaseWrap { Pending = true }); + context.AddExample(new ExampleBaseWrap(pending: true)); context.AddExample(new ExampleBaseWrap { Exception = new KnownException() }); From 37d4277907aeef3ed8692012e8609b6ee1306536 Mon Sep 17 00:00:00 2001 From: GiuseppePiscopo Date: Mon, 22 May 2017 01:11:48 +0200 Subject: [PATCH 20/26] refactor(context): move to dedicated class the 3 phases or running an example --- sln/src/NSpec/Domain/Context.cs | 102 ++++----------- sln/src/NSpec/Domain/ExampleBase.cs | 45 +------ .../NSpec/Domain/ExampleFailureException.cs | 20 ++- sln/src/NSpec/Domain/RunnableExample.cs | 123 ++++++++++++++++++ 4 files changed, 171 insertions(+), 119 deletions(-) create mode 100644 sln/src/NSpec/Domain/RunnableExample.cs diff --git a/sln/src/NSpec/Domain/Context.cs b/sln/src/NSpec/Domain/Context.cs index 28cff3f1..0ee2a708 100644 --- a/sln/src/NSpec/Domain/Context.cs +++ b/sln/src/NSpec/Domain/Context.cs @@ -16,6 +16,8 @@ public void AddExample(ExampleBase example) example.AddTo(this); Examples.Add(example); + + runnables.Add(new RunnableExample(this, example)); } public IEnumerable AllExamples() @@ -63,13 +65,11 @@ public virtual void Run(bool failFast, nspec instance = null, bool recurse = tru } // intentionally using for loop to prevent collection was modified error in sample specs - for (int i = 0; i < Examples.Count; i++) + for (int i = 0; i < runnables.Count; i++) { - var example = Examples[i]; - - if (failFast && example.Context.HasAnyFailures()) return; + if (failFast && HasAnyFailures()) return; - Exercise(example, runningInstance); + runnables[i].Exercise(runningInstance); } if (recurse) @@ -91,20 +91,12 @@ public virtual void Run(bool failFast, nspec instance = null, bool recurse = tru /// public virtual void AssignExceptions(bool recurse = true) { - // if an exception was thrown before the example (only in `act`) but was expected, ignore it - Exception unexpectedException = ClearExpectedException ? null : ActChain.AnyException(); + var beforeAllException = BeforeAllChain.AnyException(); + var afterAllException = AfterAllChain.AnyException(); - Exception previousException = BeforeAllChain.AnyException() ?? BeforeChain.AnyException() ?? unexpectedException; - Exception followingException = AfterChain.AnyException() ?? AfterAllChain.AnyException(); - - for (int i = 0; i < Examples.Count; i++) + for (int i = 0; i < runnables.Count; i++) { - var example = Examples[i]; - - if (!example.Pending) - { - example.AssignProperException(previousException, followingException); - } + runnables[i].AssignException(beforeAllException, afterAllException); } if (recurse) @@ -124,14 +116,7 @@ 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); + runnables[i].Write(formatter); } if (recurse) @@ -154,52 +139,6 @@ public string FullContext() return Parent != null ? Parent.FullContext() + ". " + Name : Name; } - public void Exercise(ExampleBase example, nspec instance) - { - if (example.ShouldSkip(instance.tagsFilter)) - { - return; - } - - example.HasRun = true; - - if (example.Pending) - { - ContextUtils.RunAndHandleException(example.RunPending, instance, ref example.Exception); - - return; - } - - var stopWatch = example.StartTiming(); - - using (new ConsoleCatcher(output => example.CapturedOutput = output)) - { - BeforeChain.Run(instance); - - ActChain.Run(instance); - - RunExample(example, instance); - - AfterChain.Run(instance); - } - - // 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 (AfterChain.AnyThrew()) ClearExpectedException = false; - - example.StopTiming(stopWatch); - } - - void RunExample(ExampleBase example, nspec instance) - { - if (BeforeAllChain.AnyThrew()) return; - - if (BeforeChain.AnyThrew()) return; - - ContextUtils.RunAndHandleException(example.Run, instance, ref example.Exception); - } - public virtual bool IsSub(Type baseType) { return false; @@ -254,9 +193,15 @@ public override string ToString() { string levelText = $"L{Level}"; string exampleText = $"{Examples.Count} exm"; - string contextText = $"{Contexts.Count} exm"; + string contextText = $"{Contexts.Count} ctx"; + + var exception = + BeforeAllChain.AnyException() ?? + BeforeChain.AnyException() ?? + ActChain.AnyException() ?? + AfterChain.AnyException() ?? + AfterAllChain.AnyException(); - var exception = BeforeChain.AnyException() ?? ActChain.AnyException() ?? AfterChain.AnyException(); string exceptionText = exception?.GetType().Name ?? String.Empty; return String.Join(",", new [] @@ -265,8 +210,11 @@ public override string ToString() }); } - void WriteAncestors(ILiveFormatter formatter) + public void WriteAncestors(ILiveFormatter formatter) { + if (alreadyWritten) return; + + // when hitting root `nspec` context, skip it if (Parent == null) { alreadyWritten = true; @@ -275,7 +223,7 @@ void WriteAncestors(ILiveFormatter formatter) Parent.WriteAncestors(formatter); - if (!alreadyWritten) formatter.Write(this); + formatter.Write(this); alreadyWritten = true; } @@ -409,6 +357,8 @@ public Context(string name = "", string tags = null, bool isPending = false, Con if (conventions == null) conventions = new DefaultConventions().Initialize(); + runnables = new List(); + BeforeAllChain = new BeforeAllChain(this, conventions); BeforeChain = new BeforeChain(this, conventions); ActChain = new ActChain(this, conventions); @@ -430,6 +380,8 @@ public Context(string name = "", string tags = null, bool isPending = false, Con public string CapturedOutput; public Context Parent; + protected List runnables; + nspec builtInstance; bool alreadyWritten, isPending; } diff --git a/sln/src/NSpec/Domain/ExampleBase.cs b/sln/src/NSpec/Domain/ExampleBase.cs index 34f0e5a9..3b294b5c 100644 --- a/sln/src/NSpec/Domain/ExampleBase.cs +++ b/sln/src/NSpec/Domain/ExampleBase.cs @@ -24,6 +24,7 @@ public static string Parse(Expression expressionBody) { sentence = Regex.Replace(sentence, parensPattern, @"$1"); } + sentence = Regex.Replace(sentence, otherSeparatorsPattern, " "); sentence = Regex.Replace(sentence, commmasPattern, ","); sentence = Regex.Replace(sentence, multiSpacesPattern, " "); @@ -51,9 +52,6 @@ public void AddTo(Context context) public abstract bool IsAsync { get; } public abstract MethodInfo BodyMethodInfo { get; } - public TimeSpan Duration { get; set; } - public string CapturedOutput { get; set; } - public string FullName() { return Context.FullContext() + ". " + Spec + "."; @@ -79,45 +77,6 @@ public void StopTiming(Stopwatch stopWatch) Duration = stopWatch.Elapsed; } - public void AssignProperException(Exception previousException, Exception followingException) - { - if (previousException == null && followingException == null) - { - // stick with whatever exception may or may not be set on this example - return; - } - - 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) { return tagsFilter.ShouldSkip(Tags); @@ -146,6 +105,8 @@ public ExampleBase(string name = "", string tags = "", bool pending = false) Pending = pending; } + public TimeSpan Duration { get; protected set; } + public string CapturedOutput { get; set; } public bool Pending { get; protected set; } public bool HasRun; public string Spec; diff --git a/sln/src/NSpec/Domain/ExampleFailureException.cs b/sln/src/NSpec/Domain/ExampleFailureException.cs index f3bde9dd..50b04007 100644 --- a/sln/src/NSpec/Domain/ExampleFailureException.cs +++ b/sln/src/NSpec/Domain/ExampleFailureException.cs @@ -4,7 +4,23 @@ namespace NSpec.Domain { public class ExampleFailureException : Exception { + public static ExampleFailureException FromContext(Exception contextException) + { + return new ExampleFailureException( + $"Context Failure: {contextException.Message}", + contextException); + } + + public static ExampleFailureException FromContextAndExample( + Exception contextException, Exception exampleException) + { + return new ExampleFailureException( + $"Context Failure: {contextException.Message}, Example Failure: {exampleException.Message}", + contextException); + } + public ExampleFailureException(string message, Exception innerException) - : base(message, innerException) {} + : base(message, innerException) + { } } -} \ No newline at end of file +} diff --git a/sln/src/NSpec/Domain/RunnableExample.cs b/sln/src/NSpec/Domain/RunnableExample.cs new file mode 100644 index 00000000..9bbfc436 --- /dev/null +++ b/sln/src/NSpec/Domain/RunnableExample.cs @@ -0,0 +1,123 @@ +using System; +using NSpec.Domain.Formatters; + +namespace NSpec.Domain +{ + public class RunnableExample + { + public void Exercise(nspec instance) + { + if (example.ShouldSkip(instance.tagsFilter)) + { + return; + } + + example.HasRun = true; + + if (example.Pending) + { + ContextUtils.RunAndHandleException(example.RunPending, instance, ref example.Exception); + + return; + } + + var stopWatch = example.StartTiming(); + + using (new ConsoleCatcher(output => example.CapturedOutput = output)) + { + context.BeforeChain.Run(instance); + + context.ActChain.Run(instance); + + if (CanRun()) + { + ContextUtils.RunAndHandleException(example.Run, instance, ref example.Exception); + } + + context.AfterChain.Run(instance); + } + + // 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 (context.AfterChain.AnyThrew()) context.ClearExpectedException = false; + + example.StopTiming(stopWatch); + } + + bool CanRun() + { + return + !context.BeforeAllChain.AnyThrew() && + !context.BeforeChain.AnyThrew(); + } + + public void AssignException(Exception beforeAllException, Exception afterAllException) + { + if (example.Pending) return; + + // if an exception was thrown before the example (only in `act`) but was expected, ignore it + Exception unexpectedException = context.ClearExpectedException + ? null + : context.ActChain.AnyException(); + + Exception previousException = + beforeAllException ?? + context.BeforeChain.AnyException() ?? + unexpectedException; + + Exception followingException = + context.AfterChain.AnyException() ?? + afterAllException; + + if (previousException == null && followingException == null) + { + // stick with whatever exception may or may not be set on this example + return; + } + + if (previousException != null) + { + if (example.Exception != null && example.Exception.GetType() != typeof(ExceptionNotThrown)) + { + example.Exception = ExampleFailureException + .FromContextAndExample(previousException, example.Exception); + + return; + } + + if (example.Exception == null) + { + example.Exception = ExampleFailureException + .FromContext(previousException); + + return; + } + } + + if (example.Exception == null) + { + example.Exception = ExampleFailureException.FromContext(followingException); + } + } + + public void Write(ILiveFormatter formatter) + { + if (example.HasRun) + { + context.WriteAncestors(formatter); + + formatter.Write(example, context.Level); + } + } + + public RunnableExample(Context context, ExampleBase example) + { + this.context = context; + this.example = example; + } + + readonly Context context; + readonly ExampleBase example; + } +} From 86d2e95a1c02c843084dc6499e48170830730738 Mon Sep 17 00:00:00 2001 From: GiuseppePiscopo Date: Mon, 22 May 2017 01:44:31 +0200 Subject: [PATCH 21/26] refactor(domain): protect access to context/example properties --- sln/src/NSpec/Domain/Context.cs | 24 ++++++++++++------------ sln/src/NSpec/Domain/ExampleBase.cs | 8 ++++---- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/sln/src/NSpec/Domain/Context.cs b/sln/src/NSpec/Domain/Context.cs index 0ee2a708..bda9d48c 100644 --- a/sln/src/NSpec/Domain/Context.cs +++ b/sln/src/NSpec/Domain/Context.cs @@ -366,19 +366,19 @@ public Context(string name = "", string tags = null, bool isPending = false, Con AfterAllChain = new AfterAllChain(this, conventions); } - public string Name; - public int Level; - public List Tags; - public List Examples; - public ContextCollection Contexts; - public BeforeAllChain BeforeAllChain; - public BeforeChain BeforeChain; - public ActChain ActChain; - public AfterChain AfterChain; - public AfterAllChain AfterAllChain; + public string Name { get; protected set; } + public int Level { get; protected set; } + public List Tags { get; protected set; } + public List Examples { get; protected set; } + public ContextCollection Contexts { get; protected set; } + public BeforeAllChain BeforeAllChain { get; protected set; } + public BeforeChain BeforeChain { get; protected set; } + public ActChain ActChain { get; protected set; } + public AfterChain AfterChain { get; protected set; } + public AfterAllChain AfterAllChain { get; protected set; } public bool ClearExpectedException; - public string CapturedOutput; - public Context Parent; + public string CapturedOutput { get; protected set; } + public Context Parent { get; protected set; } protected List runnables; diff --git a/sln/src/NSpec/Domain/ExampleBase.cs b/sln/src/NSpec/Domain/ExampleBase.cs index 3b294b5c..00646f6e 100644 --- a/sln/src/NSpec/Domain/ExampleBase.cs +++ b/sln/src/NSpec/Domain/ExampleBase.cs @@ -106,12 +106,12 @@ public ExampleBase(string name = "", string tags = "", bool pending = false) } public TimeSpan Duration { get; protected set; } - public string CapturedOutput { get; set; } + public string CapturedOutput; public bool Pending { get; protected set; } public bool HasRun; - public string Spec; - public List Tags; + public string Spec { get; protected set; } + public List Tags { get; protected set; } public Exception Exception; - public Context Context; + public Context Context { get; protected set; } } } \ No newline at end of file From 1571710abe07d9a24466df2ad8c24e32a6f2b3a4 Mon Sep 17 00:00:00 2001 From: GiuseppePiscopo Date: Mon, 22 May 2017 02:21:57 +0200 Subject: [PATCH 22/26] refactor(context): simplify when spec constructor throws --- sln/src/NSpec/Domain/ClassContext.cs | 20 +++++++++---------- sln/src/NSpec/Domain/Context.cs | 6 +++--- sln/src/NSpec/Domain/ContextBuilder.cs | 2 +- .../Domain/Extensions/DomainExtensions.cs | 8 -------- sln/src/NSpec/Domain/MethodContext.cs | 12 +++++------ 5 files changed, 19 insertions(+), 29 deletions(-) diff --git a/sln/src/NSpec/Domain/ClassContext.cs b/sln/src/NSpec/Domain/ClassContext.cs index 406efa6d..611aefdd 100644 --- a/sln/src/NSpec/Domain/ClassContext.cs +++ b/sln/src/NSpec/Domain/ClassContext.cs @@ -59,6 +59,9 @@ void AddFailingExample(Exception targetEx) var failingExample = new Example(exampleName, action: emptyAction) { + // flag the one and only failing example as being run; + // nothing else is needed: no parents, no childs, no before/after hooks + HasRun = true, Exception = new ContextBareCodeException(reportedEx), }; @@ -67,16 +70,9 @@ void AddFailingExample(Exception targetEx) public override void Run(bool failFast, nspec instance = null, bool recurse = true) { - if (cantCreateInstance) - { - // flag the one and only failing example as being run; - // nothing else is needed: no parents, no childs, no before/after hooks - Examples.Single().HasRun = true; - } - else - { - base.Run(failFast, instance, recurse); - } + if (cantCreateInstance) return; + + base.Run(failFast, instance, recurse); } public ClassContext(Type type, Conventions conventions = null, Tags tagsFilter = null, string tags = null) @@ -89,12 +85,14 @@ public ClassContext(Type type, Conventions conventions = null, Tags tagsFilter = this.classHierarchy = (type == typeof(nspec)) ? new List() : new List(type.GetAbstractBaseClassChainWithClass()); + + cantCreateInstance = false; } public Type SpecType; Tags tagsFilter; List classHierarchy; - bool cantCreateInstance = false; + bool cantCreateInstance; } } diff --git a/sln/src/NSpec/Domain/Context.cs b/sln/src/NSpec/Domain/Context.cs index bda9d48c..16bb5dfa 100644 --- a/sln/src/NSpec/Domain/Context.cs +++ b/sln/src/NSpec/Domain/Context.cs @@ -381,8 +381,8 @@ public Context(string name = "", string tags = null, bool isPending = false, Con public Context Parent { get; protected set; } protected List runnables; - - nspec builtInstance; - bool alreadyWritten, isPending; + protected nspec builtInstance; + protected bool alreadyWritten; + protected bool isPending; } } diff --git a/sln/src/NSpec/Domain/ContextBuilder.cs b/sln/src/NSpec/Domain/ContextBuilder.cs index ccf6916a..21278b50 100644 --- a/sln/src/NSpec/Domain/ContextBuilder.cs +++ b/sln/src/NSpec/Domain/ContextBuilder.cs @@ -33,7 +33,7 @@ public ClassContext CreateClassContext(Type type) type.GetAbstractBaseClassChainWithClass() .Where(s => s != type) - .Each(s => tagAttributes.Add(new TagAttribute(s.Name))); + .Do(s => tagAttributes.Add(new TagAttribute(s.Name))); var tags = TagStringFor(tagAttributes); diff --git a/sln/src/NSpec/Domain/Extensions/DomainExtensions.cs b/sln/src/NSpec/Domain/Extensions/DomainExtensions.cs index ca8a9809..2e6a25ff 100644 --- a/sln/src/NSpec/Domain/Extensions/DomainExtensions.cs +++ b/sln/src/NSpec/Domain/Extensions/DomainExtensions.cs @@ -74,14 +74,6 @@ public static string CleanMessage(this Exception exception) return exc; } - public static void Each(this IEnumerable enumerable, Action action) - { - foreach (var t in enumerable) - { - action(t); - } - } - public static bool IsAsync(this MethodInfo method) { // Inspired from: https://github.com/nunit/nunit/blob/master/src/NUnitFramework/framework/Internal/AsyncInvocationRegion.cs diff --git a/sln/src/NSpec/Domain/MethodContext.cs b/sln/src/NSpec/Domain/MethodContext.cs index 9456d89a..5bebd71f 100644 --- a/sln/src/NSpec/Domain/MethodContext.cs +++ b/sln/src/NSpec/Domain/MethodContext.cs @@ -19,12 +19,6 @@ public override void Build(nspec instance) } } - public MethodContext(MethodInfo method, string tags = null) - : base(method.Name, tags) - { - this.method = method; - } - static void AddFailingExample(nspec instance, Exception targetEx) { var reportedEx = (targetEx.InnerException != null) @@ -36,6 +30,12 @@ static void AddFailingExample(nspec instance, Exception targetEx) instance.it[exampleName] = () => { throw new ContextBareCodeException(reportedEx); }; } + public MethodContext(MethodInfo method, string tags = null) + : base(method.Name, tags) + { + this.method = method; + } + MethodInfo method; } } From d5567a8847afa7a95ebad8c7b6fb2627e008af24 Mon Sep 17 00:00:00 2001 From: GiuseppePiscopo Date: Mon, 12 Jun 2017 14:19:15 +0200 Subject: [PATCH 23/26] fix(hooks): mixed sync/async error message lacks hook name --- sln/src/NSpec/Domain/HookChainBase.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sln/src/NSpec/Domain/HookChainBase.cs b/sln/src/NSpec/Domain/HookChainBase.cs index fa712ae4..78839518 100644 --- a/sln/src/NSpec/Domain/HookChainBase.cs +++ b/sln/src/NSpec/Domain/HookChainBase.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Reflection; using System.Threading.Tasks; using NSpec.Domain.Extensions; @@ -13,7 +12,7 @@ public void BuildMethodLevel(List classHierarchy) { var methods = ContextUtils.GetMethodsFromHierarchy(classHierarchy, methodSelector); - if (reversed) + if (reversed) { methods.Reverse(); } @@ -63,7 +62,7 @@ protected void InvokeClassHooks(nspec instance) { throw new AsyncMismatchException( $"A spec class with all its ancestors cannot set both sync and async " + - "class-level '{classHookName}' hooks, they should either be all sync or all async"); + $"class-level '{classHookName}' hooks, they should either be all sync or all async"); } ClassHook.SafeInvoke(instance); @@ -112,7 +111,7 @@ public HookChainBase(Context context, public Action Hook; public Func AsyncHook; - + public Action ClassHook { get; protected set; } public Action AsyncClassHook { get; protected set; } From cb5efe7fa429af4f37a454a0d03a9ef24345ffa2 Mon Sep 17 00:00:00 2001 From: GiuseppePiscopo Date: Mon, 12 Jun 2017 14:21:34 +0200 Subject: [PATCH 24/26] refactor(hooks): invoking hooks in direct/reverse order is easier to read --- sln/src/NSpec/Domain/TraversingHookChain.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/sln/src/NSpec/Domain/TraversingHookChain.cs b/sln/src/NSpec/Domain/TraversingHookChain.cs index 45eb8375..66e9a221 100644 --- a/sln/src/NSpec/Domain/TraversingHookChain.cs +++ b/sln/src/NSpec/Domain/TraversingHookChain.cs @@ -6,17 +6,20 @@ public abstract class TraversingHookChain : HookChainBase { protected override void InvokeHooks(nspec instance) { - // parent chain if (!reversed) { + // parent chain first, then current chain + RecurseAncestors(c => chainSelector(c).InvokeHooks(instance)); + + base.InvokeHooks(instance); } + else + { + // current chain first, then parent chain - base.InvokeHooks(instance); + base.InvokeHooks(instance); - // parent chain, reverse order - if (reversed) - { RecurseAncestors(c => chainSelector(c).InvokeHooks(instance)); } } From 06ba48806e9ef49d23fcbb699045aceddf30edca Mon Sep 17 00:00:00 2001 From: GiuseppePiscopo Date: Mon, 12 Jun 2017 14:22:17 +0200 Subject: [PATCH 25/26] chore(style): clean up unused imports --- sln/src/NSpec/Domain/ActChain.cs | 6 ------ sln/src/NSpec/Domain/AfterAllChain.cs | 4 ---- sln/src/NSpec/Domain/AfterChain.cs | 6 ------ sln/src/NSpec/Domain/AsyncActionRegister.cs | 3 --- sln/src/NSpec/Domain/AsyncMethodLevelHooks.cs | 7 +------ sln/src/NSpec/Domain/AsyncMismatchException.cs | 4 ---- sln/src/NSpec/Domain/BeforeAllChain.cs | 4 ---- sln/src/NSpec/Domain/BeforeChain.cs | 6 ------ sln/src/NSpec/Domain/ClassContext.cs | 1 - sln/src/NSpec/Domain/Context.cs | 11 ++++------- sln/src/NSpec/Domain/ContextUtils.cs | 2 +- sln/src/NSpec/Domain/RunnableExample.cs | 4 ++-- 12 files changed, 8 insertions(+), 50 deletions(-) diff --git a/sln/src/NSpec/Domain/ActChain.cs b/sln/src/NSpec/Domain/ActChain.cs index ee65201c..9458eb1b 100644 --- a/sln/src/NSpec/Domain/ActChain.cs +++ b/sln/src/NSpec/Domain/ActChain.cs @@ -1,9 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Threading.Tasks; -using NSpec.Domain.Extensions; - namespace NSpec.Domain { public class ActChain : TraversingHookChain diff --git a/sln/src/NSpec/Domain/AfterAllChain.cs b/sln/src/NSpec/Domain/AfterAllChain.cs index 312a2dfb..420f9e42 100644 --- a/sln/src/NSpec/Domain/AfterAllChain.cs +++ b/sln/src/NSpec/Domain/AfterAllChain.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Reflection; -using System.Threading.Tasks; -using NSpec.Domain.Extensions; namespace NSpec.Domain { diff --git a/sln/src/NSpec/Domain/AfterChain.cs b/sln/src/NSpec/Domain/AfterChain.cs index e10635f4..4c13234a 100644 --- a/sln/src/NSpec/Domain/AfterChain.cs +++ b/sln/src/NSpec/Domain/AfterChain.cs @@ -1,9 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Threading.Tasks; -using NSpec.Domain.Extensions; - namespace NSpec.Domain { public class AfterChain : TraversingHookChain diff --git a/sln/src/NSpec/Domain/AsyncActionRegister.cs b/sln/src/NSpec/Domain/AsyncActionRegister.cs index f643a584..f5128615 100644 --- a/sln/src/NSpec/Domain/AsyncActionRegister.cs +++ b/sln/src/NSpec/Domain/AsyncActionRegister.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; namespace NSpec.Domain diff --git a/sln/src/NSpec/Domain/AsyncMethodLevelHooks.cs b/sln/src/NSpec/Domain/AsyncMethodLevelHooks.cs index 95a4bd92..7ac51421 100644 --- a/sln/src/NSpec/Domain/AsyncMethodLevelHooks.cs +++ b/sln/src/NSpec/Domain/AsyncMethodLevelHooks.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; +using System.Reflection; namespace NSpec.Domain { diff --git a/sln/src/NSpec/Domain/AsyncMismatchException.cs b/sln/src/NSpec/Domain/AsyncMismatchException.cs index c5fd09c6..8090f8ed 100644 --- a/sln/src/NSpec/Domain/AsyncMismatchException.cs +++ b/sln/src/NSpec/Domain/AsyncMismatchException.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace NSpec.Domain { diff --git a/sln/src/NSpec/Domain/BeforeAllChain.cs b/sln/src/NSpec/Domain/BeforeAllChain.cs index f1879809..424c997e 100644 --- a/sln/src/NSpec/Domain/BeforeAllChain.cs +++ b/sln/src/NSpec/Domain/BeforeAllChain.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Reflection; -using System.Threading.Tasks; -using NSpec.Domain.Extensions; namespace NSpec.Domain { diff --git a/sln/src/NSpec/Domain/BeforeChain.cs b/sln/src/NSpec/Domain/BeforeChain.cs index 6504c559..25970901 100644 --- a/sln/src/NSpec/Domain/BeforeChain.cs +++ b/sln/src/NSpec/Domain/BeforeChain.cs @@ -1,9 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Threading.Tasks; -using NSpec.Domain.Extensions; - namespace NSpec.Domain { public class BeforeChain : TraversingHookChain diff --git a/sln/src/NSpec/Domain/ClassContext.cs b/sln/src/NSpec/Domain/ClassContext.cs index 611aefdd..ad9b54d1 100644 --- a/sln/src/NSpec/Domain/ClassContext.cs +++ b/sln/src/NSpec/Domain/ClassContext.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Reflection; using NSpec.Domain.Extensions; diff --git a/sln/src/NSpec/Domain/Context.cs b/sln/src/NSpec/Domain/Context.cs index 16bb5dfa..0625113a 100644 --- a/sln/src/NSpec/Domain/Context.cs +++ b/sln/src/NSpec/Domain/Context.cs @@ -1,11 +1,8 @@ -using NSpec.Domain.Extensions; -using NSpec.Domain.Formatters; +using NSpec.Domain.Formatters; using System; using System.Collections.Generic; using System.Linq; -using System.Reflection; using System.Threading.Tasks; -using System.Diagnostics; namespace NSpec.Domain { @@ -206,7 +203,7 @@ public override string ToString() return String.Join(",", new [] { - Name, levelText, exampleText, contextText, exceptionText, + Name, levelText, exampleText, contextText, exceptionText, }); } @@ -351,7 +348,7 @@ public Context(string name = "", string tags = null, bool isPending = false, Con Name = name.Replace("_", " "); Tags = Domain.Tags.ParseTags(tags); this.isPending = isPending; - + Examples = new List(); Contexts = new ContextCollection(); @@ -379,7 +376,7 @@ public Context(string name = "", string tags = null, bool isPending = false, Con public bool ClearExpectedException; public string CapturedOutput { get; protected set; } public Context Parent { get; protected set; } - + protected List runnables; protected nspec builtInstance; protected bool alreadyWritten; diff --git a/sln/src/NSpec/Domain/ContextUtils.cs b/sln/src/NSpec/Domain/ContextUtils.cs index 68aa1e07..de6d0745 100644 --- a/sln/src/NSpec/Domain/ContextUtils.cs +++ b/sln/src/NSpec/Domain/ContextUtils.cs @@ -15,7 +15,7 @@ public static List GetMethodsFromHierarchy( .Where(m => m != null) .ToList(); } - + public static bool RunAndHandleException(Action action, nspec instance, ref Exception targetException) { bool hasThrown = false; diff --git a/sln/src/NSpec/Domain/RunnableExample.cs b/sln/src/NSpec/Domain/RunnableExample.cs index 9bbfc436..c6754a01 100644 --- a/sln/src/NSpec/Domain/RunnableExample.cs +++ b/sln/src/NSpec/Domain/RunnableExample.cs @@ -65,11 +65,11 @@ public void AssignException(Exception beforeAllException, Exception afterAllExce beforeAllException ?? context.BeforeChain.AnyException() ?? unexpectedException; - + Exception followingException = context.AfterChain.AnyException() ?? afterAllException; - + if (previousException == null && followingException == null) { // stick with whatever exception may or may not be set on this example From ffee4447e78d2dff238008ad7a90ad2e42d95bbb Mon Sep 17 00:00:00 2001 From: GiuseppePiscopo Date: Mon, 12 Jun 2017 14:29:54 +0200 Subject: [PATCH 26/26] refactor(hooks): simplify building sync/async method level hooks --- sln/src/NSpec/Domain/HookChainBase.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/sln/src/NSpec/Domain/HookChainBase.cs b/sln/src/NSpec/Domain/HookChainBase.cs index 78839518..2332a68f 100644 --- a/sln/src/NSpec/Domain/HookChainBase.cs +++ b/sln/src/NSpec/Domain/HookChainBase.cs @@ -12,9 +12,12 @@ public void BuildMethodLevel(List classHierarchy) { var methods = ContextUtils.GetMethodsFromHierarchy(classHierarchy, methodSelector); + var asyncMethods = ContextUtils.GetMethodsFromHierarchy(classHierarchy, asyncMethodSelector); + if (reversed) { methods.Reverse(); + asyncMethods.Reverse(); } if (methods.Count > 0) @@ -22,13 +25,6 @@ public void BuildMethodLevel(List classHierarchy) ClassHook = instance => methods.Do(m => m.Invoke(instance, null)); } - var asyncMethods = ContextUtils.GetMethodsFromHierarchy(classHierarchy, asyncMethodSelector); - - if (reversed) - { - asyncMethods.Reverse(); - } - if (asyncMethods.Count > 0) { AsyncClassHook = instance => asyncMethods.Do(m => new AsyncMethodLevelBefore(m).Run(instance));