From aeccb48826f12bddecfbb28d2ee7e64777e6c19e Mon Sep 17 00:00:00 2001 From: Youssef1313 Date: Tue, 14 Jan 2025 05:08:27 +0100 Subject: [PATCH] Support exponential backoff --- .../Attributes/TestMethod/DelayBackoffType.cs | 22 +++++++++++++ .../Attributes/TestMethod/RetryAttribute.cs | 32 ++++++++++++------- .../Attributes/TestMethod/RetryResult.cs | 15 +++++++++ .../PublicAPI/PublicAPI.Unshipped.txt | 5 +++ 4 files changed, 62 insertions(+), 12 deletions(-) create mode 100644 src/TestFramework/TestFramework/Attributes/TestMethod/DelayBackoffType.cs create mode 100644 src/TestFramework/TestFramework/Attributes/TestMethod/RetryResult.cs diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/DelayBackoffType.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/DelayBackoffType.cs new file mode 100644 index 0000000000..416d86b363 --- /dev/null +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/DelayBackoffType.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestTools.UnitTesting; + +/// +/// Specifies a backoff type for the delay between retries. +/// +public enum DelayBackoffType +{ + /// + /// Specifies a constant backoff type. Meaning the delay between retries is constant. + /// + Constant, + + /// + /// Specifies an exponential backoff type. + /// The delay is calculated as the base delay * 2^(n-1) where n is the retry attempt. + /// For example, if the base delay is 1000ms, the delays will be 1000ms, 2000ms, 4000ms, 8000ms, etc. + /// + Exponential, +} diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/RetryAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/RetryAttribute.cs index 22008b44ed..f892961c45 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/RetryAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/RetryAttribute.cs @@ -3,17 +3,6 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; -public sealed class RetryResult -{ - private readonly List _testResults = new(); - - public void AddResult(TestResult[] testResults) - => _testResults.Add(testResults); - - internal TestResult[]? TryGetLast() - => _testResults.Count > 0 ? _testResults[_testResults.Count - 1] : null; -} - /// /// This attribute is used to set a retry count on a test method in case of failure. /// @@ -47,6 +36,20 @@ public RetryAttribute(int maxRetryAttempts) /// public int MillisecondsDelayBetweenRetries { get; set; } + public DelayBackoffType BackoffType + { + get => field; + set + { + if (value is < DelayBackoffType.Constant or > DelayBackoffType.Exponential) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } + + field = value; + } + } + /// /// Retries the test method times in case of failure. /// Note that a first run of the method was already executed and failed before this method is called. @@ -61,10 +64,15 @@ public RetryAttribute(int maxRetryAttempts) protected internal virtual async Task ExecuteAsync(RetryContext retryContext) { var result = new RetryResult(); + int currentDelay = MillisecondsDelayBetweenRetries; for (int i = 0; i < MaxRetryAttempts; i++) { // The caller already executed the test once. So we need to do the delay here. - await Task.Delay(MillisecondsDelayBetweenRetries); + await Task.Delay(currentDelay); + if (BackoffType == DelayBackoffType.Exponential) + { + currentDelay *= 2; + } TestResult[] testResults = await retryContext.ExecuteTaskGetter(); result.AddResult(testResults); diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/RetryResult.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/RetryResult.cs new file mode 100644 index 0000000000..7d1337450f --- /dev/null +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/RetryResult.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestTools.UnitTesting; + +public sealed class RetryResult +{ + private readonly List _testResults = new(); + + public void AddResult(TestResult[] testResults) + => _testResults.Add(testResults); + + internal TestResult[]? TryGetLast() + => _testResults.Count > 0 ? _testResults[_testResults.Count - 1] : null; +} diff --git a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt index 001d6f53fa..2d255084ba 100644 --- a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt @@ -190,6 +190,9 @@ Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpola Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler.AssertThrowsExactlyInterpolatedStringHandler(int literalLength, int formattedCount, System.Action! action, out bool shouldAppend) -> void Microsoft.VisualStudio.TestTools.UnitTesting.DataRowAttribute.IgnoreMessage.get -> string? Microsoft.VisualStudio.TestTools.UnitTesting.DataRowAttribute.IgnoreMessage.set -> void +Microsoft.VisualStudio.TestTools.UnitTesting.DelayBackoffType +Microsoft.VisualStudio.TestTools.UnitTesting.DelayBackoffType.Constant = 0 -> Microsoft.VisualStudio.TestTools.UnitTesting.DelayBackoffType +Microsoft.VisualStudio.TestTools.UnitTesting.DelayBackoffType.Exponential = 1 -> Microsoft.VisualStudio.TestTools.UnitTesting.DelayBackoffType Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute.DynamicDataAttribute(string! dynamicDataSourceName) -> void Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute.DynamicDataAttribute(string! dynamicDataSourceName, Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataSourceType dynamicDataSourceType) -> void Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute.DynamicDataAttribute(string! dynamicDataSourceName, System.Type! dynamicDataDeclaringType) -> void @@ -203,6 +206,8 @@ Microsoft.VisualStudio.TestTools.UnitTesting.ITestDataSourceIgnoreCapability Microsoft.VisualStudio.TestTools.UnitTesting.ITestDataSourceIgnoreCapability.IgnoreMessage.get -> string? Microsoft.VisualStudio.TestTools.UnitTesting.ITestDataSourceIgnoreCapability.IgnoreMessage.set -> void Microsoft.VisualStudio.TestTools.UnitTesting.RetryAttribute +Microsoft.VisualStudio.TestTools.UnitTesting.RetryAttribute.BackoffType.get -> Microsoft.VisualStudio.TestTools.UnitTesting.DelayBackoffType +Microsoft.VisualStudio.TestTools.UnitTesting.RetryAttribute.BackoffType.set -> void Microsoft.VisualStudio.TestTools.UnitTesting.RetryAttribute.MaxRetryAttempts.get -> int Microsoft.VisualStudio.TestTools.UnitTesting.RetryAttribute.MillisecondsDelayBetweenRetries.get -> int Microsoft.VisualStudio.TestTools.UnitTesting.RetryAttribute.MillisecondsDelayBetweenRetries.set -> void