From 4856b7a034dbeee595a44122b138f35db1981062 Mon Sep 17 00:00:00 2001 From: Oskar Gewalli Date: Mon, 31 Mar 2025 21:54:33 +0300 Subject: [PATCH 1/2] Try to add Xunit.v3 --- Directory.Packages.props | 1 + FsCheck.sln | 8 ++++- src/FsCheck.Xunit.v3/AssemblyInfo.fs | 23 ++++++++++++++ src/FsCheck.Xunit.v3/FsCheck.Xunit.v3.fsproj | 28 +++++++++++++++++ src/FsCheck.Xunit.v3/paket.template | 32 ++++++++++++++++++++ 5 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 src/FsCheck.Xunit.v3/AssemblyInfo.fs create mode 100644 src/FsCheck.Xunit.v3/FsCheck.Xunit.v3.fsproj create mode 100644 src/FsCheck.Xunit.v3/paket.template diff --git a/Directory.Packages.props b/Directory.Packages.props index 543f6d23..75a04975 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -38,5 +38,6 @@ + \ No newline at end of file diff --git a/FsCheck.sln b/FsCheck.sln index 97d79f45..4b667080 100644 --- a/FsCheck.sln +++ b/FsCheck.sln @@ -1,4 +1,4 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 +Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.9.34616.47 MinimumVisualStudioVersion = 10.0.40219.1 @@ -59,6 +59,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSharp.DocSnippets", "examp EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FsCheck.Test.CSharp", "tests\FsCheck.Test.CSharp\FsCheck.Test.CSharp.csproj", "{86955D54-4D54-4D4C-A7DC-F98F4CCFE498}" EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FsCheck.Xunit.v3", "src\FsCheck.Xunit.v3\FsCheck.Xunit.v3.fsproj", "{CADBF086-2B3D-42D5-8A66-F9ACC6119ED0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -113,6 +115,10 @@ Global {86955D54-4D54-4D4C-A7DC-F98F4CCFE498}.Debug|Any CPU.Build.0 = Debug|Any CPU {86955D54-4D54-4D4C-A7DC-F98F4CCFE498}.Release|Any CPU.ActiveCfg = Release|Any CPU {86955D54-4D54-4D4C-A7DC-F98F4CCFE498}.Release|Any CPU.Build.0 = Release|Any CPU + {CADBF086-2B3D-42D5-8A66-F9ACC6119ED0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CADBF086-2B3D-42D5-8A66-F9ACC6119ED0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CADBF086-2B3D-42D5-8A66-F9ACC6119ED0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CADBF086-2B3D-42D5-8A66-F9ACC6119ED0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/FsCheck.Xunit.v3/AssemblyInfo.fs b/src/FsCheck.Xunit.v3/AssemblyInfo.fs new file mode 100644 index 00000000..e658dfdc --- /dev/null +++ b/src/FsCheck.Xunit.v3/AssemblyInfo.fs @@ -0,0 +1,23 @@ + +// Auto-generated; edits may be deleted at any time +namespace System +open System.Reflection +open System.Runtime.CompilerServices + +[] +[] +[] +[] +[] +[] +[] +do () + +module internal AssemblyVersionInformation = + let [] AssemblyTitle = "FsCheck.Xunit.v3" + let [] AssemblyProduct = "FsCheck.Xunit.v3" + let [] AssemblyDescription = "Integrates FsCheck with xUnit.v3.NET" + let [] AssemblyVersion = "3.1.0" + let [] AssemblyFileVersion = "3.1.0" + let [] AssemblyKeyFile = "../../FsCheckKey.snk" + let [] InternalsVisibleTo = "FsCheck.Test" diff --git a/src/FsCheck.Xunit.v3/FsCheck.Xunit.v3.fsproj b/src/FsCheck.Xunit.v3/FsCheck.Xunit.v3.fsproj new file mode 100644 index 00000000..2251ae55 --- /dev/null +++ b/src/FsCheck.Xunit.v3/FsCheck.Xunit.v3.fsproj @@ -0,0 +1,28 @@ + + + + FsCheck.Xunit + netstandard2.0;net462 + true + false + + FsCheck.Xunit integrates FsCheck with xUnit.v3.NET by adding a PropertyAttribute that runs FsCheck tests, similar to xUnit.v3.NET's FactAttribute. + + All the options normally available in vanilla FsCheck via configuration can be controlled via the PropertyAttribute. + + $(PackageTags);xunit;xunit.net + + + + + + + + + + + + + + + diff --git a/src/FsCheck.Xunit.v3/paket.template b/src/FsCheck.Xunit.v3/paket.template new file mode 100644 index 00000000..32806cca --- /dev/null +++ b/src/FsCheck.Xunit.v3/paket.template @@ -0,0 +1,32 @@ +type file +id + FsCheck.Xunit.v3 +authors + Kurt Schelfthout and contributors +owners + Kurt Schelfthout and contributors +projectUrl + https://github.com/fsharp/FsCheck +iconUrl + https://raw.githubusercontent.com/fscheck/FsCheck/master/docs/files/img/logo.png +licenseUrl + https://github.com/fsharp/FsCheck/blob/master/License.txt +requireLicenseAcceptance + false +copyright + Copyright 2017 +tags + test testing random fscheck quickcheck xunit xunit.net +summary + Integrates FsCheck with xUnit.NET +description + FsCheck.Xunit.v3 integrates FsCheck with xUnit.NET by adding a PropertyAttribute that runs FsCheck tests, similar to xUnit.NET's FactAttribute. + + All the options normally available in vanilla FsCheck via configuration can be controlled via the PropertyAttribute. +dependencies + FsCheck = CURRENTVERSION + xunit.extensibility.execution ~> 2.4 +files + bin/Release/netstandard2.0/FsCheck.Xunit.v3.dll ==> lib/netstandard2.0 + bin/Release/netstandard2.0/FsCheck.Xunit.v3.xml ==> lib/netstandard2.0 + From 2ccd29d2b8236d28c4f0168e63e3714eaafd2000 Mon Sep 17 00:00:00 2001 From: Oskar Gewalli Date: Tue, 1 Apr 2025 21:11:11 +0300 Subject: [PATCH 2/2] Fewer compilation errors --- src/FsCheck.Xunit.v3/CheckExtensions.fs | 60 ++++ src/FsCheck.Xunit.v3/FsCheck.Xunit.v3.fsproj | 6 +- src/FsCheck.Xunit.v3/PropertyAttribute.fs | 314 +++++++++++++++++++ src/FsCheck.Xunit.v3/Runner.fs | 21 ++ src/FsCheck.Xunit.v3/paket.template | 32 -- 5 files changed, 398 insertions(+), 35 deletions(-) create mode 100644 src/FsCheck.Xunit.v3/CheckExtensions.fs create mode 100644 src/FsCheck.Xunit.v3/PropertyAttribute.fs create mode 100644 src/FsCheck.Xunit.v3/Runner.fs delete mode 100644 src/FsCheck.Xunit.v3/paket.template diff --git a/src/FsCheck.Xunit.v3/CheckExtensions.fs b/src/FsCheck.Xunit.v3/CheckExtensions.fs new file mode 100644 index 00000000..811d46f2 --- /dev/null +++ b/src/FsCheck.Xunit.v3/CheckExtensions.fs @@ -0,0 +1,60 @@ +namespace FsCheck.Xunit + +open System +open System.Runtime.CompilerServices +open FsCheck +open global.Xunit + +module private Helper = + let private runner (testOutputHelper: ITestOutputHelper) = + { new IRunner with + member __.OnStartFixture t = + Runner.onStartFixtureToString t |> testOutputHelper.WriteLine + member __.OnArguments (ntest,args, every) = + every ntest args |> testOutputHelper.WriteLine + member __.OnShrink(args, everyShrink) = + everyShrink args |> testOutputHelper.WriteLine + member __.OnFinished(name,testResult) = + Runner.onFinishedToString name testResult |> testOutputHelper.WriteLine + } + + let private throwingRunner (testOutputHelper: ITestOutputHelper) = + { new IRunner with + member __.OnStartFixture t = + testOutputHelper.WriteLine (Runner.onStartFixtureToString t) + member __.OnArguments (ntest,args, every) = + testOutputHelper.WriteLine (every ntest args) + member __.OnShrink(args, everyShrink) = + testOutputHelper.WriteLine (everyShrink args) + member __.OnFinished(name,testResult) = + match testResult with + | FsCheck.TestResult.Passed _ -> testOutputHelper.WriteLine (Runner.onFinishedToString name testResult) + | _ -> failwithf "%s" (Runner.onFinishedToString name testResult) + } + + let writeToXunit (config:Config) (testOutputHelper: ITestOutputHelper) = + config.WithRunner(runner testOutputHelper) + + let writeToXunitThrow (config:Config) (testOutputHelper: ITestOutputHelper) = + config.WithRunner(throwingRunner testOutputHelper) + +[] +type CheckExtensions = + [] + static member QuickCheck(property:Property, testOutputHelper: ITestOutputHelper) = + Check.One(Helper.writeToXunit Config.Quick testOutputHelper,property) + [] + static member QuickCheck(property:Property, testName:string, testOutputHelper: ITestOutputHelper) = + Check.One(testName,Helper.writeToXunit Config.Quick testOutputHelper,property) + [] + static member QuickCheckThrowOnFailure(property:Property, testOutputHelper: ITestOutputHelper) = + Check.One(Helper.writeToXunitThrow Config.QuickThrowOnFailure testOutputHelper,property) + [] + static member VerboseCheck(property:Property, testOutputHelper: ITestOutputHelper) = + Check.One(Helper.writeToXunit Config.Verbose testOutputHelper, property) + [] + static member VerboseCheck(property:Property, testName:string, testOutputHelper: ITestOutputHelper) = + Check.One(testName, Helper.writeToXunit Config.Verbose testOutputHelper, property) + [] + static member VerboseCheckThrowOnFailure(property:Property, testOutputHelper: ITestOutputHelper) = + Check.One(Helper.writeToXunitThrow Config.VerboseThrowOnFailure testOutputHelper,property) \ No newline at end of file diff --git a/src/FsCheck.Xunit.v3/FsCheck.Xunit.v3.fsproj b/src/FsCheck.Xunit.v3/FsCheck.Xunit.v3.fsproj index 2251ae55..f3f619b7 100644 --- a/src/FsCheck.Xunit.v3/FsCheck.Xunit.v3.fsproj +++ b/src/FsCheck.Xunit.v3/FsCheck.Xunit.v3.fsproj @@ -14,9 +14,9 @@ - - - + + + diff --git a/src/FsCheck.Xunit.v3/PropertyAttribute.fs b/src/FsCheck.Xunit.v3/PropertyAttribute.fs new file mode 100644 index 00000000..c78f6188 --- /dev/null +++ b/src/FsCheck.Xunit.v3/PropertyAttribute.fs @@ -0,0 +1,314 @@ +namespace FsCheck.Xunit.v3 + +open System +open System.Reflection +open System.Threading.Tasks + +open Xunit +open Xunit.Sdk + +open FsCheck +open global.Xunit.v3 + +type PropertyFailedException = + inherit Exception + new (testResult:FsCheck.TestResult) = { + inherit Exception(sprintf "%s%s" Environment.NewLine (Runner.onFinishedToString "" testResult)) } + new (userMessage, innerException : exn) = { + inherit Exception(userMessage, innerException) } + +//can not be an anonymous type because of let mutable. +type XunitRunner() = + let mutable result = None + member __.Result = result.Value + interface IRunner with + override __.OnStartFixture _ = () + override __.OnArguments (ntest,args, every) = + every ntest args |> ignore + override __.OnShrink(args, everyShrink) = + everyShrink args |> ignore + override __.OnFinished(_ ,testResult) = + result <- Some testResult + +type internal PropertyConfig = + { MaxTest : Option + MaxRejected : Option + Replay : Option + Parallelism : Option + StartSize : Option + EndSize : Option + Verbose : Option + QuietOnSuccess : Option + Arbitrary : Type[] } + +[] +module internal PropertyConfig = + let orElse y = function + | Some x -> Some x + | None -> y + + let orDefault x y = defaultArg y x + + let zero = + { MaxTest = None + MaxRejected = None + Replay = None + Parallelism = None + StartSize = None + EndSize = None + Verbose = None + QuietOnSuccess = None + Arbitrary = [||] } + + let combine extra original = + { MaxTest = extra.MaxTest |> orElse original.MaxTest + MaxRejected = extra.MaxRejected |> orElse original.MaxRejected + Replay = extra.Replay |> orElse original.Replay + Parallelism = extra.Parallelism |> orElse original.Parallelism + StartSize = extra.StartSize |> orElse original.StartSize + EndSize = extra.EndSize |> orElse original.EndSize + Verbose = extra.Verbose |> orElse original.Verbose + QuietOnSuccess = extra.QuietOnSuccess |> orElse original.QuietOnSuccess + Arbitrary = Array.append extra.Arbitrary original.Arbitrary } + + let parseReplay (str: string) = + //if someone sets this, we want it to throw if it fails + let split = str.Trim('(',')').Split([|","|], StringSplitOptions.RemoveEmptyEntries) + let seed = UInt64.Parse(split.[0]) + let gamma = UInt64.Parse(split.[1]) + let size = if split.Length = 3 then Some <| Convert.ToInt32(UInt32.Parse(split.[2])) else None + { Rnd = Rnd (seed,gamma); Size = size } + + let toConfig (output : TestOutputHelper) propertyConfig = + Config.Default + .WithReplay( + propertyConfig.Replay + |> Option.map parseReplay + |> orElse Config.Default.Replay + ) + .WithParallelRunConfig( + propertyConfig.Parallelism + |> Option.map (fun i -> { MaxDegreeOfParallelism = i }) + |> orElse Config.Default.ParallelRunConfig + ) + .WithMaxTest(propertyConfig.MaxTest |> orDefault Config.Default.MaxTest) + .WithMaxRejected(propertyConfig.MaxRejected |> orDefault Config.Default.MaxRejected) + .WithStartSize(propertyConfig.StartSize |> orDefault Config.Default.StartSize) + .WithEndSize(propertyConfig.EndSize |> orDefault Config.Default.EndSize) + .WithQuietOnSuccess(propertyConfig.QuietOnSuccess |> orDefault Config.Default.QuietOnSuccess) + .WithArbitrary(Seq.toList propertyConfig.Arbitrary) + .WithRunner(XunitRunner()) + .WithEvery( + if propertyConfig.Verbose |> Option.exists id then + fun n args -> output.WriteLine (Config.Verbose.Every n args); "" + else + Config.Quick.Every + ) + .WithEveryShrink( + if propertyConfig.Verbose |> Option.exists id then + fun args -> output.WriteLine (Config.Verbose.EveryShrink args); "" + else + Config.Quick.EveryShrink + ) + +///Run this method as an FsCheck test. +[] +[)>] +type public PropertyAttribute() = + inherit FactAttribute() + let mutable config = PropertyConfig.zero + let mutable replay = null + let mutable parallelism = -1 + let mutable maxTest = -1 + let mutable maxRejected = -1 + let mutable startSize = -1 + let mutable endSize = -1 + let mutable verbose = false + let mutable arbitrary = [||] + let mutable quietOnSuccess = false + + ///If set, the seed to use to start testing. Allows reproduction of previous runs. You can just paste + ///the tuple from the output window, e.g. 12344,12312 or (123,123). + ///Additionally, you can also specify a start size as the third parameter, e.g. 12344,12312,10 or (123,123,10). + member __.Replay with get() = replay and set(v) = replay <- v; config <- {config with Replay = if String.IsNullOrEmpty v then None else Some v} + ///If set, run tests in parallel. Useful for Task/async related work and heavy number crunching + ///Environment.ProcessorCount have been found to be useful default. + member __.Parallelism with get() = parallelism and set(v) = parallelism <- v; config <- {config with Parallelism = Some v} + ///The maximum number of tests that are run. + member __.MaxTest with get() = maxTest and set(v) = maxTest <- v; config <- {config with MaxTest = Some v} + ///The maximum number of tests where values are rejected, e.g. as the result of ==> + member __.MaxRejected with get() = maxRejected and set(v) = maxRejected <- v; config <- {config with MaxRejected = Some v} + ///The size to use for the first test. + member __.StartSize with get() = startSize and set(v) = startSize <- v; config <- {config with StartSize = Some v} + ///The size to use for the last test, when all the tests are passing. The size increases linearly between Start- and EndSize. + member __.EndSize with get() = endSize and set(v) = endSize <- v; config <- {config with EndSize = Some v} + ///Output all generated arguments. + member __.Verbose with get() = verbose and set(v) = verbose <- v; config <- {config with Verbose = Some v} + ///The Arbitrary instances to use for this test method. The Arbitrary instances + ///are merged in back to front order i.e. instances for the same generated type + ///at the front of the array will override those at the back. + member __.Arbitrary with get() = arbitrary and set(v) = arbitrary <- v; config <- {config with Arbitrary = v} + ///If set, suppresses the output from the test if the test is successful. This can be useful when running tests + ///with TestDriven.net, because TestDriven.net pops up the Output window in Visual Studio if a test fails; thus, + ///when conditioned to that behaviour, it's always a bit jarring to receive output from passing tests. + ///The default is false, which means that FsCheck will also output test results on success, but if set to true, + ///FsCheck will suppress output in the case of a passing test. This setting doesn't affect the behaviour in case of + ///test failures. + member __.QuietOnSuccess with get() = quietOnSuccess and set(v) = quietOnSuccess <- v; config <- {config with QuietOnSuccess = Some v} + + member internal __.Config = config +and + ///Set common configuration for all properties within this class or module + [] + public PropertiesAttribute() = inherit PropertyAttribute() +and +/// The xUnit2 test runner for the PropertyAttribute that executes the test via FsCheck + PropertyTestCase(diagnosticMessageSink:IMessageSink, defaultMethodDisplay:TestMethodDisplay, defaultMethodDisplayOptions:TestMethodDisplayOptions, testMethod:ITestMethod, ?testMethodArguments:obj []) = + inherit XunitTestCase(diagnosticMessageSink, defaultMethodDisplay, defaultMethodDisplayOptions, testMethod, (match testMethodArguments with | None -> null | Some v -> v)) + + let combineAttributes (configs: (PropertyConfig option) list) = + configs + |> List.choose id + |> List.reduce(fun higherLevelAttribute lowerLevelAttribute -> + PropertyConfig.combine lowerLevelAttribute higherLevelAttribute) + + new() = new PropertyTestCase(null, TestMethodDisplay.ClassAndMethod, TestMethodDisplayOptions.None, null) + + member this.Init(output:TestOutputHelper) = + let getPropertiesOnDeclaringClasses (testClass: ITestClass) = + [ let mutable current: Type = testClass.Class.ToRuntimeType() + while not (isNull current) do + yield current.GetTypeInfo().GetCustomAttributes() + |> Seq.tryHead + |> Option.map (fun attr -> attr.Config) + current <- current.DeclaringType] + |> List.rev + + let getConfig (attr: IAttributeInfo) = + attr.GetNamedArgument "Config" + + let config = combineAttributes [ + yield this.TestMethod.TestClass.Class.Assembly.GetCustomAttributes(typeof) |> Seq.tryHead |> Option.map getConfig + yield! getPropertiesOnDeclaringClasses this.TestMethod.TestClass + yield this.TestMethod.Method.GetCustomAttributes(typeof) |> Seq.head |> getConfig |> Some] + + { config with Arbitrary = config.Arbitrary } + |> PropertyConfig.toConfig output + + override this.RunAsync(diagnosticMessageSink:IMessageSink, messageBus:IMessageBus, constructorArguments:obj [], aggregator:ExceptionAggregator, cancellationTokenSource:Threading.CancellationTokenSource) = + let test = XunitTest(this, this.DisplayName) + let summary = RunSummary(Total = 1); + + // 1. We always need an initialized TestOutputHelper so we can write output. + // 2. xunit supports test classes that have a constructor that takes a TestOutputHelper, which we need to pass along. + // xunit has two different ways of passing us the TestOutputHelper. + // pre-2.9.0: as a TestOutputHelper constructor argument + // post-2.9.0: as a Func constructor argument https://github.com/xunit/xunit/issues/2996#issuecomment-2271764192 + // The below code handles both cases. + let mutable outputHelper = TestOutputHelper() + for i in 0..constructorArguments.Length-1 do + match constructorArguments[i] with + | :? TestOutputHelper as foundHelper -> + outputHelper <- foundHelper + | :? Func as foundHelperFunc -> + outputHelper <- foundHelperFunc.Invoke() + constructorArguments.[i] <- outputHelper + | _ -> () + outputHelper.Initialize(messageBus, test) + + let dispose testClass = + match testClass with + | None -> () + | Some obj -> + match box obj with + | :? IDisposable as d -> d.Dispose() + | _ -> () + + let testExec() = + let config = this.Init(outputHelper) + let timer = ExecutionTimer() + let result = + try + let xunitRunner = if config.Runner :? XunitRunner then (config.Runner :?> XunitRunner) else XunitRunner() + let runMethod = this.TestMethod.Method.ToRuntimeMethod() + let target = + let testClass = this.TestMethod.TestClass.Class.ToRuntimeType() + if (not (isNull this.TestMethod.TestClass)) && not this.TestMethod.Method.IsStatic then + + let testInstance = test.CreateTestClass(testClass, constructorArguments, messageBus, timer, cancellationTokenSource) + + match testInstance with + | :? IAsyncLifetime as asyncLifetime -> asyncLifetime.InitializeAsync() + | _ -> Task.CompletedTask + |> Async.AwaitTask + |> Async.StartAsTask + |> Task.WaitAll + + Some testInstance + else None + + timer.Aggregate(fun () -> Check.Method(config, runMethod, ?target=target)) + + dispose target + + match xunitRunner.Result with + | TestResult.Passed _ -> + let output = Runner.onFinishedToString "" xunitRunner.Result + outputHelper.WriteLine(output) + TestPassed(test, timer.Total, outputHelper.Output) :> TestResultMessage + | TestResult.Exhausted _ -> + summary.Failed <- summary.Failed + 1 + upcast TestFailed(test, timer.Total, outputHelper.Output, PropertyFailedException(xunitRunner.Result)) + | TestResult.Failed (testdata, originalArgs, shrunkArgs, Outcome.Failed e, originalSeed, lastSeed, lastSize) -> + summary.Failed <- summary.Failed + 1 + let message = sprintf "%s%s" Environment.NewLine (Runner.onFailureToString "" testdata originalArgs shrunkArgs originalSeed lastSeed lastSize) + upcast TestFailed(test, timer.Total, outputHelper.Output, PropertyFailedException(message, e)) + | TestResult.Failed _ -> + summary.Failed <- summary.Failed + 1 + upcast TestFailed(test, timer.Total, outputHelper.Output, PropertyFailedException(xunitRunner.Result)) + with + | ex -> + summary.Failed <- summary.Failed + 1 + outputHelper.WriteLine("Exception during test") + upcast TestFailed(test, timer.Total, outputHelper.Output, ex) + + + outputHelper.Uninitialize() + + messageBus.QueueMessage(result) |> ignore + summary.Time <- summary.Time + result.ExecutionTime + if not (messageBus.QueueMessage(TestFinished(test, summary.Time, result.Output))) then + cancellationTokenSource.Cancel() |> ignore + if not (messageBus.QueueMessage(TestCaseFinished(this, summary.Time, summary.Total, summary.Failed, summary.Skipped))) then + cancellationTokenSource.Cancel() |> ignore + + summary + + if not (messageBus.QueueMessage(TestCaseStarting(this))) then + cancellationTokenSource.Cancel() |> ignore + + if not (messageBus.QueueMessage(TestStarting(test))) then + cancellationTokenSource.Cancel() |> ignore + + if not(String.IsNullOrEmpty(this.SkipReason)) then + summary.Skipped <- summary.Skipped + 1 + if not(messageBus.QueueMessage(TestSkipped(test, this.SkipReason))) then + cancellationTokenSource.Cancel() |> ignore + if not(messageBus.QueueMessage(TestCaseFinished(this, decimal 1, 0, 0, 1))) then + cancellationTokenSource.Cancel() |> ignore + Task.FromResult(summary) + else + Task.Run(testExec) +and +/// xUnit2 test case discoverer to link the method with the PropertyAttribute to the PropertyTestCase +/// so the test can be run via FsCheck. + PropertyDiscoverer(messageSink:IMessageSink) = + + new () = PropertyDiscoverer(null) + + member __.MessageSink = messageSink + + interface IXunitTestCaseDiscoverer with + override this.Discover(discoveryOptions:ITestFrameworkDiscoveryOptions, testMethod:ITestMethod, _:IAttributeInfo)= + let ptc = new PropertyTestCase(this.MessageSink, discoveryOptions.MethodDisplayOrDefault(), discoveryOptions.MethodDisplayOptionsOrDefault(), testMethod) + Seq.singleton (ptc :> IXunitTestCase) diff --git a/src/FsCheck.Xunit.v3/Runner.fs b/src/FsCheck.Xunit.v3/Runner.fs new file mode 100644 index 00000000..a706017e --- /dev/null +++ b/src/FsCheck.Xunit.v3/Runner.fs @@ -0,0 +1,21 @@ +namespace FsCheck.Xunit.v3 + +open FsCheck +open global.Xunit.v3 + +/// A runner for FsCheck (i.e. that you can use as Config.Runner) which outputs +/// to Xunit's given ITestOutputHelper. +/// For example, { Config.QuickThrowOnFailure with Runner = TestOutputRunner(output) } +type TestOutputRunner(output: TestOutputHelper) = + interface IRunner with + member _.OnStartFixture t = + output.WriteLine (Runner.onStartFixtureToString t) + member _.OnArguments (ntest, args, every) = + output.WriteLine (every ntest args) + member _.OnShrink(args, everyShrink) = + output.WriteLine (everyShrink args) + member _.OnFinished(name,testResult) = + let resultText = Runner.onFinishedToString name testResult + match testResult with + | TestResult.Passed _ -> resultText |> output.WriteLine + | _ -> failwithf "%s" resultText \ No newline at end of file diff --git a/src/FsCheck.Xunit.v3/paket.template b/src/FsCheck.Xunit.v3/paket.template deleted file mode 100644 index 32806cca..00000000 --- a/src/FsCheck.Xunit.v3/paket.template +++ /dev/null @@ -1,32 +0,0 @@ -type file -id - FsCheck.Xunit.v3 -authors - Kurt Schelfthout and contributors -owners - Kurt Schelfthout and contributors -projectUrl - https://github.com/fsharp/FsCheck -iconUrl - https://raw.githubusercontent.com/fscheck/FsCheck/master/docs/files/img/logo.png -licenseUrl - https://github.com/fsharp/FsCheck/blob/master/License.txt -requireLicenseAcceptance - false -copyright - Copyright 2017 -tags - test testing random fscheck quickcheck xunit xunit.net -summary - Integrates FsCheck with xUnit.NET -description - FsCheck.Xunit.v3 integrates FsCheck with xUnit.NET by adding a PropertyAttribute that runs FsCheck tests, similar to xUnit.NET's FactAttribute. - - All the options normally available in vanilla FsCheck via configuration can be controlled via the PropertyAttribute. -dependencies - FsCheck = CURRENTVERSION - xunit.extensibility.execution ~> 2.4 -files - bin/Release/netstandard2.0/FsCheck.Xunit.v3.dll ==> lib/netstandard2.0 - bin/Release/netstandard2.0/FsCheck.Xunit.v3.xml ==> lib/netstandard2.0 -