diff --git a/src/proper.erl b/src/proper.erl
index 06ab14b9..0c3162c9 100644
--- a/src/proper.erl
+++ b/src/proper.erl
@@ -297,6 +297,9 @@
%%%
`{stop_nodes, boolean()}'
%%% Specifies whether parallel PropEr should stop the nodes after running a property
%%% or not. Defaults to true.
+%%% `{atom_limit, infinity | }'
+%%% The overall number of atoms generated by {@link proper_types:atom/0} will not
+%%% exceed the given number, per size. Default is infinity.
%%%
%%%
%%% == Spec testing ==
@@ -539,6 +542,7 @@
| 'quiet'
| 'verbose'
| pos_integer()
+ | {'atom_limit', 'infinity' | pos_integer()}
| {'constraint_tries',pos_integer()}
| {'false_positive_mfas',false_positive_mfas()}
| {'max_shrinks',non_neg_integer()}
@@ -568,6 +572,7 @@
noshrink = false :: boolean(),
constraint_tries = 50 :: pos_integer(),
expect_fail = false :: boolean(),
+ atom_limit = infinity :: pos_integer() | 'infinity',
any_type :: {'type', proper_types:type()} | 'undefined',
spec_timeout = infinity :: timeout(),
skip_mfas = [] :: [mfa()],
@@ -744,7 +749,8 @@ global_state_init_size_seed(Size, Seed) ->
-spec global_state_init(opts()) -> 'ok'.
global_state_init(#opts{start_size = StartSize, constraint_tries = CTries,
search_strategy = Strategy, search_steps = SearchSteps,
- any_type = AnyType, seed = Seed, numworkers = NumWorkers} = Opts) ->
+ any_type = AnyType, seed = Seed, numworkers = NumWorkers,
+ atom_limit = AtomLimit} = Opts) ->
clean_garbage(),
put('$size', StartSize - 1),
put('$left', 0),
@@ -753,6 +759,7 @@ global_state_init(#opts{start_size = StartSize, constraint_tries = CTries,
grow_size(Opts),
put('$constraint_tries', CTries),
put('$any_type', AnyType),
+ put('$atom_limit', AtomLimit),
put('$property_id', erlang:unique_integer()),
put('$proper_test_incr', NumWorkers),
{_, _, _} = Seed, % just an assertion
@@ -773,6 +780,7 @@ global_state_erase() ->
proper_typeserver:stop(),
proper_arith:rand_stop(),
erase('$any_type'),
+ erase('$atom_limit'),
erase('$constraint_tries'),
erase('$left'),
erase('$size'),
diff --git a/src/proper_gen.erl b/src/proper_gen.erl
index 6f603917..a10e2769 100644
--- a/src/proper_gen.erl
+++ b/src/proper_gen.erl
@@ -406,12 +406,46 @@ float_gen(_Size, Low, High) ->
%% We make sure we never clash with internal atoms by checking that the first
%% character is not '$'.
atom_gen(Size) ->
+ atom_gen(get('$atom_limit'), Size).
+
+atom_gen(infinity, Size) ->
+ atom_gen1(Size);
+atom_gen(Limit, Size) ->
+ ?LAZY(
+ ?LET(Atom,
+ try persistent_term:get({'$proper', ?MODULE, ?FUNCTION_NAME, Size}) of
+ {N, Atoms} when N =< Limit ->
+ proper_types:oneof(Atoms);
+ {N, CurAtoms} ->
+ ?LET(MoreAtoms,
+ proper_types:vector(Limit-N, atom_gen1(Size)),
+ begin
+ Atoms = CurAtoms ++ MoreAtoms,
+ persisten_term:put({'$proper', ?MODULE, ?FUNCTION_NAME, Size},
+ {Limit, Atoms}),
+ proper_types:oneof(Atoms)
+ end
+ )
+ catch
+ error:badarg ->
+ ?LET(Atoms,
+ proper_types:vector(Limit, atom_gen1(Size)),
+ begin
+ persistent_term:put({'$proper', ?MODULE, ?FUNCTION_NAME, Size},
+ {Limit, Atoms}),
+ proper_types:oneof(Atoms)
+ end
+ )
+ end,
+ Atom)).
+
+atom_gen1(Size) ->
?LET(Str,
- ?SUCHTHAT(X,
- proper_types:resize(Size,
- proper_types:list(proper_types:byte())),
- X =:= [] orelse hd(X) =/= $$),
- list_to_atom(Str)).
+ ?SUCHTHAT(X,
+ proper_types:resize(Size,
+ proper_types:list(proper_types:byte())),
+ X=:=[] orelse hd(X)=/=$$),
+ list_to_atom(Str)).
%% @private
-spec atom_rev(atom()) -> imm_instance().