How to unit test Thread Safe Generic List in C # using NUnit?

I asked about creating a custom Thread Safe Generic List , now I'm trying to unit test, and I absolutely do not know how to do this. Since locking occurs inside the ThreadSafeList class, I do not know how to make the list locked for a certain period of time while I try to simulate a multiple call to add. Thanks.

Can_add_one_item_at_a_time

[Test] public void Can_add_one_item_at_a_time() //this test won't pass { //I am not sure how to do this test... var list = new ThreadSafeList<string>(); //some how need to call lock and sleep inside list instance //say somehow list locks for 1 sec var ta = new Thread(x => list.Add("a")); ta.Start(); //does it need to aboard say before 1 sec if locked var tb = new Thread(x => list.Add("b")); tb.Start(); //does it need to aboard say before 1 sec if locked //it involves using GetSnapshot() //which is bad idea for unit testing I think var snapshot = list.GetSnapshot(); Assert.IsFalse(snapshot.Contains("a"), "Should not contain a."); Assert.IsFalse(snapshot.Contains("b"), "Should not contain b."); } 

Snapshot_should_be_point_of_time_only

 [Test] public void Snapshot_should_be_point_of_time_only() { var list = new ThreadSafeList<string>(); var ta = new Thread(x => list.Add("a")); ta.Start(); ta.Join(); var snapshot = list.GetSnapshot(); var tb = new Thread(x => list.Add("b")); tb.Start(); var tc = new Thread(x => list.Add("c")); tc.Start(); tb.Join(); tc.Join(); Assert.IsTrue(snapshot.Count == 1, "Snapshot should only contain 1 item."); Assert.IsFalse(snapshot.Contains("b"), "Should not contain a."); Assert.IsFalse(snapshot.Contains("c"), "Should not contain b."); } 

Instance method

 public ThreadSafeList<T> Instance<T>() { return new ThreadSafeList<T>(); } 
+4
source share
3 answers

Take a look at your first test, Can_add_one_item_at_a_time .

First of all, your exit conditions do not make sense. Both elements must be added, one at a time. Therefore, of course, your test will fail.

You also do not need to take a picture; remember that this is a test, nothing else will touch the list during your test.

And last, but not least, you need to make sure that you are not trying to evaluate the exit conditions until all threads run out. The easiest way is to use a counter and a wait event. Here is an example:

 [Test] public void Can_add_from_multiple_threads() { const int MaxWorkers = 10; var list = new ThreadSafeList<int>(MaxWorkers); int remainingWorkers = MaxWorkers; var workCompletedEvent = new ManualResetEvent(false); for (int i = 0; i < MaxWorkers; i++) { int workerNum = i; // Make a copy of local variable for next thread ThreadPool.QueueUserWorkItem(s => { list.Add(workerNum); if (Interlocked.Decrement(ref remainingWorkers) == 0) workCompletedEvent.Set(); }); } workCompletedEvent.WaitOne(); workCompletedEvent.Close(); for (int i = 0; i < MaxWorkers; i++) { Assert.IsTrue(list.Contains(i), "Element was not added"); } Assert.AreEqual(MaxWorkers, list.Count, "List count does not match worker count."); } 

Now this means that Add happening so fast that neither of the two threads will ever try to do this at the same time. No return No return partially explained how to insert a conditional delay. I would actually define a special test flag instead of DEBUG . In your build configuration, add a flag called TEST , then add this to your ThreadSafeList class:

 public class ThreadSafeList<T> { // snip fields public void Add(T item) { lock (sync) { TestUtil.WaitStandardThreadDelay(); innerList.Add(item); } } // snip other methods/properties } static class TestUtil { [Conditional("TEST")] public static void WaitStandardThreadDelay() { Thread.Sleep(1000); } } 

This will cause the Add method to wait 1 second before the item is actually added if the assembly configuration defines the TEST flag. The entire test should take at least 10 seconds; if it ends faster, something is wrong.

With that in mind, I will leave the second test to you. It looks like.

+6
source

You will need to insert some TESTONLY code that will add a delay to your lock. You can create a function like this:

  [Conditional("DEBUG")] void SleepForABit(int delay) { thread.current.sleep(delay); } 

and then call it in your class. The conditional attribute ensures that it is called only in DEBUG assemblies, and you can leave it in your compiled code.

Write something that constantly lags 100M or so, and something that never waits, and let's release it.

+1
source

You might want to take a look at Chess . This is a program specifically designed to search for race conditions in multi-threaded code.

+1
source

Source: https://habr.com/ru/post/1302432/


All Articles