Fold a bunch of CoreCLR's abandoned mutex tests into a parameterized test (dotnet...
authorKoundinya Veluri <kouvel@users.noreply.github.com>
Thu, 18 Jul 2019 17:48:31 +0000 (10:48 -0700)
committerGitHub <noreply@github.com>
Thu, 18 Jul 2019 17:48:31 +0000 (10:48 -0700)
Fold a bunch of CoreCLR's abandoned mutex tests into a parameterized test

- There are a large number of tests in CoreCLR with a lot of redundancy, timing issues, and which take unnecessarily long to run
- Folded a bunch of them into the existing parameterized test and improved it to cover more cases to replace the tests in CoreCLR that will be removed
- Coverage is not 100% equivalent, but I belive this sufficiently covers the necessary aspects of the tests in CoreCLR that will be removed

Commit migrated from https://github.com/dotnet/corefx/commit/fa722debec4c18a444a207171b686e40d7c0fc1a

src/libraries/Common/tests/CoreFx.Private.TestUtilities/System/AssertExtensions.cs
src/libraries/System.Threading/tests/MutexTests.cs

index dec6f7a..9ce9cb7 100644 (file)
@@ -75,6 +75,40 @@ namespace System
             return exception;
         }
 
+        public static TException Throws<TException, TResult>(Func<TResult> func)
+            where TException : Exception
+        {
+            object result = null;
+            bool returned = false;
+            try
+            {
+                return
+                    Assert.Throws<TException>(() =>
+                    {
+                        result = func();
+                        returned = true;
+                    });
+            }
+            catch (Exception ex) when (returned)
+            {
+                string resultStr;
+                if (result == null)
+                {
+                    resultStr = "(null)";
+                }
+                else
+                {
+                    resultStr = result.ToString();
+                    if (typeof(TResult) == typeof(string))
+                    {
+                        resultStr = $"\"{resultStr}\"";
+                    }
+                }
+
+                throw new AggregateException($"Result: {resultStr}", ex);
+            }
+        }
+
         public static T Throws<T>(string expectedParamName, Func<object> testCode)
             where T : ArgumentException
         {
index 05bfbfd..e787d16 100644 (file)
@@ -150,6 +150,13 @@ namespace System.Threading.Tests
             }
         }
 
+        public enum WaitHandleWaitType
+        {
+            WaitOne,
+            WaitAny,
+            WaitAll
+        }
+
         private static IEnumerable<string> GetNamePrefixes()
         {
             yield return string.Empty;
@@ -165,45 +172,182 @@ namespace System.Threading.Tests
         public static IEnumerable<object[]> AbandonExisting_MemberData()
         {
             var nameGuidStr = Guid.NewGuid().ToString("N");
-            for (int waitType = 0; waitType < 2; ++waitType) // 0 == WaitOne, 1 == WaitAny
+            foreach (WaitHandleWaitType waitType in Enum.GetValues(typeof(WaitHandleWaitType)))
             {
-                yield return new object[] { null, waitType };
-                foreach (var namePrefix in GetNamePrefixes())
+                foreach (int waitCount in new int[] { 1, 3 })
                 {
-                    yield return new object[] { namePrefix + nameGuidStr, waitType };
+                    if (waitType == WaitHandleWaitType.WaitOne && waitCount != 1)
+                    {
+                        continue;
+                    }
+
+                    for (int notAbandonedWaitIndex = 0; notAbandonedWaitIndex < waitCount; ++notAbandonedWaitIndex)
+                    {
+                        foreach (bool abandonDuringWait in new bool[] { false, true })
+                        {
+                            var args =
+                                new object[]
+                                {
+                                    null, // name
+                                    waitType,
+                                    waitCount,
+                                    notAbandonedWaitIndex,
+                                    false, // isNotAbandonedWaitObjectSignaled
+                                    abandonDuringWait
+                                };
+
+                            bool includeArgsForSignaledNotAbandonedWaitObject =
+                                waitCount != 1 && (waitType == WaitHandleWaitType.WaitAll || !abandonDuringWait);
+
+                            yield return (object[])args.Clone();
+                            if (includeArgsForSignaledNotAbandonedWaitObject)
+                            {
+                                var newArgs = (object[])args.Clone();
+                                newArgs[4] = true; // isNotAbandonedWaitObjectSignaled
+                                yield return newArgs;
+                            }
+
+                            if (waitCount == 1 || PlatformDetection.IsWindows)
+                            {
+                                foreach (var namePrefix in GetNamePrefixes())
+                                {
+                                    var newArgs = (object[])args.Clone();
+                                    newArgs[0] = namePrefix + nameGuidStr;
+                                    yield return newArgs;
+                                    if (includeArgsForSignaledNotAbandonedWaitObject)
+                                    {
+                                        newArgs = (object[])newArgs.Clone();
+                                        newArgs[4] = true; // isNotAbandonedWaitObjectSignaled
+                                        yield return newArgs;
+                                    }
+                                }
+                            }
+                        }
+                    }
                 }
             }
         }
 
         [Theory]
         [MemberData(nameof(AbandonExisting_MemberData))]
-        public void AbandonExisting(string name, int waitType)
+        public void AbandonExisting(
+            string name,
+            WaitHandleWaitType waitType,
+            int waitCount,
+            int notAbandonedWaitIndex,
+            bool isNotAbandonedWaitObjectSignaled,
+            bool abandonDuringWait)
         {
             ThreadTestHelpers.RunTestInBackgroundThread(() =>
             {
                 using (var m = new Mutex(false, name))
+                using (Mutex m2 = waitCount == 1 ? null : new Mutex(false, name == null ? null : name + "_2"))
+                using (ManualResetEvent e = waitCount == 1 ? null : new ManualResetEvent(isNotAbandonedWaitObjectSignaled))
+                using (ManualResetEvent threadReadyForAbandon = abandonDuringWait ? new ManualResetEvent(false) : null)
+                using (ManualResetEvent abandonSoon = abandonDuringWait ? new ManualResetEvent(false) : null)
                 {
-                    Task t = Task.Factory.StartNew(() =>
+                    WaitHandle[] waitHandles = null;
+                    if (waitType != WaitHandleWaitType.WaitOne)
                     {
-                        m.CheckedWait();
-                        // don't release the mutex; abandon it on this thread
-                    }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
-                    t.CheckedWait();
+                        waitHandles = new WaitHandle[waitCount];
+                        if (waitCount == 1)
+                        {
+                            waitHandles[0] = m;
+                        }
+                        else
+                        {
+                            waitHandles[notAbandonedWaitIndex] = e;
+                            waitHandles[notAbandonedWaitIndex == 0 ? 1 : 0] = m;
+                            waitHandles[notAbandonedWaitIndex == 2 ? 1 : 2] = m2;
+                        }
+                    }
 
+                    Thread t = ThreadTestHelpers.CreateGuardedThread(out Action waitForThread, () =>
+                    {
+                        Assert.True(m.WaitOne(0));
+                        if (m2 != null)
+                        {
+                            Assert.True(m2.WaitOne(0));
+                        }
+
+                        if (abandonDuringWait)
+                        {
+                            threadReadyForAbandon.Set();
+                            abandonSoon.CheckedWait();
+                            Thread.Sleep(ThreadTestHelpers.ExpectedTimeoutMilliseconds);
+                        }
+
+                        // don't release the mutexes; abandon them on this thread
+                    });
+                    t.IsBackground = true;
+                    t.Start();
+
+                    if (abandonDuringWait)
+                    {
+                        threadReadyForAbandon.CheckedWait();
+                        abandonSoon.Set();
+                    }
+                    else
+                    {
+                        waitForThread();
+                    }
+
+                    AbandonedMutexException ame;
                     switch (waitType)
                     {
-                        case 0: // WaitOne
-                            Assert.Throws<AbandonedMutexException>(() => m.CheckedWait());
+                        case WaitHandleWaitType.WaitOne:
+                            ame =
+                                AssertExtensions.Throws<AbandonedMutexException, bool>(
+                                    () => m.WaitOne(ThreadTestHelpers.UnexpectedTimeoutMilliseconds));
+                            Assert.Equal(-1, ame.MutexIndex);
+                            Assert.Null(ame.Mutex);
+                            break;
+
+                        case WaitHandleWaitType.WaitAny:
+                            if (waitCount != 1 && isNotAbandonedWaitObjectSignaled && notAbandonedWaitIndex == 0)
+                            {
+                                Assert.Equal(0, WaitHandle.WaitAny(waitHandles, 0));
+                                AssertExtensions.Throws<AbandonedMutexException, bool>(
+                                    () => m.WaitOne(ThreadTestHelpers.UnexpectedTimeoutMilliseconds));
+                            }
+                            else
+                            {
+                                ame =
+                                    AssertExtensions.Throws<AbandonedMutexException, int>(
+                                        () => WaitHandle.WaitAny(waitHandles, ThreadTestHelpers.UnexpectedTimeoutMilliseconds));
+                                Assert.Equal(waitCount != 1 && notAbandonedWaitIndex == 0 ? 1 : 0, ame.MutexIndex);
+                                Assert.Equal(m, ame.Mutex);
+                            }
+
+                            if (m2 != null)
+                            {
+                                AssertExtensions.Throws<AbandonedMutexException, bool>(
+                                    () => m2.WaitOne(ThreadTestHelpers.UnexpectedTimeoutMilliseconds));
+                            }
                             break;
 
-                        case 1: // WaitAny
-                            AbandonedMutexException ame =
-                                Assert.Throws<AbandonedMutexException>(
-                                    () => WaitHandle.WaitAny(new[] { m }, ThreadTestHelpers.UnexpectedTimeoutMilliseconds));
-                            Assert.Equal(0, ame.MutexIndex);
-                            Assert.Equal(m, ame.Mutex);
+                        case WaitHandleWaitType.WaitAll:
+                            if (waitCount != 1 && !isNotAbandonedWaitObjectSignaled)
+                            {
+                                Assert.False(WaitHandle.WaitAll(waitHandles, ThreadTestHelpers.ExpectedTimeoutMilliseconds * 2));
+                                Assert.True(e.Set());
+                            }
+
+                            ame =
+                                AssertExtensions.Throws<AbandonedMutexException, bool>(
+                                    () => WaitHandle.WaitAll(waitHandles, ThreadTestHelpers.UnexpectedTimeoutMilliseconds));
+                            Assert.Equal(-1, ame.MutexIndex);
+                            Assert.Null(ame.Mutex);
                             break;
                     }
+
+                    if (abandonDuringWait)
+                    {
+                        waitForThread();
+                    }
+
+                    m.ReleaseMutex();
+                    m2?.ReleaseMutex();
                 }
             });
         }