#region CPL License /* Nuclex Framework Copyright (C) 2002-2009 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #endregion using System; using System.Collections.Generic; using System.IO; #if UNITTEST using NUnit.Framework; using NMock2; namespace Nuclex.Support.Tracking { /// Unit Test for the transaction group class [TestFixture] public class TransactionGroupTest { #region interface ITransactionGroupSubscriber /// Interface used to test the transaction group public interface ITransactionGroupSubscriber { /// Called when the transaction group's progress changes /// Transaction group whose progress has changed /// Contains the new progress achieved void ProgressChanged(object sender, ProgressReportEventArgs arguments); /// Called when the transaction group has ended /// Transaction group that as ended /// Not used void Ended(object sender, EventArgs arguments); } #endregion // interface ITransactionGroupSubscriber #region class ProgressUpdateEventArgsMatcher /// Compares two ProgressUpdateEventArgsInstances for NMock validation private class ProgressUpdateEventArgsMatcher : Matcher { /// Initializes a new ProgressUpdateEventArgsMatcher /// Expected progress update event arguments public ProgressUpdateEventArgsMatcher(ProgressReportEventArgs expected) { this.expected = expected; } /// /// Called by NMock to verfiy the ProgressUpdateEventArgs match the expected value /// /// Actual value to compare to the expected value /// /// True if the actual value matches the expected value; otherwise false /// public override bool Matches(object actualAsObject) { ProgressReportEventArgs actual = (actualAsObject as ProgressReportEventArgs); if(actual == null) return false; return (actual.Progress == this.expected.Progress); } /// Creates a string representation of the expected value /// Writer to write the string representation into public override void DescribeTo(TextWriter writer) { writer.Write(this.expected.Progress.ToString()); } /// Expected progress update event args value private ProgressReportEventArgs expected; } #endregion // class ProgressUpdateEventArgsMatcher #region class TestTransaction /// Transaction used for testing in this unit test private class TestTransaction : Transaction, IProgressReporter { /// will be triggered to report when progress has been achieved public event EventHandler AsyncProgressChanged; /// Changes the testing transaction's indicated progress /// /// New progress to be reported by the testing transaction /// public void ChangeProgress(float progress) { OnAsyncProgressChanged(progress); } /// Transitions the transaction into the ended state public void End() { OnAsyncEnded(); } /// Fires the progress update event /// Progress to report (ranging from 0.0 to 1.0) /// /// Informs the observers of this transaction about the achieved progress. /// protected virtual void OnAsyncProgressChanged(float progress) { OnAsyncProgressChanged(new ProgressReportEventArgs(progress)); } /// Fires the progress update event /// Progress to report (ranging from 0.0 to 1.0) /// /// Informs the observers of this transaction about the achieved progress. /// Allows for classes derived from the transaction class to easily provide /// a custom event arguments class that has been derived from the /// transaction's ProgressUpdateEventArgs class. /// protected virtual void OnAsyncProgressChanged(ProgressReportEventArgs eventArguments) { EventHandler copy = AsyncProgressChanged; if(copy != null) copy(this, eventArguments); } } #endregion // class TestTransaction /// Initialization routine executed before each test is run [SetUp] public void Setup() { this.mockery = new Mockery(); } /// Validates that the set transaction properly sums the progress [Test] public void TestSummedProgress() { using( TransactionGroup testTransactionGroup = new TransactionGroup( new TestTransaction[] { new TestTransaction(), new TestTransaction() } ) ) { ITransactionGroupSubscriber mockedSubscriber = mockSubscriber(testTransactionGroup); Expect.Once.On(mockedSubscriber). Method("ProgressChanged"). With( new Matcher[] { new NMock2.Matchers.TypeMatcher(typeof(TransactionGroup)), new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.25f)) } ); testTransactionGroup.Children[0].Transaction.ChangeProgress(0.5f); this.mockery.VerifyAllExpectationsHaveBeenMet(); } } /// Validates that the transaction group respects the weights [Test] public void TestWeightedSummedProgress() { using( TransactionGroup testTransactionGroup = new TransactionGroup( new WeightedTransaction[] { new WeightedTransaction(new TestTransaction(), 1.0f), new WeightedTransaction(new TestTransaction(), 2.0f) } ) ) { ITransactionGroupSubscriber mockedSubscriber = mockSubscriber(testTransactionGroup); Expect.Once.On(mockedSubscriber). Method("ProgressChanged"). With( new Matcher[] { new NMock2.Matchers.TypeMatcher(typeof(TransactionGroup)), new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.5f / 3.0f)) } ); testTransactionGroup.Children[0].Transaction.ChangeProgress(0.5f); Expect.Once.On(mockedSubscriber). Method("ProgressChanged"). With( new Matcher[] { new NMock2.Matchers.TypeMatcher(typeof(TransactionGroup)), new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.5f)) } ); testTransactionGroup.Children[1].Transaction.ChangeProgress(0.5f); this.mockery.VerifyAllExpectationsHaveBeenMet(); } } /// /// Validates that the ended event is triggered when the last transaction ends /// [Test] public void TestEndedEvent() { using( TransactionGroup testTransactionGroup = new TransactionGroup( new TestTransaction[] { new TestTransaction(), new TestTransaction() } ) ) { ITransactionGroupSubscriber mockedSubscriber = mockSubscriber(testTransactionGroup); Expect.Exactly(2).On(mockedSubscriber). Method("ProgressChanged"). WithAnyArguments(); Expect.Once.On(mockedSubscriber). Method("Ended"). WithAnyArguments(); testTransactionGroup.Children[0].Transaction.End(); testTransactionGroup.Children[1].Transaction.End(); this.mockery.VerifyAllExpectationsHaveBeenMet(); } } /// Mocks a subscriber for the events of a transaction /// Transaction to mock an event subscriber for /// The mocked event subscriber private ITransactionGroupSubscriber mockSubscriber(Transaction transaction) { ITransactionGroupSubscriber mockedSubscriber = this.mockery.NewMock(); transaction.AsyncEnded += new EventHandler(mockedSubscriber.Ended); (transaction as IProgressReporter).AsyncProgressChanged += new EventHandler(mockedSubscriber.ProgressChanged); return mockedSubscriber; } /// Mock object factory private Mockery mockery; } } // namespace Nuclex.Support.Tracking #endif // UNITTEST