Add random seed options to tests (#30735)

* Add random seed options to tests

* Ensure profile randomization
This commit is contained in:
Leon Friedrich
2024-08-09 17:25:43 +12:00
committed by GitHub
parent da0b8d4731
commit 8a4ef69e86
3 changed files with 90 additions and 31 deletions

View File

@@ -9,6 +9,7 @@ using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Random;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.UnitTesting; using Robust.UnitTesting;
@@ -28,6 +29,12 @@ public sealed partial class TestPair
public TestMapData? TestMap; public TestMapData? TestMap;
private List<NetUserId> _modifiedProfiles = new(); private List<NetUserId> _modifiedProfiles = new();
private int _nextServerSeed;
private int _nextClientSeed;
public int ServerSeed;
public int ClientSeed;
public RobustIntegrationTest.ServerIntegrationInstance Server { get; private set; } = default!; public RobustIntegrationTest.ServerIntegrationInstance Server { get; private set; } = default!;
public RobustIntegrationTest.ClientIntegrationInstance Client { get; private set; } = default!; public RobustIntegrationTest.ClientIntegrationInstance Client { get; private set; } = default!;
@@ -74,22 +81,27 @@ public sealed partial class TestPair
await Server.WaitPost(() => gameTicker.RestartRound()); await Server.WaitPost(() => gameTicker.RestartRound());
} }
if (settings.ShouldBeConnected) // Always initially connect clients to generate an initial random set of preferences/profiles.
{ // This is to try and prevent issues where if the first test that connects the client is consistently some test
Client.SetConnectTarget(Server); // that uses a fixed seed, it would effectively prevent it from beingrandomized.
await Client.WaitIdleAsync();
var netMgr = Client.ResolveDependency<IClientNetManager>();
await Client.WaitPost(() => Client.SetConnectTarget(Server);
{ await Client.WaitIdleAsync();
if (!netMgr.IsConnected) var netMgr = Client.ResolveDependency<IClientNetManager>();
{ await Client.WaitPost(() => netMgr.ClientConnect(null!, 0, null!));
netMgr.ClientConnect(null!, 0, null!); await ReallyBeIdle(10);
} await Client.WaitRunTicks(1);
});
if (!settings.ShouldBeConnected)
{
await Client.WaitPost(() => netMgr.ClientDisconnect("Initial disconnect"));
await ReallyBeIdle(10); await ReallyBeIdle(10);
await Client.WaitRunTicks(1);
} }
var cRand = Client.ResolveDependency<IRobustRandom>();
var sRand = Server.ResolveDependency<IRobustRandom>();
_nextClientSeed = cRand.Next();
_nextServerSeed = sRand.Next();
} }
public void Kill() public void Kill()
@@ -129,4 +141,33 @@ public sealed partial class TestPair
CleanDisposed = 2, CleanDisposed = 2,
Dead = 3, Dead = 3,
} }
public void SetupSeed()
{
var sRand = Server.ResolveDependency<IRobustRandom>();
if (Settings.ServerSeed is { } severSeed)
{
ServerSeed = severSeed;
sRand.SetSeed(ServerSeed);
}
else
{
ServerSeed = _nextServerSeed;
sRand.SetSeed(ServerSeed);
_nextServerSeed = sRand.Next();
}
var cRand = Client.ResolveDependency<IRobustRandom>();
if (Settings.ClientSeed is { } clientSeed)
{
ClientSeed = clientSeed;
cRand.SetSeed(ClientSeed);
}
else
{
ClientSeed = _nextClientSeed;
cRand.SetSeed(ClientSeed);
_nextClientSeed = cRand.Next();
}
}
} }

View File

@@ -252,7 +252,7 @@ public static partial class PoolManager
} }
finally finally
{ {
if (pair != null && pair.TestHistory.Count > 1) if (pair != null && pair.TestHistory.Count > 0)
{ {
await testOut.WriteLineAsync($"{nameof(GetServerClientPair)}: Pair {pair.Id} Test History Start"); await testOut.WriteLineAsync($"{nameof(GetServerClientPair)}: Pair {pair.Id} Test History Start");
for (var i = 0; i < pair.TestHistory.Count; i++) for (var i = 0; i < pair.TestHistory.Count; i++)
@@ -268,12 +268,14 @@ public static partial class PoolManager
var poolRetrieveTime = poolRetrieveTimeWatch.Elapsed; var poolRetrieveTime = poolRetrieveTimeWatch.Elapsed;
await testOut.WriteLineAsync( await testOut.WriteLineAsync(
$"{nameof(GetServerClientPair)}: Retrieving pair {pair.Id} from pool took {poolRetrieveTime.TotalMilliseconds} ms"); $"{nameof(GetServerClientPair)}: Retrieving pair {pair.Id} from pool took {poolRetrieveTime.TotalMilliseconds} ms");
await testOut.WriteLineAsync(
$"{nameof(GetServerClientPair)}: Returning pair {pair.Id}");
pair.ClearModifiedCvars(); pair.ClearModifiedCvars();
pair.Settings = poolSettings; pair.Settings = poolSettings;
pair.TestHistory.Add(currentTestName); pair.TestHistory.Add(currentTestName);
pair.SetupSeed();
await testOut.WriteLineAsync(
$"{nameof(GetServerClientPair)}: Returning pair {pair.Id} with client/server seeds: {pair.ClientSeed}/{pair.ServerSeed}");
pair.Watch.Restart(); pair.Watch.Restart();
return pair; return pair;
} }

View File

@@ -1,5 +1,7 @@
#nullable enable #nullable enable
using Robust.Shared.Random;
namespace Content.IntegrationTests; namespace Content.IntegrationTests;
/// <summary> /// <summary>
@@ -9,16 +11,6 @@ namespace Content.IntegrationTests;
/// </summary> /// </summary>
public sealed class PoolSettings public sealed class PoolSettings
{ {
/// <summary>
/// If the returned pair must not be reused
/// </summary>
public bool MustNotBeReused => Destructive || NoLoadContent || NoLoadTestPrototypes;
/// <summary>
/// If the given pair must be brand new
/// </summary>
public bool MustBeNew => Fresh || NoLoadContent || NoLoadTestPrototypes;
/// <summary> /// <summary>
/// Set to true if the test will ruin the server/client pair. /// Set to true if the test will ruin the server/client pair.
/// </summary> /// </summary>
@@ -34,8 +26,6 @@ public sealed class PoolSettings
/// </summary> /// </summary>
public bool DummyTicker { get; init; } = true; public bool DummyTicker { get; init; } = true;
public bool UseDummyTicker => !InLobby && DummyTicker;
/// <summary> /// <summary>
/// If true, this enables the creation of admin logs during the test. /// If true, this enables the creation of admin logs during the test.
/// </summary> /// </summary>
@@ -48,8 +38,6 @@ public sealed class PoolSettings
/// </summary> /// </summary>
public bool Connected { get; init; } public bool Connected { get; init; }
public bool ShouldBeConnected => InLobby || Connected;
/// <summary> /// <summary>
/// Set to true if the given server/client pair should be in the lobby. /// Set to true if the given server/client pair should be in the lobby.
/// If the pair is not in the lobby at the end of the test, this test must be marked as dirty. /// If the pair is not in the lobby at the end of the test, this test must be marked as dirty.
@@ -92,6 +80,34 @@ public sealed class PoolSettings
/// </summary> /// </summary>
public string? TestName { get; set; } public string? TestName { get; set; }
/// <summary>
/// If set, this will be used to call <see cref="IRobustRandom.SetSeed"/>
/// </summary>
public int? ServerSeed { get; set; }
/// <summary>
/// If set, this will be used to call <see cref="IRobustRandom.SetSeed"/>
/// </summary>
public int? ClientSeed { get; set; }
#region Inferred Properties
/// <summary>
/// If the returned pair must not be reused
/// </summary>
public bool MustNotBeReused => Destructive || NoLoadContent || NoLoadTestPrototypes;
/// <summary>
/// If the given pair must be brand new
/// </summary>
public bool MustBeNew => Fresh || NoLoadContent || NoLoadTestPrototypes;
public bool UseDummyTicker => !InLobby && DummyTicker;
public bool ShouldBeConnected => InLobby || Connected;
#endregion
/// <summary> /// <summary>
/// Tries to guess if we can skip recycling the server/client pair. /// Tries to guess if we can skip recycling the server/client pair.
/// </summary> /// </summary>