Toolshed (#17895)
* ogh * i should save my work * ogh * hhcdfhjbghshbxdfhghshc - lots of bugs in parsing still - invocation is a stub * expr parsing works * awawa * Saving work * Improve APIs a bit all around, add shortcuts. * awa * awa * AAAAAA * save work * Move shit to engine * lord * bql is kill * forgot the fucking bike rack * bql is kill for real * pjb will kill me * aughfhbdj * adgddf * gdsgvfvxshngfgh * b * hfsjhghj * a * tf you mean i have to document it * follow C# standards * Assorted cleanup and documentation pass, minor bugfix in ValueRefParser. * Start porting old commands, remove that pesky prefix in favor of integrating with the shell. * bw * Fix valueref up a bit, improve autocomplete for it. * awa * fix tests * git shut up * Arithmetic commands. * parse improvements * Update engine. --------- Co-authored-by: moonheart08 <moonheart08@users.noreply.github.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using Content.Server.Administration.Commands;
|
||||
using Content.Server.Administration.Systems;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.FixedPoint;
|
||||
@@ -11,7 +12,7 @@ using Robust.Shared.Prototypes;
|
||||
namespace Content.IntegrationTests.Tests.Commands
|
||||
{
|
||||
[TestFixture]
|
||||
[TestOf(typeof(RejuvenateCommand))]
|
||||
[TestOf(typeof(RejuvenateSystem))]
|
||||
public sealed class RejuvenateTest
|
||||
{
|
||||
private const string Prototypes = @"
|
||||
@@ -42,6 +43,7 @@ namespace Content.IntegrationTests.Tests.Commands
|
||||
var prototypeManager = server.ResolveDependency<IPrototypeManager>();
|
||||
var mobStateSystem = entManager.EntitySysManager.GetEntitySystem<MobStateSystem>();
|
||||
var damSystem = entManager.EntitySysManager.GetEntitySystem<DamageableSystem>();
|
||||
var rejuvenateSystem = entManager.EntitySysManager.GetEntitySystem<RejuvenateSystem>();
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
@@ -78,7 +80,7 @@ namespace Content.IntegrationTests.Tests.Commands
|
||||
});
|
||||
|
||||
// Rejuvenate them
|
||||
RejuvenateCommand.PerformRejuvenate(human);
|
||||
rejuvenateSystem.PerformRejuvenate(human);
|
||||
|
||||
// Check that it is alive and with no damage
|
||||
Assert.Multiple(() =>
|
||||
|
||||
20
Content.IntegrationTests/Tests/Toolshed/AdminTest.cs
Normal file
20
Content.IntegrationTests/Tests/Toolshed/AdminTest.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Robust.Shared.Toolshed;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Toolshed;
|
||||
|
||||
[TestFixture]
|
||||
public sealed class AdminTest : ToolshedTest
|
||||
{
|
||||
[Test]
|
||||
public async Task AllCommandsHavePermissions()
|
||||
{
|
||||
await Server.WaitAssertion(() =>
|
||||
{
|
||||
Assert.That(InvokeCommand("cmd:list where { acmd:perms isnull }", out var res));
|
||||
var list = ((IEnumerable<CommandSpec>) res).ToList();
|
||||
Assert.That(list, Is.Empty, "All commands must have admin permissions set up.");
|
||||
});
|
||||
}
|
||||
}
|
||||
84
Content.IntegrationTests/Tests/Toolshed/CommandParseTest.cs
Normal file
84
Content.IntegrationTests/Tests/Toolshed/CommandParseTest.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Toolshed;
|
||||
using Robust.Shared.Toolshed.Syntax;
|
||||
using Robust.Shared.Toolshed.TypeParsers;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Toolshed;
|
||||
|
||||
[TestFixture]
|
||||
public sealed class CommandRunTest : ToolshedTest
|
||||
{
|
||||
[Test]
|
||||
public async Task SimpleCommandRun()
|
||||
{
|
||||
await Server.WaitAssertion(() =>
|
||||
{
|
||||
ParseCommand("entities");
|
||||
ParseCommand("entities select 1");
|
||||
ParseCommand("entities with Item select 1");
|
||||
|
||||
ExpectError<OutOfInputError>();
|
||||
ParseCommand("entities with");
|
||||
|
||||
ExpectError<NoImplementationError>();
|
||||
ParseCommand("player:list with MetaData");
|
||||
|
||||
ExpectError<ExpressionOfWrongType>();
|
||||
ParseCommand("player:list", expectedType: typeof(IEnumerable<EntityUid>));
|
||||
|
||||
ParseCommand("entities not with MetaData");
|
||||
ParseCommand("with MetaData select 2 any", inputType: typeof(List<EntityUid>));
|
||||
|
||||
ParseCommand("entities not with MetaData => $myEntities");
|
||||
ParseCommand("=> $fooBar with MetaData", inputType: typeof(List<EntityUid>));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task EntityUidTypeParser()
|
||||
{
|
||||
await Server.WaitAssertion(() =>
|
||||
{
|
||||
ParseCommand("ent 1");
|
||||
ParseCommand("ent c1");
|
||||
|
||||
ExpectError<InvalidEntityUid>();
|
||||
ParseCommand("ent foodigity");
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task QuantityTypeParser()
|
||||
{
|
||||
await Server.WaitAssertion(() =>
|
||||
{
|
||||
ParseCommand("entities select 100");
|
||||
ParseCommand("entities select 50%");
|
||||
|
||||
ExpectError<InvalidQuantity>();
|
||||
ParseCommand("entities select -1");
|
||||
|
||||
ExpectError<InvalidQuantity>();
|
||||
ParseCommand("entities select 200%");
|
||||
|
||||
ExpectError<InvalidQuantity>();
|
||||
ParseCommand("entities select hotdog");
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ComponentTypeParser()
|
||||
{
|
||||
await Server.WaitAssertion(() =>
|
||||
{
|
||||
ParseCommand("entities with MetaData");
|
||||
|
||||
ExpectError<UnknownComponentError>();
|
||||
ParseCommand("entities with Foodiddy");
|
||||
|
||||
ExpectError<UnknownComponentError>();
|
||||
ParseCommand("entities with MetaDataComponent");
|
||||
});
|
||||
}
|
||||
}
|
||||
19
Content.IntegrationTests/Tests/Toolshed/LocTest.cs
Normal file
19
Content.IntegrationTests/Tests/Toolshed/LocTest.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Robust.Shared.Toolshed;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Toolshed;
|
||||
|
||||
[TestFixture]
|
||||
public sealed class LocTest : ToolshedTest
|
||||
{
|
||||
[Test]
|
||||
public async Task AllCommandsHaveDescriptions()
|
||||
{
|
||||
await Server.WaitAssertion(() =>
|
||||
{
|
||||
Assert.That(InvokeCommand("cmd:list where { cmd:descloc loc:tryloc isnull }", out var res));
|
||||
Assert.That((IEnumerable<CommandSpec>)res, Is.Empty, "All commands must have localized descriptions.");
|
||||
});
|
||||
}
|
||||
}
|
||||
153
Content.IntegrationTests/Tests/Toolshed/ToolshedTest.cs
Normal file
153
Content.IntegrationTests/Tests/Toolshed/ToolshedTest.cs
Normal file
@@ -0,0 +1,153 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Toolshed;
|
||||
using Robust.Shared.Toolshed.Errors;
|
||||
using Robust.Shared.Toolshed.Syntax;
|
||||
using Robust.UnitTesting;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Toolshed;
|
||||
|
||||
[TestFixture]
|
||||
[FixtureLifeCycle(LifeCycle.SingleInstance)]
|
||||
public abstract class ToolshedTest : IInvocationContext
|
||||
{
|
||||
protected PairTracker PairTracker = default!;
|
||||
|
||||
protected virtual bool NoClient => true;
|
||||
protected virtual bool AssertOnUnexpectedError => true;
|
||||
|
||||
protected RobustIntegrationTest.ServerIntegrationInstance Server = default!;
|
||||
protected RobustIntegrationTest.ClientIntegrationInstance? Client = null;
|
||||
protected ToolshedManager Toolshed = default!;
|
||||
protected IAdminManager AdminManager = default!;
|
||||
|
||||
protected IInvocationContext? Context = null;
|
||||
|
||||
[TearDown]
|
||||
public virtual async Task TearDown()
|
||||
{
|
||||
Assert.IsEmpty(_expectedErrors);
|
||||
ClearErrors();
|
||||
}
|
||||
|
||||
[OneTimeSetUp]
|
||||
public virtual async Task Setup()
|
||||
{
|
||||
PairTracker = await PoolManager.GetServerClient(new PoolSettings {NoClient = NoClient});
|
||||
Server = PairTracker.Pair.Server;
|
||||
|
||||
if (!NoClient)
|
||||
{
|
||||
Client = PairTracker.Pair.Client;
|
||||
await Client.WaitIdleAsync();
|
||||
}
|
||||
|
||||
await Server.WaitIdleAsync();
|
||||
|
||||
Toolshed = Server.ResolveDependency<ToolshedManager>();
|
||||
AdminManager = Server.ResolveDependency<IAdminManager>();
|
||||
}
|
||||
|
||||
protected bool InvokeCommand(string command, out object? result, IPlayerSession? session = null)
|
||||
{
|
||||
return Toolshed.InvokeCommand(this, command, null, out result);
|
||||
}
|
||||
|
||||
protected void ParseCommand(string command, Type? inputType = null, Type? expectedType = null, bool once = false)
|
||||
{
|
||||
var parser = new ForwardParser(command, Toolshed);
|
||||
var success = CommandRun.TryParse(false, false, parser, inputType, expectedType, once, out _, out _, out var error);
|
||||
|
||||
if (error is not null)
|
||||
ReportError(error);
|
||||
|
||||
if (error is null)
|
||||
Assert.That(success, $"Parse failed despite no error being reported. Parsed {command}");
|
||||
}
|
||||
|
||||
public bool CheckInvokable(CommandSpec command, out IConError? error)
|
||||
{
|
||||
if (Context is not null)
|
||||
{
|
||||
return Context.CheckInvokable(command, out error);
|
||||
}
|
||||
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected IPlayerSession? InvocationSession { get; set; }
|
||||
|
||||
public ICommonSession? Session
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Context is not null)
|
||||
{
|
||||
return Context.Session;
|
||||
}
|
||||
|
||||
return InvocationSession;
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteLine(string line)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
private Queue<Type> _expectedErrors = new();
|
||||
|
||||
private List<IConError> _errors = new();
|
||||
|
||||
public void ReportError(IConError err)
|
||||
{
|
||||
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 void ClearErrors()
|
||||
{
|
||||
_errors.Clear();
|
||||
}
|
||||
|
||||
public Dictionary<string, object?> Variables { get; } = new();
|
||||
|
||||
protected void ExpectError(Type err)
|
||||
{
|
||||
_expectedErrors.Enqueue(err);
|
||||
}
|
||||
|
||||
protected void ExpectError<T>()
|
||||
{
|
||||
_expectedErrors.Enqueue(typeof(T));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user