Make character UI retries transactional
This commit is contained in:
parent
aeac874dab
commit
1dd5706e15
6 changed files with 352 additions and 42 deletions
|
|
@ -188,6 +188,66 @@ public sealed class CharacterManagementUiControllerTests
|
|||
Assert.Equal(3, resourceAttempts);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MountCoordinator_PostAttachFailuresDisposeBeforeRetryAndRecovery()
|
||||
{
|
||||
var host = new UiRoot { Width = 800f, Height = 600f };
|
||||
var runtime = new FakeRuntime();
|
||||
using var dialogs = new RetailDialogFactory(
|
||||
host,
|
||||
RetailDialogFactoryTests.BuildDialogLayout);
|
||||
var screens = new List<ImportedLayout>();
|
||||
int failingAttempts = 2;
|
||||
using var coordinator = new CharacterManagementUiMountCoordinator(
|
||||
host,
|
||||
runtime.Bindings,
|
||||
() => dialogs,
|
||||
() =>
|
||||
{
|
||||
ImportedLayout screen = BuildScreen();
|
||||
screens.Add(screen);
|
||||
bool throwAfterAttach = failingAttempts-- > 0;
|
||||
return new CharacterManagementUiMountResources(
|
||||
0x21000004u,
|
||||
screen,
|
||||
(_, _) => throwAfterAttach
|
||||
? throw new InvalidOperationException(
|
||||
"template failed after root attach")
|
||||
: BuildRow(),
|
||||
TestStrings());
|
||||
});
|
||||
|
||||
coordinator.Tick();
|
||||
Assert.Null(coordinator.Controller);
|
||||
Assert.Empty(host.Children);
|
||||
Assert.Single(screens);
|
||||
AssertDetachedAndUnbound(screens[0]);
|
||||
|
||||
coordinator.Tick();
|
||||
Assert.Null(coordinator.Controller);
|
||||
Assert.Empty(host.Children);
|
||||
Assert.Equal(2, screens.Count);
|
||||
Assert.All(screens, AssertDetachedAndUnbound);
|
||||
|
||||
coordinator.Tick();
|
||||
CharacterManagementUiController mounted = Assert.IsType<
|
||||
CharacterManagementUiController>(coordinator.Controller);
|
||||
Assert.Equal(3, screens.Count);
|
||||
Assert.Single(host.Children);
|
||||
Assert.Same(mounted.Root, host.Children[0]);
|
||||
Assert.NotNull(Assert.IsType<UiButton>(screens[2].FindElement(
|
||||
CharacterManagementUiController.EnterElementId)).OnClick);
|
||||
|
||||
coordinator.Tick();
|
||||
Assert.Equal(3, screens.Count);
|
||||
Assert.Single(host.Children);
|
||||
|
||||
coordinator.Dispose();
|
||||
Assert.Empty(host.Children);
|
||||
Assert.Null(coordinator.Controller);
|
||||
Assert.All(screens, AssertDetachedAndUnbound);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DeleteConfirmation_IsCaseInsensitive_ThenWaitsThroughAckUntilFreshRoster()
|
||||
{
|
||||
|
|
@ -509,6 +569,17 @@ public sealed class CharacterManagementUiControllerTests
|
|||
"Please Wait",
|
||||
"Entering World");
|
||||
|
||||
private static void AssertDetachedAndUnbound(ImportedLayout screen)
|
||||
{
|
||||
Assert.Null(screen.Root.Parent);
|
||||
Assert.Null(Assert.IsType<UiButton>(screen.FindElement(
|
||||
CharacterManagementUiController.EnterElementId)).OnClick);
|
||||
Assert.Null(Assert.IsType<UiButton>(screen.FindElement(
|
||||
CharacterManagementUiController.DeleteElementId)).OnClick);
|
||||
Assert.Null(Assert.IsType<UiButton>(screen.FindElement(
|
||||
CharacterManagementUiController.RestoreElementId)).OnClick);
|
||||
}
|
||||
|
||||
private static ImportedLayout BuildScreen(bool includePreview = false)
|
||||
{
|
||||
var root = new ElementInfo
|
||||
|
|
|
|||
|
|
@ -542,6 +542,110 @@ public sealed class RetailDialogFactoryTests
|
|||
Assert.True(factory.CloseDialog(later));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PriorityRequest_PreemptsOrdinaryRetryAndPreservesOrdinaryFifo()
|
||||
{
|
||||
var root = new UiRoot { Width = 800f, Height = 600f };
|
||||
bool waitsAvailable = false;
|
||||
using var factory = new RetailDialogFactory(root, type =>
|
||||
type == RetailDialogType.Wait && !waitsAvailable
|
||||
? null
|
||||
: BuildDialogLayout(type));
|
||||
|
||||
uint failed = factory.MakeWait("failed ordinary");
|
||||
uint later = factory.MakeWait("later ordinary");
|
||||
uint priority = factory.MakeDialog(
|
||||
Priority(RetailDialogData.Message("priority")));
|
||||
|
||||
Assert.Equal(1, factory.ActiveCount);
|
||||
Assert.Equal(1, factory.RetryCount);
|
||||
Assert.Equal(1, factory.PendingCount);
|
||||
Assert.Equal("priority", MessageFromAnyDialog(root.Modal!));
|
||||
|
||||
Assert.True(factory.CloseDialog(priority));
|
||||
Assert.Equal(0, factory.ActiveCount);
|
||||
Assert.Equal(1, factory.RetryCount);
|
||||
Assert.Equal(1, factory.PendingCount);
|
||||
|
||||
waitsAvailable = true;
|
||||
factory.Tick();
|
||||
Assert.Equal("failed ordinary", MessageFromAnyDialog(root.Modal!));
|
||||
Assert.True(factory.CloseDialog(failed));
|
||||
Assert.Equal("later ordinary", MessageFromAnyDialog(root.Modal!));
|
||||
Assert.True(factory.CloseDialog(later));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FailedPriority_RetriesAheadOfRestoredActiveAndQueuedDialog()
|
||||
{
|
||||
var root = new UiRoot { Width = 800f, Height = 600f };
|
||||
bool priorityAvailable = false;
|
||||
using var factory = new RetailDialogFactory(root, type =>
|
||||
type == RetailDialogType.Message && !priorityAvailable
|
||||
? null
|
||||
: BuildDialogLayout(type));
|
||||
|
||||
uint active = factory.MakeWait("active");
|
||||
uint queued = factory.MakeWait("queued");
|
||||
uint priority = factory.MakeDialog(
|
||||
Priority(RetailDialogData.Message("priority")));
|
||||
|
||||
Assert.Equal(1, factory.ActiveCount);
|
||||
Assert.Equal(1, factory.RetryCount);
|
||||
Assert.Equal(1, factory.PendingCount);
|
||||
Assert.Equal("active", MessageFromAnyDialog(root.Modal!));
|
||||
|
||||
priorityAvailable = true;
|
||||
factory.Tick();
|
||||
|
||||
Assert.Equal(1, factory.ActiveCount);
|
||||
Assert.Equal(0, factory.RetryCount);
|
||||
Assert.Equal(2, factory.PendingCount);
|
||||
Assert.Equal("priority", MessageFromAnyDialog(root.Modal!));
|
||||
|
||||
Assert.True(factory.CloseDialog(priority));
|
||||
Assert.Equal("active", MessageFromAnyDialog(root.Modal!));
|
||||
Assert.Equal(1, factory.PendingCount);
|
||||
Assert.True(factory.CloseDialog(active));
|
||||
Assert.Equal("queued", MessageFromAnyDialog(root.Modal!));
|
||||
Assert.True(factory.CloseDialog(queued));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MultipleRetries_NewestPriorityFirstThenOlderPriorityThenOrdinaryFifo()
|
||||
{
|
||||
var root = new UiRoot { Width = 800f, Height = 600f };
|
||||
bool available = false;
|
||||
using var factory = new RetailDialogFactory(root, type =>
|
||||
!available ? null : BuildDialogLayout(type));
|
||||
|
||||
uint ordinary = factory.MakeWait("ordinary");
|
||||
uint later = factory.MakeWait("later");
|
||||
uint olderPriority = factory.MakeDialog(
|
||||
Priority(RetailDialogData.Message("older priority")));
|
||||
uint newerPriority = factory.MakeDialog(Priority(
|
||||
RetailDialogData.ConfirmationTextInput("newer priority")));
|
||||
|
||||
Assert.Equal(0, factory.ActiveCount);
|
||||
Assert.Equal(3, factory.RetryCount);
|
||||
Assert.Equal(1, factory.PendingCount);
|
||||
|
||||
available = true;
|
||||
factory.Tick();
|
||||
|
||||
Assert.Equal(1, factory.ActiveCount);
|
||||
Assert.Equal(2, factory.RetryCount);
|
||||
Assert.Equal("newer priority", MessageFromAnyDialog(root.Modal!));
|
||||
|
||||
Assert.True(factory.CloseDialog(newerPriority));
|
||||
Assert.Equal("older priority", MessageFromAnyDialog(root.Modal!));
|
||||
Assert.True(factory.CloseDialog(olderPriority));
|
||||
Assert.Equal("ordinary", MessageFromAnyDialog(root.Modal!));
|
||||
Assert.True(factory.CloseDialog(ordinary));
|
||||
Assert.Equal("later", MessageFromAnyDialog(root.Modal!));
|
||||
Assert.True(factory.CloseDialog(later));
|
||||
}
|
||||
|
||||
private static RetailDialogFactory CreateFactory(
|
||||
UiRoot root,
|
||||
List<ImportedLayout> layouts)
|
||||
|
|
@ -575,6 +679,11 @@ public sealed class RetailDialogFactoryTests
|
|||
.LinesProvider()
|
||||
.Select(static line => line.Text));
|
||||
|
||||
private static RetailDialogData Priority(RetailDialogData data) =>
|
||||
data.Set(RetailDialogProperty.Priority, true)
|
||||
.Set(RetailDialogProperty.QueueKey,
|
||||
RetailDialogFactory.DefaultQueueKey);
|
||||
|
||||
internal static ImportedLayout BuildDialogLayout(RetailDialogType type)
|
||||
{
|
||||
uint rootId = RetailDialogFactory.RootElementId(type);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue