cs-mic/src/tests/InputInterpreterTests.cs

141 lines
4.1 KiB
C#

using System.Globalization;
using csmic;
namespace tests;
public class InputInterpreterTests
{
private InputInterpreter _interp = null!;
[SetUp]
public void Setup()
{
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
_interp = new InputInterpreter();
}
private static void AssertSuccess(decimal result, decimal expected, InputInterpreter interp)
{
Assert.That(result, Is.EqualTo(expected));
Assert.That(interp.NumericValue, Is.EqualTo(expected));
Assert.That(interp.StringValue, Is.EqualTo(string.Empty));
}
private static void AssertSoftError(decimal result, InputInterpreter interp)
{
Assert.That(result, Is.EqualTo(0m));
Assert.That(interp.NumericValue, Is.EqualTo(0m));
Assert.That(string.IsNullOrEmpty(interp.StringValue), Is.False);
}
[TestCase("2", 2)]
[TestCase("-5", -5)]
[TestCase("2+3*4", 14)]
[TestCase("(2+3)*4", 20)]
[TestCase("8/2", 4)]
[TestCase("7%4", 3)]
[TestCase("2^3", 8)]
[TestCase("1e2", 100)]
[TestCase("2.0e2", 200)]
[TestCase("0xFF", 255)]
[TestCase("1010b", 10)]
[TestCase("0xFF*1010b", 2550)]
public void Arithmetic_Works(string expr, decimal expected)
{
var result = _interp.Interpret(expr);
AssertSuccess(result, expected, _interp);
}
[TestCase("2==2", 1)]
[TestCase("2<3", 1)]
[TestCase("3<2", 0)]
[TestCase("2>=2", 1)]
[TestCase("2<=1", 0)]
public void Comparisons_YieldBooleanAsNumeric(string expr, decimal expected)
{
var result = _interp.Interpret(expr);
AssertSuccess(result, expected, _interp);
}
[Test]
public void Variables_AssignAndUse_PersistAcrossCalls()
{
AssertSuccess(_interp.Interpret("x :: 2"), 2, _interp);
AssertSuccess(_interp.Interpret("x+1"), 3, _interp);
AssertSuccess(_interp.Interpret("x :: 3"), 3, _interp);
AssertSuccess(_interp.Interpret("x+1"), 4, _interp);
}
[Test]
public void UnknownVariable_ProducesSoftError()
{
var result = _interp.Interpret("nope + 1");
AssertSoftError(result, _interp);
}
[Test]
public void ExpressionBinding_EvaluatesAtUseTime()
{
AssertSuccess(_interp.Interpret("num :: 2"), 2, _interp);
AssertSuccess(_interp.Interpret("exp := 1 + num"), 0, _interp); // assignment returns 0
AssertSuccess(_interp.Interpret("exp"), 3, _interp);
AssertSuccess(_interp.Interpret("num :: 3"), 3, _interp);
AssertSuccess(_interp.Interpret("exp"), 4, _interp);
}
[Test]
public void ExpressionBinding_InvalidExpression_ProducesSoftError()
{
_interp.Interpret("exp :== bad + 1");
var result = _interp.Interpret("exp");
AssertSoftError(result, _interp);
}
[Test]
public void Arrays_AssignAndIndex()
{
AssertSuccess(_interp.Interpret("arr -> [1,2,3]"), 0, _interp); // array assignment returns 0
AssertSuccess(_interp.Interpret("arr[0]"), 1, _interp);
AssertSuccess(_interp.Interpret("arr[2]"), 3, _interp);
}
[Test]
public void Arrays_OutOfRange_ProducesSoftError()
{
_interp.Interpret("arr -> [1,2,3]");
var result = _interp.Interpret("arr[3]");
AssertSoftError(result, _interp);
}
[Test]
public void Arrays_IndexExpression_Works()
{
_interp.Interpret("arr -> [1,2,3]");
_interp.Interpret("i :: 1");
AssertSuccess(_interp.Interpret("arr[i+1]"), 3, _interp);
}
[Test]
public void StringLiteral_Alone_IsError()
{
var result = _interp.Interpret("\"hi\"");
AssertSoftError(result, _interp);
}
[Test]
public void DivideByZero_ProducesSoftError()
{
var result = _interp.Interpret("1/0");
AssertSoftError(result, _interp);
}
[Test]
public void LastExecutionTime_IsMeasured()
{
_interp.Interpret("2+2");
Assert.That(_interp.LastExecutionTime, Is.GreaterThanOrEqualTo(TimeSpan.Zero));
_interp.Interpret("3+3");
Assert.That(_interp.LastExecutionTime, Is.GreaterThanOrEqualTo(TimeSpan.Zero));
}
}