All remaining references to the old name of the waitable class, 'progression' changed to 'waitable'; QueueOperation renamed to OperationQueue; SetProgression renamed to WaitableGroup; improved documentation in various places
git-svn-id: file:///srv/devel/repo-conversion/nusu@78 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
parent
df860b8e57
commit
7bff8c5d52
|
@ -151,9 +151,9 @@
|
|||
<Compile Include="Source\Scheduling\AbortedException.cs" />
|
||||
<Compile Include="Source\Scheduling\IAbortable.cs" />
|
||||
<Compile Include="Source\Scheduling\Operation.cs" />
|
||||
<Compile Include="Source\Scheduling\QueueOperation.cs" />
|
||||
<Compile Include="Source\Scheduling\QueueOperation.Test.cs">
|
||||
<DependentUpon>QueueOperation.cs</DependentUpon>
|
||||
<Compile Include="Source\Scheduling\OperationQueue.cs" />
|
||||
<Compile Include="Source\Scheduling\OperationQueue.Test.cs">
|
||||
<DependentUpon>OperationQueue.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Source\Scheduling\ThreadCallbackOperation.cs" />
|
||||
<Compile Include="Source\Scheduling\ThreadOperation.cs" />
|
||||
|
@ -178,9 +178,9 @@
|
|||
<DependentUpon>ProgressTracker.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Source\Tracking\Request.cs" />
|
||||
<Compile Include="Source\Tracking\SetProgression.cs" />
|
||||
<Compile Include="Source\Tracking\SetProgression.Test.cs">
|
||||
<DependentUpon>SetProgression.cs</DependentUpon>
|
||||
<Compile Include="Source\Tracking\WaitableGroup.cs" />
|
||||
<Compile Include="Source\Tracking\WaitableGroup.Test.cs">
|
||||
<DependentUpon>WaitableGroup.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Source\Tracking\StatusReportEventArgs.cs" />
|
||||
<Compile Include="Source\Tracking\Waitable.cs" />
|
||||
|
|
|
@ -57,7 +57,7 @@ namespace Nuclex.Support.Collections {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts an element into the ProgressionCollection at the specified index
|
||||
/// Inserts an element into the ObservableCollection at the specified index
|
||||
/// </summary>
|
||||
/// <param name="index">
|
||||
/// The object to insert. The value can be null for reference types.
|
||||
|
@ -70,7 +70,7 @@ namespace Nuclex.Support.Collections {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the element at the specified index of the ProgressionCollection
|
||||
/// Removes the element at the specified index of the ObservableCollection
|
||||
/// </summary>
|
||||
/// <param name="index">The zero-based index of the element to remove</param>
|
||||
protected override void RemoveItem(int index) {
|
||||
|
|
|
@ -33,11 +33,11 @@ namespace Nuclex.Support.Scheduling {
|
|||
|
||||
/// <summary>Unit Test for the queue operation class</summary>
|
||||
[TestFixture]
|
||||
public class QueueOperationTest {
|
||||
public class OperationQueueTest {
|
||||
|
||||
#region interface IQueueOperationSubscriber
|
||||
|
||||
/// <summary>Interface used to test the set progression.</summary>
|
||||
/// <summary>Interface used to test the set waitable.</summary>
|
||||
public interface IQueueOperationSubscriber {
|
||||
|
||||
/// <summary>Called when the queue operations's progress changes</summary>
|
||||
|
@ -95,7 +95,7 @@ namespace Nuclex.Support.Scheduling {
|
|||
|
||||
#region class TestOperation
|
||||
|
||||
/// <summary>Progression used for testing in this unit test</summary>
|
||||
/// <summary>Operation used for testing in this unit test</summary>
|
||||
private class TestOperation : Operation, IProgressReporter {
|
||||
|
||||
/// <summary>will be triggered to report when progress has been achieved</summary>
|
||||
|
@ -116,9 +116,9 @@ namespace Nuclex.Support.Scheduling {
|
|||
OnAsyncEnded();
|
||||
}
|
||||
|
||||
/// <summary>Changes the testing progression's indicated progress</summary>
|
||||
/// <summary>Changes the testing operation's indicated progress</summary>
|
||||
/// <param name="progress">
|
||||
/// New progress to be reported by the testing progression
|
||||
/// New progress to be reported by the testing operation
|
||||
/// </param>
|
||||
public void ChangeProgress(float progress) {
|
||||
OnAsyncProgressChanged(progress);
|
||||
|
@ -136,7 +136,7 @@ namespace Nuclex.Support.Scheduling {
|
|||
/// <summary>Fires the progress update event</summary>
|
||||
/// <param name="progress">Progress to report (ranging from 0.0 to 1.0)</param>
|
||||
/// <remarks>
|
||||
/// Informs the observers of this progression about the achieved progress.
|
||||
/// Informs the observers of this operation about the achieved progress.
|
||||
/// </remarks>
|
||||
protected virtual void OnAsyncProgressChanged(float progress) {
|
||||
OnAsyncProgressChanged(new ProgressReportEventArgs(progress));
|
||||
|
@ -145,10 +145,10 @@ namespace Nuclex.Support.Scheduling {
|
|||
/// <summary>Fires the progress update event</summary>
|
||||
/// <param name="eventArguments">Progress to report (ranging from 0.0 to 1.0)</param>
|
||||
/// <remarks>
|
||||
/// Informs the observers of this progression about the achieved progress.
|
||||
/// Allows for classes derived from the Progression class to easily provide
|
||||
/// Informs the observers of this operation about the achieved progress.
|
||||
/// Allows for classes derived from the Operation class to easily provide
|
||||
/// a custom event arguments class that has been derived from the
|
||||
/// Progression's ProgressUpdateEventArgs class.
|
||||
/// operation's ProgressUpdateEventArgs class.
|
||||
/// </remarks>
|
||||
protected virtual void OnAsyncProgressChanged(ProgressReportEventArgs eventArguments) {
|
||||
EventHandler<ProgressReportEventArgs> copy = AsyncProgressChanged;
|
||||
|
@ -175,8 +175,8 @@ namespace Nuclex.Support.Scheduling {
|
|||
TestOperation operation1 = new TestOperation();
|
||||
TestOperation operation2 = new TestOperation();
|
||||
|
||||
QueueOperation<TestOperation> testQueueOperation =
|
||||
new QueueOperation<TestOperation>(
|
||||
OperationQueue<TestOperation> testQueueOperation =
|
||||
new OperationQueue<TestOperation>(
|
||||
new TestOperation[] { operation1, operation2 }
|
||||
);
|
||||
|
||||
|
@ -188,7 +188,7 @@ namespace Nuclex.Support.Scheduling {
|
|||
Method("ProgressChanged").
|
||||
With(
|
||||
new Matcher[] {
|
||||
new NMock2.Matchers.TypeMatcher(typeof(QueueOperation<TestOperation>)),
|
||||
new NMock2.Matchers.TypeMatcher(typeof(OperationQueue<TestOperation>)),
|
||||
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.25f))
|
||||
}
|
||||
);
|
||||
|
@ -199,7 +199,7 @@ namespace Nuclex.Support.Scheduling {
|
|||
Method("ProgressChanged").
|
||||
With(
|
||||
new Matcher[] {
|
||||
new NMock2.Matchers.TypeMatcher(typeof(QueueOperation<TestOperation>)),
|
||||
new NMock2.Matchers.TypeMatcher(typeof(OperationQueue<TestOperation>)),
|
||||
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.5f))
|
||||
}
|
||||
);
|
|
@ -29,7 +29,7 @@ namespace Nuclex.Support.Scheduling {
|
|||
/// <typeparam name="OperationType">
|
||||
/// Type of the child operations the QueueOperation will contain
|
||||
/// </typeparam>
|
||||
public class QueueOperation<OperationType> : Operation, IProgressReporter
|
||||
public class OperationQueue<OperationType> : Operation, IProgressReporter
|
||||
where OperationType : Operation {
|
||||
|
||||
/// <summary>will be triggered to report when progress has been achieved</summary>
|
||||
|
@ -40,24 +40,24 @@ namespace Nuclex.Support.Scheduling {
|
|||
/// <remarks>
|
||||
/// All child operations will have a default weight of 1.0
|
||||
/// </remarks>
|
||||
public QueueOperation(IEnumerable<OperationType> childs) : this() {
|
||||
public OperationQueue(IEnumerable<OperationType> childs) : this() {
|
||||
|
||||
// Construct a WeightedProgression with the default weight for each
|
||||
// progression and wrap it in an ObservedProgression
|
||||
// Construct a WeightedWaitable with the default weight for each
|
||||
// waitable and wrap it in an ObservedWaitable
|
||||
foreach(OperationType operation in childs)
|
||||
this.children.Add(new WeightedWaitable<OperationType>(operation));
|
||||
|
||||
// Since all progressions have a weight of 1.0, the total weight is
|
||||
// equal to the number of progressions in our list
|
||||
// Since all waitables have a weight of 1.0, the total weight is
|
||||
// equal to the number of waitables in our list
|
||||
this.totalWeight = (float)this.children.Count;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new queue operation with custom weights</summary>
|
||||
/// <param name="childs">Child operations to execute in this operation</param>
|
||||
public QueueOperation(IEnumerable<WeightedWaitable<OperationType>> childs) : this() {
|
||||
public OperationQueue(IEnumerable<WeightedWaitable<OperationType>> childs) : this() {
|
||||
|
||||
// Construct an ObservedProgression around each of the WeightedProgressions
|
||||
// Construct an ObservedWaitablen around each of the WeightedWaitables
|
||||
foreach(WeightedWaitable<OperationType> operation in childs) {
|
||||
this.children.Add(operation);
|
||||
|
||||
|
@ -68,7 +68,7 @@ namespace Nuclex.Support.Scheduling {
|
|||
}
|
||||
|
||||
/// <summary>Initializes a new queue operation</summary>
|
||||
private QueueOperation() {
|
||||
private OperationQueue() {
|
||||
this.asyncOperationEndedDelegate = new EventHandler(asyncOperationEnded);
|
||||
this.asyncOperationProgressChangedDelegate = new EventHandler<ProgressReportEventArgs>(
|
||||
asyncOperationProgressChanged
|
||||
|
@ -99,7 +99,7 @@ namespace Nuclex.Support.Scheduling {
|
|||
/// <summary>Fires the progress update event</summary>
|
||||
/// <param name="progress">Progress to report (ranging from 0.0 to 1.0)</param>
|
||||
/// <remarks>
|
||||
/// Informs the observers of this progression about the achieved progress.
|
||||
/// Informs the observers of this waitable about the achieved progress.
|
||||
/// </remarks>
|
||||
protected virtual void OnAsyncProgressChanged(float progress) {
|
||||
OnAsyncProgressChanged(new ProgressReportEventArgs(progress));
|
||||
|
@ -108,10 +108,10 @@ namespace Nuclex.Support.Scheduling {
|
|||
/// <summary>Fires the progress update event</summary>
|
||||
/// <param name="eventArguments">Progress to report (ranging from 0.0 to 1.0)</param>
|
||||
/// <remarks>
|
||||
/// Informs the observers of this progression about the achieved progress.
|
||||
/// Allows for classes derived from the Progression class to easily provide
|
||||
/// Informs the observers of this waitable about the achieved progress.
|
||||
/// Allows for classes derived from the Waitable class to easily provide
|
||||
/// a custom event arguments class that has been derived from the
|
||||
/// Progression's ProgressUpdateEventArgs class.
|
||||
/// waitable's ProgressUpdateEventArgs class.
|
||||
/// </remarks>
|
||||
protected virtual void OnAsyncProgressChanged(ProgressReportEventArgs eventArguments) {
|
||||
EventHandler<ProgressReportEventArgs> copy = AsyncProgressChanged;
|
|
@ -23,33 +23,33 @@ using System.Collections.Generic;
|
|||
|
||||
namespace Nuclex.Support.Tracking {
|
||||
|
||||
/// <summary>Progression being observed by another class</summary>
|
||||
/// <typeparam name="ProgressionType">
|
||||
/// Type of the progression that is being observed
|
||||
/// <summary>Waitable being observed by another object</summary>
|
||||
/// <typeparam name="WaitableType">
|
||||
/// Type of the waitable that is being observed
|
||||
/// </typeparam>
|
||||
internal class ObservedWeightedWaitable<ProgressionType> : IDisposable
|
||||
where ProgressionType : Waitable {
|
||||
internal class ObservedWeightedWaitable<WaitableType> : IDisposable
|
||||
where WaitableType : Waitable {
|
||||
|
||||
/// <summary>Delegate for reporting progress updates</summary>
|
||||
public delegate void ReportDelegate();
|
||||
|
||||
/// <summary>Initializes a new observed progression</summary>
|
||||
/// <param name="weightedProgression">Weighted progression being observed</param>
|
||||
/// <summary>Initializes a new observed waitable</summary>
|
||||
/// <param name="weightedWaitable">Weighted waitable being observed</param>
|
||||
/// <param name="progressUpdateCallback">
|
||||
/// Callback to invoke when the progression's progress changes
|
||||
/// Callback to invoke when the waitable's progress changes
|
||||
/// </param>
|
||||
/// <param name="endedCallback">
|
||||
/// Callback to invoke when the progression has ended
|
||||
/// Callback to invoke when the waitable has ended
|
||||
/// </param>
|
||||
internal ObservedWeightedWaitable(
|
||||
WeightedWaitable<ProgressionType> weightedProgression,
|
||||
WeightedWaitable<WaitableType> weightedWaitable,
|
||||
ReportDelegate progressUpdateCallback,
|
||||
ReportDelegate endedCallback
|
||||
) {
|
||||
this.weightedProgression = weightedProgression;
|
||||
this.weightedWaitable = weightedWaitable;
|
||||
|
||||
// See if this progression has already ended (initial check for performance)
|
||||
if(weightedProgression.Waitable.Ended) {
|
||||
// See if this waitable has already ended (initial check for performance)
|
||||
if(weightedWaitable.Waitable.Ended) {
|
||||
|
||||
this.progress = 1.0f;
|
||||
|
||||
|
@ -58,18 +58,18 @@ namespace Nuclex.Support.Tracking {
|
|||
this.endedCallback = endedCallback;
|
||||
this.progressUpdateCallback = progressUpdateCallback;
|
||||
|
||||
this.weightedProgression.Waitable.AsyncEnded +=
|
||||
this.weightedWaitable.Waitable.AsyncEnded +=
|
||||
new EventHandler(asyncEnded);
|
||||
|
||||
// Check whether this progression might have ended before we were able to
|
||||
// Check whether this waitable might have ended before we were able to
|
||||
// attach ourselfes to its event. If so, don't bother registering to the
|
||||
// other event and (important) set our progress to 1.0 because, since we
|
||||
// might not have gotten the 'Ended' event, it might otherwise stay at 0.0
|
||||
// even though the progression is in the 'Ended' state.
|
||||
if(weightedProgression.Waitable.Ended) {
|
||||
// even though the waitable is in the 'Ended' state.
|
||||
if(weightedWaitable.Waitable.Ended) {
|
||||
this.progress = 1.0f;
|
||||
} else {
|
||||
this.progressReporter = this.weightedProgression.Waitable as IProgressReporter;
|
||||
this.progressReporter = this.weightedWaitable.Waitable as IProgressReporter;
|
||||
|
||||
if(this.progressReporter != null) {
|
||||
this.asyncProgressChangedEventHandler = new EventHandler<ProgressReportEventArgs>(
|
||||
|
@ -89,18 +89,18 @@ namespace Nuclex.Support.Tracking {
|
|||
asyncDisconnectEvents();
|
||||
}
|
||||
|
||||
/// <summary>Weighted progression being observed</summary>
|
||||
public WeightedWaitable<ProgressionType> WeightedWaitable {
|
||||
get { return this.weightedProgression; }
|
||||
/// <summary>Weighted waitable being observed</summary>
|
||||
public WeightedWaitable<WaitableType> WeightedWaitable {
|
||||
get { return this.weightedWaitable; }
|
||||
}
|
||||
|
||||
/// <summary>Amount of progress this progression has achieved so far</summary>
|
||||
/// <summary>Amount of progress this waitable has achieved so far</summary>
|
||||
public float Progress {
|
||||
get { return this.progress; }
|
||||
}
|
||||
|
||||
/// <summary>Called when the observed progression has ended</summary>
|
||||
/// <param name="sender">Progression that has ended</param>
|
||||
/// <summary>Called when the observed waitable has ended</summary>
|
||||
/// <param name="sender">Waitable that has ended</param>
|
||||
/// <param name="e">Not used</param>
|
||||
private void asyncEnded(object sender, EventArgs e) {
|
||||
ReportDelegate endedCallback = this.endedCallback;
|
||||
|
@ -109,11 +109,11 @@ namespace Nuclex.Support.Tracking {
|
|||
asyncDisconnectEvents(); // We don't need those anymore!
|
||||
|
||||
// If the progress hasn't reached 1.0 yet, make a fake report so that even
|
||||
// when a progression doesn't report any progress at all, the set or queue
|
||||
// owning us will have a percentage of progressions completed.
|
||||
// when a waitable doesn't report any progress at all, the set or queue
|
||||
// owning us will have a percentage of waitables completed.
|
||||
//
|
||||
// There is the possibility of a race condition here, as a final progress
|
||||
// report could have been generated by a thread running the progression
|
||||
// report could have been generated by a thread running the waitable
|
||||
// that was preempted by this thread. This would cause the progress to
|
||||
// jump to 1.0 and then back to whatever the waiting thread will report.
|
||||
if(this.progress != 1.0f) {
|
||||
|
@ -124,15 +124,15 @@ namespace Nuclex.Support.Tracking {
|
|||
endedCallback();
|
||||
}
|
||||
|
||||
/// <summary>Called when the progress of the observed progression changes</summary>
|
||||
/// <param name="sender">Progression whose progress has changed</param>
|
||||
/// <summary>Called when the progress of the observed waitable changes</summary>
|
||||
/// <param name="sender">Waitable whose progress has changed</param>
|
||||
/// <param name="e">Contains the updated progress</param>
|
||||
private void asyncProgressChanged(object sender, ProgressReportEventArgs e) {
|
||||
this.progress = e.Progress;
|
||||
this.progressUpdateCallback();
|
||||
}
|
||||
|
||||
/// <summary>Unsubscribes from all events of the observed progression</summary>
|
||||
/// <summary>Unsubscribes from all events of the observed waitable</summary>
|
||||
private void asyncDisconnectEvents() {
|
||||
|
||||
// Make use of the double check locking idiom to avoid the costly lock when
|
||||
|
@ -143,8 +143,7 @@ namespace Nuclex.Support.Tracking {
|
|||
// is no risk of deadlock involved, so we don't need a fancy syncRoot!
|
||||
lock(this) {
|
||||
if(this.endedCallback != null) {
|
||||
this.weightedProgression.Waitable.AsyncEnded -=
|
||||
new EventHandler(asyncEnded);
|
||||
this.weightedWaitable.Waitable.AsyncEnded -= new EventHandler(asyncEnded);
|
||||
|
||||
if(this.progressReporter != null) {
|
||||
this.progressReporter.AsyncProgressChanged -=
|
||||
|
@ -163,13 +162,13 @@ namespace Nuclex.Support.Tracking {
|
|||
}
|
||||
|
||||
private EventHandler<ProgressReportEventArgs> asyncProgressChangedEventHandler;
|
||||
/// <summary>The observed progression's progress reporting interface</summary>
|
||||
/// <summary>The observed waitable's progress reporting interface</summary>
|
||||
private IProgressReporter progressReporter;
|
||||
/// <summary>The weighted progression that is being observed</summary>
|
||||
private WeightedWaitable<ProgressionType> weightedProgression;
|
||||
/// <summary>The weighted wable that is being observed</summary>
|
||||
private WeightedWaitable<WaitableType> weightedWaitable;
|
||||
/// <summary>Callback to invoke when the progress updates</summary>
|
||||
private volatile ReportDelegate progressUpdateCallback;
|
||||
/// <summary>Callback to invoke when the progression ends</summary>
|
||||
/// <summary>Callback to invoke when the waitable ends</summary>
|
||||
private volatile ReportDelegate endedCallback;
|
||||
/// <summary>Progress achieved so far</summary>
|
||||
private volatile float progress;
|
||||
|
|
|
@ -27,36 +27,36 @@ using Nuclex.Support.Collections;
|
|||
|
||||
namespace Nuclex.Support.Tracking {
|
||||
|
||||
/// <summary>Collection of progressions with a weighting value</summary>
|
||||
/// <typeparam name="ProgressionType">Type of progressions to manage</typeparam>
|
||||
/// <summary>Collection of waitables with a weighting value</summary>
|
||||
/// <typeparam name="WaitableType">Type of waitables to manage</typeparam>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This collection is exposed as a read-only collection to the user that
|
||||
/// stores WeightedProgressions. Internally, it merely wraps a collection of
|
||||
/// an internal type used to keep track of the individual progression's
|
||||
/// progress in the SetProgression and QueueOperation classes.
|
||||
/// stores WeightedWaitables. Internally, it merely wraps a collection of
|
||||
/// an internal type used to keep track of the individual waitable's
|
||||
/// progress in the WaitableSet and OperationQueue classes.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// It is read-only because the design requires a progression to only ever
|
||||
/// finish once. If it was possible eg. to add items after a SetProgression
|
||||
/// It is read-only because the design requires a waitable to only ever
|
||||
/// finish once. If it was possible eg. to add items after a WaitableSet
|
||||
/// had signalled itself as being finished, it would be moved into an
|
||||
/// unfinished state again. Also, an empty SetProgression is, by definition,
|
||||
/// unfinished state again. Also, an empty WaitableSet is, by definition,
|
||||
/// finished (simply because there is no work to do) - unless the contents
|
||||
/// of set are passed to the SetProgression's constructor and never modified
|
||||
/// of set are passed to the WaitableSet's constructor and never modified
|
||||
/// at all, the design would be violated as soon as ab instance of the
|
||||
/// SetProgression or QueueOperation classes was created.
|
||||
/// WaitableSet or OperationQueue classes was created.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
internal class WeightedProgressionWrapperCollection<ProgressionType> :
|
||||
internal class WeightedWaitableWrapperCollection<WaitableType> :
|
||||
TransformingReadOnlyCollection<
|
||||
ObservedWeightedWaitable<ProgressionType>, WeightedWaitable<ProgressionType>
|
||||
ObservedWeightedWaitable<WaitableType>, WeightedWaitable<WaitableType>
|
||||
>
|
||||
where ProgressionType : Waitable {
|
||||
where WaitableType : Waitable {
|
||||
|
||||
/// <summary>Initializes a new weighted progression collection wrapper</summary>
|
||||
/// <param name="items">Items to be exposed as weighted progressions</param>
|
||||
internal WeightedProgressionWrapperCollection(
|
||||
IList<ObservedWeightedWaitable<ProgressionType>> items
|
||||
/// <summary>Initializes a new weighted waitable collection wrapper</summary>
|
||||
/// <param name="items">Items to be exposed as weighted waitables</param>
|
||||
internal WeightedWaitableWrapperCollection(
|
||||
IList<ObservedWeightedWaitable<WaitableType>> items
|
||||
)
|
||||
: base(items) { }
|
||||
|
||||
|
@ -69,8 +69,8 @@ namespace Nuclex.Support.Tracking {
|
|||
/// be called frequently, because the TransformingReadOnlyCollection does
|
||||
/// not cache otherwise store the transformed items.
|
||||
/// </remarks>
|
||||
protected override WeightedWaitable<ProgressionType> Transform(
|
||||
ObservedWeightedWaitable<ProgressionType> item
|
||||
protected override WeightedWaitable<WaitableType> Transform(
|
||||
ObservedWeightedWaitable<WaitableType> item
|
||||
) {
|
||||
return item.WeightedWaitable;
|
||||
}
|
||||
|
|
|
@ -29,28 +29,28 @@ using NMock2;
|
|||
|
||||
namespace Nuclex.Support.Tracking {
|
||||
|
||||
/// <summary>Unit Test for the progression tracker class</summary>
|
||||
/// <summary>Unit Test for the progress tracker class</summary>
|
||||
[TestFixture]
|
||||
public class ProgressionTrackerTest {
|
||||
public class ProgressTrackerTest {
|
||||
|
||||
#region interface IProgressionTrackerSubscriber
|
||||
#region interface IProgressTrackerSubscriber
|
||||
|
||||
/// <summary>Interface used to test the progression tracker.</summary>
|
||||
public interface IProgressionTrackerSubscriber {
|
||||
/// <summary>Interface used to test the progress tracker</summary>
|
||||
public interface IProgressTrackerSubscriber {
|
||||
|
||||
/// <summary>Called when the progression tracker's progress changes</summary>
|
||||
/// <param name="sender">Progression tracker whose progress has changed</param>
|
||||
/// <summary>Called when the tracked progress changes</summary>
|
||||
/// <param name="sender">Progress tracker whose progress has changed</param>
|
||||
/// <param name="e">Contains the new progress achieved</param>
|
||||
void ProgressChanged(object sender, ProgressReportEventArgs e);
|
||||
|
||||
/// <summary>Called when the progression tracker's idle state changes</summary>
|
||||
/// <param name="sender">Progression tracker whose idle state has changed</param>
|
||||
/// <summary>Called when the progress tracker's idle state changes</summary>
|
||||
/// <param name="sender">Progress tracker whose idle state has changed</param>
|
||||
/// <param name="e">Contains the new idle state of the tracker</param>
|
||||
void IdleStateChanged(object sender, IdleStateEventArgs e);
|
||||
|
||||
}
|
||||
|
||||
#endregion // interface IProgressionTrackerSubscriber
|
||||
#endregion // interface IProgressTrackerSubscriber
|
||||
|
||||
#region class ProgressUpdateEventArgsMatcher
|
||||
|
||||
|
@ -91,23 +91,21 @@ namespace Nuclex.Support.Tracking {
|
|||
|
||||
#endregion // class ProgressUpdateEventArgsMatcher
|
||||
|
||||
#region class TestProgression
|
||||
#region class TestWaitable
|
||||
|
||||
/// <summary>Progression used for testing in this unit test</summary>
|
||||
private class TestProgression : Waitable, IProgressReporter {
|
||||
/// <summary>Waitable used for testing in this unit test</summary>
|
||||
private class TestWaitable : Waitable, IProgressReporter {
|
||||
|
||||
/// <summary>will be triggered to report when progress has been achieved</summary>
|
||||
public event EventHandler<ProgressReportEventArgs> AsyncProgressChanged;
|
||||
|
||||
/// <summary>Changes the testing progression's indicated progress</summary>
|
||||
/// <param name="progress">
|
||||
/// New progress to be reported by the testing progression
|
||||
/// </param>
|
||||
/// <summary>Changes the testing waitable's indicated progress</summary>
|
||||
/// <param name="progress">New progress to be reported by the testing waitable</param>
|
||||
public void ChangeProgress(float progress) {
|
||||
OnAsyncProgressChanged(progress);
|
||||
}
|
||||
|
||||
/// <summary>Transitions the progression into the ended state</summary>
|
||||
/// <summary>Transitions the waitable into the ended state</summary>
|
||||
public void End() {
|
||||
OnAsyncEnded();
|
||||
}
|
||||
|
@ -115,7 +113,7 @@ namespace Nuclex.Support.Tracking {
|
|||
/// <summary>Fires the progress update event</summary>
|
||||
/// <param name="progress">Progress to report (ranging from 0.0 to 1.0)</param>
|
||||
/// <remarks>
|
||||
/// Informs the observers of this progression about the achieved progress.
|
||||
/// Informs the observers of this waitable about the achieved progress.
|
||||
/// </remarks>
|
||||
protected virtual void OnAsyncProgressChanged(float progress) {
|
||||
OnAsyncProgressChanged(new ProgressReportEventArgs(progress));
|
||||
|
@ -124,10 +122,10 @@ namespace Nuclex.Support.Tracking {
|
|||
/// <summary>Fires the progress update event</summary>
|
||||
/// <param name="eventArguments">Progress to report (ranging from 0.0 to 1.0)</param>
|
||||
/// <remarks>
|
||||
/// Informs the observers of this progression about the achieved progress.
|
||||
/// Allows for classes derived from the Progression class to easily provide
|
||||
/// Informs the observers of this waitable about the achieved progress.
|
||||
/// Allows for classes derived from the Waitable class to easily provide
|
||||
/// a custom event arguments class that has been derived from the
|
||||
/// Progression's ProgressUpdateEventArgs class.
|
||||
/// waitable's ProgressUpdateEventArgs class.
|
||||
/// </remarks>
|
||||
protected virtual void OnAsyncProgressChanged(ProgressReportEventArgs eventArguments) {
|
||||
EventHandler<ProgressReportEventArgs> copy = AsyncProgressChanged;
|
||||
|
@ -137,7 +135,7 @@ namespace Nuclex.Support.Tracking {
|
|||
|
||||
}
|
||||
|
||||
#endregion // class TestProgression
|
||||
#endregion // class TestWiatable
|
||||
|
||||
/// <summary>Initialization routine executed before each test is run</summary>
|
||||
[SetUp]
|
||||
|
@ -150,7 +148,7 @@ namespace Nuclex.Support.Tracking {
|
|||
public void TestSummedProgress() {
|
||||
ProgressTracker tracker = new ProgressTracker();
|
||||
|
||||
IProgressionTrackerSubscriber mockedSubscriber = mockSubscriber(tracker);
|
||||
IProgressTrackerSubscriber mockedSubscriber = mockSubscriber(tracker);
|
||||
|
||||
Expect.Once.On(mockedSubscriber).
|
||||
Method("IdleStateChanged").
|
||||
|
@ -165,9 +163,9 @@ namespace Nuclex.Support.Tracking {
|
|||
}
|
||||
);
|
||||
|
||||
TestProgression test1 = new TestProgression();
|
||||
TestWaitable test1 = new TestWaitable();
|
||||
tracker.Track(test1);
|
||||
TestProgression test2 = new TestProgression();
|
||||
TestWaitable test2 = new TestWaitable();
|
||||
tracker.Track(test2);
|
||||
|
||||
Expect.Once.On(mockedSubscriber).
|
||||
|
@ -185,18 +183,18 @@ namespace Nuclex.Support.Tracking {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that the tracker only removes progressions when the whole
|
||||
/// Validates that the tracker only removes waitables when the whole
|
||||
/// tracking list has reached the 'ended' state.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the tracker would remove ended progressions right when they finished,
|
||||
/// If the tracker would remove ended waitables right when they finished,
|
||||
/// the total progress would jump back each time. This is unwanted, of course.
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public void TestDelayedRemoval() {
|
||||
ProgressTracker tracker = new ProgressTracker();
|
||||
|
||||
IProgressionTrackerSubscriber mockedSubscriber = mockSubscriber(tracker);
|
||||
IProgressTrackerSubscriber mockedSubscriber = mockSubscriber(tracker);
|
||||
|
||||
Expect.Once.On(mockedSubscriber).
|
||||
Method("IdleStateChanged").
|
||||
|
@ -211,9 +209,9 @@ namespace Nuclex.Support.Tracking {
|
|||
}
|
||||
);
|
||||
|
||||
TestProgression test1 = new TestProgression();
|
||||
TestWaitable test1 = new TestWaitable();
|
||||
tracker.Track(test1);
|
||||
TestProgression test2 = new TestProgression();
|
||||
TestWaitable test2 = new TestWaitable();
|
||||
tracker.Track(test2);
|
||||
|
||||
Expect.Once.On(mockedSubscriber).
|
||||
|
@ -236,8 +234,8 @@ namespace Nuclex.Support.Tracking {
|
|||
}
|
||||
);
|
||||
|
||||
// Total progress should be 0.75 after this call (one progression at 1.0,
|
||||
// the other one at 0.5). If the second progression would be removed,
|
||||
// Total progress should be 0.75 after this call (one waitable at 1.0,
|
||||
// the other one at 0.5). If the second waitable would be removed,
|
||||
// the progress would jump to 0.5 instead.
|
||||
test2.End();
|
||||
|
||||
|
@ -260,14 +258,14 @@ namespace Nuclex.Support.Tracking {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that the tracker behaves correctly if it is fed with progressions
|
||||
/// Validates that the tracker behaves correctly if it is fed with waitables
|
||||
/// that have already ended.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestSoleEndedProgression() {
|
||||
public void TestSoleEndedWaitable() {
|
||||
ProgressTracker tracker = new ProgressTracker();
|
||||
|
||||
IProgressionTrackerSubscriber mockedSubscriber = mockSubscriber(tracker);
|
||||
IProgressTrackerSubscriber mockedSubscriber = mockSubscriber(tracker);
|
||||
|
||||
tracker.Track(Waitable.EndedDummy);
|
||||
|
||||
|
@ -275,14 +273,14 @@ namespace Nuclex.Support.Tracking {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that the tracker behaves correctly if it is fed with progressions
|
||||
/// that have already ended in addition to progressions that are actively executing.
|
||||
/// Validates that the tracker behaves correctly if it is fed with waitables
|
||||
/// that have already ended in addition to waitables that are actively executing.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestEndedProgression() {
|
||||
public void TestEndedWaitable() {
|
||||
ProgressTracker tracker = new ProgressTracker();
|
||||
|
||||
IProgressionTrackerSubscriber mockedSubscriber = mockSubscriber(tracker);
|
||||
IProgressTrackerSubscriber mockedSubscriber = mockSubscriber(tracker);
|
||||
|
||||
Expect.Once.On(mockedSubscriber).
|
||||
Method("IdleStateChanged").
|
||||
|
@ -297,7 +295,7 @@ namespace Nuclex.Support.Tracking {
|
|||
}
|
||||
);
|
||||
|
||||
TestProgression test1 = new TestProgression();
|
||||
TestWaitable test1 = new TestWaitable();
|
||||
tracker.Track(test1);
|
||||
|
||||
Expect.Once.On(mockedSubscriber).
|
||||
|
@ -337,7 +335,7 @@ namespace Nuclex.Support.Tracking {
|
|||
public void TestProvokedDeadlock() {
|
||||
ProgressTracker tracker = new ProgressTracker();
|
||||
|
||||
TestProgression test1 = new TestProgression();
|
||||
TestWaitable test1 = new TestWaitable();
|
||||
tracker.Track(test1);
|
||||
|
||||
tracker.AsyncIdleStateChanged +=
|
||||
|
@ -351,9 +349,9 @@ namespace Nuclex.Support.Tracking {
|
|||
/// <summary>Mocks a subscriber for the events of a tracker</summary>
|
||||
/// <param name="tracker">Tracker to mock an event subscriber for</param>
|
||||
/// <returns>The mocked event subscriber</returns>
|
||||
private IProgressionTrackerSubscriber mockSubscriber(ProgressTracker tracker) {
|
||||
IProgressionTrackerSubscriber mockedSubscriber =
|
||||
this.mockery.NewMock<IProgressionTrackerSubscriber>();
|
||||
private IProgressTrackerSubscriber mockSubscriber(ProgressTracker tracker) {
|
||||
IProgressTrackerSubscriber mockedSubscriber =
|
||||
this.mockery.NewMock<IProgressTrackerSubscriber>();
|
||||
|
||||
tracker.AsyncIdleStateChanged +=
|
||||
new EventHandler<IdleStateEventArgs>(mockedSubscriber.IdleStateChanged);
|
||||
|
|
|
@ -23,18 +23,18 @@ using System.Collections.Generic;
|
|||
|
||||
namespace Nuclex.Support.Tracking {
|
||||
|
||||
/// <summary>Extended type of progression that is able to fail</summary>
|
||||
/// <summary>Asynchronous request running in the background</summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// If the background process fails, the exception that caused it to fail is
|
||||
/// communicated to all parties waiting on the progression through the
|
||||
/// Exception property. Implementers should place their code in try..catch
|
||||
/// blocks and call SetException() to temporarily store the exception for
|
||||
/// retrieval by the caller(s).
|
||||
/// communicated to all parties waiting on the Request through the Join()
|
||||
/// method. Implementers should store any errors occuring in the asynchronous
|
||||
/// parts of their code in a try..catch block (or avoid throwing and just
|
||||
/// store a new exception) and re-throw them when in ReraiseExceptions()
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// As with all progressions, the interface contract still requires you to call
|
||||
/// OnAsyncEnded(), no matter what the outcome of your background operation is.
|
||||
/// Like in the Waitable class, the contract requires you to always call
|
||||
/// OnAsyncEnded(), no matter what the outcome of your operation is.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public abstract class Request : Waitable {
|
||||
|
@ -65,6 +65,13 @@ namespace Nuclex.Support.Tracking {
|
|||
|
||||
#endregion // EndedDummyRequest
|
||||
|
||||
/// <summary>Succeeded dummy request</summary>
|
||||
/// <remarks>
|
||||
/// Use to indicate success if the request has already been completed at
|
||||
/// the time you are asked to perform it.
|
||||
/// </remarks>
|
||||
public static readonly Request SucceededDummy = new EndedDummyRequest();
|
||||
|
||||
/// <summary>Creates a new failed dummy request</summary>
|
||||
/// <param name="exception">Exception that supposedly caused the request to fail</param>
|
||||
/// <returns>
|
||||
|
@ -83,7 +90,7 @@ namespace Nuclex.Support.Tracking {
|
|||
/// </remarks>
|
||||
public virtual void Join() {
|
||||
|
||||
// If the progression itself hasn't ended yet, block the caller until it has.
|
||||
// If the request itself hasn't ended yet, block the caller until it has.
|
||||
// We could just use WaitHandle.WaitOne() here, but since the WaitHandle is created
|
||||
// on-the-fly only when it is requested, we can avoid the WaitHandle creation in
|
||||
// case the request is already finished!
|
||||
|
@ -109,13 +116,36 @@ namespace Nuclex.Support.Tracking {
|
|||
/// </typeparam>
|
||||
public abstract class Request<ResultType> : Request {
|
||||
|
||||
#region class EndedDummyRequest
|
||||
#region class SucceededDummyRequest
|
||||
|
||||
/// <summary>Dummy request that is always in the ended state</summary>
|
||||
private class EndedDummyRequest : Request<ResultType> {
|
||||
/// <summary>Succeeded dummy request that is always in the ended state</summary>
|
||||
private class SucceededDummyRequest : Request<ResultType> {
|
||||
/// <summary>Creates a new failed dummy request</summary>
|
||||
/// <param name="result">Result to return to the request's caller</param>
|
||||
public SucceededDummyRequest(ResultType result) {
|
||||
this.result = result;
|
||||
OnAsyncEnded();
|
||||
}
|
||||
/// <summary>
|
||||
/// Allows the specific request implementation to re-throw an exception if
|
||||
/// the background process finished unsuccessfully
|
||||
/// </summary>
|
||||
protected override ResultType GatherResults() {
|
||||
return this.result;
|
||||
}
|
||||
/// <summary>Results the succeede dummy request will provide to the caller</summary>
|
||||
private ResultType result;
|
||||
}
|
||||
|
||||
#endregion // SucceededDummyRequest
|
||||
|
||||
#region class FailedDummyRequest
|
||||
|
||||
/// <summary>Failed dummy request that is always in the ended state</summary>
|
||||
private class FailedDummyRequest : Request<ResultType> {
|
||||
/// <summary>Creates a new failed dummy request</summary>
|
||||
/// <param name="exception">Exception that caused the dummy to fail</param>
|
||||
public EndedDummyRequest(Exception exception) {
|
||||
public FailedDummyRequest(Exception exception) {
|
||||
this.exception = exception;
|
||||
OnAsyncEnded();
|
||||
}
|
||||
|
@ -130,15 +160,24 @@ namespace Nuclex.Support.Tracking {
|
|||
private Exception exception;
|
||||
}
|
||||
|
||||
#endregion // EndedDummyRequest
|
||||
#endregion // FailedDummyRequest
|
||||
|
||||
/// <summary>Creates a new failed dummy request</summary>
|
||||
/// <param name="result">Result to provide to the caller</param>
|
||||
/// <returns>
|
||||
/// A succeeded request that returns the provided result to the caller
|
||||
/// </returns>
|
||||
public static Request<ResultType> CreateSucceededDummy(ResultType result) {
|
||||
return new SucceededDummyRequest(result);
|
||||
}
|
||||
|
||||
/// <summary>Creates a new failed dummy request</summary>
|
||||
/// <param name="exception">Exception that supposedly caused the request to fail</param>
|
||||
/// <returns>
|
||||
/// A failed request that reports the provided exception as cause for its failure
|
||||
/// </returns>
|
||||
public static new Request CreateFailedDummy(Exception exception) {
|
||||
return new EndedDummyRequest(exception);
|
||||
public static new Request<ResultType> CreateFailedDummy(Exception exception) {
|
||||
return new FailedDummyRequest(exception);
|
||||
}
|
||||
|
||||
/// <summary>Waits for the background operation to end</summary>
|
||||
|
|
|
@ -62,7 +62,7 @@ namespace Nuclex.Support.Tracking {
|
|||
/// <summary>A dummy waitable that's always in the 'ended' state</summary>
|
||||
/// <remarks>
|
||||
/// Useful if an operation is already complete when it's being asked for or
|
||||
/// when a progression that's lazily created is accessed after the original
|
||||
/// when a waitable that's lazily created is accessed after the original
|
||||
/// operation has ended already.
|
||||
/// </remarks>
|
||||
public static readonly Waitable EndedDummy = new EndedDummyWaitable();
|
||||
|
@ -186,14 +186,14 @@ namespace Nuclex.Support.Tracking {
|
|||
/// </para>
|
||||
/// <para>
|
||||
/// Calling this method is mandatory. Implementers need to take care that
|
||||
/// the OnAsyncEnded() method is called on any instance of Progression that's
|
||||
/// the OnAsyncEnded() method is called on any instance of Waitable that's
|
||||
/// being created. This method also must not be called more than once.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
protected virtual void OnAsyncEnded() {
|
||||
|
||||
// Make sure the progression is not ended more than once. By guaranteeing that
|
||||
// a progression can only be ended once, we allow users of this class to
|
||||
// Make sure the waitable is not ended more than once. By guaranteeing that
|
||||
// a waitable can only be ended once, we allow users of this class to
|
||||
// skip some safeguards against notifications arriving twice.
|
||||
lock(this) {
|
||||
|
||||
|
@ -202,7 +202,7 @@ namespace Nuclex.Support.Tracking {
|
|||
// to waste any effort optimizing the speed at which an implementation fault
|
||||
// will be noticed.
|
||||
if(this.ended)
|
||||
throw new InvalidOperationException("The progression has already been ended");
|
||||
throw new InvalidOperationException("The Waitable has already been ended");
|
||||
|
||||
this.ended = true;
|
||||
|
||||
|
@ -236,7 +236,7 @@ namespace Nuclex.Support.Tracking {
|
|||
protected volatile List<EventHandler> endedEventSubscribers;
|
||||
/// <summary>Whether the operation has completed yet</summary>
|
||||
protected volatile bool ended;
|
||||
/// <summary>Event that will be set when the progression is completed</summary>
|
||||
/// <summary>Event that will be set when the waitable is completed</summary>
|
||||
/// <remarks>
|
||||
/// This event is will only be created when it is specifically asked for using
|
||||
/// the WaitHandle property.
|
||||
|
|
|
@ -29,28 +29,28 @@ using NMock2;
|
|||
|
||||
namespace Nuclex.Support.Tracking {
|
||||
|
||||
/// <summary>Unit Test for the progression set class</summary>
|
||||
/// <summary>Unit Test for the waitable group class</summary>
|
||||
[TestFixture]
|
||||
public class SetProgressionTest {
|
||||
public class WaitableGroupTest {
|
||||
|
||||
#region interface ISetProgressionSubscriber
|
||||
#region interface IWaitableGroupSubscriber
|
||||
|
||||
/// <summary>Interface used to test the set progression.</summary>
|
||||
public interface ISetProgressionSubscriber {
|
||||
/// <summary>Interface used to test the set waitable.</summary>
|
||||
public interface IWaitableGroupSubscriber {
|
||||
|
||||
/// <summary>Called when the set progression's progress changes</summary>
|
||||
/// <param name="sender">Set progression whose progress has changed</param>
|
||||
/// <summary>Called when the set waitable's progress changes</summary>
|
||||
/// <param name="sender">Waitable group whose progress has changed</param>
|
||||
/// <param name="e">Contains the new progress achieved</param>
|
||||
void ProgressChanged(object sender, ProgressReportEventArgs e);
|
||||
|
||||
/// <summary>Called when the set progression has ended</summary>
|
||||
/// <param name="sender">Set progression that as ended</param>
|
||||
/// <summary>Called when the set waitable has ended</summary>
|
||||
/// <param name="sender">Waitable group that as ended</param>
|
||||
/// <param name="e">Not used</param>
|
||||
void Ended(object sender, EventArgs e);
|
||||
|
||||
}
|
||||
|
||||
#endregion // interface ISetProgressionSubscriber
|
||||
#endregion // interface IWaitableGroupSubscriber
|
||||
|
||||
#region class ProgressUpdateEventArgsMatcher
|
||||
|
||||
|
@ -93,21 +93,21 @@ namespace Nuclex.Support.Tracking {
|
|||
|
||||
#region class TestWaitable
|
||||
|
||||
/// <summary>Progression used for testing in this unit test</summary>
|
||||
/// <summary>Waitable used for testing in this unit test</summary>
|
||||
private class TestWaitable : Waitable, IProgressReporter {
|
||||
|
||||
/// <summary>will be triggered to report when progress has been achieved</summary>
|
||||
public event EventHandler<ProgressReportEventArgs> AsyncProgressChanged;
|
||||
|
||||
/// <summary>Changes the testing progression's indicated progress</summary>
|
||||
/// <summary>Changes the testing waitable's indicated progress</summary>
|
||||
/// <param name="progress">
|
||||
/// New progress to be reported by the testing progression
|
||||
/// New progress to be reported by the testing waitable
|
||||
/// </param>
|
||||
public void ChangeProgress(float progress) {
|
||||
OnAsyncProgressChanged(progress);
|
||||
}
|
||||
|
||||
/// <summary>Transitions the progression into the ended state</summary>
|
||||
/// <summary>Transitions the waitable into the ended state</summary>
|
||||
public void End() {
|
||||
OnAsyncEnded();
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ namespace Nuclex.Support.Tracking {
|
|||
/// <summary>Fires the progress update event</summary>
|
||||
/// <param name="progress">Progress to report (ranging from 0.0 to 1.0)</param>
|
||||
/// <remarks>
|
||||
/// Informs the observers of this progression about the achieved progress.
|
||||
/// Informs the observers of this waitable about the achieved progress.
|
||||
/// </remarks>
|
||||
protected virtual void OnAsyncProgressChanged(float progress) {
|
||||
OnAsyncProgressChanged(new ProgressReportEventArgs(progress));
|
||||
|
@ -124,10 +124,10 @@ namespace Nuclex.Support.Tracking {
|
|||
/// <summary>Fires the progress update event</summary>
|
||||
/// <param name="eventArguments">Progress to report (ranging from 0.0 to 1.0)</param>
|
||||
/// <remarks>
|
||||
/// Informs the observers of this progression about the achieved progress.
|
||||
/// Allows for classes derived from the Progression class to easily provide
|
||||
/// Informs the observers of this waitable about the achieved progress.
|
||||
/// Allows for classes derived from the Waitable class to easily provide
|
||||
/// a custom event arguments class that has been derived from the
|
||||
/// Progression's ProgressUpdateEventArgs class.
|
||||
/// Waitable's ProgressUpdateEventArgs class.
|
||||
/// </remarks>
|
||||
protected virtual void OnAsyncProgressChanged(ProgressReportEventArgs eventArguments) {
|
||||
EventHandler<ProgressReportEventArgs> copy = AsyncProgressChanged;
|
||||
|
@ -145,79 +145,79 @@ namespace Nuclex.Support.Tracking {
|
|||
this.mockery = new Mockery();
|
||||
}
|
||||
|
||||
/// <summary>Validates that the set progression properly sums the progress</summary>
|
||||
/// <summary>Validates that the set waitable properly sums the progress</summary>
|
||||
[Test]
|
||||
public void TestSummedProgress() {
|
||||
SetProgression<TestWaitable> testSetProgression =
|
||||
new SetProgression<TestWaitable>(
|
||||
WaitableGroup<TestWaitable> testWaitableGroup =
|
||||
new WaitableGroup<TestWaitable>(
|
||||
new TestWaitable[] { new TestWaitable(), new TestWaitable() }
|
||||
);
|
||||
|
||||
ISetProgressionSubscriber mockedSubscriber = mockSubscriber(testSetProgression);
|
||||
IWaitableGroupSubscriber mockedSubscriber = mockSubscriber(testWaitableGroup);
|
||||
|
||||
Expect.Once.On(mockedSubscriber).
|
||||
Method("ProgressChanged").
|
||||
With(
|
||||
new Matcher[] {
|
||||
new NMock2.Matchers.TypeMatcher(typeof(SetProgression<TestWaitable>)),
|
||||
new NMock2.Matchers.TypeMatcher(typeof(WaitableGroup<TestWaitable>)),
|
||||
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.25f))
|
||||
}
|
||||
);
|
||||
|
||||
testSetProgression.Children[0].Waitable.ChangeProgress(0.5f);
|
||||
testWaitableGroup.Children[0].Waitable.ChangeProgress(0.5f);
|
||||
|
||||
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||
}
|
||||
|
||||
/// <summary>Validates that the set progression respects the weights</summary>
|
||||
/// <summary>Validates that the waitable group respects the weights</summary>
|
||||
[Test]
|
||||
public void TestWeightedSummedProgress() {
|
||||
SetProgression<TestWaitable> testSetProgression =
|
||||
new SetProgression<TestWaitable>(
|
||||
WaitableGroup<TestWaitable> testWaitableGroup =
|
||||
new WaitableGroup<TestWaitable>(
|
||||
new WeightedWaitable<TestWaitable>[] {
|
||||
new WeightedWaitable<TestWaitable>(new TestWaitable(), 1.0f),
|
||||
new WeightedWaitable<TestWaitable>(new TestWaitable(), 2.0f)
|
||||
}
|
||||
);
|
||||
|
||||
ISetProgressionSubscriber mockedSubscriber = mockSubscriber(testSetProgression);
|
||||
IWaitableGroupSubscriber mockedSubscriber = mockSubscriber(testWaitableGroup);
|
||||
|
||||
Expect.Once.On(mockedSubscriber).
|
||||
Method("ProgressChanged").
|
||||
With(
|
||||
new Matcher[] {
|
||||
new NMock2.Matchers.TypeMatcher(typeof(SetProgression<TestWaitable>)),
|
||||
new NMock2.Matchers.TypeMatcher(typeof(WaitableGroup<TestWaitable>)),
|
||||
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.5f / 3.0f))
|
||||
}
|
||||
);
|
||||
|
||||
testSetProgression.Children[0].Waitable.ChangeProgress(0.5f);
|
||||
testWaitableGroup.Children[0].Waitable.ChangeProgress(0.5f);
|
||||
|
||||
Expect.Once.On(mockedSubscriber).
|
||||
Method("ProgressChanged").
|
||||
With(
|
||||
new Matcher[] {
|
||||
new NMock2.Matchers.TypeMatcher(typeof(SetProgression<TestWaitable>)),
|
||||
new NMock2.Matchers.TypeMatcher(typeof(WaitableGroup<TestWaitable>)),
|
||||
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.5f))
|
||||
}
|
||||
);
|
||||
|
||||
testSetProgression.Children[1].Waitable.ChangeProgress(0.5f);
|
||||
testWaitableGroup.Children[1].Waitable.ChangeProgress(0.5f);
|
||||
|
||||
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that the ended event is triggered when the last progression ends
|
||||
/// Validates that the ended event is triggered when the last waitable ends
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestEndedEvent() {
|
||||
SetProgression<TestWaitable> testSetProgression =
|
||||
new SetProgression<TestWaitable>(
|
||||
WaitableGroup<TestWaitable> testWaitableGroup =
|
||||
new WaitableGroup<TestWaitable>(
|
||||
new TestWaitable[] { new TestWaitable(), new TestWaitable() }
|
||||
);
|
||||
|
||||
ISetProgressionSubscriber mockedSubscriber = mockSubscriber(testSetProgression);
|
||||
IWaitableGroupSubscriber mockedSubscriber = mockSubscriber(testWaitableGroup);
|
||||
|
||||
Expect.Exactly(2).On(mockedSubscriber).
|
||||
Method("ProgressChanged").
|
||||
|
@ -227,21 +227,21 @@ namespace Nuclex.Support.Tracking {
|
|||
Method("Ended").
|
||||
WithAnyArguments();
|
||||
|
||||
testSetProgression.Children[0].Waitable.End();
|
||||
testSetProgression.Children[1].Waitable.End();
|
||||
testWaitableGroup.Children[0].Waitable.End();
|
||||
testWaitableGroup.Children[1].Waitable.End();
|
||||
|
||||
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||
}
|
||||
|
||||
/// <summary>Mocks a subscriber for the events of a progression</summary>
|
||||
/// <param name="progression">Progression to mock an event subscriber for</param>
|
||||
/// <summary>Mocks a subscriber for the events of a waitable</summary>
|
||||
/// <param name="waitable">Waitable to mock an event subscriber for</param>
|
||||
/// <returns>The mocked event subscriber</returns>
|
||||
private ISetProgressionSubscriber mockSubscriber(Waitable progression) {
|
||||
ISetProgressionSubscriber mockedSubscriber =
|
||||
this.mockery.NewMock<ISetProgressionSubscriber>();
|
||||
private IWaitableGroupSubscriber mockSubscriber(Waitable waitable) {
|
||||
IWaitableGroupSubscriber mockedSubscriber =
|
||||
this.mockery.NewMock<IWaitableGroupSubscriber>();
|
||||
|
||||
progression.AsyncEnded += new EventHandler(mockedSubscriber.Ended);
|
||||
(progression as IProgressReporter).AsyncProgressChanged +=
|
||||
waitable.AsyncEnded += new EventHandler(mockedSubscriber.Ended);
|
||||
(waitable as IProgressReporter).AsyncProgressChanged +=
|
||||
new EventHandler<ProgressReportEventArgs>(mockedSubscriber.ProgressChanged);
|
||||
|
||||
return mockedSubscriber;
|
|
@ -26,66 +26,66 @@ using Nuclex.Support.Collections;
|
|||
|
||||
namespace Nuclex.Support.Tracking {
|
||||
|
||||
/// <summary>Forms a single progression from a set of progressions</summary>
|
||||
/// <typeparam name="ProgressionType">Type of progressions to manage as a set</typeparam>
|
||||
public class SetProgression<ProgressionType> : Waitable, IDisposable, IProgressReporter
|
||||
where ProgressionType : Waitable {
|
||||
/// <summary>Forms a single waitable from a group of waitables</summary>
|
||||
/// <typeparam name="WaitableType">Type of waitables to manage as a set</typeparam>
|
||||
public class WaitableGroup<WaitableType> : Waitable, IDisposable, IProgressReporter
|
||||
where WaitableType : Waitable {
|
||||
|
||||
/// <summary>will be triggered to report when progress has been achieved</summary>
|
||||
public event EventHandler<ProgressReportEventArgs> AsyncProgressChanged;
|
||||
|
||||
/// <summary>Initializes a new set progression</summary>
|
||||
/// <param name="childs">Progressions to track with this set</param>
|
||||
/// <summary>Initializes a new waitable group</summary>
|
||||
/// <param name="childs">Waitables to track with this group</param>
|
||||
/// <remarks>
|
||||
/// Uses a default weighting factor of 1.0 for all progressions.
|
||||
/// Uses a default weighting factor of 1.0 for all waitables.
|
||||
/// </remarks>
|
||||
public SetProgression(IEnumerable<ProgressionType> childs)
|
||||
public WaitableGroup(IEnumerable<WaitableType> childs)
|
||||
: this() {
|
||||
|
||||
// Construct a WeightedProgression with the default weight for each
|
||||
// progression and wrap it in an ObservedProgression
|
||||
foreach(ProgressionType progression in childs) {
|
||||
// Construct a WeightedWaitable with the default weight for each
|
||||
// waitable and wrap it in an ObservedWaitable
|
||||
foreach(WaitableType waitable in childs) {
|
||||
this.children.Add(
|
||||
new ObservedWeightedWaitable<ProgressionType>(
|
||||
new WeightedWaitable<ProgressionType>(progression),
|
||||
new ObservedWeightedWaitable<ProgressionType>.ReportDelegate(asyncProgressUpdated),
|
||||
new ObservedWeightedWaitable<ProgressionType>.ReportDelegate(asyncEnded)
|
||||
new ObservedWeightedWaitable<WaitableType>(
|
||||
new WeightedWaitable<WaitableType>(waitable),
|
||||
new ObservedWeightedWaitable<WaitableType>.ReportDelegate(asyncProgressUpdated),
|
||||
new ObservedWeightedWaitable<WaitableType>.ReportDelegate(asyncChildEnded)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Since all progressions have a weight of 1.0, the total weight is
|
||||
// equal to the number of progressions in our list
|
||||
// Since all waitables have a weight of 1.0, the total weight is
|
||||
// equal to the number of waitables in our list
|
||||
this.totalWeight = (float)this.children.Count;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new set progression</summary>
|
||||
/// <param name="childs">Progressions to track with this set</param>
|
||||
public SetProgression(
|
||||
IEnumerable<WeightedWaitable<ProgressionType>> childs
|
||||
/// <summary>Initializes a new waitable group</summary>
|
||||
/// <param name="childs">Waitables to track with this group</param>
|
||||
public WaitableGroup(
|
||||
IEnumerable<WeightedWaitable<WaitableType>> childs
|
||||
)
|
||||
: this() {
|
||||
|
||||
// Construct an ObservedProgression around each of the WeightedProgressions
|
||||
foreach(WeightedWaitable<ProgressionType> progression in childs) {
|
||||
// Construct an ObservedWaitable around each of the WeightedWaitables
|
||||
foreach(WeightedWaitable<WaitableType> waitable in childs) {
|
||||
this.children.Add(
|
||||
new ObservedWeightedWaitable<ProgressionType>(
|
||||
progression,
|
||||
new ObservedWeightedWaitable<ProgressionType>.ReportDelegate(asyncProgressUpdated),
|
||||
new ObservedWeightedWaitable<ProgressionType>.ReportDelegate(asyncEnded)
|
||||
new ObservedWeightedWaitable<WaitableType>(
|
||||
waitable,
|
||||
new ObservedWeightedWaitable<WaitableType>.ReportDelegate(asyncProgressUpdated),
|
||||
new ObservedWeightedWaitable<WaitableType>.ReportDelegate(asyncChildEnded)
|
||||
)
|
||||
);
|
||||
|
||||
// Sum up the total weight
|
||||
this.totalWeight += progression.Weight;
|
||||
this.totalWeight += waitable.Weight;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>Performs common initialization for the public constructors</summary>
|
||||
private SetProgression() {
|
||||
this.children = new List<ObservedWeightedWaitable<ProgressionType>>();
|
||||
private WaitableGroup() {
|
||||
this.children = new List<ObservedWeightedWaitable<WaitableType>>();
|
||||
}
|
||||
|
||||
/// <summary>Immediately releases all resources owned by the object</summary>
|
||||
|
@ -93,8 +93,8 @@ namespace Nuclex.Support.Tracking {
|
|||
|
||||
if(this.children != null) {
|
||||
|
||||
// Dispose all the observed progressions, disconnecting the events from the
|
||||
// actual progressions so the GC can more easily collect this class
|
||||
// Dispose all the observed waitables, disconnecting the events from the
|
||||
// actual waitables so the GC can more easily collect this class
|
||||
for(int index = 0; index < this.children.Count; ++index)
|
||||
this.children[index].Dispose();
|
||||
|
||||
|
@ -105,18 +105,18 @@ namespace Nuclex.Support.Tracking {
|
|||
|
||||
}
|
||||
|
||||
/// <summary>Childs contained in the progression set</summary>
|
||||
public IList<WeightedWaitable<ProgressionType>> Children {
|
||||
/// <summary>Childs contained in the waitable set</summary>
|
||||
public IList<WeightedWaitable<WaitableType>> Children {
|
||||
get {
|
||||
|
||||
// The wrapper is constructed only when needed. Most of the time, users will
|
||||
// just create a SetProgression and monitor its progress without ever using
|
||||
// just create a waitable group and monitor its progress without ever using
|
||||
// the Childs collection.
|
||||
if(this.wrapper == null) {
|
||||
|
||||
// This doesn't need a lock because it's a stateless wrapper.
|
||||
// If it is constructed twice, then so be it, no problem at all.
|
||||
this.wrapper = new WeightedProgressionWrapperCollection<ProgressionType>(
|
||||
this.wrapper = new WeightedWaitableWrapperCollection<WaitableType>(
|
||||
this.children
|
||||
);
|
||||
|
||||
|
@ -130,7 +130,7 @@ namespace Nuclex.Support.Tracking {
|
|||
/// <summary>Fires the progress update event</summary>
|
||||
/// <param name="progress">Progress to report (ranging from 0.0 to 1.0)</param>
|
||||
/// <remarks>
|
||||
/// Informs the observers of this progression about the achieved progress.
|
||||
/// Informs the observers of this waitables about the achieved progress.
|
||||
/// </remarks>
|
||||
protected virtual void OnAsyncProgressChanged(float progress) {
|
||||
OnAsyncProgressChanged(new ProgressReportEventArgs(progress));
|
||||
|
@ -139,10 +139,10 @@ namespace Nuclex.Support.Tracking {
|
|||
/// <summary>Fires the progress update event</summary>
|
||||
/// <param name="eventArguments">Progress to report (ranging from 0.0 to 1.0)</param>
|
||||
/// <remarks>
|
||||
/// Informs the observers of this progression about the achieved progress.
|
||||
/// Allows for classes derived from the Progression class to easily provide
|
||||
/// Informs the observers of this waitable about the achieved progress.
|
||||
/// Allows for classes derived from the Waitable class to easily provide
|
||||
/// a custom event arguments class that has been derived from the
|
||||
/// Progression's ProgressUpdateEventArgs class.
|
||||
/// waitable's ProgressUpdateEventArgs class.
|
||||
/// </remarks>
|
||||
protected virtual void OnAsyncProgressChanged(ProgressReportEventArgs eventArguments) {
|
||||
EventHandler<ProgressReportEventArgs> copy = AsyncProgressChanged;
|
||||
|
@ -151,13 +151,13 @@ namespace Nuclex.Support.Tracking {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the progress of one of the observed progressions changes
|
||||
/// Called when the progress of one of the observed waitables changes
|
||||
/// </summary>
|
||||
private void asyncProgressUpdated() {
|
||||
float totalProgress = 0.0f;
|
||||
|
||||
// Calculate the sum of the progress reported by our child progressions,
|
||||
// scaled to the weight each progression has assigned to it.
|
||||
// Calculate the sum of the progress reported by our child waitables,
|
||||
// scaled to the weight each waitable has assigned to it.
|
||||
for(int index = 0; index < this.children.Count; ++index) {
|
||||
totalProgress +=
|
||||
this.children[index].Progress * this.children[index].WeightedWaitable.Weight;
|
||||
|
@ -172,29 +172,29 @@ namespace Nuclex.Support.Tracking {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when an observed progression ends
|
||||
/// Called when an observed waitable ends
|
||||
/// </summary>
|
||||
private void asyncEnded() {
|
||||
private void asyncChildEnded() {
|
||||
|
||||
// If there's still at least one progression going, don't report that
|
||||
// the SetProgression has finished yet.
|
||||
// If there's still at least one waitable going, don't report that
|
||||
// the waitable group has finished yet.
|
||||
for(int index = 0; index < this.children.Count; ++index)
|
||||
if(!this.children[index].WeightedWaitable.Waitable.Ended)
|
||||
return;
|
||||
|
||||
// All child progressions have ended, so the set has now ended as well
|
||||
// All child waitables have ended, so the set has now ended as well
|
||||
OnAsyncEnded();
|
||||
|
||||
}
|
||||
|
||||
/// <summary>Progressions being managed in the set</summary>
|
||||
private List<ObservedWeightedWaitable<ProgressionType>> children;
|
||||
/// <summary>Waitables being managed in the set</summary>
|
||||
private List<ObservedWeightedWaitable<WaitableType>> children;
|
||||
/// <summary>
|
||||
/// Wrapper collection for exposing the child progressions under the
|
||||
/// WeightedProgression interface
|
||||
/// Wrapper collection for exposing the child waitables under the
|
||||
/// WeightedWaitable interface
|
||||
/// </summary>
|
||||
private volatile WeightedProgressionWrapperCollection<ProgressionType> wrapper;
|
||||
/// <summary>Summed weight of all progressions in the set</summary>
|
||||
private volatile WeightedWaitableWrapperCollection<WaitableType> wrapper;
|
||||
/// <summary>Summed weight of all waitables in the set</summary>
|
||||
private float totalWeight;
|
||||
|
||||
}
|
|
@ -23,36 +23,36 @@ using System.Collections.Generic;
|
|||
|
||||
namespace Nuclex.Support.Tracking {
|
||||
|
||||
/// <summary>Progression with an associated weight for the total progress</summary>
|
||||
public class WeightedWaitable<ProgressionType> where ProgressionType : Waitable {
|
||||
/// <summary>Waitable with an associated weight for the total progress</summary>
|
||||
public class WeightedWaitable<WaitableType> where WaitableType : Waitable {
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new weighted progression with a default weight of 1.0
|
||||
/// Initializes a new weighted waitable with a default weight of 1.0
|
||||
/// </summary>
|
||||
/// <param name="progression">Progression whose progress to monitor</param>
|
||||
public WeightedWaitable(ProgressionType progression) : this(progression, 1.0f) { }
|
||||
/// <param name="waitable">Waitable whose progress to monitor</param>
|
||||
public WeightedWaitable(WaitableType waitable) : this(waitable, 1.0f) { }
|
||||
|
||||
/// <summary>Initializes a new weighted progression</summary>
|
||||
/// <param name="progression">Progression whose progress to monitor</param>
|
||||
/// <param name="weight">Weighting of the progression's progress</param>
|
||||
public WeightedWaitable(ProgressionType progression, float weight) {
|
||||
this.progression = progression;
|
||||
/// <summary>Initializes a new weighted waitable</summary>
|
||||
/// <param name="waitable">Waitable whose progress to monitor</param>
|
||||
/// <param name="weight">Weighting of the waitable's progress</param>
|
||||
public WeightedWaitable(WaitableType waitable, float weight) {
|
||||
this.waitable = waitable;
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
/// <summary>Progression being wrapped by this weighted progression</summary>
|
||||
public ProgressionType Waitable {
|
||||
get { return this.progression; }
|
||||
/// <summary>Waitable being wrapped by this weighted waitable</summary>
|
||||
public WaitableType Waitable {
|
||||
get { return this.waitable; }
|
||||
}
|
||||
|
||||
/// <summary>The contribution of this progression to the total progress</summary>
|
||||
/// <summary>The contribution of this waitable to the total progress</summary>
|
||||
public float Weight {
|
||||
get { return this.weight; }
|
||||
}
|
||||
|
||||
/// <summary>Progression whose progress we're tracking</summary>
|
||||
private ProgressionType progression;
|
||||
/// <summary>Weighting of this progression in the total progress</summary>
|
||||
/// <summary>Waitable whose progress we're tracking</summary>
|
||||
private WaitableType waitable;
|
||||
/// <summary>Weighting of this waitable in the total progress</summary>
|
||||
private float weight;
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user