From e3e9d2060e45bd970ae546790ce6f4e8915757cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20El-Saig?= Date: Sat, 7 Oct 2023 17:21:35 +0200 Subject: [PATCH 1/4] Improve stability directly after a page load. --- .../Extensions/ExtendedLoggingExtensions.cs | 68 ++++++++++++++++--- 1 file changed, 59 insertions(+), 9 deletions(-) diff --git a/Lombiq.Tests.UI/Extensions/ExtendedLoggingExtensions.cs b/Lombiq.Tests.UI/Extensions/ExtendedLoggingExtensions.cs index 9289aa4fc..a6a3f7959 100644 --- a/Lombiq.Tests.UI/Extensions/ExtendedLoggingExtensions.cs +++ b/Lombiq.Tests.UI/Extensions/ExtendedLoggingExtensions.cs @@ -1,5 +1,6 @@ using Atata; using Lombiq.Tests.UI.Services; +using Microsoft.SqlServer.Management.Dmf; using OpenQA.Selenium; using System; using System.Threading.Tasks; @@ -8,6 +9,11 @@ namespace Lombiq.Tests.UI.Extensions; public static class ExtendedLoggingExtensions { + /// + /// Used for edge cases like when a scope becomes stale + /// + private const int StabilityRetryCount = 3; + public static Task ExecuteLoggedAsync( this UITestContext context, string operationName, IWebElement element, Func functionAsync) => context.ExecuteSectionAsync(GetLogSection(operationName, element), functionAsync); @@ -87,10 +93,41 @@ private static string GetSectionMessage(string operationName, string objectOfOpe $"{operationName} applied to: {Environment.NewLine}{objectOfOperation}"; private static void ExecuteSection(this UITestContext context, LogSection section, Action action) => - context.Scope.AtataContext.Log.ExecuteSection(section, action); + context.Scope.AtataContext.Log.ExecuteSection(section, () => + { + for (int i = 0; i < StabilityRetryCount; i++) + { + var notLast = i < StabilityRetryCount - 1; + try + { + action(); + return; + } + catch (StaleElementReferenceException) when (notLast) + { + Task.Delay(TimeSpan.FromSeconds(1)).Wait(); + } + } + }); private static TResult ExecuteSection(this UITestContext context, LogSection section, Func function) => - context.Scope.AtataContext.Log.ExecuteSection(section, function); + context.Scope.AtataContext.Log.ExecuteSection(section, () => + { + for (int i = 0; i < StabilityRetryCount; i++) + { + var notLast = i < StabilityRetryCount - 1; + try + { + return function(); + } + catch (StaleElementReferenceException) when (notLast) + { + Task.Delay(TimeSpan.FromSeconds(1)).Wait(); + } + } + + throw new InvalidOperandException("Impossible to reach."); + }); private static Task ExecuteSectionAsync(this UITestContext context, LogSection section, Func functionAsync) => context.ExecuteSectionAsync( @@ -104,12 +141,25 @@ private static Task ExecuteSectionAsync(this UITestContext context, LogSection s private static async Task ExecuteSectionAsync( this UITestContext context, LogSection section, Func> functionAsync) { - // This is somewhat risky. ILogManager is not thread-safe and uses as stack to keep track of sections, so if - // multiple sections are started in concurrent threads, the result will be incorrect. This shouldn't be too much - // of an issue for now though since tests, while async, are single-threaded. - context.Scope.AtataContext.Log.Start(section); - var result = await functionAsync(); - context.Scope.AtataContext.Log.EndSection(); - return result; + for (int i = 0; i < StabilityRetryCount; i++) + { + var notLast = i < StabilityRetryCount - 1; + try + { + // This is somewhat risky. ILogManager is not thread-safe and uses as stack to keep track of sections, so if + // multiple sections are started in concurrent threads, the result will be incorrect. This shouldn't be too much + // of an issue for now though since tests, while async, are single-threaded. + context.Scope.AtataContext.Log.Start(section); + var result = await functionAsync(); + context.Scope.AtataContext.Log.EndSection(); + return result; + } + catch (StaleElementReferenceException) when (notLast) + { + await Task.Delay(TimeSpan.FromSeconds(1)); + } + } + + throw new InvalidOperandException("Impossible to reach."); } } From 69d6a027e1e4550212c25e0b9d42437c05b7e37d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20El-Saig?= Date: Sat, 7 Oct 2023 17:29:39 +0200 Subject: [PATCH 2/4] Fix typo. --- Lombiq.Tests.UI/Extensions/ExtendedLoggingExtensions.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Lombiq.Tests.UI/Extensions/ExtendedLoggingExtensions.cs b/Lombiq.Tests.UI/Extensions/ExtendedLoggingExtensions.cs index a6a3f7959..ff69211ad 100644 --- a/Lombiq.Tests.UI/Extensions/ExtendedLoggingExtensions.cs +++ b/Lombiq.Tests.UI/Extensions/ExtendedLoggingExtensions.cs @@ -1,6 +1,5 @@ using Atata; using Lombiq.Tests.UI.Services; -using Microsoft.SqlServer.Management.Dmf; using OpenQA.Selenium; using System; using System.Threading.Tasks; @@ -126,7 +125,7 @@ private static TResult ExecuteSection(this UITestContext context, LogSe } } - throw new InvalidOperandException("Impossible to reach."); + throw new InvalidOperationException("Impossible to reach."); }); private static Task ExecuteSectionAsync(this UITestContext context, LogSection section, Func functionAsync) => @@ -160,6 +159,6 @@ private static async Task ExecuteSectionAsync( } } - throw new InvalidOperandException("Impossible to reach."); + throw new InvalidOperationException("Impossible to reach."); } } From 1796c54610c9f810232f9c63a04e2864d8a145b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20El-Saig?= Date: Sat, 7 Oct 2023 17:37:17 +0200 Subject: [PATCH 3/4] Fix doc. --- Lombiq.Tests.UI/Extensions/ExtendedLoggingExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lombiq.Tests.UI/Extensions/ExtendedLoggingExtensions.cs b/Lombiq.Tests.UI/Extensions/ExtendedLoggingExtensions.cs index ff69211ad..7240b5e1a 100644 --- a/Lombiq.Tests.UI/Extensions/ExtendedLoggingExtensions.cs +++ b/Lombiq.Tests.UI/Extensions/ExtendedLoggingExtensions.cs @@ -9,7 +9,7 @@ namespace Lombiq.Tests.UI.Extensions; public static class ExtendedLoggingExtensions { /// - /// Used for edge cases like when a scope becomes stale + /// Used for edge cases like when a scope becomes stale. /// private const int StabilityRetryCount = 3; From c3412e203bda7ce163e3bab4c6be7124600ed4a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20El-Saig?= Date: Tue, 17 Oct 2023 21:29:53 +0200 Subject: [PATCH 4/4] Use Thread.Sleep instead. --- Lombiq.Tests.UI/Extensions/ExtendedLoggingExtensions.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Lombiq.Tests.UI/Extensions/ExtendedLoggingExtensions.cs b/Lombiq.Tests.UI/Extensions/ExtendedLoggingExtensions.cs index 7240b5e1a..849fb0b32 100644 --- a/Lombiq.Tests.UI/Extensions/ExtendedLoggingExtensions.cs +++ b/Lombiq.Tests.UI/Extensions/ExtendedLoggingExtensions.cs @@ -2,6 +2,7 @@ using Lombiq.Tests.UI.Services; using OpenQA.Selenium; using System; +using System.Threading; using System.Threading.Tasks; namespace Lombiq.Tests.UI.Extensions; @@ -104,7 +105,7 @@ private static void ExecuteSection(this UITestContext context, LogSection sectio } catch (StaleElementReferenceException) when (notLast) { - Task.Delay(TimeSpan.FromSeconds(1)).Wait(); + Thread.Sleep(TimeSpan.FromSeconds(1)); } } }); @@ -121,7 +122,7 @@ private static TResult ExecuteSection(this UITestContext context, LogSe } catch (StaleElementReferenceException) when (notLast) { - Task.Delay(TimeSpan.FromSeconds(1)).Wait(); + Thread.Sleep(TimeSpan.FromSeconds(1)); } }