Created
April 14, 2015 12:41
-
-
Save davidwhitney/5fb59a6cfcebb565afac to your computer and use it in GitHub Desktop.
Retry example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using NUnit.Framework; | |
namespace ClassLibrary1 | |
{ | |
[TestFixture] | |
public class Class1 | |
{ | |
[Test] | |
public void Try_GivenPassingThing_CompletesSuccessfully() | |
{ | |
var fakeHttp = new FakeHttp("data here"); | |
var output = Try.To(() => fakeHttp.GetHttpData()).Now(); | |
Assert.That(output, Is.EqualTo("data here")); | |
} | |
[Test] | |
public void Try_GivenFailingCall_RetriesCall() | |
{ | |
var fakeHttp = new FakeHttp("data here", failCount: 1); | |
var tryWrapper = Try.To(() => fakeHttp.GetHttpData()); | |
var output = tryWrapper.Now(); | |
Assert.That(tryWrapper.Failures, Is.EqualTo(1)); | |
Assert.That(output, Is.EqualTo("data here")); | |
} | |
[Test] | |
public void Try_GivenFailingCall_DoesNotTryForever() | |
{ | |
var tryWrapper = Try.To(() => | |
{ | |
throw new Exception("Haha, always throws!"); | |
return "hi"; | |
}); | |
Assert.Catch(() => tryWrapper.MaxAttempts(10).Now()); | |
Assert.That(tryWrapper.Failures, Is.EqualTo(10)); | |
} | |
[Test] | |
public void Try_NoMaxAttemptSet_DefaultsTo100Attempts() | |
{ | |
var tryWrapper = Try.To(() => | |
{ | |
throw new Exception("Haha, always throws!"); | |
return "hi"; | |
}); | |
Assert.Catch(() => tryWrapper.Now()); | |
Assert.That(tryWrapper.Failures, Is.EqualTo(100)); | |
} | |
[Test] | |
public void Try_NeverWorks_ThrowsAggregateException() | |
{ | |
var tryWrapper = Try.To(() => | |
{ | |
throw new Exception("Haha, always throws!"); | |
return "hi"; | |
}); | |
Assert.Throws<AggregateException>(() => tryWrapper.Now()); | |
Assert.That(tryWrapper.Failures, Is.EqualTo(100)); | |
} | |
[TestCase(1, 5)] | |
[TestCase(2, 10)] | |
[TestCase(3, 20)] | |
public void Try_FailsOnce_DelayedBy5Seconds(int failCount, int delaySeconds) | |
{ | |
var fakeHttp = new FakeHttp("data here", failCount: failCount); | |
var tryWrapper = Try.To(() => fakeHttp.GetHttpData()); | |
var output = tryWrapper.Now(); | |
Assert.That(tryWrapper.Delays.Count, Is.EqualTo(failCount)); | |
Assert.That(tryWrapper.Delays.Last(), Is.EqualTo(new TimeSpan(0, 0, 0, delaySeconds))); | |
} | |
[Test] | |
public void Try_KnownException_ThrowsRightAway() | |
{ | |
var tryWrapper = Try.To(() => | |
{ | |
throw new ArgumentException("Haha, always throws!"); | |
return "hi"; | |
}); | |
tryWrapper.Throw<ArgumentException>(); | |
var ex = Assert.Throws<ArgumentException>(() => tryWrapper.Now()); | |
Assert.That(ex.Message, Is.EqualTo("Haha, always throws!")); | |
Assert.That(tryWrapper.Failures, Is.EqualTo(0)); | |
} | |
[Test] | |
public void ConfigExample() | |
{ | |
var value = Try.To(() => | |
{ | |
return "hi"; | |
}) | |
.Throw<InvalidCastException>() | |
.Throw<ArgumentException>() | |
.MaxAttempts(20) | |
.Now(); | |
Assert.That(value, Is.EqualTo("hi")); | |
} | |
} | |
public static class Try | |
{ | |
public static MethodRunner<TReturnType> To<TReturnType>(Func<TReturnType> methodToExecute) | |
{ | |
return new MethodRunner<TReturnType>(methodToExecute); | |
} | |
} | |
public class MethodRunner<TReturnType> | |
{ | |
public List<TimeSpan> Delays { get; private set; } | |
public int Failures { get { return _errors.Count; } } | |
private readonly List<Exception> _errors; | |
private readonly List<Type> _throwAll; | |
private readonly Func<TReturnType> _methodToExecute; | |
private int _maxAttempts = 100; | |
private double _lastDelaySeconds = 2.5; | |
public MethodRunner(Func<TReturnType> methodToExecute) | |
{ | |
_methodToExecute = methodToExecute; | |
_errors = new List<Exception>(); | |
Delays = new List<TimeSpan>(); | |
_throwAll = new List<Type>(); | |
} | |
public MethodRunner<TReturnType> MaxAttempts(int maxAttempts) | |
{ | |
_maxAttempts = maxAttempts; | |
return this; | |
} | |
public TReturnType Now() | |
{ | |
while (true) | |
{ | |
try | |
{ | |
return _methodToExecute(); | |
} | |
catch (Exception ex) | |
{ | |
if (_throwAll.Contains(ex.GetType())) | |
{ | |
throw; | |
} | |
_errors.Add(ex); | |
if (Failures >= _maxAttempts) | |
{ | |
break; | |
} | |
Delay(); | |
} | |
} | |
throw new AggregateException(_errors); | |
} | |
private void Delay() | |
{ | |
_lastDelaySeconds = _lastDelaySeconds * 2; | |
var delay = new TimeSpan(0, 0, 0, (int)_lastDelaySeconds); | |
Delays.Add(delay); | |
//Thread.Sleep(delay); | |
} | |
public MethodRunner<TReturnType> Throw<T>() | |
{ | |
_throwAll.Add(typeof (T)); | |
} | |
} | |
public class FakeHttp | |
{ | |
private readonly string _dataHere; | |
private readonly int _failCount; | |
private int _attempts = 0; | |
public FakeHttp(string dataHere, int failCount = 0) | |
{ | |
_dataHere = dataHere; | |
_failCount = failCount; | |
} | |
public string GetHttpData() | |
{ | |
if (_attempts < _failCount) | |
{ | |
_attempts++; | |
throw new Exception("Fake failure"); | |
} | |
return _dataHere; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment