CsCheck/Check.cs
/// <summary>Sample the gen calling the assert each time across multiple threads. Shrink any exceptions if necessary.</summary>
/// <param name="gen">The sample input data generator.</param>
/// <param name="assert">The code to call with the input data raising an exception if it fails.</param>
/// <param name="seed">The initial seed to use for the first iteration.</param>
/// <param name="iter">The number of iterations to run in the sample (default 100).</param>
/// <param name="time">The number of seconds to run the sample.</param>
/// <param name="threads">The number of threads to run the sample on (default number logical CPUs).</param>
/// <param name="print">A function to convert the input data to a string for error reporting (default Check.Print).</param>
Sample the gen calling the assert each time across multiple threads. Shrink any exceptions if necessary.
Sample the gen calling the assert each time across multiple threads . Shrink any exceptions if necessary .
public static void Sample<T>(this Gen<T> gen, Action<T> assert,
            string seed = null, long iter = -1, int time = -1, int threads = -1, Func<T, string> print = null)
        {
            if (seed is null) seed = Seed;
            if (iter == -1) iter = Iter;
            if (time == -1) time = Time;
            if (threads == -1) threads = Threads;
            if (print is null) print = Print;
            PCG minPCG = null;
            ulong minState = 0UL;
            Size minSize = null;
            T minT = default;
            Exception minException = null;
            int shrinks = -1;
            if (seed is not null)
            {
                var pcg = PCG.Parse(seed);
                ulong state = pcg.State;
                Size s = null;
                T t = default;
                try
                {
                    assert(t = gen.Generate(pcg, null, out s));
                }
                catch (Exception e)
                {
                    shrinks++;
                    minPCG = pcg;
                    minState = state;
                    minSize = s;
                    minT = t;
                    minException = e;
                }
            }
            int skipped = 0;
            bool isIter = time < 0;
            long target = isIter ? seed is null ? iter : iter - 1
                        : Stopwatch.GetTimestamp() + time * Stopwatch.Frequency;
            long total = seed is null ? 0 : 1;
            var cde = new CountdownEvent(threads);
            while (threads-- > 0)
                ThreadPool.UnsafeQueueUserWorkItem(_ =>
                {
                    var pcg = PCG.ThreadPCG;
                    Size s = null;
                    T t = default;
                    while ((isIter ? Interlocked.Decrement(ref target) : target - Stopwatch.GetTimestamp()) >= 0)
                    {
                        ulong state = pcg.State;
                        try
                        {
                            t = gen.Generate(pcg, minSize, out s);
                            if (Size.IsLessThan(s, minSize))
                                assert(t);
                            else
                                skipped++;
                        }
                        catch (Exception e)
                        {
                            lock (cde)
                            {
                                if (Size.IsLessThan(s, minSize))
                                {
                                    shrinks++;
                                    minPCG = pcg;
                                    minState = state;
                                    minSize = s;
                                    minT = t;
                                    minException = e;
                                }
                            }
                        }
                        Interlocked.Increment(ref total);
                    }
                    cde.Signal();
                }, null);
            cde.Wait();
            if (minPCG is not null)
            {
                var seedString = minPCG.ToString(minState);
                var tString = print(minT);
                if (tString.Length > MAX_LENGTH) tString = tString.Substring(0, MAX_LENGTH) + " ...";
                var summary = $"Set seed: \"{seedString}\" or $env:CsCheck_Seed = \"{seedString}\" to reproduce ({shrinks:#,0} shrinks, {skipped:#,0} skipped, {total:#,0} total).\n";
                throw new CsCheckException(summary + tString, minException);
            }
        }
public static void Sample < T > ( this Gen < T > gen , Action < T > assert , string seed = null , long iter = - 1 , int time = - 1 , int threads = - 1 , Func < T , string > print = null ) { if ( seed is null ) seed = Seed ; if ( iter == - 1 ) iter = Iter ; if ( time == - 1 ) time = Time ; if ( threads == - 1 ) threads = Threads ; if ( print is null ) print = Print ; PCG minPCG = null ; ulong minState = 0UL ; Size minSize = null ; T minT = default ; Exception minException = null ; int shrinks = - 1 ; if ( seed is not null ) { var pcg = PCG . Parse ( seed ) ; ulong state = pcg . State ; Size s = null ; T t = default ; try { assert ( t = gen . Generate ( pcg , null , out s ) ) ; } catch ( Exception e ) { shrinks ++ ; minPCG = pcg ; minState = state ; minSize = s ; minT = t ; minException = e ; } } int skipped = 0 ; bool isIter = time < 0 ; long target = isIter ? seed is null ? iter : iter - 1 : Stopwatch . GetTimestamp ( ) + time * Stopwatch . Frequency ; long total = seed is null ? 0 : 1 ; var cde = new CountdownEvent ( threads ) ; while ( threads -- > 0 ) ThreadPool . UnsafeQueueUserWorkItem ( _ => { var pcg = PCG . ThreadPCG ; Size s = null ; T t = default ; while ( ( isIter ? Interlocked . Decrement ( ref target ) : target - Stopwatch . GetTimestamp ( ) ) >= 0 ) { ulong state = pcg . State ; try { t = gen . Generate ( pcg , minSize , out s ) ; if ( Size . IsLessThan ( s , minSize ) ) assert ( t ) ; else skipped ++ ; } catch ( Exception e ) { lock ( cde ) { if ( Size . IsLessThan ( s , minSize ) ) { shrinks ++ ; minPCG = pcg ; minState = state ; minSize = s ; minT = t ; minException = e ; } } } Interlocked . Increment ( ref total ) ; } cde . Signal ( ) ; } , null ) ; cde . Wait ( ) ; if ( minPCG is not null ) { var seedString = minPCG . ToString ( minState ) ; var tString = print ( minT ) ; if ( tString . Length > MAX_LENGTH ) tString = tString . Substring ( 0 , MAX_LENGTH ) + " " ; var summary = $" Set seed:  \" { seedString } \"  or $env:CsCheck_Seed =  \" { seedString } \"  to reproduce ( { shrinks : }  shrinks,  { skipped : }  skipped,  { total : }  total). \n " ; throw new CsCheckException ( summary + tString , minException ) ; } }
Sample the gen calling the assert each time across multiple threads.
Sample the gen calling the assert each time across multiple threads .
Gen<T>
this
assert
Action<T>
The code to call with the input data raising an exception if it fails.
The code to call with the input data raising an exception if it fails .
seed
string
The initial seed to use for the first iteration.
The initial seed to use for the first iteration .
iter
long
The number of iterations to run in the sample (default 100).
The number of iterations to run in the sample ( default 100 ) .
time
int
The number of seconds to run the sample.
The number of seconds to run the sample .
threads
int
The number of threads to run the sample on (default number logical CPUs).
The number of threads to run the sample on ( default number logical CPUs ) .
print
Func<T, string>
A function to convert the input data to a string for error reporting (default Check.Print).
A function to convert the input data to a string for error reporting ( default Check . Print ) .
gen
The sample input data generator.
The sample input data generator .