diff --git a/.gitignore b/.gitignore index 1766714..237c136 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ result .analyzerpackages/ analysis.sarif .direnv/ +TestResults/ Generated*.fs diff --git a/TestRunner.Lib/Result.fs b/TestRunner.Lib/Result.fs new file mode 100644 index 0000000..3d385e7 --- /dev/null +++ b/TestRunner.Lib/Result.fs @@ -0,0 +1,30 @@ +namespace TestRunner + +[] +module internal Result = + + let inline getError<'r, 'e> (r : Result<'r, 'e>) : 'e option = + match r with + | Ok _ -> None + | Error e -> Some e + + let inline get<'r, 'e> (r : Result<'r, 'e>) : 'r option = + match r with + | Ok r -> Some r + | Error _ -> None + + let allOkOrError<'o, 'e> (a : Result<'o, 'e> list) : Result<'o list, 'o list * 'e list> = + let oks = ResizeArray () + let errors = ResizeArray () + + for i in a do + match i with + | Error e -> errors.Add e + | Ok o -> oks.Add o + + let oks = oks |> Seq.toList + + if errors.Count = 0 then + Ok oks + else + Error (oks, Seq.toList errors) diff --git a/TestRunner.Lib/SurfaceBaseline.txt b/TestRunner.Lib/SurfaceBaseline.txt index e339479..dc20e38 100644 --- a/TestRunner.Lib/SurfaceBaseline.txt +++ b/TestRunner.Lib/SurfaceBaseline.txt @@ -253,6 +253,224 @@ TestRunner.TestMemberSuccess.Ok [static property]: [read-only] TestRunner.TestMe TestRunner.TestMemberSuccess.Tag [property]: [read-only] int TestRunner.TestProgress inherit obj TestRunner.TestProgress.toStderr [static method]: unit -> TestRunner.ITestProgress +TestRunner.TrxCounters inherit obj, implements TestRunner.TrxCounters System.IEquatable, System.Collections.IStructuralEquatable, TestRunner.TrxCounters System.IComparable, System.IComparable, System.Collections.IStructuralComparable +TestRunner.TrxCounters..ctor [constructor]: (System.UInt32, System.UInt32, System.UInt32, System.UInt32, System.UInt32, System.UInt32, System.UInt32, System.UInt32, System.UInt32, System.UInt32, System.UInt32, System.UInt32, System.UInt32, System.UInt32, System.UInt32, System.UInt32) +TestRunner.TrxCounters.Aborted [property]: [read-only] System.UInt32 +TestRunner.TrxCounters.Completed [property]: [read-only] System.UInt32 +TestRunner.TrxCounters.Disconnected [property]: [read-only] System.UInt32 +TestRunner.TrxCounters.Errors [property]: [read-only] System.UInt32 +TestRunner.TrxCounters.Executed [property]: [read-only] System.UInt32 +TestRunner.TrxCounters.Failed [property]: [read-only] System.UInt32 +TestRunner.TrxCounters.get_Aborted [method]: unit -> System.UInt32 +TestRunner.TrxCounters.get_Completed [method]: unit -> System.UInt32 +TestRunner.TrxCounters.get_Disconnected [method]: unit -> System.UInt32 +TestRunner.TrxCounters.get_Errors [method]: unit -> System.UInt32 +TestRunner.TrxCounters.get_Executed [method]: unit -> System.UInt32 +TestRunner.TrxCounters.get_Failed [method]: unit -> System.UInt32 +TestRunner.TrxCounters.get_Inconclusive [method]: unit -> System.UInt32 +TestRunner.TrxCounters.get_InProgress [method]: unit -> System.UInt32 +TestRunner.TrxCounters.get_NotExecuted [method]: unit -> System.UInt32 +TestRunner.TrxCounters.get_NotRunnable [method]: unit -> System.UInt32 +TestRunner.TrxCounters.get_Passed [method]: unit -> System.UInt32 +TestRunner.TrxCounters.get_PassedButRunAborted [method]: unit -> System.UInt32 +TestRunner.TrxCounters.get_Pending [method]: unit -> System.UInt32 +TestRunner.TrxCounters.get_Timeout [method]: unit -> System.UInt32 +TestRunner.TrxCounters.get_Total [method]: unit -> System.UInt32 +TestRunner.TrxCounters.get_Warning [method]: unit -> System.UInt32 +TestRunner.TrxCounters.get_Zero [static method]: unit -> TestRunner.TrxCounters +TestRunner.TrxCounters.Inconclusive [property]: [read-only] System.UInt32 +TestRunner.TrxCounters.InProgress [property]: [read-only] System.UInt32 +TestRunner.TrxCounters.NotExecuted [property]: [read-only] System.UInt32 +TestRunner.TrxCounters.NotRunnable [property]: [read-only] System.UInt32 +TestRunner.TrxCounters.Passed [property]: [read-only] System.UInt32 +TestRunner.TrxCounters.PassedButRunAborted [property]: [read-only] System.UInt32 +TestRunner.TrxCounters.Pending [property]: [read-only] System.UInt32 +TestRunner.TrxCounters.Timeout [property]: [read-only] System.UInt32 +TestRunner.TrxCounters.Total [property]: [read-only] System.UInt32 +TestRunner.TrxCounters.Warning [property]: [read-only] System.UInt32 +TestRunner.TrxCounters.Zero [static property]: [read-only] TestRunner.TrxCounters +TestRunner.TrxDeployment inherit obj, implements TestRunner.TrxDeployment System.IEquatable, System.Collections.IStructuralEquatable, TestRunner.TrxDeployment System.IComparable, System.IComparable, System.Collections.IStructuralComparable +TestRunner.TrxDeployment..ctor [constructor]: string +TestRunner.TrxDeployment.get_RunDeploymentRoot [method]: unit -> string +TestRunner.TrxDeployment.RunDeploymentRoot [property]: [read-only] string +TestRunner.TrxErrorInfo inherit obj, implements TestRunner.TrxErrorInfo System.IEquatable, System.Collections.IStructuralEquatable, TestRunner.TrxErrorInfo System.IComparable, System.IComparable, System.Collections.IStructuralComparable +TestRunner.TrxErrorInfo..ctor [constructor]: (string option, string option) +TestRunner.TrxErrorInfo.get_Message [method]: unit -> string option +TestRunner.TrxErrorInfo.get_StackTrace [method]: unit -> string option +TestRunner.TrxErrorInfo.Message [property]: [read-only] string option +TestRunner.TrxErrorInfo.StackTrace [property]: [read-only] string option +TestRunner.TrxExecution inherit obj, implements TestRunner.TrxExecution System.IEquatable, System.Collections.IStructuralEquatable, TestRunner.TrxExecution System.IComparable, System.IComparable, System.Collections.IStructuralComparable +TestRunner.TrxExecution..ctor [constructor]: System.Guid +TestRunner.TrxExecution.get_Id [method]: unit -> System.Guid +TestRunner.TrxExecution.Id [property]: [read-only] System.Guid +TestRunner.TrxOutcome inherit obj, implements TestRunner.TrxOutcome System.IEquatable, System.Collections.IStructuralEquatable, TestRunner.TrxOutcome System.IComparable, System.IComparable, System.Collections.IStructuralComparable - union type with 3 cases +TestRunner.TrxOutcome+Tags inherit obj +TestRunner.TrxOutcome+Tags.Completed [static field]: int = 0 +TestRunner.TrxOutcome+Tags.Failed [static field]: int = 2 +TestRunner.TrxOutcome+Tags.Warning [static field]: int = 1 +TestRunner.TrxOutcome.Completed [static property]: [read-only] TestRunner.TrxOutcome +TestRunner.TrxOutcome.Failed [static property]: [read-only] TestRunner.TrxOutcome +TestRunner.TrxOutcome.get_Completed [static method]: unit -> TestRunner.TrxOutcome +TestRunner.TrxOutcome.get_Failed [static method]: unit -> TestRunner.TrxOutcome +TestRunner.TrxOutcome.get_IsCompleted [method]: unit -> bool +TestRunner.TrxOutcome.get_IsFailed [method]: unit -> bool +TestRunner.TrxOutcome.get_IsWarning [method]: unit -> bool +TestRunner.TrxOutcome.get_Tag [method]: unit -> int +TestRunner.TrxOutcome.get_Warning [static method]: unit -> TestRunner.TrxOutcome +TestRunner.TrxOutcome.IsCompleted [property]: [read-only] bool +TestRunner.TrxOutcome.IsFailed [property]: [read-only] bool +TestRunner.TrxOutcome.IsWarning [property]: [read-only] bool +TestRunner.TrxOutcome.Parse [static method]: string -> TestRunner.TrxOutcome option +TestRunner.TrxOutcome.Tag [property]: [read-only] int +TestRunner.TrxOutcome.Warning [static property]: [read-only] TestRunner.TrxOutcome +TestRunner.TrxOutput inherit obj, implements TestRunner.TrxOutput System.IEquatable, System.Collections.IStructuralEquatable, TestRunner.TrxOutput System.IComparable, System.IComparable, System.Collections.IStructuralComparable +TestRunner.TrxOutput..ctor [constructor]: (string option, TestRunner.TrxErrorInfo option) +TestRunner.TrxOutput.ErrorInfo [property]: [read-only] TestRunner.TrxErrorInfo option +TestRunner.TrxOutput.get_ErrorInfo [method]: unit -> TestRunner.TrxErrorInfo option +TestRunner.TrxOutput.get_StdOut [method]: unit -> string option +TestRunner.TrxOutput.StdOut [property]: [read-only] string option +TestRunner.TrxReport inherit obj, implements TestRunner.TrxReport System.IEquatable, System.Collections.IStructuralEquatable +TestRunner.TrxReport..ctor [constructor]: (System.Guid, string, TestRunner.TrxReportTimes, TestRunner.TrxTestSettings, TestRunner.TrxUnitTestResult list, TestRunner.TrxUnitTest list, TestRunner.TrxTestEntry list, TestRunner.TrxTestListEntry list, TestRunner.TrxResultsSummary) +TestRunner.TrxReport.get_Id [method]: unit -> System.Guid +TestRunner.TrxReport.get_Name [method]: unit -> string +TestRunner.TrxReport.get_Results [method]: unit -> TestRunner.TrxUnitTestResult list +TestRunner.TrxReport.get_ResultsSummary [method]: unit -> TestRunner.TrxResultsSummary +TestRunner.TrxReport.get_Settings [method]: unit -> TestRunner.TrxTestSettings +TestRunner.TrxReport.get_TestDefinitions [method]: unit -> TestRunner.TrxUnitTest list +TestRunner.TrxReport.get_TestEntries [method]: unit -> TestRunner.TrxTestEntry list +TestRunner.TrxReport.get_TestLists [method]: unit -> TestRunner.TrxTestListEntry list +TestRunner.TrxReport.get_Times [method]: unit -> TestRunner.TrxReportTimes +TestRunner.TrxReport.Id [property]: [read-only] System.Guid +TestRunner.TrxReport.Name [property]: [read-only] string +TestRunner.TrxReport.Results [property]: [read-only] TestRunner.TrxUnitTestResult list +TestRunner.TrxReport.ResultsSummary [property]: [read-only] TestRunner.TrxResultsSummary +TestRunner.TrxReport.Settings [property]: [read-only] TestRunner.TrxTestSettings +TestRunner.TrxReport.TestDefinitions [property]: [read-only] TestRunner.TrxUnitTest list +TestRunner.TrxReport.TestEntries [property]: [read-only] TestRunner.TrxTestEntry list +TestRunner.TrxReport.TestLists [property]: [read-only] TestRunner.TrxTestListEntry list +TestRunner.TrxReport.Times [property]: [read-only] TestRunner.TrxReportTimes +TestRunner.TrxReportModule inherit obj +TestRunner.TrxReportModule.parse [static method]: string -> Microsoft.FSharp.Core.FSharpResult +TestRunner.TrxReportTimes inherit obj, implements TestRunner.TrxReportTimes System.IEquatable, System.Collections.IStructuralEquatable, TestRunner.TrxReportTimes System.IComparable, System.IComparable, System.Collections.IStructuralComparable +TestRunner.TrxReportTimes..ctor [constructor]: (System.DateTimeOffset, System.DateTimeOffset, System.DateTimeOffset, System.DateTimeOffset) +TestRunner.TrxReportTimes.Creation [property]: [read-only] System.DateTimeOffset +TestRunner.TrxReportTimes.Finish [property]: [read-only] System.DateTimeOffset +TestRunner.TrxReportTimes.get_Creation [method]: unit -> System.DateTimeOffset +TestRunner.TrxReportTimes.get_Finish [method]: unit -> System.DateTimeOffset +TestRunner.TrxReportTimes.get_Queuing [method]: unit -> System.DateTimeOffset +TestRunner.TrxReportTimes.get_Start [method]: unit -> System.DateTimeOffset +TestRunner.TrxReportTimes.Queuing [property]: [read-only] System.DateTimeOffset +TestRunner.TrxReportTimes.Start [property]: [read-only] System.DateTimeOffset +TestRunner.TrxResultsSummary inherit obj, implements TestRunner.TrxResultsSummary System.IEquatable, System.Collections.IStructuralEquatable, TestRunner.TrxResultsSummary System.IComparable, System.IComparable, System.Collections.IStructuralComparable +TestRunner.TrxResultsSummary..ctor [constructor]: (TestRunner.TrxOutcome, TestRunner.TrxCounters, TestRunner.TrxOutput, TestRunner.TrxRunInfo list) +TestRunner.TrxResultsSummary.Counters [property]: [read-only] TestRunner.TrxCounters +TestRunner.TrxResultsSummary.get_Counters [method]: unit -> TestRunner.TrxCounters +TestRunner.TrxResultsSummary.get_Outcome [method]: unit -> TestRunner.TrxOutcome +TestRunner.TrxResultsSummary.get_Output [method]: unit -> TestRunner.TrxOutput +TestRunner.TrxResultsSummary.get_RunInfos [method]: unit -> TestRunner.TrxRunInfo list +TestRunner.TrxResultsSummary.Outcome [property]: [read-only] TestRunner.TrxOutcome +TestRunner.TrxResultsSummary.Output [property]: [read-only] TestRunner.TrxOutput +TestRunner.TrxResultsSummary.RunInfos [property]: [read-only] TestRunner.TrxRunInfo list +TestRunner.TrxRunInfo inherit obj, implements TestRunner.TrxRunInfo System.IEquatable, System.Collections.IStructuralEquatable, TestRunner.TrxRunInfo System.IComparable, System.IComparable, System.Collections.IStructuralComparable +TestRunner.TrxRunInfo..ctor [constructor]: (string, TestRunner.TrxOutcome, System.DateTimeOffset, string) +TestRunner.TrxRunInfo.ComputerName [property]: [read-only] string +TestRunner.TrxRunInfo.get_ComputerName [method]: unit -> string +TestRunner.TrxRunInfo.get_Outcome [method]: unit -> TestRunner.TrxOutcome +TestRunner.TrxRunInfo.get_Text [method]: unit -> string +TestRunner.TrxRunInfo.get_Timestamp [method]: unit -> System.DateTimeOffset +TestRunner.TrxRunInfo.Outcome [property]: [read-only] TestRunner.TrxOutcome +TestRunner.TrxRunInfo.Text [property]: [read-only] string +TestRunner.TrxRunInfo.Timestamp [property]: [read-only] System.DateTimeOffset +TestRunner.TrxTestEntry inherit obj, implements TestRunner.TrxTestEntry System.IEquatable, System.Collections.IStructuralEquatable, TestRunner.TrxTestEntry System.IComparable, System.IComparable, System.Collections.IStructuralComparable +TestRunner.TrxTestEntry..ctor [constructor]: (System.Guid, System.Guid, System.Guid) +TestRunner.TrxTestEntry.ExecutionId [property]: [read-only] System.Guid +TestRunner.TrxTestEntry.get_ExecutionId [method]: unit -> System.Guid +TestRunner.TrxTestEntry.get_TestId [method]: unit -> System.Guid +TestRunner.TrxTestEntry.get_TestListId [method]: unit -> System.Guid +TestRunner.TrxTestEntry.TestId [property]: [read-only] System.Guid +TestRunner.TrxTestEntry.TestListId [property]: [read-only] System.Guid +TestRunner.TrxTestListEntry inherit obj, implements TestRunner.TrxTestListEntry System.IEquatable, System.Collections.IStructuralEquatable, TestRunner.TrxTestListEntry System.IComparable, System.IComparable, System.Collections.IStructuralComparable +TestRunner.TrxTestListEntry..ctor [constructor]: (string, System.Guid) +TestRunner.TrxTestListEntry.get_Id [method]: unit -> System.Guid +TestRunner.TrxTestListEntry.get_Name [method]: unit -> string +TestRunner.TrxTestListEntry.Id [property]: [read-only] System.Guid +TestRunner.TrxTestListEntry.Name [property]: [read-only] string +TestRunner.TrxTestMethod inherit obj, implements TestRunner.TrxTestMethod System.IEquatable, System.Collections.IStructuralEquatable +TestRunner.TrxTestMethod..ctor [constructor]: (string, System.Uri, string, string) +TestRunner.TrxTestMethod.AdapterTypeName [property]: [read-only] System.Uri +TestRunner.TrxTestMethod.ClassName [property]: [read-only] string +TestRunner.TrxTestMethod.CodeBase [property]: [read-only] string +TestRunner.TrxTestMethod.get_AdapterTypeName [method]: unit -> System.Uri +TestRunner.TrxTestMethod.get_ClassName [method]: unit -> string +TestRunner.TrxTestMethod.get_CodeBase [method]: unit -> string +TestRunner.TrxTestMethod.get_Name [method]: unit -> string +TestRunner.TrxTestMethod.Name [property]: [read-only] string +TestRunner.TrxTestOutcome inherit obj, implements TestRunner.TrxTestOutcome System.IEquatable, System.Collections.IStructuralEquatable, TestRunner.TrxTestOutcome System.IComparable, System.IComparable, System.Collections.IStructuralComparable - union type with 3 cases +TestRunner.TrxTestOutcome+Tags inherit obj +TestRunner.TrxTestOutcome+Tags.Failed [static field]: int = 1 +TestRunner.TrxTestOutcome+Tags.NotExecuted [static field]: int = 2 +TestRunner.TrxTestOutcome+Tags.Passed [static field]: int = 0 +TestRunner.TrxTestOutcome.Failed [static property]: [read-only] TestRunner.TrxTestOutcome +TestRunner.TrxTestOutcome.get_Failed [static method]: unit -> TestRunner.TrxTestOutcome +TestRunner.TrxTestOutcome.get_IsFailed [method]: unit -> bool +TestRunner.TrxTestOutcome.get_IsNotExecuted [method]: unit -> bool +TestRunner.TrxTestOutcome.get_IsPassed [method]: unit -> bool +TestRunner.TrxTestOutcome.get_NotExecuted [static method]: unit -> TestRunner.TrxTestOutcome +TestRunner.TrxTestOutcome.get_Passed [static method]: unit -> TestRunner.TrxTestOutcome +TestRunner.TrxTestOutcome.get_Tag [method]: unit -> int +TestRunner.TrxTestOutcome.IsFailed [property]: [read-only] bool +TestRunner.TrxTestOutcome.IsNotExecuted [property]: [read-only] bool +TestRunner.TrxTestOutcome.IsPassed [property]: [read-only] bool +TestRunner.TrxTestOutcome.NotExecuted [static property]: [read-only] TestRunner.TrxTestOutcome +TestRunner.TrxTestOutcome.Parse [static method]: string -> TestRunner.TrxTestOutcome option +TestRunner.TrxTestOutcome.Passed [static property]: [read-only] TestRunner.TrxTestOutcome +TestRunner.TrxTestOutcome.Tag [property]: [read-only] int +TestRunner.TrxTestSettings inherit obj, implements TestRunner.TrxTestSettings System.IEquatable, System.Collections.IStructuralEquatable, TestRunner.TrxTestSettings System.IComparable, System.IComparable, System.Collections.IStructuralComparable +TestRunner.TrxTestSettings..ctor [constructor]: (string, System.Guid, TestRunner.TrxDeployment) +TestRunner.TrxTestSettings.Deployment [property]: [read-only] TestRunner.TrxDeployment +TestRunner.TrxTestSettings.get_Deployment [method]: unit -> TestRunner.TrxDeployment +TestRunner.TrxTestSettings.get_Id [method]: unit -> System.Guid +TestRunner.TrxTestSettings.get_Name [method]: unit -> string +TestRunner.TrxTestSettings.Id [property]: [read-only] System.Guid +TestRunner.TrxTestSettings.Name [property]: [read-only] string +TestRunner.TrxUnitTest inherit obj, implements TestRunner.TrxUnitTest System.IEquatable, System.Collections.IStructuralEquatable +TestRunner.TrxUnitTest..ctor [constructor]: (string, string, System.Guid, TestRunner.TrxExecution, TestRunner.TrxTestMethod) +TestRunner.TrxUnitTest.Execution [property]: [read-only] TestRunner.TrxExecution +TestRunner.TrxUnitTest.get_Execution [method]: unit -> TestRunner.TrxExecution +TestRunner.TrxUnitTest.get_Id [method]: unit -> System.Guid +TestRunner.TrxUnitTest.get_Name [method]: unit -> string +TestRunner.TrxUnitTest.get_Storage [method]: unit -> string +TestRunner.TrxUnitTest.get_TestMethod [method]: unit -> TestRunner.TrxTestMethod +TestRunner.TrxUnitTest.Id [property]: [read-only] System.Guid +TestRunner.TrxUnitTest.Name [property]: [read-only] string +TestRunner.TrxUnitTest.Storage [property]: [read-only] string +TestRunner.TrxUnitTest.TestMethod [property]: [read-only] TestRunner.TrxTestMethod +TestRunner.TrxUnitTestResult inherit obj, implements TestRunner.TrxUnitTestResult System.IEquatable, System.Collections.IStructuralEquatable, TestRunner.TrxUnitTestResult System.IComparable, System.IComparable, System.Collections.IStructuralComparable +TestRunner.TrxUnitTestResult..ctor [constructor]: (System.Guid, System.Guid, string, string, System.TimeSpan, System.DateTimeOffset, System.DateTimeOffset, System.Guid, TestRunner.TrxTestOutcome, System.Guid, string, TestRunner.TrxOutput option) +TestRunner.TrxUnitTestResult.ComputerName [property]: [read-only] string +TestRunner.TrxUnitTestResult.Duration [property]: [read-only] System.TimeSpan +TestRunner.TrxUnitTestResult.EndTime [property]: [read-only] System.DateTimeOffset +TestRunner.TrxUnitTestResult.ExecutionId [property]: [read-only] System.Guid +TestRunner.TrxUnitTestResult.get_ComputerName [method]: unit -> string +TestRunner.TrxUnitTestResult.get_Duration [method]: unit -> System.TimeSpan +TestRunner.TrxUnitTestResult.get_EndTime [method]: unit -> System.DateTimeOffset +TestRunner.TrxUnitTestResult.get_ExecutionId [method]: unit -> System.Guid +TestRunner.TrxUnitTestResult.get_Outcome [method]: unit -> TestRunner.TrxTestOutcome +TestRunner.TrxUnitTestResult.get_Output [method]: unit -> TestRunner.TrxOutput option +TestRunner.TrxUnitTestResult.get_RelativeResultsDirectory [method]: unit -> string +TestRunner.TrxUnitTestResult.get_StartTime [method]: unit -> System.DateTimeOffset +TestRunner.TrxUnitTestResult.get_TestId [method]: unit -> System.Guid +TestRunner.TrxUnitTestResult.get_TestListId [method]: unit -> System.Guid +TestRunner.TrxUnitTestResult.get_TestName [method]: unit -> string +TestRunner.TrxUnitTestResult.get_TestType [method]: unit -> System.Guid +TestRunner.TrxUnitTestResult.Outcome [property]: [read-only] TestRunner.TrxTestOutcome +TestRunner.TrxUnitTestResult.Output [property]: [read-only] TestRunner.TrxOutput option +TestRunner.TrxUnitTestResult.RelativeResultsDirectory [property]: [read-only] string +TestRunner.TrxUnitTestResult.StartTime [property]: [read-only] System.DateTimeOffset +TestRunner.TrxUnitTestResult.TestId [property]: [read-only] System.Guid +TestRunner.TrxUnitTestResult.TestListId [property]: [read-only] System.Guid +TestRunner.TrxUnitTestResult.TestName [property]: [read-only] string +TestRunner.TrxUnitTestResult.TestType [property]: [read-only] System.Guid TestRunner.UserMethodFailure inherit obj, implements TestRunner.UserMethodFailure System.IEquatable, System.Collections.IStructuralEquatable - union type with 2 cases TestRunner.UserMethodFailure+ReturnedNonUnit inherit TestRunner.UserMethodFailure TestRunner.UserMethodFailure+ReturnedNonUnit.get_name [method]: unit -> string diff --git a/TestRunner.Lib/TestRunner.Lib.fsproj b/TestRunner.Lib/TestRunner.Lib.fsproj index 0c609bb..764fe46 100644 --- a/TestRunner.Lib/TestRunner.Lib.fsproj +++ b/TestRunner.Lib/TestRunner.Lib.fsproj @@ -20,11 +20,14 @@ + + + True \ diff --git a/TestRunner.Lib/TrxReport.fs b/TestRunner.Lib/TrxReport.fs new file mode 100644 index 0000000..64b467d --- /dev/null +++ b/TestRunner.Lib/TrxReport.fs @@ -0,0 +1,1277 @@ +namespace TestRunner + +open System +open System.Xml + +/// Describes the times at which a complete test run went through state transitions. +/// These all have semantics specific to the test runner, and I have not rigorously worked out what +/// semantics NUnit has, so take these with considerable amounts of salt. +type TrxReportTimes = + { + /// The time at which this test run was instantiated. (Note that this is *not* what NUnit does. + /// I don't know what NUnit does, but it's not this.) + Creation : DateTimeOffset + /// Don't know what this means! But it's emitted in `dotnet test --logger trx`. + Queuing : DateTimeOffset + /// The time at which the first test in this run began. (Note that this is *not* what NUnit does. + /// I don't know what NUnit does, but it's not this.) + Start : DateTimeOffset + /// The time at which this test run finished. + Finish : DateTimeOffset + } + + static member internal ofXml (node : XmlNode) : Result = + if node.HasChildNodes then + Error "expected to have no child nodes" + else + + let creation = + match node.Attributes.["creation"] with + | null -> Error "got no creation node" + | v -> + match DateTimeOffset.TryParse v.Value with + | false, _ -> Error $"could not parse '%s{v.Value}' as a creation time" + | true, t -> Ok t + + let queuing = + match node.Attributes.["queuing"] with + | null -> Error "got no queuing node" + | v -> + match DateTimeOffset.TryParse v.Value with + | false, _ -> Error $"could not parse '%s{v.Value}' as a queuing time" + | true, t -> Ok t + + let start = + match node.Attributes.["start"] with + | null -> Error "got no start node" + | v -> + match DateTimeOffset.TryParse v.Value with + | false, _ -> Error $"could not parse '%s{v.Value}' as a start time" + | true, t -> Ok t + + let finish = + match node.Attributes.["finish"] with + | null -> Error "got no finish node" + | v -> + match DateTimeOffset.TryParse v.Value with + | false, _ -> Error $"could not parse '%s{v.Value}' as a finish time" + | true, t -> Ok t + + match creation, queuing, start, finish with + | Ok creation, Ok queuing, Ok start, Ok finish -> + { + Creation = creation + Queuing = queuing + Start = start + Finish = finish + } + |> Ok + | _ -> + [ creation ; queuing ; start ; finish ] + |> Seq.choose ( + function + | Ok _ -> None + | Error e -> Some e + ) + |> String.concat "; " + |> Error + +/// Information about this particular instance of the test runner in space and time. +type TrxDeployment = + { + /// Identifier saying where and when this instance of the runner took place. + /// Nothing else cares about this value; it's for the human's benefit. + RunDeploymentRoot : string + } + + static member internal ofXml (node : XmlNode) : Result = + if node.HasChildNodes then + Error "Expected node to have no child nodes" + else + + match node.Attributes.["runDeploymentRoot"] with + | null -> Error "got no runDeploymentRoot node" + | v -> + { + RunDeploymentRoot = v.Value + } + |> Ok + +/// Don't really know what this is; I'm guessing it identifies a configuration of one of the various test runners +/// that may have taken part in this test run? +type TrxTestSettings = + { + /// Name of one particular configuration of a test runner, human-readable. + Name : string + /// Machine-readable ID of this particular configuration of a test runner. + Id : Guid + /// Data about how this particular instance of the test runner was deployed. + Deployment : TrxDeployment + } + + static member internal ofXml (node : XmlNode) : Result = + let name = + match node.Attributes.["name"] with + | null -> Error "expected a name field on TestSettings" + | v -> Ok v.Value + + let guid = + match node.Attributes.["id"] with + | null -> Error "expected an id field on TestSettings" + | v -> + match Guid.TryParse v.Value with + | true, v -> Ok v + | false, _ -> Error "could not parse id field as a GUID" + + let deployment = + match node with + | NodeWithNamedChild "Deployment" v -> TrxDeployment.ofXml v + | _ -> Error "expected a child of TestSettings" + + match name, guid, deployment with + | Ok name, Ok guid, Ok deployment -> + { + Name = name + Id = guid + Deployment = deployment + } + |> Ok + | _ -> + [ Result.getError name ; Result.getError guid ; Result.getError deployment ] + |> Seq.choose id + |> String.concat "; " + |> Error + +/// The outcome of a specific single test. +type TrxTestOutcome = + /// The test passed. + | Passed + /// The test failed. + | Failed + /// The test was not executed. + | NotExecuted + + /// Serialisation suitable for direct interpolation into a TRX report. + override this.ToString () = + match this with + | TrxTestOutcome.Passed -> "Passed" + | TrxTestOutcome.Failed -> "Failed" + | TrxTestOutcome.NotExecuted -> "NotExecuted" + + /// Round-trips with `ToString`; returns None if parse was unsuccessful. + static member Parse (s : string) : TrxTestOutcome option = + if s.Equals ("passed", StringComparison.OrdinalIgnoreCase) then + Some TrxTestOutcome.Passed + elif s.Equals ("failed", StringComparison.OrdinalIgnoreCase) then + Some TrxTestOutcome.Failed + elif s.Equals ("notexecuted", StringComparison.OrdinalIgnoreCase) then + Some TrxTestOutcome.NotExecuted + else + None + +/// Description of an error emitted by something that runs (e.g. the test harness or an individual test). +/// This is not a description of a test *failure*. +type TrxErrorInfo = + { + /// Description of the error. + Message : string option + /// Stack trace if this error arose from an exception. + StackTrace : string option + } + + static member internal ofXml (node : XmlNode) : Result = + let message = + match node with + | NodeWithNamedChild "Message" (OneChildNode "Message" (NoChildrenNode message)) -> Some message + | _ -> None + + let stackTrace = + match node with + | NodeWithNamedChild "StackTrace" (OneChildNode "StackTrace" (NoChildrenNode stackTrace)) -> Some stackTrace + | _ -> None + + { + Message = message + StackTrace = stackTrace + } + |> Ok + +/// Information output by something that runs (e.g. the test harness itself, or an individual test). +type TrxOutput = + { + /// What the entity printed to standard output. + StdOut : string option + /// Description of any error the entity encountered. + ErrorInfo : TrxErrorInfo option + } + + static member internal ofXml (node : XmlNode) : Result = + let stdout = + match node with + | NodeWithNamedChild "StdOut" (OneChildNode "StdOut" (NoChildrenNode stdout)) -> Some stdout + | _ -> None + + let errorInfo = + match node with + | NodeWithNamedChild "ErrorInfo" node -> + match TrxErrorInfo.ofXml node with + | Ok n -> Ok (Some n) + | Error e -> Error e + | _ -> Ok None + + match errorInfo with + | Error e -> Error e + | Ok errorInfo -> + { + StdOut = stdout + ErrorInfo = errorInfo + } + |> Ok + +/// The result of a run of a single test. +type TrxUnitTestResult = + { + /// An ID that lets us identify this test result among the executions (see the data object). + ExecutionId : Guid + /// An ID that lets us identify this test result among the tests (see the data object). + TestId : Guid + /// Name of this test, likely the same as the name of the method that defined it. + TestName : string + /// Name of the computer on which this test ran. + ComputerName : string + /// How long this test took to run. + Duration : TimeSpan + /// When this test started. + StartTime : DateTimeOffset + /// When this test finished. + EndTime : DateTimeOffset + /// An undocumented GUID that is always 13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b and appears to mean "automated + /// test". + TestType : Guid + /// Outcome of this test. + Outcome : TrxTestOutcome + /// An ID that lets us identify what collection of tests this was part of (see the data object). + TestListId : Guid + /// It's awfully unclear what this is, but it seems to take the same value as ExecutionId. + RelativeResultsDirectory : string + /// Any output the test had, beyond its passed/failed (etc) outcome. + Output : TrxOutput option + } + + static member internal ofXml (node : XmlNode) : Result = + let attrs = + node.Attributes + |> Seq.cast + |> Seq.map (fun attr -> attr.Name, attr.Value) + |> Map.ofSeq // silently ignore duplicates + + let executionId = + match attrs.TryGetValue "executionId" with + | false, _ -> Error "executionId" + | true, v -> + match Guid.TryParse v with + | false, _ -> Error $"executionId was not a GUID: %s{v}" + | true, v -> Ok v + + let testId = + match attrs.TryGetValue "testId" with + | false, _ -> Error "testId" + | true, v -> + match Guid.TryParse v with + | false, _ -> Error $"testId was not a GUID: %s{v}" + | true, v -> Ok v + + let testName = + match attrs.TryGetValue "testName" with + | false, _ -> Error "testName" + | true, v -> Ok v + + let computerName = + match attrs.TryGetValue "computerName" with + | false, _ -> Error "computerName" + | true, v -> Ok v + + let duration = + match attrs.TryGetValue "duration" with + | false, _ -> Error "duration" + | true, v -> + match TimeSpan.TryParse v with + | false, _ -> Error $"duration was not a TimeSpan: %s{v}" + | true, v -> Ok v + + let startTime = + match attrs.TryGetValue "startTime" with + | false, _ -> Error "startTime" + | true, v -> + match DateTimeOffset.TryParse v with + | false, _ -> Error $"startTime was not a DateTimeOffset: %s{v}" + | true, v -> Ok v + + let endTime = + match attrs.TryGetValue "endTime" with + | false, _ -> Error "endTime" + | true, v -> + match DateTimeOffset.TryParse v with + | false, _ -> Error $"endTime was not a DateTimeOffset: %s{v}" + | true, v -> Ok v + + let testType = + match attrs.TryGetValue "testType" with + | false, _ -> Error "testType" + | true, v -> + match Guid.TryParse v with + | false, _ -> Error $"testType was not a GUID: %s{v}" + | true, v -> Ok v + + let outcome = + match attrs.TryGetValue "outcome" with + | false, _ -> Error "outcome" + | true, v -> + match TrxTestOutcome.Parse v with + | None -> Error $"Outcome was not recognised: %s{v}" + | Some v -> Ok v + + let testListId = + match attrs.TryGetValue "testListId" with + | false, _ -> Error "testListId" + | true, v -> + match Guid.TryParse v with + | false, _ -> Error $"testListId was not a GUID: %s{v}" + | true, v -> Ok v + + let relativeResultsDirectory = + match attrs.TryGetValue "relativeResultsDirectory" with + | false, _ -> Error "relativeResultsDirectory" + | true, v -> Ok v + + let output = + match node with + | NodeWithNamedChild "Output" node -> Some (TrxOutput.ofXml node) + | _ -> None + + let errors = + [ + executionId |> Result.getError + testId |> Result.getError + testName |> Result.getError + computerName |> Result.getError + duration |> Result.getError + startTime |> Result.getError + endTime |> Result.getError + testType |> Result.getError + outcome |> Result.getError + testListId |> Result.getError + relativeResultsDirectory |> Result.getError + output |> Option.bind Result.getError + ] + |> List.choose id + + if not errors.IsEmpty then + errors |> String.concat "; " |> Error + else + + let executionId = executionId |> Result.get |> Option.get + let testId = testId |> Result.get |> Option.get + let testName = testName |> Result.get |> Option.get + let computerName = computerName |> Result.get |> Option.get + let duration = duration |> Result.get |> Option.get + let startTime = startTime |> Result.get |> Option.get + let endTime = endTime |> Result.get |> Option.get + let testType = testType |> Result.get |> Option.get + let outcome = outcome |> Result.get |> Option.get + let testListId = testListId |> Result.get |> Option.get + let relativeResultsDirectory = relativeResultsDirectory |> Result.get |> Option.get + let output = output |> Option.bind Result.get + + { + ExecutionId = executionId + TestId = testId + TestName = testName + ComputerName = computerName + Duration = duration + StartTime = startTime + EndTime = endTime + TestType = testType + Outcome = outcome + TestListId = testListId + RelativeResultsDirectory = relativeResultsDirectory + Output = output + } + |> Ok + +/// A method which, being run, caused a test to run. +/// You get one of these for each run, so in particular you get a different one for each parameter of the method. +type TrxTestMethod = + { + /// Path to the DLL from which this test was extracted. + CodeBase : string + /// Identifier for the entity or framework which executed this test. + AdapterTypeName : Uri + /// Name of the .NET class in which this test method was defined. + ClassName : string + /// Name of the test method. This includes string representations of any parameters to the method. + Name : string + } + + static member internal ofXml (node : XmlNode) : Result = + let attrs = + node.Attributes + |> Seq.cast + |> Seq.map (fun attr -> attr.Name, attr.Value) + |> Map.ofSeq // silently ignore duplicates + + let codeBase = + match attrs.TryGetValue "codeBase" with + | false, _ -> Error "Expected codeBase attribute" + | true, v -> Ok v + + let adapterTypeName = + match attrs.TryGetValue "adapterTypeName" with + | false, _ -> Error "Expected adapterTypeName attribute" + | true, v -> Uri v |> Ok + + let className = + match attrs.TryGetValue "className" with + | false, _ -> Error "Expected className attribute" + | true, v -> Ok v + + let name = + match attrs.TryGetValue "name" with + | false, _ -> Error "Expected name attribute" + | true, v -> Ok v + + let errors = + [ + codeBase |> Result.getError + adapterTypeName |> Result.getError + className |> Result.getError + name |> Result.getError + ] + |> List.choose id + + if not errors.IsEmpty then + errors |> String.concat "; " |> Error + else + + let codeBase = codeBase |> Result.get |> Option.get + let adapterTypeName = adapterTypeName |> Result.get |> Option.get + let className = className |> Result.get |> Option.get + let name = name |> Result.get |> Option.get + + { + CodeBase = codeBase + AdapterTypeName = adapterTypeName + ClassName = className + Name = name + } + |> Ok + +/// Information about a single "execution"; it's not entirely clear to me what this is. +type TrxExecution = + { + /// Identifier for the execution that ran this specific test. + Id : Guid + } + + static member internal ofXml (node : XmlNode) : Result = + let attrs = + node.Attributes + |> Seq.cast + |> Seq.map (fun attr -> attr.Name, attr.Value) + |> Map.ofSeq // silently ignore duplicates + + let ident = + match attrs.TryGetValue "id" with + | false, _ -> Error "Expected id attribute" + | true, v -> + match Guid.TryParse v with + | false, _ -> Error $"Could not parse GUID: %s{v}" + | true, v -> Ok v + + let errors = [ ident |> Result.getError ] |> List.choose id + + if not errors.IsEmpty then + errors |> String.concat "; " |> Error + else + + let ident = ident |> Result.get |> Option.get + + { + Id = ident + } + |> Ok + + +/// A single test. (So, for example, you get different ones of these for each parameter to the test method.) +type TrxUnitTest = + { + /// Name of the test, incorporating string representations of any parameters to the test. + Name : string + /// Not really sure what this is, but in my runs it's always been a path to the test DLL. + Storage : string + /// Identifier for this test, for cross-referencing in the TestEntries and Results lists. + Id : Guid + /// Identifier for the execution that ran this specific test. + Execution : TrxExecution + /// The method we ran to run this test. + TestMethod : TrxTestMethod + } + + static member internal ofXml (node : XmlNode) : Result = + let attrs = + node.Attributes + |> Seq.cast + |> Seq.map (fun attr -> attr.Name, attr.Value) + |> Map.ofSeq // silently ignore duplicates + + let name = + match attrs.TryGetValue "name" with + | false, _ -> Error "Expected name attribute" + | true, v -> Ok v + + let storage = + match attrs.TryGetValue "storage" with + | false, _ -> Error "Expected storage attribute" + | true, v -> Ok v + + let ident = + match attrs.TryGetValue "id" with + | false, _ -> Error "Expected id attribute" + | true, v -> + match Guid.TryParse v with + | false, _ -> Error $"Could not parse GUID: %s{v}" + | true, v -> Ok v + + let execution = + match node with + | NodeWithNamedChild "Execution" node -> TrxExecution.ofXml node + | _ -> Error "Expected Execution node, but got none" + + let testMethod = + match node with + | NodeWithNamedChild "TestMethod" node -> TrxTestMethod.ofXml node + | _ -> Error "Expected TestMethod node, but got none" + + let errors = + [ + name |> Result.getError + storage |> Result.getError + ident |> Result.getError + execution |> Result.getError + testMethod |> Result.getError + ] + |> List.choose id + + if not errors.IsEmpty then + errors |> String.concat "; " |> Error + else + + let name = name |> Result.get |> Option.get + let storage = storage |> Result.get |> Option.get + let ident = ident |> Result.get |> Option.get + let execution = execution |> Result.get |> Option.get + let testMethod = testMethod |> Result.get |> Option.get + + { + Name = name + Storage = storage + Id = ident + Execution = execution + TestMethod = testMethod + } + |> Ok + +/// Essentially a mapping that tells you which list and execution a test is in. +type TrxTestEntry = + { + /// For cross-referencing with the TestLists list. + TestListId : Guid + /// Don't really know what this means. + ExecutionId : Guid + /// For cross-referencing with the TestDefinitions list. + TestId : Guid + } + + static member internal ofXml (node : XmlNode) : Result = + let attrs = + node.Attributes + |> Seq.cast + |> Seq.map (fun attr -> attr.Name, attr.Value) + |> Map.ofSeq // silently ignore duplicates + + let testListId = + match attrs.TryGetValue "testListId" with + | false, _ -> Error "Expected name testListId" + | true, v -> + match Guid.TryParse v with + | false, _ -> Error $"Could not parse GUID: %s{v}" + | true, v -> Ok v + + let testId = + match attrs.TryGetValue "testId" with + | false, _ -> Error "Expected testId attribute" + | true, v -> + match Guid.TryParse v with + | false, _ -> Error $"Could not parse GUID: %s{v}" + | true, v -> Ok v + + let executionId = + match attrs.TryGetValue "executionId" with + | false, _ -> Error "Expected executionId attribute" + | true, v -> + match Guid.TryParse v with + | false, _ -> Error $"Could not parse GUID: %s{v}" + | true, v -> Ok v + + let errors = + [ + testListId |> Result.getError + testId |> Result.getError + executionId |> Result.getError + ] + |> List.choose id + + if not errors.IsEmpty then + errors |> String.concat "; " |> Error + else + + let testListId = testListId |> Result.get |> Option.get + let testId = testId |> Result.get |> Option.get + let executionId = executionId |> Result.get |> Option.get + + { + TestListId = testListId + TestId = testId + ExecutionId = executionId + } + |> Ok + +/// An entry in the TestList, assigning a name to each TestListId. +type TrxTestListEntry = + { + /// Human-readable name of this list. + Name : string + /// ID of this list, for cross-referencing with other tests' TestListId. + Id : Guid + } + + static member internal ofXml (node : XmlNode) : Result = + let attrs = + node.Attributes + |> Seq.cast + |> Seq.map (fun attr -> attr.Name, attr.Value) + |> Map.ofSeq // silently ignore duplicates + + let name = + match attrs.TryGetValue "name" with + | false, _ -> Error "Expected name attribute" + | true, v -> Ok v + + let ident = + match attrs.TryGetValue "id" with + | false, _ -> Error "Expected id attribute" + | true, v -> + match Guid.TryParse v with + | false, _ -> Error $"Could not parse GUID: %s{v}" + | true, v -> Ok v + + let errors = + [ name |> Result.getError ; ident |> Result.getError ] |> List.choose id + + if not errors.IsEmpty then + errors |> String.concat "; " |> Error + else + + let name = name |> Result.get |> Option.get + let ident = ident |> Result.get |> Option.get + + { + Name = name + Id = ident + } + |> Ok + +/// Outcome of a test run or of a test. +type TrxOutcome = + /// Test run was successful. + | Completed + /// Dunno why this can be emitted. I've seen it applied to the RunInfo of a specific test. + | Warning + /// Test run failed, perhaps because some tests failed. + | Failed + + /// Serialisation suitable for direct interpolation into a TRX report. + override this.ToString () = + match this with + | TrxOutcome.Warning -> "Warning" + | TrxOutcome.Completed -> "Completed" + | TrxOutcome.Failed -> "Failed" + + /// Round-trips with ToString. Returns None if parse failed. + static member Parse (s : string) : TrxOutcome option = + if s.Equals ("warning", StringComparison.OrdinalIgnoreCase) then + Some TrxOutcome.Warning + elif s.Equals ("failed", StringComparison.OrdinalIgnoreCase) then + Some TrxOutcome.Failed + elif s.Equals ("completed", StringComparison.OrdinalIgnoreCase) then + Some TrxOutcome.Completed + else + None + +/// Ancillary information about text emitted during a complete test run. +type TrxRunInfo = + { + /// The computer on which this text was emitted. + ComputerName : string + /// Dunno why this field is here; it seems to be "Warning" when text is emitted? + Outcome : TrxOutcome + /// When this text was emitted. + Timestamp : DateTimeOffset + /// The text which was emitted in this event within the run. + Text : string + } + + static member internal ofXml (node : XmlNode) : Result = + let attrs = + node.Attributes + |> Seq.cast + |> Seq.map (fun attr -> attr.Name, attr.Value) + |> Map.ofSeq // silently ignore duplicates + + let computerName = + match attrs.TryGetValue "computerName" with + | false, _ -> Error "Expected computerName attribute" + | true, v -> Ok v + + let outcome = + match attrs.TryGetValue "outcome" with + | false, _ -> Error "Expected outcome attribute" + | true, v -> + match TrxOutcome.Parse v with + | Some v -> Ok v + | None -> Error $"Could not parse '%s{v}' as an Outcome" + + let timestamp = + match attrs.TryGetValue "timestamp" with + | false, _ -> Error "Expected timestamp attribute" + | true, v -> + match DateTimeOffset.TryParse v with + | false, _ -> Error $"Could not parse as a timestamp: %s{v}" + | true, v -> Ok v + + let text = + match node with + | NodeWithNamedChild "Text" n -> + match n with + | OneChildNode "Text" (NoChildrenNode n) -> Ok n + | _ -> Error "Text node unexpectedly had children" + | _ -> Error "Expected a Text node, but found none" + + let errors = + [ + computerName |> Result.getError + outcome |> Result.getError + timestamp |> Result.getError + text |> Result.getError + ] + |> List.choose id + + if not errors.IsEmpty then + errors |> String.concat "; " |> Error + else + + let computerName = computerName |> Result.get |> Option.get + let outcome = outcome |> Result.get |> Option.get + let timestamp = timestamp |> Result.get |> Option.get + let text = text |> Result.get |> Option.get + + { + ComputerName = computerName + Outcome = outcome + Timestamp = timestamp + Text = text + } + |> Ok + +/// Summary statistic of what happened to all the tests in the run. +type TrxCounters = + { + /// All the tests known to the runner. + Total : uint + /// All the tests which were executed (e.g. skipping out [] ones). + Executed : uint + /// The tests which passed. + Passed : uint + /// The tests which failed. + Failed : uint + /// The tests which experienced an error which prevented them from terminating correctly. + Errors : uint + /// The tests which timed out before they could pass or fail. + Timeout : uint + /// Tests where the runner crashed, I think. + Aborted : uint + /// Tests which terminated with Assert.Inconclusive + Inconclusive : uint + /// Wonder when this can happen + PassedButRunAborted : uint + /// Dunno! + NotRunnable : uint + /// Search me! + NotExecuted : uint + /// No idea! + Disconnected : uint + /// Tests which succeeded but warned. + Warning : uint + /// I *think* this describes how many tests have been completed *for a run which is still going*. + /// When a run's finished, this is 0. + Completed : uint + /// Tests which are still running. + InProgress : uint + /// Tests which are waiting to run. + Pending : uint + } + + static member internal ofXml (node : XmlNode) : Result = + let attrs = + node.Attributes + |> Seq.cast + |> Seq.map (fun attr -> attr.Name, attr.Value) + |> Map.ofSeq // silently ignore duplicates + + // strap in, boys - thank the Lord, or possibly Bram, for Vim macros + + let total = + match attrs.TryGetValue "total" with + | false, _ -> Error "Expected total attribute" + | true, v -> + match UInt32.TryParse v with + | true, v -> Ok v + | false, _ -> Error $"Could not parse integer: %s{v}" + + let executed = + match attrs.TryGetValue "executed" with + | false, _ -> Error "Expected executed attribute" + | true, v -> + match UInt32.TryParse v with + | true, v -> Ok v + | false, _ -> Error $"Could not parse integer: %s{v}" + + let passed = + match attrs.TryGetValue "passed" with + | false, _ -> Error "Expected passed attribute" + | true, v -> + match UInt32.TryParse v with + | true, v -> Ok v + | false, _ -> Error $"Could not parse integer: %s{v}" + + let failed = + match attrs.TryGetValue "failed" with + | false, _ -> Error "Expected failed attribute" + | true, v -> + match UInt32.TryParse v with + | true, v -> Ok v + | false, _ -> Error $"Could not parse integer: %s{v}" + + let errors = + match attrs.TryGetValue "error" with + | false, _ -> Error "Expected error attribute" + | true, v -> + match UInt32.TryParse v with + | true, v -> Ok v + | false, _ -> Error $"Could not parse integer: %s{v}" + + let timeout = + match attrs.TryGetValue "timeout" with + | false, _ -> Error "Expected timeout attribute" + | true, v -> + match UInt32.TryParse v with + | true, v -> Ok v + | false, _ -> Error $"Could not parse integer: %s{v}" + + let aborted = + match attrs.TryGetValue "aborted" with + | false, _ -> Error "Expected aborted attribute" + | true, v -> + match UInt32.TryParse v with + | true, v -> Ok v + | false, _ -> Error $"Could not parse integer: %s{v}" + + let inconclusive = + match attrs.TryGetValue "inconclusive" with + | false, _ -> Error "Expected inconclusive attribute" + | true, v -> + match UInt32.TryParse v with + | true, v -> Ok v + | false, _ -> Error $"Could not parse integer: %s{v}" + + let passedButRunAborted = + match attrs.TryGetValue "passedButRunAborted" with + | false, _ -> Error "Expected passedButRunAborted attribute" + | true, v -> + match UInt32.TryParse v with + | true, v -> Ok v + | false, _ -> Error $"Could not parse integer: %s{v}" + + let notRunnable = + match attrs.TryGetValue "notRunnable" with + | false, _ -> Error "Expected notRunnable attribute" + | true, v -> + match UInt32.TryParse v with + | true, v -> Ok v + | false, _ -> Error $"Could not parse integer: %s{v}" + + let notExecuted = + match attrs.TryGetValue "notExecuted" with + | false, _ -> Error "Expected notExecuted attribute" + | true, v -> + match UInt32.TryParse v with + | true, v -> Ok v + | false, _ -> Error $"Could not parse integer: %s{v}" + + let disconnected = + match attrs.TryGetValue "disconnected" with + | false, _ -> Error "Expected disconnected attribute" + | true, v -> + match UInt32.TryParse v with + | true, v -> Ok v + | false, _ -> Error $"Could not parse integer: %s{v}" + + let warning = + match attrs.TryGetValue "warning" with + | false, _ -> Error "Expected warning attribute" + | true, v -> + match UInt32.TryParse v with + | true, v -> Ok v + | false, _ -> Error $"Could not parse integer: %s{v}" + + let completed = + match attrs.TryGetValue "completed" with + | false, _ -> Error "Expected completed attribute" + | true, v -> + match UInt32.TryParse v with + | true, v -> Ok v + | false, _ -> Error $"Could not parse integer: %s{v}" + + let inProgress = + match attrs.TryGetValue "inProgress" with + | false, _ -> Error "Expected inProgress attribute" + | true, v -> + match UInt32.TryParse v with + | true, v -> Ok v + | false, _ -> Error $"Could not parse integer: %s{v}" + + let pending = + match attrs.TryGetValue "pending" with + | false, _ -> Error "Expected pending attribute" + | true, v -> + match UInt32.TryParse v with + | true, v -> Ok v + | false, _ -> Error $"Could not parse integer: %s{v}" + + let parseErrors = + [ + total |> Result.getError + executed |> Result.getError + passed |> Result.getError + failed |> Result.getError + errors |> Result.getError + timeout |> Result.getError + aborted |> Result.getError + inconclusive |> Result.getError + passedButRunAborted |> Result.getError + notRunnable |> Result.getError + notExecuted |> Result.getError + disconnected |> Result.getError + warning |> Result.getError + completed |> Result.getError + inProgress |> Result.getError + pending |> Result.getError + ] + |> List.choose id + + if not parseErrors.IsEmpty then + parseErrors |> String.concat "; " |> Error + else + + let total = total |> Result.get |> Option.get + let executed = executed |> Result.get |> Option.get + let passed = passed |> Result.get |> Option.get + let failed = failed |> Result.get |> Option.get + let errors = errors |> Result.get |> Option.get + let timeout = timeout |> Result.get |> Option.get + let aborted = aborted |> Result.get |> Option.get + let inconclusive = inconclusive |> Result.get |> Option.get + let passedButRunAborted = passedButRunAborted |> Result.get |> Option.get + let notRunnable = notRunnable |> Result.get |> Option.get + let notExecuted = notExecuted |> Result.get |> Option.get + let disconnected = disconnected |> Result.get |> Option.get + let warning = warning |> Result.get |> Option.get + let completed = completed |> Result.get |> Option.get + let inProgress = inProgress |> Result.get |> Option.get + let pending = pending |> Result.get |> Option.get + + { + Total = total + Executed = executed + Passed = passed + Failed = failed + Errors = errors + Timeout = timeout + Aborted = aborted + Inconclusive = inconclusive + PassedButRunAborted = passedButRunAborted + NotRunnable = notRunnable + NotExecuted = notExecuted + Disconnected = disconnected + Warning = warning + Completed = completed + InProgress = inProgress + Pending = pending + } + |> Ok + + + /// A set of counters where every counter is 0. + static member Zero = + { + Total = 0u + Executed = 0u + Passed = 0u + Failed = 0u + Errors = 0u + Timeout = 0u + Aborted = 0u + Inconclusive = 0u + PassedButRunAborted = 0u + NotRunnable = 0u + NotExecuted = 0u + Disconnected = 0u + Warning = 0u + Completed = 0u + InProgress = 0u + Pending = 0u + } + +/// Summary of the results; in general this doesn't explicitly mention specific test results +/// unless they have some unusual property like "printed to stdout". +type TrxResultsSummary = + { + /// Aggregated outcome of the entire test run. + Outcome : TrxOutcome + /// Summary of the number of tests in each possible state. + Counters : TrxCounters + /// Any messages emitted by the test runner itself, not by the tests. + Output : TrxOutput + /// Further information about some specific tests which were run. + RunInfos : TrxRunInfo list + } + + static member internal ofXml (node : XmlNode) : Result = + match node.Attributes.["outcome"] with + | null -> Error "Expected outcome node" + | outcome -> + + match TrxOutcome.Parse outcome.Value with + | None -> Error $"Could not parse outcome attribute: %s{outcome.Value}" + | Some outcome -> + + let counters = + match node with + | NodeWithNamedChild "Counters" n -> TrxCounters.ofXml n + | _ -> Error "Expected Counters node on ResultsSummary" + + let output = + match node with + | NodeWithNamedChild "Output" n -> TrxOutput.ofXml n + | _ -> Error "Expected Output node on ResultsSummary" + + let runInfos = + match node with + | NodeWithNamedChild "RunInfos" v -> + v.ChildNodes + |> Seq.cast + |> Seq.map TrxRunInfo.ofXml + |> Seq.toList + |> Result.allOkOrError + |> Result.mapError (fun (_, errors) -> String.concat "; " errors) + | _ -> Error "Expected Counters node on ResultsSummary" + + let errors = + [ + counters |> Result.getError + runInfos |> Result.getError + output |> Result.getError + ] + |> List.choose id + + if not errors.IsEmpty then + errors |> String.concat "; " |> Error + else + + let counters = counters |> Result.get |> Option.get + let runInfos = runInfos |> Result.get |> Option.get + let output = output |> Result.get |> Option.get + + { + Outcome = outcome + Counters = counters + RunInfos = runInfos + Output = output + } + |> Ok + +/// A report on a test run. +type TrxReport = + { + /// An ID for this test run; this probably won't appear anywhere else in the report, + /// it's to discriminate between *different* runs. + Id : Guid + /// A human-readable name for this run. + Name : string + /// Information about when the test run entered certain states. + Times : TrxReportTimes + /// Some rather inscrutable information about the configuration of this run. + Settings : TrxTestSettings + /// Results of all individual tests. + Results : TrxUnitTestResult list + /// Metadata about each individual test. + TestDefinitions : TrxUnitTest list + /// Mapping telling you which execution and test list each test belongs to. + TestEntries : TrxTestEntry list + /// It's possible for tests to be grouped into "lists"; this is metadata about those lists. + TestLists : TrxTestListEntry list + /// Summary information about the test run's overall status. + ResultsSummary : TrxResultsSummary + } + + static member internal ofXml (node : XmlNode) : Result = + let attrs = + node.Attributes + |> Seq.cast + |> Seq.map (fun attr -> attr.Name, attr.Value) + |> Map.ofSeq // silently ignore duplicates + + let name = + match attrs.TryGetValue "name" with + | false, _ -> Error "Expected name attribute" + | true, v -> Ok v + + let ident = + match attrs.TryGetValue "id" with + | false, _ -> Error "Expected id attribute" + | true, v -> + match Guid.TryParse v with + | false, _ -> Error $"Could not parse GUID: %s{v}" + | true, v -> Ok v + + let times = + match node with + | NodeWithNamedChild "Times" n -> TrxReportTimes.ofXml n + | _ -> Error "Expected Times node on TrxReport" + + let testSettings = + match node with + | NodeWithNamedChild "TestSettings" n -> TrxTestSettings.ofXml n + | _ -> Error "Expected TestSettings node on TrxReport" + + let resultSummary = + match node with + | NodeWithNamedChild "ResultSummary" n -> TrxResultsSummary.ofXml n + | _ -> Error "Expected ResultSummary node on TrxReport" + + let testEntries = + match node with + | NodeWithNamedChild "TestEntries" v -> + v.ChildNodes + |> Seq.cast + |> Seq.map TrxTestEntry.ofXml + |> Seq.toList + |> Result.allOkOrError + |> Result.mapError (fun (_, errors) -> String.concat "; " errors) + | _ -> Error "Expected TestEntries node on TrxReport" + + let testLists = + match node with + | NodeWithNamedChild "TestLists" v -> + v.ChildNodes + |> Seq.cast + |> Seq.map TrxTestListEntry.ofXml + |> Seq.toList + |> Result.allOkOrError + |> Result.mapError (fun (_, errors) -> String.concat "; " errors) + | _ -> Error "Expected TestLists node on TrxReport" + + let testDefinitions = + match node with + | NodeWithNamedChild "TestDefinitions" v -> + v.ChildNodes + |> Seq.cast + |> Seq.map TrxUnitTest.ofXml + |> Seq.toList + |> Result.allOkOrError + |> Result.mapError (fun (_, errors) -> String.concat "; " errors) + | _ -> Error "Expected TestDefinitions node on TrxReport" + + let testResults = + match node with + | NodeWithNamedChild "Results" v -> + v.ChildNodes + |> Seq.cast + |> Seq.map TrxUnitTestResult.ofXml + |> Seq.toList + |> Result.allOkOrError + |> Result.mapError (fun (_, errors) -> String.concat "; " errors) + | _ -> Error "Expected Results node on TrxReport" + + let errors = + [ + name |> Result.getError + ident |> Result.getError + times |> Result.getError + testSettings |> Result.getError + resultSummary |> Result.getError + testEntries |> Result.getError + testLists |> Result.getError + testDefinitions |> Result.getError + testResults |> Result.getError + ] + |> List.choose id + + if not errors.IsEmpty then + errors |> String.concat "; " |> Error + else + + let name = name |> Result.get |> Option.get + let ident = ident |> Result.get |> Option.get + let times = times |> Result.get |> Option.get + let testSettings = testSettings |> Result.get |> Option.get + let resultSummary = resultSummary |> Result.get |> Option.get + let testEntries = testEntries |> Result.get |> Option.get + let testLists = testLists |> Result.get |> Option.get + let testDefinitions = testDefinitions |> Result.get |> Option.get + let testResults = testResults |> Result.get |> Option.get + + { + Id = ident + Name = name + Times = times + Settings = testSettings + Results = testResults + TestDefinitions = testDefinitions + TestEntries = testEntries + TestLists = testLists + ResultsSummary = resultSummary + } + |> Ok + +/// A report on a test run. +[] +module TrxReport = + /// Parse the report. May instead return a human-readable description of why we couldn't parse. + let parse (s : string) : Result = + let node = XmlDocument () + node.LoadXml s + + match node with + | NodeWithNamedChild "TestRun" node -> TrxReport.ofXml node + | _ -> Error "XML document did not have a TestRun node" diff --git a/TestRunner.Lib/Xml.fs b/TestRunner.Lib/Xml.fs new file mode 100644 index 0000000..f55c6ac --- /dev/null +++ b/TestRunner.Lib/Xml.fs @@ -0,0 +1,47 @@ +namespace TestRunner + +open System.Xml + +[] +module internal XmlPatterns = + [] + let (|NodeWithChildren|_|) (expectedName : string) (node : XmlNode) : unit voption = + if node.Name = expectedName && node.HasChildNodes then + ValueSome () + else + ValueNone + + let (|NodeWithNamedChild|_|) (childName : string) (node : XmlNode) : XmlNode option = + if node.HasChildNodes then + node.ChildNodes + |> Seq.cast + |> Seq.tryFind (fun n -> n.Name = childName) + else + None + + let (|OneChildNode|_|) (expectedName : string) (node : XmlNode) : XmlNode option = + if node.Name = expectedName && node.HasChildNodes && node.ChildNodes.Count = 1 then + Some node.FirstChild + else + None + + let (|NoChildrenNode|_|) (node : XmlNode) : string option = + if node.HasChildNodes then None else Some node.Value + + [] + let (|NamedNoChildren|_|) (name : string) (node : XmlNode) : unit voption = + if node.HasChildNodes then ValueNone + elif node.Name = name then ValueSome () + else ValueNone + + [] + let (|Int64|_|) (s : string) : int64 voption = + match System.Int64.TryParse s with + | false, _ -> ValueNone + | true, v -> ValueSome v + + [] + let (|Int|_|) (s : string) : int voption = + match System.Int32.TryParse s with + | false, _ -> ValueNone + | true, v -> ValueSome v diff --git a/TestRunner.Lib/version.json b/TestRunner.Lib/version.json index df56be9..3d400aa 100644 --- a/TestRunner.Lib/version.json +++ b/TestRunner.Lib/version.json @@ -1,5 +1,5 @@ { - "version": "0.4", + "version": "0.5", "publicReleaseRefSpec": null, "pathFilters": [ "./", diff --git a/TestRunner/TestRunner.Test/EmbeddedResource.fs b/TestRunner/TestRunner.Test/EmbeddedResource.fs new file mode 100644 index 0000000..43defc3 --- /dev/null +++ b/TestRunner/TestRunner.Test/EmbeddedResource.fs @@ -0,0 +1,20 @@ +namespace TestRunner.Test + +open System +open System.IO + +[] +module internal EmbeddedResource = + type Dummy = class end + + let read (name : string) : string = + let assy = typeof.Assembly + + let manifestName = + assy.GetManifestResourceNames () + |> Seq.filter (fun s -> s.EndsWith (name, StringComparison.Ordinal)) + |> Seq.exactlyOne + + use s = assy.GetManifestResourceStream manifestName + use reader = new StreamReader (s) + reader.ReadToEnd () diff --git a/TestRunner/TestRunner.Test/Example1.trx b/TestRunner/TestRunner.Test/Example1.trx new file mode 100644 index 0000000..fb8b03e --- /dev/null +++ b/TestRunner/TestRunner.Test/Example1.trx @@ -0,0 +1,536 @@ + + + + + + + + + + + + + + + + System.Exception : Unexpected difference. + +The following 205 member(s) have been added (i.e. are NOT present in the baseline): + + TestRunner.TrxCounters inherit obj, implements TestRunner.TrxCounters System.IEquatable, System.Collections.IStructuralEquatable, TestRunner.TrxCounters System.IComparable, System.IComparable, System.Collections.IStructuralComparable + + TestRunner.TrxCounters..ctor [constructor]: (System.UInt32, System.UInt32, System.UInt32, System.UInt32, System.UInt32, System.UInt32, System.UInt32, System.UInt32, System.UInt32, System.UInt32, System.UInt32, System.UInt32, System.UInt32, System.UInt32, System.UInt32, System.UInt32) + + TestRunner.TrxCounters.Aborted [property]: [read-only] System.UInt32 + + TestRunner.TrxCounters.Completed [property]: [read-only] System.UInt32 + + TestRunner.TrxCounters.Disconnected [property]: [read-only] System.UInt32 + + TestRunner.TrxCounters.Errors [property]: [read-only] System.UInt32 + + TestRunner.TrxCounters.Executed [property]: [read-only] System.UInt32 + + TestRunner.TrxCounters.Failed [property]: [read-only] System.UInt32 + + TestRunner.TrxCounters.InProgress [property]: [read-only] System.UInt32 + + TestRunner.TrxCounters.Inconclusive [property]: [read-only] System.UInt32 + + TestRunner.TrxCounters.NotExecuted [property]: [read-only] System.UInt32 + + TestRunner.TrxCounters.NotRunnable [property]: [read-only] System.UInt32 + + TestRunner.TrxCounters.Passed [property]: [read-only] System.UInt32 + + TestRunner.TrxCounters.PassedButRunAborted [property]: [read-only] System.UInt32 + + TestRunner.TrxCounters.Pending [property]: [read-only] System.UInt32 + + TestRunner.TrxCounters.Timeout [property]: [read-only] System.UInt32 + + TestRunner.TrxCounters.Total [property]: [read-only] System.UInt32 + + TestRunner.TrxCounters.Warning [property]: [read-only] System.UInt32 + + TestRunner.TrxCounters.Zero [static property]: [read-only] TestRunner.TrxCounters + + TestRunner.TrxCounters.get_Aborted [method]: unit -> System.UInt32 + + TestRunner.TrxCounters.get_Completed [method]: unit -> System.UInt32 + + TestRunner.TrxCounters.get_Disconnected [method]: unit -> System.UInt32 + + TestRunner.TrxCounters.get_Errors [method]: unit -> System.UInt32 + + TestRunner.TrxCounters.get_Executed [method]: unit -> System.UInt32 + + TestRunner.TrxCounters.get_Failed [method]: unit -> System.UInt32 + + TestRunner.TrxCounters.get_InProgress [method]: unit -> System.UInt32 + + TestRunner.TrxCounters.get_Inconclusive [method]: unit -> System.UInt32 + + TestRunner.TrxCounters.get_NotExecuted [method]: unit -> System.UInt32 + + TestRunner.TrxCounters.get_NotRunnable [method]: unit -> System.UInt32 + + TestRunner.TrxCounters.get_Passed [method]: unit -> System.UInt32 + + TestRunner.TrxCounters.get_PassedButRunAborted [method]: unit -> System.UInt32 + + TestRunner.TrxCounters.get_Pending [method]: unit -> System.UInt32 + + TestRunner.TrxCounters.get_Timeout [method]: unit -> System.UInt32 + + TestRunner.TrxCounters.get_Total [method]: unit -> System.UInt32 + + TestRunner.TrxCounters.get_Warning [method]: unit -> System.UInt32 + + TestRunner.TrxCounters.get_Zero [static method]: unit -> TestRunner.TrxCounters + + TestRunner.TrxDeployment inherit obj, implements TestRunner.TrxDeployment System.IEquatable, System.Collections.IStructuralEquatable, TestRunner.TrxDeployment System.IComparable, System.IComparable, System.Collections.IStructuralComparable + + TestRunner.TrxDeployment..ctor [constructor]: string + + TestRunner.TrxDeployment.RunDeploymentRoot [property]: [read-only] string + + TestRunner.TrxDeployment.get_RunDeploymentRoot [method]: unit -> string + + TestRunner.TrxErrorInfo inherit obj, implements TestRunner.TrxErrorInfo System.IEquatable, System.Collections.IStructuralEquatable + + TestRunner.TrxErrorInfo..ctor [constructor]: (string, System.Diagnostics.StackTrace option) + + TestRunner.TrxErrorInfo.Message [property]: [read-only] string + + TestRunner.TrxErrorInfo.StackTrace [property]: [read-only] System.Diagnostics.StackTrace option + + TestRunner.TrxErrorInfo.get_Message [method]: unit -> string + + TestRunner.TrxErrorInfo.get_StackTrace [method]: unit -> System.Diagnostics.StackTrace option + + TestRunner.TrxOutcome inherit obj, implements TestRunner.TrxOutcome System.IEquatable, System.Collections.IStructuralEquatable, TestRunner.TrxOutcome System.IComparable, System.IComparable, System.Collections.IStructuralComparable - union type with 3 cases + + TestRunner.TrxOutcome+Tags inherit obj + + TestRunner.TrxOutcome+Tags.Completed [static field]: int = 0 + + TestRunner.TrxOutcome+Tags.Failed [static field]: int = 2 + + TestRunner.TrxOutcome+Tags.Warning [static field]: int = 1 + + TestRunner.TrxOutcome.Completed [static property]: [read-only] TestRunner.TrxOutcome + + TestRunner.TrxOutcome.Failed [static property]: [read-only] TestRunner.TrxOutcome + + TestRunner.TrxOutcome.IsCompleted [property]: [read-only] bool + + TestRunner.TrxOutcome.IsFailed [property]: [read-only] bool + + TestRunner.TrxOutcome.IsWarning [property]: [read-only] bool + + TestRunner.TrxOutcome.Tag [property]: [read-only] int + + TestRunner.TrxOutcome.Warning [static property]: [read-only] TestRunner.TrxOutcome + + TestRunner.TrxOutcome.get_Completed [static method]: unit -> TestRunner.TrxOutcome + + TestRunner.TrxOutcome.get_Failed [static method]: unit -> TestRunner.TrxOutcome + + TestRunner.TrxOutcome.get_IsCompleted [method]: unit -> bool + + TestRunner.TrxOutcome.get_IsFailed [method]: unit -> bool + + TestRunner.TrxOutcome.get_IsWarning [method]: unit -> bool + + TestRunner.TrxOutcome.get_Tag [method]: unit -> int + + TestRunner.TrxOutcome.get_Warning [static method]: unit -> TestRunner.TrxOutcome + + TestRunner.TrxOutput inherit obj, implements TestRunner.TrxOutput System.IEquatable, System.Collections.IStructuralEquatable + + TestRunner.TrxOutput..ctor [constructor]: (string option, TestRunner.TrxErrorInfo option) + + TestRunner.TrxOutput.ErrorInfo [property]: [read-only] TestRunner.TrxErrorInfo option + + TestRunner.TrxOutput.StdOut [property]: [read-only] string option + + TestRunner.TrxOutput.get_ErrorInfo [method]: unit -> TestRunner.TrxErrorInfo option + + TestRunner.TrxOutput.get_StdOut [method]: unit -> string option + + TestRunner.TrxReport inherit obj, implements TestRunner.TrxReport System.IEquatable, System.Collections.IStructuralEquatable + + TestRunner.TrxReport..ctor [constructor]: (System.Guid, string, TestRunner.TrxReportTimes, TestRunner.TrxTestSettings, TestRunner.TrxUnitTestResult list, TestRunner.TrxUnitTest list, TestRunner.TrxTestEntry list, TestRunner.TrxTestList list, TestRunner.TrxResultsSummary) + + TestRunner.TrxReport.Id [property]: [read-only] System.Guid + + TestRunner.TrxReport.Name [property]: [read-only] string + + TestRunner.TrxReport.Results [property]: [read-only] TestRunner.TrxUnitTestResult list + + TestRunner.TrxReport.ResultsSummary [property]: [read-only] TestRunner.TrxResultsSummary + + TestRunner.TrxReport.Settings [property]: [read-only] TestRunner.TrxTestSettings + + TestRunner.TrxReport.TestDefinitions [property]: [read-only] TestRunner.TrxUnitTest list + + TestRunner.TrxReport.TestEntries [property]: [read-only] TestRunner.TrxTestEntry list + + TestRunner.TrxReport.TestLists [property]: [read-only] TestRunner.TrxTestList list + + TestRunner.TrxReport.Times [property]: [read-only] TestRunner.TrxReportTimes + + TestRunner.TrxReport.get_Id [method]: unit -> System.Guid + + TestRunner.TrxReport.get_Name [method]: unit -> string + + TestRunner.TrxReport.get_Results [method]: unit -> TestRunner.TrxUnitTestResult list + + TestRunner.TrxReport.get_ResultsSummary [method]: unit -> TestRunner.TrxResultsSummary + + TestRunner.TrxReport.get_Settings [method]: unit -> TestRunner.TrxTestSettings + + TestRunner.TrxReport.get_TestDefinitions [method]: unit -> TestRunner.TrxUnitTest list + + TestRunner.TrxReport.get_TestEntries [method]: unit -> TestRunner.TrxTestEntry list + + TestRunner.TrxReport.get_TestLists [method]: unit -> TestRunner.TrxTestList list + + TestRunner.TrxReport.get_Times [method]: unit -> TestRunner.TrxReportTimes + + TestRunner.TrxReportTimes inherit obj, implements TestRunner.TrxReportTimes System.IEquatable, System.Collections.IStructuralEquatable, TestRunner.TrxReportTimes System.IComparable, System.IComparable, System.Collections.IStructuralComparable + + TestRunner.TrxReportTimes..ctor [constructor]: (System.DateTimeOffset, System.DateTimeOffset, System.DateTimeOffset, System.DateTimeOffset) + + TestRunner.TrxReportTimes.Creation [property]: [read-only] System.DateTimeOffset + + TestRunner.TrxReportTimes.Finish [property]: [read-only] System.DateTimeOffset + + TestRunner.TrxReportTimes.Queuing [property]: [read-only] System.DateTimeOffset + + TestRunner.TrxReportTimes.Start [property]: [read-only] System.DateTimeOffset + + TestRunner.TrxReportTimes.get_Creation [method]: unit -> System.DateTimeOffset + + TestRunner.TrxReportTimes.get_Finish [method]: unit -> System.DateTimeOffset + + TestRunner.TrxReportTimes.get_Queuing [method]: unit -> System.DateTimeOffset + + TestRunner.TrxReportTimes.get_Start [method]: unit -> System.DateTimeOffset + + TestRunner.TrxResultsSummary inherit obj, implements TestRunner.TrxResultsSummary System.IEquatable, System.Collections.IStructuralEquatable + + TestRunner.TrxResultsSummary..ctor [constructor]: (TestRunner.TrxOutcome, TestRunner.TrxCounters, TestRunner.TrxOutput, TestRunner.TrxRunInfo list) + + TestRunner.TrxResultsSummary.Counters [property]: [read-only] TestRunner.TrxCounters + + TestRunner.TrxResultsSummary.Outcome [property]: [read-only] TestRunner.TrxOutcome + + TestRunner.TrxResultsSummary.Output [property]: [read-only] TestRunner.TrxOutput + + TestRunner.TrxResultsSummary.RunInfos [property]: [read-only] TestRunner.TrxRunInfo list + + TestRunner.TrxResultsSummary.get_Counters [method]: unit -> TestRunner.TrxCounters + + TestRunner.TrxResultsSummary.get_Outcome [method]: unit -> TestRunner.TrxOutcome + + TestRunner.TrxResultsSummary.get_Output [method]: unit -> TestRunner.TrxOutput + + TestRunner.TrxResultsSummary.get_RunInfos [method]: unit -> TestRunner.TrxRunInfo list + + TestRunner.TrxRunInfo inherit obj, implements TestRunner.TrxRunInfo System.IEquatable, System.Collections.IStructuralEquatable, TestRunner.TrxRunInfo System.IComparable, System.IComparable, System.Collections.IStructuralComparable + + TestRunner.TrxRunInfo..ctor [constructor]: (string, TestRunner.TrxOutcome, System.DateTimeOffset, string) + + TestRunner.TrxRunInfo.ComputerName [property]: [read-only] string + + TestRunner.TrxRunInfo.Outcome [property]: [read-only] TestRunner.TrxOutcome + + TestRunner.TrxRunInfo.Text [property]: [read-only] string + + TestRunner.TrxRunInfo.Timestamp [property]: [read-only] System.DateTimeOffset + + TestRunner.TrxRunInfo.get_ComputerName [method]: unit -> string + + TestRunner.TrxRunInfo.get_Outcome [method]: unit -> TestRunner.TrxOutcome + + TestRunner.TrxRunInfo.get_Text [method]: unit -> string + + TestRunner.TrxRunInfo.get_Timestamp [method]: unit -> System.DateTimeOffset + + TestRunner.TrxTestEntry inherit obj, implements TestRunner.TrxTestEntry System.IEquatable, System.Collections.IStructuralEquatable, TestRunner.TrxTestEntry System.IComparable, System.IComparable, System.Collections.IStructuralComparable + + TestRunner.TrxTestEntry..ctor [constructor]: (System.Guid, System.Guid, System.Guid) + + TestRunner.TrxTestEntry.ExecutionId [property]: [read-only] System.Guid + + TestRunner.TrxTestEntry.TestId [property]: [read-only] System.Guid + + TestRunner.TrxTestEntry.TestListId [property]: [read-only] System.Guid + + TestRunner.TrxTestEntry.get_ExecutionId [method]: unit -> System.Guid + + TestRunner.TrxTestEntry.get_TestId [method]: unit -> System.Guid + + TestRunner.TrxTestEntry.get_TestListId [method]: unit -> System.Guid + + TestRunner.TrxTestList inherit obj, implements TestRunner.TrxTestList System.IEquatable, System.Collections.IStructuralEquatable, TestRunner.TrxTestList System.IComparable, System.IComparable, System.Collections.IStructuralComparable + + TestRunner.TrxTestList..ctor [constructor]: (string, System.Guid) + + TestRunner.TrxTestList.Id [property]: [read-only] System.Guid + + TestRunner.TrxTestList.Name [property]: [read-only] string + + TestRunner.TrxTestList.get_Id [method]: unit -> System.Guid + + TestRunner.TrxTestList.get_Name [method]: unit -> string + + TestRunner.TrxTestMethod inherit obj, implements TestRunner.TrxTestMethod System.IEquatable, System.Collections.IStructuralEquatable + + TestRunner.TrxTestMethod..ctor [constructor]: (System.IO.FileInfo, System.Uri, string, string) + + TestRunner.TrxTestMethod.AdapterTypeName [property]: [read-only] System.Uri + + TestRunner.TrxTestMethod.ClassName [property]: [read-only] string + + TestRunner.TrxTestMethod.CodeBase [property]: [read-only] System.IO.FileInfo + + TestRunner.TrxTestMethod.Name [property]: [read-only] string + + TestRunner.TrxTestMethod.get_AdapterTypeName [method]: unit -> System.Uri + + TestRunner.TrxTestMethod.get_ClassName [method]: unit -> string + + TestRunner.TrxTestMethod.get_CodeBase [method]: unit -> System.IO.FileInfo + + TestRunner.TrxTestMethod.get_Name [method]: unit -> string + + TestRunner.TrxTestOutcome inherit obj, implements TestRunner.TrxTestOutcome System.IEquatable, System.Collections.IStructuralEquatable, TestRunner.TrxTestOutcome System.IComparable, System.IComparable, System.Collections.IStructuralComparable - union type with 2 cases + + TestRunner.TrxTestOutcome+Tags inherit obj + + TestRunner.TrxTestOutcome+Tags.Failed [static field]: int = 1 + + TestRunner.TrxTestOutcome+Tags.Passed [static field]: int = 0 + + TestRunner.TrxTestOutcome.Failed [static property]: [read-only] TestRunner.TrxTestOutcome + + TestRunner.TrxTestOutcome.IsFailed [property]: [read-only] bool + + TestRunner.TrxTestOutcome.IsPassed [property]: [read-only] bool + + TestRunner.TrxTestOutcome.Passed [static property]: [read-only] TestRunner.TrxTestOutcome + + TestRunner.TrxTestOutcome.Tag [property]: [read-only] int + + TestRunner.TrxTestOutcome.get_Failed [static method]: unit -> TestRunner.TrxTestOutcome + + TestRunner.TrxTestOutcome.get_IsFailed [method]: unit -> bool + + TestRunner.TrxTestOutcome.get_IsPassed [method]: unit -> bool + + TestRunner.TrxTestOutcome.get_Passed [static method]: unit -> TestRunner.TrxTestOutcome + + TestRunner.TrxTestOutcome.get_Tag [method]: unit -> int + + TestRunner.TrxTestSettings inherit obj, implements TestRunner.TrxTestSettings System.IEquatable, System.Collections.IStructuralEquatable, TestRunner.TrxTestSettings System.IComparable, System.IComparable, System.Collections.IStructuralComparable + + TestRunner.TrxTestSettings..ctor [constructor]: (string, System.Guid, TestRunner.TrxDeployment) + + TestRunner.TrxTestSettings.Deployment [property]: [read-only] TestRunner.TrxDeployment + + TestRunner.TrxTestSettings.Id [property]: [read-only] System.Guid + + TestRunner.TrxTestSettings.Name [property]: [read-only] string + + TestRunner.TrxTestSettings.get_Deployment [method]: unit -> TestRunner.TrxDeployment + + TestRunner.TrxTestSettings.get_Id [method]: unit -> System.Guid + + TestRunner.TrxTestSettings.get_Name [method]: unit -> string + + TestRunner.TrxUnitTest inherit obj, implements TestRunner.TrxUnitTest System.IEquatable, System.Collections.IStructuralEquatable + + TestRunner.TrxUnitTest..ctor [constructor]: (string, System.IO.FileInfo, System.Guid, System.Guid, TestRunner.TrxTestMethod) + + TestRunner.TrxUnitTest.ExecutionId [property]: [read-only] System.Guid + + TestRunner.TrxUnitTest.Id [property]: [read-only] System.Guid + + TestRunner.TrxUnitTest.Name [property]: [read-only] string + + TestRunner.TrxUnitTest.Storage [property]: [read-only] System.IO.FileInfo + + TestRunner.TrxUnitTest.TestMethod [property]: [read-only] TestRunner.TrxTestMethod + + TestRunner.TrxUnitTest.get_ExecutionId [method]: unit -> System.Guid + + TestRunner.TrxUnitTest.get_Id [method]: unit -> System.Guid + + TestRunner.TrxUnitTest.get_Name [method]: unit -> string + + TestRunner.TrxUnitTest.get_Storage [method]: unit -> System.IO.FileInfo + + TestRunner.TrxUnitTest.get_TestMethod [method]: unit -> TestRunner.TrxTestMethod + + TestRunner.TrxUnitTestResult inherit obj, implements TestRunner.TrxUnitTestResult System.IEquatable, System.Collections.IStructuralEquatable + + TestRunner.TrxUnitTestResult..ctor [constructor]: (System.Guid, System.Guid, string, string, System.TimeSpan, System.DateTimeOffset, System.DateTimeOffset, System.Guid, TestRunner.TrxTestOutcome, System.Guid, System.Guid, TestRunner.TrxOutput option) + + TestRunner.TrxUnitTestResult.ComputerName [property]: [read-only] string + + TestRunner.TrxUnitTestResult.Duration [property]: [read-only] System.TimeSpan + + TestRunner.TrxUnitTestResult.EndTime [property]: [read-only] System.DateTimeOffset + + TestRunner.TrxUnitTestResult.ExecutionId [property]: [read-only] System.Guid + + TestRunner.TrxUnitTestResult.Outcome [property]: [read-only] TestRunner.TrxTestOutcome + + TestRunner.TrxUnitTestResult.Output [property]: [read-only] TestRunner.TrxOutput option + + TestRunner.TrxUnitTestResult.RelativeResultsDirectory [property]: [read-only] System.Guid + + TestRunner.TrxUnitTestResult.StartTime [property]: [read-only] System.DateTimeOffset + + TestRunner.TrxUnitTestResult.TestId [property]: [read-only] System.Guid + + TestRunner.TrxUnitTestResult.TestListId [property]: [read-only] System.Guid + + TestRunner.TrxUnitTestResult.TestName [property]: [read-only] string + + TestRunner.TrxUnitTestResult.TestType [property]: [read-only] System.Guid + + TestRunner.TrxUnitTestResult.get_ComputerName [method]: unit -> string + + TestRunner.TrxUnitTestResult.get_Duration [method]: unit -> System.TimeSpan + + TestRunner.TrxUnitTestResult.get_EndTime [method]: unit -> System.DateTimeOffset + + TestRunner.TrxUnitTestResult.get_ExecutionId [method]: unit -> System.Guid + + TestRunner.TrxUnitTestResult.get_Outcome [method]: unit -> TestRunner.TrxTestOutcome + + TestRunner.TrxUnitTestResult.get_Output [method]: unit -> TestRunner.TrxOutput option + + TestRunner.TrxUnitTestResult.get_RelativeResultsDirectory [method]: unit -> System.Guid + + TestRunner.TrxUnitTestResult.get_StartTime [method]: unit -> System.DateTimeOffset + + TestRunner.TrxUnitTestResult.get_TestId [method]: unit -> System.Guid + + TestRunner.TrxUnitTestResult.get_TestListId [method]: unit -> System.Guid + + TestRunner.TrxUnitTestResult.get_TestName [method]: unit -> string + + TestRunner.TrxUnitTestResult.get_TestType [method]: unit -> System.Guid + + at Microsoft.FSharp.Core.PrintfModule.PrintFormatToStringThenFail@1448.Invoke(String message) + at ApiSurface.SurfaceComparisonModule.AssertIdentical(SurfaceComparison _arg1) in /_//ApiSurface/SurfaceComparison.fs:line 41 + at TestRunner.Test.TestSurface.Ensure API surface has not been modified() in /Users/patrick/Documents/GitHub/TestRunner/TestRunner/TestRunner.Test/TestSurface.fs:line 12 + at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor) + at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr) + + + + + + + + Not yet published + + Not yet published + + + + + + + System.Exception : Unexpected difference. + +The following 3 member(s) are only in 'TestRunner.Lib.xml': + + T:TestRunner.FilterModule + + T:TestRunner.SingleTestMethodModule + + T:TestRunner.TestFixtureModule + +The following 74 member(s) are only in 'TestRunner.Lib.dll': + - T:TestRunner.TrxCounters + - P:TestRunner.TrxCounters.Aborted + - P:TestRunner.TrxCounters.Completed + - P:TestRunner.TrxCounters.Disconnected + - P:TestRunner.TrxCounters.Errors + - P:TestRunner.TrxCounters.Executed + - P:TestRunner.TrxCounters.Failed + - P:TestRunner.TrxCounters.InProgress + - P:TestRunner.TrxCounters.Inconclusive + - P:TestRunner.TrxCounters.NotExecuted + - P:TestRunner.TrxCounters.NotRunnable + - P:TestRunner.TrxCounters.Passed + - P:TestRunner.TrxCounters.PassedButRunAborted + - P:TestRunner.TrxCounters.Pending + - P:TestRunner.TrxCounters.Timeout + - P:TestRunner.TrxCounters.Total + - P:TestRunner.TrxCounters.Warning + - T:TestRunner.TrxDeployment + - P:TestRunner.TrxDeployment.RunDeploymentRoot + - T:TestRunner.TrxOutcome + - T:TestRunner.TrxOutcome.Completed + - T:TestRunner.TrxOutcome.Failed + - T:TestRunner.TrxOutcome.Warning + - T:TestRunner.TrxReport + - P:TestRunner.TrxReport.Id + - P:TestRunner.TrxReport.Name + - P:TestRunner.TrxReport.Results + - P:TestRunner.TrxReport.ResultsSummary + - P:TestRunner.TrxReport.Settings + - P:TestRunner.TrxReport.TestDefinitions + - P:TestRunner.TrxReport.TestEntries + - P:TestRunner.TrxReport.TestLists + - P:TestRunner.TrxReport.Times + - P:TestRunner.TrxReportTimes.Queuing + - T:TestRunner.TrxResultsSummary + - P:TestRunner.TrxResultsSummary.Counters + - P:TestRunner.TrxResultsSummary.Outcome + - P:TestRunner.TrxResultsSummary.Output + - P:TestRunner.TrxResultsSummary.RunInfos + - P:TestRunner.TrxRunInfo.Outcome + - T:TestRunner.TrxTestEntry + - P:TestRunner.TrxTestEntry.ExecutionId + - P:TestRunner.TrxTestEntry.TestId + - P:TestRunner.TrxTestEntry.TestListId + - T:TestRunner.TrxTestList + - P:TestRunner.TrxTestList.Id + - P:TestRunner.TrxTestList.Name + - T:TestRunner.TrxTestMethod + - P:TestRunner.TrxTestMethod.AdapterTypeName + - P:TestRunner.TrxTestMethod.ClassName + - P:TestRunner.TrxTestMethod.CodeBase + - P:TestRunner.TrxTestMethod.Name + - T:TestRunner.TrxTestSettings + - P:TestRunner.TrxTestSettings.Deployment + - P:TestRunner.TrxTestSettings.Id + - P:TestRunner.TrxTestSettings.Name + - T:TestRunner.TrxUnitTest + - P:TestRunner.TrxUnitTest.ExecutionId + - P:TestRunner.TrxUnitTest.Id + - P:TestRunner.TrxUnitTest.Name + - P:TestRunner.TrxUnitTest.Storage + - P:TestRunner.TrxUnitTest.TestMethod + - T:TestRunner.TrxUnitTestResult + - P:TestRunner.TrxUnitTestResult.ComputerName + - P:TestRunner.TrxUnitTestResult.Duration + - P:TestRunner.TrxUnitTestResult.EndTime + - P:TestRunner.TrxUnitTestResult.ExecutionId + - P:TestRunner.TrxUnitTestResult.Outcome + - P:TestRunner.TrxUnitTestResult.Output + - P:TestRunner.TrxUnitTestResult.RelativeResultsDirectory + - P:TestRunner.TrxUnitTestResult.StartTime + - P:TestRunner.TrxUnitTestResult.TestId + - P:TestRunner.TrxUnitTestResult.TestListId + - P:TestRunner.TrxUnitTestResult.TestType + + at Microsoft.FSharp.Core.PrintfModule.PrintFormatToStringThenFail@1448.Invoke(String message) + at ApiSurface.SurfaceComparisonModule.assertNoneRemoved$cont@48(Boolean shouldPrint, SurfaceComparison comparison, Unit unitVar) in /_//ApiSurface/SurfaceComparison.fs:line 70 + at TestRunner.Test.TestSurface.Ensure public API is fully documented() in /Users/patrick/Documents/GitHub/TestRunner/TestRunner/TestRunner.Test/TestSurface.fs:line 20 + at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor) + at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr) + + + + + + + + + + + + + + + + + + + Ok, passed 100 tests. + + + + + + + + + + Ok, passed 100 tests. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NUnit Adapter 4.5.0.0: Test execution started +Running all tests in /Users/patrick/Documents/GitHub/TestRunner/TestRunner/TestRunner.Test/bin/Debug/net8.0/TestRunner.Test.dll + NUnit3TestExecutor discovered 26 of 28 NUnit test cases using Current Discovery mode, Non-Explicit run +Ensure version is monotonic: Not yet published +NUnit Adapter 4.5.0.0: Test execution complete + + + + + Ok, passed 100 tests. + + + + Ok, passed 100 tests. + + + + + + diff --git a/TestRunner/TestRunner.Test/TestList.fs b/TestRunner/TestRunner.Test/TestList.fs index 37d75d9..1d27004 100644 --- a/TestRunner/TestRunner.Test/TestList.fs +++ b/TestRunner/TestRunner.Test/TestList.fs @@ -21,6 +21,8 @@ module TestList = [] let ``each combination is drawn from the right set`` () = let property (xs : int list list) = + let xs = if xs.Length > 6 then xs |> List.take 6 else xs + let xs = xs |> List.map (fun xs -> if xs.Length > 6 then xs |> List.take 6 else xs) let combs = List.combinations xs for comb in combs do diff --git a/TestRunner/TestRunner.Test/TestRunner.Test.fsproj b/TestRunner/TestRunner.Test/TestRunner.Test.fsproj index 30c55ec..fc31665 100644 --- a/TestRunner/TestRunner.Test/TestRunner.Test.fsproj +++ b/TestRunner/TestRunner.Test/TestRunner.Test.fsproj @@ -2,15 +2,17 @@ net8.0 - false true + + + diff --git a/TestRunner/TestRunner.Test/TestTrx.fs b/TestRunner/TestRunner.Test/TestTrx.fs new file mode 100644 index 0000000..da9381c --- /dev/null +++ b/TestRunner/TestRunner.Test/TestTrx.fs @@ -0,0 +1,180 @@ +namespace TestRunner.Test + +open System +open NuGet.Packaging.Signing +open TestRunner +open NUnit.Framework +open FsUnitTyped + +[] +module TestTrx = + + [] + let ``Can parse the first example`` () = + let resource = EmbeddedResource.read "Example1.trx" + + let parsed = + match TrxReport.parse resource with + | Error e -> failwith $"Expected successful parse: %s{e}" + | Ok r -> r + + parsed.Id.ToString () |> shouldEqual "ccbc0600-fe62-46d9-a3d9-6551da338a3e" + parsed.Name |> shouldEqual "@Patricks-MacBook-Pro 2024-06-06 22:51:47" + + parsed.Times + |> shouldEqual + { + Creation = DateTimeOffset.Parse "2024-06-06T22:51:47.0155230+01:00" + Queuing = DateTimeOffset.Parse "2024-06-06T22:51:47.0155230+01:00" + Start = DateTimeOffset.Parse "2024-06-06T22:51:46.6031940+01:00" + Finish = DateTimeOffset.Parse "2024-06-06T22:51:47.1574390+01:00" + } + + parsed.Settings.Name |> shouldEqual "default" + + parsed.Settings.Id.ToString () + |> shouldEqual "542f16c8-664f-40a7-9829-0d13fc32f19f" + + parsed.Settings.Deployment + |> shouldEqual + { + RunDeploymentRoot = "_Patricks-MacBook-Pro_2024-06-06_22_51_47" + } + + do + parsed.TestDefinitions |> shouldHaveLength 28 + let defn = parsed.TestDefinitions.[0] + defn.Name |> shouldEqual "Ensure API surface has not been modified" + + defn.Storage + |> shouldEqual + "/users/patrick/documents/github/testrunner/testrunner/testrunner.test/bin/debug/net8.0/testrunner.test.dll" + + defn.Id.ToString () |> shouldEqual "1bd5500a-baad-f37f-76eb-3905c00e9884" + + defn.Execution.Id.ToString () + |> shouldEqual "663a073f-f01c-400e-bffe-dd8d68355b83" + + defn.TestMethod + |> shouldEqual + { + CodeBase = + "/Users/patrick/Documents/GitHub/TestRunner/TestRunner/TestRunner.Test/bin/Debug/net8.0/TestRunner.Test.dll" + AdapterTypeName = Uri "executor://nunit3testexecutor/" + ClassName = "TestRunner.Test.TestSurface" + Name = "Ensure API surface has not been modified" + } + + // TODO: test contents of UnitTestResults + parsed.Results |> shouldHaveLength 28 + + do + parsed.Results.[5].ExecutionId.ToString () + |> shouldEqual "663a073f-f01c-400e-bffe-dd8d68355b83" + + parsed.Results.[5].TestId.ToString () + |> shouldEqual "1bd5500a-baad-f37f-76eb-3905c00e9884" + + parsed.Results.[5].TestName + |> shouldEqual "Ensure API surface has not been modified" + + parsed.Results.[5].ComputerName |> shouldEqual "Patricks-MacBook-Pro" + parsed.Results.[5].Duration |> shouldEqual (TimeSpan.Parse "00:00:00.0329610") + + parsed.Results.[5].StartTime + |> shouldEqual (DateTimeOffset.Parse "2024-06-06T22:51:47.0636370+01:00") + + parsed.Results.[5].EndTime + |> shouldEqual (DateTimeOffset.Parse "2024-06-06T22:51:47.0965980+01:00") + + parsed.Results.[5].TestType.ToString () + |> shouldEqual "13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" + + parsed.Results.[5].Outcome |> shouldEqual TrxTestOutcome.Failed + + parsed.Results.[5].TestListId.ToString () + |> shouldEqual "8c84fa94-04c1-424b-9868-57a2d4851a1d" + + parsed.Results.[5].RelativeResultsDirectory + |> shouldEqual "663a073f-f01c-400e-bffe-dd8d68355b83" + + match parsed.Results.[5].Output with + | None -> failwith "expected output" + | Some output -> + match output.ErrorInfo with + | None -> failwith "expected errorinfo" + | Some ei -> + let message = ei.Message |> Option.get + + message.StartsWith ("System.Exception : Unexpected", StringComparison.Ordinal) + |> shouldEqual true + + message |> shouldContainText "unit -> string" + let stackTrace = ei.StackTrace |> Option.get + stackTrace |> shouldContainText "Ensure API surface has not been modified()" + + do + parsed.TestEntries |> shouldHaveLength 28 + let testEntry = parsed.TestEntries.[0] + + testEntry.TestId.ToString () + |> shouldEqual "099ab0d7-e616-e3ed-18e1-8238c2e316c4" + + testEntry.ExecutionId.ToString () + |> shouldEqual "0f224761-6bbd-43f2-9559-1327816450d9" + + testEntry.TestListId.ToString () + |> shouldEqual "8c84fa94-04c1-424b-9868-57a2d4851a1d" + + do + parsed.TestLists |> shouldHaveLength 2 + parsed.TestLists.[0].Name |> shouldEqual "Results Not in a List" + + parsed.TestLists.[0].Id.ToString () + |> shouldEqual "8c84fa94-04c1-424b-9868-57a2d4851a1d" + + parsed.TestLists.[1].Name |> shouldEqual "All Loaded Results" + + parsed.TestLists.[1].Id.ToString () + |> shouldEqual "19431567-8539-422a-85d7-44ee4e166bda" + + let expectedResultsSummary = + { + Counters = + { TrxCounters.Zero with + Total = 28u + Executed = 26u + Passed = 24u + Failed = 2u + } + Output = + { + StdOut = + Some + """NUnit Adapter 4.5.0.0: Test execution started +Running all tests in /Users/patrick/Documents/GitHub/TestRunner/TestRunner/TestRunner.Test/bin/Debug/net8.0/TestRunner.Test.dll + NUnit3TestExecutor discovered 26 of 28 NUnit test cases using Current Discovery mode, Non-Explicit run +Ensure version is monotonic: Not yet published +NUnit Adapter 4.5.0.0: Test execution complete +""" + ErrorInfo = None + } + Outcome = TrxOutcome.Failed + RunInfos = + [ + { + ComputerName = "Patricks-MacBook-Pro" + Outcome = TrxOutcome.Warning + Timestamp = DateTimeOffset.Parse "2024-06-06T22:51:47.0471530+01:00" + Text = $"Ok, passed 100 tests.%s{Environment.NewLine}" + } + { + ComputerName = "Patricks-MacBook-Pro" + Outcome = TrxOutcome.Warning + Timestamp = DateTimeOffset.Parse "2024-06-06T22:51:47.0640360+01:00" + Text = $"Ok, passed 100 tests.%s{Environment.NewLine}" + } + ] + } + + parsed.ResultsSummary |> shouldEqual expectedResultsSummary