Compare commits

...

2 Commits

Author SHA1 Message Date
Patrick Stevens
2e066a1a9a Recognise parallelism attributes (#76) 2024-06-15 23:26:19 +01:00
Patrick Stevens
6468a301b9 Capture stderr too (#75) 2024-06-15 23:06:11 +01:00
11 changed files with 284 additions and 37 deletions

View File

@@ -10,6 +10,7 @@
<ItemGroup>
<Compile Include="NoAttribute.fs" />
<Compile Include="Inconclusive.fs" />
<Compile Include="TestParallel.fs" />
<Compile Include="TestParameterisedFixture.fs" />
<Compile Include="TestSetUp.fs" />
<Compile Include="TestValues.fs" />

49
Consumer/TestParallel.fs Normal file
View File

@@ -0,0 +1,49 @@
namespace Consumer
open NUnit.Framework
open FsUnitTyped
[<TestFixture>]
[<Parallelizable>]
module TestParallelDefault =
let defaults = List.init 100 id
[<TestCaseSource(nameof defaults)>]
let ``Default thing`` (i : int) = i |> shouldEqual i
[<TestFixture>]
[<Parallelizable(ParallelScope.All)>]
module TestParallelAllScope =
let defaults = List.init 100 id
[<TestCaseSource(nameof defaults)>]
let ``Default thing`` (i : int) = i |> shouldEqual i
[<TestFixture>]
[<Parallelizable(ParallelScope.Self)>]
module TestParallelSelfScope =
let defaults = List.init 100 id
[<TestCaseSource(nameof defaults)>]
let ``Default thing`` (i : int) = i |> shouldEqual i
[<TestFixture>]
[<Parallelizable(ParallelScope.Children)>]
module TestParallelChildrenScope =
let defaults = List.init 100 id
[<TestCaseSource(nameof defaults)>]
let ``Default thing`` (i : int) = i |> shouldEqual i
[<TestFixture>]
[<Parallelizable(ParallelScope.Fixtures)>]
module TestParallelFixturesScope =
let defaults = List.init 100 id
[<TestCaseSource(nameof defaults)>]
let ``Default thing`` (i : int) = i |> shouldEqual i

View File

@@ -8,3 +8,9 @@ To supply special characters in a string, XML-encode them and `"quote"` the stri
We support at least the [documented `dotnet test` examples](https://learn.microsoft.com/en-us/dotnet/core/testing/selective-unit-tests).
However, we would recommend phrasing some of them differently, for maximum peace of mind:
* `FullyQualifiedName=MyNamespace.MyTestsClass<ParameterType1%2CParameterType2>.MyTestMethod`. This would be better phrased with quotes and escaping as `FullyQualifiedName="MyNamespace.MyTestsClass&lt;ParameterType1%2CParameterType2&gt;.MyTestMethod"`
## Parallelism
WoofWare.NUnitTestRunner has *limited* support for parallelism.
By default, we run tests serially; we may or may not respect the NUnit parallelism attributes to any given extent (but we will never incorrectly run tests in parallel).
For example, as of this writing, we do not run any tests in parallel (but the internal infrastructure is set up so that we will be able to do this soon).

View File

@@ -31,6 +31,36 @@ type Combinatorial =
/// each", and so on. Spare slots are filled with `Unchecked.defaultof<_>`.
| Sequential
/// Describes the level of parallelism permitted in some context.
[<RequireQualifiedAccess>]
type ClassParallelScope =
/// "I may be run in parallel with other tests, although my children might not be able to run in parallel with each
/// other".
| Self
/// "The set of things I contain may be run in parallel with itself".
| Children
/// "Fixtures within me may be run in parallel with each other, but the tests within a given fixture might not
/// be runnable in parallel with each other".
| Fixtures
/// "All my descendents are happy to run in parallel with anything else, and also so am I".
| All
/// Describes the level of parallelism permitted within an assembly.
[<RequireQualifiedAccess>]
type AssemblyParallelScope =
/// "The set of things I contain may be run in parallel with itself".
| Children
/// "Fixtures within me may be run in parallel with each other, but the tests within a given fixture might not
/// necessarily be runnable in parallel with each other".
| Fixtures
/// Describes whether a test can be run concurrently with other tests.
type Parallelizable<'scope> =
/// This test is happy, under some conditions (specified by the scope), to be run alongside other tests.
| Yes of 'scope
/// This test must always be run on its own.
| No
/// A single method or member which holds some tests. (Often such a member will represent only one test, but e.g.
/// if it has [<TestCaseSource>] then it represents multiple tests.)
type SingleTestMethod =
@@ -49,6 +79,8 @@ type SingleTestMethod =
/// If this test has data supplied by `[<Value>]` annotations, specifies how those annotations are combined
/// to produce the complete collection of args.
Combinatorial : Combinatorial option
/// If this test has declared a parallelisability, that goes here.
Parallelize : Parallelizable<unit> option
}
/// Human-readable name of this test method.
@@ -85,10 +117,12 @@ type TestFixture =
Parameters : obj list list
/// The individual test methods present within this fixture.
Tests : SingleTestMethod list
/// If this fixture has declared a parallelisability, that goes here.
Parallelize : Parallelizable<ClassParallelScope> option
}
/// A test fixture about which we know nothing. No tests, no setup/teardown.
static member Empty (ty : Type) (args : obj list list) =
static member Empty (ty : Type) (par : Parallelizable<ClassParallelScope> option) (args : obj list list) =
{
ContainingAssembly = ty.Assembly
Type = ty
@@ -99,6 +133,7 @@ type TestFixture =
TearDown = []
Parameters = args
Tests = []
Parallelize = par
}
/// User code in the unit under test has failed somehow.

View File

@@ -18,15 +18,15 @@ module SingleTestMethod =
(attrs : CustomAttributeData list)
: SingleTestMethod option * CustomAttributeData list
=
let remaining, isTest, sources, hasData, modifiers, categories, repeat, comb =
(([], false, [], None, [], [], None, None), attrs)
||> List.fold (fun (remaining, isTest, sources, hasData, mods, cats, repeat, comb) attr ->
let remaining, isTest, sources, hasData, modifiers, categories, repeat, comb, par =
(([], false, [], None, [], [], None, None, None), attrs)
||> List.fold (fun (remaining, isTest, sources, hasData, mods, cats, repeat, comb, par) attr ->
match attr.AttributeType.FullName with
| "NUnit.Framework.TestAttribute" ->
if attr.ConstructorArguments.Count > 0 then
failwith "Unexpectedly got arguments to the Test attribute"
(remaining, true, sources, hasData, mods, cats, repeat, comb)
(remaining, true, sources, hasData, mods, cats, repeat, comb, par)
| "NUnit.Framework.TestCaseAttribute" ->
let args = attr.ConstructorArguments |> Seq.map _.Value |> Seq.toList
@@ -40,62 +40,73 @@ module SingleTestMethod =
| _ -> args
match hasData with
| None -> (remaining, isTest, sources, Some [ List.ofSeq args ], mods, cats, repeat, comb)
| None -> (remaining, isTest, sources, Some [ List.ofSeq args ], mods, cats, repeat, comb, par)
| Some existing ->
(remaining, isTest, sources, Some ((List.ofSeq args) :: existing), mods, cats, repeat, comb)
let args = (List.ofSeq args) :: existing |> Some
(remaining, isTest, sources, args, mods, cats, repeat, comb, par)
| "NUnit.Framework.TestCaseSourceAttribute" ->
let arg = attr.ConstructorArguments |> Seq.exactlyOne |> _.Value |> unbox<string>
(remaining, isTest, arg :: sources, hasData, mods, cats, repeat, comb)
(remaining, isTest, arg :: sources, hasData, mods, cats, repeat, comb, par)
| "NUnit.Framework.ExplicitAttribute" ->
let reason =
attr.ConstructorArguments
|> Seq.tryHead
|> Option.map (_.Value >> unbox<string>)
(remaining, isTest, sources, hasData, (Modifier.Explicit reason) :: mods, cats, repeat, comb)
(remaining, isTest, sources, hasData, (Modifier.Explicit reason) :: mods, cats, repeat, comb, par)
| "NUnit.Framework.IgnoreAttribute" ->
let reason =
attr.ConstructorArguments
|> Seq.tryHead
|> Option.map (_.Value >> unbox<string>)
(remaining, isTest, sources, hasData, (Modifier.Ignored reason) :: mods, cats, repeat, comb)
(remaining, isTest, sources, hasData, (Modifier.Ignored reason) :: mods, cats, repeat, comb, par)
| "NUnit.Framework.CategoryAttribute" ->
let category =
attr.ConstructorArguments |> Seq.exactlyOne |> _.Value |> unbox<string>
(remaining, isTest, sources, hasData, mods, category :: cats, repeat, comb)
(remaining, isTest, sources, hasData, mods, category :: cats, repeat, comb, par)
| "NUnit.Framework.RepeatAttribute" ->
match repeat with
| Some _ -> failwith $"Got RepeatAttribute multiple times on %s{method.Name}"
| None ->
let repeat = attr.ConstructorArguments |> Seq.exactlyOne |> _.Value |> unbox<int>
(remaining, isTest, sources, hasData, mods, cats, Some repeat, comb)
(remaining, isTest, sources, hasData, mods, cats, Some repeat, comb, par)
| "NUnit.Framework.CombinatorialAttribute" ->
match comb with
| Some _ ->
failwith $"Got CombinatorialAttribute or SequentialAttribute multiple times on %s{method.Name}"
| None ->
(remaining, isTest, sources, hasData, mods, cats, repeat, Some Combinatorial.Combinatorial)
(remaining, isTest, sources, hasData, mods, cats, repeat, Some Combinatorial.Combinatorial, par)
| "NUnit.Framework.SequentialAttribute" ->
match comb with
| Some _ ->
failwith $"Got CombinatorialAttribute or SequentialAttribute multiple times on %s{method.Name}"
| None -> (remaining, isTest, sources, hasData, mods, cats, repeat, Some Combinatorial.Sequential)
| None ->
(remaining, isTest, sources, hasData, mods, cats, repeat, Some Combinatorial.Sequential, par)
| "NUnit.Framework.NonParallelizableAttribute" ->
match par with
| Some _ -> failwith $"Got a parallelization attribute multiple times on %s{method.Name}"
| None -> (remaining, isTest, sources, hasData, mods, cats, repeat, comb, Some Parallelizable.No)
| "NUnit.Framework.ParallelizableAttribute" ->
match par with
| Some _ -> failwith $"Got multiple parallelization attributes on %s{method.Name}"
| None ->
(remaining, isTest, sources, hasData, mods, cats, repeat, comb, Some (Parallelizable.Yes ()))
| s when s.StartsWith ("NUnit.Framework", StringComparison.Ordinal) ->
failwith $"Unrecognised attribute on function %s{method.Name}: %s{attr.AttributeType.FullName}"
| _ -> (attr :: remaining, isTest, sources, hasData, mods, cats, repeat, comb)
| _ -> (attr :: remaining, isTest, sources, hasData, mods, cats, repeat, comb, par)
)
let test =
match isTest, sources, hasData, modifiers, categories, repeat, comb with
| _, _ :: _, Some _, _, _, _, _ ->
match isTest, sources, hasData, modifiers, categories, repeat, comb, par with
| _, _ :: _, Some _, _, _, _, _, _ ->
failwith
$"Test '%s{method.Name}' unexpectedly has both TestData and TestCaseSource; not currently supported"
| false, [], None, [], _, _, _ -> None
| _, _ :: _, None, mods, categories, repeat, comb ->
| false, [], None, [], _, _, _, _ -> None
| _, _ :: _, None, mods, categories, repeat, comb, par ->
{
Kind = TestKind.Source sources
Method = method
@@ -103,9 +114,10 @@ module SingleTestMethod =
Categories = categories @ parentCategories
Repeat = repeat
Combinatorial = comb
Parallelize = par
}
|> Some
| _, [], Some data, mods, categories, repeat, comb ->
| _, [], Some data, mods, categories, repeat, comb, par ->
{
Kind = TestKind.Data data
Method = method
@@ -113,9 +125,10 @@ module SingleTestMethod =
Categories = categories @ parentCategories
Repeat = repeat
Combinatorial = comb
Parallelize = par
}
|> Some
| true, [], None, mods, categories, repeat, comb ->
| true, [], None, mods, categories, repeat, comb, par ->
{
Kind = TestKind.Single
Method = method
@@ -123,9 +136,10 @@ module SingleTestMethod =
Categories = categories @ parentCategories
Repeat = repeat
Combinatorial = comb
Parallelize = par
}
|> Some
| false, [], None, _ :: _, _, _, _ ->
| false, [], None, _ :: _, _, _, _, _ ->
failwith
$"Unexpectedly got test modifiers but no test settings on '%s{method.Name}', which you probably didn't intend."

View File

@@ -1,3 +1,41 @@
WoofWare.NUnitTestRunner.AssemblyParallelScope inherit obj, implements WoofWare.NUnitTestRunner.AssemblyParallelScope System.IEquatable, System.Collections.IStructuralEquatable, WoofWare.NUnitTestRunner.AssemblyParallelScope System.IComparable, System.IComparable, System.Collections.IStructuralComparable - union type with 2 cases
WoofWare.NUnitTestRunner.AssemblyParallelScope+Tags inherit obj
WoofWare.NUnitTestRunner.AssemblyParallelScope+Tags.Children [static field]: int = 0
WoofWare.NUnitTestRunner.AssemblyParallelScope+Tags.Fixtures [static field]: int = 1
WoofWare.NUnitTestRunner.AssemblyParallelScope.Children [static property]: [read-only] WoofWare.NUnitTestRunner.AssemblyParallelScope
WoofWare.NUnitTestRunner.AssemblyParallelScope.Fixtures [static property]: [read-only] WoofWare.NUnitTestRunner.AssemblyParallelScope
WoofWare.NUnitTestRunner.AssemblyParallelScope.get_Children [static method]: unit -> WoofWare.NUnitTestRunner.AssemblyParallelScope
WoofWare.NUnitTestRunner.AssemblyParallelScope.get_Fixtures [static method]: unit -> WoofWare.NUnitTestRunner.AssemblyParallelScope
WoofWare.NUnitTestRunner.AssemblyParallelScope.get_IsChildren [method]: unit -> bool
WoofWare.NUnitTestRunner.AssemblyParallelScope.get_IsFixtures [method]: unit -> bool
WoofWare.NUnitTestRunner.AssemblyParallelScope.get_Tag [method]: unit -> int
WoofWare.NUnitTestRunner.AssemblyParallelScope.IsChildren [property]: [read-only] bool
WoofWare.NUnitTestRunner.AssemblyParallelScope.IsFixtures [property]: [read-only] bool
WoofWare.NUnitTestRunner.AssemblyParallelScope.Tag [property]: [read-only] int
WoofWare.NUnitTestRunner.ClassParallelScope inherit obj, implements WoofWare.NUnitTestRunner.ClassParallelScope System.IEquatable, System.Collections.IStructuralEquatable, WoofWare.NUnitTestRunner.ClassParallelScope System.IComparable, System.IComparable, System.Collections.IStructuralComparable - union type with 4 cases
WoofWare.NUnitTestRunner.ClassParallelScope+Tags inherit obj
WoofWare.NUnitTestRunner.ClassParallelScope+Tags.All [static field]: int = 3
WoofWare.NUnitTestRunner.ClassParallelScope+Tags.Children [static field]: int = 1
WoofWare.NUnitTestRunner.ClassParallelScope+Tags.Fixtures [static field]: int = 2
WoofWare.NUnitTestRunner.ClassParallelScope+Tags.Self [static field]: int = 0
WoofWare.NUnitTestRunner.ClassParallelScope.All [static property]: [read-only] WoofWare.NUnitTestRunner.ClassParallelScope
WoofWare.NUnitTestRunner.ClassParallelScope.Children [static property]: [read-only] WoofWare.NUnitTestRunner.ClassParallelScope
WoofWare.NUnitTestRunner.ClassParallelScope.Fixtures [static property]: [read-only] WoofWare.NUnitTestRunner.ClassParallelScope
WoofWare.NUnitTestRunner.ClassParallelScope.get_All [static method]: unit -> WoofWare.NUnitTestRunner.ClassParallelScope
WoofWare.NUnitTestRunner.ClassParallelScope.get_Children [static method]: unit -> WoofWare.NUnitTestRunner.ClassParallelScope
WoofWare.NUnitTestRunner.ClassParallelScope.get_Fixtures [static method]: unit -> WoofWare.NUnitTestRunner.ClassParallelScope
WoofWare.NUnitTestRunner.ClassParallelScope.get_IsAll [method]: unit -> bool
WoofWare.NUnitTestRunner.ClassParallelScope.get_IsChildren [method]: unit -> bool
WoofWare.NUnitTestRunner.ClassParallelScope.get_IsFixtures [method]: unit -> bool
WoofWare.NUnitTestRunner.ClassParallelScope.get_IsSelf [method]: unit -> bool
WoofWare.NUnitTestRunner.ClassParallelScope.get_Self [static method]: unit -> WoofWare.NUnitTestRunner.ClassParallelScope
WoofWare.NUnitTestRunner.ClassParallelScope.get_Tag [method]: unit -> int
WoofWare.NUnitTestRunner.ClassParallelScope.IsAll [property]: [read-only] bool
WoofWare.NUnitTestRunner.ClassParallelScope.IsChildren [property]: [read-only] bool
WoofWare.NUnitTestRunner.ClassParallelScope.IsFixtures [property]: [read-only] bool
WoofWare.NUnitTestRunner.ClassParallelScope.IsSelf [property]: [read-only] bool
WoofWare.NUnitTestRunner.ClassParallelScope.Self [static property]: [read-only] WoofWare.NUnitTestRunner.ClassParallelScope
WoofWare.NUnitTestRunner.ClassParallelScope.Tag [property]: [read-only] int
WoofWare.NUnitTestRunner.Combinatorial inherit obj, implements WoofWare.NUnitTestRunner.Combinatorial System.IEquatable, System.Collections.IStructuralEquatable, WoofWare.NUnitTestRunner.Combinatorial System.IComparable, System.IComparable, System.Collections.IStructuralComparable - union type with 2 cases
WoofWare.NUnitTestRunner.Combinatorial+Tags inherit obj
WoofWare.NUnitTestRunner.Combinatorial+Tags.Combinatorial [static field]: int = 0
@@ -141,8 +179,24 @@ WoofWare.NUnitTestRunner.Modifier.IsIgnored [property]: [read-only] bool
WoofWare.NUnitTestRunner.Modifier.NewExplicit [static method]: string option -> WoofWare.NUnitTestRunner.Modifier
WoofWare.NUnitTestRunner.Modifier.NewIgnored [static method]: string option -> WoofWare.NUnitTestRunner.Modifier
WoofWare.NUnitTestRunner.Modifier.Tag [property]: [read-only] int
WoofWare.NUnitTestRunner.Parallelizable`1 inherit obj, implements 'scope WoofWare.NUnitTestRunner.Parallelizable System.IEquatable, System.Collections.IStructuralEquatable, 'scope WoofWare.NUnitTestRunner.Parallelizable System.IComparable, System.IComparable, System.Collections.IStructuralComparable - union type with 2 cases
WoofWare.NUnitTestRunner.Parallelizable`1+Tags inherit obj
WoofWare.NUnitTestRunner.Parallelizable`1+Tags.No [static field]: int = 1
WoofWare.NUnitTestRunner.Parallelizable`1+Tags.Yes [static field]: int = 0
WoofWare.NUnitTestRunner.Parallelizable`1+Yes inherit 'scope WoofWare.NUnitTestRunner.Parallelizable
WoofWare.NUnitTestRunner.Parallelizable`1+Yes.get_Item [method]: unit -> 'scope
WoofWare.NUnitTestRunner.Parallelizable`1+Yes.Item [property]: [read-only] 'scope
WoofWare.NUnitTestRunner.Parallelizable`1.get_IsNo [method]: unit -> bool
WoofWare.NUnitTestRunner.Parallelizable`1.get_IsYes [method]: unit -> bool
WoofWare.NUnitTestRunner.Parallelizable`1.get_No [static method]: unit -> 'scope WoofWare.NUnitTestRunner.Parallelizable
WoofWare.NUnitTestRunner.Parallelizable`1.get_Tag [method]: unit -> int
WoofWare.NUnitTestRunner.Parallelizable`1.IsNo [property]: [read-only] bool
WoofWare.NUnitTestRunner.Parallelizable`1.IsYes [property]: [read-only] bool
WoofWare.NUnitTestRunner.Parallelizable`1.NewYes [static method]: 'scope -> 'scope WoofWare.NUnitTestRunner.Parallelizable
WoofWare.NUnitTestRunner.Parallelizable`1.No [static property]: [read-only] 'scope WoofWare.NUnitTestRunner.Parallelizable
WoofWare.NUnitTestRunner.Parallelizable`1.Tag [property]: [read-only] int
WoofWare.NUnitTestRunner.SingleTestMethod inherit obj, implements WoofWare.NUnitTestRunner.SingleTestMethod System.IEquatable, System.Collections.IStructuralEquatable
WoofWare.NUnitTestRunner.SingleTestMethod..ctor [constructor]: (System.Reflection.MethodInfo, WoofWare.NUnitTestRunner.TestKind, WoofWare.NUnitTestRunner.Modifier list, string list, int option, WoofWare.NUnitTestRunner.Combinatorial option)
WoofWare.NUnitTestRunner.SingleTestMethod..ctor [constructor]: (System.Reflection.MethodInfo, WoofWare.NUnitTestRunner.TestKind, WoofWare.NUnitTestRunner.Modifier list, string list, int option, WoofWare.NUnitTestRunner.Combinatorial option, unit WoofWare.NUnitTestRunner.Parallelizable option)
WoofWare.NUnitTestRunner.SingleTestMethod.Categories [property]: [read-only] string list
WoofWare.NUnitTestRunner.SingleTestMethod.Combinatorial [property]: [read-only] WoofWare.NUnitTestRunner.Combinatorial option
WoofWare.NUnitTestRunner.SingleTestMethod.get_Categories [method]: unit -> string list
@@ -151,11 +205,13 @@ WoofWare.NUnitTestRunner.SingleTestMethod.get_Kind [method]: unit -> WoofWare.NU
WoofWare.NUnitTestRunner.SingleTestMethod.get_Method [method]: unit -> System.Reflection.MethodInfo
WoofWare.NUnitTestRunner.SingleTestMethod.get_Modifiers [method]: unit -> WoofWare.NUnitTestRunner.Modifier list
WoofWare.NUnitTestRunner.SingleTestMethod.get_Name [method]: unit -> string
WoofWare.NUnitTestRunner.SingleTestMethod.get_Parallelize [method]: unit -> unit WoofWare.NUnitTestRunner.Parallelizable option
WoofWare.NUnitTestRunner.SingleTestMethod.get_Repeat [method]: unit -> int option
WoofWare.NUnitTestRunner.SingleTestMethod.Kind [property]: [read-only] WoofWare.NUnitTestRunner.TestKind
WoofWare.NUnitTestRunner.SingleTestMethod.Method [property]: [read-only] System.Reflection.MethodInfo
WoofWare.NUnitTestRunner.SingleTestMethod.Modifiers [property]: [read-only] WoofWare.NUnitTestRunner.Modifier list
WoofWare.NUnitTestRunner.SingleTestMethod.Name [property]: [read-only] string
WoofWare.NUnitTestRunner.SingleTestMethod.Parallelize [property]: [read-only] unit WoofWare.NUnitTestRunner.Parallelizable option
WoofWare.NUnitTestRunner.SingleTestMethod.Repeat [property]: [read-only] int option
WoofWare.NUnitTestRunner.SingleTestMethodModule inherit obj
WoofWare.NUnitTestRunner.SingleTestMethodModule.parse [static method]: string list -> System.Reflection.MethodInfo -> System.Reflection.CustomAttributeData list -> (WoofWare.NUnitTestRunner.SingleTestMethod option * System.Reflection.CustomAttributeData list)
@@ -187,13 +243,14 @@ WoofWare.NUnitTestRunner.TestFailure.NewTearDownFailed [static method]: WoofWare
WoofWare.NUnitTestRunner.TestFailure.NewTestFailed [static method]: WoofWare.NUnitTestRunner.UserMethodFailure -> WoofWare.NUnitTestRunner.TestFailure
WoofWare.NUnitTestRunner.TestFailure.Tag [property]: [read-only] int
WoofWare.NUnitTestRunner.TestFixture inherit obj, implements WoofWare.NUnitTestRunner.TestFixture System.IEquatable, System.Collections.IStructuralEquatable
WoofWare.NUnitTestRunner.TestFixture..ctor [constructor]: (System.Reflection.Assembly, string, System.Type, System.Reflection.MethodInfo option, System.Reflection.MethodInfo option, System.Reflection.MethodInfo list, System.Reflection.MethodInfo list, obj list list, WoofWare.NUnitTestRunner.SingleTestMethod list)
WoofWare.NUnitTestRunner.TestFixture..ctor [constructor]: (System.Reflection.Assembly, string, System.Type, System.Reflection.MethodInfo option, System.Reflection.MethodInfo option, System.Reflection.MethodInfo list, System.Reflection.MethodInfo list, obj list list, WoofWare.NUnitTestRunner.SingleTestMethod list, WoofWare.NUnitTestRunner.ClassParallelScope WoofWare.NUnitTestRunner.Parallelizable option)
WoofWare.NUnitTestRunner.TestFixture.ContainingAssembly [property]: [read-only] System.Reflection.Assembly
WoofWare.NUnitTestRunner.TestFixture.Empty [static method]: System.Type -> obj list list -> WoofWare.NUnitTestRunner.TestFixture
WoofWare.NUnitTestRunner.TestFixture.Empty [static method]: System.Type -> WoofWare.NUnitTestRunner.ClassParallelScope WoofWare.NUnitTestRunner.Parallelizable option -> obj list list -> WoofWare.NUnitTestRunner.TestFixture
WoofWare.NUnitTestRunner.TestFixture.get_ContainingAssembly [method]: unit -> System.Reflection.Assembly
WoofWare.NUnitTestRunner.TestFixture.get_Name [method]: unit -> string
WoofWare.NUnitTestRunner.TestFixture.get_OneTimeSetUp [method]: unit -> System.Reflection.MethodInfo option
WoofWare.NUnitTestRunner.TestFixture.get_OneTimeTearDown [method]: unit -> System.Reflection.MethodInfo option
WoofWare.NUnitTestRunner.TestFixture.get_Parallelize [method]: unit -> WoofWare.NUnitTestRunner.ClassParallelScope WoofWare.NUnitTestRunner.Parallelizable option
WoofWare.NUnitTestRunner.TestFixture.get_Parameters [method]: unit -> obj list list
WoofWare.NUnitTestRunner.TestFixture.get_SetUp [method]: unit -> System.Reflection.MethodInfo list
WoofWare.NUnitTestRunner.TestFixture.get_TearDown [method]: unit -> System.Reflection.MethodInfo list
@@ -202,6 +259,7 @@ WoofWare.NUnitTestRunner.TestFixture.get_Type [method]: unit -> System.Type
WoofWare.NUnitTestRunner.TestFixture.Name [property]: [read-only] string
WoofWare.NUnitTestRunner.TestFixture.OneTimeSetUp [property]: [read-only] System.Reflection.MethodInfo option
WoofWare.NUnitTestRunner.TestFixture.OneTimeTearDown [property]: [read-only] System.Reflection.MethodInfo option
WoofWare.NUnitTestRunner.TestFixture.Parallelize [property]: [read-only] WoofWare.NUnitTestRunner.ClassParallelScope WoofWare.NUnitTestRunner.Parallelizable option
WoofWare.NUnitTestRunner.TestFixture.Parameters [property]: [read-only] obj list list
WoofWare.NUnitTestRunner.TestFixture.SetUp [property]: [read-only] System.Reflection.MethodInfo list
WoofWare.NUnitTestRunner.TestFixture.TearDown [property]: [read-only] System.Reflection.MethodInfo list
@@ -358,10 +416,12 @@ WoofWare.NUnitTestRunner.TrxOutcome.Parse [static method]: string -> WoofWare.NU
WoofWare.NUnitTestRunner.TrxOutcome.Tag [property]: [read-only] int
WoofWare.NUnitTestRunner.TrxOutcome.Warning [static property]: [read-only] WoofWare.NUnitTestRunner.TrxOutcome
WoofWare.NUnitTestRunner.TrxOutput inherit obj, implements WoofWare.NUnitTestRunner.TrxOutput System.IEquatable, System.Collections.IStructuralEquatable
WoofWare.NUnitTestRunner.TrxOutput..ctor [constructor]: (string option, WoofWare.NUnitTestRunner.TrxErrorInfo option)
WoofWare.NUnitTestRunner.TrxOutput..ctor [constructor]: (string option, string option, WoofWare.NUnitTestRunner.TrxErrorInfo option)
WoofWare.NUnitTestRunner.TrxOutput.ErrorInfo [property]: [read-only] WoofWare.NUnitTestRunner.TrxErrorInfo option
WoofWare.NUnitTestRunner.TrxOutput.get_ErrorInfo [method]: unit -> WoofWare.NUnitTestRunner.TrxErrorInfo option
WoofWare.NUnitTestRunner.TrxOutput.get_StdErr [method]: unit -> string option
WoofWare.NUnitTestRunner.TrxOutput.get_StdOut [method]: unit -> string option
WoofWare.NUnitTestRunner.TrxOutput.StdErr [property]: [read-only] string option
WoofWare.NUnitTestRunner.TrxOutput.StdOut [property]: [read-only] string option
WoofWare.NUnitTestRunner.TrxReport inherit obj, implements WoofWare.NUnitTestRunner.TrxReport System.IEquatable, System.Collections.IStructuralEquatable
WoofWare.NUnitTestRunner.TrxReport..ctor [constructor]: (System.Guid, string, WoofWare.NUnitTestRunner.TrxReportTimes, WoofWare.NUnitTestRunner.TrxTestSettings, WoofWare.NUnitTestRunner.TrxUnitTestResult list, WoofWare.NUnitTestRunner.TrxUnitTest list, WoofWare.NUnitTestRunner.TrxTestEntry list, WoofWare.NUnitTestRunner.TrxTestListEntry list, WoofWare.NUnitTestRunner.TrxResultsSummary)

View File

@@ -520,15 +520,15 @@ module TestFixture =
/// Interpret this type as a [<TestFixture>], extracting the test members from it and annotating them with all
/// relevant information about how we should run them.
let parse (parentType : Type) : TestFixture =
let categories, args =
(([], []), parentType.CustomAttributes)
||> Seq.fold (fun (categories, args) attr ->
let categories, args, par =
(([], [], None), parentType.CustomAttributes)
||> Seq.fold (fun (categories, args, par) attr ->
match attr.AttributeType.FullName with
| "NUnit.Framework.SetUpFixtureAttribute" ->
failwith "This test runner does not support SetUpFixture. Please shout if you want this."
| "NUnit.Framework.CategoryAttribute" ->
let cat = attr.ConstructorArguments |> Seq.exactlyOne |> _.Value |> unbox<string>
cat :: categories, args
cat :: categories, args, par
| "NUnit.Framework.TestFixtureAttribute" ->
let newArgs =
match attr.ConstructorArguments |> Seq.map _.Value |> Seq.toList with
@@ -536,11 +536,36 @@ module TestFixture =
x |> Seq.cast<CustomAttributeTypedArgument> |> Seq.map _.Value |> Seq.toList
| xs -> xs
categories, newArgs :: args
| _ -> categories, args
categories, newArgs :: args, par
| "NUnit.Framework.NonParallelizableAttribute" ->
match par with
| Some _ -> failwith $"Got multiple parallelism attributes on %s{parentType.FullName}"
| None -> categories, args, Some Parallelizable.No
| "NUnit.Framework.ParallelizableAttribute" ->
match par with
| Some _ -> failwith $"Got multiple parallelism attributes on %s{parentType.FullName}"
| None ->
match attr.ConstructorArguments |> Seq.toList with
| [] -> categories, args, Some (Parallelizable.Yes ClassParallelScope.Self)
| [ v ] ->
match v.Value with
| :? int as v ->
match v with
| 512 -> categories, args, Some (Parallelizable.Yes ClassParallelScope.Fixtures)
| 256 -> categories, args, Some (Parallelizable.Yes ClassParallelScope.Children)
| 257 -> categories, args, Some (Parallelizable.Yes ClassParallelScope.All)
| 1 -> categories, args, Some (Parallelizable.Yes ClassParallelScope.Self)
| v ->
failwith
$"Could not recognise value %i{v} of parallel scope in %s{parentType.FullName}"
| v ->
failwith
$"Unexpectedly non-int value %O{v} of parallel scope in %s{parentType.FullName}"
| _ -> failwith $"unexpectedly got multiple args to Parallelizable on %s{parentType.FullName}"
| _ -> categories, args, par
)
(TestFixture.Empty parentType args, parentType.GetRuntimeMethods ())
(TestFixture.Empty parentType par args, parentType.GetRuntimeMethods ())
||> Seq.fold (fun state mi ->
((state, []), mi.CustomAttributes)
||> Seq.fold (fun (state, unrecognisedAttrs) attr ->

View File

@@ -290,6 +290,8 @@ type TrxOutput =
{
/// What the entity printed to standard output.
StdOut : string option
/// What the entity printed to standard error.
StdErr : string option
/// Description of any error the entity encountered.
ErrorInfo : TrxErrorInfo option
}
@@ -305,6 +307,14 @@ type TrxOutput =
childNode.AppendChild text |> ignore<XmlNode>
node.AppendChild childNode |> ignore<XmlNode>
match this.StdErr with
| None -> ()
| Some stderr ->
let text = doc.CreateTextNode stderr
let childNode = doc.CreateElement ("StdErr", XmlUtil.NS)
childNode.AppendChild text |> ignore<XmlNode>
node.AppendChild childNode |> ignore<XmlNode>
match this.ErrorInfo with
| None -> ()
| Some errInfo -> node.AppendChild (errInfo.toXml doc) |> ignore<XmlNode>
@@ -317,6 +327,11 @@ type TrxOutput =
| NodeWithNamedChild "StdOut" (OneChildNode "StdOut" (NoChildrenNode stdout)) -> Some stdout
| _ -> None
let stderr =
match node with
| NodeWithNamedChild "StdErr" (OneChildNode "StdErr" (NoChildrenNode stdout)) -> Some stdout
| _ -> None
let errorInfo =
match node with
| NodeWithNamedChild "ErrorInfo" node ->
@@ -330,6 +345,7 @@ type TrxOutput =
| Ok errorInfo ->
{
StdOut = stdout
StdErr = stderr
ErrorInfo = errorInfo
}
|> Ok

View File

@@ -1,5 +1,5 @@
{
"version": "0.10",
"version": "0.12",
"publicReleaseRefSpec": [
"^refs/heads/main$"
],
@@ -8,4 +8,4 @@
":/Directory.Build.props",
":/README.md"
]
}
}

View File

@@ -68,6 +68,45 @@ module Program =
let ctx = Ctx (testDll, DotnetRuntime.locate testDll)
let assy = ctx.LoadFromAssemblyPath testDll.FullName
let levelOfParallelism, par =
((None, None), assy.CustomAttributes)
||> Seq.fold (fun (levelPar, par) attr ->
match attr.AttributeType.FullName with
| "NUnit.Framework.LevelOfParallelismAttribute" ->
let arg = attr.ConstructorArguments |> Seq.exactlyOne |> _.Value |> unbox<int>
match levelPar with
| None -> (Some arg, par)
| Some existing ->
failwith $"Assembly %s{assy.Location} declares parallelism %i{arg} and also %i{existing}"
| "NUnit.Framework.NonParallelizableAttribute" ->
match levelPar with
| None -> (Some 1, par)
| Some existing ->
failwith
$"Assembly %s{assy.Location} declares non-parallelizable and also parallelism %i{existing}"
| "NUnit.Framework.ParallelizableAttribute" ->
match par with
| Some _ -> failwith "Got multiple Parallelize attributes in assembly"
| None ->
match attr.ConstructorArguments |> Seq.toList with
| [] -> levelPar, Some (Parallelizable.Yes AssemblyParallelScope.Fixtures)
| [ v ] ->
match v.Value with
| :? int as v ->
match v with
| 512 -> levelPar, Some (Parallelizable.Yes AssemblyParallelScope.Fixtures)
| 256 -> levelPar, Some (Parallelizable.Yes AssemblyParallelScope.Children)
| 257 ->
failwith "ParallelScope.All is invalid on assemblies; only Fixtures or Children"
| 1 ->
failwith "ParallelScope.Self is invalid on assemblies; only Fixtures or Children"
| v -> failwith $"Could not recognise value %i{v} of parallel scope on assembly"
| v -> failwith $"Unexpectedly non-int value %O{v} of parallel scope on assembly"
| _ -> failwith "unexpectedly got multiple args to Parallelizable on assembly"
| _ -> levelPar, par
)
let testFixtures = assy.ExportedTypes |> Seq.map TestFixture.parse |> Seq.toList
let creationTime = DateTimeOffset.Now
@@ -174,6 +213,7 @@ module Program =
Output =
{
StdOut = None
StdErr = None
ErrorInfo = None
}
RunInfos =
@@ -277,11 +317,11 @@ module Program =
Output =
match i.StdOut, i.StdErr, exc with
| None, None, None -> None
// TODO surely stderr can be emitted
| stdout, _stderr, exc ->
| stdout, stderr, exc ->
Some
{
TrxOutput.StdOut = stdout
StdErr = stderr
ErrorInfo = exc
}
}

View File

@@ -197,6 +197,7 @@ Running all tests in /Users/patrick/Documents/GitHub/TestRunner/TestRunner/TestR
Ensure version is monotonic: Not yet published
NUnit Adapter 4.5.0.0: Test execution complete
"""
StdErr = None
ErrorInfo = None
}
Outcome = TrxOutcome.Failed