diff options
Diffstat (limited to 'Test/UnitTests/TestMultithreading.cs')
-rw-r--r-- | Test/UnitTests/TestMultithreading.cs | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/Test/UnitTests/TestMultithreading.cs b/Test/UnitTests/TestMultithreading.cs new file mode 100644 index 0000000..646cdef --- /dev/null +++ b/Test/UnitTests/TestMultithreading.cs @@ -0,0 +1,243 @@ + +using System; +using System.IO; +using NUnit.Framework; +using Mono.Addins; +using SimpleApp; +using System.Threading; +using System.Diagnostics; +using System.Linq; + +namespace UnitTests +{ + [TestFixture ()] + public class TestMultiThreading: TestBase + { + public override void Setup () + { + base.Setup (); + GlobalInfoCondition.Value = "res"; + } + + [Test] + public void ChildConditionAttributeMultithread () + { + int threads = 50; + using var testData = new TestData(threads); + + GlobalInfoCondition.Value = ""; + + // Threads check the number of nodes in /SimpleApp/ConditionedWriters, which changes + // from 0 to 1 as the GlobalInfoCondition condition changes + + testData.StartThreads (RunChildConditionAttributeTest); + + for (int n = 0; n < 20; n++) { + Console.WriteLine ("Step " + n); + testData.CheckCounters (0, 10000); + + GlobalInfoCondition.Value = "foo"; + + testData.CheckCounters (1, 10000); + + GlobalInfoCondition.Value = ""; + } + } + + void RunChildConditionAttributeTest (int index, TestData data) + { + while (!data.Stopped) { + var writers = AddinManager.GetExtensionObjects<IWriter> ("/SimpleApp/ConditionedWriters"); + data.Counters [index] = writers.Length; + } + } + + [Test] + public void EnableDisableMultithread () + { + int threads = 50; + int steps = 20; + + using var testData = new TestData (threads); + + testData.StartThreads ((index, data) => { + while (!data.Stopped) { + var writers = AddinManager.GetExtensionObjects<IWriter> ("/SimpleApp/Writers"); + testData.Counters [index] = writers.Length; + } + }); + + for (int n = 0; n < steps; n++) { + Console.WriteLine ("Step " + n); + + testData.CheckCounters (4, 10000); + + var ainfo1 = AddinManager.Registry.GetAddin ("SimpleApp.HelloWorldExtension"); + ainfo1.Enabled = false; + + testData.CheckCounters (3, 10000); + + var ainfo2 = AddinManager.Registry.GetAddin ("SimpleApp.FileContentExtension"); + ainfo2.Enabled = false; + + testData.CheckCounters (2, 10000); + + ainfo1.Enabled = true; + ainfo2.Enabled = true; + } + } + + [Test] + public void EventsMultithread() + { + int threads = 50; + + int totalAdded = 0; + int totalRemoved = 0; + int nodesCount = 0; + int minCount = 0; + int maxCount = 0; + + var node = AddinManager.GetExtensionNode("/SimpleApp/Writers"); + + nodesCount = 0; + + node.ExtensionNodeChanged += (s, args) => + { + if (args.Change == ExtensionChange.Add) + { + nodesCount++; + totalAdded++; + } + else + { + nodesCount--; + totalRemoved++; + } + + if (nodesCount < minCount) + minCount = nodesCount; + if (nodesCount > maxCount) + maxCount = nodesCount; + }; + + Assert.AreEqual(4, nodesCount); + + minCount = 4; + maxCount = 4; + totalAdded = 0; + totalRemoved = 0; + + var ainfo1 = AddinManager.Registry.GetAddin("SimpleApp.HelloWorldExtension"); + var ainfo2 = AddinManager.Registry.GetAddin("SimpleApp.FileContentExtension"); + + using var enablers = new TestData(threads); + + enablers.StartThreads((index, data) => + { + var random = new Random(10000 + index); + int iterations = 100; + while (--iterations > 0) + { + var action = random.Next(4); + switch (action) + { + case 0: ainfo1.Enabled = false; break; + case 1: ainfo1.Enabled = true; break; + case 2: ainfo2.Enabled = false; break; + case 3: ainfo2.Enabled = true; break; + } + } + data.Counters[index] = 1; + }); + + // Wait for the threads to do the work. 5 seconds should be enough + enablers.CheckCounters(1, 20000); + + // Go back to the initial status + + ainfo1.Enabled = true; + ainfo2.Enabled = true; + + // If all events have been sent correctly, the node count should have never gone below 2 and over 4. + + Assert.That(nodesCount, Is.EqualTo(4)); + Assert.That(totalAdded, Is.AtLeast(100)); + Assert.That(totalAdded, Is.EqualTo(totalRemoved)); + Assert.That(minCount, Is.AtLeast(2)); + Assert.That(maxCount, Is.AtMost(4)); + } + + [Test] + public void ChildConditionWithContextMultithread () + { + int threads = 50; + using var testData = new TestData (threads); + + GlobalInfoCondition.Value = ""; + + testData.StartThreads (RunChildConditionWithContextMultithread); + + for (int n = 0; n < 20; n++) { + Console.WriteLine ("Step " + n); + testData.CheckCounters (0, 10000); + + GlobalInfoCondition.Value = "foo"; + + testData.CheckCounters (1, 10000); + + GlobalInfoCondition.Value = ""; + } + } + + void RunChildConditionWithContextMultithread (int index, TestData data) + { + var ctx = AddinManager.CreateExtensionContext (); + var writers = ctx.GetExtensionNode ("/SimpleApp/ConditionedWriters"); + while (!data.Stopped) { + data.Counters [index] = writers.GetChildNodes().Count; + } + } + + class TestData: IDisposable + { + int numThreads; + + public bool Stopped; + public int [] Counters; + + public TestData (int numThreads) + { + this.numThreads = numThreads; + Counters = new int [numThreads]; + } + + public void CheckCounters (int expectedResult, int timeout) + { + var sw = Stopwatch.StartNew (); + do { + if (Counters.All (c => c == expectedResult)) + return; + Thread.Sleep (10); + } while (sw.ElapsedMilliseconds < timeout); + + Assert.Fail ("Expected " + expectedResult); + } + + public void Dispose () + { + Stopped = true; + } + + public void StartThreads (Action<int, TestData> threadAction) + { + for (int n = 0; n < numThreads; n++) { + Counters [n] = -1; + var index = n; + var t = new Thread (() => threadAction(index, this)); + t.Start (); + } + } + } + } +} |