Skip to content
This repository has been archived by the owner on Oct 18, 2023. It is now read-only.

Commit

Permalink
Adding Retry Capability
Browse files Browse the repository at this point in the history
  • Loading branch information
dansiegel committed Mar 5, 2019
1 parent 9acc51b commit 5a18266
Show file tree
Hide file tree
Showing 10 changed files with 187 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="XunitRetry" Version="2.0.0" />
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System.Collections.Generic;
using Xunit.Abstractions;
using Xunit.Sdk;

namespace Xunit
{
/// <summary>
/// Used to capture messages to potentially be forwarded later. Messages are forwarded by
/// disposing of the message bus.
/// </summary>
public class DelayedMessageBus : IMessageBus
{
private readonly IMessageBus innerBus;
private readonly List<IMessageSinkMessage> messages = new List<IMessageSinkMessage>();

public DelayedMessageBus(IMessageBus innerBus)
{
this.innerBus = innerBus;
}

public bool QueueMessage(IMessageSinkMessage message)
{
lock (messages)
messages.Add(message);

// No way to ask the inner bus if they want to cancel without sending them the message, so
// we just go ahead and continue always.
return true;
}

public void Dispose()
{
foreach (var message in messages)
innerBus.QueueMessage(message);
}
}
}
27 changes: 27 additions & 0 deletions tests/Prism.Plugin.Popups.Tests.Shared/Helpers/RetryAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Xunit;
using Xunit.Sdk;

namespace Xunit
{
/// <summary>
/// Works just like [Fact] except that failures are retried (by default, 3 times).
/// </summary>
#if DRYIOC
[XunitTestCaseDiscoverer("Xunit.RetryFactDiscoverer", "Prism.Plugin.Popups.DryIoc.Tests")]
#else
[XunitTestCaseDiscoverer("Xunit.RetryFactDiscoverer", "Prism.Plugin.Popups.Unity.Tests")]
#endif
public class RetryAttribute : FactAttribute
{
public RetryAttribute(int maxRetries = 3)
{
MaxRetries = maxRetries;
}

/// <summary>
/// Number of retries allowed for a failed test. If unset (or set less than 1), will
/// default to 3 attempts.
/// </summary>
public int MaxRetries { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Collections.Generic;
using Xunit.Abstractions;
using Xunit.Sdk;

namespace Xunit
{
public class RetryFactDiscoverer : IXunitTestCaseDiscoverer
{
readonly IMessageSink diagnosticMessageSink;

public RetryFactDiscoverer(IMessageSink diagnosticMessageSink)
{
this.diagnosticMessageSink = diagnosticMessageSink;
}

public IEnumerable<IXunitTestCase> Discover(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo factAttribute)
{
var maxRetries = factAttribute.GetNamedArgument<int>("MaxRetries");
if (maxRetries < 1)
maxRetries = 3;

yield return new RetryTestCase(diagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), TestMethodDisplayOptions.All, testMethod, maxRetries);
}
}
}
79 changes: 79 additions & 0 deletions tests/Prism.Plugin.Popups.Tests.Shared/Helpers/RetryTestCase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using System;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using Xunit.Abstractions;
using Xunit.Sdk;

namespace Xunit
{
[Serializable]
public class RetryTestCase : XunitTestCase
{
private int maxRetries;

[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Called by the de-serializer", true)]
public RetryTestCase() { }

//[Obsolete]
//public RetryTestCase(IMessageSink diagnosticMessageSink, TestMethodDisplay testMethodDisplay, ITestMethod testMethod, int maxRetries)
// : base(diagnosticMessageSink, testMethodDisplay, testMethod, testMethodArguments: null)
//{
// this.maxRetries = maxRetries;
//}

public RetryTestCase(IMessageSink diagnosticMessageSink, TestMethodDisplay defaultMethodDisplay, TestMethodDisplayOptions defaultMethodDisplayOptions, ITestMethod testMethod, int maxRetries, object[] testMethodArguments = null)
: base(diagnosticMessageSink, defaultMethodDisplay, defaultMethodDisplayOptions, testMethod, testMethodArguments)
{
this.maxRetries = maxRetries;
}

// 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.
public override async Task<RunSummary> RunAsync(IMessageSink diagnosticMessageSink,
IMessageBus messageBus,
object[] constructorArguments,
ExceptionAggregator aggregator,
CancellationTokenSource cancellationTokenSource)
{
var 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)
{
delayedMessageBus.Dispose(); // Sends all the delayed messages
return summary;
}

diagnosticMessageSink.OnMessage(new DiagnosticMessage("Execution of '{0}' failed (attempt #{1}), retrying...", DisplayName, runCount));
Xamarin.Forms.Mocks.MockForms.Init();
Prism.Navigation.PageNavigationRegistry.ClearRegistrationCache();
GC.Collect();
await Task.Delay(100);
}
}

public override void Serialize(IXunitSerializationInfo data)
{
base.Serialize(data);

data.AddValue("MaxRetries", maxRetries);
}

public override void Deserialize(IXunitSerializationInfo data)
{
base.Deserialize(data);

maxRetries = data.GetValue<int>("MaxRetries");
}
}
}
9 changes: 7 additions & 2 deletions tests/Prism.Plugin.Popups.Tests.Shared/Mocks/AppMock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Prism.Plugin.Popups.Tests.Mocks.Views;
using Prism.Navigation;
using Xamarin.Forms;
using Prism.Logging;
#if AUTOFAC
using Prism.Autofac;
#elif DRYIOC
Expand All @@ -20,9 +21,13 @@ public AppMock(IPlatformInitializer platformInitializer)

}

protected override void OnInitialized()
protected override async void OnInitialized()
{
NavigationService.NavigateAsync("MainPage").ContinueWith(t => { });
var result = await NavigationService.NavigateAsync("MainPage");
if(!result.Success)
{
Container.Resolve<ILoggerFacade>().Log(result.Exception.ToString(), Category.Exception, Priority.High);
}
}

protected override void RegisterTypes(IContainerRegistry containerRegistry)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
<Import_RootNamespace>Prism.Plugin.Popups.Tests</Import_RootNamespace>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)Helpers\DelayedMessageBus.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Helpers\RetryAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Helpers\RetryFactDiscoverer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Helpers\RetryTestCase.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Tests\FixtureBase.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Tests\NavigationServiceFixture.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Tests\RegistrationFixture.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Xunit;
using Xunit.Abstractions;
using Prism.Navigation;
using Prism.Common;

#if AUTOFAC
namespace Prism.Plugin.Popups.Autofac.Tests
Expand All @@ -27,11 +28,13 @@ public NavigationServiceFixture(ITestOutputHelper testOutputHelper)
{
}

public void PopupNavigationService_SetsStandardPages()
[Retry]
public async Task PopupNavigationService_SetsStandardPages()
{
var app = GetApp();
Assert.Empty(PopupNavigation.Instance.PopupStack);
await Task.Delay(150);

Assert.NotNull(app.MainPage);
Assert.IsType<MainPage>(app.MainPage);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;

Expand Down Expand Up @@ -67,10 +68,12 @@ public void Application_Has_PopupNavigationService()
Assert.IsType<PopupPageNavigationService>(app.GetNavigationService());
}

public void MainPage_Has_PopupNavigationService()
[Retry]
public async Task MainPage_Has_PopupNavigationService()
{
var app = GetApp();
await Task.Delay(150);

var vm = app.MainPage.BindingContext as MainPageViewModel;
Assert.NotNull(vm);
Assert.NotNull(vm.NavigationService);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="XunitRetry" Version="2.0.0" />
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
</ItemGroup>

Expand Down

0 comments on commit 5a18266

Please sign in to comment.