diff --git a/CHANGELOG.md b/CHANGELOG.md index a99b6dc..0818019 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +### 0.7.1 (2025-05-12) + +* `AutoGenConfig`s are automatically merged when using both `Properties` and `Property`. + ### 0.7.0 (2025-05-01) * Updated to .NET 8 diff --git a/readme.md b/readme.md index 7554fda..536e4b8 100644 --- a/readme.md +++ b/readme.md @@ -213,19 +213,25 @@ let ``"i" mostly ranges between -1 and 1`` i = 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 `[]`. ```f# -type Int13 = static member __ = GenX.defaults |> AutoGenConfig.addGenerator (Gen.constant 13) -type Int2718 = static member __ = GenX.defaults |> AutoGenConfig.addGenerator (Gen.constant 2718) - -[, 1)>] -module ``Module with tests`` = - - [] - let ``this passes and runs once`` (i: int) = - i = 13 - - [, 2)>] - let ``this passes and runs twice`` (i: int) = - i = 2718 +type Int13A = static member __ = GenX.defaults |> AutoGenConfig.addGenerator (Gen.constant 13 ) |> AutoGenConfig.addGenerator (Gen.constant "A") +type Int2718 = static member __ = GenX.defaults |> AutoGenConfig.addGenerator (Gen.constant 2718) +type Int2718Minimal = static member __ = AutoGenConfig.defaults |> AutoGenConfig.addGenerator (Gen.constant 2718) + +[, 1)>] +module ``Module with tests`` = + + [] + let ``this passes and runs once`` (i: int) = + i = 13 + + [, 2)>] + let ``this passes and runs twice`` (i: int) = + i = 2718 + + [)>] + let ``the `Int13A` and `Int2718Minimal` configs are merged, so this passes`` (i: int, s: string) = + // `` uses the "minimal" `AutoGenConfig.defaults`. Using `GenX.defaults` would override the `A` in `Int13A`. + i = 2718 && s = "A" ``` ### 🔖 `GenAttribute` diff --git a/src/Hedgehog.Xunit/Hedgehog.Xunit.fsproj b/src/Hedgehog.Xunit/Hedgehog.Xunit.fsproj index a6eab82..f52700e 100644 --- a/src/Hedgehog.Xunit/Hedgehog.Xunit.fsproj +++ b/src/Hedgehog.Xunit/Hedgehog.Xunit.fsproj @@ -14,7 +14,7 @@ https://github.com/hedgehogqa/fsharp-hedgehog-xunit f# fsharp testing xunit hedgehog-logo.png - 0.7.0 + 0.7.1 Hedgehog.Xunit Hedgehog with convenience attributes for xUnit. diff --git a/src/Hedgehog.Xunit/InternalLogic.fs b/src/Hedgehog.Xunit/InternalLogic.fs index 44270ae..77572be 100644 --- a/src/Hedgehog.Xunit/InternalLogic.fs +++ b/src/Hedgehog.Xunit/InternalLogic.fs @@ -37,6 +37,34 @@ let private genxAutoBoxWith<'T> x = x |> GenX.autoWith<'T> |> Gen.map box let private genxAutoBoxWithMethodInfo = typeof.DeclaringType.GetTypeInfo().GetDeclaredMethod "genxAutoBoxWith" +let config (configType: Type) (configArgs: Option) = + let configArgs = configArgs |> Option.defaultValue [||] + configType.GetMethods() + |> Seq.filter (fun p -> + p.IsStatic && + p.ReturnType = typeof + ) |> seqTryExactlyOne + |> Option.requireSome (sprintf "%s must have exactly one public static property that returns an AutoGenConfig. + +An example type definition: + +type %s = + static member __ = + GenX.defaults |> AutoGenConfig.addGenerator (Gen.constant 13) +" configType.FullName configType.Name) + |> fun methodInfo -> + let methodInfo = + if methodInfo.IsGenericMethod then + methodInfo.GetParameters() + |> Array.map (fun p -> p.ParameterType.IsGenericParameter) + |> Array.zip configArgs + |> Array.filter snd + |> Array.map (fun (arg, _) -> arg.GetType()) + |> fun argTypes -> methodInfo.MakeGenericMethod argTypes + else methodInfo + methodInfo.Invoke(null, configArgs) + :?> AutoGenConfig + let parseAttributes (testMethod:MethodInfo) (testClass:Type) = let classAutoGenConfig, classAutoGenConfigArgs, classTests, classShrinks, classSize = testClass.GetCustomAttributes(typeof) @@ -45,14 +73,22 @@ let parseAttributes (testMethod:MethodInfo) (testClass:Type) = |> function | Some x -> x.GetAutoGenConfig, x.GetAutoGenConfigArgs, x.GetTests, x.GetShrinks, x.GetSize | None -> None , None , None , None , None - let configType, configArgs, tests, shrinks, size = + let config, tests, shrinks, size = typeof |> testMethod.GetCustomAttributes |> Seq.exactlyOne :?> PropertyAttribute |> fun methodAttribute -> - methodAttribute.GetAutoGenConfig ++ classAutoGenConfig , - methodAttribute.GetAutoGenConfigArgs ++ classAutoGenConfigArgs |> Option.defaultValue [||], + let config = + match classAutoGenConfig, methodAttribute.GetAutoGenConfig with + | Some c, Some m -> + let methodConfig = config m methodAttribute.GetAutoGenConfigArgs + let classConfig = config c classAutoGenConfigArgs + AutoGenConfig.merge classConfig methodConfig + | Some c, None -> config c classAutoGenConfigArgs + | None , Some m -> config m methodAttribute.GetAutoGenConfigArgs + | None , None -> GenX.defaults + config, methodAttribute.GetTests ++ classTests , methodAttribute.GetShrinks ++ classShrinks , methodAttribute.GetSize ++ classSize @@ -65,35 +101,6 @@ let parseAttributes (testMethod:MethodInfo) (testClass:Type) = :?> RecheckAttribute |> fun x -> x.GetRecheckData ) - let config = - match configType with - | None -> GenX.defaults - | Some t -> - t.GetMethods() - |> Seq.filter (fun p -> - p.IsStatic && - p.ReturnType = typeof - ) |> seqTryExactlyOne - |> Option.requireSome (sprintf "%s must have exactly one public static property that returns an AutoGenConfig. - -An example type definition: - -type %s = - static member __ = - GenX.defaults |> AutoGenConfig.addGenerator (Gen.constant 13) -" t.FullName t.Name) - |> fun methodInfo -> - let methodInfo = - if methodInfo.IsGenericMethod then - methodInfo.GetParameters() - |> Array.map (fun p -> p.ParameterType.IsGenericParameter) - |> Array.zip configArgs - |> Array.filter snd - |> Array.map (fun (arg, _) -> arg.GetType()) - |> fun argTypes -> methodInfo.MakeGenericMethod argTypes - else methodInfo - methodInfo.Invoke(null, configArgs) - :?> AutoGenConfig config, tests, shrinks, recheck, size let resultIsOk r = diff --git a/tests/Hedgehog.Xunit.Tests.FSharp/PropertyTests.fs b/tests/Hedgehog.Xunit.Tests.FSharp/PropertyTests.fs index e0d223c..2367790 100644 --- a/tests/Hedgehog.Xunit.Tests.FSharp/PropertyTests.fs +++ b/tests/Hedgehog.Xunit.Tests.FSharp/PropertyTests.fs @@ -20,7 +20,7 @@ type Int6() = inherit GenAttribute() override _.Generator = Gen.constant 6 -type IntConstantRange(max: int, min: int)= +type IntConstantRange(max: int, min: int) = inherit GenAttribute() override _.Generator = Range.constant max min |> Gen.int32 @@ -246,7 +246,7 @@ type NonAutoGenConfig = GenX.defaults |> AutoGenConfig.addGenerator (Gen.constant 13) ", e.Message) -type Int2718 = static member __ = GenX.defaults |> AutoGenConfig.addGenerator (Gen.constant 2718) +type Int2718 = static member __ = AutoGenConfig.defaults |> AutoGenConfig.addGenerator (Gen.constant 2718) [, 200)>] module ``Module with tests`` = @@ -781,3 +781,20 @@ module ``GenAttribute with Properties Tests`` = [)>] let ``overrides Properties' and Property's autoGenConfig`` ([] i) = Assert.StrictEqual(5, i) + +type Int13A = + static member __ = + AutoGenConfig.defaults + |> AutoGenConfig.addGenerator (Gen.constant 13) + |> AutoGenConfig.addGenerator (Gen.constant "A") + +[)>] +module ``Module with )>`` = + + [] + let ``Module's works`` (i, s) = + i = 13 && s = "A" + + [)>] + let ``Module's merges with Method level `` (i, s) = + i = 2718 && s = "A"