Adopt .NET naming and structure: CsMic.* namespaces, PascalCase projects and solution; update Coco namespace; update package id and NuGet publish target; adjust project refs and tests.

This commit is contained in:
Jordan Wages 2025-08-20 20:10:34 -05:00
commit 0a67e6e2cc
24 changed files with 54 additions and 49 deletions

View file

@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<RootNamespace>CsMic.Tests</RootNamespace>
<AssemblyName>CsMic.Tests</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="NUnit" Version="4.2.2" />
<PackageReference Include="NUnit.Analyzers" Version="4.3.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Core\CsMic.Core.csproj" />
<ProjectReference Include="..\StandardLibrary\CsMic.StandardLibrary.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="NUnit.Framework" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,141 @@
using System.Globalization;
using CsMic;
namespace CsMic.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));
}
}

View file

@ -0,0 +1,115 @@
using CsMic;
using CsMic.StandardLibrary;
using NUnit.Framework;
using CsMic.StandardLibrary.Functions;
using System.Globalization;
using System.Reflection.Metadata;
namespace CsMic.Tests;
public class StdlibFunctionsTests
{
private InputInterpreter _interp = null!;
[SetUp]
public void Setup()
{
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
_interp = new InputInterpreter();
Constants.Initialize(_interp);
Functions.Initialize(_interp); ;
}
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));
}
[TestCase("abs(-1)", 1)]
[TestCase("abs(0)", 0)]
[TestCase("abs(2)", 2)]
[TestCase("2 + abs(-1)", 3)]
public void AbsoluteValue_Works(string expr, decimal expected)
{
var result = _interp.Interpret(expr);
AssertSuccess(result, expected, _interp);
}
[TestCase("abs(-3.5)", "3.5")]
public void AbsoluteValue_Works_Decimal(string expr, string expectedStr)
{
var result = _interp.Interpret(expr);
var expected = decimal.Parse(expectedStr, CultureInfo.InvariantCulture);
AssertSuccess(result, expected, _interp);
}
[TestCase("sign(5)", 1)]
[TestCase("sign(0)", 1)]
[TestCase("sign(-2)", -1)]
[TestCase("2*sign(-3)", -2)]
public void Sign_Works(string expr, decimal expected)
{
var result = _interp.Interpret(expr);
AssertSuccess(result, expected, _interp);
}
[TestCase("min(1,2)", 1)]
[TestCase("min(2,1)", 1)]
[TestCase("min(-3,4)", -3)]
[TestCase("1 + min(3,4)", 4)]
public void Min_Works(string expr, decimal expected)
{
var result = _interp.Interpret(expr);
AssertSuccess(result, expected, _interp);
}
[TestCase("min(2.5, 2.6)", "2.5")]
public void Min_Works_Decimals(string expr, string expectedStr)
{
var result = _interp.Interpret(expr);
var expected = decimal.Parse(expectedStr, CultureInfo.InvariantCulture);
AssertSuccess(result, expected, _interp);
}
[TestCase("max(1,2)", 2)]
[TestCase("max(2,1)", 2)]
[TestCase("max(-3,4)", 4)]
[TestCase("1 + max(3,4)", 5)]
public void Max_Works(string expr, decimal expected)
{
var result = _interp.Interpret(expr);
AssertSuccess(result, expected, _interp);
}
[TestCase("max(2.5, 2.6)", "2.6")]
public void Max_Works_Decimals(string expr, string expectedStr)
{
var result = _interp.Interpret(expr);
var expected = decimal.Parse(expectedStr, CultureInfo.InvariantCulture);
AssertSuccess(result, expected, _interp);
}
[TestCase("pi", "3.1415926535897931")]
[TestCase("e", "2.7182818284590451")]
[TestCase("tau", "6.2831853071795862")]
[TestCase("phi", "1.6180339887498948")]
[TestCase("goldenratio", "1.6180339887498948")]
[TestCase("eurler", "0.5772156649015329")]
[TestCase("omega", "0.5671432904097839")]
public void Constants_AreAvailable(string expr, string expectedStr)
{
var result = _interp.Interpret(expr);
var expected = decimal.Parse(expectedStr, CultureInfo.InvariantCulture);
AssertSuccess(result, expected, _interp);
}
[Test]
public void Constants_CanBeUsedInArithmetic()
{
AssertSuccess(_interp.Interpret("2*pi"), 6.2831853071795862m, _interp);
AssertSuccess(_interp.Interpret("pi + e"), 5.8598744820488382m, _interp);
AssertSuccess(_interp.Interpret("tau / pi"), 2m, _interp);
}
}