{"id":13773616,"url":"https://github.com/AnthonyLloyd/CsCheck","last_synced_at":"2025-05-11T05:35:07.314Z","repository":{"id":38831633,"uuid":"276580462","full_name":"AnthonyLloyd/CsCheck","owner":"AnthonyLloyd","description":"Random testing library for C#","archived":false,"fork":false,"pushed_at":"2025-05-06T21:20:39.000Z","size":1207,"stargazers_count":176,"open_issues_count":4,"forks_count":5,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-05-06T21:40:58.646Z","etag":null,"topics":["causal-profiling","concurrency-testing","metamorphic-testing","model-based-testing","parallel-testing","performance-testing","quickcheck","random","regression-testing","stateful-testing","testing"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AnthonyLloyd.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-07-02T07:38:29.000Z","updated_at":"2025-05-06T21:20:43.000Z","dependencies_parsed_at":"2024-03-03T11:27:56.924Z","dependency_job_id":"d2c819c3-0706-404a-8a0e-1c344713f78e","html_url":"https://github.com/AnthonyLloyd/CsCheck","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnthonyLloyd%2FCsCheck","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnthonyLloyd%2FCsCheck/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnthonyLloyd%2FCsCheck/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnthonyLloyd%2FCsCheck/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AnthonyLloyd","download_url":"https://codeload.github.com/AnthonyLloyd/CsCheck/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253523688,"owners_count":21921815,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["causal-profiling","concurrency-testing","metamorphic-testing","model-based-testing","parallel-testing","performance-testing","quickcheck","random","regression-testing","stateful-testing","testing"],"created_at":"2024-08-03T17:01:17.994Z","updated_at":"2025-05-11T05:35:02.304Z","avatar_url":"https://github.com/AnthonyLloyd.png","language":"C#","readme":"# CsCheck\n[![build](https://github.com/AnthonyLloyd/CsCheck/workflows/CI/badge.svg?branch=master)](https://github.com/AnthonyLloyd/CsCheck/actions) [![Nuget](https://img.shields.io/nuget/v/CsCheck.svg)](https://www.nuget.org/packages/CsCheck/)\n\n\nCsCheck is a C# random testing library inspired by QuickCheck.\n\nIt differs in that generation and shrinking are both based on [PCG](https://www.pcg-random.org), a fast random number generator.\n\nThis gives the following advantages over tree based shrinking libraries:\n\n- Automatic shrinking. Gen classes are composable with no need for Arb classes. So less boilerplate.\n- Random testing and shrinking are parallelized. This and PCG make it very fast.\n- Shrunk cases have a seed value. Simpler examples can easily be reproduced.\n- Shrinking can be continued later to give simpler cases for high dimensional problems.\n- Parallel testing and random shrinking work well together. Repeat is not needed.\n\nSee [why](https://github.com/AnthonyLloyd/CsCheck/blob/master/Why.md) you should use it, the [comparison](https://github.com/AnthonyLloyd/CsCheck/blob/master/Comparison.md) with other random testing libraries, or how CsCheck does in the [shrinking challenge](https://github.com/jlink/shrinking-challenge).\nIn one [shrinking challenge test](https://github.com/jlink/shrinking-challenge/blob/main/challenges/binheap.md) CsCheck managed to shrink to a new smaller example than was thought possible and is not reached by any other testing library.\nCsCheck is the only random testing library that can always shrink to the simplest example (given enough time).\n\nCsCheck also has functionality to make multiple types of testing simple and fast:\n\n- [Random testing](#Random-testing)\n- [Model-based testing](#Model-based-testing)\n- [Metamorphic testing](#Metamorphic-testing)\n- [Parallel testing](#Parallel-testing)\n- [Causal profiling](#Causal-profiling)\n- [Regression testing](#Regression-testing)\n- [Performance testing](#Performance-testing)\n- [Debug utilities](#Debug-utilities)\n- [Configuration](#Configuration)\n- [Development](#Development)\n\nThe following tests are in xUnit but could equally be used in any testing framework.\n\nMore to see in the [Tests](https://github.com/AnthonyLloyd/CsCheck/tree/master/Tests). There are also 1,000+ F# tests using CsCheck in [MKL.NET](https://github.com/MKL-NET/MKL.NET/tree/master/Tests).\n\nNo Reflection was used in the making of this product.\n\n## Generator Creation Example\n\nUse **Gen** and its Linq methods to compose generators for any type. Here we create a **Gen** for json documents.\nMore often it will simply be composing a few primitives and collections.\nDon't worry about shrinking as it's automatic and the best in the business.\n\n```csharp\nstatic readonly Gen\u003cstring\u003e genString = Gen.String[Gen.Char.AlphaNumeric, 2, 5];\nstatic readonly Gen\u003cJsonNode\u003e genJsonValue = Gen.OneOf\u003cJsonNode\u003e(\n    Gen.Bool.Select(x =\u003e JsonValue.Create(x)),\n    Gen.Byte.Select(x =\u003e JsonValue.Create(x)),\n    Gen.Char.AlphaNumeric.Select(x =\u003e JsonValue.Create(x)),\n    Gen.DateTime.Select(x =\u003e JsonValue.Create(x)),\n    Gen.DateTimeOffset.Select(x =\u003e JsonValue.Create(x)),\n    Gen.Decimal.Select(x =\u003e JsonValue.Create(x)),\n    Gen.Double.Select(x =\u003e JsonValue.Create(x)),\n    Gen.Float.Select(x =\u003e JsonValue.Create(x)),\n    Gen.Guid.Select(x =\u003e JsonValue.Create(x)),\n    Gen.Int.Select(x =\u003e JsonValue.Create(x)),\n    Gen.Long.Select(x =\u003e JsonValue.Create(x)),\n    Gen.SByte.Select(x =\u003e JsonValue.Create(x)),\n    Gen.Short.Select(x =\u003e JsonValue.Create(x)),\n    genString.Select(x =\u003e JsonValue.Create(x)),\n    Gen.UInt.Select(x =\u003e JsonValue.Create(x)),\n    Gen.ULong.Select(x =\u003e JsonValue.Create(x)),\n    Gen.UShort.Select(x =\u003e JsonValue.Create(x)));\nstatic readonly Gen\u003cJsonNode\u003e genJsonNode = Gen.Recursive\u003cJsonNode\u003e((depth, genJsonNode) =\u003e\n{\n    if (depth == 5) return genJsonValue;\n    var genJsonObject = Gen.Dictionary(genString, genJsonNode.Null())[0, 5].Select(d =\u003e new JsonObject(d));\n    var genJsonArray = genJsonNode.Null().Array[0, 5].Select(i =\u003e new JsonArray(i));\n    return Gen.OneOf(genJsonObject, genJsonArray, genJsonValue);\n});\n```\n\n## Random testing\n\n**Sample** is used to perform tests with a generator. Either return false or throw an exception for failure.\n**Sample** will aggressively shrink any failure down to the simplest example.  \nThe default sample size is 100 iterations. Set iter: to change this or time: to run for a number of seconds.  \nSetting these from the command line can be a good way to run your tests in different ways and in Release mode.\n\n### Unit Single\n```csharp\n[Fact]\npublic void Single_Unit_Range()\n{\n    Gen.Single.Unit.Sample(f =\u003e Assert.InRange(f, 0f, 0.9999999f));\n}\n```\n\n### Long Range\n```csharp\n[Fact]\npublic void Long_Range()\n{\n    (from t in Gen.Select(Gen.Long, Gen.Long)\n     let start = Math.Min(t.V0, t.V1)\n     let finish = Math.Max(t.V0, t.V1)\n     from value in Gen.Long[start, finish]\n     select (value, start, finish))\n    .Sample(i =\u003e Assert.InRange(i.value, i.start, i.finish));\n}\n```\n\n### Int Distribution\n```csharp\n[Fact]\npublic void Int_Distribution()\n{\n    int buckets = 70;\n    int frequency = 10;\n    int[] expected = Enumerable.Repeat(frequency, buckets).ToArray();\n    Gen.Int[0, buckets - 1].Array[frequency * buckets]\n    .Select(sample =\u003e Tally(buckets, sample))\n    .Sample(actual =\u003e Check.ChiSquared(expected, actual));\n}\n```\n\n### Serialization Roundtrip\n```csharp\nstatic void TestRoundtrip\u003cT\u003e(Gen\u003cT\u003e gen, Action\u003cStream, T\u003e serialize, Func\u003cStream, T\u003e deserialize)\n{\n    gen.Sample(t =\u003e\n    {\n        using var ms = new MemoryStream();\n        serialize(ms, t);\n        ms.Position = 0;\n        return deserialize(ms).Equals(t);\n    });\n}\n[Fact]\npublic void Varint()\n{\n    TestRoundtrip(Gen.UInt, StreamSerializer.WriteVarint, StreamSerializer.ReadVarint);\n}\n[Fact]\npublic void Double()\n{\n    TestRoundtrip(Gen.Double, StreamSerializer.WriteDouble, StreamSerializer.ReadDouble);\n}\n[Fact]\npublic void DateTime()\n{\n    TestRoundtrip(Gen.DateTime, StreamSerializer.WriteDateTime, StreamSerializer.ReadDateTime);\n}\n```\n\n### Shrinking Challenge\n```csharp\n[Fact]\npublic void No2_LargeUnionList()\n{\n    Gen.Int.Array.Array\n    .Sample(aa =\u003e\n    {\n        var hs = new HashSet\u003cint\u003e();\n        foreach (var a in aa)\n        {\n            foreach (var i in a) hs.Add(i);\n            if (hs.Count \u003e= 5) return false;\n        }\n        return true;\n    });\n}\n```\n\n### Recursive\n```csharp\nrecord MyObj(int Id, MyObj[] Children);\n\n[Fact]\npublic void RecursiveDepth()\n{\n    int maxDepth = 4;\n    Gen.Recursive\u003cMyObj\u003e((i, my) =\u003e\n        Gen.Select(Gen.Int, my.Array[0, i \u003c maxDepth ? 6 : 0], (i, a) =\u003e new MyObj(i, a))\n    )\n    .Sample(i =\u003e\n    {\n        static int Depth(MyObj o) =\u003e o.Children.Length == 0 ? 0 : 1 + o.Children.Max(Depth);\n        return Depth(i) \u003c= maxDepth;\n    });\n}\n```\n\n### Classify\n\nChange the return in **Sample** to a string to produce a summary classification table.\nAll other optional parameters work the same but writeLine: is now mandatory.\n\n```csharp\n[Fact]\npublic void AllocatorMany_Classify()\n{\n    Gen.Select(Gen.Int[3, 30], Gen.Int[3, 15]).SelectMany((rows, cols) =\u003e\n        Gen.Select(\n            Gen.Int[0, 5].Array[cols].Where(a =\u003e a.Sum() \u003e 0).Array[rows],\n            Gen.Int[900, 1000].Array[rows],\n            Gen.Int.Uniform))\n    .Sample((solution,\n             rowPrice,\n             seed) =\u003e\n    {\n        var rowTotal = Array.ConvertAll(solution, row =\u003e row.Sum());\n        var colTotal = Enumerable.Range(0, solution[0].Length).Select(col =\u003e solution.SumCol(col)).ToArray();\n        var allocation = AllocatorMany.Allocate(rowPrice, rowTotal, colTotal, new(seed), time: 60);\n        if (!TotalsCorrectly(rowTotal, colTotal, allocation.Solution))\n            throw new Exception(\"Does not total correctly\");\n        return $\"{(allocation.KnownGlobal ? \"Global\" : \"Local\")}/{allocation.SolutionType}\";\n    }, output.WriteLine, time: 900);\n}\n```\n\n|                    | Count |       % |      Median |     Lower Q |     Upper Q |     Minimum |     Maximum |\n|--------------------|------:|--------:|------------:|------------:|------------:|------------:|------------:|\n| Global             |   458 |  50.22% |             |             |             |             |             |\n|   RoundingMinimum  |   343 |  37.61% |      2.68ms |      0.50ms |     10.85ms |      0.03ms |    190.92ms |\n|   EveryCombination |    87 |   9.54% |    173.99ms |     16.80ms |  1,199.64ms |      0.20ms | 42,257.35ms |\n|   RandomChange     |    28 |   3.07% | 59,592.98ms | 55,267.94ms | 59,901.58ms | 38,575.41ms | 60,107.64ms |\n| Local              |   454 |  49.78% |             |             |             |             |             |\n|   RoundingMinimum  |   301 |  33.00% | 60,000.12ms | 60,000.04ms | 60,003.70ms | 60,000.02ms | 60,144.84ms |\n|   RandomChange     |    90 |   9.87% | 60,000.06ms | 60,000.03ms | 60,004.41ms | 60,000.02ms | 60,136.59ms |\n|   EveryCombination |    63 |   6.91% | 60,000.10ms | 60,000.03ms | 60,001.29ms | 60,000.01ms | 60,019.36ms |\n\n## Model-based testing\n\nModel-based is the most efficient form of random testing.\nOnly a small amount of code is needed to fully test functionality.\nSampleModelBased generates an initial actual and model and then applies a random sequence of operations to both checking that the actual and model are still equal.\n\n### SetSlim Add\n```csharp\n[Fact]\npublic void\nSetSlim_ModelBased()\n{\n    Gen.Int.Array.Select(a =\u003e (new SetSlim\u003cint\u003e(a), new HashSet\u003cint\u003e(a)))\n    .SampleModelBased(\n        Gen.Int.Operation\u003cSetSlim\u003cint\u003e, HashSet\u003cint\u003e\u003e(\n            (ss, i) =\u003e ss.Add(i),\n            (hs, i) =\u003e hs.Add(i)\n        )\n        // ... other operations\n    );\n}\n```\n\n## Metamorphic testing\n\nThe second most efficient form of random testing is metamorphic which means doing something two different ways and checking they produce the same result.\nSampleMetamorphic generates two identical initial samples and then applies the two functions and asserts the results are equal.\nThis can be needed when no model can be found that is not just a reimplementation.\n\nMore about how useful metamorphic tests can be here: [How to specify it!](https://youtu.be/G0NUOst-53U?t=1639).\n\n### MapSlim Update\n```csharp\n[Fact]\npublic void MapSlim_Metamorphic()\n{\n    Gen.Dictionary(Gen.Int, Gen.Byte)\n    .Select(d =\u003e new MapSlim\u003cint, byte\u003e(d))\n    .SampleMetamorphic(\n        Gen.Select(Gen.Int[0, 100], Gen.Byte, Gen.Int[0, 100], Gen.Byte).Metamorphic\u003cMapSlim\u003cint, byte\u003e\u003e(\n            (d, t) =\u003e { d[t.V0] = t.V1; d[t.V2] = t.V3; },\n            (d, t) =\u003e { if (t.V0 == t.V2) d[t.V2] = t.V3; else { d[t.V2] = t.V3; d[t.V0] = t.V1; } }\n        )\n    );\n}\n```\n\n## Parallel testing\n\nCsCheck has support for parallel testing with full shrinking capability.\nA number of operations are run sequentially and then a number in parallel on an initial state and the result is compared to all the possible linearized versions.\nAt least one of these must be equal to the parallel result.\n\nIdea from John Hughes [talk](https://youtu.be/1LNEWF8s1hI?t=1603) and [paper](https://github.com/AnthonyLloyd/AnthonyLloyd.github.io/raw/master/public/cscheck/finding-race-conditions.pdf). This is easier to implement with CsCheck than QuickCheck because the random shrinking does not need to repeat each step as QuickCheck does (10 times by default) to make shrinking deterministic.\n\n```csharp\n[Fact]\npublic void SampleParallel_ConcurrentQueue()\n{\n    Gen.Const(() =\u003e new ConcurrentQueue\u003cint\u003e())\n    .SampleParallel(\n        Gen.Int.Operation\u003cConcurrentQueue\u003cint\u003e\u003e(i =\u003e $\"Enqueue({i})\", (q, i) =\u003e q.Enqueue(i)),\n        Gen.Operation\u003cConcurrentQueue\u003cint\u003e\u003e(\"TryDequeue()\", q =\u003e q.TryDequeue(out _))\n    );\n}\n```\n\nCan also be tested against a model (which doesn't need to be thread-safe):\n\n```csharp\n[Fact]\npublic void SampleParallelModel_ConcurrentQueue()\n{\n    Gen.Const(() =\u003e (new ConcurrentQueue\u003cint\u003e(), new Queue\u003cint\u003e()))\n    .SampleParallel(\n        Gen.Int.Operation\u003cConcurrentQueue\u003cint\u003e, Queue\u003cint\u003e\u003e(i =\u003e $\"Enqueue({i})\", (q, i) =\u003e q.Enqueue(i), (q, i) =\u003e q.Enqueue(i)),\n        Gen.Operation\u003cConcurrentQueue\u003cint\u003e, Queue\u003cint\u003e\u003e(\"TryDequeue()\", q =\u003e q.TryDequeue(out _), q =\u003e q.TryDequeue(out _))\n    );\n}\n```\n\n## Causal profiling\n\nCausal profiling is a technique to investigate the effect of speeding up one or more concurrent regions of code.\nIt shows which regions are the bottleneck and what overall performance gain could be achieved from each region.\n\nIdea from Emery Berger. My blog posts on this [here](http://anthonylloyd.github.io/blog/2019/10/11/causal-profiling).\n\n```csharp\n[Fact]\npublic void Fasta()\n{\n    Causal.Profile(() =\u003e FastaUtils.Fasta.NotMain(10_000_000, null)).Output(writeLine);\n}\n\nstatic int[] Rnds(int i, int j, ref int seed)\n{\n    var region = Causal.RegionStart(\"rnds\");\n    var a = intPool.Rent(BlockSize1);\n    var s = a.AsSpan(0, i);\n    s[0] = j;\n    for (i = 1, j = Width; i \u003c s.Length; i++)\n    {\n        if (j-- == 0)\n        {\n            j = Width;\n            s[i] = IM * 3 / 2;\n        }\n        else\n        {\n            s[i] = seed = (seed * IA + IC) % IM;\n        }\n    }\n    Causal.RegionEnd(region);\n    return a;\n}\n```\n\n## Regression testing\n\n### Portfolio Calculation\n**Single** is used to find, pin and continue to check a suitable generated example e.g. to cover a certain codepath.  \n**Hash** is used to find and check a hash for a number of results.  \nIt saves a temp cache of the results on a successful hash check and each subsequent run will fail with actual vs expected at the first point of any difference.  \nTogether **Single** and **Hash** eliminate the need to commit data files in regression testing while also giving detailed information of any change.\n\n```csharp\n[Fact]\npublic void Portfolio_Small_Mixed_Example()\n{\n    var portfolio = ModelGen.Portfolio.Single(p =\u003e\n           p.Positions.Count == 5\n        \u0026\u0026 p.Positions.Any(p =\u003e p.Instrument is Bond)\n        \u0026\u0026 p.Positions.Any(p =\u003e p.Instrument is Equity)\n    , \"0N0XIzNsQ0O2\");\n    var currencies = portfolio.Positions.Select(p =\u003e p.Instrument.Currency).Distinct().ToArray();\n    var fxRates = ModelGen.Price.Array[currencies.Length].Single(a =\u003e\n        a.All(p =\u003e pp is \u003e 0.75 and \u003c 1.5)\n    , \"ftXKwKhS6ec4\");\n    double fxRate(Currency c) =\u003e fxRates[Array.IndexOf(currencies, c)];\n    Check.Hash(h =\u003e\n    {\n        h.Add(portfolio.Positions.Select(p =\u003e p.Profit));\n        h.Add(portfolio.Profit(fxRate));\n        h.Add(portfolio.RiskByPosition(fxRate));\n    }, 5857230471108592669, decimalPlaces: 2);\n}\n```\n\n## Performance testing\n\n**Faster** is used to statistically test that the first method is faster than the second and some condition is satisfied (by default equality of the output of the two methods).  \nSince it's statistical and relative you can run it as a normal test anywhere e.g. across multiple platforms on a continuous integration server.  \nIt's fast because it runs in parallel and knows when to stop.\nIt's just what you need to iteratively improve performance while making sure it still produces the correct results.\n\n```csharp\n[Fact]\npublic void Faster_Linq_Random()\n{\n    Gen.Byte.Array[100, 1000]\n    .Faster(\n        data =\u003e data.Aggregate(0.0, (t, b) =\u003e t + b),\n        data =\u003e data.Select(i =\u003e (double)i).Sum(),\n        writeLine: output.WriteLine\n    );\n}\n```\n\nThe performance is raised in an exception if it fails but can also be output if it passes with the above output function.\n```\nTests.CheckTests.Faster_Linq_Random [27ms]\nStandard Output Messages:\n32.29%[29.47%..36.51%] 1.48x[1.42x..1.58x] faster, sigma=50.0 (2,551 vs 17)\n```\n\n The first number is the estimated percentage median performance improvement with the interquartile range in the square brackets.\n The second number is the estimated times median performance improvement with the interquartile range in the square brackets.\n 33⅓% faster = 1.5x faster and 90% faster = 10x faster take your pick.\n The counts of faster vs slower and the corresponding sigma (the number of standard deviations of the binomial\n distribution for the null hypothesis P(faster) = P(slower) = 0.5) are also shown. The default sigma used is 6.0.\n\n### Matrix Multiply\n\n```csharp\n[Fact]\npublic void Faster_Matrix_Multiply_Range()\n{\n    var genDim = Gen.Int[5, 30];\n    var genArray = Gen.Double.Unit.Array2D;\n    Gen.SelectMany(genDim, genDim, genDim, (i, j, k) =\u003e Gen.Select(genArray[i, j], genArray[j, k]))\n    .Faster(\n        MulIKJ,\n        MulIJK\n    );\n}\n```\n\n### MapSlim Increment\n\n```csharp\n[Fact]\npublic void MapSlim_Performance_Increment()\n{\n    Gen.Byte.Array\n    .Select(a =\u003e (a, new MapSlim\u003cbyte, int\u003e(), new Dictionary\u003cint, int\u003e()))\n    .Faster(\n        (items, mapslim, _) =\u003e\n        {\n            foreach (var b in items)\n                mapslim.GetValueOrNullRef(b)++;\n        },\n        (items, _, dict) =\u003e\n        {\n            foreach (var b in items)\n            {\n                dict.TryGetValue(b, out int c);\n                dict[b] = c + 1;\n            }\n        },\n        repeat: 100,\n        writeLine: output.WriteLine);\n}\n```\n\n```\nTests.SlimCollectionsTests.MapSlim_Performance_Increment [27 s]\nStandard Output Messages:\n66.02%[56.48%..74.81%] 2.94x[2.30x..3.97x] faster, sigma=200.0 (72,690 vs 13,853)\n```\n\n### Benchmarks Game\n```csharp\n[Fact]\npublic void ReverseComplement_Faster()\n{\n    if (!File.Exists(Utils.Fasta.Filename)) Utils.Fasta.NotMain(new[] { \"25000000\" });\n\n    Check.Faster(\n        ReverseComplementNew.RevComp.NotMain,\n        ReverseComplementOld.RevComp.NotMain,\n        threads: 1, timeout: 600_000, sigma: 6\n        writeLine: output.WriteLine);\n}\n```\n\n```\nTests.ReverseComplementTests.ReverseComplement_Faster [27s 870ms]\nStandard Output Messages:\n25.15%[20.58%..31.60%] 1.34x[1.26x..1.46x] faster, sigma=6.0 (36 vs 0)\n```\n\n### Varint\nRepeat is used as the functions are very quick.\n```csharp\n[Fact]\npublic void Varint_Faster()\n{\n    Gen.Select(Gen.UInt, Gen.Const(() =\u003e new byte[8]))\n    .Faster(\n        (i, bytes) =\u003e\n        {\n            int pos = 0;\n            ArraySerializer.WriteVarint(bytes, ref pos, i);\n            pos = 0;\n            return ArraySerializer.ReadVarint(bytes, ref pos);\n        },\n        (i, bytes) =\u003e\n        {\n            int pos = 0;\n            ArraySerializer.WritePrefixVarint(bytes, ref pos, i);\n            pos = 0;\n            return ArraySerializer.ReadPrefixVarint(bytes, ref pos);\n        }, sigma: 10, repeat: 200, writeLine: output.WriteLine);\n}\n```\n\n```\nTests.ArraySerializerTests.Varint_Faster [45 ms]\nStandard Output Messages:\n10.94%[-3.27%..25.81%] 1.12x[0.97x..1.35x] faster, sigma=10.0 (442 vs 190)\n```\n\n## Debug utilities\n\nThe Dbg module is a set of utilities to collect, count and output debug info, time, classify generators, define and remotely call functions, and perform in code regression during testing.\nCsCheck can temporarily be added as a reference to run in non test code.\nNote this module is only for temporary debug use and the API may change between minor versions.\n\n\n### Count, Info, Set, Get, CallAdd, Call\n```csharp\npublic void Normal_Code(int z)\n{\n    Dbg.Count();\n    var d = Calc1(z).DbgSet(\"d\");\n    Dbg.Call(\"helpful\");\n    var c = Calc2(d).DbgInfo(\"c\");\n    Dbg.CallAdd(\"test cache\", () =\u003e\n    {\n        Dbg.Info(Dbg.Get(\"d\"));\n        Dbg.Info(cacheItems);\n    });\n}\n\n[Fact]\npublic void Test()\n{\n    Dbg.CallAdd(\"helpful\", () =\u003e\n    {\n        var d = (double)Dbg.Get(\"d\");\n        // ...\n        Dbg.Set(\"d\", d);\n    });\n    Normal_Code(z);\n    Dbg.Call(\"test cache\");\n    Dbg.Output(writeLine);\n}\n```\n\n### Regression\n```csharp\npublic double[] Calculation(InputData input)\n{\n    var part1 = CalcPart1(input);\n    // Add items to the regression on first pass, throw/break here if different on subsequent.\n    Dbg.Regression.Add(part1);\n    var part2 = CalcPart2(part1).DbgTee(Dbg.Regression.Add); // Tee can be used to do this inline.\n    // ...\n    return CalcFinal(partN).DbgTee(Dbg.Regression.Add);\n}\n\n[Fact]\npublic void Test()\n{\n    // Remove any previously saved regression data.\n    Dbg.Regression.Delete();\n\n    Calculation(InputSource1());\n\n    // End first pass save mode (only needed if second pass is in this process run).\n    Dbg.Regression.Close();\n\n    // Subsequent pass could be now or a code change and rerun (without the Delete).\n    Calculation(InputSource2());\n\n    // Check full number of items have been reconciled (optional).\n    Dbg.Regression.Close();\n}\n```\n\n### Time\n```csharp\n\npublic Result CalcPart2(InputData input)\n{\n    using var time = Dbg.Time();\n    // Calc\n    time.Line();\n    // Calc more\n    time.Line();\n    // ...\n    return result;\n}\n\n\npublic void LongProcess()\n{\n    using var time = Dbg.Time();\n    var part1 = CalcPart1(input);\n    time.Line();\n    var part2 = new List\u003cResult\u003e();\n    foreach(var item in part1)\n        part2.Add(CalcPart2(item));\n    time.Line();\n    // ...\n    return CalcFinal(partN);\n}\n\n[Fact]\npublic void Test()\n{\n    LongProcess();\n    Dbg.Output(writeLine);\n}\n```\n\n## Logging\n\nCsCheck now supports logging types and pass and fail results for analysis in Sample. We include a Tyche logging implementation.\n\n## Configuration\n\nCheck functions accept configuration optional parameters e.g. iter: 100_000, seed: \"0N0XIzNsQ0O2\", print: t =\u003e string.Join(\", \", t):\n\niter - The number of iterations to run in the sample (default 100).  \ntime - The number of seconds to run the sample.  \nseed - The seed to use for the first iteration.  \nthreads - The number of threads to run the sample on (default number logical CPUs).  \ntimeout - The timeout in seconds to use for Faster (default 60 seconds).  \nprint - A function to convert the state to a string for error reporting (default Check.Print).  \nequal - A function to check if the two states are the same (default Check.Equal).  \nsigma - For Faster sigma is the number of standard deviations from the null hypothesis (default 6).  \nreplay - The number of times to retry the seed to reproduce a SampleParallel fail (default 100).  \n\nGlobal defaults can also be set via environment variables:\n\n```powershell\ndotnet test -c Release -e CsCheck_Iter=10000 --filter Multithreading\n\ndotnet test -c Release -e CsCheck_Time=60 --filter Multithreading\n\ndotnet test -c Release -e CsCheck_Seed=0N0XIzNsQ0O2 --filter List\n\ndotnet test -c Release -e CsCheck_Sigma=50 -l 'console;verbosity=detailed' --filter Faster\n\ndotnet test -c Release -e CsCheck_Threads=1 -l 'console;verbosity=detailed' --filter Perf\n```\n\n## Development\n\nContributions are very welcome!\n\nCsCheck was designed to be easily extended. If you have created a cool `Gen` or extension, please consider a PR.\n\nApache 2 and free forever.","funding_links":[],"categories":["Testing","Performance tools","Identifiers"],"sub_categories":["Benchmarking","GUI - other"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAnthonyLloyd%2FCsCheck","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FAnthonyLloyd%2FCsCheck","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAnthonyLloyd%2FCsCheck/lists"}