ex-ex-01-math-interpreter/MathInterpreter.Test/ExpressionScannerTests.cs
github-classroom[bot] 4b1294b32d
Initial commit
2025-06-03 15:18:01 +00:00

233 lines
8 KiB
C#

using MathInterpreter.Core;
namespace MathInterpreter.Test;
public sealed class ExpressionScannerTests
{
[Theory]
[InlineData("3abc", 3, 1, "abc")]
[InlineData("35gh", 35, 2, "gh")]
[InlineData("3afc2", 3, 1, "afc2")]
[InlineData(" 3abc", 3, 1, "abc")]
[InlineData("2 3abc", 23, 2, "abc")]
[InlineData("2 3 abc", 23, 2, "abc")]
[InlineData("78944", 78944, 5, "")]
[InlineData("12?!", 12, 2, "?!")]
public void ScanNumber_Simple_Unsigned(string expression, int expectedValue, int expectedLength,
string expectedRemainingExpression)
{
ScanResult<int> result = ExpressionScanner.ScanNumber(expression.AsSpan(), false);
result.Value.Should().Be(expectedValue);
result.Length.Should().Be(expectedLength);
result.RemainingExpression.ToString().Should().Be(expectedRemainingExpression);
}
[Theory]
[InlineData("+3abc", 3, 1, "abc")]
[InlineData("+5ab3c", 5, 1, "ab3c")]
[InlineData("+356abc", 356, 3, "abc")]
[InlineData("-3abc", -3, 1, "abc")]
[InlineData("-8ab2c", -8, 1, "ab2c")]
[InlineData("-37abc", -37, 2, "abc")]
[InlineData(" - 3abc", -3, 1, "abc")]
[InlineData("+ 3abc", 3, 1, "abc")]
public void ScanNumber_Simple_Signed(string expression, int expectedValue, int expectedLength,
string expectedRemainingExpression)
{
ScanResult<int> result = ExpressionScanner.ScanNumber(expression.AsSpan(), true);
result.Value.Should().Be(expectedValue);
result.Length.Should().Be(expectedLength);
result.RemainingExpression.ToString().Should().Be(expectedRemainingExpression);
}
[Theory]
[InlineData("cba")]
[InlineData("c1b2a3")]
[InlineData(" t")]
[InlineData(" t2")]
public void ScanNumber_NoDigits(string invalidExpression)
{
var action = BuildScanNumberWrapper(invalidExpression, false);
action.Should().Throw<NumberFormatException>()
.WithMessage($"Unable to read number from beginning of '{invalidExpression}'");
}
[Theory]
[InlineData("+56")]
[InlineData("-90")]
public void ScanNumber_SignNotAllowed(string invalidExpression)
{
var action = BuildScanNumberWrapper(invalidExpression, false);
action.Should().Throw<NumberFormatException>()
.WithMessage($"Unable to read number from beginning of '{invalidExpression}' " +
"(Sign is not allowed here)");
}
[Theory]
[InlineData("++56")]
[InlineData("+-56")]
[InlineData("--90")]
[InlineData("-+90")]
public void ScanNumber_MultipleSignNotAllowed(string invalidExpression)
{
var action = BuildScanNumberWrapper(invalidExpression, true);
action.Should().Throw<NumberFormatException>()
.WithMessage($"Unable to read number from beginning of '{invalidExpression}' " +
"(Sign may occur only once)");
}
[Theory]
[InlineData("a+56")]
[InlineData("b-56")]
[InlineData("hello")]
[InlineData("-")]
[InlineData("+")]
[InlineData(" ")]
[InlineData(" a ")]
[InlineData(" a6 ")]
[InlineData("?1")]
[InlineData("")]
public void ScanNumber_Invalid(string invalidExpression)
{
var action = BuildScanNumberWrapper(invalidExpression, true);
action.Should().Throw<NumberFormatException>()
.WithMessage($"Unable to read number from beginning of '{invalidExpression}'");
}
[Theory]
[InlineData("+", Operator.Plus, "")]
[InlineData("-abc", Operator.Minus, "abc")]
[InlineData("* abc", Operator.Multiply, " abc")]
[InlineData("/abc ", Operator.Divide, "abc ")]
public void ScanOperator_Simple(string expression, Operator expectedOperator, string remainingExpression)
{
ScanResult<Operator> result = ExpressionScanner.ScanOperator(expression);
result.Value.Should().Be(expectedOperator);
result.Length.Should().Be(1);
result.RemainingExpression.ToString().Should().Be(remainingExpression);
}
[Theory]
[InlineData("&")]
[InlineData("abc")]
[InlineData(" abc")]
[InlineData("g /")]
public void ScanOperator_Invalid(string expression)
{
var action = BuildScanOperatorWrapper(expression);
action.Should().Throw<OperatorException>()
.WithMessage($"Unknown operator: {expression.TrimStart()[0]}");
}
[Fact]
public void ScanOperator_Empty()
{
var action = BuildScanOperatorWrapper(string.Empty);
action.Should().Throw<OperatorException>().WithMessage("Empty expression");
}
[Theory]
[InlineData("123abc", 123, 3, "abc")]
[InlineData("23.49cba", 23.49, 5, "cba")]
[InlineData("0.45ab2c", 0.45, 4, "ab2c")]
[InlineData("0.452ab1c", 0.452, 5, "ab1c")]
public void ScanOperand_Simple(string expression, double expectedValue, int expectedLength,
string remainingExpression)
{
ScanResult<double> result = ExpressionScanner.ScanOperand(expression);
result.Value.Should().BeApproximately(expectedValue, double.Epsilon);
result.Length.Should().Be(expectedLength);
result.RemainingExpression.ToString().Should().Be(remainingExpression);
}
[Theory]
[InlineData("+123", 123, 3, "")]
[InlineData("+123abc", 123, 3, "abc")]
[InlineData("+123.12abc", 123.12, 6, "abc")]
[InlineData("-123", -123, 3, "")]
[InlineData("-123cb", -123, 3, "cb")]
[InlineData("-123.89cb", -123.89, 6, "cb")]
public void ScanOperand_Simple_Signed(string expression, double expectedValue, int expectedLength,
string remainingExpression)
{
ScanResult<double> result = ExpressionScanner.ScanOperand(expression);
result.Value.Should().BeApproximately(expectedValue, double.Epsilon);
result.Length.Should().Be(expectedLength);
result.RemainingExpression.ToString().Should().Be(remainingExpression);
}
[Theory]
[InlineData("abc")]
[InlineData("")]
[InlineData(" ")]
[InlineData("cb43")]
public void ScanOperand_Invalid_NoNumber(string expression)
{
BuildScanOperandWrapper(expression).Should()
.Throw<ExpressionFormatException>()
.WithMessage("Operand is missing");
}
[Theory]
[InlineData(".12")]
[InlineData(" .12")]
[InlineData(".12.55")]
public void ScanOperand_Invalid_Integral(string expression)
{
BuildScanOperandWrapper(expression).Should()
.Throw<ExpressionFormatException>()
.WithMessage("Unable to parse integral part of number");
}
[Theory]
[InlineData("123.a")]
[InlineData("123. ")]
[InlineData("123. b")]
[InlineData("123.g5")]
[InlineData("123.-5")]
[InlineData("123.+5")]
public void ScanOperand_Invalid_Fractional(string expression)
{
BuildScanOperandWrapper(expression).Should()
.Throw<ExpressionFormatException>()
.WithMessage("Unable to parse fractional part of number");
}
private static Action BuildScanNumberWrapper(string expression, bool allowSign)
{
return () =>
{
ReadOnlySpan<char> span = expression.AsSpan();
ExpressionScanner.ScanNumber(span, allowSign);
};
}
private static Action BuildScanOperandWrapper(string expression)
{
return () =>
{
ReadOnlySpan<char> span = expression.AsSpan();
ExpressionScanner.ScanOperand(span);
};
}
private static Action BuildScanOperatorWrapper(string expression)
{
return () =>
{
ReadOnlySpan<char> span = expression.AsSpan();
ExpressionScanner.ScanOperator(span);
};
}
}