/// <inheritdoc cref="XunitTestCase"/> /// <remarks> /// This method is called by the xUnit test framework classes to run the test case. We will do the /// loop here, forwarding on to the implementation in XunitTestCase to do the heavy lifting.We will /// continue to re-run the test until the aggregator has an error(meaning that some internal error /// condition happened), or the test runs without failure, or we've hit the maximum number of tries. /// </remarks> public override async Task <RunSummary> RunAsync( IMessageSink diagnosticMessageSink, IMessageBus messageBus, object[] constructorArguments, ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource ) { int runCount = 0; while (true) { // This is really the only tricky bit: we need to capture and delay messages (since those will // contain run status) until we know we've decided to accept the final result; var delayedMessageBus = new DelayedMessageBus(messageBus); string testName = DisplayName; if (runCount > 0) { testName += $"\n\nRETRY:{runCount}"; } await Policy .Handle <TaskCanceledException>() .Or <HttpRequestException>() .Or <ApiRequestException>() .WaitAndRetry(1, i => TimeSpan.FromSeconds(30)) .Execute(() => TestsFixture.Instance.SendTestCaseNotificationAsync(testName) ); var summary = await base.RunAsync (diagnosticMessageSink, delayedMessageBus, constructorArguments, aggregator, cancellationTokenSource); if (aggregator.HasExceptions || summary.Failed == 0 || ++runCount > _maxRetries || (summary.Failed == 1 && !string.IsNullOrEmpty(_exceptionTypeFullName) && !delayedMessageBus.FailedMessages.ExceptionTypes.Contains(_exceptionTypeFullName)) ) { delayedMessageBus.Dispose(); // Sends all the delayed messages return(summary); } diagnosticMessageSink.OnMessage(new DiagnosticMessage( "Execution of '{0}' failed (attempt #{1}), retrying in {2} seconds...", DisplayName, runCount, _delaySeconds )); await Task.Delay(_delaySeconds * 1_000); } }
/// <inheritdoc cref="XunitTestCase"/> /// <remarks> /// This method is called by the xUnit test framework classes to run the test case. We will do the /// loop here, forwarding on to the implementation in XunitTestCase to do the heavy lifting.We will /// continue to re-run the test until the aggregator has an error(meaning that some internal error /// condition happened), or the test runs without failure, or we've hit the maximum number of tries. /// </remarks> public override async Task <RunSummary> RunAsync( IMessageSink diagnosticMessageSink, IMessageBus messageBus, object[] constructorArguments, ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource ) { int runCount = 0; while (true) { // This is really the only tricky bit: we need to capture and delay messages (since those will // contain run status) until we know we've decided to accept the final result; var delayedMessageBus = new DelayedMessageBus(messageBus); var summary = await base.RunAsync (diagnosticMessageSink, delayedMessageBus, constructorArguments, aggregator, cancellationTokenSource); if (aggregator.HasExceptions || summary.Failed == 0 || ++runCount > _maxRetries || (summary.Failed == 1 && !string.IsNullOrEmpty(_exceptionTypeFullName) && !delayedMessageBus.FailedMessages.ExceptionTypes.Contains(_exceptionTypeFullName)) ) { delayedMessageBus.Dispose(); // Sends all the delayed messages return(summary); } diagnosticMessageSink.OnMessage(new DiagnosticMessage( "Execution of '{0}' failed (attempt #{1}), retrying in {2} seconds...", DisplayName, runCount, _delaySeconds )); await Task.Delay(_delaySeconds * 1_000); } }
/// <inheritdoc cref="XunitTestCase"/> /// <remarks> /// This method is called by the xUnit test framework classes to run the test case. We will do the /// loop here, forwarding on to the implementation in XunitTestCase to do the heavy lifting.We will /// continue to re-run the test until the aggregator has an error(meaning that some internal error /// condition happened), or the test runs without failure, or we've hit the maximum number of tries. /// </remarks> public override async Task <RunSummary> RunAsync( IMessageSink diagnosticMessageSink, IMessageBus messageBus, object[] constructorArguments, ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource ) { int runCount = 0; while (true) { // This is really the only tricky bit: we need to capture and delay messages (since those will // contain run status) until we know we've decided to accept the final result; var delayedMessageBus = new DelayedMessageBus(messageBus); string testName = DisplayName; if (runCount > 0) { testName += $"\n\nRETRY:{runCount}"; } // Do not throw any exceptions here if can't send test case notification because // xunit do not expects any exceptions here and so it crashes the process. // Notification sending fails probably because of rate limiting by Telegram. for (int i = 0; i < 2; i++) { try { await TestsFixture.Instance.SendTestCaseNotificationAsync(testName); break; } catch (Exception) { // Log any exceptions here so we could at least know if notification // sending failed var waitTimeout = 30; var message = new DiagnosticMessage( "Couldn't send test name notification for test '{0}', " + "will try one more in {1} seconds.", DisplayName, waitTimeout ); diagnosticMessageSink.OnMessage(message); await Task.Delay(TimeSpan.FromSeconds(waitTimeout)); } } // await Policy // .Handle<TaskCanceledException>() // .Or<HttpRequestException>() // .Or<ApiRequestException>() // .WaitAndRetry(1, i => TimeSpan.FromSeconds(30)) // .Execute(() => // TestsFixture.Instance.SendTestCaseNotificationAsync(testName) // ); var summary = await base.RunAsync( diagnosticMessageSink, delayedMessageBus, constructorArguments, aggregator, cancellationTokenSource ); runCount += 1; var testRunHasUnexpectedErrors = aggregator.HasExceptions || summary.Failed == 0; var retryExceeded = runCount > _maxRetries; var testRunHasExpectedException = summary.Failed == 1 && !delayedMessageBus .ContainsException(_exceptionTypeFullName); var testCaseRunShouldReturn = testRunHasUnexpectedErrors || retryExceeded || testRunHasExpectedException; if (testCaseRunShouldReturn) { delayedMessageBus.Dispose(); // Sends all the delayed messages return(summary); } diagnosticMessageSink.OnMessage(new DiagnosticMessage( "Execution of '{0}' failed (attempt #{1}), retrying in {2} seconds...", DisplayName, runCount, _delaySeconds )); await Task.Delay(_delaySeconds * 1_000); } }