Added more docs to integration pool manager (#10632)
This commit is contained in:
@@ -12,12 +12,9 @@ using Content.IntegrationTests.Tests.Interaction.Click;
|
||||
using Content.IntegrationTests.Tests.Networking;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Maps;
|
||||
using NUnit.Framework;
|
||||
using Robust.Client;
|
||||
using Robust.Server;
|
||||
using Robust.Server.GameStates;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Exceptions;
|
||||
@@ -30,11 +27,13 @@ using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.UnitTesting;
|
||||
|
||||
|
||||
[assembly: LevelOfParallelism(3)]
|
||||
|
||||
namespace Content.IntegrationTests;
|
||||
|
||||
/// <summary>
|
||||
/// Making clients, and servers is slow, this manages a pool of them so tests can reuse them.
|
||||
/// </summary>
|
||||
public static class PoolManager
|
||||
{
|
||||
private static readonly (string cvar, string value, bool tryAdd)[] ServerTestCvars =
|
||||
@@ -60,8 +59,8 @@ public static class PoolManager
|
||||
(CCVars.GameRoleTimers.Name, "false", false),
|
||||
};
|
||||
|
||||
private static int PairId = 0;
|
||||
private static object PairLock = new object();
|
||||
private static int PairId;
|
||||
private static object PairLock = new();
|
||||
private static List<Pair> Pairs = new();
|
||||
|
||||
private static async Task ConfigurePrototypes(RobustIntegrationTest.IntegrationInstance instance,
|
||||
@@ -119,6 +118,10 @@ public static class PoolManager
|
||||
return server;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This shuts down the pool, and disposes all the server/client pairs.
|
||||
/// This is a one time operation to be used when the testing program is exiting.
|
||||
/// </summary>
|
||||
public static void Shutdown()
|
||||
{
|
||||
lock (PairLock)
|
||||
@@ -211,6 +214,11 @@ public static class PoolManager
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="PairTracker"/>, which can be used to get access to a server, and client <see cref="Pair"/>
|
||||
/// </summary>
|
||||
/// <param name="poolSettings">See <see cref="PoolSettings"/></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<PairTracker> GetServerClient(PoolSettings poolSettings = null,
|
||||
[System.Runtime.CompilerServices.CallerFilePath] string testMethodFilePath = "",
|
||||
[System.Runtime.CompilerServices.CallerMemberName] string testMethodName = "") =>
|
||||
@@ -294,7 +302,7 @@ public static class PoolManager
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used after checking pairs, Don't use this directly
|
||||
/// Used by PairTracker after checking the server/client pair, Don't use this.
|
||||
/// </summary>
|
||||
/// <param name="pair"></param>
|
||||
public static void NoCheckReturn(Pair pair)
|
||||
@@ -424,6 +432,11 @@ public static class PoolManager
|
||||
return pair;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a map, a grid, and a tile, and gives back references to them.
|
||||
/// </summary>
|
||||
/// <param name="pairTracker">A pairTracker</param>
|
||||
/// <returns>A TestMapData</returns>
|
||||
public static async Task<TestMapData> CreateTestMap(PairTracker pairTracker)
|
||||
{
|
||||
var server = pairTracker.Pair.Server;
|
||||
@@ -451,6 +464,11 @@ public static class PoolManager
|
||||
return mapData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs a server/client pair in sync
|
||||
/// </summary>
|
||||
/// <param name="pair">A server/client pair</param>
|
||||
/// <param name="ticks">How many ticks to run them for</param>
|
||||
public static async Task RunTicksSync(Pair pair, int ticks)
|
||||
{
|
||||
for (var i = 0; i < ticks; i++)
|
||||
@@ -460,13 +478,11 @@ public static class PoolManager
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task WaitUntil(RobustIntegrationTest.IntegrationInstance instance, Func<bool> func,
|
||||
int maxTicks = 600,
|
||||
int tickStep = 1)
|
||||
{
|
||||
await WaitUntil(instance, async () => await Task.FromResult(func()), maxTicks, tickStep);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the server/client in sync, but also ensures they are both idle each tick.
|
||||
/// </summary>
|
||||
/// <param name="pair">The server/client pair</param>
|
||||
/// <param name="runTicks">How many ticks to run</param>
|
||||
public static async Task ReallyBeIdle(Pair pair, int runTicks = 25)
|
||||
{
|
||||
for (int i = 0; i < runTicks; i++)
|
||||
@@ -481,6 +497,27 @@ public static class PoolManager
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs a server, or a client until a condition is true
|
||||
/// </summary>
|
||||
/// <param name="instance">The server or client</param>
|
||||
/// <param name="func">The condition to check</param>
|
||||
/// <param name="maxTicks">How many ticks to try before giving up</param>
|
||||
/// <param name="tickStep">How many ticks to wait between checks</param>
|
||||
public static async Task WaitUntil(RobustIntegrationTest.IntegrationInstance instance, Func<bool> func,
|
||||
int maxTicks = 600,
|
||||
int tickStep = 1)
|
||||
{
|
||||
await WaitUntil(instance, async () => await Task.FromResult(func()), maxTicks, tickStep);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs a server, or a client until a condition is true
|
||||
/// </summary>
|
||||
/// <param name="instance">The server or client</param>
|
||||
/// <param name="func">The async condition to check</param>
|
||||
/// <param name="maxTicks">How many ticks to try before giving up</param>
|
||||
/// <param name="tickStep">How many ticks to wait between checks</param>
|
||||
public static async Task WaitUntil(RobustIntegrationTest.IntegrationInstance instance, Func<Task<bool>> func,
|
||||
int maxTicks = 600,
|
||||
int tickStep = 1)
|
||||
@@ -515,78 +552,96 @@ public static class PoolManager
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Settings for the pooled server, and client pair.
|
||||
/// Some options are for changing the pair, and others are
|
||||
/// so the pool can properly clean up what you borrowed.
|
||||
/// </summary>
|
||||
public sealed class PoolSettings
|
||||
{
|
||||
// Todo: We can make more of these pool-able, if we need enough of them for it to matter
|
||||
// TODO: We can make more of these pool-able, if we need enough of them for it to matter
|
||||
|
||||
/// <summary>
|
||||
/// If the returned pair must not be reused
|
||||
/// </summary>
|
||||
public bool MustNotBeReused => Destructive || NoLoadContent || DisableInterpolate || DummyTicker;
|
||||
|
||||
/// <summary>
|
||||
/// If the given pair must be brand new
|
||||
/// </summary>
|
||||
public bool MustBeNew => Fresh || NoLoadContent || DisableInterpolate || DummyTicker;
|
||||
|
||||
public bool NotConnected => NoClient || NoServer || Disconnected;
|
||||
/// <summary>
|
||||
/// We are going to ruin this pair
|
||||
/// If the given pair must not be connected
|
||||
/// </summary>
|
||||
public bool NotConnected => NoClient || NoServer || Disconnected;
|
||||
|
||||
/// <summary>
|
||||
/// Set to true if the test will ruin the server/client pair.
|
||||
/// </summary>
|
||||
public bool Destructive { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// We need a brand new pair
|
||||
/// Set to true if the given server/client pair should be created fresh.
|
||||
/// </summary>
|
||||
public bool Fresh { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// We need a pair that uses a dummy ticker
|
||||
/// Set to true if the given server should be using a dummy ticker.
|
||||
/// </summary>
|
||||
public bool DummyTicker { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// We need the client, and server to be disconnected
|
||||
/// Set to true if the given server/client pair should be disconnected from each other.
|
||||
/// </summary>
|
||||
public bool Disconnected { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// We need the server to be in the lobby
|
||||
/// Set to true if the given server/client pair should be in the lobby.
|
||||
/// </summary>
|
||||
public bool InLobby { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// We don't want content loaded
|
||||
/// Set this to true to skip loading the content files.
|
||||
/// Note: This setting won't work with a client.
|
||||
/// </summary>
|
||||
public bool NoLoadContent { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// We want to add some prototypes
|
||||
/// Set this to raw yaml text to load prototypes onto the given server/client pair.
|
||||
/// </summary>
|
||||
public string ExtraPrototypes { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Disables NetInterp
|
||||
/// Set this to true to disable the NetInterp CVar on the given server/client pair
|
||||
/// </summary>
|
||||
public bool DisableInterpolate { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Tells the pool it has to clean up before the server/client can be used.
|
||||
/// Set this to true to always clean up the server/client pair before giving it to another borrower
|
||||
/// </summary>
|
||||
public bool Dirty { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the map Cvar, and loads the map
|
||||
/// Set this to the path of a map to have the given server/client pair load the map.
|
||||
/// </summary>
|
||||
public string Map { get; init; } // TODO for map painter
|
||||
|
||||
/// <summary>
|
||||
/// The test won't use the client (so we can skip cleaning it)
|
||||
/// Set to true if the test won't use the client (so we can skip cleaning it up)
|
||||
/// </summary>
|
||||
public bool NoClient { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The test won't use the client (so we can skip cleaning it)
|
||||
/// Set to true if the test won't use the server (so we can skip cleaning it up)
|
||||
/// </summary>
|
||||
public bool NoServer { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Guess if skipping recycling is ok
|
||||
/// Tries to guess if we can skip recycling the server/client pair.
|
||||
/// </summary>
|
||||
/// <param name="nextSettings">The next set of settings the old pair will be set to</param>
|
||||
/// <returns></returns>
|
||||
/// <returns>If we can skip cleaning it up</returns>
|
||||
public bool CanFastRecycle(PoolSettings nextSettings)
|
||||
{
|
||||
if (Dirty) return false;
|
||||
@@ -603,6 +658,9 @@ public sealed class PoolSettings
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Holds a reference to things commonly needed when testing on a map
|
||||
/// </summary>
|
||||
public sealed class TestMapData
|
||||
{
|
||||
public MapId MapId { get; set; }
|
||||
@@ -612,6 +670,9 @@ public sealed class TestMapData
|
||||
public TileRef Tile { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A server/client pair
|
||||
/// </summary>
|
||||
public sealed class Pair
|
||||
{
|
||||
public int PairId { get; init; }
|
||||
@@ -621,11 +682,14 @@ public sealed class Pair
|
||||
public RobustIntegrationTest.ClientIntegrationInstance Client { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used by the pool to keep track of a borrowed server/client pair.
|
||||
/// </summary>
|
||||
public sealed class PairTracker : IAsyncDisposable
|
||||
{
|
||||
private int _disposed;
|
||||
|
||||
public async Task OnDirtyDispose()
|
||||
private async Task OnDirtyDispose()
|
||||
{
|
||||
var usageTime = UsageWatch.Elapsed;
|
||||
await TestContext.Out.WriteLineAsync($"Dirty: Test returned in {usageTime.TotalMilliseconds} ms");
|
||||
@@ -637,7 +701,7 @@ public sealed class PairTracker : IAsyncDisposable
|
||||
await TestContext.Out.WriteLineAsync($"Dirty: Disposed in {disposeTime.TotalMilliseconds} ms");
|
||||
}
|
||||
|
||||
public async Task OnCleanDispose()
|
||||
private async Task OnCleanDispose()
|
||||
{
|
||||
var usageTime = UsageWatch.Elapsed;
|
||||
await TestContext.Out.WriteLineAsync($"Clean: Test returned in {usageTime.TotalMilliseconds} ms");
|
||||
|
||||
Reference in New Issue
Block a user