111 lines
4.0 KiB
C#
111 lines
4.0 KiB
C#
using System.Collections.Generic;
|
||
using Xunit;
|
||
using XWorld.Framework;
|
||
using XWorld.Framework.Protocol;
|
||
using XWorld.Server.Host;
|
||
using RPS.Core;
|
||
|
||
namespace XWorld.Server.Host.Tests
|
||
{
|
||
public class RoomHostTests
|
||
{
|
||
private sealed class Capture : IRoomOutput
|
||
{
|
||
public List<NetMessage> Broadcasts = new List<NetMessage>();
|
||
public void Broadcast(NetMessage m) => Broadcasts.Add(m);
|
||
public void SendTo(int playerId, NetMessage m) { }
|
||
}
|
||
|
||
// 1 真人(seat 0) + 1 AI(seat 1)
|
||
private static readonly IReadOnlyList<PlayerInfo> Players = new[]
|
||
{
|
||
new PlayerInfo { PlayerId = 1, Name = "H", IsAI = false },
|
||
new PlayerInfo { PlayerId = -1, Name = "AI", IsAI = true },
|
||
};
|
||
|
||
private static RpsState LastState(Capture cap)
|
||
=> new RpsLogic().Decode(cap.Broadcasts[cap.Broadcasts.Count - 1].Payload);
|
||
|
||
private static RoomHost NewHost()
|
||
=> new RoomHost(new GameModuleRegistry(new GameModuleLoader(), TestGames.Root), new ConsoleLogger());
|
||
|
||
private static RoomConfig Cfg()
|
||
=> new RoomConfig { GameId = "rps", Version = 1, Seed = 1, PlayerCount = 2 };
|
||
|
||
[Fact]
|
||
public void CreateRoom_TickToEnd_ReleasesModule()
|
||
{
|
||
var host = NewHost();
|
||
var cap = new Capture();
|
||
|
||
var room = host.CreateRoom("A", "rps", 1, Cfg(), Players, cap);
|
||
Assert.Equal(1, host.ActiveRoomCount);
|
||
|
||
// 大 dt 推进:60s 对局在数个大 tick 内结束
|
||
for (int i = 0; i < 10 && host.ActiveRoomCount > 0; i++) host.TickAll(10f);
|
||
|
||
Assert.Equal(0, host.ActiveRoomCount);
|
||
Assert.False(room.IsActive);
|
||
Assert.Equal(RpsPhase.Finished, LastState(cap).Phase);
|
||
}
|
||
|
||
[Fact]
|
||
public void Deliver_RoutesChoice_AffectsState()
|
||
{
|
||
var host = NewHost();
|
||
var cap = new Capture();
|
||
host.CreateRoom("A", "rps", 1, Cfg(), Players, cap);
|
||
|
||
// seat 0(玩家 1)出 Rock:opcode=1(ChoiceOpcode),payload=单字节 choice
|
||
var w = new PacketWriter();
|
||
w.WriteByte((byte)Choice.Rock);
|
||
host.DeliverTo("A", 1, new NetMessage(1, w.ToArray()));
|
||
host.TickAll(0.1f); // 小 dt,仍在 Choosing,广播快照
|
||
|
||
Assert.Equal(Choice.Rock, LastState(cap).Choices[0]);
|
||
}
|
||
|
||
[Fact]
|
||
public void CreateRoom_DuplicateRoomId_Throws()
|
||
{
|
||
var host = NewHost();
|
||
host.CreateRoom("dup", "rps", 1, Cfg(), Players, new Capture());
|
||
Assert.Throws<System.InvalidOperationException>(
|
||
() => host.CreateRoom("dup", "rps", 1, Cfg(), Players, new Capture()));
|
||
}
|
||
|
||
[Fact]
|
||
public void TickAll_ReturnsEndedRoomIds()
|
||
{
|
||
var host = NewHost();
|
||
host.CreateRoom("E", "rps", 1, Cfg(), Players, new Capture());
|
||
|
||
var first = host.TickAll(10f); // 第一大 tick:TotalElapsed=10 < 60,不结束
|
||
Assert.Empty(first);
|
||
|
||
var ended = new List<EndedRoom>();
|
||
for (int i = 0; i < 10 && !ended.Exists(e => e.RoomId == "E"); i++)
|
||
ended.AddRange(host.TickAll(10f));
|
||
Assert.Contains(ended, e => e.RoomId == "E"); // 累计超过 60s 后该 tick 返回结束房间
|
||
}
|
||
|
||
[Fact]
|
||
public void TickAll_EndedRoom_CarriesResult()
|
||
{
|
||
var host = NewHost();
|
||
host.CreateRoom("R", "rps", 1, Cfg(), Players, new Capture());
|
||
|
||
RoomEndResult result = null;
|
||
bool found = false;
|
||
for (int i = 0; i < 10 && !found; i++)
|
||
foreach (var e in host.TickAll(10f))
|
||
if (e.RoomId == "R") { found = true; result = e.Result; }
|
||
|
||
Assert.True(found);
|
||
Assert.NotNull(result); // 夹具已重建,RPS 走 EndRoom(result)
|
||
Assert.NotNull(result.ResultBlob);
|
||
Assert.NotEmpty(result.ResultBlob); // "最终比分 x : y" 或 "平局 x : y"
|
||
}
|
||
}
|
||
}
|