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

Implement Property.select via bind to avoid bug #318

Merged
11 commits merged into from
Sep 4, 2021
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ trim_trailing_whitespace = true

[*.csproj, *.fsproj, *.props]
indent_size = 2

[*.cs]
csharp_new_line_before_open_brace = all
1 change: 1 addition & 0 deletions src/Hedgehog/Hedgehog.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ /~https://github.com/hedgehogqa/fsharp-hedgehog/blob/master/doc/index.md
<Compile Include="Linq\PropertyConfig.fs" />
<Compile Include="Linq\Property.fs" />
<Compile Include="Linq\Range.fs" />
<Compile Include="Linq\Report.fs" />
<None Include="Script.fsx" />
</ItemGroup>
<ItemGroup>
Expand Down
3 changes: 2 additions & 1 deletion src/Hedgehog/Linq/Property.fs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,8 @@ type PropertyExtensions private () =

[<Extension>]
static member Select (property : Property<'T>, mapper : Func<'T, 'TResult>) : Property<'TResult> =
Property.map mapper.Invoke property
property
|> Property.bind (Property.ofThrowing mapper.Invoke)

[<Extension>]
static member Select (property : Property<'T>, mapper : Action<'T>) : Property =
Expand Down
16 changes: 16 additions & 0 deletions src/Hedgehog/Linq/Report.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace Hedgehog.Linq

#if !FABLE_COMPILER

open System.Runtime.CompilerServices
open Hedgehog

[<Extension>]
[<AbstractClass; Sealed>]
type ReportExtensions private () =

[<Extension>]
static member Render (report: Report) : string =
Report.render report

#endif
16 changes: 8 additions & 8 deletions src/Hedgehog/Property.fs
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,10 @@ module Property =

value |> prepareForPrinting |> sprintf "%A"

let private handle (e : exn) =
Gen.constant (Journal.singletonMessage (string e), Failure) |> ofGen

let forAll (k : 'a -> Property<'b>) (gen : Gen<'a>) : Property<'b> =
let handle (e : exn) =
Gen.constant (Journal.singletonMessage (string e), Failure) |> ofGen
let prepend (x : 'a) =
counterexample (fun () -> printValue x)
|> bind (fun _ -> try k x with e -> handle e)
Expand Down Expand Up @@ -212,13 +213,12 @@ module Property =
g |> bind ofBool |> checkWith config

/// Converts a possibly-throwing function to
/// a property by treating "no exception" as success.
let ofThrowing (f : 'a -> unit) (x : 'a) : Property<unit> =
/// a property by treating an exception as a failure.
let ofThrowing (f : 'a -> 'b) (a : 'a) : Property<'b> =
try
f x
success ()
with
| _ -> failure
success (f a)
with e ->
handle e

let reportRecheckWith (size : Size) (seed : Seed) (config : PropertyConfig) (p : Property<unit>) : Report =
let args = {
Expand Down
41 changes: 36 additions & 5 deletions tests/Hedgehog.Linq.Tests/LinqTests.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,47 @@
using Xunit;
using System;
using Xunit;

// Import ForAll:
using static Hedgehog.Linq.Property;

namespace Hedgehog.Linq.Tests
{
/*
* The main object here is just to make sure that the examples compile,
* there's nothing fancy in the properties being tested.
*/
public class LinqTests
{

[Fact]
public void ExceptionInSelect_Action_FailedStatus()
{
var guid = Guid.NewGuid().ToString();
void action() => throw new Exception(guid);
var property =
from _ in Property.ForAll(Gen.Int32(Range.Constant(0, 0)))
select action();
var report = property.Report();
var rendered = report.Render();
Assert.True(report.Status.IsFailed);
Assert.Contains(guid, rendered);
}

[Fact]
public void ExceptionInSelect_Func_FailedStatus()
{
var guid = Guid.NewGuid().ToString();
bool func() => throw new Exception(guid);
var property =
from x in Property.ForAll(Gen.Int32(Range.Constant(0, 0)))
select func();
var report = property.Report();
var rendered = report.Render();
Assert.True(report.Status.IsFailed);
Assert.Contains(guid, rendered);
}

/*
* The main object the following tests is just to make sure that the examples compile.
* There's nothing fancy in the properties being tested.
*/

[Fact]
public void CanUseSelectWithAssertion()
{
Expand Down