BindingFlags.Public needed here as Exception.HResult is now public in .NET 4.5. This...
[mono.git] / mcs / class / Mono.Posix / Test / Mono.Unix / UnixSignalTest.cs
1 //
2 // UnixSignalTest.cs - NUnit Test Cases for Mono.Unix.UnixSignal
3 //
4 // Authors:
5 //      Jonathan Pryor  <jonpryor@vt.edu>
6 //
7 // (C) 2008 Jonathan Pryor
8 //
9
10 using NUnit.Framework;
11 using NUnit.Framework.SyntaxHelpers;
12 using System;
13 using System.Text;
14 using System.Threading;
15 using Mono.Unix;
16 using Mono.Unix.Native;
17
18 namespace NUnit.Framework.SyntaxHelpers { class Dummy {} }
19
20 namespace MonoTests.Mono.Unix {
21
22         [TestFixture]
23         public class UnixSignalTest {
24
25                 // helper method to create a thread waiting on a UnixSignal
26                 static Thread CreateWaitSignalThread (UnixSignal signal, int timeout)
27                 {
28                         Thread t1 = new Thread(delegate() {
29                                                 DateTime start = DateTime.Now;
30                                                 bool r = signal.WaitOne (timeout, false);
31                                                 DateTime end = DateTime.Now;
32                                                 Assert.AreEqual (signal.Count, 1);
33                                                 Assert.AreEqual (r, true);
34                                                 if ((end - start) > new TimeSpan (0, 0, timeout/1000))
35                                                         throw new InvalidOperationException ("Signal slept too long");
36                                         });
37                         return t1;
38                 }
39
40                 // helper method to create a two-thread test
41                 static void MultiThreadTest (UnixSignal signal, int timeout, ThreadStart tstart)
42                 {
43                         Thread t1 = CreateWaitSignalThread (signal, timeout);
44                         Thread t2 = new Thread (tstart);
45                         t1.Start ();
46                         t2.Start ();
47                         t1.Join ();
48                         t2.Join ();
49                 }
50
51                 [Test]
52                 public void TestNestedInvocation()
53                 {
54                         UnixSignal s = new UnixSignal(Signum.SIGINT);
55                         Thread a = new Thread(delegate() {
56                                         bool r = s.WaitOne (1000, false);
57       });
58                         Thread b = new Thread(delegate() {
59                                         bool r = s.WaitOne (500, false);
60       });
61                         a.Start();
62                         b.Start();
63                         a.Join();
64                         b.Join();
65                 }
66
67                 [Test]
68                 public void TestWaitAnyFailsWithMore64Signals()
69                 {
70                         UnixSignal s1 = new UnixSignal(Signum.SIGINT);
71                         UnixSignal[] signals = new UnixSignal[65];
72                         for (int i=0; i<65; ++i)
73                                 signals[i] = s1;
74                         
75                         Assert.That(UnixSignal.WaitAny(signals, new TimeSpan(0,0,1)), Is.EqualTo(-1));
76                 }
77
78                 [Test]
79                 public void TestConcurrentWaitOne()
80                 {
81                         UnixSignal s1 = new UnixSignal(Signum.SIGINT);
82                         UnixSignal s2 = new UnixSignal(Signum.SIGINT);
83                         Thread a = CreateWaitSignalThread(s1, 10000);
84                         Thread b = CreateWaitSignalThread(s2, 5000);
85                         Thread c = new Thread (delegate () {
86                                         Thread.Sleep (1000);
87                                         Stdlib.raise (Signum.SIGINT);
88                         });
89                         a.Start();
90                         b.Start();
91                         c.Start();
92                         a.Join();
93                         b.Join();
94                         c.Join();
95                         Assert.That(s1.Count, Is.EqualTo(1), "Expected 1 signal raised");
96                         Assert.That(s2.Count, Is.EqualTo(1), "Expected 1 signal raised");
97                 }
98
99                 [Test]
100                 public void TestConcurrentWaitOneSameInstance()
101                 {
102                         UnixSignal s1 = new UnixSignal(Signum.SIGINT);
103                         Thread a = CreateWaitSignalThread(s1, 10000);
104                         Thread b = CreateWaitSignalThread(s1, 10000);
105                         Thread c = new Thread (delegate () {
106                                         Thread.Sleep (500);
107                                         Stdlib.raise (Signum.SIGINT);
108                         });
109                         a.Start();
110                         b.Start();
111                         c.Start();
112                         a.Join();
113                         b.Join();
114                         c.Join();
115                 }
116
117                 [Test]
118                 public void TestSignumProperty ()
119                 {
120                         UnixSignal signal1 = new UnixSignal (Signum.SIGSEGV);
121                         Assert.That (signal1.Signum, Is.EqualTo (Signum.SIGSEGV));
122                 }
123         
124                 [Test]
125                 [Category ("NotOnMac")]
126                 public void TestRealTimeCstor ()
127                 {
128                         RealTimeSignum rts = new RealTimeSignum (0);
129                         using (UnixSignal s = new UnixSignal (rts))
130                         {
131                                 Assert.That(s.IsRealTimeSignal);
132                                 Assert.That(s.RealTimeSignum, Is.EqualTo (rts));
133                         }
134                 }
135
136                 [Test]
137                 [ExpectedException]
138                 [Category ("NotOnMac")]
139                 public void TestSignumPropertyThrows ()
140                 {
141                         UnixSignal signal1 = new UnixSignal (new RealTimeSignum (0));
142                         Signum s = signal1.Signum;
143                 }
144
145                 [Test]
146                 [Category ("NotOnMac")]
147                 public void TestRealTimeSignumProperty ()
148                 {
149                         RealTimeSignum rts = new RealTimeSignum (0);
150                         UnixSignal signal1 = new UnixSignal (rts);
151                         Assert.That (signal1.RealTimeSignum, Is.EqualTo (rts));
152                 }
153         
154                 [Test]
155                 [ExpectedException]
156                 [Category ("NotOnMac")]
157                 public void TestRealTimePropertyThrows ()
158                 {
159                         UnixSignal signal1 = new UnixSignal (Signum.SIGSEGV);
160                         RealTimeSignum s = signal1.RealTimeSignum;
161                 }
162
163                 [Test]
164                 [Category ("NotOnMac")]
165                 public void TestRaiseRTMINSignal ()
166                 {
167                         RealTimeSignum rts = new RealTimeSignum (0);
168                         using (UnixSignal signal = new UnixSignal (rts))
169                         {
170                                 MultiThreadTest (signal, 5000, delegate() {
171                                         Thread.Sleep (1000);
172                                         Stdlib.raise (rts);
173                                         });
174                         }
175                 }
176
177                 [Test]
178                 [Category ("NotOnMac")]
179                 public void TestRaiseRTMINPlusOneSignal ()
180                 {
181                         /*this number is a guestimate, but it's ok*/
182                         for (int i = 1; i < 10; ++i) {
183                                 RealTimeSignum rts = new RealTimeSignum (i);
184                                 UnixSignal signal;
185                                 try {
186                                         signal  = new UnixSignal (rts);
187                                 } catch (ArgumentException) { /*skip the ones that are unavailable*/
188                                         continue;
189                                 }
190                                 using (signal)
191                                 {
192                                         MultiThreadTest (signal, 5000, delegate() {
193                                                 Thread.Sleep(1000);
194                                                 Stdlib.raise(rts);
195                                                 });
196                                 }
197                                 return;
198                         }
199                         Assert.IsTrue (false, "#1 No available RT signal");
200                 }
201
202                 [Test]
203                 [Category ("NotOnMac")]
204                 public void TestCanRegisterRTSignalMultipleTimes ()
205                 {
206                         /*this number is a guestimate, but it's ok*/
207                         for (int i = 1; i < 10; ++i) {
208                                 RealTimeSignum rts = new RealTimeSignum (i);
209                                 UnixSignal signal;
210                                 try {
211                                         signal  = new UnixSignal (rts);
212                                 } catch (ArgumentException) { /*skip the ones that are unavailable*/
213                                         continue;
214                                 }
215                                 try {
216                                         using (UnixSignal signal2 =  new UnixSignal (rts))
217                                         {
218                                                 //ok
219                                                 return;
220                                         }
221                                 } catch (ArgumentException) { /*skip the ones that are unavailable*/
222                                                 Assert.IsTrue (false, "#1 Could not register second signal handler");
223                                 }
224                         }
225                         Assert.IsTrue (false, "#2 No available RT signal");
226                 }
227
228                 [Test]
229                 public void TestRaise ()
230                 {
231                         Thread t1 = new Thread (delegate () {
232                                         using (UnixSignal a = new UnixSignal (Signum.SIGINT)) {
233                                                 DateTime start = DateTime.Now;
234                                                 bool r = a.WaitOne (5000, false);
235                                                 DateTime end = DateTime.Now;
236                                                 Assert.AreEqual (a.Count, 1);
237                                                 Assert.AreEqual (r, true);
238                                                 if ((end - start) > new TimeSpan (0, 0, 5))
239                                                         throw new InvalidOperationException ("Signal slept too long");
240                                         }
241                         });
242                         Thread t2 = new Thread (delegate () {
243                                         Thread.Sleep (1000);
244                                         Stdlib.raise (Signum.SIGINT);
245                         });
246                         t1.Start ();
247                         t2.Start ();
248                         t1.Join ();
249                         t2.Join ();
250                 }
251
252                 [Test]
253                 public void TestRaiseAny ()
254                 {
255                         Thread t1 = new Thread (delegate () {
256                                         using (UnixSignal a = new UnixSignal (Signum.SIGINT)) {
257                                                 DateTime start = DateTime.Now;
258                                                 int idx = UnixSignal.WaitAny (new UnixSignal[]{a}, 5000);
259                                                 DateTime end = DateTime.Now;
260                                                 Assert.AreEqual (idx, 0);
261                                                 Assert.AreEqual (a.Count, 1);
262                                                 if ((end - start) > new TimeSpan (0, 0, 5))
263                                                         throw new InvalidOperationException ("Signal slept too long");
264                                         }
265                         });
266                         Thread t2 = new Thread (delegate () {
267                                         Thread.Sleep (1000);
268                                         Stdlib.raise (Signum.SIGINT);
269                         });
270                         t1.Start ();
271                         t2.Start ();
272                         t1.Join ();
273                         t2.Join ();
274                 }
275
276                 [Test]
277                 public void TestSeparation ()
278                 {
279                         Thread t1 = new Thread (delegate () {
280                                         using (UnixSignal a = new UnixSignal (Signum.SIGINT))
281                                         using (UnixSignal b = new UnixSignal (Signum.SIGTERM)) {
282                                                 DateTime start = DateTime.Now;
283                                                 int idx = UnixSignal.WaitAny (new UnixSignal[]{a, b}, 5000);
284                                                 DateTime end = DateTime.Now;
285                                                 Assert.AreEqual (idx, 1);
286                                                 Assert.AreEqual (a.Count, 0);
287                                                 Assert.AreEqual (b.Count, 1);
288                                                 if ((end - start) > new TimeSpan (0, 0, 5))
289                                                         throw new InvalidOperationException ("Signal slept too long");
290                                         }
291                         });
292                         Thread t2 = new Thread (delegate () {
293                                         Thread.Sleep (1000);
294                                         Stdlib.raise (Signum.SIGTERM);
295                         });
296                         t1.Start ();
297                         t2.Start ();
298                         t1.Join ();
299                         t2.Join ();
300                 }
301
302                 [Test]
303                 public void TestNoEmit ()
304                 {
305                         using (UnixSignal u = new UnixSignal (Signum.SIGINT)) {
306                                 DateTime start = DateTime.Now;
307                                 bool r = u.WaitOne (5100, false);
308                                 Assert.AreEqual (r, false);
309                                 DateTime end = DateTime.Now;
310                                 if ((end - start) < new TimeSpan (0, 0, 5))
311                                         throw new InvalidOperationException ("Signal didn't block for 5s; blocked for " + (end-start).ToString());
312                         }
313                 }
314
315                 [Test]
316                 public void TestNoEmitAny ()
317                 {
318                         using (UnixSignal u = new UnixSignal (Signum.SIGINT)) {
319                                 int idx = UnixSignal.WaitAny (new UnixSignal[]{u}, 5100);
320                                 Assert.AreEqual (idx, 5100);
321                         }
322                 }
323
324                 [Test]
325                 public void TestDispose1 ()
326                 {
327                         UnixSignal a = new UnixSignal (Signum.SIGINT);
328                         UnixSignal b = new UnixSignal (Signum.SIGINT);
329
330                         Stdlib.raise (Signum.SIGINT);
331
332                         Assert.AreEqual (a.Count, 1);
333                         Assert.AreEqual (b.Count, 1);
334
335                         a.Close ();
336                         b.Reset ();
337
338                         Stdlib.raise (Signum.SIGINT);
339                         Assert.AreEqual (b.Count, 1);
340
341                         b.Close ();
342                 }
343
344                 [Test]
345                 public void TestDispose2 ()
346                 {
347                         UnixSignal a = new UnixSignal (Signum.SIGINT);
348                         UnixSignal b = new UnixSignal (Signum.SIGINT);
349
350                         Stdlib.raise (Signum.SIGINT);
351
352                         Assert.AreEqual (a.Count, 1);
353                         Assert.AreEqual (b.Count, 1);
354
355                         b.Close ();
356                         a.Reset ();
357
358                         Stdlib.raise (Signum.SIGINT);
359                         Assert.AreEqual (a.Count, 1);
360
361                         a.Close ();
362                 }
363
364                 [Test]
365                 public void TestSignalActionInteraction ()
366                 {
367                         using (UnixSignal a = new UnixSignal (Signum.SIGINT)) {
368                                 Stdlib.SetSignalAction (Signum.SIGINT, SignalAction.Ignore);
369                                 Stdlib.raise (Signum.SIGINT);
370                                 Assert.AreEqual (a.Count, 0); // never invoked
371                         }
372                 }
373
374                 static readonly Signum[] signals = new Signum[] {
375                         Signum.SIGHUP, Signum.SIGINT, Signum.SIGTERM, Signum.SIGCONT,
376                 };
377
378                 const int StormCount = 100000;
379
380                 [Test]
381                 [Category("NotOnMac")] // OSX signal storming will not deliver every one
382                 public void TestRaiseStorm ()
383                 {
384                         UnixSignal[] usignals = CreateSignals (signals);
385                         Thread[] threads = new Thread[]{
386                                 CreateRaiseStormThread (StormCount/4),
387                                 CreateRaiseStormThread (StormCount/4),
388                                 CreateRaiseStormThread (StormCount/4),
389                                 CreateRaiseStormThread (StormCount/4),
390                         };
391                         foreach (Thread t in threads)
392                                 t.Start ();
393                         foreach (Thread t in threads)
394                                 t.Join ();
395                         AssertCount (usignals);
396                         CloseSignals (usignals);
397                 }
398
399                 static void AssertCount (UnixSignal[] usignals)
400                 {
401                         int sum = 0;
402                         foreach (UnixSignal s in usignals)
403                                 sum += s.Count;
404                         Assert.AreEqual (sum, StormCount);
405                 }
406
407                 static UnixSignal[] CreateSignals (Signum[] signals)
408                 {
409                         UnixSignal[] s = new UnixSignal [signals.Length];
410                         for (int i = 0; i < signals.Length; ++i)
411                                 s [i] = new UnixSignal (signals [i]);
412                         return s;
413                 }
414
415                 static void CloseSignals (UnixSignal[] signals)
416                 {
417                         foreach (UnixSignal s in signals)
418                                 s.Close ();
419                 }
420
421                 static Thread CreateRaiseStormThread (int max)
422                 {
423                         return new Thread (delegate () {
424                                 Random r = new Random (Environment.TickCount);
425                                 for (int i = 0; i < max; ++i) {
426                                         int n = r.Next (0, signals.Length);
427                                         Stdlib.raise (signals [n]);
428                                 }
429                         });
430                 }
431
432                 [Test]
433                 [Category("NotOnMac")] // OSX signal storming will not deliver every one
434                 public void TestAddRemove ()
435                 {
436                         UnixSignal[] usignals = CreateSignals (signals);
437
438                         Thread[] threads = new Thread[]{
439                                 CreateRaiseStormThread (StormCount),
440                                 CreateSignalCreatorThread (),
441                         };
442
443                         foreach (Thread t in threads)
444                                 t.Start ();
445                         foreach (Thread t in threads)
446                                 t.Join ();
447
448                         AssertCount (usignals);
449                         CloseSignals (usignals);
450                 }
451
452                 static Thread CreateSignalCreatorThread ()
453                 {
454                         return new Thread (delegate () {
455                                 Random r = new Random (Environment.TickCount << 4);
456                                 for (int i = 0; i < StormCount; ++i) {
457                                         int n = r.Next (0, signals.Length);
458                                         using (new UnixSignal (signals [n]))
459                                         using (new UnixSignal (signals [(n+1)%signals.Length]))
460                                         using (new UnixSignal (signals [(n+2)%signals.Length]))
461                                         using (new UnixSignal (signals [(n+3)%signals.Length])) {
462                                         }
463                                 }
464                         });
465                 }
466
467                 [Test]
468                 [Category("NotOnMac")] // OSX signal storming will not deliver every one
469                 public void TestWaitAny ()
470                 {
471                         UnixSignal[] usignals = CreateSignals (signals);
472
473                         Thread[] threads = new Thread[]{
474                                 CreateRaiseStormThread (StormCount),
475                                 CreateSignalCreatorThread (),
476                                 CreateWaitAnyThread (usignals [0], usignals [2]),
477                                 CreateWaitAnyThread (usignals [1], usignals [3]),
478                                 CreateWaitAnyThread (usignals [1], usignals [2]),
479                         };
480
481                         foreach (Thread t in threads)
482                                 t.Start ();
483                         foreach (Thread t in threads)
484                                 t.Join ();
485
486                         AssertCount (usignals);
487                         CloseSignals (usignals);
488                 }
489
490                 static Thread CreateWaitAnyThread (params UnixSignal[] usignals)
491                 {
492                         return new Thread (delegate () {
493                                 int idx = UnixSignal.WaitAny (usignals, 30000);
494                                 Assert.AreEqual (idx >= 0 && idx < usignals.Length, true);
495                         });
496                 }
497         }
498 }