Skip to content

Commit 5adb86f

Browse files
committed
AutoGenConfigs are automatically merged when using both Properties and Property
1 parent 97ee343 commit 5adb86f

File tree

5 files changed

+82
-48
lines changed

5 files changed

+82
-48
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
### 0.7.1 (2025-05-12)
4+
5+
* `AutoGenConfig`s are automatically merged when using both `Properties` and `Property`.
6+
37
### 0.7.0 (2025-05-01)
48

59
* Updated to .NET 8

readme.md

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -213,19 +213,25 @@ let ``"i" mostly ranges between -1 and 1`` i =
213213
This optional attribute can decorate modules or classes. It sets default arguments for [`AutoGenConfig`, `AutoGenConfigArgs`](#-autogenconfig-and-autogenconfigargs), [`Tests`](#-tests), [`Shrinks`](#-shrinks), and [`Size`](#-size). These will be overridden by any explicit arguments on `[<Property>]`.
214214

215215
```f#
216-
type Int13 = static member __ = GenX.defaults |> AutoGenConfig.addGenerator (Gen.constant 13)
217-
type Int2718 = static member __ = GenX.defaults |> AutoGenConfig.addGenerator (Gen.constant 2718)
218-
219-
[<Properties(typeof<Int13>, 1<tests>)>]
220-
module ``Module with <Properties> tests`` =
221-
222-
[<Property>]
223-
let ``this passes and runs once`` (i: int) =
224-
i = 13
225-
226-
[<Property(typeof<Int2718>, 2<tests>)>]
227-
let ``this passes and runs twice`` (i: int) =
228-
i = 2718
216+
type Int13A = static member __ = GenX.defaults |> AutoGenConfig.addGenerator (Gen.constant 13 ) |> AutoGenConfig.addGenerator (Gen.constant "A")
217+
type Int2718 = static member __ = GenX.defaults |> AutoGenConfig.addGenerator (Gen.constant 2718)
218+
type Int2718Minimal = static member __ = AutoGenConfig.defaults |> AutoGenConfig.addGenerator (Gen.constant 2718)
219+
220+
[<Properties(typeof<Int13A>, 1<tests>)>]
221+
module ``Module with <Properties> tests`` =
222+
223+
[<Property>]
224+
let ``this passes and runs once`` (i: int) =
225+
i = 13
226+
227+
[<Property(typeof<Int2718>, 2<tests>)>]
228+
let ``this passes and runs twice`` (i: int) =
229+
i = 2718
230+
231+
[<Property(typeof<Int2718Minimal>)>]
232+
let ``the `Int13A` and `Int2718Minimal` configs are merged, so this passes`` (i: int, s: string) =
233+
// `<Property>` uses the "minimal" `AutoGenConfig.defaults`. Using `GenX.defaults` would override the `A` in `Int13A`.
234+
i = 2718 && s = "A"
229235
```
230236

231237
### 🔖 `GenAttribute`

src/Hedgehog.Xunit/Hedgehog.Xunit.fsproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<PackageProjectUrl>https://github.com/hedgehogqa/fsharp-hedgehog-xunit</PackageProjectUrl>
1515
<PackageTags>f# fsharp testing xunit</PackageTags>
1616
<PackageIcon>hedgehog-logo.png</PackageIcon>
17-
<Version>0.7.0</Version>
17+
<Version>0.7.1</Version>
1818
<PackageId>Hedgehog.Xunit</PackageId>
1919
<PackageDescription>
2020
Hedgehog with convenience attributes for xUnit.

src/Hedgehog.Xunit/InternalLogic.fs

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,34 @@ let private genxAutoBoxWith<'T> x = x |> GenX.autoWith<'T> |> Gen.map box
3737
let private genxAutoBoxWithMethodInfo =
3838
typeof<Marker>.DeclaringType.GetTypeInfo().GetDeclaredMethod "genxAutoBoxWith"
3939

40+
let config (configType: Type) (configArgs: Option<obj array>) =
41+
let configArgs = configArgs |> Option.defaultValue [||]
42+
configType.GetMethods()
43+
|> Seq.filter (fun p ->
44+
p.IsStatic &&
45+
p.ReturnType = typeof<AutoGenConfig>
46+
) |> seqTryExactlyOne
47+
|> Option.requireSome (sprintf "%s must have exactly one public static property that returns an AutoGenConfig.
48+
49+
An example type definition:
50+
51+
type %s =
52+
static member __ =
53+
GenX.defaults |> AutoGenConfig.addGenerator (Gen.constant 13)
54+
" configType.FullName configType.Name)
55+
|> fun methodInfo ->
56+
let methodInfo =
57+
if methodInfo.IsGenericMethod then
58+
methodInfo.GetParameters()
59+
|> Array.map (fun p -> p.ParameterType.IsGenericParameter)
60+
|> Array.zip configArgs
61+
|> Array.filter snd
62+
|> Array.map (fun (arg, _) -> arg.GetType())
63+
|> fun argTypes -> methodInfo.MakeGenericMethod argTypes
64+
else methodInfo
65+
methodInfo.Invoke(null, configArgs)
66+
:?> AutoGenConfig
67+
4068
let parseAttributes (testMethod:MethodInfo) (testClass:Type) =
4169
let classAutoGenConfig, classAutoGenConfigArgs, classTests, classShrinks, classSize =
4270
testClass.GetCustomAttributes(typeof<PropertiesAttribute>)
@@ -45,14 +73,22 @@ let parseAttributes (testMethod:MethodInfo) (testClass:Type) =
4573
|> function
4674
| Some x -> x.GetAutoGenConfig, x.GetAutoGenConfigArgs, x.GetTests, x.GetShrinks, x.GetSize
4775
| None -> None , None , None , None , None
48-
let configType, configArgs, tests, shrinks, size =
76+
let config, tests, shrinks, size =
4977
typeof<PropertyAttribute>
5078
|> testMethod.GetCustomAttributes
5179
|> Seq.exactlyOne
5280
:?> PropertyAttribute
5381
|> fun methodAttribute ->
54-
methodAttribute.GetAutoGenConfig ++ classAutoGenConfig ,
55-
methodAttribute.GetAutoGenConfigArgs ++ classAutoGenConfigArgs |> Option.defaultValue [||],
82+
let config =
83+
match classAutoGenConfig, methodAttribute.GetAutoGenConfig with
84+
| Some c, Some m ->
85+
let methodConfig = config m methodAttribute.GetAutoGenConfigArgs
86+
let classConfig = config c classAutoGenConfigArgs
87+
AutoGenConfig.merge classConfig methodConfig
88+
| Some c, None -> config c classAutoGenConfigArgs
89+
| None , Some m -> config m methodAttribute.GetAutoGenConfigArgs
90+
| None , None -> GenX.defaults
91+
config,
5692
methodAttribute.GetTests ++ classTests ,
5793
methodAttribute.GetShrinks ++ classShrinks ,
5894
methodAttribute.GetSize ++ classSize
@@ -65,35 +101,6 @@ let parseAttributes (testMethod:MethodInfo) (testClass:Type) =
65101
:?> RecheckAttribute
66102
|> fun x -> x.GetRecheckData
67103
)
68-
let config =
69-
match configType with
70-
| None -> GenX.defaults
71-
| Some t ->
72-
t.GetMethods()
73-
|> Seq.filter (fun p ->
74-
p.IsStatic &&
75-
p.ReturnType = typeof<AutoGenConfig>
76-
) |> seqTryExactlyOne
77-
|> Option.requireSome (sprintf "%s must have exactly one public static property that returns an AutoGenConfig.
78-
79-
An example type definition:
80-
81-
type %s =
82-
static member __ =
83-
GenX.defaults |> AutoGenConfig.addGenerator (Gen.constant 13)
84-
" t.FullName t.Name)
85-
|> fun methodInfo ->
86-
let methodInfo =
87-
if methodInfo.IsGenericMethod then
88-
methodInfo.GetParameters()
89-
|> Array.map (fun p -> p.ParameterType.IsGenericParameter)
90-
|> Array.zip configArgs
91-
|> Array.filter snd
92-
|> Array.map (fun (arg, _) -> arg.GetType())
93-
|> fun argTypes -> methodInfo.MakeGenericMethod argTypes
94-
else methodInfo
95-
methodInfo.Invoke(null, configArgs)
96-
:?> AutoGenConfig
97104
config, tests, shrinks, recheck, size
98105

99106
let resultIsOk r =

tests/Hedgehog.Xunit.Tests.FSharp/PropertyTests.fs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ type Int6() =
2020
inherit GenAttribute<int>()
2121
override _.Generator = Gen.constant 6
2222

23-
type IntConstantRange(max: int, min: int)=
23+
type IntConstantRange(max: int, min: int) =
2424
inherit GenAttribute<int>()
2525
override _.Generator = Range.constant max min |> Gen.int32
2626

@@ -246,7 +246,7 @@ type NonAutoGenConfig =
246246
GenX.defaults |> AutoGenConfig.addGenerator (Gen.constant 13)
247247
", e.Message)
248248

249-
type Int2718 = static member __ = GenX.defaults |> AutoGenConfig.addGenerator (Gen.constant 2718)
249+
type Int2718 = static member __ = AutoGenConfig.defaults |> AutoGenConfig.addGenerator (Gen.constant 2718)
250250

251251
[<Properties(typeof<Int13>, 200<tests>)>]
252252
module ``Module with <Properties> tests`` =
@@ -781,3 +781,20 @@ module ``GenAttribute with Properties Tests`` =
781781
[<Property(typeof<Int13>)>]
782782
let ``overrides Properties' and Property's autoGenConfig`` ([<Int5>] i) =
783783
Assert.StrictEqual(5, i)
784+
785+
type Int13A =
786+
static member __ =
787+
AutoGenConfig.defaults
788+
|> AutoGenConfig.addGenerator (Gen.constant 13)
789+
|> AutoGenConfig.addGenerator (Gen.constant "A")
790+
791+
[<Properties(typeof<Int13A>)>]
792+
module ``Module with <Properties(typeof<Int13A>)>`` =
793+
794+
[<Property>]
795+
let ``Module's <Properties> works`` (i, s) =
796+
i = 13 && s = "A"
797+
798+
[<Property(typeof<Int2718>)>]
799+
let ``Module's <Properties> merges with Method level <Property>`` (i, s) =
800+
i = 2718 && s = "A"

0 commit comments

Comments
 (0)