async Task <StepExecutionState> IProgressStepOperation.Run(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.RunTask <StepExecutionState>(this.controller, context, () => { DoStatefulExecution(progressCallback, cancellationToken); return(this.ExecutionState); }, cancellationToken); return(stepState); }
private void ControllerStarted(object sender, ProgressEventArgs e) { // Show can be modal and block, so begin invoke it VsThreadingHelper.BeginTask(this.serviceProvider, VsTaskRunContext.UIThreadNormalPriority, this.host.Show); // Flag that handled to assist with the verification, otherwise the controller will assert e.Handled(); }
private void OnCancellationSupportChanged(object sender, CancellationSupportChangedEventArgs e) { VsThreadingHelper.RunInline(this.serviceProvider, VsTaskRunContext.UIThreadNormalPriority, () => { ChangeCancellability(e.Cancellable); }); // Flag that handled to assist with the verification, otherwise the controller will assert e.Handled(); }
private static ProgressObserver CreateAndConfigureInstance(IServiceProvider serviceProvider, IProgressVisualizer visualizer, IProgressEvents progressEvents, ICommand cancelCommand, ProgressControllerViewModel state) { return(VsThreadingHelper.RunInline(serviceProvider, VsTaskRunContext.UIThreadNormalPriority, () => { ProgressObserver returnValue = new ProgressObserver(serviceProvider, visualizer, progressEvents, state); returnValue.CancelCommand = cancelCommand; return returnValue; }, null)); }
/// <summary> /// Stops the specified <see cref="ProgressObserver"/> from observing. /// The method is thread safe. /// </summary> /// <param name="observer">An existing <see cref="ProgressObserver"/> to stop observing with</param> public static void StopObserving(ProgressObserver observer) { if (observer == null) { throw new ArgumentNullException(nameof(observer)); } if (observer.disposed) { return; } VsThreadingHelper.RunInline(observer.serviceProvider, VsTaskRunContext.UIThreadNormalPriority, () => ((IDisposable)observer).Dispose()); }
/// <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) { Assert.IsNotNull(controller, "Controller argument is required"); Assert.IsNotNull(controller.Steps, "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.RunTask(controller, Microsoft.VisualStudio.Shell.VsTaskRunContext.UIThreadNormalPriority, () => { controller.Finished -= onFinished; }).Wait(); // Satisfy the sequential controller verification code e.Handled(); if (originalException != null) { Assert.AreEqual(ProgressControllerResult.Failed, e.Result, "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.RunTask(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); }
/// <summary> /// Invokes the <see cref="StateChanged"/> event based on the <see cref="ExecutionState"/> of the object /// </summary> protected virtual void OnExecutionStateChanged() { if (this.StateChangedPrivate != null) { VsThreadingHelper.RunInline(this.controller, VsTaskRunContext.UIThreadBackgroundPriority, () => { var delegates = this.StateChangedPrivate; if (delegates != null) { delegates(this, new StepExecutionChangedEventArgs(this)); } }); } }
void IProgressErrorNotifier.Notify(Exception ex) { if (ex == null) { throw new ArgumentNullException(nameof(ex)); } VsThreadingHelper.RunInline(this.serviceProvider, VsTaskRunContext.UIThreadNormalPriority, () => { int hr = this.pane.OutputStringThreadSafe(ProgressControllerHelper.FormatErrorMessage(ex, this.messageFormat, this.logFullException) + Environment.NewLine); if (this.ensureOutputVisible && ErrorHandler.Succeeded(hr) && ErrorHandler.Succeeded(pane.Activate())) { ShowOutputWindowFrame(serviceProvider); } }); }
private void ControllerFinished(object sender, ProgressControllerFinishedEventArgs e) { this.IsFinished = true; VsThreadingHelper.RunInline(this.serviceProvider, VsTaskRunContext.BackgroundThread, () => { // Give the last progress a chance to render System.Threading.Thread.Sleep(DelayAfterFinishInMS); }); VsThreadingHelper.RunInline(this.serviceProvider, VsTaskRunContext.UIThreadNormalPriority, () => { this.host.Hide(); ((IDisposable)this).Dispose(); }); // Flag that handled to assist with the verification, otherwise the controller will assert e.Handled(); }
private void OnStepStateChanged(object sender, StepExecutionChangedEventArgs args) { if (this.StepExecutionChangedPrivate != null) { VsThreadingHelper.RunInline(this, VsTaskRunContext.UIThreadNormalPriority, () => { var delegates = this.StepExecutionChangedPrivate; if (delegates != null) { delegates(sender, args); // Verify that the observer handled it since now easy way of testing // serialized raising and handling of the event across the classes args.CheckHandled(); } }); } }
private void OnCancellableChanged(bool cancellable) { if (this.CancellationSupportChangedPrivate != null) { VsThreadingHelper.RunInline(this, VsTaskRunContext.UIThreadNormalPriority, () => { var delegates = this.CancellationSupportChangedPrivate; if (delegates != null) { CancellationSupportChangedEventArgs args = new CancellationSupportChangedEventArgs(cancellable); delegates(this, args); // Verify that the observer handled it since now easy way of testing // serialized raising and handling of the event across the classes args.CheckHandled(); } }); } }
void IProgressErrorNotifier.Notify(Exception ex) { if (ex == null) { throw new ArgumentNullException(nameof(ex)); } VsThreadingHelper.RunInline(this.serviceProvider, VsTaskRunContext.UIThreadNormalPriority, () => { IVsActivityLog log = (IVsActivityLog)this.serviceProvider.GetService(typeof(SVsActivityLog)); if (log != null) { log.LogEntry((uint)__ACTIVITYLOG_ENTRYTYPE.ALE_ERROR, this.entrySource, ProgressControllerHelper.FormatErrorMessage(ex, messageFormat, logWholeMessage)); } else { Debug.Fail("Cannot find SVsActivityLog"); } }); }
private void OnFinished(ProgressControllerResult result) { this.IsFinished = true; this.ThreadSafeDisposeCancellationTokenSource(); VsThreadingHelper.RunInline(this, VsTaskRunContext.UIThreadNormalPriority, () => { ConfigureStepEventListeners(false); var delegates = this.FinishedPrivate; if (delegates != null) { ProgressControllerFinishedEventArgs args = new ProgressControllerFinishedEventArgs(result); delegates(this, args); // Verify that the observer handled it since now easy way of testing // serialized raising and handling of the event across the classes args.CheckHandled(); } }); }
private void OnStepExecutionChanged(object sender, StepExecutionChangedEventArgs e) { try { // Don't have to do it on the UI thread since we don't expect the mapping to change during execution if (!this.progressStepToViewModelMapping.ContainsKey(e.Step)) { Debug.Assert(!e.Step.ImpactsProgress, "View model out of sync. The step execution is for unexpected step"); return; } VsThreadingHelper.RunInline(this.serviceProvider, VsTaskRunContext.UIThreadNormalPriority, () => { this.UpdateViewModelStep(e); this.UpdateMainProgress(e); }); } finally { // Flag that handled to assist with the verification, otherwise the controller will assert e.Handled(); } }
/// <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> Start() { if (this.IsStarted) { throw new InvalidOperationException(ProgressResources.AlreadyStartedException); } this.OnStarted(); ProgressControllerResult controllerResult = await VsThreadingHelper.RunTask <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.Run(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); }