TestPair.cs 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. #nullable enable
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using Content.Server.GameTicking;
  6. using Content.Shared.Players;
  7. using Robust.Shared.Configuration;
  8. using Robust.Shared.GameObjects;
  9. using Robust.Shared.IoC;
  10. using Robust.Shared.Network;
  11. using Robust.Shared.Player;
  12. using Robust.Shared.Random;
  13. using Robust.Shared.Timing;
  14. using Robust.UnitTesting;
  15. namespace Content.IntegrationTests.Pair;
  16. /// <summary>
  17. /// This object wraps a pooled server+client pair.
  18. /// </summary>
  19. public sealed partial class TestPair
  20. {
  21. public readonly int Id;
  22. private bool _initialized;
  23. private TextWriter _testOut = default!;
  24. public readonly Stopwatch Watch = new();
  25. public readonly List<string> TestHistory = new();
  26. public PoolSettings Settings = default!;
  27. public TestMapData? TestMap;
  28. private List<NetUserId> _modifiedProfiles = new();
  29. private int _nextServerSeed;
  30. private int _nextClientSeed;
  31. public int ServerSeed;
  32. public int ClientSeed;
  33. public RobustIntegrationTest.ServerIntegrationInstance Server { get; private set; } = default!;
  34. public RobustIntegrationTest.ClientIntegrationInstance Client { get; private set; } = default!;
  35. public void Deconstruct(
  36. out RobustIntegrationTest.ServerIntegrationInstance server,
  37. out RobustIntegrationTest.ClientIntegrationInstance client)
  38. {
  39. server = Server;
  40. client = Client;
  41. }
  42. public ICommonSession? Player => Server.PlayerMan.SessionsDict.GetValueOrDefault(Client.User!.Value);
  43. public ContentPlayerData? PlayerData => Player?.Data.ContentData();
  44. public PoolTestLogHandler ServerLogHandler { get; private set; } = default!;
  45. public PoolTestLogHandler ClientLogHandler { get; private set; } = default!;
  46. public TestPair(int id)
  47. {
  48. Id = id;
  49. }
  50. public async Task Initialize(PoolSettings settings, TextWriter testOut, List<string> testPrototypes)
  51. {
  52. if (_initialized)
  53. throw new InvalidOperationException("Already initialized");
  54. _initialized = true;
  55. Settings = settings;
  56. (Client, ClientLogHandler) = await PoolManager.GenerateClient(settings, testOut);
  57. (Server, ServerLogHandler) = await PoolManager.GenerateServer(settings, testOut);
  58. ActivateContext(testOut);
  59. Client.CfgMan.OnCVarValueChanged += OnClientCvarChanged;
  60. Server.CfgMan.OnCVarValueChanged += OnServerCvarChanged;
  61. if (!settings.NoLoadTestPrototypes)
  62. await LoadPrototypes(testPrototypes!);
  63. if (!settings.UseDummyTicker)
  64. {
  65. var gameTicker = Server.ResolveDependency<IEntityManager>().System<GameTicker>();
  66. await Server.WaitPost(() => gameTicker.RestartRound());
  67. }
  68. // Always initially connect clients to generate an initial random set of preferences/profiles.
  69. // This is to try and prevent issues where if the first test that connects the client is consistently some test
  70. // that uses a fixed seed, it would effectively prevent it from beingrandomized.
  71. Client.SetConnectTarget(Server);
  72. await Client.WaitIdleAsync();
  73. var netMgr = Client.ResolveDependency<IClientNetManager>();
  74. await Client.WaitPost(() => netMgr.ClientConnect(null!, 0, null!));
  75. await ReallyBeIdle(10);
  76. await Client.WaitRunTicks(1);
  77. if (!settings.ShouldBeConnected)
  78. {
  79. await Client.WaitPost(() => netMgr.ClientDisconnect("Initial disconnect"));
  80. await ReallyBeIdle(10);
  81. }
  82. var cRand = Client.ResolveDependency<IRobustRandom>();
  83. var sRand = Server.ResolveDependency<IRobustRandom>();
  84. _nextClientSeed = cRand.Next();
  85. _nextServerSeed = sRand.Next();
  86. }
  87. public void Kill()
  88. {
  89. State = PairState.Dead;
  90. ServerLogHandler.ShuttingDown = true;
  91. ClientLogHandler.ShuttingDown = true;
  92. Server.Dispose();
  93. Client.Dispose();
  94. }
  95. private void ClearContext()
  96. {
  97. _testOut = default!;
  98. ServerLogHandler.ClearContext();
  99. ClientLogHandler.ClearContext();
  100. }
  101. public void ActivateContext(TextWriter testOut)
  102. {
  103. _testOut = testOut;
  104. ServerLogHandler.ActivateContext(testOut);
  105. ClientLogHandler.ActivateContext(testOut);
  106. }
  107. public void Use()
  108. {
  109. if (State != PairState.Ready)
  110. throw new InvalidOperationException($"Pair is not ready to use. State: {State}");
  111. State = PairState.InUse;
  112. }
  113. public enum PairState : byte
  114. {
  115. Ready = 0,
  116. InUse = 1,
  117. CleanDisposed = 2,
  118. Dead = 3,
  119. }
  120. public void SetupSeed()
  121. {
  122. var sRand = Server.ResolveDependency<IRobustRandom>();
  123. if (Settings.ServerSeed is { } severSeed)
  124. {
  125. ServerSeed = severSeed;
  126. sRand.SetSeed(ServerSeed);
  127. }
  128. else
  129. {
  130. ServerSeed = _nextServerSeed;
  131. sRand.SetSeed(ServerSeed);
  132. _nextServerSeed = sRand.Next();
  133. }
  134. var cRand = Client.ResolveDependency<IRobustRandom>();
  135. if (Settings.ClientSeed is { } clientSeed)
  136. {
  137. ClientSeed = clientSeed;
  138. cRand.SetSeed(ClientSeed);
  139. }
  140. else
  141. {
  142. ClientSeed = _nextClientSeed;
  143. cRand.SetSeed(ClientSeed);
  144. _nextClientSeed = cRand.Next();
  145. }
  146. }
  147. }