Merge pull request #1909 from esdrubal/reflection
[mono.git] / mcs / class / corlib / Test / System.Runtime.InteropServices / SafeHandleTest.cs
1 //
2 // System.Runtime.InteropServices.SafeHandle Test Cases
3 //
4 // Authors:
5 //      Miguel de Icaza (miguel@novell.com)
6 //
7 // Copyright (C) 2004-2006 Novell, Inc (http://www.novell.com)
8 //
9 using NUnit.Framework;
10 using System;
11 using System.Runtime.InteropServices;
12 using System.Security;
13 using Microsoft.Win32.SafeHandles;
14
15 namespace MonoTests.System.Runtime.InteropServices
16 {
17         [TestFixture]
18         public class SafeHandleTest 
19         {
20                 //
21                 // This mimics SafeFileHandle, but does not actually own a handle
22                 // We use this to test ownership and dispose exceptions.
23                 //
24                 public class FakeSafeHandle : SafeHandleZeroOrMinusOneIsInvalid
25                 {
26                         public bool released = false;
27                         public bool disposed = false;
28                         
29                         public FakeSafeHandle (): base (true)
30                         {
31                         }
32                         
33                         public FakeSafeHandle (bool ownership) : base (ownership)
34                         {
35                         }
36
37                         public void ChangeHandle (IntPtr hnd)
38                         {
39                                 this.handle = hnd;
40                         }
41
42                         protected override bool ReleaseHandle ()
43                         {
44                                 released = true;
45                                 return true;
46                         }
47
48                         protected override void Dispose (bool manual)
49                         {
50                                 disposed = true;
51 #if !MONODROID
52                                 // Bombs on Android (ObjectDisposedException)
53                                 base.Dispose (manual);
54 #endif
55                         }
56                 }
57
58                 [Test]
59                 public void SimpleDispose ()
60                 {
61                         FakeSafeHandle sf = new FakeSafeHandle ();
62                         sf.Dispose ();
63                 }
64
65                 [Test]
66                 public void BadDispose1 ()
67                 {
68                         FakeSafeHandle sf = new FakeSafeHandle ();
69
70                         sf.DangerousRelease ();
71
72                         try {
73                                 sf.DangerousRelease ();
74                                 Assert.Fail ("#1");
75                         } catch (ObjectDisposedException) {
76                         }
77
78                         GC.SuppressFinalize (sf);
79                 }
80
81                 [Test]
82                 [ExpectedException (typeof (ObjectDisposedException))]
83                 [Category ("AndroidNotWorking")] // Because of the FakeSafeHandle.Dispose issue
84                 public void BadDispose2 ()
85                 {
86                         FakeSafeHandle sf = new FakeSafeHandle ();
87
88                         sf.Close ();
89                         sf.DangerousRelease ();
90                 }
91
92                 [Test]
93                 [ExpectedException (typeof (ObjectDisposedException))]
94                 [Category ("AndroidNotWorking")] // Because of the FakeSafeHandle.Dispose issue
95                 public void BadDispose3 ()
96                 {
97                         FakeSafeHandle sf = new FakeSafeHandle ();
98
99                         sf.Dispose ();
100                         sf.DangerousRelease ();
101                 }
102
103                 [Test]
104                 public void MultipleDisposes ()
105                 {
106                         FakeSafeHandle sf = new FakeSafeHandle ();
107
108                         sf.Dispose ();
109                         sf.Dispose ();
110                         sf.Dispose ();
111                 }
112
113                 [Test]
114                 public void CloseWillDispose ()
115                 {
116                         FakeSafeHandle sf = new FakeSafeHandle ();
117
118                         sf.Close ();
119                         Assert.IsTrue (sf.disposed, "disposed");
120                 }
121
122                 [Test]
123                 [Category ("AndroidNotWorking")] // Because of the FakeSafeHandle.Dispose issue
124                 public void GoodDispose ()
125                 {
126                         int dummyHandle = 0xDEAD;
127                         FakeSafeHandle sf = new FakeSafeHandle ();
128                         sf.ChangeHandle (new IntPtr (dummyHandle));
129                         Assert.AreEqual ((int)sf.DangerousGetHandle(), dummyHandle, "handle");
130
131                         sf.DangerousRelease ();
132
133                         try {
134                                 sf.Close ();
135                                 Assert.Fail ("#1");
136                         } catch (ObjectDisposedException) {
137                         }
138
139                         try {
140                                 sf.Dispose ();
141                                 Assert.Fail ("#2");
142                         } catch (ObjectDisposedException) {
143                         }
144
145                         //In Ms.Net SafeHandle does not change the value of the handle after being SetInvalid or Disposed.
146                         Assert.AreEqual ((int)sf.DangerousGetHandle(), dummyHandle, "handle");
147                         //Handle was closed properly.
148                         Assert.IsTrue (sf.released, "released");
149                         Assert.IsTrue (sf.IsClosed, "closed");
150                         //Handle value is not changed, so the value itself is still valid (not 0 or -1)
151                         Assert.IsFalse (sf.IsInvalid, "invalid");
152
153                         GC.SuppressFinalize (sf);
154                 }
155
156                 [Test]
157                 public void SetHandleAsInvalid ()
158                 {
159                         int dummyHandle = 0xDEAD;
160                         FakeSafeHandle sf = new FakeSafeHandle ();
161
162                         sf.ChangeHandle (new IntPtr (dummyHandle));
163                         Assert.AreEqual ((int)sf.DangerousGetHandle(), dummyHandle, "handle");
164
165                         sf.SetHandleAsInvalid();
166
167                         //In Ms.Net SafeHandle does not change the value of the handle after being SetInvalid or Disposed.
168                         Assert.AreEqual ((int)sf.DangerousGetHandle(), dummyHandle, "handle");
169                         //Released == false since handle was not released, Set Invalid was called before it could be released.
170                         Assert.IsFalse (sf.released, "released");
171                         //IsClosed == true since handle is pointing to a disposed or invalid object.
172                         Assert.IsTrue (sf.IsClosed, "closed");
173                         //Handle value is not changed, so the value itself is still valid (not 0 or -1)
174                         Assert.IsFalse (sf.IsInvalid, "invalid");
175                 }
176
177                 [Test]
178                 public void SetInvalidDispose ()
179                 {
180                         int dummyHandle = 0xDEAD;
181                         FakeSafeHandle sf = new FakeSafeHandle (true);
182
183                         sf.ChangeHandle (new IntPtr (dummyHandle));
184                         Assert.AreEqual ((int)sf.DangerousGetHandle(), dummyHandle, "handle");
185
186                         sf.SetHandleAsInvalid();
187                         sf.Dispose ();
188
189                         //In Ms.Net SafeHandle does not change the value of the handle after being SetInvalid or Disposed.
190                         Assert.AreEqual ((int)sf.DangerousGetHandle(), dummyHandle, "handle");
191                         //Released == false since handle was not released, Set Invalid was called before it could be released.
192                         Assert.IsFalse (sf.released, "released");
193                         //IsClosed == true since handle is pointing to a disposed or invalid object.
194                         Assert.IsTrue (sf.IsClosed, "closed");
195                         //Handle value is not changed, so the value itself is still valid (not 0 or -1)
196                         Assert.IsFalse (sf.IsInvalid, "invalid");
197                 }
198
199                 [Test]
200                 public void SetInvalidRelease1 ()
201                 {
202                         FakeSafeHandle sf = new FakeSafeHandle (true);
203
204                         bool success = false;
205                         sf.DangerousAddRef(ref success);
206                         Assert.IsTrue (success, "dar");
207
208                         sf.SetHandleAsInvalid();
209
210                         Assert.IsFalse (sf.released, "released");
211                         Assert.IsTrue (sf.IsClosed, "closed");
212
213                         //Allow remaining refs to be released after SetHandleAsInvalid
214                         sf.DangerousRelease ();
215                         sf.DangerousRelease ();
216
217                         Assert.IsFalse (sf.released, "released");
218                         Assert.IsTrue (sf.IsClosed, "closed");
219                 }
220
221                 [Test]
222                 [ExpectedException (typeof (ObjectDisposedException))]
223                 public void SetInvalidRelease2 ()
224                 {
225                         FakeSafeHandle sf = new FakeSafeHandle (true);
226
227                         bool success = false;
228                         sf.DangerousAddRef(ref success);
229                         Assert.IsTrue (success, "dar");
230
231                         sf.SetHandleAsInvalid();
232                         sf.DangerousRelease ();
233                         sf.DangerousRelease ();
234
235                         //This release need to throw ObjectDisposedException.
236                         //No more ref to release.
237                         sf.DangerousRelease ();
238                 }
239
240                 [Test]
241                 [Category ("AndroidNotWorking")] // Because of the FakeSafeHandle.Dispose issue
242                 public void ReleaseAfterDispose1 ()
243                 {
244                         int dummyHandle = 0xDEAD;
245                         FakeSafeHandle sf = new FakeSafeHandle (true);
246                         sf.ChangeHandle (new IntPtr (dummyHandle));
247                         Assert.AreEqual ((int)sf.DangerousGetHandle(), dummyHandle, "handle");
248
249                         bool success = false;
250                         sf.DangerousAddRef(ref success);
251                         Assert.IsTrue (success, "dar");
252
253                         sf.Dispose ();
254                         //Still one ref left.
255                         Assert.IsFalse (sf.released, "released");
256                         Assert.IsFalse (sf.IsClosed, "closed");
257
258                         sf.DangerousRelease ();
259                         //In Ms.Net SafeHandle does not change the value of the handle after being SetInvalid or Disposed.
260                         Assert.AreEqual ((int)sf.DangerousGetHandle(), dummyHandle, "handle");
261                         //Handle was closed properly.
262                         Assert.IsTrue (sf.released, "released");
263                         Assert.IsTrue (sf.IsClosed, "closed");
264                         //Handle value is not changed, so the value itself is still valid (not 0 or -1)
265                         Assert.IsFalse (sf.IsInvalid, "invalid");
266                 }
267
268                 [Test]
269                 [ExpectedException (typeof (ObjectDisposedException))]
270                 [Category ("AndroidNotWorking")] // Because of the FakeSafeHandle.Dispose issue
271                 public void ReleaseAfterDispose2 ()
272                 {
273                         FakeSafeHandle sf = new FakeSafeHandle (true);
274
275                         bool success = false;
276                         sf.DangerousAddRef(ref success);
277                         Assert.IsTrue (success, "dar");
278
279                         sf.Dispose ();
280
281                         sf.DangerousRelease ();
282
283                         //Second release need to throw ObjectDisposedException.
284                         //No more ref to release.
285                         sf.DangerousRelease ();
286                 }
287
288                 [Test]
289                 [Category ("AndroidNotWorking")] // Not until Dispose runtime crasher in FakeSafeHandle is fixed
290                 public void NoReleaseUnowned ()
291                 {
292                         FakeSafeHandle sf = new FakeSafeHandle (false);
293
294                         sf.Close ();
295                         Assert.IsFalse (sf.released, "r1");
296                         Assert.IsTrue (sf.IsClosed, "c1");
297
298                         sf = new FakeSafeHandle (false);
299                         sf.DangerousRelease ();
300                         Assert.IsFalse (sf.released, "r2");
301                         Assert.IsTrue (sf.IsClosed, "c2");
302
303                         sf = new FakeSafeHandle (false);
304                         ((IDisposable) sf).Dispose ();
305                         Assert.IsFalse (sf.released, "r3");
306                         Assert.IsTrue (sf.IsClosed, "c3");
307                 }
308
309                 //
310                 // This test does a DangerousAddRef on a new instance
311                 // of a custom user Safe Handle, and it just happens
312                 // that the default value for the handle is an invalid
313                 // handle.
314                 //
315                 // .NET does not throw an exception in this case, so
316                 // we should not either
317                 //
318                 [Test]
319                 public void DangerousAddRefOnNewInstance ()
320                 {
321                         FakeSafeHandle sf = new FakeSafeHandle ();
322                         sf.ChangeHandle (IntPtr.Zero);
323                         Assert.IsTrue (sf.IsInvalid, "invalid");
324
325                         bool success = false;
326                         sf.DangerousAddRef (ref success);
327                         Assert.IsTrue (success, "daroni");
328                 }
329         }
330 }
331