Files
2026-07-10 10:24:29 +08:00

111 lines
4.0 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)出 Rockopcode=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); // 第一大 tickTotalElapsed=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"
}
}
}