Skip to content

Commit

Permalink
Merged Pull Request '#8 feature/cancellation-tests->main : 'TEST: Ins…
Browse files Browse the repository at this point in the history
…tead of using time taken to determine if a test is cancelled, set and check variables instead.''

TEST: Instead of using time taken to determine if a test is cancelled, set and check variables instead.
  • Loading branch information
Automation51D authored Sep 13, 2023
2 parents 2855d41 + d31baf7 commit 94bfd81
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 33 deletions.
6 changes: 5 additions & 1 deletion FiftyOne.Caching.Tests/Loaders/ReturnKeyLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,13 @@ internal class ReturnKeyLoader<T> : TrackingLoaderBase<T, T>
{
public ReturnKeyLoader()
{
}

public ReturnKeyLoader(int delayMillis) : base(delayMillis)
{
}

public ReturnKeyLoader(int delayMillis) : base(delayMillis)
public ReturnKeyLoader(int delayMillis, bool runWithToken) : base(delayMillis, runWithToken)
{
}

Expand Down
22 changes: 18 additions & 4 deletions FiftyOne.Caching.Tests/Loaders/TrackingLoaderBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,30 @@ internal abstract class TrackingLoaderBase<TKey, TValue> : IValueTaskLoader<TKey

private volatile int _cancels = 0;

private volatile int _completeWaits = 0;

private volatile bool _runWithToken;

public int Calls => _calls;

public int Cancels => _cancels;

public int CompleteWaits => _completeWaits;

public TrackingLoaderBase() : this(0)
{

}

public TrackingLoaderBase(int delayMillis)
public TrackingLoaderBase(int delayMillis) : this(delayMillis, false)
{
_delayMillis = delayMillis;
}

public TrackingLoaderBase(int delayMillis, bool runWithToken)
{
_delayMillis = delayMillis;
_runWithToken = runWithToken;
}

public Task<TValue> Load(TKey key, CancellationToken token)
{
Expand All @@ -59,19 +69,23 @@ public Task<TValue> Load(TKey key, CancellationToken token)
if (_delayMillis > 0)
{
var start = DateTime.Now;
while (DateTime.Now < start.AddMilliseconds(_delayMillis) &&
while (DateTime.Now <= start.AddMilliseconds(_delayMillis) &&
token.IsCancellationRequested == false)
{
Thread.Sleep(1);
}
if (DateTime.Now >= start.AddMilliseconds(_delayMillis))
{
Interlocked.Increment(ref _completeWaits);
}
if (token.IsCancellationRequested)
{
Interlocked.Increment(ref _cancels);
throw new OperationCanceledException();
}
}
return GetValue(key);
});
}, _runWithToken ? token : new CancellationToken());
}

public TValue Load(TKey key)
Expand Down
135 changes: 107 additions & 28 deletions FiftyOne.Caching.Tests/LoadingDictionaryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -354,8 +354,8 @@ public void LoadingDictionary_TryGetFails()

Assert.IsFalse(success);
Assert.AreEqual(0, dict.Keys.Count());
}

}

/// <summary>
/// Test that calling the cancellation token results in the get method
/// returning immediately, and not waiting for the Task to complete.
Expand All @@ -367,15 +367,56 @@ public void LoadingDictionary_GetCancelled()
{
// Arrange

var millis = 1000;
var millis = 5000;
var value = "teststring";
var loader = new ReturnKeyLoader<string>(millis);
var dict = new LoadingDictionary<string, string>(_logger.Object, loader);
Exception exception = null;

// Act

var getter = Task.Run(() => dict[value, _token.Token]);
_token.Cancel();
try
{
getter.Wait(millis * 2);
Assert.Fail(
"The prior cancel of the token should prevent getting here");
}
catch (AggregateException ex)
{
Assert.IsTrue(ex.InnerExceptions.Count == 1);
Assert.IsTrue(typeof(TaskCanceledException) == ex.InnerException.GetType());
exception = ex.InnerException;
}

// Assert

Assert.AreEqual(0, loader.CompleteWaits);
Assert.AreEqual(1, loader.Cancels);
Assert.IsNotNull(exception);
}

/// <summary>
/// Test that calling the get method with a cancellation token that
/// is already cancelled results in it returning immediately, and
/// does not even start the task.
/// Also check that the cancellation was passed to the loader properly,
/// and that the correct exception is thrown.
/// </summary>
[TestMethod]
public void LoadingDictionary_GetAlreadyCancelled()
{
// Arrange

var millis = 5000;
var value = "teststring";
var loader = new ReturnKeyLoader<string>(millis, true);
var dict = new LoadingDictionary<string, string>(_logger.Object, loader);
OperationCanceledException exception = null;

// Act

var start = DateTime.Now;
_token.Cancel();
try
{
Expand All @@ -384,37 +425,76 @@ public void LoadingDictionary_GetCancelled()
catch (OperationCanceledException e)
{
exception = e;
}

// Assert

Assert.AreEqual(0, loader.CompleteWaits);
Assert.AreEqual(0, loader.Cancels);
Assert.IsNotNull(exception);
}

/// <summary>
/// Test that calling the cancellation token results in the TryGet method
/// returning immediately, and not waiting for the Task to complete.
/// Also check that the cancellation was passed to the loader properly,
/// and that the correct exception is thrown.
/// </summary>
[TestMethod]
public void LoadingDictionary_TryGetCancelled()
{
// Arrange

var millis = 5000;
var value = "teststring";
var loader = new ReturnKeyLoader<string>(millis);
var dict = new LoadingDictionary<string, string>(_logger.Object, loader);
Exception exception = null;

// Act

var getter = Task.Run(() => dict.TryGet(value, _token.Token, out _));
_token.Cancel();
try
{
getter.Wait(millis * 2);
Assert.Fail(
"The prior cancel of the token should prevent getting here");
}
catch (AggregateException ex)
{
Assert.IsTrue(ex.InnerExceptions.Count == 1);
Assert.IsTrue(typeof(TaskCanceledException) == ex.InnerException.GetType());
exception = ex.InnerException;
}
var end = DateTime.Now;
Thread.Sleep(100);

// Assert

Assert.IsTrue((end - start).TotalMilliseconds < millis);
Assert.AreEqual(0, loader.CompleteWaits);
Assert.AreEqual(1, loader.Cancels);
Assert.IsNotNull(exception);
}

/// <summary>
/// Test that calling the cancellation token results in the TryGet method
/// returning immediately, and not waiting for the Task to complete.
/// Test that calling the TryGet method with a cancellation token that
/// is already cancelled results in it returning immediately, and
/// does not even start the task.
/// Also check that the cancellation was passed to the loader properly,
/// and that the correct exception is thrown.
/// </summary>
[TestMethod]
public void LoadingDictionary_TryGetCancelled()
public void LoadingDictionary_TryGetAlreadyCancelled()
{
// Arrange

var millis = 1000;
var millis = 5000;
var value = "teststring";
var loader = new ReturnKeyLoader<string>(millis);
var loader = new ReturnKeyLoader<string>(millis, true);
var dict = new LoadingDictionary<string, string>(_logger.Object, loader);
OperationCanceledException exception = null;

// Act

var start = DateTime.Now;
_token.Cancel();
try
{
Expand All @@ -424,13 +504,11 @@ public void LoadingDictionary_TryGetCancelled()
{
exception = e;
}
var end = DateTime.Now;
Thread.Sleep(100);

// Assert

Assert.IsTrue((end - start).TotalMilliseconds < millis);
Assert.AreEqual(1, loader.Cancels);
Assert.AreEqual(0, loader.CompleteWaits);
Assert.AreEqual(0, loader.Cancels);
Assert.IsNotNull(exception);
}

Expand Down Expand Up @@ -579,25 +657,26 @@ public void LoadingDictionary_GetCanceledIsNotReused()
// Arrange

var value = "testvalue";
var millis = 1000;
var millis = 5000;
var loader = new ReturnKeyLoader<string>(millis);
var dict = new LoadingDictionary<string, string>(_logger.Object, loader);
OperationCanceledException exception = null;
Exception exception = null;
var count = 2;

// Act

for (int i = 0; i < count; i++)
{
_token.Cancel();
var getter = Task.Run(() => _ = dict[value, _token.Token]);
_token.Cancel();

try
{
_ = dict[value, _token.Token];
_ = getter.Result;
}
catch (OperationCanceledException e)
catch (AggregateException e)
{
exception = e;
exception = e.InnerException;
}
Assert.IsNotNull(exception);
exception = null;
Expand Down Expand Up @@ -654,24 +733,24 @@ public void LoadingDictionary_TryGetCanceledIsRemoved()
// Arrange

var value = "testvalue";
var millis = 1000;
var millis = 5000;
var loader = new ReturnKeyLoader<string>(millis);
var dict = new LoadingDictionary<string, string>(_logger.Object, loader);
var count = 2;

// Act

for (int i = 0; i < count; i++)
{
{
var getter = Task.Run(() => dict.TryGet(value, _token.Token, out _));
_token.Cancel();

Assert.ThrowsException<TaskCanceledException>(() =>
Assert.ThrowsException<AggregateException>(() =>
{
_ = dict.TryGet(value, _token.Token, out _);
_ = getter.Result;
});

_token = new CancellationTokenSource();
Thread.Sleep(100);
}

// Assert
Expand Down

0 comments on commit 94bfd81

Please sign in to comment.