Merge pull request #1502 from madrang/SafeHandle.CloseTestDispose
[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                                 base.Dispose (manual);
52                         }
53                 }
54                 
55                 [Test]
56                 [ExpectedException (typeof (ObjectDisposedException))]
57                 public void BadDispose1 ()
58                 {
59                         FakeSafeHandle sf = new FakeSafeHandle ();
60
61                         sf.DangerousRelease ();
62                         sf.DangerousRelease ();
63                 }
64
65                 [Test]
66                 [ExpectedException (typeof (ObjectDisposedException))]
67                 public void BadDispose2 ()
68                 {
69                         FakeSafeHandle sf = new FakeSafeHandle ();
70
71                         sf.Close ();
72                         sf.DangerousRelease ();
73                 }
74
75                 [Test]
76                 [ExpectedException (typeof (ObjectDisposedException))]
77                 public void BadDispose3 ()
78                 {
79                         FakeSafeHandle sf = new FakeSafeHandle ();
80
81                         sf.Dispose ();
82                         sf.DangerousRelease ();
83                 }
84
85                 [Test]
86                 public void MultipleDisposes ()
87                 {
88                         FakeSafeHandle sf = new FakeSafeHandle ();
89
90                         sf.Dispose ();
91                         sf.Dispose ();
92                         sf.Dispose ();
93                 }
94
95                 [Test]
96                 public void CloseWillDispose ()
97                 {
98                         FakeSafeHandle sf = new FakeSafeHandle ();
99
100                         sf.Close ();
101                         Assert.IsTrue (sf.disposed, "disposed");
102                 }
103
104                 [Test]
105                 public void GoodDispose ()
106                 {
107                         int dummyHandle = 0xDEAD;
108                         FakeSafeHandle sf = new FakeSafeHandle ();
109                         sf.ChangeHandle (new IntPtr (dummyHandle));
110                         Assert.AreEqual ((int)sf.DangerousGetHandle(), dummyHandle, "handle");
111
112                         sf.DangerousRelease ();
113
114                         try {
115                                 sf.Close ();
116                                 Assert.Fail ("#1");
117                         } catch (ObjectDisposedException) {
118                         }
119
120                         try {
121                                 sf.Dispose ();
122                                 Assert.Fail ("#2");
123                         } catch (ObjectDisposedException) {
124                         }
125
126                         //In Ms.Net SafeHandle does not change the value of the handle after being SetInvalid or Disposed.
127                         Assert.AreEqual ((int)sf.DangerousGetHandle(), dummyHandle, "handle");
128                         //Handle was closed properly.
129                         Assert.IsTrue (sf.released, "released");
130                         Assert.IsTrue (sf.IsClosed, "closed");
131                         //Handle value is not changed, so the value itself is still valid (not 0 or -1)
132                         Assert.IsFalse (sf.IsInvalid, "invalid");
133                 }
134
135                 [Test]
136                 public void SetHandleAsInvalid ()
137                 {
138                         int dummyHandle = 0xDEAD;
139                         FakeSafeHandle sf = new FakeSafeHandle ();
140
141                         sf.ChangeHandle (new IntPtr (dummyHandle));
142                         Assert.AreEqual ((int)sf.DangerousGetHandle(), dummyHandle, "handle");
143
144                         sf.SetHandleAsInvalid();
145
146                         //In Ms.Net SafeHandle does not change the value of the handle after being SetInvalid or Disposed.
147                         Assert.AreEqual ((int)sf.DangerousGetHandle(), dummyHandle, "handle");
148                         //Released == false since handle was not released, Set Invalid was called before it could be released.
149                         Assert.IsFalse (sf.released, "released");
150                         //IsClosed == true since handle is pointing to a disposed or invalid object.
151                         Assert.IsTrue (sf.IsClosed, "closed");
152                         //Handle value is not changed, so the value itself is still valid (not 0 or -1)
153                         Assert.IsFalse (sf.IsInvalid, "invalid");
154                 }
155
156                 [Test]
157                 public void SetInvalidDispose ()
158                 {
159                         int dummyHandle = 0xDEAD;
160                         FakeSafeHandle sf = new FakeSafeHandle (true);
161
162                         sf.ChangeHandle (new IntPtr (dummyHandle));
163                         Assert.AreEqual ((int)sf.DangerousGetHandle(), dummyHandle, "handle");
164
165                         sf.SetHandleAsInvalid();
166                         sf.Dispose ();
167
168                         //In Ms.Net SafeHandle does not change the value of the handle after being SetInvalid or Disposed.
169                         Assert.AreEqual ((int)sf.DangerousGetHandle(), dummyHandle, "handle");
170                         //Released == false since handle was not released, Set Invalid was called before it could be released.
171                         Assert.IsFalse (sf.released, "released");
172                         //IsClosed == true since handle is pointing to a disposed or invalid object.
173                         Assert.IsTrue (sf.IsClosed, "closed");
174                         //Handle value is not changed, so the value itself is still valid (not 0 or -1)
175                         Assert.IsFalse (sf.IsInvalid, "invalid");
176                 }
177
178                 [Test]
179                 public void SetInvalidRelease1 ()
180                 {
181                         FakeSafeHandle sf = new FakeSafeHandle (true);
182
183                         bool success = false;
184                         sf.DangerousAddRef(ref success);
185                         Assert.IsTrue (success, "dar");
186
187                         sf.SetHandleAsInvalid();
188
189                         Assert.IsFalse (sf.released, "released");
190                         Assert.IsTrue (sf.IsClosed, "closed");
191
192                         //Allow remaining refs to be released after SetHandleAsInvalid
193                         sf.DangerousRelease ();
194                         sf.DangerousRelease ();
195
196                         Assert.IsFalse (sf.released, "released");
197                         Assert.IsTrue (sf.IsClosed, "closed");
198                 }
199
200                 [Test]
201                 [ExpectedException (typeof (ObjectDisposedException))]
202                 public void SetInvalidRelease2 ()
203                 {
204                         FakeSafeHandle sf = new FakeSafeHandle (true);
205
206                         bool success = false;
207                         sf.DangerousAddRef(ref success);
208                         Assert.IsTrue (success, "dar");
209
210                         sf.SetHandleAsInvalid();
211                         sf.DangerousRelease ();
212                         sf.DangerousRelease ();
213
214                         //This release need to throw ObjectDisposedException.
215                         //No more ref to release.
216                         sf.DangerousRelease ();
217                 }
218
219                 [Test]
220                 public void ReleaseAfterDispose1 ()
221                 {
222                         int dummyHandle = 0xDEAD;
223                         FakeSafeHandle sf = new FakeSafeHandle (true);
224                         sf.ChangeHandle (new IntPtr (dummyHandle));
225                         Assert.AreEqual ((int)sf.DangerousGetHandle(), dummyHandle, "handle");
226
227                         bool success = false;
228                         sf.DangerousAddRef(ref success);
229                         Assert.IsTrue (success, "dar");
230
231                         sf.Dispose ();
232                         //Still one ref left.
233                         Assert.IsFalse (sf.released, "released");
234                         Assert.IsFalse (sf.IsClosed, "closed");
235
236                         sf.DangerousRelease ();
237                         //In Ms.Net SafeHandle does not change the value of the handle after being SetInvalid or Disposed.
238                         Assert.AreEqual ((int)sf.DangerousGetHandle(), dummyHandle, "handle");
239                         //Handle was closed properly.
240                         Assert.IsTrue (sf.released, "released");
241                         Assert.IsTrue (sf.IsClosed, "closed");
242                         //Handle value is not changed, so the value itself is still valid (not 0 or -1)
243                         Assert.IsFalse (sf.IsInvalid, "invalid");
244                 }
245
246                 [Test]
247                 [ExpectedException (typeof (ObjectDisposedException))]
248                 public void ReleaseAfterDispose2 ()
249                 {
250                         FakeSafeHandle sf = new FakeSafeHandle (true);
251
252                         bool success = false;
253                         sf.DangerousAddRef(ref success);
254                         Assert.IsTrue (success, "dar");
255
256                         sf.Dispose ();
257
258                         sf.DangerousRelease ();
259
260                         //Second release need to throw ObjectDisposedException.
261                         //No more ref to release.
262                         sf.DangerousRelease ();
263                 }
264
265                 [Test]
266                 public void NoReleaseUnowned ()
267                 {
268                         FakeSafeHandle sf = new FakeSafeHandle (false);
269
270                         sf.Close ();
271                         Assert.IsFalse (sf.released, "r1");
272                         Assert.IsTrue (sf.IsClosed, "c1");
273
274                         sf = new FakeSafeHandle (false);
275                         sf.DangerousRelease ();
276                         Assert.IsFalse (sf.released, "r2");
277                         Assert.IsTrue (sf.IsClosed, "c2");
278
279                         sf = new FakeSafeHandle (false);
280                         ((IDisposable) sf).Dispose ();
281                         Assert.IsFalse (sf.released, "r3");
282                         Assert.IsTrue (sf.IsClosed, "c3");
283                 }
284
285                 //
286                 // This test does a DangerousAddRef on a new instance
287                 // of a custom user Safe Handle, and it just happens
288                 // that the default value for the handle is an invalid
289                 // handle.
290                 //
291                 // .NET does not throw an exception in this case, so
292                 // we should not either
293                 //
294                 [Test]
295                 public void DangerousAddRefOnNewInstance ()
296                 {
297                         FakeSafeHandle sf = new FakeSafeHandle ();
298                         sf.ChangeHandle (IntPtr.Zero);
299                         Assert.IsTrue (sf.IsInvalid, "invalid");
300
301                         bool success = false;
302                         sf.DangerousAddRef (ref success);
303                         Assert.IsTrue (success, "daroni");
304                 }
305         }
306 }
307