Click here to Skip to main content
15,894,180 members
Articles / Desktop Programming / WPF

Catel - Part 4 of n: Unit testing with Catel

Rate me:
Please Sign up or sign in to vote.
4.55/5 (10 votes)
28 Jan 2011CPOL11 min read 49.1K   572   11  
This article explains how to write unit tests for MVVM using Catel.
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="RetrySafetyCounter.cs" company="Catel development team">
//   Copyright (c) 2008 - 2011 Catel development team. All rights reserved.
// </copyright>
// <summary>
//   Delegate to execute the work by the <see cref="RetrySafetyCounter" />.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

using System;
using System.Threading;

namespace Catel
{
	#region Delegates
	/// <summary>
	/// Delegate to execute the work by the <see cref="RetrySafetyCounter"/>.
	/// </summary>
	/// <returns><c>true</c> if successful; otherwise <c>false</c>.</returns>
	public delegate bool DoWorkDelegate();

	/// <summary>
	/// Delegate to check whether work is completed by the <see cref="RetrySafetyCounter"/>.
	/// </summary>
    /// <returns><c>true</c> if the work is completed; otherwise <c>false</c>.</returns>
	public delegate bool IsWorkCompletedDelegate();
	#endregion

	/// <summary>
	/// Class to use when an action should be retried for a specific amount of time, and between the retries, the thread must
	/// sleep for a specific amount of time.
	/// </summary>
	/// <example>
	/// <code>
    /// int safetyCount = 25;
    /// bool isSucceeded = false;
    /// while ((isSucceeded != true) &amp;&amp; (safetyCount > 0))
    /// {
    ///     if (PerformAction())
    ///     {
    ///         isSucceeded = true;
    ///     }
    /// 
    ///     safetyCount--;
    /// }
    /// </code>
	/// </example>
	public class RetrySafetyCounter
	{
		#region Variables
		#endregion

		#region Constructor & destructor
		/// <summary>
		/// Initializes a new instance of the <see cref="RetrySafetyCounter"/> class with a default of 25 retries and
		/// a sleep time of 50 milliseconds.
		/// </summary>
		/// <param name="doWorkDelegate">The delegate that actually executes the work that should be done.</param>
		/// <param name="isWorkCompletedDelegate">The delegate that checks whether the work is successful.</param>
		/// <exception cref="ArgumentNullException">when <paramref name="doWorkDelegate"/> is <c>null</c>.</exception>
		/// <exception cref="ArgumentNullException">when <paramref name="isWorkCompletedDelegate"/> is <c>null</c>.</exception>
		public RetrySafetyCounter(DoWorkDelegate doWorkDelegate, IsWorkCompletedDelegate isWorkCompletedDelegate)
			: this(doWorkDelegate, isWorkCompletedDelegate, 25)
		{
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="RetrySafetyCounter"/> class with a sleep time of 50 milliseconds.
		/// </summary>
		/// <param name="doWorkDelegate">The delegate that actually executes the work that should be done.</param>
		/// <param name="isWorkCompletedDelegate">The delegate that checks whether the work is successful.</param>
		/// <param name="maximumNumberOfRetries">The number of retries.</param>
		/// <exception cref="ArgumentNullException">when <paramref name="doWorkDelegate"/> is <c>null</c>.</exception>
		/// <exception cref="ArgumentNullException">when <paramref name="isWorkCompletedDelegate"/> is <c>null</c>.</exception>
		public RetrySafetyCounter(DoWorkDelegate doWorkDelegate, IsWorkCompletedDelegate isWorkCompletedDelegate, int maximumNumberOfRetries)
			: this(doWorkDelegate, isWorkCompletedDelegate, maximumNumberOfRetries, 50)
		{
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="RetrySafetyCounter"/> class.
		/// </summary>
		/// <param name="doWorkDelegate">The delegate that actually executes the work that should be done.</param>
		/// <param name="isWorkCompletedDelegate">The delegate that checks whether the work is successful.</param>
		/// <param name="maximumNumberOfRetries">The number of retries.</param>
		/// <param name="millisecondsToSleep">The milliseconds to sleep.</param>
		/// <exception cref="ArgumentNullException">when <paramref name="doWorkDelegate"/> is <c>null</c>.</exception>
		/// <exception cref="ArgumentNullException">when <paramref name="isWorkCompletedDelegate"/> is <c>null</c>.</exception>
		public RetrySafetyCounter(DoWorkDelegate doWorkDelegate, IsWorkCompletedDelegate isWorkCompletedDelegate,
			int maximumNumberOfRetries, int millisecondsToSleep)
		{
			if (doWorkDelegate == null)
			{
				throw new ArgumentNullException("doWorkDelegate");
			}

			if (isWorkCompletedDelegate == null)
			{
				throw new ArgumentNullException("isWorkCompletedDelegate");
			}

			DoWorkDelegate = doWorkDelegate;
			IsWorkCompletedDelegate = isWorkCompletedDelegate;
			MaximumNumberOfRetries = maximumNumberOfRetries;
			MillisecondsToSleep = millisecondsToSleep;

			Reset();
		}
		#endregion

		#region Properties
		/// <summary>
		/// Gets or sets the delegate that actually executes the work that should be done.
		/// </summary>
		/// <value>The delegate.</value>
		private DoWorkDelegate DoWorkDelegate { get; set; }

		/// <summary>
		/// Gets or sets the delegate that checks whether the work is successful.
		/// </summary>
		/// <value>The delegate.</value>
		private IsWorkCompletedDelegate IsWorkCompletedDelegate { get; set; }

		/// <summary>
		/// Gets a value indicating whether this <see cref="RetrySafetyCounter"/> is successful.
		/// </summary>
		/// <value><c>true</c> if successful; otherwise, <c>false</c>.</value>
		public bool IsSuccessful { get; private set; }

		/// <summary>
		/// Gets or sets the maximum number of retries.
		/// </summary>
		/// <value>The maximum number of retries.</value>
		public int MaximumNumberOfRetries { get; set; }

		/// <summary>
		/// Gets the number of retries.
		/// </summary>
		/// <value>The number of retries.</value>
		public int NumberOfRetries { get; private set; }

		/// <summary>
		/// Gets the number of retries left.
		/// </summary>
		/// <value>The number of retries left.</value>
		public int NumberOfRetriesLeft { get; private set; }

		/// <summary>
		/// Gets or sets the milliseconds to sleep.
		/// </summary>
		/// <value>The milliseconds to sleep.</value>
		public int MillisecondsToSleep { get; set; }
		#endregion

		#region Methods

		/// <summary>
		/// Executes the work that has to be performed in the while loop. If the <see cref="DoWorkDelegate"/>
		/// returns <c>false</c> at any moment, the loop will be exited, even when number of retries left has not yet reached
		/// zero.
		/// </summary>
		/// <returns><c>true</c> if succeeded; otherwise <c>false</c>.</returns>
		public bool DoWork()
		{
			IsSuccessful = false;

			while (NumberOfRetriesLeft > 0)
			{
				NumberOfRetriesLeft--;
				NumberOfRetries++;

				if (!DoWorkDelegate())
				{
					break;
				}

				if (IsWorkCompletedDelegate())
				{
					IsSuccessful = true;
					break;
				}

				if (MillisecondsToSleep > 0)
				{
					Thread.Sleep(MillisecondsToSleep);
				}
			}

			// A negative number would be dumb
			if (NumberOfRetriesLeft < 0)
			{
				NumberOfRetriesLeft = 0;
			}

			return IsSuccessful;
		}

		/// <summary>
		/// Resets the counter.
		/// </summary>
		public void Reset()
		{
			IsSuccessful = false;
			NumberOfRetriesLeft = MaximumNumberOfRetries;
			NumberOfRetries = 0;
		}
		#endregion
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Netherlands Netherlands
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions