[System] Fix SmtpClientTest.Deliver_Async to not require main thread processing.
authorRolf Bjarne Kvinge <rolf@xamarin.com>
Wed, 25 Nov 2015 13:03:56 +0000 (14:03 +0100)
committerRolf Bjarne Kvinge <rolf@xamarin.com>
Wed, 25 Nov 2015 13:04:34 +0000 (14:04 +0100)
mcs/class/System/Test/System.Net.Mail/SmtpClientTest.cs

index 8bba21c0bdec4383fceb4d7c5cc11d9fbed2d611..8fa752a4476c8981af260df18a15091fbe5bef54 100644 (file)
@@ -392,21 +392,48 @@ namespace MonoTests.System.Net.Mail
                [Test]
                public void Deliver_Async ()
                {
-                       var server = new SmtpServer ();
-                       var client = new SmtpClient ("localhost", server.EndPoint.Port);
-                       var msg = new MailMessage ("foo@example.com", "bar@example.com", "hello", "howdydoo\r\n");
-
-                       Thread t = new Thread (server.Run);
-                       t.Start ();
-                       var task = client.SendMailAsync (msg);
-                       t.Join ();
+                       // SmtpClient uses BackgroundWorker and listens for the RunWorkerCompleted
+                       // to mark an async task as completed. The problem is that BackgroundWorker uses
+                       // System.ComponentModel.AsyncOperationManager to get the synchronization
+                       // context, and in monotouch that returns by default a synchronization
+                       // context for the main thread. Since tests are also run on the main thread,
+                       // we'll block the main thread while waiting for the async send to complete,
+                       // while the async completion is waiting for the main thread to process it.
+                       // So instead use a SynchronizationContext that uses the threadpool instead
+                       // of the main thread.
+                       var existing_context = global::System.ComponentModel.AsyncOperationManager.SynchronizationContext;
+                       global::System.ComponentModel.AsyncOperationManager.SynchronizationContext = new ThreadPoolSynchronizationContext ();
+                       try {
+                               var server = new SmtpServer ();
+                               var client = new SmtpClient ("localhost", server.EndPoint.Port);
+                               var msg = new MailMessage ("foo@example.com", "bar@example.com", "hello", "howdydoo\r\n");
+
+                               Thread t = new Thread (server.Run);
+                               t.Start ();
+                               var task = client.SendMailAsync (msg);
+                               t.Join ();
+
+                               Assert.AreEqual ("<foo@example.com>", server.mail_from);
+                               Assert.AreEqual ("<bar@example.com>", server.rcpt_to);
+
+                               Assert.IsTrue (task.Wait (1000));
+                               Assert.IsTrue (task.IsCompleted, "task");
+                       } finally {
+                               global::System.ComponentModel.AsyncOperationManager.SynchronizationContext = existing_context;
+                       }
+               }
 
-                       Assert.AreEqual ("<foo@example.com>", server.mail_from);
-                       Assert.AreEqual ("<bar@example.com>", server.rcpt_to);
+               internal class ThreadPoolSynchronizationContext : SynchronizationContext
+               {
+                       public override void Post (SendOrPostCallback d, object state)
+                       {
+                               ThreadPool.QueueUserWorkItem ((v) => d (state));
+                       }
 
-                       Assert.IsTrue (task.Wait (1000));
-                       Assert.IsTrue (task.IsCompleted, "task");
+                       public override void Send (SendOrPostCallback d, object state)
+                       {
+                               d (state);
+                       }
                }
-
        }
 }