/// <summary> /// To the extent reasonably possible, this method resets the state of the test environment to the same state as /// it started, ensuring that tests running in sequence cannot influence the outcome of later tests. /// </summary> /// <remarks> /// <para>The test cleanup runs in two primary steps:</para> /// <list type="number"> /// <item>Waiting for asynchronous operations started by the test to complete.</item> /// <item>Disposing of mutable resources created by the test.</item> /// <item>Clearing static state variables related to the use of MEF during a test.</item> /// </list> /// </remarks> public override void After(MethodInfo methodUnderTest) { var exportProvider = ExportProviderCache.ExportProviderForCleanup; try { var listenerProvider = exportProvider?.GetExportedValues <IAsynchronousOperationListenerProvider>().SingleOrDefault(); if (listenerProvider != null) { if (ForegroundThreadAffinitizedObject.CurrentForegroundThreadData.Kind != ForegroundThreadDataKind.Unknown) { // Immediately clear items from the foreground notification service for which cancellation is // requested. This service maintains a queue separately from Tasks, and work items scheduled for // execution after a delay are not immediately purged when cancellation is requested. This code // instructs the service to walk the list of queued work items and immediately cancel and purge any // which are already cancelled. var foregroundNotificationService = exportProvider?.GetExportedValues <IForegroundNotificationService>().SingleOrDefault() as ForegroundNotificationService; foregroundNotificationService?.ReleaseCancelledItems(); } // Join remaining operations with a timeout using (var timeoutTokenSource = new CancellationTokenSource(CleanupTimeout)) { try { var waiter = ((AsynchronousOperationListenerProvider)listenerProvider).WaitAllDispatcherOperationAndTasksAsync(); waiter.JoinUsingDispatcher(timeoutTokenSource.Token); } catch (OperationCanceledException ex) when(timeoutTokenSource.IsCancellationRequested) { var messageBuilder = new StringBuilder("Failed to clean up listeners in a timely manner."); foreach (var token in ((AsynchronousOperationListenerProvider)listenerProvider).GetTokens()) { messageBuilder.AppendLine().Append($" {token}"); } throw new TimeoutException(messageBuilder.ToString(), ex); } } } } finally { // Dispose of the export provider, including calling Dispose for any IDisposable services created during // the test. exportProvider?.Dispose(); // Replace hooks with ones that always throw exceptions. These hooks detect cases where code executing // after the end of a test attempts to create an ExportProvider. MefHostServices.HookServiceCreation(DenyMefHostServicesCreationBetweenTests); RoslynServices.HookHostServices(() => throw new InvalidOperationException("Cannot create host services after test tear down.")); // Reset static state variables. DesktopMefHostServices.ResetHostServicesTestOnly(); _hostServices = null; ExportProviderCache.SetEnabled_OnlyUseExportProviderAttributeCanCall(false); } }
public override void Before(MethodInfo methodUnderTest) { MefHostServices.HookServiceCreation(CreateMefHostServices); RoslynServices.HookHostServices(() => _remoteHostServices.Value); DesktopMefHostServices.ResetHostServicesTestOnly(); // make sure we enable this for all unit tests AsynchronousOperationListenerProvider.Enable(enable: true, diagnostics: true); ExportProviderCache.SetEnabled_OnlyUseExportProviderAttributeCanCall(true); }
public override void After(MethodInfo methodUnderTest) { base.After(methodUnderTest); DesktopMefHostServices.ResetHostServicesTestOnly(); }
public override void Before(MethodInfo methodUnderTest) { DesktopMefHostServices.ResetHostServicesTestOnly(); base.Before(methodUnderTest); }