Example #1
0
        async Task <StepExecutionState> IProgressStepOperation.RunAsync(CancellationToken cancellationToken, IProgressStepExecutionEvents progressCallback)
        {
            if (this.ExecutionState != StepExecutionState.NotStarted)
            {
                throw new InvalidOperationException(ProgressResources.StepOperationWasAlreadyExecuted);
            }

            if (this.Cancellable && cancellationToken.IsCancellationRequested)
            {
                return(this.ExecutionState = StepExecutionState.Cancelled);
            }

            VsTaskRunContext context = GetContext(this.Execution);

            StepExecutionState stepState = await VsThreadingHelper.RunTaskAsync <StepExecutionState>(this.controller, context,
                                                                                                     () =>
            {
                DoStatefulExecution(progressCallback, cancellationToken);
                return(this.ExecutionState);
            },

                                                                                                     cancellationToken);

            return(stepState);
        }
Example #2
0
        /// <summary>
        /// The <see cref="ProgressControllerStep"/> which are used by default will swallow the assert exceptions
        /// which means that investigating why something is failing requires more time and effort.
        /// This extension method will record the first <see cref="UnitTestAssertException"/> which was thrown during
        /// execution and will rethrow it on a way that will allow the test to fail and see the original stack
        /// that caused the test failure (on Finished event)
        /// </summary>
        /// <param name="controller">The controller to configure</param>
        /// <returns>The notifier that was used for configuration of the assert exception</returns>
        public static ConfigurableErrorNotifier ConfigureToThrowAssertExceptions(SequentialProgressController controller)
        {
            controller.Should().NotBeNull("Controller argument is required");
            controller.Steps.Should().NotBeNull("Controller needs to be initialized");

            ConfigurableErrorNotifier errorHandler = new ConfigurableErrorNotifier();

            controller.ErrorNotificationManager.AddNotifier(errorHandler);

            UnitTestAssertException originalException = null;

            // Controller.Finished is executed out of the awaitable state machine and on the calling (UI) thread
            // which means that at this point the test runtime engine will be able to catch it and fail the test
            EventHandler <ProgressControllerFinishedEventArgs> onFinished = null;

            onFinished = (s, e) =>
            {
                // Need to register on the UI thread
                VsThreadingHelper.RunTaskAsync(controller, Microsoft.VisualStudio.Shell.VsTaskRunContext.UIThreadNormalPriority, () =>
                {
                    controller.Finished -= onFinished;
                }).Wait();

                // Satisfy the sequential controller verification code
                e.Handled();

                if (originalException != null)
                {
                    e.Result.Should().Be(ProgressControllerResult.Failed, "Expected to be failed since the assert failed which causes an exception");
                    throw new RestoredUnitTestAssertException(originalException.Message, originalException);
                }
            };

            // Need to register on the UI thread
            VsThreadingHelper.RunTaskAsync(controller, Microsoft.VisualStudio.Shell.VsTaskRunContext.UIThreadNormalPriority, () =>
            {
                controller.Finished += onFinished;
            }).Wait();

            errorHandler.NotifyAction = (e) =>
            {
                // Only the first one
                if (originalException == null)
                {
                    originalException = e as UnitTestAssertException;
                }
            };
            return(errorHandler);
        }
Example #3
0
        /// <summary>
        /// Starts executing the initialized steps.
        /// The method is not thread safe but can be called from any thread.
        /// </summary>
        /// <returns>An await-able</returns>
        public async TPL.Task <ProgressControllerResult> StartAsync()
        {
            if (this.IsStarted)
            {
                throw new InvalidOperationException(ProgressResources.AlreadyStartedException);
            }

            this.OnStarted();

            ProgressControllerResult controllerResult = await VsThreadingHelper.RunTaskAsync <ProgressControllerResult>(this, VsTaskRunContext.BackgroundThread,
                                                                                                                        () =>
            {
                ThreadHelper.ThrowIfOnUIThread();

                // By default can abort, the individual step may changed that
                this.CanAbort = true;

                ProgressControllerResult result = ProgressControllerResult.Cancelled;
                foreach (IProgressStepOperation operation in this.progressStepOperations)
                {
                    // Try to cancel (in case the step itself will not cancel itself)
                    if (this.cancellationTokenSource.IsCancellationRequested)
                    {
                        result = ProgressControllerResult.Cancelled;
                        break;
                    }

                    this.CanAbort = operation.Step.Cancellable;

                    IProgressStepExecutionEvents notifier = this.stepFactory.GetExecutionCallback(operation);

                    // Give another try before running the operation (there's a test that covers cancellation
                    // before running the operation which requires this check after CanAbort is set)
                    if (this.cancellationTokenSource.IsCancellationRequested)
                    {
                        result = ProgressControllerResult.Cancelled;
                        break;
                    }

                    StepExecutionState stepResult = operation.RunAsync(this.cancellationTokenSource.Token, notifier).Result;

                    /* Not trying to cancel here intentionally. The reason being
                     * in case the step was the last one, there's nothing to cancel really,
                     * otherwise there will be an attempt to cancel just before the next
                     * step execution*/

                    if (stepResult == StepExecutionState.Succeeded)
                    {
                        result = ProgressControllerResult.Succeeded;
                    }
                    else if (stepResult == StepExecutionState.Failed)
                    {
                        result = ProgressControllerResult.Failed;
                        break;
                    }
                    else if (stepResult == StepExecutionState.Cancelled)
                    {
                        result = ProgressControllerResult.Cancelled;
                        break;
                    }
                    else
                    {
                        Debug.Fail("Unexpected step execution result:" + stepResult);
                    }
                }

                return(result);
            },

                                                                                                                        this.cancellationTokenSource.Token);

            this.OnFinished(controllerResult);

            return(controllerResult);
        }