System.Threading.Tests.ExecutionContextTests.FlowTest C# (CSharp) Method

FlowTest() private method

private FlowTest ( ) : void
return void
        public static void FlowTest()
        {
            ThreadTestHelpers.RunTestInBackgroundThread(() =>
            {
                var asyncLocal = new AsyncLocal<int>();
                asyncLocal.Value = 1;

                var asyncFlowControl = default(AsyncFlowControl);
                Action<Action, Action> verifySuppressRestore =
                    (suppressFlow, restoreFlow) =>
                    {
                        VerifyExecutionContextFlow(asyncLocal, 1);
                        ExecutionContext executionContext2 = ExecutionContext.Capture();

                        suppressFlow();
                        VerifyExecutionContextFlow(asyncLocal, 0);
                        VerifyExecutionContext(executionContext2, asyncLocal, 1);
                        executionContext2 = ExecutionContext.Capture();

                        restoreFlow();
                        VerifyExecutionContextFlow(asyncLocal, 1);
                        VerifyExecutionContext(executionContext2, asyncLocal, 0);
                    };

                verifySuppressRestore(
                    () => asyncFlowControl = ExecutionContext.SuppressFlow(),
                    () => asyncFlowControl.Undo());
                verifySuppressRestore(
                    () => asyncFlowControl = ExecutionContext.SuppressFlow(),
                    () => asyncFlowControl.Dispose());
                verifySuppressRestore(
                    () => ExecutionContext.SuppressFlow(),
                    () => ExecutionContext.RestoreFlow());

                Assert.Throws<InvalidOperationException>(() => ExecutionContext.RestoreFlow());
                asyncFlowControl = ExecutionContext.SuppressFlow();
                Assert.Throws<InvalidOperationException>(() => ExecutionContext.SuppressFlow());

                ThreadTestHelpers.RunTestInBackgroundThread(() =>
                {
                    ExecutionContext.SuppressFlow();
                    Assert.Throws<InvalidOperationException>(() => asyncFlowControl.Undo());
                    Assert.Throws<InvalidOperationException>(() => asyncFlowControl.Dispose());
                    ExecutionContext.RestoreFlow();
                });

                asyncFlowControl.Undo();
                Assert.Throws<InvalidOperationException>(() => asyncFlowControl.Undo());
                Assert.Throws<InvalidOperationException>(() => asyncFlowControl.Dispose());

                // Changing an async local value does not prevent undoing a flow-suppressed execution context. In .NET Core, the
                // execution context is immutable, so changing an async local value changes the execution context instance,
                // contrary to the desktop framework.
                asyncFlowControl = ExecutionContext.SuppressFlow();
                asyncLocal.Value = 2;
                asyncFlowControl.Undo();
                VerifyExecutionContextFlow(asyncLocal, 2);
                asyncFlowControl = ExecutionContext.SuppressFlow();
                asyncLocal.Value = 3;
                asyncFlowControl.Dispose();
                VerifyExecutionContextFlow(asyncLocal, 3);
                ExecutionContext.SuppressFlow();
                asyncLocal.Value = 4;
                ExecutionContext.RestoreFlow();
                VerifyExecutionContextFlow(asyncLocal, 4);

                // An async flow control cannot be undone when a different execution context is applied. The desktop framework
                // mutates the execution context when its state changes, and only changes the instance when an execution context
                // is applied (for instance, through ExecutionContext.Run). The framework prevents a suppressed-flow execution
                // context from being applied by returning null from ExecutionContext.Capture, so the only type of execution
                // context that can be applied is one whose flow is not suppressed. After suppressing flow and changing an async
                // local's value, the desktop framework verifies that a different execution context has not been applied by
                // checking the execution context instance against the one saved from when flow was suppressed. In .NET Core,
                // since the execution context instance will change after changing the async local's value, it verifies that a
                // different execution context has not been applied, by instead ensuring that the current execution context's
                // flow is suppressed.
                {
                    ExecutionContext executionContext = null;
                    Action verifyCannotUndoAsyncFlowControlAfterChangingExecutionContext =
                        () =>
                        {
                            ExecutionContext.Run(
                                executionContext,
                                state =>
                                {
                                    Assert.Throws<InvalidOperationException>(() => asyncFlowControl.Undo());
                                    Assert.Throws<InvalidOperationException>(() => asyncFlowControl.Dispose());
                                },
                                null);
                        };

                    executionContext = ExecutionContext.Capture();
                    asyncFlowControl = ExecutionContext.SuppressFlow();
                    verifyCannotUndoAsyncFlowControlAfterChangingExecutionContext();
                    asyncFlowControl.Undo();

                    executionContext = ExecutionContext.Capture();
                    asyncFlowControl = ExecutionContext.SuppressFlow();
                    asyncLocal.Value = 5;
                    verifyCannotUndoAsyncFlowControlAfterChangingExecutionContext();
                    asyncFlowControl.Undo();
                    VerifyExecutionContextFlow(asyncLocal, 5);
                }
            });
        }