acdream/tests/AcDream.Core.Net.Tests/WorldSessionWiringOwnershipTests.cs

131 lines
4.8 KiB
C#

using System.Net;
using System.Reflection;
using AcDream.Core.Chat;
using AcDream.Core.Combat;
using AcDream.Core.Items;
using AcDream.Core.Net.Messages;
using AcDream.Core.Player;
using AcDream.Core.Spells;
namespace AcDream.Core.Net.Tests;
public sealed class WorldSessionWiringOwnershipTests
{
[Fact]
public void ObjectTableWiring_DisposeDetachesEveryExactSessionDelegate()
{
using var session = NewSession();
var table = new ClientObjectTable();
var player = new LocalPlayerState();
IDisposable registration = ObjectTableWiring.Wire(
session,
table,
playerGuid: () => 0x50000001u,
localPlayer: player);
Assert.Equal(1, HandlerCount(session, nameof(session.ObjectIntPropertyUpdated)));
Assert.Equal(1, HandlerCount(session, nameof(session.PlayerIntPropertyUpdated)));
Assert.Equal(1, HandlerCount(session, nameof(session.PlayerInt64PropertyUpdated)));
Assert.Equal(1, HandlerCount(session, nameof(session.StackSizeUpdated)));
Assert.Equal(1, HandlerCount(session, nameof(session.InventoryObjectRemoved)));
registration.Dispose();
registration.Dispose();
Assert.Equal(0, HandlerCount(session, nameof(session.ObjectIntPropertyUpdated)));
Assert.Equal(0, HandlerCount(session, nameof(session.PlayerIntPropertyUpdated)));
Assert.Equal(0, HandlerCount(session, nameof(session.PlayerInt64PropertyUpdated)));
Assert.Equal(0, HandlerCount(session, nameof(session.StackSizeUpdated)));
Assert.Equal(0, HandlerCount(session, nameof(session.InventoryObjectRemoved)));
}
[Fact]
public void CombatStateWiring_DisposeDetachesOnlyItsExactDelegate()
{
using var session = NewSession();
var combat = new CombatState();
Action<WorldSession.PlayerIntPropertyUpdate> predecessor = _ => { };
session.PlayerIntPropertyUpdated += predecessor;
IDisposable registration = CombatStateWiring.Wire(session, combat);
Assert.Equal(2, HandlerCount(session, nameof(session.PlayerIntPropertyUpdated)));
registration.Dispose();
registration.Dispose();
Assert.Equal(1, HandlerCount(session, nameof(session.PlayerIntPropertyUpdated)));
session.PlayerIntPropertyUpdated -= predecessor;
}
[Fact]
public void CombatStateWiring_ConcurrentDisposeDetachesExactlyOnce()
{
using var session = NewSession();
IDisposable registration = CombatStateWiring.Wire(session, new CombatState());
Parallel.For(0, 64, _ => registration.Dispose());
Assert.Equal(0, HandlerCount(session, nameof(session.PlayerIntPropertyUpdated)));
}
[Fact]
public void GameEventWiring_DisposeRestoresPreexistingHandlerAndRemovesOwnedSet()
{
var dispatcher = new GameEventDispatcher();
int predecessorCalls = 0;
dispatcher.Register(GameEventType.Tell, _ => predecessorCalls++);
IDisposable registration = GameEventWiring.WireAll(
dispatcher,
new ClientObjectTable(),
new CombatState(),
new Spellbook(),
new ChatLog());
int wiredCount = dispatcher.RegisteredHandlerCount;
dispatcher.Dispatch(new GameEventEnvelope(0, 0, GameEventType.Tell, default));
Assert.Equal(0, predecessorCalls);
registration.Dispose();
registration.Dispose();
dispatcher.Dispatch(new GameEventEnvelope(0, 0, GameEventType.Tell, default));
Assert.True(wiredCount > 1);
Assert.Equal(1, dispatcher.RegisteredHandlerCount);
Assert.Equal(1, predecessorCalls);
}
[Fact]
public void GameEventWiring_OnWorldSession_RestoresInternalRegistrations()
{
using var session = NewSession();
int baselineCount = session.GameEvents.RegisteredHandlerCount;
IDisposable registration = GameEventWiring.WireAll(
session.GameEvents,
new ClientObjectTable(),
new CombatState(),
new Spellbook(),
new ChatLog(),
turbineChat: new TurbineChatState());
Assert.True(session.GameEvents.RegisteredHandlerCount > baselineCount);
registration.Dispose();
Assert.Equal(baselineCount, session.GameEvents.RegisteredHandlerCount);
}
private static WorldSession NewSession() =>
new(new IPEndPoint(IPAddress.Loopback, 9));
private static int HandlerCount(WorldSession session, string eventName)
{
FieldInfo field = typeof(WorldSession).GetField(
eventName,
BindingFlags.Instance | BindingFlags.NonPublic)
?? throw new InvalidOperationException($"event field {eventName} not found");
return (field.GetValue(session) as MulticastDelegate)?
.GetInvocationList()
.Length ?? 0;
}
}