From d8c8f951af71e7a406dc81e388ea391e6be51c7c Mon Sep 17 00:00:00 2001 From: Pierre Fenoll Date: Sun, 17 Jun 2018 21:16:21 +0200 Subject: [PATCH] introduce seed user option --- src/proper.erl | 4 ++++ test/proper_tests.erl | 48 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/src/proper.erl b/src/proper.erl index 3130e0f4..dc0bcbb0 100644 --- a/src/proper.erl +++ b/src/proper.erl @@ -239,6 +239,8 @@ %%%
This is equivalent to the {@link numtests/1} property wrapper. Any %%% {@link numtests/1} wrappers in the actual property will overwrite this %%% setting.
+%%%
`{seed, {,,}}'
+%%%
Pass a seed to the RNG so that random results can be reproduced.
%%%
`{start_size, }'
%%%
Specifies the initial value of the `size' parameter (default is 1), see %%% the documentation of the {@link proper_types} module for details.
@@ -489,6 +491,7 @@ | {'search_steps',pos_integer()} | {'search_strategy',proper_target:strategy()} | pos_integer() + | {'seed',proper_gen:seed()} | {'start_size',proper_gen:size()} | {'max_size',proper_gen:size()} | {'max_shrinks',non_neg_integer()} @@ -924,6 +927,7 @@ parse_opt(UserOpt, Opts) -> {search_steps, N} -> Opts#opts{search_steps = N}; {search_strategy, S} -> Opts#opts{search_strategy = S}; N when is_integer(N) -> Opts#opts{numtests = N}; + {seed,Seed} -> Opts#opts{seed = Seed}; {start_size,Size} -> Opts#opts{start_size = Size}; {max_size,Size} -> Opts#opts{max_size = Size}; {max_shrinks,N} -> Opts#opts{max_shrinks = N}; diff --git a/test/proper_tests.erl b/test/proper_tests.erl index be3ec1e1..9f5a6608 100644 --- a/test/proper_tests.erl +++ b/test/proper_tests.erl @@ -1119,6 +1119,54 @@ options_test_() -> ?FORALL(_,?SIZED(Size,integer(Size,Size)),false), [{start_size,12}])]. +seeded_test_() -> + Seed = os:timestamp(), + BaseOpts = [noshrink, {start_size,65536}, quiet], + Seeded = fun (Prop) -> + R = proper:counterexample(Prop, [{seed,Seed}|BaseOpts]), + proper:clean_garbage(), + R + end, + NoSeed = fun (Prop) -> + R = proper:counterexample(Prop, BaseOpts), + proper:clean_garbage(), + R + end, + ReSeeded = fun (Prop) -> + OtherSeed = os:timestamp(), + R = proper:counterexample(Prop, [{seed,OtherSeed}|BaseOpts]), + proper:clean_garbage(), + R + end, + [[?_assert(state_is_clean()), + ?_assertMatch({Name,{_,Equals}} when Equals > 6, {Name,equaltimes(Seeded,Prop,Check,10)}), + ?_assert(state_is_clean()), + ?_assertMatch({Name,{_,Equals}} when Equals < 4, {Name,equaltimes(NoSeed,Prop,Check,10)}), + ?_assert(state_is_clean()), + ?_assertMatch({Name,{_,Equals}} when Equals < 4, {Name,equaltimes(ReSeeded,Prop,Check,10)}), + ?_assert(state_is_clean())] + %% For each of these properties... + || {Name,Prop} <- [{forall,?FORALL(_, integer(), false)}, + {trapexit,?FORALL(_, integer(), ?TRAPEXIT(false))}, + {targeted,?FORALL_TARGETED(I, integer(), begin ?MAXIMIZE(I),false end)}], + %% Ensure that, using a large enough size and at least 60% of the time: + %% * provided a seed, another run gives the same counterexample; + %% * when not provided a seed: run gives out differing results to the seeded one; + %% * and similarly when given a different seed. + Check <- [Seeded(Prop)]]. + +equaltimes(Runner, Prop, Expected, Max) -> + equaltimes(Runner, Prop, Expected, Max, Max, []). +equaltimes(_, _, _, Max, 0, Unexpecteds) -> + {Unexpecteds, Max - length(Unexpecteds)}; +equaltimes(Runner, Prop, Expected, Max, N, Acc) -> + case Runner(Prop) of + Expected -> + equaltimes(Runner, Prop, Expected, Max, N-1, Acc); + Got -> + equaltimes(Runner, Prop, Expected, Max, N-1, [Got|Acc]) + end. + setup_prop() -> ?SETUP(fun () -> put(setup_token, true),