public async Task InvokeAsync_SyncWorkInAsyncTaskIsCompletedFirst()
        {
            // Simplified version of ServerComponentRenderingTest.CanDispatchAsyncWorkToSyncContext
            var    expected = "First Second Third Fourth Fifth";
            var    context  = new RendererSynchronizationContext();
            string actual;

            // Act
            await Task.Yield();

            actual = "First";

            var invokeTask = context.InvokeAsync(async() =>
            {
                // When the sync context is idle, queued work items start synchronously
                actual += " Second";
                await Task.Delay(250);
                actual += " Fourth";
            });

            actual += " Third";
            await invokeTask;

            actual += " Fifth";

            // Assert
            Assert.Equal(expected, actual);
        }
        public async Task Post_BackgroundWorkItem_CanProcessMoreItemsInline()
        {
            // Arrange
            var    context        = new RendererSynchronizationContext();
            Thread capturedThread = null;

            var e1 = new ManualResetEventSlim();
            var e2 = new ManualResetEventSlim();
            var e3 = new ManualResetEventSlim();
            var e4 = new ManualResetEventSlim();
            var e5 = new ManualResetEventSlim();
            var e6 = new ManualResetEventSlim();

            // Force task2 to execute in the background
            var task1 = Task.Run(() => context.Send((_) =>
            {
                e1.Set();
                Assert.True(e2.Wait(Timeout), "timeout");
            }, null));

            Assert.True(e1.Wait(Timeout), "timeout");

            var task2 = Task.Run(() =>
            {
                context.Send((_) =>
                {
                    e3.Set();
                    Assert.True(e4.Wait(Timeout), "timeout");
                    capturedThread = Thread.CurrentThread;
                }, null);
            });

            e2.Set();
            await task1;

            Assert.True(e3.Wait(Timeout), "timeout");

            // Act
            //
            // Now task2 is 'running' in the sync context. Schedule more work items - they will be
            // run immediately after the second item
            context.Post((_) =>
            {
                e5.Set();
                Assert.Same(Thread.CurrentThread, capturedThread);
            }, null);
            context.Post((_) =>
            {
                e6.Set();
                Assert.Same(Thread.CurrentThread, capturedThread);
            }, null);


            // Assert
            e4.Set();
            await task2;

            Assert.True(e5.Wait(Timeout), "timeout");
            Assert.True(e6.Wait(Timeout), "timeout");
        }
        public void Send_CapturesContext()
        {
            // Arrange
            var context = new RendererSynchronizationContext();

            var e1 = new ManualResetEventSlim();

            // CultureInfo uses the execution context.
            CultureInfo.CurrentCulture = new CultureInfo("en-GB");
            CultureInfo capturedCulture = null;

            SynchronizationContext capturedContext = null;

            // Act
            context.Send(async(_) =>
            {
                await Task.Yield();

                capturedCulture = CultureInfo.CurrentCulture;
                capturedContext = SynchronizationContext.Current;

                e1.Set();
            }, null);

            // Assert
            Assert.True(e1.Wait(Timeout), "timeout");
            Assert.Same(CultureInfo.CurrentCulture, capturedCulture);
            Assert.Same(context, capturedContext);
        }
 public RendererSynchronizationContextDispatcher()
 {
     _context = new RendererSynchronizationContext();
     _context.UnhandledException += (sender, e) =>
     {
         OnUnhandledException(e);
     };
 }
        public void Send_CanRunSynchronously_Exception()
        {
            // Arrange
            var context = new RendererSynchronizationContext();

            // Act & Assert
            Assert.Throws <InvalidTimeZoneException>(() => context.Send((_) =>
            {
                throw new InvalidTimeZoneException();
            }, null));
        }
        public async Task Post_CanRunAsynchronously_CaptureExecutionContext()
        {
            // Arrange
            var context = new RendererSynchronizationContext();

            // CultureInfo uses the execution context.
            CultureInfo.CurrentCulture = new CultureInfo("en-GB");
            CultureInfo capturedCulture = null;

            SynchronizationContext capturedContext = null;

            var e1 = new ManualResetEventSlim();
            var e2 = new ManualResetEventSlim();
            var e3 = new ManualResetEventSlim();

            var task = Task.Run(() =>
            {
                context.Send((_) =>
                {
                    e1.Set();
                    Assert.True(e2.Wait(Timeout), "timeout");
                }, null);
            });

            Assert.True(e1.Wait(Timeout), "timeout");

            // Act
            SynchronizationContext original = SynchronizationContext.Current;

            try
            {
                SynchronizationContext.SetSynchronizationContext(context);
                context.Post((_) =>
                {
                    capturedCulture = CultureInfo.CurrentCulture;
                    capturedContext = SynchronizationContext.Current;
                    e3.Set();
                }, null);
            }
            finally
            {
                SynchronizationContext.SetSynchronizationContext(original);
            }

            // Assert
            Assert.False(e2.IsSet);
            e2.Set(); // Unblock the first item
            await task;

            Assert.True(e3.Wait(Timeout), "timeout");
            Assert.Same(CultureInfo.CurrentCulture, capturedCulture);
            Assert.Same(context, capturedContext);
        }
        public async Task InvokeAsync_FuncTaskT_CanRethrowExceptions()
        {
            // Arrange
            var context = new RendererSynchronizationContext();

            // Act
            var task = context.InvokeAsync <string>((Func <Task <string> >)(() =>
            {
                throw new InvalidTimeZoneException();
            }));

            // Assert
            await Assert.ThrowsAsync <InvalidTimeZoneException>(async() => await task);
        }
        public async Task InvokeAsync_FuncTaskT_CanReportCancellation()
        {
            // Arrange
            var context = new RendererSynchronizationContext();

            // Act
            var task = context.InvokeAsync <string>((Func <Task <string> >)(() =>
            {
                throw new OperationCanceledException();
            }));

            // Assert
            Assert.Equal(TaskStatus.Canceled, task.Status);
            await Assert.ThrowsAsync <TaskCanceledException>(async() => await task);
        }
        public async Task InvokeAsync_FuncTaskT_CanRunSynchronously_WhenNotBusy()
        {
            // Arrange
            var context = new RendererSynchronizationContext();
            var thread  = Thread.CurrentThread;

            // Act
            var task = context.InvokeAsync(() =>
            {
                return(Task.FromResult(Thread.CurrentThread));
            });

            // Assert
            Assert.Same(thread, await task);
        }
        public void Send_CanRunSynchronously()
        {
            // Arrange
            var    context        = new RendererSynchronizationContext();
            var    thread         = Thread.CurrentThread;
            Thread capturedThread = null;

            // Act
            context.Send((_) =>
            {
                capturedThread = Thread.CurrentThread;
            }, null);

            // Assert
            Assert.Same(thread, capturedThread);
        }
        public async Task Send_BlocksWhenOtherWorkRunning()
        {
            // Arrange
            var context = new RendererSynchronizationContext();

            var e1 = new ManualResetEventSlim();
            var e2 = new ManualResetEventSlim();
            var e3 = new ManualResetEventSlim();
            var e4 = new ManualResetEventSlim();

            // Force task2 to execute in the background
            var task1 = Task.Run(() =>
            {
                context.Send((_) =>
                {
                    e1.Set();
                    Assert.True(e2.Wait(Timeout), "timeout");
                }, null);
            });

            Assert.True(e1.Wait(Timeout), "timeout");

            // Act
            //
            // Dispatch this on the background thread because otherwise it would block.
            var task2 = Task.Run(() =>
            {
                e3.Set();
                context.Send((_) =>
                {
                    e4.Set();
                }, null);
            });

            // Assert
            Assert.True(e3.Wait(Timeout), "timeout");
            Assert.True(e3.IsSet);

            // Unblock the first item
            e2.Set();
            await task1;

            await task2;

            Assert.True(e4.IsSet);
        }
        public async Task InvokeAsync_Action_CanRunSynchronously_WhenNotBusy()
        {
            // Arrange
            var    context        = new RendererSynchronizationContext();
            var    thread         = Thread.CurrentThread;
            Thread capturedThread = null;

            // Act
            var task = context.InvokeAsync(() =>
            {
                capturedThread = Thread.CurrentThread;
            });

            // Assert
            await task;

            Assert.Same(thread, capturedThread);
        }
        public async Task Post_CanRunAsynchronously_WhenBusy_Exception()
        {
            // Arrange
            var context = new RendererSynchronizationContext();

            Exception exception = null;

            context.UnhandledException += (sender, e) =>
            {
                exception = (InvalidTimeZoneException)e.ExceptionObject;
            };

            var e1 = new ManualResetEventSlim();
            var e2 = new ManualResetEventSlim();

            var task = Task.Run(() =>
            {
                context.Send((_) =>
                {
                    e1.Set();
                    Assert.True(e2.Wait(Timeout), "timeout");
                }, null);
            });

            Assert.True(e1.Wait(Timeout), "timeout");

            // Act
            context.Post((_) =>
            {
                throw new InvalidTimeZoneException();
            }, null);

            // Assert
            Assert.False(e2.IsSet);
            e2.Set(); // Unblock the first item
            await task;

            // Use another item to 'push through' the throwing one
            context.Send((_) => { }, null);
            Assert.NotNull(exception);
        }
        public void Post_RunsAsynchronously_WhenNotBusy()
        {
            // Arrange
            var    context        = new RendererSynchronizationContext();
            var    thread         = Thread.CurrentThread;
            Thread capturedThread = null;

            var e = new ManualResetEventSlim();

            // Act
            context.Post((_) =>
            {
                capturedThread = Thread.CurrentThread;

                e.Set();
            }, null);

            // Assert
            Assert.True(e.Wait(Timeout), "timeout");
            Assert.NotSame(thread, capturedThread);
        }
        public async Task InvokeAsync_FuncTask_CanRunAsynchronously_WhenBusy()
        {
            // Arrange
            var    context        = new RendererSynchronizationContext();
            var    thread         = Thread.CurrentThread;
            Thread capturedThread = null;

            var e1 = new ManualResetEventSlim();
            var e2 = new ManualResetEventSlim();
            var e3 = new ManualResetEventSlim();

            var task1 = Task.Run(() =>
            {
                context.Send((_) =>
                {
                    e1.Set();
                    Assert.True(e2.Wait(Timeout), "timeout");
                }, null);
            });

            Assert.True(e1.Wait(Timeout), "timeout");

            var task2 = context.InvokeAsync(() =>
            {
                capturedThread = Thread.CurrentThread;

                e3.Set();
                return(Task.CompletedTask);
            });

            // Assert
            Assert.False(e2.IsSet);
            e2.Set(); // Unblock the first item
            await task1;

            Assert.True(e3.Wait(Timeout), "timeout");
            await task2;

            Assert.NotSame(thread, capturedThread);
        }
        public void Post_RunsAsynchronously_WhenNotBusy_Exception()
        {
            // Arrange
            var context = new RendererSynchronizationContext();

            Exception exception = null;

            context.UnhandledException += (sender, e) =>
            {
                exception = (InvalidTimeZoneException)e.ExceptionObject;
            };

            // Act
            context.Post((_) =>
            {
                throw new InvalidTimeZoneException();
            }, null);

            // Assert
            //
            // Use another item to 'push through' the throwing one
            context.Send((_) => { }, null);
            Assert.NotNull(exception);
        }