Last active
October 16, 2018 07:05
-
-
Save hafuu/b3d873d3256954edf1699bb3732121d3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| module Persimmon.FsCheck | |
| open Persimmon | |
| open FsCheck | |
| open System.Diagnostics | |
| open FsCheck.Random | |
| open System | |
| type FsCheckConfig = { | |
| MaxTest : int | |
| MaxFail : int | |
| Replay : StdGen option | |
| StartSize : int | |
| EndSize : int | |
| QuietOnSuccess: bool | |
| Every : int -> list<obj> -> string | |
| EveryShrink : list<obj> -> string | |
| Arbitrary : list<Type> | |
| } | |
| with | |
| static member Default = | |
| let defaultConfig = Config.Default | |
| { | |
| MaxTest = defaultConfig.MaxTest | |
| MaxFail = defaultConfig.MaxFail | |
| Replay = defaultConfig.Replay | |
| StartSize = defaultConfig.StartSize | |
| EndSize = defaultConfig.EndSize | |
| QuietOnSuccess = defaultConfig.QuietOnSuccess | |
| Every = defaultConfig.Every | |
| EveryShrink = defaultConfig.EveryShrink | |
| Arbitrary = defaultConfig.Arbitrary | |
| } | |
| type PropertyState = { | |
| FsCheckConfig: FsCheckConfig | |
| Properties: Property seq | |
| } | |
| with | |
| static member Default = | |
| { | |
| FsCheckConfig = FsCheckConfig.Default | |
| Properties = Seq.empty | |
| } | |
| module private Impl = | |
| exception FsCheckFailException of string | |
| let runner = | |
| { new IRunner with | |
| member __.OnStartFixture(_) = () | |
| member __.OnArguments(_, _, _) = () | |
| member __.OnShrink(_, _) = () | |
| member __.OnFinished(name, result) = | |
| match result with | |
| | TestResult.True _ -> () | |
| | _ -> raise (FsCheckFailException(Runner.onFinishedToString name result)) | |
| } | |
| let toConfig (tc: TestCase) (c: FsCheckConfig) = | |
| { | |
| MaxTest = c.MaxTest | |
| MaxFail = c.MaxFail | |
| Replay = c.Replay | |
| Name = Option.defaultValue Config.Default.Name tc.Name | |
| StartSize = c.StartSize | |
| EndSize = c.EndSize | |
| QuietOnSuccess = c.QuietOnSuccess | |
| Every = c.Every | |
| EveryShrink = c.EveryShrink | |
| Arbitrary = c.Arbitrary | |
| Runner = runner | |
| } | |
| let run (tc: TestCase) (state: PropertyState) = | |
| let config = toConfig tc state.FsCheckConfig | |
| state.Properties | |
| |> Seq.map (fun prop -> | |
| try | |
| Check.One(config, prop) | |
| Passed() | |
| with | |
| | FsCheckFailException e -> NotPassed (None, NotPassedCause.Violated e) | |
| | _ -> reraise() | |
| ) | |
| |> Seq.toArray | |
| |> function | |
| | [||] -> NonEmptyList.singleton (Passed ()) | |
| | xs -> NonEmptyList.ofSeq xs | |
| type PropertyBuilder(name: string option) = | |
| new() = PropertyBuilder(None) | |
| new(name: string) = PropertyBuilder(Some name) | |
| member __.Yield(()) = PropertyState.Default | |
| [<CustomOperation("maxTest")>] | |
| member __.MaxTest(state: PropertyState, value) = { state with FsCheckConfig = { state.FsCheckConfig with MaxTest = value } } | |
| [<CustomOperation("maxFail")>] | |
| member __.MaxFail(state: PropertyState, value) = { state with FsCheckConfig = { state.FsCheckConfig with MaxFail = value } } | |
| [<CustomOperation("replay")>] | |
| member __.Replay(state: PropertyState, value) = { state with FsCheckConfig = { state.FsCheckConfig with Replay = value } } | |
| [<CustomOperation("startSize")>] | |
| member __.StartSize(state: PropertyState, value) = { state with FsCheckConfig = { state.FsCheckConfig with StartSize = value } } | |
| [<CustomOperation("endSize")>] | |
| member __.EndSize(state: PropertyState, value) = { state with FsCheckConfig = { state.FsCheckConfig with EndSize = value } } | |
| [<CustomOperation("quietOnSuccess")>] | |
| member __.QuietOnSuccess(state: PropertyState, value) = { state with FsCheckConfig = { state.FsCheckConfig with QuietOnSuccess = value } } | |
| [<CustomOperation("every")>] | |
| member __.Every(state: PropertyState, value) = { state with FsCheckConfig = { state.FsCheckConfig with Every = value } } | |
| [<CustomOperation("everyShrink")>] | |
| member __.EveryShrink(state: PropertyState, value) = { state with FsCheckConfig = { state.FsCheckConfig with EveryShrink = value } } | |
| [<CustomOperation("arbitrary")>] | |
| member __.Arbitrary(state: PropertyState, value) = { state with FsCheckConfig = { state.FsCheckConfig with Arbitrary = value } } | |
| [<CustomOperation("config")>] | |
| member __.Arbitrary(state: PropertyState, value) = { state with FsCheckConfig = value } | |
| [<CustomOperation("apply")>] | |
| member __.Apply(state: PropertyState, testable: 'testable) = | |
| let properties = seq { | |
| yield! state.Properties | |
| yield Prop.ofTestable testable | |
| } | |
| { state with Properties = properties } | |
| member __.Delay(f: unit -> _) = f | |
| member __.Run(f: unit -> PropertyState) : TestCase<unit> = | |
| let body (tc: TestCase) = async { | |
| let watch = Stopwatch.StartNew() | |
| try | |
| let state = f() | |
| let results = Impl.run tc state | |
| do watch.Stop() | |
| return Done(tc, results, watch.Elapsed) | |
| with | |
| | e -> | |
| do watch.Stop() | |
| return Error (tc, [| ExceptionWrapper(e) |], [], watch.Elapsed) | |
| } | |
| TestCase.init name [] [] body | |
| [<AutoOpen>] | |
| module Syntax = | |
| let property (name: string) = PropertyBuilder(name) | |
| module UseTestNameByReflection = | |
| let property = PropertyBuilder() | |
| module private Test = | |
| open Syntax.UseTestNameByReflection | |
| let t: TestCase<unit> = property { | |
| apply (Prop.forAll Arb.from<int> (fun _ -> false)) | |
| apply (Prop.forAll Arb.from<string> (fun _ -> true)) | |
| apply (Prop.forAll Arb.from<int list> (fun _ -> false)) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment