Merge pull request #799 from kebby/master
[mono.git] / mcs / class / corlib / Test / System.Threading.Tasks / TaskTest.cs
index 060b2585148de4216bf57aba9009106bb81f3869..f61a64d4aa0f89fceb29833e418b9910cafabce2 100644 (file)
 using System;
 using System.Threading;
 using System.Threading.Tasks;
-
+using System.Collections.Generic;
 using NUnit.Framework;
 
+#if !MOBILE
+using NUnit.Framework.SyntaxHelpers;
+#endif
+
 namespace MonoTests.System.Threading.Tasks
 {
        [TestFixture]
        public class TaskTests
        {
+               class MockScheduler : TaskScheduler
+               {
+                       public event Action<Task, bool> TryExecuteTaskInlineHandler;
+
+                       protected override IEnumerable<Task> GetScheduledTasks ()
+                       {
+                               throw new NotImplementedException ();
+                       }
+
+                       protected override void QueueTask (Task task)
+                       {
+                               return;
+                       }
+
+                       protected override bool TryExecuteTaskInline (Task task, bool taskWasPreviouslyQueued)
+                       {
+                               if (TryExecuteTaskInlineHandler != null)
+                                       TryExecuteTaskInlineHandler (task, taskWasPreviouslyQueued);
+
+                               return base.TryExecuteTask (task);
+                       }
+               }
+
+               class NonInlineableScheduler : TaskScheduler
+               {
+                       protected override IEnumerable<Task> GetScheduledTasks ()
+                       {
+                               throw new NotImplementedException ();
+                       }
+
+                       protected override void QueueTask (Task task)
+                       {
+                               if (!base.TryExecuteTask (task))
+                                       throw new ApplicationException ();
+                       }
+
+                       protected override bool TryExecuteTaskInline (Task task, bool taskWasPreviouslyQueued)
+                       {
+                               return false;
+                       }
+               }
+
+
                Task[] tasks;
                const int max = 6;
                
@@ -307,7 +354,8 @@ namespace MonoTests.System.Threading.Tasks
                        Assert.IsTrue (tasks[0].IsCompleted, "#3");
                        Assert.IsTrue (tasks[1].IsCanceled, "#4");
                }
-               
+
+#if NET_4_5            
                [Test]
                public void WaitAll_CancelledAndTimeout ()
                {
@@ -316,6 +364,7 @@ namespace MonoTests.System.Threading.Tasks
                        var t2 = Task.Delay (3000);
                        Assert.IsFalse (Task.WaitAll (new[] { t1, t2 }, 10));
                }
+#endif
 
                [Test]
                public void WaitAllExceptionThenCancelled ()
@@ -332,7 +381,7 @@ namespace MonoTests.System.Threading.Tasks
                                Task.WaitAll (tasks);
                                Assert.Fail ("#1");
                        } catch (AggregateException e) {
-                               Assert.IsInstanceOfType (typeof (ApplicationException), e.InnerException, "#2");
+                               Assert.That (e.InnerException, Is.TypeOf (typeof (ApplicationException)), "#2");
                                var inner = (TaskCanceledException) e.InnerExceptions[1];
                                Assert.AreEqual (tasks[1], inner.Task, "#3");
                        }
@@ -397,6 +446,23 @@ namespace MonoTests.System.Threading.Tasks
                        }
                }
 
+               [Test]
+               public void Wait_Inlined ()
+               {
+                       bool? previouslyQueued = null;
+
+                       var scheduler = new MockScheduler ();
+                       scheduler.TryExecuteTaskInlineHandler += (task, b) => {
+                               previouslyQueued = b;
+                       };
+
+                       var tf = new TaskFactory (scheduler);
+                       var t = tf.StartNew (() => { });
+                       t.Wait ();
+
+                       Assert.AreEqual (true, previouslyQueued);
+               }
+
                [Test, ExpectedException (typeof (InvalidOperationException))]
                public void CreationWhileInitiallyCanceled ()
                {
@@ -626,6 +692,30 @@ namespace MonoTests.System.Threading.Tasks
                        }, 10);
                }
 
+               Task parent_wfc;
+
+               [Test]
+               public void WaitingForChildrenToComplete ()
+               {
+                       Task nested = null;
+                       var mre = new ManualResetEvent (false);
+
+                       parent_wfc = Task.Factory.StartNew (() => {
+                               nested = Task.Factory.StartNew (() => {
+                                       Assert.IsTrue (mre.WaitOne (4000), "parent_wfc needs to be set first");
+                                       Assert.IsFalse (parent_wfc.Wait (10), "#1a");
+                                       Assert.AreEqual (TaskStatus.WaitingForChildrenToComplete, parent_wfc.Status, "#1b");
+                               }, TaskCreationOptions.AttachedToParent).ContinueWith (l => {
+                                       Assert.IsTrue (parent_wfc.Wait (2000), "#2a");
+                                       Assert.AreEqual (TaskStatus.RanToCompletion, parent_wfc.Status, "#2b");                                 
+                               }, TaskContinuationOptions.ExecuteSynchronously);
+                       });
+
+                       mre.Set ();
+                       Assert.IsTrue (parent_wfc.Wait (2000), "#3");
+                       Assert.IsTrue (nested.Wait (2000), "#4");
+               }
+
                [Test]
                public void WaitChildWithContinuationAttachedTest ()
                {
@@ -679,22 +769,21 @@ namespace MonoTests.System.Threading.Tasks
                public void DoubleWaitTest ()
                {
                        ParallelTestHelper.Repeat (delegate {
-                               Console.WriteLine ("run");
                                var evt = new ManualResetEventSlim ();
-                               var t = Task.Factory.StartNew (() => evt.Wait (2000));
+                               var t = Task.Factory.StartNew (() => evt.Wait (5000));
                                var cntd = new CountdownEvent (2);
+                               var cntd2 = new CountdownEvent (2);
 
                                bool r1 = false, r2 = false;
-                               ThreadPool.QueueUserWorkItem (delegate { cntd.Signal (); r1 = t.Wait (1000); Console.WriteLine ("out 1 {0}", r1); cntd.Signal (); });
-                               ThreadPool.QueueUserWorkItem (delegate { cntd.Signal (); r2 = t.Wait (1000); Console.WriteLine ("out 2 {0}", r2); cntd.Signal (); });
+                               ThreadPool.QueueUserWorkItem (delegate { cntd.Signal (); r1 = t.Wait (1000) && t.Result; cntd2.Signal (); });
+                               ThreadPool.QueueUserWorkItem (delegate { cntd.Signal (); r2 = t.Wait (1000) && t.Result; cntd2.Signal (); });
 
-                               cntd.Wait (2000);
-                               cntd.Reset ();
+                               Assert.IsTrue (cntd.Wait (2000), "#1");
                                evt.Set ();
-                               cntd.Wait (2000);
-                               Assert.IsTrue (r1);
-                               Assert.IsTrue (r2);
-                       }, 5);
+                               Assert.IsTrue (cntd2.Wait (2000), "#2");
+                               Assert.IsTrue (r1, "r1");
+                               Assert.IsTrue (r2, "r2");
+                       }, 10);
                }
 
                [Test]
@@ -714,13 +803,27 @@ namespace MonoTests.System.Threading.Tasks
                }
 
                [Test]
-               public void ExecuteSynchronouslyTest ()
+               public void RunSynchronously ()
                {
                        var val = 0;
                        Task t = new Task (() => { Thread.Sleep (100); val = 1; });
                        t.RunSynchronously ();
 
-                       Assert.AreEqual (1, val);
+                       Assert.AreEqual (1, val, "#1");
+
+                       t = new Task (() => { Thread.Sleep (0); val = 2; });
+
+                       bool? previouslyQueued = null;
+
+                       var scheduler = new MockScheduler ();
+                       scheduler.TryExecuteTaskInlineHandler += (task, b) => {
+                               previouslyQueued = b;
+                       };
+
+                       t.RunSynchronously (scheduler);
+
+                       Assert.AreEqual (2, val, "#2");
+                       Assert.AreEqual (false, previouslyQueued, "#2a");
                }
 
                [Test]
@@ -745,6 +848,18 @@ namespace MonoTests.System.Threading.Tasks
                        Assert.IsTrue (result);
                }
 
+               [Test]
+               public void RunSynchronouslyOnContinuation ()
+               {
+                       Task t = new Task<int> (() => 1);
+                       t = t.ContinueWith (l => { });
+                       try {
+                               t.RunSynchronously ();
+                               Assert.Fail ("#1");
+                       } catch (InvalidOperationException) {
+                       }
+               }
+
                [Test]
                public void UnobservedExceptionOnFinalizerThreadTest ()
                {
@@ -947,6 +1062,14 @@ namespace MonoTests.System.Threading.Tasks
                        Assert.IsTrue (r2);
                }
 
+               [Test]
+               public void AsyncWaitHandleSet ()
+               {
+                       var task = new TaskFactory ().StartNew (() => { });
+                       var ar = (IAsyncResult)task;
+                       ar.AsyncWaitHandle.WaitOne ();
+               }
+
 #if NET_4_5
                [Test]
                public void Delay_Invalid ()
@@ -1002,6 +1125,15 @@ namespace MonoTests.System.Threading.Tasks
                        }
                }
 
+               [Test]
+               public void Delay_TimeManagement ()
+               {
+                       var delay1 = Task.Delay(50);
+                       var delay2 = Task.Delay(25);
+                       Assert.IsTrue (Task.WhenAny(new[] { delay1, delay2 }).Wait (1000));
+                       Assert.AreEqual (TaskStatus.RanToCompletion, delay2.Status);
+               }
+
                [Test]
                public void WaitAny_WithNull ()
                {
@@ -1017,6 +1149,16 @@ namespace MonoTests.System.Threading.Tasks
                        }
                }
 
+               [Test]
+               public void WhenAll_Empty ()
+               {
+                       var tasks = new Task[0];
+
+                       Task t = Task.WhenAll(tasks);
+
+                       Assert.IsTrue(t.Wait(1000), "#1");
+               }
+
                [Test]
                public void WhenAll_WithNull ()
                {
@@ -1088,7 +1230,7 @@ namespace MonoTests.System.Threading.Tasks
                                Assert.IsTrue (t.Wait (1000), "#2");
                                Assert.Fail ("#2a");
                        } catch (AggregateException e) {
-                               Assert.IsInstanceOfType (typeof (TaskCanceledException), e.InnerException, "#3");
+                               Assert.That (e.InnerException, Is.TypeOf (typeof (TaskCanceledException)), "#3");
                        }
                }
 
@@ -1119,8 +1261,8 @@ namespace MonoTests.System.Threading.Tasks
                                Assert.IsTrue (t.Wait (1000), "#2");
                                Assert.Fail ("#2a");
                        } catch (AggregateException e) {
-                               Assert.IsInstanceOfType (typeof (ApplicationException), e.InnerException, "#3");
-                               Assert.IsInstanceOfType (typeof (InvalidTimeZoneException), e.InnerExceptions[1], "#4");
+                               Assert.That (e.InnerException, Is.TypeOf (typeof (ApplicationException)), "#3");
+                               Assert.That (e.InnerExceptions[1], Is.TypeOf (typeof (InvalidTimeZoneException)), "#4");
                        }
                }
 
@@ -1142,6 +1284,18 @@ namespace MonoTests.System.Threading.Tasks
                        Assert.IsTrue (t.Wait (1000), "#2");
                }
 
+               [Test]
+               public void WhenAllResult_Empty ()
+               {
+                       var tasks = new Task<int>[0];
+
+                       Task<int[]> t = Task.WhenAll(tasks);
+
+                       Assert.IsTrue(t.Wait(1000), "#1");
+                       Assert.IsNotNull(t.Result, "#2");
+                       Assert.AreEqual(t.Result.Length, 0, "#3");
+               }
+
                [Test]
                public void WhenAllResult_WithNull ()
                {
@@ -1183,7 +1337,7 @@ namespace MonoTests.System.Threading.Tasks
                                Assert.IsTrue (t.Wait (1000), "#2");
                                Assert.Fail ("#2a");
                        } catch (AggregateException e) {
-                               Assert.IsInstanceOfType (typeof (TaskCanceledException), e.InnerException, "#3");
+                               Assert.That (e.InnerException, Is.TypeOf (typeof (TaskCanceledException)), "#3");
                        }
 
                        try {
@@ -1332,7 +1486,7 @@ namespace MonoTests.System.Threading.Tasks
                        Assert.IsTrue (t.Wait (1000), "#2");
                        Assert.IsNull (t.Exception, "#3");
 
-                       Assert.IsInstanceOfType (typeof (ApplicationException), t.Result.Exception.InnerException, "#4");
+                       Assert.That (t.Result.Exception.InnerException, Is.TypeOf (typeof (ApplicationException)), "#4");
                }
 
                [Test]
@@ -1457,7 +1611,7 @@ namespace MonoTests.System.Threading.Tasks
                        Assert.IsTrue (t.Wait (1000), "#2");
                        Assert.IsNull (t.Exception, "#3");
 
-                       Assert.IsInstanceOfType (typeof (ApplicationException), t.Result.Exception.InnerException, "#4");
+                       Assert.That (t.Result.Exception.InnerException, Is.TypeOf (typeof (ApplicationException)), "#4");
                }
 
                [Test]
@@ -1534,6 +1688,16 @@ namespace MonoTests.System.Threading.Tasks
                        Assert.AreEqual ('d', d.Result, "#3r");
                }
 
+               [Test]
+               public void ContinueWith_CustomScheduleRejected ()
+               {
+                       var scheduler = new NonInlineableScheduler ();
+                       var t = Task.Factory.StartNew (delegate { }).
+                               ContinueWith (r => {}, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, scheduler);
+                       
+                       Assert.IsTrue (t.Wait (5000));
+               }
+
                [Test]
                public void FromResult ()
                {
@@ -1578,9 +1742,11 @@ namespace MonoTests.System.Threading.Tasks
                [Test]
                public void Run ()
                {
-                       var t = Task.Run (delegate { });
+                       bool ranOnDefaultScheduler = false;
+                       var t = Task.Run (delegate { ranOnDefaultScheduler = Thread.CurrentThread.IsThreadPoolThread; });
                        Assert.AreEqual (TaskCreationOptions.DenyChildAttach, t.CreationOptions, "#1");
                        t.Wait ();
+                       Assert.IsTrue (ranOnDefaultScheduler, "#2");
                }
 
                [Test]
@@ -1597,7 +1763,7 @@ namespace MonoTests.System.Threading.Tasks
                }
 
                [Test]
-               public void Run_ExistingTask ()
+               public void Run_ExistingTaskT ()
                {
                        var t = new Task<int> (() => 5);
                        var t2 = Task.Run (() => { t.Start (); return t; });
@@ -1605,6 +1771,87 @@ namespace MonoTests.System.Threading.Tasks
                        Assert.IsTrue (t2.Wait (1000), "#1");
                        Assert.AreEqual (5, t2.Result, "#2");
                }
+
+               [Test]
+               public void Run_ExistingTask ()
+               {
+                       var t = new Task (delegate { throw new Exception ("Foo"); });
+                       var t2 = Task.Run (() => { t.Start (); return t; });
+
+                       try {
+                               t2.Wait (1000);
+                               Assert.Fail ();
+                       } catch (Exception) {}
+
+                       Assert.AreEqual (TaskStatus.Faulted, t.Status, "#2");
+               }
+
+               [Test]
+               public void DenyChildAttachTest ()
+               {
+                       var mre = new ManualResetEventSlim ();
+                       Task nested = null;
+                       Task parent = Task.Factory.StartNew (() => {
+                               nested = Task.Factory.StartNew (() => mre.Wait (2000), TaskCreationOptions.AttachedToParent);
+                       }, TaskCreationOptions.DenyChildAttach);
+                       Assert.IsTrue (parent.Wait (1000), "#1");
+                       mre.Set ();
+                       Assert.IsTrue (nested.Wait (2000), "#2");
+               }
+
+               class SynchronousScheduler : TaskScheduler
+               {
+                       protected override IEnumerable<Task> GetScheduledTasks ()
+                       {
+                               throw new NotImplementedException ();
+                       }
+
+                       protected override void QueueTask (Task task)
+                       {
+                               TryExecuteTaskInline (task, false);
+                       }
+
+                       protected override bool TryExecuteTaskInline (Task task, bool taskWasPreviouslyQueued)
+                       {
+                               return base.TryExecuteTask (task);
+                       }
+               }
+
+               [Test]
+               public void HideSchedulerTest ()
+               {
+                       var mre = new ManualResetEventSlim ();
+                       var ranOnDefault = false;
+                       var scheduler = new SynchronousScheduler ();
+
+                       Task parent = Task.Factory.StartNew (() => {
+                               Task.Factory.StartNew (() => {
+                                       ranOnDefault = Thread.CurrentThread.IsThreadPoolThread;
+                                       mre.Set ();
+                               });
+                       }, CancellationToken.None, TaskCreationOptions.HideScheduler, scheduler);
+
+                       Assert.IsTrue (mre.Wait (1000), "#1");
+                       Assert.IsTrue (ranOnDefault, "#2");
+               }
+
+               [Test]
+               public void LazyCancelationTest ()
+               {
+                       var source = new CancellationTokenSource ();
+                       source.Cancel ();
+                       var parent = new Task (delegate {});
+                       var cont = parent.ContinueWith (delegate {}, source.Token, TaskContinuationOptions.LazyCancellation, TaskScheduler.Default);
+
+                       Assert.AreNotEqual (TaskStatus.Canceled, cont.Status, "#1");
+                       parent.Start ();
+                       try {
+                               Assert.IsTrue (cont.Wait (1000), "#2");
+                               Assert.Fail ();
+                       } catch (AggregateException ex) {
+                               Assert.That (ex.InnerException, Is.TypeOf (typeof (TaskCanceledException)), "#3");
+                       }
+               }
 #endif
        }
 }