Index: src/Common/DelftTools.Shell.Core/Workflow/Activity.cs =================================================================== diff -u -rd505e5545e25721aee2dfb60bc2a27ab1878a0ad -rcedd555c4bb61b019885230ca6bf99165187eea7 --- src/Common/DelftTools.Shell.Core/Workflow/Activity.cs (.../Activity.cs) (revision d505e5545e25721aee2dfb60bc2a27ab1878a0ad) +++ src/Common/DelftTools.Shell.Core/Workflow/Activity.cs (.../Activity.cs) (revision cedd555c4bb61b019885230ca6bf99165187eea7) @@ -11,6 +11,11 @@ private string progressText; private ActivityStatus status; + protected Activity() + { + DependsOn = new EventedList(); + } + public virtual string Name { get; set; } public virtual IEventedList DependsOn { get; set; } @@ -38,11 +43,6 @@ get { return progressText; } } - protected Activity() - { - DependsOn = new EventedList(); - } - public virtual void Initialize() { ChangeState(OnInitialize, ActivityStatus.Initializing, ActivityStatus.Initialized); @@ -85,7 +85,6 @@ Status = ActivityStatus.Executed; } - public virtual void Cancel() { ChangeState(OnCancel, ActivityStatus.Cancelling, ActivityStatus.Cancelled); Index: src/Common/DelftTools.Shell.Core/Workflow/IActivity.cs =================================================================== diff -u -r8f6ae890fed8e8eae3a32f9c0498a10f82e0ddf9 -rcedd555c4bb61b019885230ca6bf99165187eea7 --- src/Common/DelftTools.Shell.Core/Workflow/IActivity.cs (.../IActivity.cs) (revision 8f6ae890fed8e8eae3a32f9c0498a10f82e0ddf9) +++ src/Common/DelftTools.Shell.Core/Workflow/IActivity.cs (.../IActivity.cs) (revision cedd555c4bb61b019885230ca6bf99165187eea7) @@ -15,24 +15,22 @@ /// /// Returns current status of the activity (executing, cancelling, etc.) - /// uit + /// ActivityStatus Status { get; } /// - /// Text to describe the current progress of the activity. Most often a percentage + /// Text to describe the current progress of the activity. Most often a percentage. /// - /// string ProgressText { get; } /// - /// Initializes activity, it initialization step is successful - activity status will change to Initialized. + /// Initializes activity. If initialization step is successful, will change to . /// void Initialize(); /// /// Executes activity. Depending on status of the activity execution may need to be repeated. /// - /// void Execute(); /// @@ -53,12 +51,12 @@ void Cleanup(); /// - /// Event to be fired when we want to publish changes in progress. + /// Event to be fired when we want to publish changes in . /// event EventHandler ProgressChanged; /// - /// Event to be fired on every status change. + /// Event to be fired on every change. /// event EventHandler StatusChanged; } Index: src/Common/DelftTools.Utils/INameable.cs =================================================================== diff -u -r8f6ae890fed8e8eae3a32f9c0498a10f82e0ddf9 -rcedd555c4bb61b019885230ca6bf99165187eea7 --- src/Common/DelftTools.Utils/INameable.cs (.../INameable.cs) (revision 8f6ae890fed8e8eae3a32f9c0498a10f82e0ddf9) +++ src/Common/DelftTools.Utils/INameable.cs (.../INameable.cs) (revision cedd555c4bb61b019885230ca6bf99165187eea7) @@ -5,6 +5,9 @@ /// public interface INameable { + /// + /// Gets or sets the name of this object. + /// string Name { get; set; } } } Index: test/Common/DelftTools.Tests/DelftTools.Tests.csproj =================================================================== diff -u -rd6ebde6f276f860d87f3b98524d6e84bc070f70e -rcedd555c4bb61b019885230ca6bf99165187eea7 --- test/Common/DelftTools.Tests/DelftTools.Tests.csproj (.../DelftTools.Tests.csproj) (revision d6ebde6f276f860d87f3b98524d6e84bc070f70e) +++ test/Common/DelftTools.Tests/DelftTools.Tests.csproj (.../DelftTools.Tests.csproj) (revision cedd555c4bb61b019885230ca6bf99165187eea7) @@ -80,6 +80,7 @@ ..\..\..\lib\SharpTestsEx.dll + 3.5 @@ -134,6 +135,7 @@ + Index: test/Common/DelftTools.Tests/Shell/Core/WorkFlow/ActivityTest.cs =================================================================== diff -u --- test/Common/DelftTools.Tests/Shell/Core/WorkFlow/ActivityTest.cs (revision 0) +++ test/Common/DelftTools.Tests/Shell/Core/WorkFlow/ActivityTest.cs (revision cedd555c4bb61b019885230ca6bf99165187eea7) @@ -0,0 +1,379 @@ +using System; + +using DelftTools.Shell.Core.Workflow; +using DelftTools.TestUtils; + +using NUnit.Framework; + +namespace DelftTools.Tests.Shell.Core.WorkFlow +{ + [TestFixture] + public class ActivityTest + { + [Test] + public void DefaultConstructor_ExpectedValues() + { + // call + var activity = new SimpleActivity(); + + // assert + Assert.IsInstanceOf(activity); + Assert.IsNull(activity.Name); + CollectionAssert.IsEmpty(activity.DependsOn); + Assert.AreEqual(ActivityStatus.None, activity.Status); + Assert.IsNull(activity.ProgressText); + } + + [Test] + public void Name_SetAndGetValue_ReturnSetValue() + { + // setup & call + const string someName = "Some name"; + var activity = new SimpleActivity { Name = someName }; + + // assert + Assert.AreEqual(someName, activity.Name); + } + + [Test] + public void Status_SetNewValue_ChangeStatusAndBroadcastEvent() + { + // setup + int callCount = 0; + const ActivityStatus newStatus = ActivityStatus.Done; + + var activity = new SimpleActivity(); + ActivityStatus originalStatus = activity.Status; + activity.StatusChanged += (sender, args) => + { + callCount++; + Assert.AreEqual(originalStatus, args.OldStatus); + Assert.AreEqual(newStatus, args.NewStatus); + Assert.AreSame(activity, sender); + }; + + // call + activity.SetStatus(newStatus); + + // assert + Assert.AreEqual(newStatus, activity.Status); + Assert.AreEqual(1, callCount); + } + + [Test] + public void Status_SetSameValue_DoNothing() + { + // setup + int callCount = 0; + + var activity = new SimpleActivity(); + ActivityStatus originalStatus = activity.Status; + activity.StatusChanged += (sender, args) => + { + callCount++; + }; + + // call + activity.SetStatus(originalStatus); + + // assert + Assert.AreEqual(originalStatus, activity.Status); + Assert.AreEqual(0, callCount); + } + + [Test] + public void Initialize_ActivityNotYetInitialized_UpdateStatusAndPerformInitialization() + { + // setup + int statusChangeCount = 0; + int onInitializeCallCount = 0; + + var activity = new SimpleActivity + { + OnInitializeInjection = () => onInitializeCallCount++ + }; + ActivityStatus originalStatus = activity.Status; + activity.StatusChanged += (sender, args) => + { + if (statusChangeCount == 0) + { + // 1st transition + Assert.AreEqual(originalStatus, args.OldStatus); + Assert.AreEqual(ActivityStatus.Initializing, args.NewStatus); + } + else if (statusChangeCount == 1) + { + // 2nd transition + Assert.AreEqual(ActivityStatus.Initializing, args.OldStatus); + Assert.AreEqual(ActivityStatus.Initialized, args.NewStatus); + } + else + { + Assert.Fail("Expect 2 status updates."); + } + Assert.AreSame(activity, sender); + + statusChangeCount++; + }; + + // call + activity.Initialize(); + + // assert + Assert.AreEqual(2, statusChangeCount); + Assert.AreEqual(1, onInitializeCallCount); + } + + [Test] + public void Initialize_ActivityNotYetInitializedAndInitalizationCausesException_CatchExceptionAndUpdateStatusToFailedAndLogException() + { + // setup + int statusChangeCount = 0; + const string someErrorMessage = "Some error message"; + + var activity = new SimpleActivity + { + OnInitializeInjection = () => { throw new Exception(someErrorMessage); } + }; + ActivityStatus originalStatus = activity.Status; + activity.StatusChanged += (sender, args) => + { + if (statusChangeCount == 0) + { + // 1st transition + Assert.AreEqual(originalStatus, args.OldStatus); + Assert.AreEqual(ActivityStatus.Initializing, args.NewStatus); + } + else if (statusChangeCount == 1) + { + // 2nd transition + Assert.AreEqual(ActivityStatus.Initializing, args.OldStatus); + Assert.AreEqual(ActivityStatus.Failed, args.NewStatus); + } + else + { + Assert.Fail("Expect 2 status updates."); + } + Assert.AreSame(activity, sender); + + statusChangeCount++; + }; + + // call + Action call = () => activity.Initialize(); + + // assert + TestHelper.AssertLogMessageIsGenerated(call, someErrorMessage, 1); + Assert.AreEqual(2, statusChangeCount); + } + + [Test] + public void Initialize_ActivityNotYetInitializedAndInitializationEncountersError_UpdateStatusAndEarlyExit() + { + // setup + int statusChangeCount = 0; + + var activity = new SimpleActivity(); + + activity.OnInitializeInjection = () => activity.SetStatus(ActivityStatus.Failed); + ActivityStatus originalStatus = activity.Status; + + activity.StatusChanged += (sender, args) => + { + if (statusChangeCount == 0) + { + // 1st transition + Assert.AreEqual(originalStatus, args.OldStatus); + Assert.AreEqual(ActivityStatus.Initializing, args.NewStatus); + } + else if (statusChangeCount == 1) + { + // 2nd transition + Assert.AreEqual(ActivityStatus.Initializing, args.OldStatus); + Assert.AreEqual(ActivityStatus.Failed, args.NewStatus); + } + else + { + Assert.Fail("Expect 2 status updates."); + } + Assert.AreSame(activity, sender); + + statusChangeCount++; + }; + + // call + activity.Initialize(); + + // assert + Assert.AreEqual(2, statusChangeCount); + } + + [Test] + public void Cancel_ActivityNotYetCancelled_UpdateStatusAndPerformCancel() + { + // setup + int statusChangeCount = 0; + int onCancelCallCount = 0; + + var activity = new SimpleActivity + { + OnCancelInjection = () => onCancelCallCount++ + }; + ActivityStatus originalStatus = activity.Status; + activity.StatusChanged += (sender, args) => + { + if (statusChangeCount == 0) + { + // 1st transition + Assert.AreEqual(originalStatus, args.OldStatus); + Assert.AreEqual(ActivityStatus.Cancelling, args.NewStatus); + } + else if (statusChangeCount == 1) + { + // 2nd transition + Assert.AreEqual(ActivityStatus.Cancelling, args.OldStatus); + Assert.AreEqual(ActivityStatus.Cancelled, args.NewStatus); + } + else + { + Assert.Fail("Expect 2 status updates."); + } + Assert.AreSame(activity, sender); + + statusChangeCount++; + }; + + // call + activity.Cancel(); + + // assert + Assert.AreEqual(2, statusChangeCount); + Assert.AreEqual(1, onCancelCallCount); + } + + [Test] + public void Cancel_ActivityNotYetCancelledAndCancellingCausesException_CatchExceptionAndUpdateStatusToFailedAndLogException() + { + // setup + int statusChangeCount = 0; + const string someErrorMessage = "Some error message"; + + var activity = new SimpleActivity + { + OnCancelInjection = () => { throw new Exception(someErrorMessage); } + }; + ActivityStatus originalStatus = activity.Status; + activity.StatusChanged += (sender, args) => + { + if (statusChangeCount == 0) + { + // 1st transition + Assert.AreEqual(originalStatus, args.OldStatus); + Assert.AreEqual(ActivityStatus.Cancelling, args.NewStatus); + } + else if (statusChangeCount == 1) + { + // 2nd transition + Assert.AreEqual(ActivityStatus.Cancelling, args.OldStatus); + Assert.AreEqual(ActivityStatus.Failed, args.NewStatus); + } + else + { + Assert.Fail("Expect 2 status updates."); + } + Assert.AreSame(activity, sender); + + statusChangeCount++; + }; + + // call + Action call = () => activity.Cancel(); + + // assert + TestHelper.AssertLogMessageIsGenerated(call, someErrorMessage, 1); + Assert.AreEqual(2, statusChangeCount); + } + + [Test] + public void Cancel_ActivityNotYetCancelledAndCancellingEncountersError_UpdateStatusAndEarlyExit() + { + // setup + int statusChangeCount = 0; + + var activity = new SimpleActivity(); + + activity.OnCancelInjection = () => activity.SetStatus(ActivityStatus.Failed); + ActivityStatus originalStatus = activity.Status; + + activity.StatusChanged += (sender, args) => + { + if (statusChangeCount == 0) + { + // 1st transition + Assert.AreEqual(originalStatus, args.OldStatus); + Assert.AreEqual(ActivityStatus.Cancelling, args.NewStatus); + } + else if (statusChangeCount == 1) + { + // 2nd transition + Assert.AreEqual(ActivityStatus.Cancelling, args.OldStatus); + Assert.AreEqual(ActivityStatus.Failed, args.NewStatus); + } + else + { + Assert.Fail("Expect 2 status updates."); + } + Assert.AreSame(activity, sender); + + statusChangeCount++; + }; + + // call + activity.Cancel(); + + // assert + Assert.AreEqual(2, statusChangeCount); + } + + private class SimpleActivity : Activity + { + /// + /// Sets the implementation of . + /// + public Action OnInitializeInjection { private get; set; } + protected override void OnInitialize() + { + OnInitializeInjection(); + } + + protected override void OnExecute() + { + throw new System.NotImplementedException(); + } + + /// + /// Sets the implementation of . + /// + public Action OnCancelInjection { private get; set; } + protected override void OnCancel() + { + OnCancelInjection(); + } + + protected override void OnCleanUp() + { + throw new System.NotImplementedException(); + } + + protected override void OnFinish() + { + throw new System.NotImplementedException(); + } + + public void SetStatus(ActivityStatus newStatus) + { + Status = newStatus; + } + } + } +} \ No newline at end of file