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