Switch to Expecto (#89)

This commit is contained in:
Patrick Stevens
2025-07-03 00:14:29 +01:00
committed by GitHub
parent f39e7c07bf
commit 2efe9803da
7 changed files with 107 additions and 102 deletions

View File

@@ -42,8 +42,13 @@ dotnet fantomas .
```
### Running the Application
A playground C# file is in CSharpExample/Class1.cs.
This environment is convenient for running WoofWare.PawPrint against a standalone DLL.
Interpolate the approprate strings like `{Platform}` as necessary depending on the current environment and the output of the `dotnet publish`.
```bash
dotnet publish --self-contained --runtime-id osx-arm64 CSharpExample/ && dotnet run --project WoofWare.PawPrint.App/WoofWare.PawPrint.App.fsproj -- CSharpExample/bin/Debug/net9.0/osx-arm64/publish/CSharpExample.dll
dotnet publish --self-contained --runtime {Platform} CSharpExample/
dotnet run --project WoofWare.PawPrint.App/WoofWare.PawPrint.App.fsproj -- CSharpExample/bin/{Configuration}/{Framework}/{Platform}/publish/CSharpExample.dll
```
## Architecture
@@ -62,10 +67,13 @@ dotnet publish --self-contained --runtime-id osx-arm64 CSharpExample/ && dotnet
- `Corelib.fs`: Core library type definitions (String, Array, etc.)
**WoofWare.PawPrint.Test**
- Uses NUnit as the test framework
- Uses Expecto as the test framework
- Test cases are defined in `TestCases.fs`
- C# source files in `sources/` are compiled and executed by the runtime as test cases
- C# source files in `sources{Pure,Impure}/` are compiled and executed by the runtime as test cases
- `TestHarness.fs` provides infrastructure for running test assemblies through the interpreter
- Run all tests with `dotnet run --project WoofWare.PawPrint.Test/WoofWare.PawPrint.Test.fsproj -- --no-spinner`
- Run a specific test with `dotnet run --project WoofWare.PawPrint.Test/WoofWare.PawPrint.Test.fsproj -- --filter-test-case StringWithinTestName --no-spinner`
- Pending test definitions must be moved into the non-pending test case list before they can be run.
**WoofWare.PawPrint.App**
- Entry point application for running the interpreter

View File

@@ -6,6 +6,11 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<WarningsAsErrors>false</WarningsAsErrors>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<EnableDefaultItems>false</EnableDefaultItems>
</PropertyGroup>
<ItemGroup>
<Compile Include="Class1.cs" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,7 @@
namespace WoofWare.PawPrint.Test
open Expecto
module Program =
[<EntryPoint>]
let main argv = runTestsInAssemblyWithCLIArgs [] argv

View File

@@ -3,14 +3,11 @@ namespace WoofWare.Pawprint.Test
open System.Collections.Immutable
open System.IO
open FsUnitTyped
open NUnit.Framework
open WoofWare.DotnetRuntimeLocator
open WoofWare.PawPrint
open WoofWare.PawPrint.ExternImplementations
open WoofWare.PawPrint.Test
[<TestFixture>]
[<Parallelizable(ParallelScope.All)>]
module TestImpureCases =
let assy = typeof<RunResult>.Assembly
@@ -51,8 +48,7 @@ module TestImpureCases =
}
]
[<TestCaseSource(nameof cases)>]
let ``Can evaluate C# files`` (case : TestCase) : unit =
let runTest (case : TestCase) : unit =
let source = Assembly.getEmbeddedResourceAsString case.FileName assy
let image = Roslyn.compile [ source ]
let messages, loggerFactory = LoggerFactory.makeTest ()
@@ -90,34 +86,23 @@ module TestImpureCases =
reraise ()
[<TestCaseSource(nameof unimplemented)>]
[<Explicit "not yet implemented">]
let ``Can evaluate C# files, unimplemented`` (case : TestCase) : unit =
let source = Assembly.getEmbeddedResourceAsString case.FileName assy
let image = Roslyn.compile [ source ]
let messages, loggerFactory = LoggerFactory.makeTest ()
open Expecto
let dotnetRuntimes =
DotnetRuntime.SelectForDll assy.Location |> ImmutableArray.CreateRange
use peImage = new MemoryStream (image)
try
let terminalState, terminatingThread =
Program.run loggerFactory (Some case.FileName) peImage dotnetRuntimes case.NativeImpls []
let exitCode =
match terminalState.ThreadState.[terminatingThread].MethodState.EvaluationStack.Values with
| [] -> failwith "expected program to return a value, but it returned void"
| head :: _ ->
match head with
| EvalStackValue.Int32 i -> i
| ret -> failwith $"expected program to return an int, but it returned %O{ret}"
exitCode |> shouldEqual case.ExpectedReturnCode
with _ ->
for message in messages () do
System.Console.Error.WriteLine $"{message}"
reraise ()
[<Tests>]
let tests =
testList
"Impure cases"
[
testList
"Can evaluate C# files"
[
for case in cases do
testCase case.FileName (fun () -> runTest case)
]
ptestList
"Can evaluate C# files (unimplemented)"
[
for case in unimplemented do
testCase case.FileName (fun () -> runTest case)
]
]

View File

@@ -3,14 +3,11 @@ namespace WoofWare.Pawprint.Test
open System.Collections.Immutable
open System.IO
open FsUnitTyped
open NUnit.Framework
open WoofWare.DotnetRuntimeLocator
open WoofWare.PawPrint
open WoofWare.PawPrint.ExternImplementations
open WoofWare.PawPrint.Test
[<TestFixture>]
[<Parallelizable(ParallelScope.All)>]
module TestPureCases =
let assy = typeof<RunResult>.Assembly
@@ -232,8 +229,7 @@ module TestPureCases =
}
]
[<TestCaseSource(nameof cases)>]
let ``Can evaluate C# files`` (case : TestCase) : unit =
let runTest (case : TestCase) : unit =
let source = Assembly.getEmbeddedResourceAsString case.FileName assy
let image = Roslyn.compile [ source ]
let messages, loggerFactory = LoggerFactory.makeTest ()
@@ -274,34 +270,23 @@ module TestPureCases =
reraise ()
[<TestCaseSource(nameof unimplemented)>]
[<Explicit "not yet implemented">]
let ``Can evaluate C# files, unimplemented`` (case : TestCase) : unit =
let source = Assembly.getEmbeddedResourceAsString case.FileName assy
let image = Roslyn.compile [ source ]
let messages, loggerFactory = LoggerFactory.makeTest ()
open Expecto
let dotnetRuntimes =
DotnetRuntime.SelectForDll assy.Location |> ImmutableArray.CreateRange
use peImage = new MemoryStream (image)
try
let terminalState, terminatingThread =
Program.run loggerFactory (Some case.FileName) peImage dotnetRuntimes case.NativeImpls []
let exitCode =
match terminalState.ThreadState.[terminatingThread].MethodState.EvaluationStack.Values with
| [] -> failwith "expected program to return a value, but it returned void"
| head :: _ ->
match head with
| EvalStackValue.Int32 i -> i
| ret -> failwith $"expected program to return an int, but it returned %O{ret}"
exitCode |> shouldEqual case.ExpectedReturnCode
with _ ->
for message in messages () do
System.Console.Error.WriteLine $"{message}"
reraise ()
[<Tests>]
let tests =
testList
"Pure cases"
[
testList
"Can evaluate C# files"
[
for case in cases do
testCase case.FileName (fun () -> runTest case)
]
ptestList
"Can evaluate C# files (unimplemented)"
[
for case in unimplemented do
testCase case.FileName (fun () -> runTest case)
]
]

View File

@@ -4,9 +4,7 @@
<TargetFramework>net9.0</TargetFramework>
<IsPackable>false</IsPackable>
<OutputType>Exe</OutputType>
<IsTestProject>true</IsTestProject>
<TestingPlatformDotnetTestSupport>true</TestingPlatformDotnetTestSupport>
<EnableNUnitRunner>true</EnableNUnitRunner>
<GenerateProgramFile>false</GenerateProgramFile>
</PropertyGroup>
<ItemGroup>
@@ -17,6 +15,7 @@
<Compile Include="TestHarness.fs"/>
<Compile Include="TestPureCases.fs" />
<Compile Include="TestImpureCases.fs" />
<Compile Include="Program.fs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="sourcesPure\BasicLock.cs" />
@@ -51,13 +50,14 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Expecto" Version="10.2.3" />
<PackageReference Include="Expecto.Diff" Version="10.2.3" />
<PackageReference Include="FsUnit" Version="7.1.1"/>
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1"/>
<PackageReference Include="NUnit" Version="4.3.2"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0"/>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.6" />
<PackageReference Include="WoofWare.DotnetRuntimeLocator" Version="0.3.2"/>
<PackageReference Include="YoloDev.Expecto.TestSdk" Version="0.15.3" />
</ItemGroup>
</Project>

View File

@@ -1,4 +1,19 @@
[
{
"pname": "DiffPlex",
"version": "1.7.1",
"hash": "sha256-0kDBRvlMALkuE0G86ACEkZ4hNCeFwMmLPOvleMHJ6OA="
},
{
"pname": "Expecto",
"version": "10.2.3",
"hash": "sha256-klv7QbGwd0ClMSe+wJ38URVmIawV2ksyrjzQbGhVUPg="
},
{
"pname": "Expecto.Diff",
"version": "10.2.3",
"hash": "sha256-8WBr/2Ezit8RoOA7Pwrs50BDHkLe6a8lCkvzTcT/rkE="
},
{
"pname": "fantomas",
"version": "7.0.0",
@@ -171,33 +186,33 @@
},
{
"pname": "Microsoft.Testing.Extensions.Telemetry",
"version": "1.5.3",
"hash": "sha256-bIXwPSa3jkr2b6xINOqMUs6/uj/r4oVFM7xq3uVIZDU="
"version": "1.6.2",
"hash": "sha256-GZYUH+0vWAif5p4S81Oo9Tsez68ylsLjEQWhI2rgcik="
},
{
"pname": "Microsoft.Testing.Extensions.TrxReport.Abstractions",
"version": "1.5.3",
"hash": "sha256-IfMRfcyaIKEMRtx326ICKtinDBEfGw/Sv8ZHawJ96Yc="
"version": "1.6.2",
"hash": "sha256-UOpDBbW3xgahofRjqdw3wUj63zXVdkIAfUE7dA4uI4Y="
},
{
"pname": "Microsoft.Testing.Extensions.VSTestBridge",
"version": "1.5.3",
"hash": "sha256-XpM/yFjhLSsuzyDV+xKubs4V1zVVYiV05E0+N4S1h0g="
"version": "1.6.2",
"hash": "sha256-SWaSPgc2PQNqsz7ea/SQsmqOX6le6dmrDbwCHfbppZo="
},
{
"pname": "Microsoft.Testing.Platform",
"version": "1.5.3",
"hash": "sha256-y61Iih6w5D79dmrj2V675mcaeIiHoj1HSa1FRit2BLM="
"version": "1.6.2",
"hash": "sha256-RfdgATa3aTYLpGfv8ORI5uEP8dH87L5/gBDkxAG6ho4="
},
{
"pname": "Microsoft.Testing.Platform.MSBuild",
"version": "1.5.3",
"hash": "sha256-YspvjE5Jfi587TAfsvfDVJXNrFOkx1B3y1CKV6m7YLY="
"version": "1.6.2",
"hash": "sha256-bzlz10QeFrCAR+1og0gXbxSbpO64wCFrwAhBUwAp0nI="
},
{
"pname": "Microsoft.TestPlatform.ObjectModel",
"version": "17.12.0",
"hash": "sha256-3XBHBSuCxggAIlHXmKNQNlPqMqwFlM952Av6RrLw1/w="
"version": "17.13.0",
"hash": "sha256-6S0fjfj8vA+h6dJVNwLi6oZhYDO/I/6hBZaq2VTW+Uk="
},
{
"pname": "Microsoft.TestPlatform.ObjectModel",
@@ -209,6 +224,11 @@
"version": "17.14.1",
"hash": "sha256-1cxHWcvHRD7orQ3EEEPPxVGEkTpxom1/zoICC9SInJs="
},
{
"pname": "Mono.Cecil",
"version": "0.11.4",
"hash": "sha256-HrnRgFsOzfqAWw0fUxi/vkzZd8dMn5zueUeLQWA9qvs="
},
{
"pname": "Myriad.Core",
"version": "0.8.3",
@@ -231,13 +251,8 @@
},
{
"pname": "NUnit",
"version": "4.3.2",
"hash": "sha256-0RWe8uFoxYp6qhPlDDEghOMcKJgyw2ybvEoAqBLebeE="
},
{
"pname": "NUnit3TestAdapter",
"version": "5.0.0",
"hash": "sha256-7jZM4qAbIzne3AcdFfMbvbgogqpxvVe6q2S7Ls8xQy0="
"version": "4.0.1",
"hash": "sha256-jd1CD5nHVXkpvBNpVDJcJyfTggCHLyDBySVSvtrA8Uk="
},
{
"pname": "runtime.any.System.Runtime",
@@ -284,11 +299,6 @@
"version": "4.3.0",
"hash": "sha256-fVfgcoP4AVN1E5wHZbKBIOPYZ/xBeSIdsNF+bdukIRM="
},
{
"pname": "System.Reflection.Metadata",
"version": "1.6.0",
"hash": "sha256-JJfgaPav7UfEh4yRAQdGhLZF1brr0tUWPl6qmfNWq/E="
},
{
"pname": "System.Reflection.Metadata",
"version": "8.0.0",
@@ -328,5 +338,10 @@
"pname": "WoofWare.Whippet.Fantomas",
"version": "0.6.3",
"hash": "sha256-FkW/HtVp8/HE2k6d7yFpnJcnP3FNNe9kGlkoIWmNgDw="
},
{
"pname": "YoloDev.Expecto.TestSdk",
"version": "0.15.3",
"hash": "sha256-POsY+kmSMIGev1OzF6WYk0/sKKou8Z9f7C6r6Txk9aU="
}
]