feat(items): port retail external-container looting
Add the ClientUISystem ground-object lifecycle, authoritative root and nested ViewContents projections, replacement and close semantics, and the DAT-authored gmExternalContainerUI strip for chests and corpses. Route double-click loot and full or partial drag transfers through the shared retail item policy without optimistic external ownership. Remove the incorrect NoLongerViewingContents behavior from owned side packs and retire AP-106/#196. Release build succeeds and all 5,875 tests pass with five intentional skips. Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
48a118db91
commit
20ce67b625
27 changed files with 1750 additions and 45 deletions
|
|
@ -19,6 +19,8 @@ public sealed class ItemInteractionControllerTests
|
|||
public readonly List<(uint Source, uint Target)> UseWithTarget = new();
|
||||
public readonly List<(uint Item, uint Mask)> Wields = new();
|
||||
public readonly List<(uint Item, uint Container, int Placement)> Puts = new();
|
||||
public readonly List<(uint Item, uint Container, uint Placement, uint Amount)> SplitPuts = new();
|
||||
public readonly List<uint> ExternalRequests = new();
|
||||
public readonly List<uint> Drops = new();
|
||||
public readonly List<(uint Item, uint Amount)> SplitDrops = new();
|
||||
public readonly List<(uint Target, uint Item, uint Amount)> Gives = new();
|
||||
|
|
@ -28,6 +30,7 @@ public sealed class ItemInteractionControllerTests
|
|||
public uint SelectedObject;
|
||||
public bool NonCombatMode;
|
||||
public bool DragOnPlayerOpensSecureTrade = true;
|
||||
public uint GroundObject;
|
||||
public long Now = 1_000;
|
||||
|
||||
public Harness()
|
||||
|
|
@ -61,11 +64,19 @@ public sealed class ItemInteractionControllerTests
|
|||
selectedObjectId: () => SelectedObject,
|
||||
stackSplitQuantity: SplitQuantity,
|
||||
inNonCombatMode: () => NonCombatMode,
|
||||
groundObjectId: () => GroundObject,
|
||||
sendPutItemInContainer: (item, container, placement) =>
|
||||
Puts.Add((item, container, placement)),
|
||||
sendGive: (target, item, amount) => Gives.Add((target, item, amount)),
|
||||
dragOnPlayerOpensSecureTrade: () => DragOnPlayerOpensSecureTrade,
|
||||
systemMessage: SystemMessages.Add);
|
||||
systemMessage: SystemMessages.Add,
|
||||
sendSplitToContainer: (item, container, placement, amount) =>
|
||||
SplitPuts.Add((item, container, placement, amount)),
|
||||
requestExternalContainer: id =>
|
||||
{
|
||||
GroundObject = id;
|
||||
ExternalRequests.Add(id);
|
||||
});
|
||||
}
|
||||
|
||||
public ItemInteractionController Controller { get; }
|
||||
|
|
@ -103,6 +114,51 @@ public sealed class ItemInteractionControllerTests
|
|||
Assert.Empty(h.UseWithTarget);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExternalContainerUse_RequestsGroundObjectAndSendsUse()
|
||||
{
|
||||
var h = new Harness();
|
||||
const uint chest = 0x70000001u;
|
||||
h.Objects.AddOrUpdate(new ClientObject
|
||||
{
|
||||
ObjectId = chest,
|
||||
Type = ItemType.Container,
|
||||
ItemsCapacity = 24,
|
||||
Useability = ItemUseability.Remote,
|
||||
});
|
||||
|
||||
Assert.True(h.Controller.ActivateItem(chest));
|
||||
|
||||
Assert.Equal(new uint[] { chest }, h.Uses);
|
||||
Assert.Equal(new uint[] { chest }, h.ExternalRequests);
|
||||
Assert.Equal(chest, h.GroundObject);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropOwnedItemOnOpenExternalContainer_SendsPutWithoutOptimisticMove()
|
||||
{
|
||||
var h = new Harness();
|
||||
const uint itemId = 0x50000A33u;
|
||||
const uint chest = 0x70000001u;
|
||||
ClientObject item = h.AddContained(itemId);
|
||||
h.Objects.AddOrUpdate(new ClientObject
|
||||
{
|
||||
ObjectId = chest,
|
||||
Type = ItemType.Container,
|
||||
ItemsCapacity = 24,
|
||||
PublicWeenieBitfield = (uint)PublicWeenieFlags.Openable,
|
||||
});
|
||||
h.GroundObject = chest;
|
||||
var cell = new UiItemSlot();
|
||||
cell.SetItem(itemId, 0u);
|
||||
var payload = new ItemDragPayload(itemId, ItemDragSource.Inventory, 0, cell);
|
||||
|
||||
Assert.True(h.Controller.PlaceIn3D(payload, chest));
|
||||
|
||||
Assert.Equal(new[] { (itemId, chest, 0) }, h.Puts);
|
||||
Assert.Equal(Pack, item.ContainerId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PrimaryClickRouter_distinguishesInactiveSuccessAndConsumedRejection()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,203 @@
|
|||
using AcDream.App.UI;
|
||||
using AcDream.App.UI.Layout;
|
||||
using AcDream.App.World;
|
||||
using AcDream.Core.Items;
|
||||
using AcDream.Core.Selection;
|
||||
|
||||
namespace AcDream.App.Tests.UI.Layout;
|
||||
|
||||
public sealed class ExternalContainerControllerTests
|
||||
{
|
||||
private const uint Player = 0x50000001u;
|
||||
private const uint Chest = 0x70000001u;
|
||||
private const uint Item = 0x60000001u;
|
||||
|
||||
private sealed class TestElement : UiElement { }
|
||||
|
||||
private sealed class Harness : IDisposable
|
||||
{
|
||||
public readonly ExternalContainerState State = new();
|
||||
public readonly ClientObjectTable Objects = new();
|
||||
public readonly SelectionState Selection = new();
|
||||
public readonly StackSplitQuantityState Split = new();
|
||||
public readonly List<uint> Uses = new();
|
||||
public readonly List<uint> NoLonger = new();
|
||||
public readonly List<uint> Pickups = new();
|
||||
public readonly List<(uint Item, uint Container, int Placement)> Puts = new();
|
||||
public readonly List<(uint Item, uint Container, uint Placement, uint Amount)> Splits = new();
|
||||
public readonly UiRoot Screen = new() { Width = 800f, Height = 600f };
|
||||
public readonly UiItemList Top = new() { Width = 36f, Height = 36f };
|
||||
public readonly UiItemList Containers = new() { Width = 680f, Height = 36f };
|
||||
public readonly UiItemList Contents = new() { Width = 784f, Height = 32f };
|
||||
public readonly RetailWindowHandle Window;
|
||||
public readonly ItemInteractionController Interaction;
|
||||
public readonly ExternalContainerLifecycleController Lifecycle;
|
||||
public readonly ExternalContainerController Controller;
|
||||
public bool InRange = true;
|
||||
|
||||
public Harness()
|
||||
{
|
||||
var root = new TestElement { Width = 800f, Height = 110f };
|
||||
var close = new UiButton(
|
||||
new ElementInfo { Id = ExternalContainerController.CloseButtonId, Type = 1 },
|
||||
static _ => (0u, 0, 0));
|
||||
var scrollbar = new UiScrollbar { Width = 784f, Height = 16f };
|
||||
root.AddChild(Top);
|
||||
root.AddChild(Containers);
|
||||
root.AddChild(Contents);
|
||||
root.AddChild(close);
|
||||
root.AddChild(scrollbar);
|
||||
var layout = new ImportedLayout(root, new Dictionary<uint, UiElement>
|
||||
{
|
||||
[ExternalContainerController.TopContainerId] = Top,
|
||||
[ExternalContainerController.ContainerListId] = Containers,
|
||||
[ExternalContainerController.ContentsListId] = Contents,
|
||||
[ExternalContainerController.ContentsScrollbarId] = scrollbar,
|
||||
[ExternalContainerController.CloseButtonId] = close,
|
||||
});
|
||||
|
||||
Window = RetailWindowFrame.Mount(
|
||||
Screen,
|
||||
root,
|
||||
static _ => (0u, 0, 0),
|
||||
new RetailWindowFrame.Options
|
||||
{
|
||||
WindowName = WindowNames.ExternalContainer,
|
||||
Chrome = RetailWindowChrome.Imported,
|
||||
Visible = false,
|
||||
Draggable = false,
|
||||
Resizable = false,
|
||||
});
|
||||
|
||||
Interaction = new ItemInteractionController(
|
||||
Objects,
|
||||
playerGuid: () => Player,
|
||||
sendUse: Uses.Add,
|
||||
sendUseWithTarget: null,
|
||||
sendWield: null,
|
||||
sendDrop: null,
|
||||
groundObjectId: () => State.CurrentContainerId,
|
||||
placeInBackpack: Pickups.Add,
|
||||
sendPutItemInContainer: (item, container, placement) =>
|
||||
Puts.Add((item, container, placement)),
|
||||
sendSplitToContainer: (item, container, placement, amount) =>
|
||||
Splits.Add((item, container, placement, amount)),
|
||||
requestExternalContainer: id => State.RequestOpen(id));
|
||||
Lifecycle = new ExternalContainerLifecycleController(State, Objects, NoLonger.Add);
|
||||
Controller = ExternalContainerController.Bind(
|
||||
layout,
|
||||
State,
|
||||
Objects,
|
||||
Selection,
|
||||
Interaction,
|
||||
Split,
|
||||
static (_, _, _, _, _) => 0u,
|
||||
static (_, _, _, _, _) => 0u,
|
||||
Uses.Add,
|
||||
(item, container, placement) => Puts.Add((item, container, placement)),
|
||||
(item, container, placement, amount) =>
|
||||
Splits.Add((item, container, placement, amount)),
|
||||
_ => InRange,
|
||||
Window);
|
||||
Screen.WindowManager.AttachController(WindowNames.ExternalContainer, Controller);
|
||||
}
|
||||
|
||||
public void Open(uint id = Chest, params ContainerContentEntry[] contents)
|
||||
{
|
||||
Objects.AddOrUpdate(new ClientObject
|
||||
{
|
||||
ObjectId = id,
|
||||
Type = ItemType.Container,
|
||||
ItemsCapacity = 24,
|
||||
Useability = ItemUseability.Remote,
|
||||
});
|
||||
State.RequestOpen(id);
|
||||
Objects.ReplaceContents(id, contents);
|
||||
State.ApplyViewContents(id);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Lifecycle.Dispose();
|
||||
Interaction.Dispose();
|
||||
Screen.WindowManager.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AuthoritativeView_ShowsAuthoredStripAndPopulatesLists()
|
||||
{
|
||||
using var h = new Harness();
|
||||
h.Open(Chest, new ContainerContentEntry(Item, 0u));
|
||||
|
||||
Assert.True(h.Window.IsVisible);
|
||||
Assert.Equal(Chest, h.Top.GetItem(0)!.ItemId);
|
||||
Assert.Equal(Item, h.Contents.GetItem(0)!.ItemId);
|
||||
Assert.True(h.Contents.HorizontalScroll);
|
||||
Assert.Same(h.Contents.Scroll,
|
||||
Assert.IsType<UiScrollbar>(h.Window.ContentRoot.Children.Last()).Model);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoubleClickLoot_UsesSharedItemPolicyAndPicksUpItem()
|
||||
{
|
||||
using var h = new Harness();
|
||||
h.Open(Chest, new ContainerContentEntry(Item, 0u));
|
||||
|
||||
h.Contents.GetItem(0)!.DoubleClicked!();
|
||||
|
||||
Assert.Equal(new uint[] { Item }, h.Pickups);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CloseAndRangeExit_SendUseOnceWithoutNoLongerViewing()
|
||||
{
|
||||
using var h = new Harness();
|
||||
h.Open();
|
||||
h.InRange = false;
|
||||
|
||||
h.Controller.Tick();
|
||||
h.Controller.Tick();
|
||||
|
||||
Assert.Equal(new uint[] { Chest }, h.Uses);
|
||||
Assert.Empty(h.NoLonger);
|
||||
Assert.False(h.Window.IsVisible);
|
||||
|
||||
h.State.ApplyClose(Chest);
|
||||
Assert.Empty(h.NoLonger);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Replacement_SendsNoLongerViewingForPreviousRootExactlyOnce()
|
||||
{
|
||||
using var h = new Harness();
|
||||
h.Open(Chest);
|
||||
h.Open(0x70000002u);
|
||||
|
||||
Assert.Equal(new uint[] { Chest }, h.NoLonger);
|
||||
Assert.Equal(0x70000002u, h.State.CurrentContainerId);
|
||||
Assert.True(h.Window.IsVisible);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropOwnedPartialStackIntoChest_UsesSplitRequestWithoutLocalMove()
|
||||
{
|
||||
using var h = new Harness();
|
||||
h.Open();
|
||||
var item = new ClientObject { ObjectId = Item, StackSize = 5, Type = ItemType.Misc };
|
||||
h.Objects.AddOrUpdate(item);
|
||||
h.Objects.MoveItem(Item, Player, 0);
|
||||
h.Selection.Select(Item, SelectionChangeSource.Inventory);
|
||||
h.Split.Reset(5u, 2u);
|
||||
var source = new UiItemSlot { SourceKind = ItemDragSource.Inventory };
|
||||
source.SetItem(Item, 0u);
|
||||
var payload = new ItemDragPayload(Item, ItemDragSource.Inventory, 0, source);
|
||||
UiItemSlot target = h.Contents.GetItem(0)!;
|
||||
|
||||
h.Controller.HandleDropRelease(h.Contents, target, payload);
|
||||
|
||||
Assert.Equal(new[] { (Item, Chest, 0u, 2u) }, h.Splits);
|
||||
Assert.Equal(Player, h.Objects.Get(Item)!.ContainerId);
|
||||
Assert.Empty(h.Puts);
|
||||
}
|
||||
}
|
||||
|
|
@ -54,7 +54,7 @@ public class InventoryControllerTests
|
|||
}
|
||||
|
||||
private static InventoryController Bind(ImportedLayout layout, ClientObjectTable objects,
|
||||
int? strength = 100, List<uint>? uses = null, List<uint>? closes = null,
|
||||
int? strength = 100, List<uint>? uses = null,
|
||||
List<(uint item, uint container, int placement)>? puts = null,
|
||||
List<(uint item, uint container, uint placement, uint amount)>? splits = null,
|
||||
List<(uint source, uint target, uint amount)>? merges = null,
|
||||
|
|
@ -68,7 +68,6 @@ public class InventoryControllerTests
|
|||
strength: () => strength, datFont: null,
|
||||
ownerName: ownerName is null ? null : () => ownerName,
|
||||
sendUse: uses is null ? null : g => uses.Add(g),
|
||||
sendNoLongerViewing: closes is null ? null : g => closes.Add(g),
|
||||
sendPutItemInContainer: puts is null ? null : (i, c, p) => puts.Add((i, c, p)),
|
||||
sendStackableSplitToContainer: splits is null
|
||||
? null
|
||||
|
|
@ -410,7 +409,7 @@ public class InventoryControllerTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void ClickMainPackCell_afterBag_closesBag_returnsToMainPack()
|
||||
public void ClickMainPackCell_afterBag_returnsToMainPack_withoutExternalCloseWire()
|
||||
{
|
||||
var (layout, grid, containers, top, _, _, _, _) = BuildLayout();
|
||||
var objects = new ClientObjectTable();
|
||||
|
|
@ -418,34 +417,30 @@ public class InventoryControllerTests
|
|||
SeedBag(objects, 0xC, slot: 1);
|
||||
SeedContained(objects, 0xB1, 0xC, slot: 0);
|
||||
var uses = new List<uint>();
|
||||
var closes = new List<uint>();
|
||||
var ctrl = Bind(layout, objects, uses: uses, closes: closes);
|
||||
var ctrl = Bind(layout, objects, uses: uses);
|
||||
|
||||
containers.GetItem(0)!.Clicked!(); // open the bag
|
||||
top.GetItem(0)!.Clicked!(); // click the main-pack cell
|
||||
|
||||
Assert.Contains(0xCu, closes); // NoLongerViewingContents(bag) sent
|
||||
Assert.DoesNotContain(Player, uses); // no Use for the main pack
|
||||
Assert.Equal(0xAu, grid.GetItem(0)!.ItemId); // grid back to the main pack
|
||||
Assert.Equal(102, grid.GetNumUIItems());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SwitchBetweenTwoBags_closesPrevious_opensNext()
|
||||
public void SwitchBetweenTwoBags_opensEach_withoutExternalCloseWire()
|
||||
{
|
||||
var (layout, _, containers, _, _, _, _, _) = BuildLayout();
|
||||
var objects = new ClientObjectTable();
|
||||
SeedBag(objects, 0xC1, slot: 0);
|
||||
SeedBag(objects, 0xC2, slot: 1);
|
||||
var uses = new List<uint>();
|
||||
var closes = new List<uint>();
|
||||
Bind(layout, objects, uses: uses, closes: closes);
|
||||
Bind(layout, objects, uses: uses);
|
||||
|
||||
containers.GetItem(0)!.Clicked!(); // open bag A (column index 0 → 0xC1)
|
||||
containers.GetItem(1)!.Clicked!(); // open bag B (column index 1 → 0xC2)
|
||||
|
||||
Assert.Equal(new[] { 0xC1u, 0xC2u }, uses.ToArray()); // Use(A) then Use(B)
|
||||
Assert.Equal(new[] { 0xC1u }, closes.ToArray()); // close A when switching to B (not the main pack)
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -469,15 +464,13 @@ public class InventoryControllerTests
|
|||
var objects = new ClientObjectTable();
|
||||
SeedContained(objects, 0xA, Player, slot: 0);
|
||||
var uses = new List<uint>();
|
||||
var closes = new List<uint>();
|
||||
Bind(layout, objects, uses: uses, closes: closes);
|
||||
Bind(layout, objects, uses: uses);
|
||||
|
||||
grid.GetItem(0)!.Clicked!(); // select the loose item
|
||||
|
||||
Assert.True(grid.GetItem(0)!.Selected); // square on the selected grid item
|
||||
Assert.True(top.GetItem(0)!.IsOpenContainer); // main pack still the open container (unchanged)
|
||||
Assert.Empty(uses); // selection sends no wire
|
||||
Assert.Empty(closes);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -114,4 +114,36 @@ public class UiItemListTests
|
|||
list.ScrollItemIntoView(0);
|
||||
Assert.Equal(0, list.Scroll.ScrollY);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HorizontalScroll_UsesPixelOffsetAndKeepsPartialEdgeCells()
|
||||
{
|
||||
var list = new UiItemList
|
||||
{
|
||||
Width = 80f,
|
||||
Height = 32f,
|
||||
CellWidth = 32f,
|
||||
CellHeight = 32f,
|
||||
SingleRow = true,
|
||||
HorizontalScroll = true,
|
||||
};
|
||||
list.Flush();
|
||||
for (int i = 0; i < 4; i++) list.AddItem(new UiItemSlot());
|
||||
|
||||
list.Scroll.SetScrollY(17);
|
||||
list.LayoutCells();
|
||||
|
||||
Assert.Equal(128, list.Scroll.ContentHeight);
|
||||
Assert.Equal(80, list.Scroll.ViewHeight);
|
||||
Assert.Equal(-17f, list.GetItem(0)!.Left);
|
||||
Assert.True(list.GetItem(0)!.Visible);
|
||||
Assert.True(list.GetItem(3)!.Visible);
|
||||
Assert.False(list.GetItem(2) is null);
|
||||
|
||||
list.ScrollItemIntoView(3);
|
||||
Assert.Equal(48, list.Scroll.ScrollY);
|
||||
list.LayoutCells();
|
||||
Assert.Equal(48f, list.GetItem(3)!.Left);
|
||||
Assert.Equal(80f, list.GetItem(3)!.Left + list.GetItem(3)!.Width);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,6 +103,31 @@ public class UiScrollbarTests
|
|||
Assert.True(bar.OnEvent(new UiEvent(0u, bar, UiEventType.MouseUp, Data1: 45)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HorizontalModel_ButtonsTrackAndThumbDriveSharedScroll()
|
||||
{
|
||||
var model = new UiScrollable { ContentHeight = 320, ViewHeight = 80, LineHeight = 32 };
|
||||
var bar = new UiScrollbar
|
||||
{
|
||||
Width = 160f,
|
||||
Height = 16f,
|
||||
Horizontal = true,
|
||||
Model = model,
|
||||
};
|
||||
|
||||
Assert.True(bar.OnEvent(new UiEvent(0u, bar, UiEventType.MouseDown, Data1: 159)));
|
||||
Assert.Equal(32, model.ScrollY);
|
||||
|
||||
Assert.True(bar.OnEvent(new UiEvent(0u, bar, UiEventType.MouseDown, Data1: 100)));
|
||||
Assert.True(model.ScrollY >= 80);
|
||||
|
||||
model.SetScrollY(0);
|
||||
Assert.True(bar.OnEvent(new UiEvent(0u, bar, UiEventType.MouseDown, Data1: 20)));
|
||||
Assert.True(bar.OnEvent(new UiEvent(0u, bar, UiEventType.MouseMove, Data1: 144)));
|
||||
Assert.Equal(model.MaxScroll, model.ScrollY);
|
||||
Assert.True(bar.OnEvent(new UiEvent(0u, bar, UiEventType.MouseUp, Data1: 144)));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0f, 0f, 0f)]
|
||||
[InlineData(0.5f, 0f, 50f)]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
using AcDream.App.World;
|
||||
using AcDream.Core.Items;
|
||||
|
||||
namespace AcDream.App.Tests.World;
|
||||
|
||||
public sealed class ExternalContainerLifecycleControllerTests
|
||||
{
|
||||
[Fact]
|
||||
public void Replacement_RetiresPreviousRootExactlyOnce()
|
||||
{
|
||||
var state = new ExternalContainerState();
|
||||
var objects = new ClientObjectTable();
|
||||
var retired = new List<uint>();
|
||||
using var controller = new ExternalContainerLifecycleController(state, objects, retired.Add);
|
||||
objects.AddOrUpdate(new ClientObject { ObjectId = 100u });
|
||||
objects.ReplaceContents(10u, new[] { new ContainerContentEntry(100u, 0u) });
|
||||
|
||||
state.RequestOpen(10u);
|
||||
state.ApplyViewContents(10u);
|
||||
state.RequestOpen(20u);
|
||||
state.ApplyViewContents(20u);
|
||||
state.ApplyViewContents(20u);
|
||||
|
||||
Assert.Equal(new uint[] { 10u }, retired);
|
||||
Assert.Empty(objects.GetContents(10u));
|
||||
Assert.NotNull(objects.Get(100u));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AuthoredCloseAndReset_DoNotSendNoLongerViewing()
|
||||
{
|
||||
var state = new ExternalContainerState();
|
||||
var objects = new ClientObjectTable();
|
||||
var retired = new List<uint>();
|
||||
using var controller = new ExternalContainerLifecycleController(state, objects, retired.Add);
|
||||
|
||||
state.RequestOpen(10u);
|
||||
state.ApplyViewContents(10u);
|
||||
objects.ReplaceContents(10u, new[] { new ContainerContentEntry(100u, 0u) });
|
||||
state.ApplyClose(10u);
|
||||
Assert.Empty(objects.GetContents(10u));
|
||||
state.RequestOpen(20u);
|
||||
state.ApplyViewContents(20u);
|
||||
objects.ReplaceContents(20u, new[] { new ContainerContentEntry(200u, 0u) });
|
||||
state.Reset();
|
||||
|
||||
Assert.Empty(retired);
|
||||
Assert.Empty(objects.GetContents(20u));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue