Skip to content

Commit

Permalink
Allow PropertiesAttribute to affect properties on nested types/modules.
Browse files Browse the repository at this point in the history
  • Loading branch information
kurtschelfthout committed Aug 31, 2021
1 parent 6fc0fb0 commit a69cb7a
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 23 deletions.
2 changes: 1 addition & 1 deletion src/FsCheck.Xunit/FsCheck.Xunit.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AssemblyName>FsCheck.Xunit</AssemblyName>
<TargetFrameworks>net452;netstandard1.6;netstandard2.0</TargetFrameworks>
<TargetFrameworks>net452;netstandard2.0;netstandard1.6</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
Expand Down
24 changes: 18 additions & 6 deletions src/FsCheck.Xunit/PropertyAttribute.fs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace FsCheck.Xunit

open System
open System.Reflection
open System.Threading.Tasks

open FsCheck
Expand Down Expand Up @@ -155,10 +156,9 @@ type public PropertiesAttribute() = inherit PropertyAttribute()
type PropertyTestCase(diagnosticMessageSink:IMessageSink, defaultMethodDisplay:TestMethodDisplay, testMethod:ITestMethod, ?testMethodArguments:obj []) =
inherit XunitTestCase(diagnosticMessageSink, defaultMethodDisplay, testMethod, (match testMethodArguments with | None -> null | Some v -> v))

let combineAttributes (attributes: (IAttributeInfo option) list) =
attributes
let combineAttributes (configs: (PropertyConfig option) list) =
configs
|> List.choose id
|> List.map(fun attr -> attr.GetNamedArgument<PropertyConfig> "Config")
|> List.reduce(fun higherLevelAttribute lowerLevelAttribute ->
PropertyConfig.combine lowerLevelAttribute higherLevelAttribute)

Expand All @@ -170,10 +170,22 @@ type PropertyTestCase(diagnosticMessageSink:IMessageSink, defaultMethodDisplay:T
|> Seq.collect (fun attr -> attr.GetNamedArgument "Arbitrary")
|> Seq.toArray

let getPropertiesOnDeclaringClasses (testClass: ITestClass) =
[ let mutable current: Type = testClass.Class.ToRuntimeType()
while not (isNull current) do
yield current.GetTypeInfo().GetCustomAttributes<PropertiesAttribute>()
|> Seq.tryHead
|> Option.map (fun attr -> attr.Config)
current <- current.DeclaringType]
|> List.rev

let getConfig (attr: IAttributeInfo) =
attr.GetNamedArgument<PropertyConfig> "Config"

let config = combineAttributes [
this.TestMethod.TestClass.Class.Assembly.GetCustomAttributes(typeof<PropertiesAttribute>) |> Seq.tryHead
this.TestMethod.TestClass.Class.GetCustomAttributes(typeof<PropertiesAttribute>) |> Seq.tryHead
this.TestMethod.Method.GetCustomAttributes(typeof<PropertyAttribute>) |> Seq.head |> Some]
yield this.TestMethod.TestClass.Class.Assembly.GetCustomAttributes(typeof<PropertiesAttribute>) |> Seq.tryHead |> Option.map getConfig
yield! getPropertiesOnDeclaringClasses this.TestMethod.TestClass
yield this.TestMethod.Method.GetCustomAttributes(typeof<PropertyAttribute>) |> Seq.head |> getConfig |> Some]

{ config with Arbitrary = Array.append config.Arbitrary arbitrariesOnClass }
|> PropertyConfig.toConfig output
Expand Down
60 changes: 44 additions & 16 deletions tests/FsCheck.Test/Runner.fs
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,33 @@ module Runner =
open FsCheck.Xunit
open Swensen.Unquote

type TestArbitrary1 =
type PositiveDoublesOnly =
static member PositiveDouble() =
Arb.Default.Float()
|> Arb.mapFilter abs (fun t -> t >= 0.0)

type TestArbitrary2 =
type NegativeDoublesOnly =
static member NegativeDouble() =
Arb.Default.Float()
|> Arb.mapFilter (abs >> ((-) 0.0)) (fun t -> t <= 0.0)

[<Property( Arbitrary=[| typeof<TestArbitrary2>; typeof<TestArbitrary1> |] )>]
[<Property( Arbitrary=[| typeof<NegativeDoublesOnly>; typeof<PositiveDoublesOnly> |] )>]
let ``should register Arbitrary instances from Config in last to first order``(underTest:float) =
underTest <= 0.0

type TestArbitrary3 =
type NegativeDoublesOnlyAsProperty =
static member NegativeDouble =
TestArbitrary2.NegativeDouble()
NegativeDoublesOnly.NegativeDouble()

[<Property( Arbitrary=[| typeof<TestArbitrary3> |] )>]
[<Property( Arbitrary=[| typeof<NegativeDoublesOnlyAsProperty> |] )>]
let ``should register Arbitrary instances defined as properties``(underTest:float) =
underTest <= 0.0

type TestArbitrary4() =
type NegativeDoublesOnlyAsAutoProperty() =
static member val NegativeDouble =
TestArbitrary2.NegativeDouble()
NegativeDoublesOnly.NegativeDouble()

[<Property( Arbitrary=[| typeof<TestArbitrary4> |] )>]
[<Property( Arbitrary=[| typeof<NegativeDoublesOnlyAsAutoProperty> |] )>]
let ``should register Arbitrary instances defined as auto-implemented properties ``(underTest:float) =
underTest <= 0.0

Expand Down Expand Up @@ -114,11 +114,11 @@ module Runner =

[<Fact>]
let ``PropertyConfig combine should prepend extra Arbitrary``() =
let original = { PropertyConfig.zero with Arbitrary = [| typeof<TestArbitrary1> |] }
let extra = { PropertyConfig.zero with Arbitrary = [| typeof<TestArbitrary2> |] }
let original = { PropertyConfig.zero with Arbitrary = [| typeof<PositiveDoublesOnly> |] }
let extra = { PropertyConfig.zero with Arbitrary = [| typeof<NegativeDoublesOnly> |] }
let combined = PropertyConfig.combine extra original

combined.Arbitrary.[0] =! typeof<TestArbitrary2>
combined.Arbitrary.[0] =! typeof<NegativeDoublesOnly>

[<Property>]
let ``PropertyConfig combine should favor extra config``(orignalMaxTest, extraMaxTest) =
Expand Down Expand Up @@ -148,14 +148,27 @@ module Runner =
[<Property>]
member __.``Should run a property on an instance``(_:int) = ()

[<Properties(Arbitrary = [| typeof<TestArbitrary2> |])>]
[<Properties(Arbitrary = [| typeof<NegativeDoublesOnly> |])>]
module ModuleWithPropertiesArb =

[<Property>]
let ``should use Arb instances from enclosing module``(underTest:float) =
underTest <= 0.0

[<Property( Arbitrary=[| typeof<TestArbitrary1> |] )>]
module NestedModuleWithPropertiesArb =

[<Property>]
let ``should use Arb instances from enclosing enclosing module``(underTest:float) =
underTest <= 0.0

[<Properties( Arbitrary=[| typeof<PositiveDoublesOnly> |])>]
module NestedModuleWithPropertiesConfig =

[<Property>]
let ``should use Arb instances from closest enclosing module``(underTest:float) =
underTest >= 0.0

[<Property( Arbitrary=[| typeof<PositiveDoublesOnly> |] )>]
let ``should use Arb instance on method preferentially``(underTest:float) =
underTest >= 0.0

Expand All @@ -167,6 +180,21 @@ module Runner =
// checking if the generated value is always the same (-59) from "01234,56789" Replay
x =! -59

module NestedModuleWithoutPropertiesConfig =

[<Property>]
let ``should use configuration from enclosing enclosing module``(x:int) =
// checking if the generated value is always the same (-59) from "01234,56789" Replay
x =! -59

[<Properties( MaxTest = 1, StartSize = 100, EndSize = 100, Replay = "12345,67890")>]
module NestedModuleWithPropertiesConfig =

[<Property>]
let ``should use configuration from closest enclosing module``(x:int) =
/// checking if the generated value is always the same (18) from "12345,67890" Replay
x =! 18

[<Property( Replay = "12345,67890")>]
let ``should use configuration on method preferentially``(x:int) =
// checking if the generated value is always the same (18) from "12345,67890" Replay
Expand Down Expand Up @@ -276,14 +304,14 @@ module Deprecated =
open FsCheck.Xunit
open Runner

[<Arbitrary(typeof<TestArbitrary2>)>]
[<Arbitrary(typeof<NegativeDoublesOnly>)>]
module ModuleWithArbitrary =

[<Property>]
let ``should use Arb instances from enclosing module``(underTest:float) =
underTest <= 0.0

[<Property( Arbitrary=[| typeof<TestArbitrary1> |] )>]
[<Property( Arbitrary=[| typeof<PositiveDoublesOnly> |] )>]
let ``should use Arb instance on method preferentially``(underTest:float) =
underTest >= 0.0

Expand Down

0 comments on commit a69cb7a

Please sign in to comment.