Renamed ProgressUpdated event to ProgressChanged in order to conform with the status report naming convention; removed progress reporting from the Progression class; moved progress reporting into its own interface which is now optional to implement for any background task; renamed Progression to Waitable since it no longer has anything to do with progress; fixed unit tests

git-svn-id: file:///srv/devel/repo-conversion/nusu@64 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
Markus Ewald 2008-03-26 21:03:49 +00:00
parent 1161ef8f25
commit 7dbfc3c422
15 changed files with 321 additions and 160 deletions

View File

@ -147,9 +147,10 @@
<Compile Include="Source\Tracking\IdleStateEventArgs.cs" />
<Compile Include="Source\Tracking\Internal\ObservedWeightedProgression.cs" />
<Compile Include="Source\Tracking\Internal\WeightedProgressionWrapperCollection.cs" />
<Compile Include="Source\Tracking\IProgressReporter.cs" />
<Compile Include="Source\Tracking\IStatusReporter.cs" />
<Compile Include="Source\Tracking\Request.cs" />
<Compile Include="Source\Tracking\Progression.cs" />
<Compile Include="Source\Tracking\Waitable.cs" />
<Compile Include="Source\Tracking\ProgressionTracker.cs" />
<Compile Include="Source\Tracking\ProgressionTracker.Test.cs">
<DependentUpon>ProgressionTracker.cs</DependentUpon>

View File

@ -43,7 +43,7 @@ namespace Nuclex.Support.Scheduling {
/// <summary>Called when the queue operations's progress changes</summary>
/// <param name="sender">Queue operation whose progress has changed</param>
/// <param name="e">Contains the new progress achieved</param>
void ProgressUpdated(object sender, ProgressUpdateEventArgs e);
void ProgressChanged(object sender, ProgressReportEventArgs e);
/// <summary>Called when the queue operation has ended</summary>
/// <param name="sender">Queue operation that as ended</param>
@ -61,7 +61,7 @@ namespace Nuclex.Support.Scheduling {
/// <summary>Initializes a new ProgressUpdateEventArgsMatcher </summary>
/// <param name="expected">Expected progress update event arguments</param>
public ProgressUpdateEventArgsMatcher(ProgressUpdateEventArgs expected) {
public ProgressUpdateEventArgsMatcher(ProgressReportEventArgs expected) {
this.expected = expected;
}
@ -73,7 +73,7 @@ namespace Nuclex.Support.Scheduling {
/// True if the actual value matches the expected value; otherwise false
/// </returns>
public override bool Matches(object actualAsObject) {
ProgressUpdateEventArgs actual = (actualAsObject as ProgressUpdateEventArgs);
ProgressReportEventArgs actual = (actualAsObject as ProgressReportEventArgs);
if(actual == null)
return false;
@ -87,7 +87,7 @@ namespace Nuclex.Support.Scheduling {
}
/// <summary>Expected progress update event args value</summary>
private ProgressUpdateEventArgs expected;
private ProgressReportEventArgs expected;
}
@ -96,7 +96,10 @@ namespace Nuclex.Support.Scheduling {
#region class TestOperation
/// <summary>Progression used for testing in this unit test</summary>
private class TestOperation : Operation {
private class TestOperation : Operation, IProgressReporter {
/// <summary>will be triggered to report when progress has been achieved</summary>
public event EventHandler<ProgressReportEventArgs> AsyncProgressChanged;
/// <summary>Begins executing the operation. Yeah, sure :)</summary>
public override void Start() { }
@ -118,7 +121,7 @@ namespace Nuclex.Support.Scheduling {
/// New progress to be reported by the testing progression
/// </param>
public void ChangeProgress(float progress) {
OnAsyncProgressUpdated(progress);
OnAsyncProgressChanged(progress);
}
/// <summary>
@ -130,6 +133,29 @@ namespace Nuclex.Support.Scheduling {
throw this.exception;
}
/// <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.
/// </remarks>
protected virtual void OnAsyncProgressChanged(float progress) {
OnAsyncProgressChanged(new ProgressReportEventArgs(progress));
}
/// <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
/// a custom event arguments class that has been derived from the
/// Progression's ProgressUpdateEventArgs class.
/// </remarks>
protected virtual void OnAsyncProgressChanged(ProgressReportEventArgs eventArguments) {
EventHandler<ProgressReportEventArgs> copy = AsyncProgressChanged;
if(copy != null)
copy(this, eventArguments);
}
/// <summary>Exception that has occured in the background process</summary>
private volatile Exception exception;
@ -159,22 +185,22 @@ namespace Nuclex.Support.Scheduling {
testQueueOperation.Start();
Expect.Once.On(mockedSubscriber).
Method("ProgressUpdated").
Method("ProgressChanged").
With(
new Matcher[] {
new NMock2.Matchers.TypeMatcher(typeof(QueueOperation<TestOperation>)),
new ProgressUpdateEventArgsMatcher(new ProgressUpdateEventArgs(0.25f))
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.25f))
}
);
operation1.ChangeProgress(0.5f);
Expect.Once.On(mockedSubscriber).
Method("ProgressUpdated").
Method("ProgressChanged").
With(
new Matcher[] {
new NMock2.Matchers.TypeMatcher(typeof(QueueOperation<TestOperation>)),
new ProgressUpdateEventArgsMatcher(new ProgressUpdateEventArgs(0.5f))
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.5f))
}
);
@ -191,8 +217,8 @@ namespace Nuclex.Support.Scheduling {
this.mockery.NewMock<IQueueOperationSubscriber>();
operation.AsyncEnded += new EventHandler(mockedSubscriber.Ended);
operation.AsyncProgressUpdated +=
new EventHandler<ProgressUpdateEventArgs>(mockedSubscriber.ProgressUpdated);
(operation as IProgressReporter).AsyncProgressChanged +=
new EventHandler<ProgressReportEventArgs>(mockedSubscriber.ProgressChanged);
return mockedSubscriber;
}

View File

@ -32,15 +32,8 @@ namespace Nuclex.Support.Scheduling {
public class QueueOperation<OperationType> : Operation
where OperationType : Operation {
/// <summary>Initializes a new queue operation</summary>
private QueueOperation() {
this.asyncOperationEndedDelegate = new EventHandler(asyncOperationEnded);
this.asyncOperationProgressUpdatedDelegate = new EventHandler<ProgressUpdateEventArgs>(
asyncOperationProgressUpdated
);
this.children = new List<WeightedProgression<OperationType>>();
}
/// <summary>will be triggered to report when progress has been achieved</summary>
public event EventHandler<ProgressReportEventArgs> AsyncProgressUpdated;
/// <summary>Initializes a new queue operation with default weights</summary>
/// <param name="childs">Child operations to execute in this operation</param>
@ -74,6 +67,16 @@ namespace Nuclex.Support.Scheduling {
}
/// <summary>Initializes a new queue operation</summary>
private QueueOperation() {
this.asyncOperationEndedDelegate = new EventHandler(asyncOperationEnded);
this.asyncOperationProgressChangedDelegate = new EventHandler<ProgressReportEventArgs>(
asyncOperationProgressChanged
);
this.children = new List<WeightedProgression<OperationType>>();
}
/// <summary>Provides access to the child operations of this queue</summary>
public IList<WeightedProgression<OperationType>> Children {
get { return this.children; }
@ -93,6 +96,29 @@ namespace Nuclex.Support.Scheduling {
throw this.exception;
}
/// <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.
/// </remarks>
protected virtual void OnAsyncProgressChanged(float progress) {
OnAsyncProgressChanged(new ProgressReportEventArgs(progress));
}
/// <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
/// a custom event arguments class that has been derived from the
/// Progression's ProgressUpdateEventArgs class.
/// </remarks>
protected virtual void OnAsyncProgressChanged(ProgressReportEventArgs eventArguments) {
EventHandler<ProgressReportEventArgs> copy = AsyncProgressUpdated;
if(copy != null)
copy(this, eventArguments);
}
/// <summary>Prepares the current operation and calls its Begin() method</summary>
/// <remarks>
/// This subscribes the queue to the events of to the current operation
@ -102,7 +128,10 @@ namespace Nuclex.Support.Scheduling {
OperationType operation = this.children[this.currentOperationIndex].Progression;
operation.AsyncEnded += this.asyncOperationEndedDelegate;
operation.AsyncProgressUpdated += this.asyncOperationProgressUpdatedDelegate;
IProgressReporter progressReporter = operation as IProgressReporter;
if(progressReporter != null)
progressReporter.AsyncProgressChanged += this.asyncOperationProgressChangedDelegate;
operation.Start();
}
@ -118,7 +147,10 @@ namespace Nuclex.Support.Scheduling {
// Disconnect from the operation's events
operation.AsyncEnded -= this.asyncOperationEndedDelegate;
operation.AsyncProgressUpdated -= this.asyncOperationProgressUpdatedDelegate;
IProgressReporter progressReporter = operation as IProgressReporter;
if(progressReporter != null)
progressReporter.AsyncProgressChanged -= this.asyncOperationProgressChangedDelegate;
try {
operation.Join();
@ -127,7 +159,7 @@ namespace Nuclex.Support.Scheduling {
this.completedWeight += this.children[this.currentOperationIndex].Weight;
// Trigger another progress update
OnAsyncProgressUpdated(this.completedWeight / this.totalWeight);
OnAsyncProgressChanged(this.completedWeight / this.totalWeight);
}
catch(Exception exception) {
this.exception = exception;
@ -165,7 +197,7 @@ namespace Nuclex.Support.Scheduling {
/// <summary>Called when currently executing operation makes progress</summary>
/// <param name="sender">Operation that has achieved progress</param>
/// <param name="e">Not used</param>
private void asyncOperationProgressUpdated(object sender, ProgressUpdateEventArgs e) {
private void asyncOperationProgressChanged(object sender, ProgressReportEventArgs e) {
// Determine the completed weight of the currently executing operation
float currentOperationCompletedWeight =
@ -176,14 +208,14 @@ namespace Nuclex.Support.Scheduling {
(this.completedWeight + currentOperationCompletedWeight) / this.totalWeight;
// Done, we can send the actual progress to any event subscribers
OnAsyncProgressUpdated(progress);
OnAsyncProgressChanged(progress);
}
/// <summary>Delegate to the asyncOperationEnded() method</summary>
private EventHandler asyncOperationEndedDelegate;
/// <summary>Delegate to the asyncOperationProgressUpdated() method</summary>
private EventHandler<ProgressUpdateEventArgs> asyncOperationProgressUpdatedDelegate;
private EventHandler<ProgressReportEventArgs> asyncOperationProgressChangedDelegate;
/// <summary>Operations being managed in the queue</summary>
private List<WeightedProgression<OperationType>> children;
/// <summary>Summed weight of all operations in the queue</summary>

View File

@ -0,0 +1,34 @@
#region CPL License
/*
Nuclex Framework
Copyright (C) 2002-2008 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;
namespace Nuclex.Support.Tracking {
/// <summary>Interface for processes that report their progress</summary>
public interface IProgressReporter {
/// <summary>Triggered when the status of the process changes</summary>
event EventHandler<ProgressReportEventArgs> AsyncProgressChanged;
}
} // namespace Nuclex.Support.Tracking

View File

@ -27,7 +27,7 @@ namespace Nuclex.Support.Tracking {
public interface IStatusReporter {
/// <summary>Triggered when the status of the process changes</summary>
event EventHandler<StatusReportEventArgs> StatusChanged;
event EventHandler<StatusReportEventArgs> AsyncStatusChanged;
}

View File

@ -28,7 +28,7 @@ namespace Nuclex.Support.Tracking {
/// Type of the progression that is being observed
/// </typeparam>
internal class ObservedWeightedProgression<ProgressionType> : IDisposable
where ProgressionType : Progression {
where ProgressionType : Waitable {
/// <summary>Delegate for reporting progress updates</summary>
public delegate void ReportDelegate();
@ -69,8 +69,16 @@ namespace Nuclex.Support.Tracking {
if(weightedProgression.Progression.Ended) {
this.progress = 1.0f;
} else {
this.weightedProgression.Progression.AsyncProgressUpdated +=
new EventHandler<ProgressUpdateEventArgs>(asyncProgressUpdated);
this.progressReporter = this.weightedProgression.Progression as IProgressReporter;
if(this.progressReporter != null) {
this.asyncProgressChangedEventHandler = new EventHandler<ProgressReportEventArgs>(
asyncProgressChanged
);
this.progressReporter.AsyncProgressChanged +=
this.asyncProgressChangedEventHandler;
}
}
}
@ -119,7 +127,7 @@ namespace Nuclex.Support.Tracking {
/// <summary>Called when the progress of the observed progression changes</summary>
/// <param name="sender">Progression whose progress has changed</param>
/// <param name="e">Contains the updated progress</param>
private void asyncProgressUpdated(object sender, ProgressUpdateEventArgs e) {
private void asyncProgressChanged(object sender, ProgressReportEventArgs e) {
this.progress = e.Progress;
this.progressUpdateCallback();
}
@ -138,8 +146,12 @@ namespace Nuclex.Support.Tracking {
this.weightedProgression.Progression.AsyncEnded -=
new EventHandler(asyncEnded);
this.weightedProgression.Progression.AsyncProgressUpdated -=
new EventHandler<ProgressUpdateEventArgs>(asyncProgressUpdated);
if(this.progressReporter != null) {
this.progressReporter.AsyncProgressChanged -=
this.asyncProgressChangedEventHandler;
this.asyncProgressChangedEventHandler = null;
}
this.endedCallback = null;
this.progressUpdateCallback = null;
@ -150,6 +162,9 @@ namespace Nuclex.Support.Tracking {
}
private EventHandler<ProgressReportEventArgs> asyncProgressChangedEventHandler;
/// <summary>The observed progression's progress reporting interface</summary>
private IProgressReporter progressReporter;
/// <summary>The weighted progression that is being observed</summary>
private WeightedProgression<ProgressionType> weightedProgression;
/// <summary>Callback to invoke when the progress updates</summary>

View File

@ -51,7 +51,7 @@ namespace Nuclex.Support.Tracking {
TransformingReadOnlyCollection<
ObservedWeightedProgression<ProgressionType>, WeightedProgression<ProgressionType>
>
where ProgressionType : Progression {
where ProgressionType : Waitable {
/// <summary>Initializes a new weighted progression collection wrapper</summary>
/// <param name="items">Items to be exposed as weighted progressions</param>

View File

@ -24,11 +24,11 @@ using System.Collections.Generic;
namespace Nuclex.Support.Tracking {
/// <summary>Event arguments for a progress update notification</summary>
public class ProgressUpdateEventArgs : EventArgs {
public class ProgressReportEventArgs : EventArgs {
/// <summary>Initializes the progress update informations</summary>
/// <param name="progress">Achieved progress ranging from 0.0 to 1.0</param>
public ProgressUpdateEventArgs(float progress) {
public ProgressReportEventArgs(float progress) {
this.progress = progress;
}

View File

@ -41,7 +41,7 @@ namespace Nuclex.Support.Tracking {
/// <summary>Called when the progression tracker's progress changes</summary>
/// <param name="sender">Progression tracker whose progress has changed</param>
/// <param name="e">Contains the new progress achieved</param>
void ProgressUpdated(object sender, ProgressUpdateEventArgs e);
void ProgressUpdated(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>
@ -59,7 +59,7 @@ namespace Nuclex.Support.Tracking {
/// <summary>Initializes a new ProgressUpdateEventArgsMatcher </summary>
/// <param name="expected">Expected progress update event arguments</param>
public ProgressUpdateEventArgsMatcher(ProgressUpdateEventArgs expected) {
public ProgressUpdateEventArgsMatcher(ProgressReportEventArgs expected) {
this.expected = expected;
}
@ -71,7 +71,7 @@ namespace Nuclex.Support.Tracking {
/// True if the actual value matches the expected value; otherwise false
/// </returns>
public override bool Matches(object actualAsObject) {
ProgressUpdateEventArgs actual = (actualAsObject as ProgressUpdateEventArgs);
ProgressReportEventArgs actual = (actualAsObject as ProgressReportEventArgs);
if(actual == null)
return false;
@ -85,7 +85,7 @@ namespace Nuclex.Support.Tracking {
}
/// <summary>Expected progress update event args value</summary>
private ProgressUpdateEventArgs expected;
private ProgressReportEventArgs expected;
}
@ -94,14 +94,17 @@ namespace Nuclex.Support.Tracking {
#region class TestProgression
/// <summary>Progression used for testing in this unit test</summary>
private class TestProgression : Progression {
private class TestProgression : Waitable {
/// <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>
public void ChangeProgress(float progress) {
OnAsyncProgressUpdated(progress);
OnAsyncProgressChanged(progress);
}
/// <summary>Transitions the progression into the ended state</summary>
@ -109,6 +112,29 @@ namespace Nuclex.Support.Tracking {
OnAsyncEnded();
}
/// <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.
/// </remarks>
protected virtual void OnAsyncProgressChanged(float progress) {
OnAsyncProgressChanged(new ProgressReportEventArgs(progress));
}
/// <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
/// a custom event arguments class that has been derived from the
/// Progression's ProgressUpdateEventArgs class.
/// </remarks>
protected virtual void OnAsyncProgressChanged(ProgressReportEventArgs eventArguments) {
EventHandler<ProgressReportEventArgs> copy = AsyncProgressChanged;
if(copy != null)
copy(this, eventArguments);
}
}
#endregion // class TestProgression
@ -135,7 +161,7 @@ namespace Nuclex.Support.Tracking {
With(
new Matcher[] {
new NMock2.Matchers.TypeMatcher(typeof(ProgressionTracker)),
new ProgressUpdateEventArgsMatcher(new ProgressUpdateEventArgs(0.0f))
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.0f))
}
);
@ -149,7 +175,7 @@ namespace Nuclex.Support.Tracking {
With(
new Matcher[] {
new NMock2.Matchers.TypeMatcher(typeof(ProgressionTracker)),
new ProgressUpdateEventArgsMatcher(new ProgressUpdateEventArgs(0.25f))
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.25f))
}
);
@ -181,7 +207,7 @@ namespace Nuclex.Support.Tracking {
With(
new Matcher[] {
new NMock2.Matchers.TypeMatcher(typeof(ProgressionTracker)),
new ProgressUpdateEventArgsMatcher(new ProgressUpdateEventArgs(0.0f))
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.0f))
}
);
@ -195,7 +221,7 @@ namespace Nuclex.Support.Tracking {
With(
new Matcher[] {
new NMock2.Matchers.TypeMatcher(typeof(ProgressionTracker)),
new ProgressUpdateEventArgsMatcher(new ProgressUpdateEventArgs(0.25f))
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.25f))
}
);
@ -206,7 +232,7 @@ namespace Nuclex.Support.Tracking {
With(
new Matcher[] {
new NMock2.Matchers.TypeMatcher(typeof(ProgressionTracker)),
new ProgressUpdateEventArgsMatcher(new ProgressUpdateEventArgs(0.75f))
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.75f))
}
);
@ -220,7 +246,7 @@ namespace Nuclex.Support.Tracking {
With(
new Matcher[] {
new NMock2.Matchers.TypeMatcher(typeof(ProgressionTracker)),
new ProgressUpdateEventArgsMatcher(new ProgressUpdateEventArgs(1.0f))
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(1.0f))
}
);
@ -243,7 +269,7 @@ namespace Nuclex.Support.Tracking {
IProgressionTrackerSubscriber mockedSubscriber = mockSubscriber(tracker);
tracker.Track(Progression.EndedDummy);
tracker.Track(Waitable.EndedDummy);
this.mockery.VerifyAllExpectationsHaveBeenMet();
}
@ -267,7 +293,7 @@ namespace Nuclex.Support.Tracking {
With(
new Matcher[] {
new NMock2.Matchers.TypeMatcher(typeof(ProgressionTracker)),
new ProgressUpdateEventArgsMatcher(new ProgressUpdateEventArgs(0.0f))
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.0f))
}
);
@ -279,18 +305,18 @@ namespace Nuclex.Support.Tracking {
With(
new Matcher[] {
new NMock2.Matchers.TypeMatcher(typeof(ProgressionTracker)),
new ProgressUpdateEventArgsMatcher(new ProgressUpdateEventArgs(0.5f))
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.5f))
}
);
tracker.Track(Progression.EndedDummy);
tracker.Track(Waitable.EndedDummy);
Expect.Once.On(mockedSubscriber).
Method("ProgressUpdated").
With(
new Matcher[] {
new NMock2.Matchers.TypeMatcher(typeof(ProgressionTracker)),
new ProgressUpdateEventArgsMatcher(new ProgressUpdateEventArgs(1.0f))
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(1.0f))
}
);
@ -316,7 +342,7 @@ namespace Nuclex.Support.Tracking {
tracker.AsyncIdleStateChanged +=
(EventHandler<IdleStateEventArgs>)delegate(object sender, IdleStateEventArgs arguments) {
tracker.Track(Progression.EndedDummy);
tracker.Track(Waitable.EndedDummy);
};
test1.End();
@ -333,7 +359,7 @@ namespace Nuclex.Support.Tracking {
new EventHandler<IdleStateEventArgs>(mockedSubscriber.IdleStateChanged);
tracker.AsyncProgressUpdated +=
new EventHandler<ProgressUpdateEventArgs>(mockedSubscriber.ProgressUpdated);
new EventHandler<ProgressReportEventArgs>(mockedSubscriber.ProgressUpdated);
return mockedSubscriber;
}

View File

@ -50,7 +50,7 @@ namespace Nuclex.Support.Tracking {
/// the specified progression
/// </summary>
/// <param name="toMatch">Progression to match against</param>
public ProgressionMatcher(Progression toMatch) {
public ProgressionMatcher(Waitable toMatch) {
this.toMatch = toMatch;
}
@ -59,12 +59,12 @@ namespace Nuclex.Support.Tracking {
/// progression of the instance
/// </summary>
/// <param name="other">Progression to match to the comparison progression</param>
public bool Matches(ObservedWeightedProgression<Progression> other) {
public bool Matches(ObservedWeightedProgression<Waitable> other) {
return ReferenceEquals(other.WeightedProgression.Progression, this.toMatch);
}
/// <summary>Progression this instance compares against</summary>
private Progression toMatch;
private Waitable toMatch;
}
@ -80,18 +80,18 @@ namespace Nuclex.Support.Tracking {
public event EventHandler<IdleStateEventArgs> AsyncIdleStateChanged;
/// <summary>Triggered when the total progress has changed</summary>
public event EventHandler<ProgressUpdateEventArgs> AsyncProgressUpdated;
public event EventHandler<ProgressReportEventArgs> AsyncProgressUpdated;
/// <summary>Initializes a new progression tracker</summary>
public ProgressionTracker() {
this.trackedProgressions = new List<ObservedWeightedProgression<Progression>>();
this.trackedProgressions = new List<ObservedWeightedProgression<Waitable>>();
this.idle = true;
this.asyncEndedDelegate =
new ObservedWeightedProgression<Progression>.ReportDelegate(asyncEnded);
new ObservedWeightedProgression<Waitable>.ReportDelegate(asyncEnded);
this.asyncProgressUpdatedDelegate =
new ObservedWeightedProgression<Progression>.ReportDelegate(asyncProgressUpdated);
new ObservedWeightedProgression<Waitable>.ReportDelegate(asyncProgressUpdated);
}
@ -115,14 +115,14 @@ namespace Nuclex.Support.Tracking {
/// <summary>Begins tracking the specified progression</summary>
/// <param name="progression">Progression to be tracked</param>
public void Track(Progression progression) {
public void Track(Waitable progression) {
Track(progression, 1.0f);
}
/// <summary>Begins tracking the specified progression</summary>
/// <param name="progression">Progression to be tracked</param>
/// <param name="weight">Weight to assign to this progression</param>
public void Track(Progression progression, float weight) {
public void Track(Waitable progression, float weight) {
// Add the new progression into the tracking list. This has to be done
// inside a lock to prevent issues with the progressUpdate callback, which could
@ -150,8 +150,8 @@ namespace Nuclex.Support.Tracking {
// receive them. The lock eliminates the risk of processing a progress update
// before the progression has been added to the tracked progressions list.
this.trackedProgressions.Add(
new ObservedWeightedProgression<Progression>(
new WeightedProgression<Progression>(progression, weight),
new ObservedWeightedProgression<Waitable>(
new WeightedProgression<Waitable>(progression, weight),
this.asyncProgressUpdatedDelegate,
this.asyncEndedDelegate
)
@ -167,9 +167,9 @@ namespace Nuclex.Support.Tracking {
// Construct a new progression observer and add the progression to our
// list of tracked progressions.
ObservedWeightedProgression<Progression> observedProgression =
new ObservedWeightedProgression<Progression>(
new WeightedProgression<Progression>(progression, weight),
ObservedWeightedProgression<Waitable> observedProgression =
new ObservedWeightedProgression<Waitable>(
new WeightedProgression<Waitable>(progression, weight),
this.asyncProgressUpdatedDelegate,
this.asyncEndedDelegate
);
@ -202,12 +202,12 @@ namespace Nuclex.Support.Tracking {
/// <summary>Stops tracking the specified progression</summary>
/// <param name="progression">Progression to stop tracking of</param>
public void Untrack(Progression progression) {
public void Untrack(Waitable progression) {
lock(this.trackedProgressions) {
// Locate the object to be untracked in our collection
int removeIndex = this.trackedProgressions.FindIndex(
new Predicate<ObservedWeightedProgression<Progression>>(
new Predicate<ObservedWeightedProgression<Waitable>>(
new ProgressionMatcher(progression).Matches
)
);
@ -216,7 +216,7 @@ namespace Nuclex.Support.Tracking {
// Remove and dispose the progression the user wants to untrack
{
ObservedWeightedProgression<Progression> wrappedProgression =
ObservedWeightedProgression<Waitable> wrappedProgression =
this.trackedProgressions[removeIndex];
this.trackedProgressions.RemoveAt(removeIndex);
@ -266,9 +266,9 @@ namespace Nuclex.Support.Tracking {
/// <summary>Fires the AsyncProgressUpdated event</summary>
/// <param name="progress">New progress to report</param>
protected virtual void OnAsyncProgressUpdated(float progress) {
EventHandler<ProgressUpdateEventArgs> copy = AsyncProgressUpdated;
EventHandler<ProgressReportEventArgs> copy = AsyncProgressUpdated;
if(copy != null)
copy(this, new ProgressUpdateEventArgs(progress));
copy(this, new ProgressReportEventArgs(progress));
}
/// <summary>Recalculates the total progress of the tracker</summary>
@ -351,11 +351,11 @@ namespace Nuclex.Support.Tracking {
/// <summary>Total weight of all progressions being tracked</summary>
private volatile float totalWeight;
/// <summary>Progressions being tracked by this tracker</summary>
private List<ObservedWeightedProgression<Progression>> trackedProgressions;
private List<ObservedWeightedProgression<Waitable>> trackedProgressions;
/// <summary>Delegate for the asyncEnded() method</summary>
private ObservedWeightedProgression<Progression>.ReportDelegate asyncEndedDelegate;
private ObservedWeightedProgression<Waitable>.ReportDelegate asyncEndedDelegate;
/// <summary>Delegate for the asyncProgressUpdated() method</summary>
private ObservedWeightedProgression<Progression>.ReportDelegate asyncProgressUpdatedDelegate;
private ObservedWeightedProgression<Waitable>.ReportDelegate asyncProgressUpdatedDelegate;
}

View File

@ -37,7 +37,7 @@ namespace Nuclex.Support.Tracking {
/// OnAsyncEnded(), no matter what the outcome of your background operation is.
/// </para>
/// </remarks>
public abstract class Request : Progression {
public abstract class Request : Waitable {
#region class EndedDummyRequest

View File

@ -41,7 +41,7 @@ namespace Nuclex.Support.Tracking {
/// <summary>Called when the set progression's progress changes</summary>
/// <param name="sender">Set progression whose progress has changed</param>
/// <param name="e">Contains the new progress achieved</param>
void ProgressUpdated(object sender, ProgressUpdateEventArgs e);
void ProgressChanged(object sender, ProgressReportEventArgs e);
/// <summary>Called when the set progression has ended</summary>
/// <param name="sender">Set progression that as ended</param>
@ -59,7 +59,7 @@ namespace Nuclex.Support.Tracking {
/// <summary>Initializes a new ProgressUpdateEventArgsMatcher </summary>
/// <param name="expected">Expected progress update event arguments</param>
public ProgressUpdateEventArgsMatcher(ProgressUpdateEventArgs expected) {
public ProgressUpdateEventArgsMatcher(ProgressReportEventArgs expected) {
this.expected = expected;
}
@ -71,7 +71,7 @@ namespace Nuclex.Support.Tracking {
/// True if the actual value matches the expected value; otherwise false
/// </returns>
public override bool Matches(object actualAsObject) {
ProgressUpdateEventArgs actual = (actualAsObject as ProgressUpdateEventArgs);
ProgressReportEventArgs actual = (actualAsObject as ProgressReportEventArgs);
if(actual == null)
return false;
@ -85,23 +85,26 @@ namespace Nuclex.Support.Tracking {
}
/// <summary>Expected progress update event args value</summary>
private ProgressUpdateEventArgs expected;
private ProgressReportEventArgs expected;
}
#endregion // class ProgressUpdateEventArgsMatcher
#region class TestProgression
#region class TestWaitable
/// <summary>Progression used for testing in this unit test</summary>
private class TestProgression : Progression {
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>
public void ChangeProgress(float progress) {
OnAsyncProgressUpdated(progress);
OnAsyncProgressChanged(progress);
}
/// <summary>Transitions the progression into the ended state</summary>
@ -109,9 +112,32 @@ namespace Nuclex.Support.Tracking {
OnAsyncEnded();
}
/// <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.
/// </remarks>
protected virtual void OnAsyncProgressChanged(float progress) {
OnAsyncProgressChanged(new ProgressReportEventArgs(progress));
}
/// <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
/// a custom event arguments class that has been derived from the
/// Progression's ProgressUpdateEventArgs class.
/// </remarks>
protected virtual void OnAsyncProgressChanged(ProgressReportEventArgs eventArguments) {
EventHandler<ProgressReportEventArgs> copy = AsyncProgressChanged;
if(copy != null)
copy(this, eventArguments);
}
}
#endregion // class TestProgression
#endregion // class TestWaitable
/// <summary>Initialization routine executed before each test is run</summary>
[SetUp]
@ -122,19 +148,19 @@ namespace Nuclex.Support.Tracking {
/// <summary>Validates that the set progression properly sums the progress</summary>
[Test]
public void TestSummedProgress() {
SetProgression<TestProgression> testSetProgression =
new SetProgression<TestProgression>(
new TestProgression[] { new TestProgression(), new TestProgression() }
SetProgression<TestWaitable> testSetProgression =
new SetProgression<TestWaitable>(
new TestWaitable[] { new TestWaitable(), new TestWaitable() }
);
ISetProgressionSubscriber mockedSubscriber = mockSubscriber(testSetProgression);
Expect.Once.On(mockedSubscriber).
Method("ProgressUpdated").
Method("ProgressChanged").
With(
new Matcher[] {
new NMock2.Matchers.TypeMatcher(typeof(SetProgression<TestProgression>)),
new ProgressUpdateEventArgsMatcher(new ProgressUpdateEventArgs(0.25f))
new NMock2.Matchers.TypeMatcher(typeof(SetProgression<TestWaitable>)),
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.25f))
}
);
@ -146,33 +172,33 @@ namespace Nuclex.Support.Tracking {
/// <summary>Validates that the set progression respects the weights</summary>
[Test]
public void TestWeightedSummedProgress() {
SetProgression<TestProgression> testSetProgression =
new SetProgression<TestProgression>(
new WeightedProgression<TestProgression>[] {
new WeightedProgression<TestProgression>(new TestProgression(), 1.0f),
new WeightedProgression<TestProgression>(new TestProgression(), 2.0f)
SetProgression<TestWaitable> testSetProgression =
new SetProgression<TestWaitable>(
new WeightedProgression<TestWaitable>[] {
new WeightedProgression<TestWaitable>(new TestWaitable(), 1.0f),
new WeightedProgression<TestWaitable>(new TestWaitable(), 2.0f)
}
);
ISetProgressionSubscriber mockedSubscriber = mockSubscriber(testSetProgression);
Expect.Once.On(mockedSubscriber).
Method("ProgressUpdated").
Method("ProgressChanged").
With(
new Matcher[] {
new NMock2.Matchers.TypeMatcher(typeof(SetProgression<TestProgression>)),
new ProgressUpdateEventArgsMatcher(new ProgressUpdateEventArgs(0.5f / 3.0f))
new NMock2.Matchers.TypeMatcher(typeof(SetProgression<TestWaitable>)),
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.5f / 3.0f))
}
);
testSetProgression.Children[0].Progression.ChangeProgress(0.5f);
Expect.Once.On(mockedSubscriber).
Method("ProgressUpdated").
Method("ProgressChanged").
With(
new Matcher[] {
new NMock2.Matchers.TypeMatcher(typeof(SetProgression<TestProgression>)),
new ProgressUpdateEventArgsMatcher(new ProgressUpdateEventArgs(0.5f))
new NMock2.Matchers.TypeMatcher(typeof(SetProgression<TestWaitable>)),
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.5f))
}
);
@ -186,9 +212,9 @@ namespace Nuclex.Support.Tracking {
/// </summary>
[Test]
public void TestEndedEvent() {
SetProgression<TestProgression> testSetProgression =
new SetProgression<TestProgression>(
new TestProgression[] { new TestProgression(), new TestProgression() }
SetProgression<TestWaitable> testSetProgression =
new SetProgression<TestWaitable>(
new TestWaitable[] { new TestWaitable(), new TestWaitable() }
);
ISetProgressionSubscriber mockedSubscriber = mockSubscriber(testSetProgression);
@ -210,13 +236,13 @@ namespace Nuclex.Support.Tracking {
/// <summary>Mocks a subscriber for the events of a progression</summary>
/// <param name="progression">Progression to mock an event subscriber for</param>
/// <returns>The mocked event subscriber</returns>
private ISetProgressionSubscriber mockSubscriber(Progression progression) {
private ISetProgressionSubscriber mockSubscriber(Waitable progression) {
ISetProgressionSubscriber mockedSubscriber =
this.mockery.NewMock<ISetProgressionSubscriber>();
progression.AsyncEnded += new EventHandler(mockedSubscriber.Ended);
progression.AsyncProgressUpdated +=
new EventHandler<ProgressUpdateEventArgs>(mockedSubscriber.ProgressUpdated);
(progression as IProgressReporter).AsyncProgressChanged +=
new EventHandler<ProgressReportEventArgs>(mockedSubscriber.ProgressChanged);
return mockedSubscriber;
}

View File

@ -28,13 +28,11 @@ 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> : Progression, IDisposable
where ProgressionType : Progression {
public class SetProgression<ProgressionType> : Waitable, IDisposable, IProgressReporter
where ProgressionType : Waitable {
/// <summary>Performs common initialization for the public constructors</summary>
private SetProgression() {
this.children = new List<ObservedWeightedProgression<ProgressionType>>();
}
/// <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>
@ -85,6 +83,11 @@ namespace Nuclex.Support.Tracking {
}
/// <summary>Performs common initialization for the public constructors</summary>
private SetProgression() {
this.children = new List<ObservedWeightedProgression<ProgressionType>>();
}
/// <summary>Immediately releases all resources owned by the object</summary>
public void Dispose() {
@ -124,6 +127,29 @@ 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.
/// </remarks>
protected virtual void OnAsyncProgressChanged(float progress) {
OnAsyncProgressChanged(new ProgressReportEventArgs(progress));
}
/// <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
/// a custom event arguments class that has been derived from the
/// Progression's ProgressUpdateEventArgs class.
/// </remarks>
protected virtual void OnAsyncProgressChanged(ProgressReportEventArgs eventArguments) {
EventHandler<ProgressReportEventArgs> copy = AsyncProgressChanged;
if(copy != null)
copy(this, eventArguments);
}
/// <summary>
/// Called when the progress of one of the observed progressions changes
/// </summary>
@ -142,7 +168,7 @@ namespace Nuclex.Support.Tracking {
totalProgress /= this.totalWeight;
// Send out the progress update
OnAsyncProgressUpdated(totalProgress);
OnAsyncProgressChanged(totalProgress);
}
/// <summary>

View File

@ -23,33 +23,34 @@ using System.Threading;
namespace Nuclex.Support.Tracking {
/// <summary>Base class for actions that give an indication of their progress</summary>
/// <summary>Base class for actions on which that give an indication of their progress</summary>
/// <remarks>
/// <para>
/// By encapsulating long-running operations which will ideally be running in
/// a background thread in a class that's derived from Progression you can wait
/// for the completion of the operation and receive feedback on the achieved
/// progress. This is useful for displaying a progress bar, loading screen or
/// some other means of entertaining the user while he waits for the operation to
/// complete. It is also possible to register callbacks which will be fired once
/// the progression has ended.
/// a background thread in a class that's derived from <see cref="Waitable" />
/// you can wait for the completion of the operation and optionally even receive
/// feedback on the achieved progress. This is useful for displaying a progress
/// bar, loading screen or some other means of entertaining the user while he
/// waits for the task to complete.
/// </para>
/// <para>
/// This class deliberately does not provide an Execute() method or anything similar
/// to clearly seperate the initiation of an operation from just monitoring it.
/// By omitting an Execute() method, it also becomes possible to construct a
/// progression just-in-time when it is explicitely asked for.
/// You can register callbacks which will be fired once the <see cref="Waitable" />
/// task has completed. This class deliberately does not provide an Execute()
/// method or anything similar to clearly seperate the initiation of an operation
/// from just monitoring it. By omitting an Execute() method, it also becomes
/// possible to construct a progression just-in-time when it is explicitely being
/// asked for.
/// </para>
/// </remarks>
public abstract class Progression {
public abstract class Waitable {
#region class EndedDummyProgression
/// <summary>Dummy progression which always is in the 'ended' state</summary>
private class EndedDummyProgression : Progression {
private class EndedDummyWaitable : Waitable {
/// <summary>Initializes a new ended dummy progression</summary>
public EndedDummyProgression() {
public EndedDummyWaitable() {
OnAsyncEnded();
}
@ -63,10 +64,7 @@ namespace Nuclex.Support.Tracking {
/// when a progression that's lazily created is accessed after the original
/// operation has ended already.
/// </remarks>
public static readonly Progression EndedDummy = new EndedDummyProgression();
/// <summary>will be triggered to report when progress has been achieved</summary>
public event EventHandler<ProgressUpdateEventArgs> AsyncProgressUpdated;
public static readonly Waitable EndedDummy = new EndedDummyWaitable();
/// <summary>Will be triggered when the progression has ended</summary>
public event EventHandler AsyncEnded;
@ -102,29 +100,6 @@ 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.
/// </remarks>
protected virtual void OnAsyncProgressUpdated(float progress) {
OnAsyncProgressUpdated(new ProgressUpdateEventArgs(progress));
}
/// <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
/// a custom event arguments class that has been derived from the
/// Progression's ProgressUpdateEventArgs class.
/// </remarks>
protected virtual void OnAsyncProgressUpdated(ProgressUpdateEventArgs eventArguments) {
EventHandler<ProgressUpdateEventArgs> copy = AsyncProgressUpdated;
if(copy != null)
copy(this, eventArguments);
}
/// <summary>Fires the AsyncEnded event</summary>
/// <remarks>
/// <para>

View File

@ -24,7 +24,7 @@ using System.Collections.Generic;
namespace Nuclex.Support.Tracking {
/// <summary>Progression with an associated weight for the total progress</summary>
public class WeightedProgression<ProgressionType> where ProgressionType : Progression {
public class WeightedProgression<ProgressionType> where ProgressionType : Waitable {
/// <summary>
/// Initializes a new weighted progression with a default weight of 1.0