mirror of
https://github.com/Smaug123/unofficial-nunit-runner
synced 2025-10-12 04:08:38 +00:00
Multiple TestCaseSources (#26)
This commit is contained in:
@@ -9,6 +9,27 @@ module TestCaseData =
|
|||||||
|
|
||||||
let dataSourceRaw = [ 3, "hi", [| 4.0 |] ; -10, "bye", null ]
|
let dataSourceRaw = [ 3, "hi", [| 4.0 |] ; -10, "bye", null ]
|
||||||
|
|
||||||
|
let dataSource = dataSourceRaw |> List.map TestCaseData
|
||||||
|
|
||||||
|
[<TestCaseSource(nameof dataSource)>]
|
||||||
|
let ``Consume test data`` (i : int, s : string, arr : float[]) =
|
||||||
|
lock testCasesSeen (fun () -> testCasesSeen.Add (i, s, arr))
|
||||||
|
|
||||||
|
let multipleSources = ResizeArray ()
|
||||||
|
|
||||||
|
let dataSource2Raw = [ 5, "egg", [| -1.3 ; 4.5 |] ; 100, "mycroft", [||] ]
|
||||||
|
let dataSource2 = dataSource2Raw |> List.map TestCaseData
|
||||||
|
|
||||||
|
[<TestCaseSource(nameof dataSource)>]
|
||||||
|
[<TestCaseSource(nameof dataSource2)>]
|
||||||
|
let ``Consume test data from multiple sources`` (i : int, s : string, arr : float[]) =
|
||||||
|
lock multipleSources (fun () -> multipleSources.Add (i, s, arr))
|
||||||
|
|
||||||
|
let optional = [ Some "hi" ; None ] |> List.map TestCaseData
|
||||||
|
|
||||||
|
[<TestCaseSource(nameof optional)>]
|
||||||
|
let ``Consume options`` (s : string option) : unit = s |> shouldEqual s
|
||||||
|
|
||||||
[<OneTimeTearDown>]
|
[<OneTimeTearDown>]
|
||||||
let tearDown () =
|
let tearDown () =
|
||||||
testCasesSeen
|
testCasesSeen
|
||||||
@@ -16,13 +37,7 @@ module TestCaseData =
|
|||||||
|> List.sortBy (fun (a, _, _) -> a)
|
|> List.sortBy (fun (a, _, _) -> a)
|
||||||
|> shouldEqual (dataSourceRaw |> List.sortBy (fun (a, _, _) -> a))
|
|> shouldEqual (dataSourceRaw |> List.sortBy (fun (a, _, _) -> a))
|
||||||
|
|
||||||
let dataSource = dataSourceRaw |> List.map TestCaseData
|
multipleSources
|
||||||
|
|> Seq.toList
|
||||||
[<TestCaseSource(nameof dataSource)>]
|
|> List.sortBy (fun (a, _, _) -> a)
|
||||||
let ``Consume test data`` (i : int, s : string, arr : float[]) =
|
|> shouldEqual ((dataSourceRaw @ dataSource2Raw) |> List.sortBy (fun (a, _, _) -> a))
|
||||||
lock testCasesSeen (fun () -> testCasesSeen.Add (i, s, arr))
|
|
||||||
|
|
||||||
let optional = [ Some "hi" ; None ] |> List.map TestCaseData
|
|
||||||
|
|
||||||
[<TestCaseSource(nameof optional)>]
|
|
||||||
let ``Consume options`` (s : string option) : unit = s |> shouldEqual s
|
|
||||||
|
@@ -10,7 +10,7 @@ type Modifier =
|
|||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
type TestKind =
|
type TestKind =
|
||||||
| Single
|
| Single
|
||||||
| Source of string
|
| Source of string list
|
||||||
| Data of obj list list
|
| Data of obj list list
|
||||||
|
|
||||||
type Combinatorial =
|
type Combinatorial =
|
||||||
|
@@ -12,82 +12,77 @@ module SingleTestMethod =
|
|||||||
(attrs : CustomAttributeData list)
|
(attrs : CustomAttributeData list)
|
||||||
: SingleTestMethod option * CustomAttributeData list
|
: SingleTestMethod option * CustomAttributeData list
|
||||||
=
|
=
|
||||||
let remaining, isTest, hasSource, hasData, modifiers, categories, repeat, comb =
|
let remaining, isTest, sources, hasData, modifiers, categories, repeat, comb =
|
||||||
(([], false, None, None, [], [], None, None), attrs)
|
(([], false, [], None, [], [], None, None), attrs)
|
||||||
||> List.fold (fun (remaining, isTest, hasSource, hasData, mods, cats, repeat, comb) attr ->
|
||> List.fold (fun (remaining, isTest, sources, hasData, mods, cats, repeat, comb) attr ->
|
||||||
match attr.AttributeType.FullName with
|
match attr.AttributeType.FullName with
|
||||||
| "NUnit.Framework.TestAttribute" ->
|
| "NUnit.Framework.TestAttribute" ->
|
||||||
if attr.ConstructorArguments.Count > 0 then
|
if attr.ConstructorArguments.Count > 0 then
|
||||||
failwith "Unexpectedly got arguments to the Test attribute"
|
failwith "Unexpectedly got arguments to the Test attribute"
|
||||||
|
|
||||||
(remaining, true, hasSource, hasData, mods, cats, repeat, comb)
|
(remaining, true, sources, hasData, mods, cats, repeat, comb)
|
||||||
| "NUnit.Framework.TestCaseAttribute" ->
|
| "NUnit.Framework.TestCaseAttribute" ->
|
||||||
let args = attr.ConstructorArguments |> Seq.map _.Value |> Seq.toList
|
let args = attr.ConstructorArguments |> Seq.map _.Value |> Seq.toList
|
||||||
|
|
||||||
match hasData with
|
match hasData with
|
||||||
| None -> (remaining, isTest, hasSource, Some [ List.ofSeq args ], mods, cats, repeat, comb)
|
| None -> (remaining, isTest, sources, Some [ List.ofSeq args ], mods, cats, repeat, comb)
|
||||||
| Some existing ->
|
| Some existing ->
|
||||||
(remaining, isTest, hasSource, Some ((List.ofSeq args) :: existing), mods, cats, repeat, comb)
|
(remaining, isTest, sources, Some ((List.ofSeq args) :: existing), mods, cats, repeat, comb)
|
||||||
| "NUnit.Framework.TestCaseSourceAttribute" ->
|
| "NUnit.Framework.TestCaseSourceAttribute" ->
|
||||||
let arg = attr.ConstructorArguments |> Seq.exactlyOne |> _.Value |> unbox<string>
|
let arg = attr.ConstructorArguments |> Seq.exactlyOne |> _.Value |> unbox<string>
|
||||||
|
|
||||||
match hasSource with
|
(remaining, isTest, arg :: sources, hasData, mods, cats, repeat, comb)
|
||||||
| None -> (remaining, isTest, Some arg, hasData, mods, cats, repeat, comb)
|
|
||||||
| Some existing ->
|
|
||||||
failwith
|
|
||||||
$"Unexpectedly got multiple different sources for test %s{method.Name} (%s{existing}, %s{arg})"
|
|
||||||
| "NUnit.Framework.ExplicitAttribute" ->
|
| "NUnit.Framework.ExplicitAttribute" ->
|
||||||
let reason =
|
let reason =
|
||||||
attr.ConstructorArguments
|
attr.ConstructorArguments
|
||||||
|> Seq.tryHead
|
|> Seq.tryHead
|
||||||
|> Option.map (_.Value >> unbox<string>)
|
|> Option.map (_.Value >> unbox<string>)
|
||||||
|
|
||||||
(remaining, isTest, hasSource, hasData, (Modifier.Explicit reason) :: mods, cats, repeat, comb)
|
(remaining, isTest, sources, hasData, (Modifier.Explicit reason) :: mods, cats, repeat, comb)
|
||||||
| "NUnit.Framework.IgnoreAttribute" ->
|
| "NUnit.Framework.IgnoreAttribute" ->
|
||||||
let reason =
|
let reason =
|
||||||
attr.ConstructorArguments
|
attr.ConstructorArguments
|
||||||
|> Seq.tryHead
|
|> Seq.tryHead
|
||||||
|> Option.map (_.Value >> unbox<string>)
|
|> Option.map (_.Value >> unbox<string>)
|
||||||
|
|
||||||
(remaining, isTest, hasSource, hasData, (Modifier.Ignored reason) :: mods, cats, repeat, comb)
|
(remaining, isTest, sources, hasData, (Modifier.Ignored reason) :: mods, cats, repeat, comb)
|
||||||
| "NUnit.Framework.CategoryAttribute" ->
|
| "NUnit.Framework.CategoryAttribute" ->
|
||||||
let category =
|
let category =
|
||||||
attr.ConstructorArguments |> Seq.exactlyOne |> _.Value |> unbox<string>
|
attr.ConstructorArguments |> Seq.exactlyOne |> _.Value |> unbox<string>
|
||||||
|
|
||||||
(remaining, isTest, hasSource, hasData, mods, category :: cats, repeat, comb)
|
(remaining, isTest, sources, hasData, mods, category :: cats, repeat, comb)
|
||||||
| "NUnit.Framework.RepeatAttribute" ->
|
| "NUnit.Framework.RepeatAttribute" ->
|
||||||
match repeat with
|
match repeat with
|
||||||
| Some _ -> failwith $"Got RepeatAttribute multiple times on %s{method.Name}"
|
| Some _ -> failwith $"Got RepeatAttribute multiple times on %s{method.Name}"
|
||||||
| None ->
|
| None ->
|
||||||
|
|
||||||
let repeat = attr.ConstructorArguments |> Seq.exactlyOne |> _.Value |> unbox<int>
|
let repeat = attr.ConstructorArguments |> Seq.exactlyOne |> _.Value |> unbox<int>
|
||||||
(remaining, isTest, hasSource, hasData, mods, cats, Some repeat, comb)
|
(remaining, isTest, sources, hasData, mods, cats, Some repeat, comb)
|
||||||
| "NUnit.Framework.CombinatorialAttribute" ->
|
| "NUnit.Framework.CombinatorialAttribute" ->
|
||||||
match comb with
|
match comb with
|
||||||
| Some _ ->
|
| Some _ ->
|
||||||
failwith $"Got CombinatorialAttribute or SequentialAttribute multiple times on %s{method.Name}"
|
failwith $"Got CombinatorialAttribute or SequentialAttribute multiple times on %s{method.Name}"
|
||||||
| None ->
|
| None ->
|
||||||
(remaining, isTest, hasSource, hasData, mods, cats, repeat, Some Combinatorial.Combinatorial)
|
(remaining, isTest, sources, hasData, mods, cats, repeat, Some Combinatorial.Combinatorial)
|
||||||
| "NUnit.Framework.SequentialAttribute" ->
|
| "NUnit.Framework.SequentialAttribute" ->
|
||||||
match comb with
|
match comb with
|
||||||
| Some _ ->
|
| Some _ ->
|
||||||
failwith $"Got CombinatorialAttribute or SequentialAttribute multiple times on %s{method.Name}"
|
failwith $"Got CombinatorialAttribute or SequentialAttribute multiple times on %s{method.Name}"
|
||||||
| None ->
|
| None -> (remaining, isTest, sources, hasData, mods, cats, repeat, Some Combinatorial.Sequential)
|
||||||
(remaining, isTest, hasSource, hasData, mods, cats, repeat, Some Combinatorial.Sequential)
|
|
||||||
| s when s.StartsWith ("NUnit.Framework", StringComparison.Ordinal) ->
|
| s when s.StartsWith ("NUnit.Framework", StringComparison.Ordinal) ->
|
||||||
failwith $"Unrecognised attribute on function %s{method.Name}: %s{attr.AttributeType.FullName}"
|
failwith $"Unrecognised attribute on function %s{method.Name}: %s{attr.AttributeType.FullName}"
|
||||||
| _ -> (attr :: remaining, isTest, hasSource, hasData, mods, cats, repeat, comb)
|
| _ -> (attr :: remaining, isTest, sources, hasData, mods, cats, repeat, comb)
|
||||||
)
|
)
|
||||||
|
|
||||||
let test =
|
let test =
|
||||||
match isTest, hasSource, hasData, modifiers, categories, repeat, comb with
|
match isTest, sources, hasData, modifiers, categories, repeat, comb with
|
||||||
| _, Some _, Some _, _, _, _, _ ->
|
| _, _ :: _, Some _, _, _, _, _ ->
|
||||||
failwith
|
failwith
|
||||||
$"Test %s{method.Name} unexpectedly has both TestData and TestCaseSource; not currently supported"
|
$"Test %s{method.Name} unexpectedly has both TestData and TestCaseSource; not currently supported"
|
||||||
| false, None, None, [], _, _, _ -> None
|
| false, [], None, [], _, _, _ -> None
|
||||||
| _, Some source, None, mods, categories, repeat, comb ->
|
| _, _ :: _, None, mods, categories, repeat, comb ->
|
||||||
{
|
{
|
||||||
Kind = TestKind.Source source
|
Kind = TestKind.Source sources
|
||||||
Method = method
|
Method = method
|
||||||
Modifiers = mods
|
Modifiers = mods
|
||||||
Categories = categories @ parentCategories
|
Categories = categories @ parentCategories
|
||||||
@@ -95,7 +90,7 @@ module SingleTestMethod =
|
|||||||
Combinatorial = comb
|
Combinatorial = comb
|
||||||
}
|
}
|
||||||
|> Some
|
|> Some
|
||||||
| _, None, Some data, mods, categories, repeat, comb ->
|
| _, [], Some data, mods, categories, repeat, comb ->
|
||||||
{
|
{
|
||||||
Kind = TestKind.Data data
|
Kind = TestKind.Data data
|
||||||
Method = method
|
Method = method
|
||||||
@@ -105,7 +100,7 @@ module SingleTestMethod =
|
|||||||
Combinatorial = comb
|
Combinatorial = comb
|
||||||
}
|
}
|
||||||
|> Some
|
|> Some
|
||||||
| true, None, None, mods, categories, repeat, comb ->
|
| true, [], None, mods, categories, repeat, comb ->
|
||||||
{
|
{
|
||||||
Kind = TestKind.Single
|
Kind = TestKind.Single
|
||||||
Method = method
|
Method = method
|
||||||
@@ -115,7 +110,7 @@ module SingleTestMethod =
|
|||||||
Combinatorial = comb
|
Combinatorial = comb
|
||||||
}
|
}
|
||||||
|> Some
|
|> Some
|
||||||
| false, None, None, _ :: _, _, _, _ ->
|
| false, [], None, _ :: _, _, _, _ ->
|
||||||
failwith
|
failwith
|
||||||
$"Unexpectedly got test modifiers but no test settings on '%s{method.Name}', which you probably didn't intend."
|
$"Unexpectedly got test modifiers but no test settings on '%s{method.Name}', which you probably didn't intend."
|
||||||
|
|
||||||
|
@@ -192,52 +192,53 @@ module TestFixture =
|
|||||||
|> TestMemberFailure.Malformed
|
|> TestMemberFailure.Malformed
|
||||||
|> Error
|
|> Error
|
||||||
|> Seq.singleton
|
|> Seq.singleton
|
||||||
| TestKind.Source s, None ->
|
| TestKind.Source sources, None ->
|
||||||
let args =
|
|
||||||
test.Method.DeclaringType.GetProperty (
|
|
||||||
s,
|
|
||||||
BindingFlags.Public
|
|
||||||
||| BindingFlags.NonPublic
|
|
||||||
||| BindingFlags.Instance
|
|
||||||
||| BindingFlags.Static
|
|
||||||
)
|
|
||||||
|
|
||||||
seq {
|
seq {
|
||||||
// Might not be an IEnumerable of a reference type.
|
for source in sources do
|
||||||
// Concretely, `FSharpList<HttpStatusCode> :> IEnumerable<obj>` fails.
|
let args =
|
||||||
for arg in args.GetValue (null : obj) :?> System.Collections.IEnumerable do
|
test.Method.DeclaringType.GetProperty (
|
||||||
yield
|
source,
|
||||||
match arg with
|
BindingFlags.Public
|
||||||
| :? Tuple<obj, obj> as (a, b) ->
|
||| BindingFlags.NonPublic
|
||||||
runOne setUp tearDown test.Method containingObject [| a ; b |]
|
||| BindingFlags.Instance
|
||||||
| :? Tuple<obj, obj, obj> as (a, b, c) ->
|
||| BindingFlags.Static
|
||||||
runOne setUp tearDown test.Method containingObject [| a ; b ; c |]
|
)
|
||||||
| :? Tuple<obj, obj, obj, obj> as (a, b, c, d) ->
|
|
||||||
runOne setUp tearDown test.Method containingObject [| a ; b ; c ; d |]
|
|
||||||
| arg ->
|
|
||||||
let argTy = arg.GetType ()
|
|
||||||
|
|
||||||
if argTy.FullName = "NUnit.Framework.TestCaseData" then
|
// Might not be an IEnumerable of a reference type.
|
||||||
let argsMem =
|
// Concretely, `FSharpList<HttpStatusCode> :> IEnumerable<obj>` fails.
|
||||||
argTy.GetMethod (
|
for arg in args.GetValue (null : obj) :?> System.Collections.IEnumerable do
|
||||||
"get_Arguments",
|
yield
|
||||||
BindingFlags.Public
|
match arg with
|
||||||
||| BindingFlags.Instance
|
| :? Tuple<obj, obj> as (a, b) ->
|
||||||
||| BindingFlags.FlattenHierarchy
|
runOne setUp tearDown test.Method containingObject [| a ; b |]
|
||||||
)
|
| :? Tuple<obj, obj, obj> as (a, b, c) ->
|
||||||
|
runOne setUp tearDown test.Method containingObject [| a ; b ; c |]
|
||||||
|
| :? Tuple<obj, obj, obj, obj> as (a, b, c, d) ->
|
||||||
|
runOne setUp tearDown test.Method containingObject [| a ; b ; c ; d |]
|
||||||
|
| arg ->
|
||||||
|
let argTy = arg.GetType ()
|
||||||
|
|
||||||
if isNull argsMem then
|
if argTy.FullName = "NUnit.Framework.TestCaseData" then
|
||||||
failwith "Unexpectedly could not call `.Arguments` on TestCaseData"
|
let argsMem =
|
||||||
|
argTy.GetMethod (
|
||||||
|
"get_Arguments",
|
||||||
|
BindingFlags.Public
|
||||||
|
||| BindingFlags.Instance
|
||||||
|
||| BindingFlags.FlattenHierarchy
|
||||||
|
)
|
||||||
|
|
||||||
runOne
|
if isNull argsMem then
|
||||||
setUp
|
failwith "Unexpectedly could not call `.Arguments` on TestCaseData"
|
||||||
tearDown
|
|
||||||
test.Method
|
runOne
|
||||||
containingObject
|
setUp
|
||||||
(argsMem.Invoke (arg, [||]) |> unbox<obj[]>)
|
tearDown
|
||||||
else
|
test.Method
|
||||||
runOne setUp tearDown test.Method containingObject [| arg |]
|
containingObject
|
||||||
|> normaliseError
|
(argsMem.Invoke (arg, [||]) |> unbox<obj[]>)
|
||||||
|
else
|
||||||
|
runOne setUp tearDown test.Method containingObject [| arg |]
|
||||||
|
|> normaliseError
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|> Seq.concat
|
|> Seq.concat
|
||||||
|
Reference in New Issue
Block a user