Fixing potential race condition in ListRepository. Now internally implemented as a concurrent dictionary.

This commit is contained in:
ryanbodrug-microsoft
2020-07-08 15:12:36 -07:00
parent 2c45956030
commit 9ff8246a9d
3 changed files with 121 additions and 20 deletions

View File

@@ -3,7 +3,10 @@ using Moq;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Media.Capture;
using Wox.Infrastructure.Storage;
namespace Microsoft.Plugin.Program.UnitTests.Storage
@@ -17,8 +20,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Storage
{
//Arrange
var itemName = "originalItem1";
var mockStorage = new Mock<IStorage<IList<string>>>();
IRepository<string> repository = new ListRepository<string>(mockStorage.Object) { itemName };
IRepository<string> repository = new ListRepository<string>() { itemName };
//Act
var result = repository.Contains(itemName);
@@ -31,8 +33,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Storage
public void Contains_ShouldReturnTrue_WhenListIsUpdatedWithAdd()
{
//Arrange
var mockStorage = new Mock<IStorage<IList<string>>>();
IRepository<string> repository = new ListRepository<string>(mockStorage.Object);
IRepository<string> repository = new ListRepository<string>();
//Act
var itemName = "newItem";
@@ -48,8 +49,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Storage
{
//Arrange
var itemName = "originalItem1";
var mockStorage = new Mock<IStorage<IList<string>>>();
IRepository<string> repository = new ListRepository<string>(mockStorage.Object) { itemName };
IRepository<string> repository = new ListRepository<string>() { itemName };
//Act
repository.Remove(itemName);
@@ -58,5 +58,91 @@ namespace Microsoft.Plugin.Program.UnitTests.Storage
//Assert
Assert.IsFalse(result);
}
[Test]
public async Task Add_ShouldNotThrow_WhenBeingIterated()
{
//Arrange
ListRepository<string> repository = new ListRepository<string>();
var numItems = 1000;
for(var i=0; i<numItems;++i)
{
repository.Add($"OriginalItem_{i}");
}
//Act - Begin iterating on one thread
var iterationTask = Task.Run(() =>
{
var remainingIterations = 10000;
while (remainingIterations > 0)
{
foreach (var item in repository)
{
//keep iterating
}
--remainingIterations;
}
});
//Act - Insert on another thread
var addTask = Task.Run(() =>
{
for (var i = 0; i < numItems; ++i)
{
repository.Add($"NewItem_{i}");
}
});
//Assert that this does not throw. Collections that aren't syncronized will throw an invalidoperatioexception if the list is modified while enumerating
Assert.DoesNotThrowAsync(async () =>
{
await Task.WhenAll(new Task[] { iterationTask, addTask });
});
}
[Test]
public async Task Remove_ShouldNotThrow_WhenBeingIterated()
{
//Arrange
ListRepository<string> repository = new ListRepository<string>();
var numItems = 1000;
for (var i = 0; i < numItems; ++i)
{
repository.Add($"OriginalItem_{i}");
}
//Act - Begin iterating on one thread
var iterationTask = Task.Run(() =>
{
var remainingIterations = 10000;
while (remainingIterations > 0)
{
foreach (var item in repository)
{
//keep iterating
}
--remainingIterations;
}
});
//Act - Remove on another thread
var addTask = Task.Run(() =>
{
for (var i = 0; i < numItems; ++i)
{
repository.Remove($"OriginalItem_{i}");
}
});
//Assert that this does not throw. Collections that aren't syncronized will throw an invalidoperatioexception if the list is modified while enumerating
Assert.DoesNotThrowAsync(async () =>
{
await Task.WhenAll(new Task[] { iterationTask, addTask });
});
}
}
}