mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Robust.UnitTesting was both ALL tests for RT, and also API surface for content tests. Tests are now split into separate projects as appropriate, and the API side has also been split off.
327 lines
8.5 KiB
C#
327 lines
8.5 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using NUnit.Framework;
|
|
using Robust.Shared.Console;
|
|
using Robust.Shared.Network;
|
|
using Robust.Shared.Player;
|
|
using Robust.Shared.Toolshed;
|
|
using Robust.Shared.Toolshed.Errors;
|
|
using Robust.Shared.Toolshed.Syntax;
|
|
|
|
namespace Robust.UnitTesting.Shared.Toolshed;
|
|
|
|
[TestFixture, Parallelizable(ParallelScope.Fixtures)]
|
|
[FixtureLifeCycle(LifeCycle.SingleInstance)]
|
|
public abstract class ToolshedTest : RobustIntegrationTest, IInvocationContext
|
|
{
|
|
protected virtual bool AssertOnUnexpectedError => true;
|
|
|
|
#pragma warning disable NUnit1032
|
|
protected ServerIntegrationInstance Server = default!;
|
|
#pragma warning restore NUnit1032
|
|
|
|
public ToolshedManager Toolshed { get; private set; } = default!;
|
|
public ToolshedEnvironment Environment => Toolshed.DefaultEnvironment;
|
|
|
|
protected IInvocationContext? InvocationContext = null;
|
|
|
|
[TearDown]
|
|
public async Task TearDownInternal()
|
|
{
|
|
await TearDown();
|
|
await ReturnToPool(Server);
|
|
Server = default!;
|
|
}
|
|
|
|
protected virtual Task TearDown()
|
|
{
|
|
Assert.That(ExpectedErrors, Is.Empty);
|
|
ClearErrors();
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
[SetUp]
|
|
public virtual async Task Setup()
|
|
{
|
|
var options = new ServerIntegrationOptions()
|
|
{
|
|
Pool = true
|
|
};
|
|
Server = StartServer(options);
|
|
|
|
await Server.WaitIdleAsync();
|
|
|
|
Toolshed = Server.ResolveDependency<ToolshedManager>();
|
|
}
|
|
|
|
protected bool InvokeCommand(string command, out object? result)
|
|
{
|
|
return Toolshed.InvokeCommand(this, command, null, out result);
|
|
}
|
|
|
|
protected CompletionResult? GetCompletions(string cmd)
|
|
{
|
|
return Toolshed.GetCompletions(this, cmd);
|
|
}
|
|
|
|
protected object? InvokeCommand(string command, Type output)
|
|
{
|
|
Assert.That(InvokeCommand(command, out var res));
|
|
Assert.That(res, Is.AssignableTo(output));
|
|
return res;
|
|
}
|
|
|
|
protected T InvokeCommand<T>(string command)
|
|
{
|
|
return (T) InvokeCommand(command, typeof(T))!;
|
|
}
|
|
|
|
protected object? InvokeCommand<TIn>(string command, TIn input, Type output)
|
|
{
|
|
Assert.That(Toolshed.InvokeCommand(this, command, input, out var res));
|
|
Assert.That(res, Is.AssignableTo(output));
|
|
return res;
|
|
}
|
|
|
|
protected TOut InvokeCommand<TIn, TOut>(string command, TIn input)
|
|
{
|
|
return (TOut) InvokeCommand(command, input, typeof(TOut))!;
|
|
}
|
|
|
|
protected void AssertResult(string command, object? expected)
|
|
{
|
|
Assert.That(InvokeCommand(command, out var result));
|
|
if (expected is IEnumerable @enum)
|
|
Assert.That(result, Is.EquivalentTo(@enum));
|
|
else
|
|
Assert.That(result, Is.EqualTo(expected));
|
|
}
|
|
|
|
protected void AssertParseable<T>()
|
|
{
|
|
var parser = new ParserContext("", Toolshed, Toolshed.DefaultEnvironment, IVariableParser.Empty, null);
|
|
Toolshed.TryParse<T>(parser, out _);
|
|
Assert.That(parser.Error, Is.Not.TypeOf<UnparseableValueError>(), $"Couldn't find a parser for {typeof(T).PrettyName()}");
|
|
}
|
|
|
|
#region Completion Asserts
|
|
|
|
protected void AssertCompletion(string command, CompletionResult? expected)
|
|
{
|
|
var result = GetCompletions(command);
|
|
if (expected == null)
|
|
{
|
|
Assert.That(result, Is.Null);
|
|
return;
|
|
}
|
|
|
|
Assert.That(result, Is.Not.Null);
|
|
if (result == null)
|
|
return;
|
|
|
|
Assert.That(result.Hint, Is.EqualTo(expected.Hint));
|
|
Assert.That(result.Options, Is.EquivalentTo(expected.Options));
|
|
}
|
|
|
|
protected void AssertCompletionEmpty(string command)
|
|
{
|
|
var result = GetCompletions(command);
|
|
if (result == null)
|
|
return;
|
|
|
|
Assert.That(result.Options.Length, Is.EqualTo(0));
|
|
Assert.That(result.Hint, Is.Null);
|
|
}
|
|
|
|
protected void AssertCompletionHint(string command, string hint)
|
|
{
|
|
var result = GetCompletions(command);
|
|
Assert.That(result, Is.Not.Null);
|
|
if (result == null)
|
|
return;
|
|
|
|
Assert.That(result.Hint, Is.EqualTo(hint));
|
|
}
|
|
|
|
protected void AssertCompletionSingle(string command, string expected)
|
|
{
|
|
var result = GetCompletions(command);
|
|
Assert.That(result, Is.Not.Null);
|
|
if (result == null)
|
|
return;
|
|
|
|
Assert.That(result.Options.Length, Is.EqualTo(1));
|
|
if (result.Options.Length != 1)
|
|
return;
|
|
|
|
Assert.That(result.Options[0].Value, Is.EqualTo(expected));
|
|
}
|
|
|
|
protected void AssertCompletionContains(string command, params string[] expected)
|
|
{
|
|
var result = GetCompletions(command);
|
|
|
|
Assert.That(result, Is.Not.Null);
|
|
if (result == null)
|
|
return;
|
|
|
|
Assert.That(result.Options.Length, Is.GreaterThanOrEqualTo(expected.Length));
|
|
if (result.Options.Length != 1)
|
|
return;
|
|
|
|
foreach (var ex in expected)
|
|
{
|
|
Assert.That(result.Options.Any(x => x.Value == ex));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check that the given string is not a suggested completion option.
|
|
/// </summary>
|
|
protected void AssertCompletionInvalid(string command, string invalid)
|
|
{
|
|
var result = GetCompletions(command);
|
|
if (result == null)
|
|
return;
|
|
|
|
foreach (var res in result.Options)
|
|
{
|
|
Assert.That(res.Value, Is.Not.EqualTo(invalid));
|
|
}
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
protected void ParseCommand(string command, Type? inputType = null, Type? expectedType = null)
|
|
{
|
|
var parser = new ParserContext(command, Toolshed, Toolshed.DefaultEnvironment, IVariableParser.Empty, null);
|
|
var success = CommandRun.TryParse(parser, inputType, expectedType, out _);
|
|
ReportError(parser.Error);
|
|
|
|
if (parser.Error is null)
|
|
Assert.That(success, $"Parse failed despite no error being reported. Parsed {command}");
|
|
}
|
|
|
|
public bool CheckInvokable(CommandSpec command, out IConError? error)
|
|
{
|
|
if (InvocationContext is not null)
|
|
{
|
|
return InvocationContext.CheckInvokable(command, out error);
|
|
}
|
|
|
|
error = null;
|
|
return true;
|
|
}
|
|
|
|
protected ICommonSession? InvocationSession { get; set; }
|
|
|
|
public NetUserId? User => Session?.UserId;
|
|
|
|
public ICommonSession? Session
|
|
{
|
|
get
|
|
{
|
|
if (InvocationContext is not null)
|
|
{
|
|
return InvocationContext.Session;
|
|
}
|
|
|
|
return InvocationSession;
|
|
}
|
|
}
|
|
|
|
public void WriteLine(string line)
|
|
{
|
|
return;
|
|
}
|
|
|
|
protected Queue<Type> ExpectedErrors = new();
|
|
|
|
protected List<IConError> Errors = new();
|
|
|
|
public void ReportError(IConError? err)
|
|
{
|
|
if (err == null)
|
|
{
|
|
if (ExpectedErrors.Count > 0)
|
|
Assert.Fail($"Expected an error of type {ExpectedErrors.Peek().PrettyName()}, but none was received");
|
|
return;
|
|
}
|
|
|
|
if (ExpectedErrors.Count == 0)
|
|
{
|
|
if (AssertOnUnexpectedError)
|
|
{
|
|
Assert.Fail($"Got an error, {err.GetType()}, when none was expected.\n{err.Describe()}");
|
|
}
|
|
|
|
goto done;
|
|
}
|
|
|
|
var ty = ExpectedErrors.Dequeue();
|
|
|
|
if (AssertOnUnexpectedError)
|
|
{
|
|
Assert.That(
|
|
err.GetType().IsAssignableTo(ty),
|
|
$"The error {err.GetType()} wasn't assignable to the expected type {ty}.\n{err.Describe()}"
|
|
);
|
|
}
|
|
|
|
done:
|
|
Errors.Add(err);
|
|
}
|
|
|
|
public IEnumerable<IConError> GetErrors()
|
|
{
|
|
return Errors;
|
|
}
|
|
|
|
public bool HasErrors => Errors.Count > 0;
|
|
|
|
public void ClearErrors()
|
|
{
|
|
Errors.Clear();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public object? ReadVar(string name)
|
|
{
|
|
return Variables.GetValueOrDefault(name);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void WriteVar(string name, object? value)
|
|
{
|
|
Variables[name] = value;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IEnumerable<string> GetVars()
|
|
{
|
|
return Variables.Keys;
|
|
}
|
|
|
|
public Dictionary<string, object?> Variables { get; } = new();
|
|
|
|
protected void ExpectError(Type err)
|
|
{
|
|
ExpectedErrors.Enqueue(err);
|
|
}
|
|
|
|
protected void ExpectError<T>() where T : IConError
|
|
{
|
|
ExpectError(typeof(T));
|
|
}
|
|
|
|
protected void ParseError<T>(string cmd) where T : IConError
|
|
{
|
|
ExpectError<T>();
|
|
ParseCommand(cmd);
|
|
}
|
|
}
|