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