context: //
  // ConditionalWeakTableTest.cs
  //
  // Author:
  //	Rodrigo Kumpera   <rkumpera@novell.com>
  //
  // Copyright (C) 2010 Novell, Inc (http://www.novell.com)
  //
  // Permission is hereby granted, free of charge, to any person obtaining
  // a copy of this software and associated documentation files (the
  // "Software"), to deal in the Software without restriction, including
  // without limitation the rights to use, copy, modify, merge, publish,
  // distribute, sublicense, and/or sell copies of the Software, and to
  // permit persons to whom the Software is furnished to do so, subject to
  // the following conditions:
  // 
  // The above copyright notice and this permission notice shall be
  // included in all copies or substantial portions of the Software.
  // 
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  //
  
  #if NET_4_0
  
  using NUnit.Framework;
  using System;
  using System.Reflection;
  using System.Runtime.CompilerServices;
  using System.Runtime.InteropServices;
  using System.Runtime.Serialization;
  using System.Security.Permissions;
  using System.Collections.Generic;
  using System.Threading;
  
  
  namespace MonoTests.System.Runtime.CompilerServices {
  
  	[TestFixture]
  	public class ConditionalWeakTableTest {
  
  	public class Link {
  		public object obj;
  	
  		public Link(object obj) { 
  			this.obj = obj;
  		}
  	}
  
  	class Key {
  		public int Foo;
  		public override string ToString () {
  				return "key-" + Foo;
  			}
  	}
  
  	class Val {
  		public int Foo;
  		public override string ToString () {
  			return "value-" + Foo;
  		}
  	}
  	
  
  	[Test]
  	public void GetValue () {
  		var cwt = new ConditionalWeakTable <object,object> ();
  
  		try {
  			cwt.GetValue (null, k => null);
  			Assert.Fail ("#0");
  		} catch (ArgumentNullException) {}
  
  		try {
  			cwt.GetValue (20, null);
  			Assert.Fail ("#1");
  		} catch (ArgumentNullException) {}
  
  
  		object key = "foo";
  		object val = cwt.GetValue (key, k => new Link (k));
  		Assert.IsTrue (val != null, "#2");
  		Assert.AreEqual (typeof (Link), val.GetType () , "#3");
  
  		Assert.AreEqual (val, cwt.GetValue (key, k => new object ()), "#4");
  	}
  
  	[Test]
  	public void GetOrCreateValue () {
  		var cwt = new ConditionalWeakTable <object,object> ();
  
  		try {
  			cwt.GetOrCreateValue (null);
  			Assert.Fail ("#0");
  		} catch (ArgumentNullException) {}
  
  
  		object key = "foo";
  		object val = cwt.GetOrCreateValue (key);
  		Assert.IsTrue (val != null, "#2");
  		Assert.AreEqual (typeof (object), val.GetType () , "#3");
  
  		Assert.AreEqual (val, cwt.GetOrCreateValue (key), "#4");
  
  		var cwt2 = new ConditionalWeakTable <object, string> ();
  		try {
  			cwt2.GetOrCreateValue (key);
  			Assert.Fail ("#5");
  		} catch (MissingMethodException) {}
  	}
  
  	[Test]
  	public void Remove () {
  		var cwt = new ConditionalWeakTable <object,object> ();
  		object c = new Key ();
  
  		cwt.Add (c, "x");
  
  		try {
  			cwt.Remove (null);
  			Assert.Fail ("#0");
  		} catch (ArgumentNullException) {}
  
  
  		Assert.IsFalse (cwt.Remove ("x"), "#1");
  		Assert.IsTrue (cwt.Remove (c), "#2");
  		Assert.IsFalse (cwt.Remove (c), "#3");
  	}
  
  	[Test]
  	public void Add () {
  		var cwt = new ConditionalWeakTable <object,object> ();
  		object c = new Key ();
  
  		cwt.Add (c, new Link (c));
  
  		try {
  			cwt.Add (c, new Link (c));
  			Assert.Fail ("#0");
  		} catch (ArgumentException) {}
  
  		cwt.Add ("zzz", null);//ok
  
  		try {
  			cwt.Add (null, new Link (c));
  			Assert.Fail ("#1");
  		} catch (ArgumentNullException) {}
  	}
  
  	[Test]
  	public void TryGetValue () {
  		var cwt = new ConditionalWeakTable <object,object> ();
  		object res;
  		object c = new Key ();
  
  		cwt.Add (c, "foo");
  
  		try {
  			cwt.TryGetValue (null, out res);
  			Assert.Fail ("#0");
  		} catch (ArgumentNullException) {}
  
  		Assert.IsFalse (cwt.TryGetValue ("foo", out res), "#1");
  		Assert.IsTrue (cwt.TryGetValue (c, out res), "#2");
  		Assert.AreEqual ("foo", res, "#3");
  	}
  
  
  	static void FillStuff (ConditionalWeakTable <object,object> cwt, 
  			out List<object> keepAlive, out List<WeakReference> keys) {
  
  		keepAlive = new List<object> ();
  		keys = new List<WeakReference> ();
  
  		object a = new Key ();
  		object b = new Key ();
  		object c = new Key ();
  		
  		cwt.Add (a, new string ("str0".ToCharArray ()));
  		cwt.Add (b, "str1");
  		cwt.Add (c, new Link (a));
  
  		keepAlive.Add (c);
  		keys.Add (new WeakReference(a));
  		keys.Add (new WeakReference(b));
  		keys.Add (new WeakReference(c));
  	}
  
  	[Test]
  	public void Reachability () {
  		if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
  			Assert.Ignore ("Not working on Boehm.");
  		var cwt = new ConditionalWeakTable <object,object> ();
  		List<object> keepAlive = null;
  		List<WeakReference> keys = null;
  		Thread t = new Thread (delegate () {
  
gt: 				FillStuff (cwt, out keepAlive, out keys);