[bcl] Add NUnitHelper.cs with API not in nunit-lite
[mono.git] / mcs / class / corlib / Test / System.Threading.Tasks / TaskSchedulerTest.cs
1 // TaskSchedulerTest.cs
2 //
3 // Copyright (c) 2008 Jérémie "Garuma" Laval
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 // THE SOFTWARE.
22 //
23 //
24
25
26 using System;
27 using System.Threading;
28 using System.Threading.Tasks;
29 using System.Collections.Generic;
30
31 using NUnit.Framework;
32
33 namespace MonoTests.System.Threading.Tasks
34 {
35         [TestFixture]
36         public class TaskSchedulerTests
37         {
38                 class LazyCatScheduler : TaskScheduler
39                 {
40                         public TaskStatus ExecuteInlineStatus
41                         {
42                                 get;
43                                 set;
44                         }
45
46                         protected override void QueueTask (Task task)
47                         {
48                                 throw new NotImplementedException ();
49                         }
50
51                         protected override bool TryDequeue (Task task)
52                         {
53                                 throw new NotImplementedException ();
54                         }
55
56                         protected override bool TryExecuteTaskInline (Task task, bool taskWasPreviouslyQueued)
57                         {
58                                 ExecuteInlineStatus = task.Status;
59                                 return true;
60                         }
61
62                         protected override IEnumerable<Task> GetScheduledTasks ()
63                         {
64                                 throw new NotImplementedException ();
65                         }
66                 }
67
68                 class DefaultScheduler : TaskScheduler
69                 {
70                         protected override IEnumerable<Task> GetScheduledTasks ()
71                         {
72                                 throw new NotImplementedException ();
73                         }
74
75                         protected override void QueueTask (Task task)
76                         {
77                                 throw new NotImplementedException ();
78                         }
79
80                         protected override bool TryExecuteTaskInline (Task task, bool taskWasPreviouslyQueued)
81                         {
82                                 throw new NotImplementedException ();
83                         }
84
85                         public void TestDefaultMethod ()
86                         {
87                                 Assert.IsFalse (TryDequeue (null), "#1");
88                         }
89                 }
90
91                 class UserSynchronizationContext : SynchronizationContext
92                 {
93                         
94                 }
95
96                 [Test]
97                 public void FromCurrentSynchronizationContextTest_Invalid()
98                 {
99                         var c = SynchronizationContext.Current;
100                         try {
101                                 SynchronizationContext.SetSynchronizationContext (null);
102                                 TaskScheduler.FromCurrentSynchronizationContext ();
103                                 Assert.Fail ("#1");
104                         } catch (InvalidOperationException) {
105                         } finally {
106                                 SynchronizationContext.SetSynchronizationContext (c);
107                         }
108                 }
109
110                 [Test]
111                 public void FromUserSynchronizationContext ()
112                 {
113                         var c = SynchronizationContext.Current;
114                         try {
115                                 SynchronizationContext.SetSynchronizationContext (new UserSynchronizationContext ());
116                                 var ts = TaskScheduler.FromCurrentSynchronizationContext ();
117                                 Assert.AreEqual (1, ts.MaximumConcurrencyLevel, "#1");
118                         } finally {
119                                 SynchronizationContext.SetSynchronizationContext (c);
120                         }
121                 }
122
123                 [Test]
124                 public void BasicRunSynchronouslyTest ()
125                 {
126                         bool ran = false;
127                         var t = new Task (() => ran = true);
128
129                         t.RunSynchronously ();
130                         Assert.IsTrue (t.IsCompleted);
131                         Assert.IsFalse (t.IsFaulted);
132                         Assert.IsFalse (t.IsCanceled);
133                         Assert.IsTrue (ran);
134                 }
135
136                 [Test]
137                 public void RunSynchronouslyButNoExecutionTest ()
138                 {
139                         TaskSchedulerException ex = null;
140
141                         var ts = new LazyCatScheduler ();
142                         Task t = new Task (() => {});
143
144                         try {
145                                 t.RunSynchronously (ts);
146                         } catch (TaskSchedulerException e) {
147                                 ex = e;
148                         }
149
150                         Assert.IsNotNull (ex);
151                         Assert.IsNotNull (ex.InnerException);
152                         Assert.That (ex.InnerException, Is.TypeOf (typeof (InvalidOperationException)));
153                 }
154
155                 [Test]
156                 public void RunSynchronouslyTaskStatusTest ()
157                 {
158                         var ts = new LazyCatScheduler ();
159                         var t = new Task (() => { });
160
161                         try {
162                                 t.RunSynchronously (ts);
163                         } catch {}
164                         Assert.AreEqual (TaskStatus.WaitingToRun, ts.ExecuteInlineStatus);
165                 }
166
167                 static int finalizerThreadId = -1;
168         
169                 class FinalizerCatcher
170                 {
171                         ~FinalizerCatcher ()
172                         {
173                                 finalizerThreadId = Thread.CurrentThread.ManagedThreadId;
174                         }
175                 }
176
177                 [Test]
178                 public void DefaultBehaviourTest ()
179                 {
180                         var s = new DefaultScheduler ();
181                         s.TestDefaultMethod ();
182                 }
183
184                 // This test doesn't work if the GC uses multiple finalizer thread.
185                 // For now it's fine since only one thread is used
186                 [Test]
187                 // Depends on objects getting GCd plus installs an EH handler which catches
188                 // exceptions thrown by other tasks
189                 [Category ("NotWorking")]
190                 public void UnobservedTaskExceptionOnFinalizerThreadTest ()
191                 {
192                         var foo = new FinalizerCatcher ();
193                         foo = null;
194                         GC.Collect ();
195                         GC.WaitForPendingFinalizers ();
196                         // Same than following test, if GC didn't run don't execute the rest of this test
197                         if (finalizerThreadId == -1)
198                                 return;
199
200                         int evtThreadId = -2;
201                         TaskScheduler.UnobservedTaskException += delegate {
202                                 evtThreadId = Thread.CurrentThread.ManagedThreadId;
203                         };
204                         var evt = new ManualResetEventSlim ();
205                         CreateAndForgetFaultedTask (evt);
206                         evt.Wait (500);
207                         Thread.Sleep (100);
208                         GC.Collect ();
209                         GC.WaitForPendingFinalizers ();
210                         Assert.AreEqual (finalizerThreadId, evtThreadId, "Should be ran on finalizer thread");
211                 }
212
213                 [Test]
214                 // Depends on objects getting GCd plus installs an EH handler which catches
215                 // exceptions thrown by other tasks
216                 [Category ("NotWorking")]
217                 public void UnobservedTaskExceptionArgumentTest ()
218                 {
219                         bool ran = false;
220                         bool senderIsRight = false;
221                         UnobservedTaskExceptionEventArgs args = null;
222
223                         TaskScheduler.UnobservedTaskException += (o, a) => {
224                                 senderIsRight = o.GetType ().ToString () == "System.Threading.Tasks.Task";
225                                 args = a;
226                                 ran = true;
227                         };
228
229                         var evt = new ManualResetEventSlim ();
230                         CreateAndForgetFaultedTask (evt);
231                         evt.Wait (500);
232                         Thread.Sleep (100);
233                         GC.Collect ();
234                         GC.WaitForPendingFinalizers ();
235
236                         // GC is too unreliable for some reason in that test, so backoff if finalizer wasn't ran
237                         // it needs to be run for the above test to work though (♥)
238                         if (!ran)
239                                 return;
240
241                         Assert.IsNotNull (args.Exception);
242                         Assert.IsNotNull (args.Exception.InnerException);
243                         Assert.AreEqual ("foo", args.Exception.InnerException.Message);
244                         Assert.IsFalse (args.Observed);
245                         Assert.IsTrue (senderIsRight, "Sender is a task");
246                 }
247
248                 // We use this intermediary method to improve chances of GC kicking
249                 static void CreateAndForgetFaultedTask (ManualResetEventSlim evt)
250                 {
251                         Task.Factory.StartNew (() => { evt.Set (); throw new Exception ("foo"); });
252                 }
253         }
254 }