#nullable enable
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Server.Preferences.Managers;
using Content.Shared.Preferences;
using Content.Shared.Roles;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Prototypes;
using Robust.UnitTesting;
namespace Content.IntegrationTests.Pair;
// Contains misc helper functions to make writing tests easier.
public sealed partial class TestPair
{
///
/// Creates a map, a grid, and a tile, and gives back references to them.
///
[MemberNotNull(nameof(TestMap))]
public async Task CreateTestMap(bool initialized = true, string tile = "Plating")
{
var mapData = new TestMapData();
TestMap = mapData;
await Server.WaitIdleAsync();
var tileDefinitionManager = Server.ResolveDependency();
TestMap = mapData;
await Server.WaitPost(() =>
{
mapData.MapUid = Server.System().CreateMap(out mapData.MapId, runMapInit: initialized);
mapData.Grid = Server.MapMan.CreateGridEntity(mapData.MapId);
mapData.GridCoords = new EntityCoordinates(mapData.Grid, 0, 0);
var plating = tileDefinitionManager[tile];
var platingTile = new Tile(plating.TileId);
Server.System().SetTile(mapData.Grid.Owner, mapData.Grid.Comp, mapData.GridCoords, platingTile);
mapData.MapCoords = new MapCoordinates(0, 0, mapData.MapId);
mapData.Tile = Server.System().GetAllTiles(mapData.Grid.Owner, mapData.Grid.Comp).First();
});
TestMap = mapData;
if (!Settings.Connected)
return mapData;
await RunTicksSync(10);
mapData.CMapUid = ToClientUid(mapData.MapUid);
mapData.CGridUid = ToClientUid(mapData.Grid);
mapData.CGridCoords = new EntityCoordinates(mapData.CGridUid, 0, 0);
TestMap = mapData;
return mapData;
}
///
/// Convert a client-side uid into a server-side uid
///
public EntityUid ToServerUid(EntityUid uid) => ConvertUid(uid, Client, Server);
///
/// Convert a server-side uid into a client-side uid
///
public EntityUid ToClientUid(EntityUid uid) => ConvertUid(uid, Server, Client);
private static EntityUid ConvertUid(
EntityUid uid,
RobustIntegrationTest.IntegrationInstance source,
RobustIntegrationTest.IntegrationInstance destination)
{
if (!uid.IsValid())
return EntityUid.Invalid;
if (!source.EntMan.TryGetComponent(uid, out var meta))
{
Assert.Fail($"Failed to resolve MetaData while converting the EntityUid for entity {uid}");
return EntityUid.Invalid;
}
if (!destination.EntMan.TryGetEntity(meta.NetEntity, out var otherUid))
{
Assert.Fail($"Failed to resolve net ID while converting the EntityUid entity {source.EntMan.ToPrettyString(uid)}");
return EntityUid.Invalid;
}
return otherUid.Value;
}
///
/// Execute a command on the server and wait some number of ticks.
///
public async Task WaitCommand(string cmd, int numTicks = 10)
{
await Server.ExecuteCommand(cmd);
await RunTicksSync(numTicks);
}
///
/// Execute a command on the client and wait some number of ticks.
///
public async Task WaitClientCommand(string cmd, int numTicks = 10)
{
await Client.ExecuteCommand(cmd);
await RunTicksSync(numTicks);
}
///
/// Retrieve all entity prototypes that have some component.
///
public List<(EntityPrototype, T)> GetPrototypesWithComponent(
HashSet? ignored = null,
bool ignoreAbstract = true,
bool ignoreTestPrototypes = true)
where T : IComponent
{
var id = Server.ResolveDependency().GetComponentName(typeof(T));
var list = new List<(EntityPrototype, T)>();
foreach (var proto in Server.ProtoMan.EnumeratePrototypes())
{
if (ignored != null && ignored.Contains(proto.ID))
continue;
if (ignoreAbstract && proto.Abstract)
continue;
if (ignoreTestPrototypes && IsTestPrototype(proto))
continue;
if (proto.Components.TryGetComponent(id, out var cmp))
list.Add((proto, (T)cmp));
}
return list;
}
///
/// Retrieve all entity prototypes that have some component.
///
public List GetPrototypesWithComponent(Type type,
HashSet? ignored = null,
bool ignoreAbstract = true,
bool ignoreTestPrototypes = true)
{
var id = Server.ResolveDependency().GetComponentName(type);
var list = new List();
foreach (var proto in Server.ProtoMan.EnumeratePrototypes())
{
if (ignored != null && ignored.Contains(proto.ID))
continue;
if (ignoreAbstract && proto.Abstract)
continue;
if (ignoreTestPrototypes && IsTestPrototype(proto))
continue;
if (proto.Components.ContainsKey(id))
list.Add((proto));
}
return list;
}
///
/// Set a user's antag preferences. Modified preferences are automatically reset at the end of the test.
///
public async Task SetAntagPreference(ProtoId id, bool value, NetUserId? user = null)
{
user ??= Client.User!.Value;
if (user is not {} userId)
return;
var prefMan = Server.ResolveDependency();
var prefs = prefMan.GetPreferences(userId);
// Automatic preference resetting only resets slot 0.
Assert.That(prefs.SelectedCharacterIndex, Is.EqualTo(0));
var profile = (HumanoidCharacterProfile) prefs.Characters[0];
var newProfile = profile.WithAntagPreference(id, value);
_modifiedProfiles.Add(userId);
await Server.WaitPost(() => prefMan.SetProfile(userId, 0, newProfile).Wait());
}
///
/// Set a user's job preferences. Modified preferences are automatically reset at the end of the test.
///
public async Task SetJobPriority(ProtoId id, JobPriority value, NetUserId? user = null)
{
user ??= Client.User!.Value;
if (user is { } userId)
await SetJobPriorities(userId, (id, value));
}
///
public async Task SetJobPriorities(params (ProtoId, JobPriority)[] priorities)
=> await SetJobPriorities(Client.User!.Value, priorities);
///
public async Task SetJobPriorities(NetUserId user, params (ProtoId, JobPriority)[] priorities)
{
var highCount = priorities.Count(x => x.Item2 == JobPriority.High);
Assert.That(highCount, Is.LessThanOrEqualTo(1), "Cannot have more than one high priority job");
var prefMan = Server.ResolveDependency();
var prefs = prefMan.GetPreferences(user);
var profile = (HumanoidCharacterProfile) prefs.Characters[0];
var dictionary = new Dictionary, JobPriority>(profile.JobPriorities);
// Automatic preference resetting only resets slot 0.
Assert.That(prefs.SelectedCharacterIndex, Is.EqualTo(0));
if (highCount != 0)
{
foreach (var (key, priority) in dictionary)
{
if (priority == JobPriority.High)
dictionary[key] = JobPriority.Medium;
}
}
foreach (var (job, priority) in priorities)
{
if (priority == JobPriority.Never)
dictionary.Remove(job);
else
dictionary[job] = priority;
}
var newProfile = profile.WithJobPriorities(dictionary);
_modifiedProfiles.Add(user);
await Server.WaitPost(() => prefMan.SetProfile(user, 0, newProfile).Wait());
}
}