From 42527dcd9c67141f7fdb413e7f2a09070b6a3c1a Mon Sep 17 00:00:00 2001 From: Gary Ewan Park Date: Wed, 20 Jul 2016 21:06:36 +0100 Subject: [PATCH] (GH-1089) Add additional context information to Teardown --- src/Cake.Core.Tests/Cake.Core.Tests.csproj | 1 + .../Unit/Scripting/ScriptHostTests.cs | 4 +- .../Unit/TaskSetupContextTests.cs | 8 +- .../Unit/TaskTeardownContextTests.cs | 9 +- .../Unit/TeardownContextTests.cs | 15 +++ src/Cake.Core/Cake.Core.csproj | 3 + src/Cake.Core/CakeContextAdapter.cs | 105 ++++++++++++++++++ src/Cake.Core/CakeEngine.cs | 27 +++-- src/Cake.Core/DefaultExecutionStrategy.cs | 32 +++--- src/Cake.Core/ICakeEngine.cs | 4 +- src/Cake.Core/IExecutionStrategy.cs | 7 +- src/Cake.Core/ITaskSetupContext.cs | 2 +- src/Cake.Core/ITaskTeardownContext.cs | 2 +- src/Cake.Core/ITeardownContext.cs | 27 +++++ src/Cake.Core/Scripting/IScriptHost.cs | 16 +++ src/Cake.Core/Scripting/ScriptHost.cs | 30 +++++ src/Cake.Core/TaskSetupContext.cs | 6 +- src/Cake.Core/TaskTeardownContext.cs | 6 +- src/Cake.Core/TeardownContext.cs | 51 +++++++++ 19 files changed, 311 insertions(+), 44 deletions(-) create mode 100644 src/Cake.Core.Tests/Unit/TeardownContextTests.cs create mode 100644 src/Cake.Core/CakeContextAdapter.cs create mode 100644 src/Cake.Core/ITeardownContext.cs create mode 100644 src/Cake.Core/TeardownContext.cs diff --git a/src/Cake.Core.Tests/Cake.Core.Tests.csproj b/src/Cake.Core.Tests/Cake.Core.Tests.csproj index bb44a07aeb..98ae50dbea 100644 --- a/src/Cake.Core.Tests/Cake.Core.Tests.csproj +++ b/src/Cake.Core.Tests/Cake.Core.Tests.csproj @@ -129,6 +129,7 @@ + diff --git a/src/Cake.Core.Tests/Unit/Scripting/ScriptHostTests.cs b/src/Cake.Core.Tests/Unit/Scripting/ScriptHostTests.cs index 2668d0fcca..8ba8885da1 100644 --- a/src/Cake.Core.Tests/Unit/Scripting/ScriptHostTests.cs +++ b/src/Cake.Core.Tests/Unit/Scripting/ScriptHostTests.cs @@ -68,7 +68,7 @@ public void Should_Proxy_Call_To_Engine() // Given var fixture = new ScriptHostFixture(); var host = fixture.CreateHost(); - Action action = (context, setupContext) => { }; + Action action = context => { }; // When host.TaskSetup(action); @@ -86,7 +86,7 @@ public void Should_Proxy_Call_To_Engine() // Given var fixture = new ScriptHostFixture(); var host = fixture.CreateHost(); - Action action = (context, setupContext) => { }; + Action action = context => { }; // When host.TaskTeardown(action); diff --git a/src/Cake.Core.Tests/Unit/TaskSetupContextTests.cs b/src/Cake.Core.Tests/Unit/TaskSetupContextTests.cs index b3eac95ec6..83bc0acc5e 100644 --- a/src/Cake.Core.Tests/Unit/TaskSetupContextTests.cs +++ b/src/Cake.Core.Tests/Unit/TaskSetupContextTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; +using NSubstitute; using Xunit; namespace Cake.Core.Tests.Unit @@ -13,8 +14,11 @@ public sealed class TheConstructor [Fact] public void Should_Throw_If_Task_Is_Null() { - // Given, When - var result = Record.Exception(() => new TaskTeardownContext(null,TimeSpan.Zero, false)); + // Given + var context = Substitute.For(); + + // When + var result = Record.Exception(() => new TaskTeardownContext(context, null, TimeSpan.Zero, false)); // Then Assert.IsArgumentNullException(result, "task"); diff --git a/src/Cake.Core.Tests/Unit/TaskTeardownContextTests.cs b/src/Cake.Core.Tests/Unit/TaskTeardownContextTests.cs index 60b5e495f3..dd7acb2f52 100644 --- a/src/Cake.Core.Tests/Unit/TaskTeardownContextTests.cs +++ b/src/Cake.Core.Tests/Unit/TaskTeardownContextTests.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. + +using NSubstitute; using Xunit; namespace Cake.Core.Tests.Unit @@ -12,8 +14,11 @@ public sealed class TheConstructor [Fact] public void Should_Throw_If_Task_Is_Null() { - // Given, When - var result = Record.Exception(() => new TaskSetupContext(null)); + // Given + var context = Substitute.For(); + + // When + var result = Record.Exception(() => new TaskSetupContext(context, null)); // Then Assert.IsArgumentNullException(result, "task"); diff --git a/src/Cake.Core.Tests/Unit/TeardownContextTests.cs b/src/Cake.Core.Tests/Unit/TeardownContextTests.cs new file mode 100644 index 0000000000..5c835e13c8 --- /dev/null +++ b/src/Cake.Core.Tests/Unit/TeardownContextTests.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +using Xunit; + +namespace Cake.Core.Tests.Unit +{ + public sealed class TeardownContextTests + { + public sealed class TheConstructor + { + // TODO: Put tests here + } + } +} \ No newline at end of file diff --git a/src/Cake.Core/Cake.Core.csproj b/src/Cake.Core/Cake.Core.csproj index b7275b9543..9a6fa9388a 100644 --- a/src/Cake.Core/Cake.Core.csproj +++ b/src/Cake.Core/Cake.Core.csproj @@ -56,6 +56,7 @@ + @@ -91,6 +92,7 @@ + @@ -221,6 +223,7 @@ + diff --git a/src/Cake.Core/CakeContextAdapter.cs b/src/Cake.Core/CakeContextAdapter.cs new file mode 100644 index 0000000000..616988f6ac --- /dev/null +++ b/src/Cake.Core/CakeContextAdapter.cs @@ -0,0 +1,105 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Cake.Core.Diagnostics; +using Cake.Core.IO; +using Cake.Core.Tooling; + +namespace Cake.Core +{ + public abstract class CakeContextAdapter + { + private readonly ICakeContext _cakeContext; + + public CakeContextAdapter(ICakeContext cakeContext) + { + _cakeContext = cakeContext; + } + + /// + /// Gets the file system. + /// + /// + /// The file system. + /// + public IFileSystem FileSystem + { + get { return _cakeContext.FileSystem; } + } + + /// + /// Gets the environment. + /// + /// + /// The environment. + /// + public ICakeEnvironment Environment + { + get { return _cakeContext.Environment; } + } + + /// + /// Gets the globber. + /// + /// + /// The globber. + /// + public IGlobber Globber + { + get { return _cakeContext.Globber; } + } + + /// + /// Gets the log. + /// + /// + /// The log. + /// + public ICakeLog Log { get { return _cakeContext.Log; } } + + /// + /// Gets the arguments. + /// + /// + /// The arguments. + /// + public ICakeArguments Arguments + { + get { return _cakeContext.Arguments; } + } + + /// + /// Gets the process runner. + /// + /// + /// The process runner. + /// + public IProcessRunner ProcessRunner + { + get { return _cakeContext.ProcessRunner; } + } + + /// + /// Gets the registry. + /// + /// + /// The registry. + /// + public IRegistry Registry + { + get { return _cakeContext.Registry; } + } + + /// + /// Gets the tool locator. + /// + /// + /// The tool locator. + /// + public IToolLocator Tools + { + get { return _cakeContext.Tools; } + } + } +} \ No newline at end of file diff --git a/src/Cake.Core/CakeEngine.cs b/src/Cake.Core/CakeEngine.cs index ce36a09765..0fdc5f3823 100644 --- a/src/Cake.Core/CakeEngine.cs +++ b/src/Cake.Core/CakeEngine.cs @@ -20,8 +20,8 @@ public sealed class CakeEngine : ICakeEngine private readonly List _tasks; private Action _setupAction; private Action _teardownAction; - private Action _taskSetupAction; - private Action _taskTeardownAction; + private Action _taskSetupAction; + private Action _taskTeardownAction; /// /// Gets all registered tasks. @@ -116,6 +116,7 @@ public CakeReport RunTarget(ICakeContext context, IExecutionStrategy strategy, s // while running a setup action, or a task. We do this since we don't // want to throw teardown exceptions if an exception was thrown previously. var exceptionWasThrown = false; + Exception thrownException = null; try { @@ -146,14 +147,15 @@ public CakeReport RunTarget(ICakeContext context, IExecutionStrategy strategy, s return report; } - catch + catch (Exception ex) { exceptionWasThrown = true; + thrownException = ex; throw; } finally { - PerformTeardown(strategy, context, exceptionWasThrown); + PerformTeardown(strategy, context, exceptionWasThrown, thrownException); } } @@ -162,7 +164,7 @@ public CakeReport RunTarget(ICakeContext context, IExecutionStrategy strategy, s /// If the task setup fails, the task will not be executed but the task's teardown will be performed. /// /// The action to be executed. - public void RegisterTaskSetupAction(Action action) + public void RegisterTaskSetupAction(Action action) { _taskSetupAction = action; } @@ -172,7 +174,7 @@ public void RegisterTaskSetupAction(Action acti /// If a task setup action or a task fails with or without recovery, the specified task teardown action will still be executed. /// /// The action to be executed. - public void RegisterTaskTeardownAction(Action action) + public void RegisterTaskTeardownAction(Action action) { _taskTeardownAction = action; } @@ -271,8 +273,8 @@ private void PerformTaskSetup(ICakeContext context, IExecutionStrategy strategy, { try { - var taskSetupContext = new TaskSetupContext(task); - strategy.PerformTaskSetup(_taskSetupAction, context, taskSetupContext); + var taskSetupContext = new TaskSetupContext(context, task); + strategy.PerformTaskSetup(_taskSetupAction, context); } catch { @@ -286,10 +288,10 @@ private void PerformTaskTeardown(ICakeContext context, IExecutionStrategy strate { if (_taskTeardownAction != null) { - var taskTeardownContext = new TaskTeardownContext(task, duration, skipped); + var taskTeardownContext = new TaskTeardownContext(context, task, duration, skipped); try { - strategy.PerformTaskTeardown(_taskTeardownAction, context, taskTeardownContext); + strategy.PerformTaskTeardown(_taskTeardownAction, context); } catch (Exception ex) { @@ -350,13 +352,14 @@ private void HandleErrors(IExecutionStrategy strategy, Action errorHa } } - private void PerformTeardown(IExecutionStrategy strategy, ICakeContext context, bool exceptionWasThrown) + private void PerformTeardown(IExecutionStrategy strategy, ICakeContext context, bool exceptionWasThrown, Exception thrownException) { if (_teardownAction != null) { try { - strategy.PerformTeardown(_teardownAction, context); + var teardownContext = new TeardownContext(context, !exceptionWasThrown, thrownException); + strategy.PerformTeardown(_teardownAction, teardownContext); } catch (Exception ex) { diff --git a/src/Cake.Core/DefaultExecutionStrategy.cs b/src/Cake.Core/DefaultExecutionStrategy.cs index dfe2ca66a6..bf5c3c31fc 100644 --- a/src/Cake.Core/DefaultExecutionStrategy.cs +++ b/src/Cake.Core/DefaultExecutionStrategy.cs @@ -140,17 +140,19 @@ public void InvokeFinally(Action action) /// /// The action. /// The context. - /// The setup context. - public void PerformTaskSetup(Action action, ICakeContext context, ITaskSetupContext setupContext) + public void PerformTaskSetup(Action action, ICakeContext context) { - if (setupContext == null) - { - throw new ArgumentNullException("setupContext"); - } if (action != null) { + var setupContext = context as ITaskSetupContext; + + if (setupContext == null) + { + throw new ArgumentNullException("setupContext"); + } + _log.Debug("Executing custom task setup action ({0})...", setupContext.Task.Name); - action(context, setupContext); + action(context); } } @@ -159,17 +161,19 @@ public void PerformTaskSetup(Action action, ICa /// /// The action. /// The context. - /// The teardown context. - public void PerformTaskTeardown(Action action, ICakeContext context, ITaskTeardownContext teardownContext) + public void PerformTaskTeardown(Action action, ICakeContext context) { - if (teardownContext == null) - { - throw new ArgumentNullException("teardownContext"); - } if (action != null) { + var teardownContext = context as ITaskTeardownContext; + + if (teardownContext == null) + { + throw new ArgumentNullException("context"); + } + _log.Debug("Executing custom task teardown action ({0})...", teardownContext.Task.Name); - action(context, teardownContext); + action(context); } } } diff --git a/src/Cake.Core/ICakeEngine.cs b/src/Cake.Core/ICakeEngine.cs index 075107579a..e9a9c97a43 100644 --- a/src/Cake.Core/ICakeEngine.cs +++ b/src/Cake.Core/ICakeEngine.cs @@ -52,13 +52,13 @@ public interface ICakeEngine /// If the task setup fails, the task will not be executed but the task's teardown will be performed. /// /// The action to be executed. - void RegisterTaskSetupAction(Action action); + void RegisterTaskSetupAction(Action action); /// /// Allows registration of an action that's executed after each task has been run. /// If a task setup action or a task fails with or without recovery, the specified task teardown action will still be executed. /// /// The action to be executed. - void RegisterTaskTeardownAction(Action action); + void RegisterTaskTeardownAction(Action action); } } diff --git a/src/Cake.Core/IExecutionStrategy.cs b/src/Cake.Core/IExecutionStrategy.cs index 70e21677dc..a4a61a6a8c 100644 --- a/src/Cake.Core/IExecutionStrategy.cs +++ b/src/Cake.Core/IExecutionStrategy.cs @@ -22,6 +22,7 @@ public interface IExecutionStrategy /// /// The action. /// The context. + /// The build context. void PerformTeardown(Action action, ICakeContext context); /// @@ -62,15 +63,13 @@ public interface IExecutionStrategy /// /// The action. /// The context. - /// The setup context. - void PerformTaskSetup(Action action, ICakeContext context, ITaskSetupContext setupContext); + void PerformTaskSetup(Action action, ICakeContext context); /// /// Performs the specified teardown action after each task is invoked. /// /// The action. /// The context. - /// The teardown context. - void PerformTaskTeardown(Action action, ICakeContext context, ITaskTeardownContext teardownContext); + void PerformTaskTeardown(Action action, ICakeContext context); } } diff --git a/src/Cake.Core/ITaskSetupContext.cs b/src/Cake.Core/ITaskSetupContext.cs index 7eb1f18ca8..bfd40a41ac 100644 --- a/src/Cake.Core/ITaskSetupContext.cs +++ b/src/Cake.Core/ITaskSetupContext.cs @@ -6,7 +6,7 @@ namespace Cake.Core /// /// Acts as a context providing info about a before its invocation. /// - public interface ITaskSetupContext + public interface ITaskSetupContext : ICakeContext { /// /// Gets the describing the that has just been invoked. diff --git a/src/Cake.Core/ITaskTeardownContext.cs b/src/Cake.Core/ITaskTeardownContext.cs index 7e143b52de..1ae81bbb32 100644 --- a/src/Cake.Core/ITaskTeardownContext.cs +++ b/src/Cake.Core/ITaskTeardownContext.cs @@ -8,7 +8,7 @@ namespace Cake.Core /// /// Acts as a context providing info about a following its invocation. /// - public interface ITaskTeardownContext + public interface ITaskTeardownContext : ICakeContext { /// /// Gets the describing the that has just been invoked. diff --git a/src/Cake.Core/ITeardownContext.cs b/src/Cake.Core/ITeardownContext.cs new file mode 100644 index 0000000000..189aa16d42 --- /dev/null +++ b/src/Cake.Core/ITeardownContext.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace Cake.Core +{ + /// + /// Acts as a context providing info about the overall build following its completion + /// + public interface ITeardownContext : ICakeContext + { + /// + /// Gets a value indicating whether this build was successful + /// + /// + /// true if successful; otherwise false. + /// + bool Successful { get; } + + /// + /// Gets the exception that was thrown by the target. + /// + Exception ThrownException { get; } + } +} \ No newline at end of file diff --git a/src/Cake.Core/Scripting/IScriptHost.cs b/src/Cake.Core/Scripting/IScriptHost.cs index 97e957ed01..72e3ddd101 100644 --- a/src/Cake.Core/Scripting/IScriptHost.cs +++ b/src/Cake.Core/Scripting/IScriptHost.cs @@ -65,15 +65,31 @@ public interface IScriptHost /// If the task setup fails, its task will not be executed but the task teardown will be performed. /// /// The action to be executed. + [Obsolete("Please use TaskSetup(Action) instead.", false)] void TaskSetup(Action action); + /// + /// Allows registration of an action that's executed before each task is run. + /// If the task setup fails, its task will not be executed but the task teardown will be performed. + /// + /// The action to be executed. + void TaskSetup(Action action); + /// /// Allows registration of an action that's executed after each task has been run. /// If a task setup action or a task fails with or without recovery, the specified task teardown action will still be executed. /// /// The action to be executed. + [Obsolete("Please use TaskTeardown(Action) instead.", false)] void TaskTeardown(Action action); + /// + /// Allows registration of an action that's executed after each task has been run. + /// If a task setup action or a task fails with or without recovery, the specified task teardown action will still be executed. + /// + /// The action to be executed. + void TaskTeardown(Action action); + /// /// Runs the specified target. /// diff --git a/src/Cake.Core/Scripting/ScriptHost.cs b/src/Cake.Core/Scripting/ScriptHost.cs index c1ec47829b..80283b79da 100644 --- a/src/Cake.Core/Scripting/ScriptHost.cs +++ b/src/Cake.Core/Scripting/ScriptHost.cs @@ -140,7 +140,22 @@ public void Teardown(Action action) /// If the task setup fails, its task will not be executed but the task teardown will be performed. /// /// The action to be executed. + [Obsolete("Please use TaskSetup(Action) instead.", false)] public void TaskSetup(Action action) + { + if (Context != null && Context.Log != null) + { + Context.Log.Warning("Please use TaskSetup(Action) instead."); + } + TaskSetup(context => action()); + } + + /// + /// Allows registration of an action that's executed before each task is run. + /// If the task setup fails, its task will not be executed but the task teardown will be performed. + /// + /// The action to be executed. + public void TaskSetup(Action action) { _engine.RegisterTaskSetupAction(action); } @@ -150,7 +165,22 @@ public void TaskSetup(Action action) /// If a task setup action or a task fails with or without recovery, the specified task teardown action will still be executed. /// /// The action to be executed. + [Obsolete("Please use TaskTeardown(Action) instead.", false)] public void TaskTeardown(Action action) + { + if (Context != null && Context.Log != null) + { + Context.Log.Warning("Please use TaskTeardown(Action) instead."); + } + TaskTeardown(context => action()); + } + + /// + /// Allows registration of an action that's executed after each task has been run. + /// If a task setup action or a task fails with or without recovery, the specified task teardown action will still be executed. + /// + /// The action to be executed. + public void TaskTeardown(Action action) { _engine.RegisterTaskTeardownAction(action); } diff --git a/src/Cake.Core/TaskSetupContext.cs b/src/Cake.Core/TaskSetupContext.cs index 7bec2e2798..eb243b84eb 100644 --- a/src/Cake.Core/TaskSetupContext.cs +++ b/src/Cake.Core/TaskSetupContext.cs @@ -8,15 +8,17 @@ namespace Cake.Core /// /// Acts as a context providing info about a before its invocation. /// - public sealed class TaskSetupContext : ITaskSetupContext + public sealed class TaskSetupContext : CakeContextAdapter, ITaskSetupContext { private readonly ICakeTaskInfo _task; /// /// Initializes a new instance of the class. /// + /// The Cake Context. /// The task. - public TaskSetupContext(ICakeTaskInfo task) + public TaskSetupContext(ICakeContext cakeContext, ICakeTaskInfo task) + : base(cakeContext) { if (task == null) { diff --git a/src/Cake.Core/TaskTeardownContext.cs b/src/Cake.Core/TaskTeardownContext.cs index b4f94c36ff..c4f606b886 100644 --- a/src/Cake.Core/TaskTeardownContext.cs +++ b/src/Cake.Core/TaskTeardownContext.cs @@ -8,7 +8,7 @@ namespace Cake.Core /// /// Acts as a context providing info about a following its invocation. /// - public sealed class TaskTeardownContext : ITaskTeardownContext + public sealed class TaskTeardownContext : CakeContextAdapter, ITaskTeardownContext { private readonly ICakeTaskInfo _task; private readonly TimeSpan _duration; @@ -17,10 +17,12 @@ public sealed class TaskTeardownContext : ITaskTeardownContext /// /// Initializes a new instance of the class. /// + /// The Cake Context. /// The task. /// The duration of the task. /// if set to true, the task was not executed. - public TaskTeardownContext(ICakeTaskInfo task, TimeSpan duration, bool skipped) + public TaskTeardownContext(ICakeContext cakeContext, ICakeTaskInfo task, TimeSpan duration, bool skipped) + : base(cakeContext) { if (task == null) { diff --git a/src/Cake.Core/TeardownContext.cs b/src/Cake.Core/TeardownContext.cs new file mode 100644 index 0000000000..f79d8aaa1a --- /dev/null +++ b/src/Cake.Core/TeardownContext.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +using System; +using Cake.Core.Diagnostics; +using Cake.Core.IO; +using Cake.Core.Tooling; + +namespace Cake.Core +{ + /// + /// Acts as a context providing info about the overall build following its completion + /// + public sealed class TeardownContext : CakeContextAdapter, ITeardownContext + { + private readonly bool _successful; + private readonly Exception _thrownException; + + /// + /// Initializes a new instance of the class. + /// + /// The Cake Context + /// If set to true, the build was not successful. + /// The exception that was thrown by the target. + public TeardownContext(ICakeContext cakeContext, bool successful, Exception throwException) + : base(cakeContext) + { + _successful = successful; + _thrownException = throwException; + } + + /// + /// Gets a value indicating whether this build was successful + /// + /// + /// true if successful; otherwise false. + /// + public bool Successful + { + get { return _successful; } + } + + /// + /// Gets the exception that was thrown by the target. + /// + public Exception ThrownException + { + get { return _thrownException; } + } + } +} \ No newline at end of file