From 2e9fdbed483bb6b683993684f953c1c1311c75f4 Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Sat, 30 Aug 2025 23:25:41 +0100 Subject: [PATCH] Make it much harder to omit tests (#127) --- CLAUDE.md | 5 +- WoofWare.PawPrint.Test/TestImpureCases.fs | 1 + WoofWare.PawPrint.Test/TestPureCases.fs | 254 ++++++++-------------- WoofWare.PawPrint/IlMachineState.fs | 2 +- 4 files changed, 98 insertions(+), 164 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index f470306..a7d408e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -67,13 +67,12 @@ dotnet run --project WoofWare.PawPrint.App/WoofWare.PawPrint.App.fsproj -- CShar - `Corelib.fs`: Core library type definitions (String, Array, etc.) **WoofWare.PawPrint.Test** -- Uses Expecto as the test framework +- Uses NUnit as the test framework - Test cases are defined in `TestPureCases.fs` and `TestImpureCases.fs` -- C# source files in `sources{Pure,Impure}/` 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; files in `sourcesPure` are automatically turned into test cases with no further action (see TestPureCases.fs for the mechanism) - `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` (note the additional `--`) - 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 diff --git a/WoofWare.PawPrint.Test/TestImpureCases.fs b/WoofWare.PawPrint.Test/TestImpureCases.fs index 35a4d12..8726134 100644 --- a/WoofWare.PawPrint.Test/TestImpureCases.fs +++ b/WoofWare.PawPrint.Test/TestImpureCases.fs @@ -1,5 +1,6 @@ namespace WoofWare.Pawprint.Test +open System open System.Collections.Immutable open System.IO open FsUnitTyped diff --git a/WoofWare.PawPrint.Test/TestPureCases.fs b/WoofWare.PawPrint.Test/TestPureCases.fs index 3f7aaa5..4705faf 100644 --- a/WoofWare.PawPrint.Test/TestPureCases.fs +++ b/WoofWare.PawPrint.Test/TestPureCases.fs @@ -1,5 +1,6 @@ namespace WoofWare.Pawprint.Test +open System open System.Collections.Immutable open System.IO open FsUnitTyped @@ -16,166 +17,65 @@ module TestPureCases = let unimplemented = [ - { - FileName = "CrossAssemblyTypes.cs" - ExpectedReturnCode = 0 - NativeImpls = MockEnv.make () - } - { - FileName = "OverlappingStructs.cs" - ExpectedReturnCode = 0 - NativeImpls = MockEnv.make () - } - { - FileName = "AdvancedStructLayout.cs" - ExpectedReturnCode = 0 - NativeImpls = MockEnv.make () - } - { - FileName = "InitializeArray.cs" - ExpectedReturnCode = 0 - NativeImpls = MockEnv.make () - } - { - FileName = "Threads.cs" - ExpectedReturnCode = 3 - NativeImpls = MockEnv.make () - } - { - FileName = "ComplexTryCatch.cs" - ExpectedReturnCode = 14 - NativeImpls = NativeImpls.PassThru () - } - { - FileName = "ResizeArray.cs" - ExpectedReturnCode = 114 - NativeImpls = MockEnv.make () - } - { - FileName = "LdtokenField.cs" - ExpectedReturnCode = 0 - NativeImpls = MockEnv.make () - } - { - FileName = "GenericEdgeCases.cs" - ExpectedReturnCode = 0 - NativeImpls = MockEnv.make () - } - { - FileName = "UnsafeAs.cs" - ExpectedReturnCode = 0 - NativeImpls = MockEnv.make () - } + "CrossAssemblyTypes.cs" + "OverlappingStructs.cs" + "AdvancedStructLayout.cs" + "InitializeArray.cs" + "Threads.cs" + "ComplexTryCatch.cs" + "ResizeArray.cs" + "LdtokenField.cs" + "GenericEdgeCases.cs" + "UnsafeAs.cs" ] + |> Set.ofList + + let requiresMocks = + let empty = MockEnv.make () - let cases : EndToEndTestCase list = [ - { - FileName = "NoOp.cs" - ExpectedReturnCode = 1 - NativeImpls = MockEnv.make () - } - { - FileName = "Sizeof.cs" - ExpectedReturnCode = 0 - NativeImpls = MockEnv.make () - } - { - FileName = "Sizeof2.cs" - ExpectedReturnCode = 0 - NativeImpls = MockEnv.make () - } - { - FileName = "Initobj.cs" - ExpectedReturnCode = 0 - NativeImpls = MockEnv.make () - } - { - FileName = "TestShl.cs" - ExpectedReturnCode = 0 - NativeImpls = MockEnv.make () - } - { - FileName = "TestShr.cs" - ExpectedReturnCode = 0 - NativeImpls = MockEnv.make () - } - { - FileName = "StaticVariables.cs" - ExpectedReturnCode = 0 - NativeImpls = MockEnv.make () - } - { - FileName = "Ldind.cs" - ExpectedReturnCode = 0 - NativeImpls = MockEnv.make () - } - { - FileName = "CustomDelegate.cs" - ExpectedReturnCode = 8 - NativeImpls = MockEnv.make () - } - { - FileName = "ArgumentOrdering.cs" - ExpectedReturnCode = 0 - NativeImpls = MockEnv.make () - } - { - FileName = "BasicLock.cs" - ExpectedReturnCode = 1 - NativeImpls = - let mock = MockEnv.make () - - { mock with - System_Threading_Monitor = System_Threading_Monitor.passThru - } - } - { - FileName = "TriangleNumber.cs" - ExpectedReturnCode = 10 - NativeImpls = MockEnv.make () - } - { - FileName = "ExceptionWithNoOpFinally.cs" - ExpectedReturnCode = 3 - NativeImpls = MockEnv.make () - } - { - FileName = "ExceptionWithNoOpCatch.cs" - ExpectedReturnCode = 10 - NativeImpls = MockEnv.make () - } - { - FileName = "Floats.cs" - ExpectedReturnCode = 0 - NativeImpls = MockEnv.make () - } - { - FileName = "TryCatchWithThrowInBody.cs" - ExpectedReturnCode = 4 - NativeImpls = MockEnv.make () - } - { - FileName = "Ldelema.cs" - ExpectedReturnCode = 0 - NativeImpls = MockEnv.make () - } - { - FileName = "TypeConcretization.cs" - ExpectedReturnCode = 0 - NativeImpls = MockEnv.make () - } - { - FileName = "TestOr.cs" - ExpectedReturnCode = 0 - NativeImpls = MockEnv.make () - } - { - FileName = "InterfaceDispatch.cs" - ExpectedReturnCode = 0 - NativeImpls = MockEnv.make () - } + "BasicLock.cs", + (1, + { empty with + System_Threading_Monitor = System_Threading_Monitor.passThru + }) ] + |> Map.ofList + + let customExitCodes = + [ + "NoOp.cs", 1 + "CustomDelegate.cs", 8 + "ExceptionWithNoOpFinally.cs", 3 + "ExceptionWithNoOpCatch.cs", 10 + "TryCatchWithThrowInBody.cs", 4 + "ResizeArray.cs", 114 + "Threads.cs", 3 + "TriangleNumber.cs", 10 + ] + |> Map.ofList + + let allPure = + assy.GetManifestResourceNames () + |> Seq.choose (fun res -> + let s = "WoofWare.PawPrint.Test.sourcesPure." + + if res.StartsWith (s, StringComparison.OrdinalIgnoreCase) then + res.Substring s.Length |> Some + else + None + ) + |> Set.ofSeq + + let simpleCases : string list = + allPure + |> Seq.filter (fun s -> + (customExitCodes.ContainsKey s + || requiresMocks.ContainsKey s + || unimplemented.Contains s) + |> not + ) + |> Seq.toList let runTest (case : EndToEndTestCase) : unit = let source = Assembly.getEmbeddedResourceAsString case.FileName assy @@ -210,9 +110,43 @@ module TestPureCases = reraise () + [] + let ``Standard tests`` (fileName : string) = + { + FileName = fileName + ExpectedReturnCode = 0 + NativeImpls = MockEnv.make () + } + |> runTest + + [] + let ``Custom exit code tests`` (KeyValue (fileName : string, exitCode : int)) = + if unimplemented.Contains fileName then + Assert.Inconclusive () + + { + FileName = fileName + ExpectedReturnCode = exitCode + NativeImpls = MockEnv.make () + } + |> runTest + + [] + let ``Tests which require mocks`` (KeyValue (fileName : string, (exitCode : int, mock : NativeImpls))) = + { + FileName = fileName + ExpectedReturnCode = exitCode + NativeImpls = mock + } + |> runTest + + [] [] - let ``Can evaluate C# files, unimplemented`` (case : EndToEndTestCase) = runTest case - - [] - let ``Can evaluate C# files`` (case : EndToEndTestCase) = runTest case + let ``Can evaluate C# files, unimplemented`` (fileName : string) = + { + FileName = fileName + ExpectedReturnCode = 0 + NativeImpls = MockEnv.make () + } + |> runTest diff --git a/WoofWare.PawPrint/IlMachineState.fs b/WoofWare.PawPrint/IlMachineState.fs index a209f03..34b183a 100644 --- a/WoofWare.PawPrint/IlMachineState.fs +++ b/WoofWare.PawPrint/IlMachineState.fs @@ -1693,7 +1693,7 @@ module IlMachineState = | Some ty -> ty | None -> failwith "not concretised type" - failwith "TODO" + failwith $"TODO: interpret as type %s{ty.Assembly.Name}.%s{ty.Namespace}.%s{ty.Name}, object %O{src}" let lookupTypeDefn (baseClassTypes : BaseClassTypes)