Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Property Method Attribute Generators #16

Merged
merged 35 commits into from
Jul 5, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
81c1d40
First stab as attribute parameters
JohnEffo Jun 17, 2023
865f9e1
Add demo range test
JohnEffo Jun 17, 2023
4667514
Fix attributes on ParameterGeneraterBaseType
JohnEffo Jun 18, 2023
71dfc6e
Add examples showcasing attributes in CSharp
JohnEffo Jun 18, 2023
1284400
Make file names match the examples
JohnEffo Jun 18, 2023
08eea0d
test
dharmaturtle Jun 21, 2023
9d851f6
added a docstring, some tests, and some polish
dharmaturtle Jun 21, 2023
3deb955
part way through docs
JohnEffo Jun 22, 2023
235e321
Merge wip with upstream.
JohnEffo Jun 22, 2023
609985d
Documenation completed
JohnEffo Jun 22, 2023
ce4c504
C# properties which are Task or Task<bool> now run.
JohnEffo Jun 24, 2023
c81034d
Cater for useful AsyncResult types.
JohnEffo Jun 24, 2023
c12e7e4
C# properties which are Task or Task<bool> now run.
JohnEffo Jun 24, 2023
48b88d0
Cater for useful AsyncResult types.
JohnEffo Jun 24, 2023
97cfb1e
Finish documentation
JohnEffo Jun 24, 2023
496e428
Fix single = mistake in documentation property.
JohnEffo Jun 25, 2023
9c9296a
Merge branch 'main' into nethod-parameter-generator
dharmaturtle Jun 27, 2023
c9b0eb8
polish
dharmaturtle Jun 27, 2023
5aa8682
ParameterGenerator => GenAttribute
dharmaturtle Jun 27, 2023
2d05195
Merge main int nethod-parameter-generator branch
JohnEffo Jun 28, 2023
ca4baad
Merge branch 'main' into nethod-parameter-generator
JohnEffo Jun 28, 2023
20f15df
Merge branch 'main' into nethod-parameter-generator
dharmaturtle Jun 30, 2023
d222d68
polish 1
dharmaturtle Jun 30, 2023
c45ba46
rename
dharmaturtle Jun 30, 2023
c10a87f
lol
dharmaturtle Jun 30, 2023
1567e36
oops
dharmaturtle Jun 30, 2023
5697937
polish
dharmaturtle Jun 30, 2023
c93e7d2
?
dharmaturtle Jul 1, 2023
25094f5
Task.CompletedTask => Task.Delay
dharmaturtle Jul 1, 2023
3cb81fb
Revert "?"
dharmaturtle Jul 1, 2023
b04d42f
polish
dharmaturtle Jul 1, 2023
9915cfd
more or less done with Readme
dharmaturtle Jul 3, 2023
aecd795
readmeCSharp more or less done
dharmaturtle Jul 3, 2023
19a8b7b
polish
dharmaturtle Jul 4, 2023
96907d9
changelog
dharmaturtle Jul 5, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Hedgehog.Xunit.sln
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
readme.md = readme.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "csharp-attribute-based-parameters-comparision", "examples\csharp-attribute-based-parameters-comparision\csharp-attribute-based-parameters-comparision.csproj", "{2048061B-0561-4297-A02C-A12A263177A5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -27,6 +29,10 @@ Global
{63A8D184-519E-4061-8A74-F1EACAF3B0D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{63A8D184-519E-4061-8A74-F1EACAF3B0D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{63A8D184-519E-4061-8A74-F1EACAF3B0D5}.Release|Any CPU.Build.0 = Release|Any CPU
{2048061B-0561-4297-A02C-A12A263177A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2048061B-0561-4297-A02C-A12A263177A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2048061B-0561-4297-A02C-A12A263177A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2048061B-0561-4297-A02C-A12A263177A5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Hedgehog;
using Hedgehog.Linq;
using Hedgehog.Xunit;
using Gen = Hedgehog.Linq.Gen;
using Range = Hedgehog.Linq.Range;


namespace csharp_attribute_based_parameters_comparision;

public record PositiveInt(int Value);
public record NegativeInt( int Value );

public class Generators
{
public static Gen<PositiveInt> GenPositiveInt =>
from x in Gen.Int32(Range.Constant(1, Int32.MaxValue))
select new PositiveInt(x);

public static Gen<NegativeInt> GenNegativeInt =>
from x in Gen.Int32(Range.Constant(Int32.MinValue, -1))
select new NegativeInt(x);

public static AutoGenConfig _ => GenX.defaults
.WithGenerator(GenPositiveInt)
.WithGenerator(GenNegativeInt);
}

public class PositiveAndNegativeGeneratorContainerTypes
{
[Property(typeof(Generators))]
public bool ResultOfAddingPositiveAndNegativeLessThanPositive(
PositiveInt positive,
NegativeInt negative)
=> positive.Value + negative.Value < positive.Value;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Hedgehog;
using Hedgehog.Xunit;
using Gen = Hedgehog.Linq.Gen;
using Range = Hedgehog.Linq.Range;

namespace csharp_attribute_based_parameters_comparision;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this name be more general? Might be useful for other C# examples. Or heck, could even turn it into a real test suite. I should probably add it to CICD anyway - doesn't really make sense to have broken examples.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have updated this, I realised this when I came to do the documentation and wanted to put add the documentation examples.


public class Negative : ParameterGeneraterBaseType<int>
{
public override Gen<int> Generator => Gen.Int32(Range.Constant(Int32.MinValue, -1));
}
public class Positive : ParameterGeneraterBaseType<int>
{
public override Gen<int> Generator => Gen.Int32(Range.Constant(1, Int32.MaxValue));
}

public class PositiveAndNegativeUtilizingIntegerRangeAttribute
{
[Property]
public bool ResultOfAddingPositiveAndNegativeLessThanPositive(
[Positive] int positive,
[Negative] int negative)
=> positive + negative < positive;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Hedgehog;
using Hedgehog.Xunit;
using Gen = Hedgehog.Linq.Gen;
using Range = Hedgehog.Linq.Range;

namespace csharp_attribute_based_parameters_comparision;

public class Int32Range : ParameterGeneraterBaseType<int>
{
private readonly int _min;
private readonly int _max;

public Int32Range(int min, int max)
{
_min = min;
_max = max;
}
public override Gen<int> Generator => Gen.Int32(Range.Constant(_min, _max));
}

public class PositiveAndNegativeWithAttributes
{
[Property]
public bool ResultOfAddingPositiveAndNegativeLessThanPositive(
[Int32Range(1, Int32.MaxValue)] int positive,
[Int32Range(Int32.MinValue, -1 )] int negative)
=> positive + negative < positive;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
global using Xunit;
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<RootNamespace>csharp_attribute_based_parameters_comparision</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Hedgehog.Xunit\Hedgehog.Xunit.fsproj" />
</ItemGroup>

</Project>
23 changes: 23 additions & 0 deletions src/Hedgehog.Xunit/Attributes.fs
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,26 @@ type RecheckAttribute(recheckData) =
let _recheckData : string = recheckData

member internal _.GetRecheckData = _recheckData

/// Set a Generator for a parameter of a test annotated with `Property`
///
/// Example usage:
///
/// ```
///
/// type ConstantInt(i: int) =
/// inherit ParameterGeneraterBaseType<int>()
/// override _.Generator = Gen.constant i
///
/// [<Property>]
/// let ``is always 2`` ([<ConstantInt(2)>] i) =
/// Assert.StrictEqual(2, i)
///
/// ```
[<AbstractClass>]
[<AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)>]
type ParameterGeneraterBaseType<'a>() =
dharmaturtle marked this conversation as resolved.
Show resolved Hide resolved
inherit Attribute()

abstract member Generator : Gen<'a>
dharmaturtle marked this conversation as resolved.
Show resolved Hide resolved
member this.Box() = this.Generator |> Gen.map box
33 changes: 25 additions & 8 deletions src/Hedgehog.Xunit/InternalLogic.fs
Original file line number Diff line number Diff line change
Expand Up @@ -154,18 +154,35 @@ let withShrinks = function
| Some x -> PropertyConfig.withShrinks x
| None -> PropertyConfig.withoutShrinks


let report (testMethod:MethodInfo) testClass testClassInstance =
let getAttributeGenerator (parameterInfo: ParameterInfo) =
let attributes = parameterInfo.GetCustomAttributes()
if Seq.isEmpty attributes then
None
else
attributes
|> Seq.tryPick(fun x ->
let attType = x.GetType().BaseType
if attType.IsGenericType && attType.GetGenericTypeDefinition().IsAssignableFrom(typedefof<ParameterGeneraterBaseType<_>>) then
let method = attType.GetMethods() |> Array.pick(fun x -> if x.Name = "Box" then Some x else None)
method.Invoke(x, null) :?> Gen<obj> |> Some
else
None
)

let config, tests, shrinks, recheck, size = parseAttributes testMethod testClass
let gens =
testMethod.GetParameters()
|> Array.mapi (fun i p ->
if p.ParameterType.ContainsGenericParameters then
Gen.constant Unchecked.defaultof<_>
else
genxAutoBoxWithMethodInfo
.MakeGenericMethod(p.ParameterType)
.Invoke(null, [|config|])
:?> Gen<obj>)
|> Array.map (fun p ->
match (getAttributeGenerator p, p.ParameterType.ContainsGenericParameters) with
| (Some gen, _) -> gen
| (_ , true) -> Gen.constant Unchecked.defaultof<_>
| _ -> genxAutoBoxWithMethodInfo
.MakeGenericMethod(p.ParameterType)
.Invoke(null, [|config|])
:?> Gen<obj>)

|> List.ofArray
|> ListGen.sequence
let gens =
Expand Down
41 changes: 40 additions & 1 deletion tests/Hedgehog.Xunit.Tests/PropertyTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,18 @@ open Common

type Int13 = static member __ = GenX.defaults |> AutoGenConfig.addGenerator (Gen.constant 13)

type Int5() =
inherit ParameterGeneraterBaseType<int>()
override _.Generator = Gen.constant 5

type Int6() =
inherit ParameterGeneraterBaseType<int>()
override _.Generator = Gen.constant 6

type IntCRange(max:int, min:int)=
inherit ParameterGeneraterBaseType<int>()
override _.Generator = Range.constant max min |> Gen.int32

module ``Property module tests`` =

type private Marker = class end
Expand Down Expand Up @@ -154,7 +166,6 @@ module ``Property module tests`` =
[<Property>]
let ``0 parameters passes`` () = ()


type ``Property class tests``(output: Xunit.Abstractions.ITestOutputHelper) =

[<Property>]
Expand Down Expand Up @@ -720,3 +731,31 @@ module ``returning a property runs it`` =
let report = InternalLogic.report (nameof ``returning a failing property<bool> with external gen fails and shrinks, skipped`` |> getMethod) typeof<Marker>.DeclaringType null
let actual = Assert.Throws<Exception>(fun () -> InternalLogic.tryRaise report)
actual.Message.Contains("51") |> Assert.True

module ``Attribute Parameter Type Tests`` =

[<Property>]
let ``can define parameter as 5`` ([<Int5>] i) =
Assert.StrictEqual(5, i)

[<Property(typeof<Int13>)>]
let ``overrides Property's autoGenConfig`` ([<Int5>] i) =
Assert.StrictEqual(5, i)

[<Property>]
let ``can have different generators for the same type of parameter`` ([<Int5>] five) ([<Int6>] six) =
five = 5 && six = 6

[<Property>]
let ``can restrict on range`` ([<IntCRange(min = 0, max = 5)>] i) =
i >= 0 && i <= 5

[<Properties(typeof<Int13>, 200<tests>)>]
module ``Attribute Parameter Type Tests with Properties`` =
[<Property(typeof<Int13>)>]
let ``overrides Properties' autoGenConfig`` ([<Int5>] i) =
Assert.StrictEqual(5, i)

[<Property(typeof<Int13>)>]
let ``overrides Property's autoGenConfig`` ([<Int5>] i) =
Assert.StrictEqual(5, i)