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

Add more detailed state for property. #367

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion src/Hedgehog/Hedgehog.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ https://github.com/hedgehogqa/fsharp-hedgehog/blob/master/doc/index.md
<Compile Include="GenTuple.fs" />
<Compile Include="Outcome.fs" />
<Compile Include="Report.fs" />
<Compile Include="PropertyArgs.fs" />
<Compile Include="PropertyState.fs" />
<Compile Include="PropertyConfig.fs" />
<Compile Include="Property.fs" />
<Compile Include="Linq\Gen.fs" />
Expand Down
100 changes: 52 additions & 48 deletions src/Hedgehog/Property.fs
Original file line number Diff line number Diff line change
Expand Up @@ -127,72 +127,76 @@ module Property =
let forAll' (gen : Gen<'a>) : Property<'a> =
gen |> forAll success

//
// Conditions
//

let private shouldFail (state : PropertyState) (config : PropertyConfig) : bool =
config.ShrinkLimit
|> Option.map (fun limit -> state.Shrinks >= limit)
|> Option.defaultValue false

let private shouldPass (state : PropertyState) (config : PropertyConfig) : bool =
state.Tests >= config.TestLimit

let private shouldGiveUp (state : PropertyState) (config : PropertyConfig) : bool =
state.Discards >= config.DiscardLimit

//
// Runner
//

let private shrinkInput
(args : PropertyArgs)
(shrinkLimit : int<shrinks> Option) =
let rec loop
(nshrinks : int<shrinks>)
(Node ((journal, _), xs) : Tree<Journal * Outcome<'a>>) =
let private shrinkInput (state : PropertyState) (config : PropertyConfig) (tree : Tree<Journal * Outcome<'a>>) : Status =
let rec loop (state : PropertyState) (tree : Tree<Journal * Outcome<'a>>) : Status =
let (Node ((journal, _), xs)) = tree

let failed =
Failed {
Size = args.Size
Seed = args.Seed
Shrinks = nshrinks
Size = state.Size
Seed = state.Seed
Shrinks = state.Shrinks
Journal = journal
RecheckType = args.RecheckType
RecheckType = state.RecheckType
}
match shrinkLimit, Seq.tryFind (Tree.outcome >> snd >> Outcome.isFailure) xs with
| Some shrinkLimit', _ when nshrinks >= shrinkLimit' -> failed
| _, None -> failed
| _, Some tree -> loop (nshrinks + 1<shrinks>) tree
loop 0<shrinks>
if shouldFail state config then
failed
else
match Seq.tryFind (Tree.outcome >> snd >> Outcome.isFailure) xs with
| None -> failed
| Some tree -> loop (PropertyState.countShrink state) tree
loop state tree

let private reportWith' (args : PropertyArgs) (config : PropertyConfig) (p : Property<unit>) : Report =
let private reportWith' (state : PropertyState) (config : PropertyConfig) (p : Property<unit>) : Report =
let random = toGen p |> Gen.toRandom

let nextSize size =
if size >= 100 then
1
else
size + 1

let rec loop args tests discards =
if tests = config.TestLimit then
{ Tests = tests
Discards = discards
let rec loop state =
if shouldPass state config then
{ Tests = state.Tests
Discards = state.Discards
Status = OK }
elif discards >= 100<discards> then
{ Tests = tests
Discards = discards
elif shouldGiveUp state config then
{ Tests = state.Tests
Discards = state.Discards
Status = GaveUp }
else
let seed1, seed2 = Seed.split args.Seed
let result = Random.run seed1 args.Size random
let nextArgs = {
args with
Seed = seed2
Size = nextSize args.Size
}
let seed, state = PropertyState.next state
let tree = Random.run seed state.Size random

match snd (Tree.outcome result) with
match snd (Tree.outcome tree) with
| Failure ->
{ Tests = tests + 1<tests>
Discards = discards
Status = shrinkInput args config.ShrinkLimit result }
{ Tests = state.Tests + 1<tests>
Discards = state.Discards
Status = shrinkInput state config tree }
| Success () ->
loop nextArgs (tests + 1<tests>) discards
loop (PropertyState.countTest state)
| Discard ->
loop nextArgs tests (discards + 1<discards>)
loop (PropertyState.countDiscard state)

loop args 0<tests> 0<discards>
loop state

let reportWith (config : PropertyConfig) (p : Property<unit>) : Report =
let args = PropertyArgs.init
p |> reportWith' args config
let state = PropertyState.init
p |> reportWith' state config

let report (p : Property<unit>) : Report =
p |> reportWith PropertyConfig.defaultConfig
Expand All @@ -218,13 +222,13 @@ module Property =
g |> bind ofBool |> checkWith config

let reportRecheckWith (size : Size) (seed : Seed) (config : PropertyConfig) (p : Property<unit>) : Report =
let args = {
PropertyArgs.init with
let state = {
PropertyState.init with
RecheckType = RecheckType.None
Seed = seed
Size = size
}
reportWith' args config p
reportWith' state config p

let reportRecheck (size : Size) (seed : Seed) (p : Property<unit>) : Report =
reportRecheckWith size seed PropertyConfig.defaultConfig p
Expand Down
16 changes: 0 additions & 16 deletions src/Hedgehog/PropertyArgs.fs

This file was deleted.

8 changes: 5 additions & 3 deletions src/Hedgehog/PropertyConfig.fs
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
namespace Hedgehog

type PropertyConfig = internal {
TestLimit : int<tests>
DiscardLimit : int<discards>
ShrinkLimit : int<shrinks> option
TestLimit : int<tests>
}

module PropertyConfig =

/// The default configuration for a property test.
let defaultConfig : PropertyConfig =
{ TestLimit = 100<tests>
ShrinkLimit = None }
{ DiscardLimit = 100<discards>
ShrinkLimit = None
TestLimit = 100<tests> }

/// Set the number of times a property is allowed to shrink before the test
/// runner gives up and displays the counterexample.
Expand Down
47 changes: 47 additions & 0 deletions src/Hedgehog/PropertyState.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
namespace Hedgehog

[<Struct>]
type PropertyState = private {
Discards : int<discards>
Shrinks : int<shrinks>
Tests : int<tests>
RecheckType : RecheckType
Seed : Seed
Size : Size
}

module PropertyState =

let init = {
Discards = 0<discards>
Shrinks = 0<shrinks>
Tests = 0<tests>
RecheckType = RecheckType.FSharp
Seed = Seed.random ()
Size = 0
}

let countDiscard (state : PropertyState) : PropertyState =
{ state with Discards = state.Discards + 1<discards> }

let countShrink (state : PropertyState) : PropertyState =
{ state with Shrinks = state.Shrinks + 1<shrinks> }

let countTest (state : PropertyState) : PropertyState =
{ state with Tests = state.Tests + 1<tests> }

let next (state : PropertyState) : (Seed * PropertyState) =
let nextSize =
if state.Size >= 100 then
1
else
state.Size + 1

let seed, nextSeed = Seed.split state.Seed
let state = {
state with
Seed = nextSeed
Size = nextSize
}

(seed, state)