context: // ThreadTest.cs - NUnit Test Cases for the System.Threading.Thread class
  //
  // Authors
  //	Eduardo Garcia Cebollero (kiwnix@yahoo.es)
  //	Sebastien Pouliot  <sebastien@ximian.com>
  //
  // (C) Eduardo Garcia Cebollero.
  // (C) Ximian, Inc.  http://www.ximian.com
  // (C) 2004 Novell (http://www.novell.com)
  //
  
  using System;
  using System.Globalization;
  using System.Security.Principal;
  using System.Threading;
  
  using NUnit.Framework;
  
  namespace MonoTests.System.Threading
  {
  	// These tests seem to hang the 2.0 framework. So they are disabled for now
  	// Don't reenable them until you can run a few thousand times on an SMP box.
  	[Category ("NotWorking")]
  	public class ThreadedPrincipalTest
  	{
  		public static void NoPrincipal () 
  		{
  			AppDomain.CurrentDomain.SetPrincipalPolicy (PrincipalPolicy.NoPrincipal);
  			IPrincipal p = Thread.CurrentPrincipal;
  			Assert.IsNull (p, "#1");
  
  			Thread.CurrentPrincipal = new GenericPrincipal (new GenericIdentity ("mono"), null);
  			Assert.IsNotNull (Thread.CurrentPrincipal, "#2");
  
  			Thread.CurrentPrincipal = null;
  			Assert.IsNull (Thread.CurrentPrincipal, "#3");
  			// in this case we can return to null
  		}
  
  		public static void UnauthenticatedPrincipal () 
  		{
  			AppDomain.CurrentDomain.SetPrincipalPolicy (PrincipalPolicy.UnauthenticatedPrincipal);
  			IPrincipal p = Thread.CurrentPrincipal;
  			Assert.IsNotNull (p, "#1");
  			Assert.IsTrue ((p is GenericPrincipal), "#2");
  			Assert.AreEqual (String.Empty, p.Identity.Name, "#3");
  			Assert.AreEqual (String.Empty, p.Identity.AuthenticationType, "#4");
  			Assert.IsFalse (p.Identity.IsAuthenticated, "#5");
  
  			Thread.CurrentPrincipal = new GenericPrincipal (new GenericIdentity ("mono"), null);
  			Assert.IsNotNull (Thread.CurrentPrincipal, "#6");
  
  			Thread.CurrentPrincipal = null;
  			Assert.IsNotNull (Thread.CurrentPrincipal, "#7");
  			// in this case we can't return to null
  		}
  
  		public static void WindowsPrincipal () 
  		{
  			AppDomain.CurrentDomain.SetPrincipalPolicy (PrincipalPolicy.WindowsPrincipal);
  			IPrincipal p = Thread.CurrentPrincipal;
  			Assert.IsNotNull (p, "#1");
  			Assert.IsTrue ((p is WindowsPrincipal), "#2");
  			Assert.IsNotNull (p.Identity.Name, "#3");
  			Assert.IsNotNull (p.Identity.AuthenticationType, "#4");
  			Assert.IsTrue (p.Identity.IsAuthenticated, "#5");
  
  			// note: we can switch from a WindowsPrincipal to a GenericPrincipal
  			Thread.CurrentPrincipal = new GenericPrincipal (new GenericIdentity ("mono"), null);
  			Assert.IsNotNull (Thread.CurrentPrincipal, "#6");
  
  			Thread.CurrentPrincipal = null;
  			Assert.IsNotNull (Thread.CurrentPrincipal, "#7");
  			// in this case we can't return to null
  		}
  
  		public static void CopyOnNewThread ()
  		{
  			Assert.IsNotNull (Thread.CurrentPrincipal, "#1");
  			Assert.AreEqual ("good", Thread.CurrentPrincipal.Identity.Name, "#2");
  		}
  	}
  
  	[TestFixture]
  	[Category("MobileNotWorking")] // Abort #10240
  	public class ThreadTest
  	{
  		//TimeSpan Infinite = new TimeSpan (-10000);	// -10000 ticks == -1 ms
  		TimeSpan SmallNegative = new TimeSpan (-2);	// between 0 and -1.0 (infinite) ms
  		TimeSpan Negative = new TimeSpan (-20000);	// really negative
  		//TimeSpan MaxValue = TimeSpan.FromMilliseconds ((long) Int32.MaxValue);
  		TimeSpan TooLarge = TimeSpan.FromMilliseconds ((long) Int32.MaxValue + 1);
  
  		static bool is_win32;
  		static bool is_mono;
  
  		static ThreadTest ()
  		{
  			switch (Environment.OSVersion.Platform) {
  			case PlatformID.Win32NT:
  			case PlatformID.Win32S:
  			case PlatformID.Win32Windows:
  			case PlatformID.WinCE:
  				is_win32 = true;
  				break;
  			}
  
  			// check a class in mscorlib to determine if we're running on Mono
  			if (Type.GetType ("System.MonoType", false) != null)
  				is_mono = true;
  		}
  
  		//Some Classes to test as threads
  		private class C1Test
  		{
  			public int cnt;
  			public Thread thread1;
  			public bool endm1;
  			public bool endm2;
  
  			public C1Test()
  			{
  				thread1 = (Thread)null;
  				this.cnt = 0;
  				endm1 = endm2 = false;
  			}
  			
  			public void TestMethod()
  			{
  				while (cnt < 10)
  				{
  					cnt++;
  				}
  				endm1 = true;
  			}
  			public void TestMethod2()
  			{
  				if (!(thread1==(Thread)null) )
  				{
  					thread1.Join();
  				}
  				endm2 = true;
  			}
  		}
  
  		private class C2Test
  		{
  			public int cnt;
  			public bool run = false;
  			
  			public C2Test()
  			{
  				this.cnt = 0;
  			}
  
  			public void TestMethod()
  			{
  				run = true;
  				while (true)
  				{
  					if (cnt < 1000)
  						cnt++;
  					else
  						cnt = 0;
  				}
  			}
  		}
  		
  		private class C3Test
  		{
  			public C1Test sub_class;
  			public Thread sub_thread;
  
  			public C3Test()
  			{
  				sub_class = new C1Test();
  				sub_thread = new Thread(new ThreadStart(sub_class.TestMethod));
  			}
  
  			public void TestMethod1()
  			{
  				sub_thread.Start();
  				Thread.Sleep (100);
  				sub_thread.Abort();
  			}
  		}
  		
  		private class C4Test
  		{
  			public C1Test class1;
  			public C1Test class2;
  			public Thread thread1;
  			public Thread thread2;
  			public bool T1ON ;
  			public bool T2ON ;
  
  			public C4Test()
  			{
  				T1ON = false;
  				T2ON = false;
  				class1 = new C1Test();
  				class2 = new C1Test();
  				thread1 = new Thread(new ThreadStart(class1.TestMethod));
  				thread2 = new Thread(new ThreadStart(class2.TestMethod));
  			}
  
  			public void TestMethod1()
  			{
  				thread1.Start();
  				TestUtil.WaitForAlive (thread1, "wait1");
  				T1ON = true;
  				thread2.Start();
  				TestUtil.WaitForAlive (thread2, "wait2");
  				T2ON = true;
  				thread1.Abort();
  				TestUtil.WaitForNotAlive (thread1, "wait3");
  				T1ON = false;
  				thread2.Abort();
  				TestUtil.WaitForNotAlive (thread2, "wait4");
  				T2ON = false;
  			}
  			
  			public void TestMethod2()
  			{
  				thread1.Start();
  				thread1.Join();
  			}
  		}
  
  		[Test]
  		public void TestCtor1()
  		{
  			C1Test test1 = new C1Test();
  			Thread t = new Thread (new ThreadStart (test1.TestMethod));
  
  			Assert.IsTrue (t.CurrentCulture.IsReadOnly, "CurrentCulture.IsReadOnly");
  			Assert.IsFalse (t.IsAlive, "IsAlive");
  			Assert.IsFalse (t.IsBackground, "IsBackground");
  			Assert.IsNull (t.Name, "Name");
  			Assert.AreEqual (ThreadState.Unstarted, t.ThreadState, "ThreadState");
  		}
  
  		[Test]
  		[Category ("NotWorking")] // we're not sharing (read-only) CultureInfo
  		public void CultureInfo_Shared_Across_Threads ()
  		{
  			Thread t = new Thread (TestCtor1);
  			Assert.AreSame (t.CurrentCulture, t.CurrentUICulture, "Culture");
  
  			Assert.AreSame (t.CurrentCulture, CultureInfo.CurrentCulture, "CultureInfo.CurrentCulture");
  			Assert.AreSame (t.CurrentUICulture, CultureInfo.CurrentUICulture, "CultureInfo.CurrentUICulture");
  
  			Assert.AreSame (t.CurrentCulture, Thread.CurrentThread.CurrentCulture, "Thread.CurrentThread.CurrentCulture");
  			Assert.AreSame (t.CurrentUICulture, Thread.CurrentThread.CurrentUICulture, "Thread.CurrentThread.CurrentUICulture");
  		}
  
  		[Test] // bug #325566
  		public void GetHashCodeTest ()
  		{
  			C1Test test1 = new C1Test ();
  			Thread tA = new Thread (new ThreadStart (test1.TestMethod));
  			int hA1 = tA.GetHashCode ();
  			Assert.IsTrue (hA1 > 0, "#A1");
  			tA.Start ();
  			int hA2 = tA.GetHashCode ();
  			Assert.AreEqual (hA1, hA2, "#A2");
  			tA.Join ();
  			int hA3 = tA.GetHashCode ();
  			Assert.AreEqual (hA1, hA3, "#A3");
  			Assert.AreEqual (hA1, tA.ManagedThreadId, "#A4");
  
  			test1 = new C1Test ();
  			Thread tB = new Thread (new ThreadStart (test1.TestMethod));
  			int hB1 = tB.GetHashCode ();
  			Assert.IsTrue (hB1 > 0, "#B1");
  			tB.Start ();
  			int hB2 = tB.GetHashCode ();
  			Assert.AreEqual (hB1, hB2, "#B2");
  			tB.Join ();
  			int hB3 = tB.GetHashCode ();
  			Assert.AreEqual (hB1, hB3, "#B3");
  			Assert.AreEqual (hB1, tB.ManagedThreadId, "#B4");
  			Assert.IsFalse (hA2 == hB2, "#B5");
  		}
  
  		[Test] // bug #82700
  		public void ManagedThreadId ()
  		{
  			C1Test test1 = new C1Test ();
  			Thread t1 = new Thread (new ThreadStart (test1.TestMethod));
  			int mtA1 = t1.ManagedThreadId;
  			t1.Start ();
  			int mtA2 = t1.ManagedThreadId;
  			t1.Join ();
  			int mtA3 = t1.ManagedThreadId;
  			Assert.AreEqual (mtA1, mtA2, "#A1");
  			Assert.AreEqual (mtA2, mtA3, "#A2");
  
  			test1 = new C1Test ();
  			Thread t2 = new Thread (new ThreadStart (test1.TestMethod));
  			int mtB1 = t2.ManagedThreadId;
  			t2.Start ();
  			int mtB2 = t2.ManagedThreadId;
  			t2.Join ();
  			int mtB3 = t2.ManagedThreadId;
  			Assert.AreEqual (mtB1, mtB2, "#B1");
  			Assert.AreEqual (mtB2, mtB3, "#B2");
  			Assert.IsFalse (mtB1 == mtA1, "#B3");
  		}
  
  		[Test]
  		[Category ("NotDotNet")] // it hangs.
  		public void TestStart()
  		{
  			if (is_win32 && is_mono)
  				Assert.Fail ("This test fails on Win32. The test should be fixed.");
  		{
  			C1Test test1 = new C1Test();
  			Thread TestThread = new Thread(new ThreadStart(test1.TestMethod));
  			TestThread.Start();
  			TestThread.Join();
  			Assert.AreEqual (10, test1.cnt, "#1");
  		}
  		{
  			C2Test test1 = new C2Test();
  			Thread TestThread = new Thread(new ThreadStart(test1.TestMethod));
  			TestThread.Start();
  			TestThread.Abort();
  			try {
  				TestThread.Start();
  				Assert.Fail ("#2");
  			} catch (ThreadStateException) {
  			}
  		}
  		{
  			C2Test test1 = new C2Test();
  			Thread TestThread = new Thread(new ThreadStart(test1.TestMethod));
  			TestThread.Start();
  			while (!test1.run) {
  			}
  			bool started = (TestThread.ThreadState == ThreadState.Running);
  			Assert.AreEqual (started, test1.run, "#15 Thread Is not in the correct state: ");
  			TestThread.Abort();
  		}
  		}
  
  		[Test]
  		public void TestApartmentState ()
  		{
  			if (is_win32 && is_mono)
  				Assert.Fail ("This test fails on mono on win32. Our runtime should be fixed.");
  
  			C2Test test1 = new C2Test();
  			Thread TestThread = new Thread(new ThreadStart(test1.TestMethod));
  			Assert.AreEqual (ApartmentState.Unknown, TestThread.ApartmentState, "#1");
  			TestThread.Start();
  			TestUtil.WaitForAlive (TestThread, "wait5");
  			Assert.AreEqual (ApartmentState.MTA, TestThread.ApartmentState, "#2");
  			TestThread.Abort();
  		}
  
  		[Test]
  		public void TestPriority1()
  		{
  			if (is_win32 && is_mono)
  				Assert.Fail ("This test fails on mono on Win32. Our runtime should be fixed.");
  
  			C2Test test1 = new C2Test();
  			Thread TestThread = new Thread(new ThreadStart(test1.TestMethod));
  			try {
  				TestThread.Priority=ThreadPriority.BelowNormal;
  				ThreadPriority after = TestThread.Priority;
  				TestThread.Start();
  				TestUtil.WaitForAlive (TestThread, "wait7");
  				ThreadPriority before = TestThread.Priority;
  				Assert.AreEqual (before, after, "#41 Unexpected Priority Change: ");
  			} finally {
  				TestThread.Abort();
  			}
  		}
  
  		[Test]
  		[Category ("NotDotNet")] // on MS, Thread is still in AbortRequested state when Start is invoked
  		public void AbortUnstarted ()
  		{
  			C2Test test1 = new C2Test();
  			Thread th = new Thread (new ThreadStart (test1.TestMethod));
  			th.Abort ();
  			th.Start ();
  		}
  
  		[Test]
  		[Category ("NotDotNet")] // on MS, ThreadState is immediately Stopped after Abort
  		[Category ("NotWorking")] // this is a MonoTODO -> no support for Priority
  		public void TestPriority2()
  		{
  			C2Test test1 = new C2Test();
  			Thread TestThread = new Thread(new ThreadStart(test1.TestMethod));
  			try {
  				Assert.AreEqual (ThreadPriority.Normal, TestThread.Priority, "#42 Incorrect Priority in New thread: ");
  				TestThread.Start();
  				TestUtil.WaitForAliveOrStop (TestThread, "wait8");
  				Assert.AreEqual (ThreadPriority.Normal, TestThread.Priority, "#43 Incorrect Priority in Started thread: ");
  			} finally {
  				TestThread.Abort();
  			}
  			Assert.AreEqual (ThreadPriority.Normal, TestThread.Priority, "#44 Incorrect Priority in Aborted thread: ");
  		}
  
  		[Test]
  		[Category ("NotWorking")] // this is a MonoTODO -> no support for Priority
  		public void TestPriority3()
  		{
  			C2Test test1 = new C2Test();
  			Thread TestThread = new Thread(new ThreadStart(test1.TestMethod));
  			try {
  				TestThread.Start();
  				TestThread.Priority = ThreadPriority.Lowest;
  				Assert.AreEqual (ThreadPriority.Lowest, TestThread.Priority, "#45A Incorrect Priority:");
  				TestThread.Priority = ThreadPriority.BelowNormal;
  				Assert.AreEqual (ThreadPriority.BelowNormal, TestThread.Priority, "#45B Incorrect Priority:");
  				TestThread.Priority = ThreadPriority.Normal;
  				Assert.AreEqual (ThreadPriority.Normal, TestThread.Priority, "#45C Incorrect Priority:");
  				TestThread.Priority = ThreadPriority.AboveNormal;
  				Assert.AreEqual (ThreadPriority.AboveNormal, TestThread.Priority, "#45D Incorrect Priority:");
  				TestThread.Priority = ThreadPriority.Highest;
  				Assert.AreEqual (ThreadPriority.Highest, TestThread.Priority, "#45E Incorrect Priority:");
  			}
  			finally {
  				TestThread.Abort();
  			}
  		}
  
  		[Test]
  		public void TestUndivisibleByPageSizeMaxStackSize ()
  		{
  			const int undivisible_stacksize = 1048573;
  
  			var thread = new Thread (new ThreadStart (delegate {}), undivisible_stacksize);
  			thread.Start ();
  			thread.Join ();
  		}
  
  		[Test]
  		public void TestIsBackground1 ()
  		{
  			if (is_win32 && is_mono)
  				Assert.Fail ("This test fails on mono on Win32. Our runtime should be fixed.");
  
  			C2Test test1 = new C2Test();
  			Thread TestThread = new Thread(new ThreadStart(test1.TestMethod));
  			try {
  				TestThread.Start();
  				TestUtil.WaitForAlive (TestThread, "wait9");
  				bool state = TestThread.IsBackground;
  				Assert.IsFalse (state, "#51 IsBackground not set at the default state: ");
  			} finally {
  				TestThread.Abort();
  			}
  		}
  
  		[Test]
  		public void TestIsBackground2 ()
  		{
  			C2Test test1 = new C2Test();
  			Thread TestThread = new Thread(new ThreadStart(test1.TestMethod));
  			TestThread.IsBackground = true;
  			try {
  				TestThread.Start();
  			} finally {
  				TestThread.Abort();
  			}
  			
  			if (TestThread.IsAlive) {
  				try {
  					Assert.IsTrue (TestThread.IsBackground, "#52 Is Background Changed to Start ");
  				} catch (ThreadStateException) {
  					// Ignore if thread died meantime
  				}
  			}
  		}
  
  		[Test]
  		public void TestName()
  		{
  			if (is_win32 && is_mono)
  				Assert.Fail ("This test fails on mono on Win32. Our runtime should be fixed.");
  
  			C2Test test1 = new C2Test();
  			Thread TestThread = new Thread(new ThreadStart(test1.TestMethod));
  			try {
  				TestThread.Start();
  				TestUtil.WaitForAlive (TestThread, "wait10");
  				string name = TestThread.Name;
  				Assert.IsNull (name, "#61 Name set when mustn't be set: ");
  				string newname = "Testing....";
  				TestThread.Name = newname;
  				Assert.AreEqual (newname, TestThread.Name, "#62 Name not set when must be set: ");
  			} finally {
  				TestThread.Abort();
  			}
  		}
  
  		[Test]
  		public void Name ()
  		{
  			Thread t = new Thread (new ThreadStart (Name));
  			Assert.IsNull (t.Name, "Name-1");
  			t.Name = null;
  			Assert.IsNull (t.Name, "Name-2");
  		}
  
  		[Test]
  		[ExpectedException (typeof (InvalidOperationException))]
  		public void Rename ()
  		{
  			Thread t = new Thread (new ThreadStart (Rename));
  			t.Name = "a";
  			t.Name = "b";
  		}
  
  		bool rename_finished;
  		bool rename_failed;
  
  		[Test]
  		public void RenameTpThread ()
  		{
  			object monitor = new object ();
  			ThreadPool.QueueUserWorkItem (new WaitCallback (Rename_callback), monitor);
  			lock (monitor) {
  				if (!rename_finished)
  					Monitor.Wait (monitor);
  			}
  			Assert.IsTrue (!rename_failed);
  		}
  
  		void Rename_callback (object o) {
  			Thread.CurrentThread.Name = "a";
  			try {
  				Thread.CurrentThread.Name = "b";
  				//Console.WriteLine ("Thread name is: {0}", Thread.CurrentThread.Name);
  			} catch (Exception e) {
  				//Console.Error.WriteLine (e);
  				rename_failed = true;
  			}
  			object monitor = o;
  			lock (monitor) {
  				rename_finished = true;
  				Monitor.Pulse (monitor);
  			}
  		}
  
  		[Test]
  		public void TestNestedThreads1()
  		{
  			C3Test test1 = new C3Test();
  			Thread TestThread = new Thread(new ThreadStart(test1.TestMethod1));
  			try {
  				TestThread.Start();
  				TestUtil.WaitForAlive (TestThread, "wait11");
  			} finally {
  				TestThread.Abort();
  			}
  		}
  
  		[Test]
  		public void TestNestedThreads2()
  		{
  			C4Test test1 = new C4Test();
  			Thread TestThread = new Thread(new ThreadStart(test1.TestMethod1));
  			try {
  				TestThread.Start();
  			} finally {
  				TestThread.Abort();
  			}
  		}
  
  		[Test]
  		public void TestJoin1()
  		{
  			C1Test test1 = new C1Test();
  			C1Test test2 = new C1Test();
  			Thread thread1 = new Thread(new ThreadStart(test1.TestMethod));
  			Thread thread2 = new Thread(new ThreadStart(test1.TestMethod2));
  			try {
  				thread1.Start();
  				thread2.Start();
  				thread2.Join();
  			} finally {
  				thread1.Abort();
  				thread2.Abort();
  			}
  		}
  
  		[Test]
  		[ExpectedException (typeof (ArgumentOutOfRangeException))]
  		public void Join_Int32_Negative ()
  		{
  			// -1 is Timeout.Infinite
  			Thread.CurrentThread.Join (-2);
  		}
  
  		[Test]
  		[ExpectedException (typeof (ArgumentOutOfRangeException))]
  		public void Join_TimeSpan_Negative ()
  		{
  			Thread.CurrentThread.Join (Negative);
  		}
  
  		[Test]
  		[ExpectedException (typeof (ArgumentOutOfRangeException))]
  		public void Join_TimeSpan_TooLarge ()
  		{
  			Thread.CurrentThread.Join (TooLarge);
  		}
  
  		[Test]
  		public void Join_TimeSpan_SmallNegative ()
  		{
  			Thread.CurrentThread.Join (SmallNegative);
  		}
  
  		[Test]
  		[ExpectedException (typeof (ArgumentOutOfRangeException))]
  		public void Sleep_Int32_Negative ()
  		{
  			// -1 is Timeout.Infinite
  			Thread.Sleep (-2);
  		}
  
  		[Test]
  		public void Sleep_TimeSpan_SmallNegative ()
  		{
  			Thread.Sleep (SmallNegative);
  		}
  
  		[Test]
  		[ExpectedException (typeof (ArgumentOutOfRangeException))]
  		public void Sleep_TimeSpan_Negative ()
  		{
  			Thread.Sleep (Negative);
  		}
  
  		[Test]
  		[ExpectedException (typeof (ArgumentOutOfRangeException))]
  		public void Sleep_TimeSpan_TooLarge ()
  		{
  			Thread.Sleep (TooLarge);
  		}
  
  		[Test]
  		public void SpinWait ()
  		{
  			// no exception for negative numbers
  			Thread.SpinWait (Int32.MinValue);
  			Thread.SpinWait (0);
  		}
  
  		[Test]
  		public void TestThreadState ()
  		{
  			if (is_win32 && is_mono)
  				Assert.Fail ("This test fails on mono on Win32. Our runtime should be fixed.");
  
  			//TODO: Test The rest of the possible transitions
  
gt: 			C2Test test1 = new C2Test();