Skip to content

Commit c1e9376

Browse files
authored
Merge pull request #7364 from Maria-12648430/proptest_safe_proper
Extend `common_test` PropEr with atomlimit-safe generator variants
2 parents 98f993f + c39caa3 commit c1e9376

File tree

4 files changed

+275
-13
lines changed

4 files changed

+275
-13
lines changed

lib/common_test/Makefile

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
2525
# Macros
2626
#
2727

28-
ifeq ($(findstring linux,$(TARGET)),linux)
29-
SUB_DIRECTORIES = doc/src src priv
30-
else
31-
ifeq ($(findstring solaris,$(TARGET)),solaris)
32-
SUB_DIRECTORIES = doc/src src priv
33-
else
34-
SUB_DIRECTORIES = doc/src src priv
35-
endif
36-
endif
28+
SUB_DIRECTORIES = doc/src src priv proper_ext
3729

3830
include vsn.mk
3931
VSN = $(COMMON_TEST_VSN)

lib/common_test/proper_ext/Makefile

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#
2+
# %CopyrightBegin%
3+
#
4+
# Copyright Ericsson AB 2023. All Rights Reserved.
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#
18+
# %CopyrightEnd%
19+
#
20+
21+
include $(ERL_TOP)/make/target.mk
22+
23+
# ----------------------------------------------------
24+
# Configuration info.
25+
# ----------------------------------------------------
26+
include $(ERL_TOP)/make/$(TARGET)/otp.mk
27+
28+
# ----------------------------------------------------
29+
# Release directory specification
30+
# ----------------------------------------------------
31+
PROPEREXTDIR = $(RELEASE_PATH)/lib/common_test-$(VSN)/proper_ext
32+
33+
# ----------------------------------------------------
34+
# Target Specs
35+
# ----------------------------------------------------
36+
37+
EBIN=.
38+
39+
MODULES= \
40+
ct_proper_ext
41+
42+
TARGET_MODULES= $(MODULES:%=$(EBIN)/%)
43+
TARGET_MODULES= $(MODULES:%=$(EBIN)/%)
44+
45+
ERL_FILES = $(MODULES:=.erl)
46+
HRL_FILES =
47+
48+
TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR))
49+
50+
TARGETS = $(MODULES:%=$(EBIN)/%.$(EMULATOR))
51+
52+
# ----------------------------------------------------
53+
# FLAGS
54+
# ----------------------------------------------------
55+
ERL_COMPILE_FLAGS += -I../include -Werror
56+
57+
# ----------------------------------------------------
58+
# Targets
59+
# ----------------------------------------------------
60+
61+
tests $(TYPES): $(TARGETS)
62+
63+
clean:
64+
rm -f $(TARGET_FILES)
65+
rm -f core
66+
67+
docs:
68+
69+
# ----------------------------------------------------
70+
# Special Build Targets
71+
# ----------------------------------------------------
72+
73+
# ----------------------------------------------------
74+
# Release Target
75+
# ----------------------------------------------------
76+
include $(ERL_TOP)/make/otp_release_targets.mk
77+
78+
release_spec: opt
79+
$(INSTALL_DIR) "$(PROPEREXTDIR)"
80+
$(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) \
81+
$(TARGET_FILES) \
82+
"$(PROPEREXTDIR)"
83+
84+
release_docs_spec:
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
%%
2+
%% %CopyrightBegin%
3+
%%
4+
%% Copyright Ericsson AB 2023. All Rights Reserved.
5+
%%
6+
%% Licensed under the Apache License, Version 2.0 (the "License");
7+
%% you may not use this file except in compliance with the License.
8+
%% You may obtain a copy of the License at
9+
%%
10+
%% http://www.apache.org/licenses/LICENSE-2.0
11+
%%
12+
%% Unless required by applicable law or agreed to in writing, software
13+
%% distributed under the License is distributed on an "AS IS" BASIS,
14+
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
%% See the License for the specific language governing permissions and
16+
%% limitations under the License.
17+
%%
18+
%% %CopyrightEnd%
19+
%%
20+
21+
%% For internal use only.
22+
%%
23+
%% Some generators of the PropEr framework used by OTP for property tests
24+
%% create atoms at random, ie from random strings, and are therefore likely
25+
%% to exhaust the atom table.
26+
%%
27+
%% This module provides additional variants of these generators which do
28+
%% not create new atoms but pick from the already existing atoms.
29+
%%
30+
%% Other than in PropEr, the respective atom generators provided by this module
31+
%% do not shrink.
32+
33+
-module(ct_proper_ext).
34+
35+
-export([existing_atom/0]).
36+
-export([safe_any/0]).
37+
-export([safe_atom/0]).
38+
-export([safe_list/0]).
39+
-export([safe_map/0]).
40+
-export([safe_term/0]).
41+
-export([safe_tuple/0]).
42+
43+
%% Atomlimit-safe variant of `proper_types:list()'
44+
-spec safe_list() -> proper_types:type().
45+
safe_list() ->
46+
proper_types:list(safe_any()).
47+
48+
49+
%% Atomlimit-safe variant of `proper_types:map()'
50+
-spec safe_map() -> proper_types:type().
51+
safe_map() ->
52+
proper_types:map(safe_any(), safe_any()).
53+
54+
55+
%% Atomlimit-safe variant of `proper_types:tuple()'
56+
-spec safe_tuple() -> proper_types:type().
57+
safe_tuple() ->
58+
proper_types:loose_tuple(safe_any()).
59+
60+
61+
%% Atomlimit-safe variant of `proper_types:atom()'.
62+
-spec existing_atom() -> proper_types:type().
63+
existing_atom() ->
64+
proper_types:noshrink(
65+
proper_types:lazy(fun() ->
66+
N = erlang:system_info(atom_count),
67+
get_existing_atom(rand_int0(N - 1), N)
68+
end)).
69+
70+
-define(ATOM_TERM_BIN(Index), <<131, 75, Index:24>>).
71+
get_existing_atom(Index, Max) ->
72+
Index1 = Index rem Max,
73+
case binary_to_term(?ATOM_TERM_BIN(Index1)) of
74+
'' ->
75+
'';
76+
Atom ->
77+
case hd(atom_to_list(Atom)) of
78+
$$ -> get_existing_atom(Index1 + 1, Max);
79+
_ -> Atom
80+
end
81+
end.
82+
83+
84+
%% Atomlimit-safe variant of `proper_types:atom()'.
85+
%% Like `existing_atom()', but also emphasizes some common atoms
86+
%% like `undefined', `false', `ok' etc
87+
-spec safe_atom() -> proper_types:type().
88+
safe_atom() ->
89+
proper_types:oneof([proper_types:oneof(['', true, false, ok,
90+
error, undefined,
91+
infinity, 'ätöm',
92+
'原子', '_', '"',
93+
'\'', '\\', '+', '-',
94+
'*', '/', '(', ')',
95+
'[', ']', '{', '}',
96+
'#' | erlang:nodes(known)]),
97+
existing_atom()]).
98+
99+
100+
%% Atomlimit-safe variant of `proper_types:term()'.
101+
%% Alias for `safe_any/0'.
102+
-spec safe_term() -> proper_types:type().
103+
safe_term() ->
104+
safe_any().
105+
106+
107+
%% Atomlimit-safe variant of `proper_types:any()'.
108+
-spec safe_any() -> proper_types:type().
109+
safe_any() ->
110+
proper_types:sized(fun(Size) -> safe_any(Size) end).
111+
112+
safe_any(0) ->
113+
proper_types:oneof([safe_atom(),
114+
proper_types:integer(),
115+
proper_types:float()]);
116+
safe_any(Size) ->
117+
case pick_type(Size) of
118+
simple ->
119+
safe_any(0);
120+
binary ->
121+
proper_types:resize(Size, proper_types:bitstring());
122+
{list, 0} ->
123+
[];
124+
{list, 1} ->
125+
[proper_types:lazy(fun() -> safe_any(Size - 1) end)];
126+
{list, NumEls} ->
127+
ElSizes = distribute(Size - 1, NumEls),
128+
proper_types:fixed_list([proper_types:lazy(fun() ->
129+
safe_any(S)
130+
end)
131+
|| S <- ElSizes]);
132+
{tuple, 0} ->
133+
{};
134+
{tuple, 1} ->
135+
{proper_types:lazy(fun() -> safe_any(Size - 1) end)};
136+
{tuple, NumEls} ->
137+
ElSizes = distribute(Size - 1, NumEls),
138+
proper_types:tuple([proper_types:lazy(fun() ->
139+
safe_any(S) end)
140+
|| S <- ElSizes])
141+
end.
142+
143+
%% Randomly picks a type with the following distribution (same as in PropEr):
144+
%% * 25% tuples
145+
%% * 25% lists
146+
%% * 12.5% bitstrings
147+
%% * 37.5% simple types
148+
pick_type(Size) ->
149+
case rand:uniform(1000) of
150+
X when X =< 250 ->
151+
{tuple, rand_int0(Size)};
152+
X when X =< 500 ->
153+
{list, rand_int0(Size)};
154+
X when X =< 625 ->
155+
binary;
156+
_ ->
157+
simple
158+
end.
159+
160+
%% Randomly distributes the given number of `Credits' over the given
161+
%% number of `Slots'
162+
distribute(Slots, Credits) ->
163+
[X || {_, X} <- lists:sort(distribute_1(Slots, Credits))].
164+
165+
distribute_1(0, 0) ->
166+
[];
167+
distribute_1(1, Credits) ->
168+
[{rand:uniform(1000), Credits}];
169+
distribute_1(Slots, 0) ->
170+
[{rand:uniform(1000), 0} || _ <- lists:seq(1, Slots)];
171+
distribute_1(Slots, Credits) ->
172+
N = rand_int0(Credits),
173+
[{rand:uniform(1000), N}|distribute_1(Slots - 1, Credits - N)].
174+
175+
176+
%% Random non-neg integer
177+
rand_int0(Max) ->
178+
rand:uniform(Max + 1) - 1.

lib/common_test/src/ct_property_test.erl

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,20 +69,28 @@ init_tool(Config) ->
6969
{ok,ToolModule} ->
7070
case code:where_is_file(lists:concat([ToolModule,".beam"])) of
7171
non_existing ->
72-
ct:log("Found ~p, but ~tp~n is not found",
72+
ct:log("Found ~p, but ~ts was not found",
7373
[ToolModule, lists:concat([ToolModule,".beam"])]),
7474
{skip, "Strange Property testing tool installation"};
7575
ToolPath ->
76-
ct:pal("Found property tester ~p~n"
77-
"at ~tp",
76+
ct:pal("Found property tester ~p at ~ts",
7877
[ToolModule, ToolPath]),
78+
init_tool_extensions(ToolModule),
7979
[{property_test_tool, ToolModule} | Config]
8080
end;
8181
not_found ->
8282
ct:pal("No property tester found",[]),
8383
{skip, "No property testing tool found"}
8484
end.
85-
85+
86+
init_tool_extensions(proper) ->
87+
ProperExtDir = code:lib_dir(common_test, proper_ext),
88+
true = code:add_patha(ProperExtDir),
89+
ct:pal("Added ~ts to code path~n", [ProperExtDir]),
90+
ok;
91+
init_tool_extensions(_) ->
92+
ok.
93+
8694
%%%----------------------------------------------------------------
8795
%%%
8896
%%% Call the found property tester (if any)

0 commit comments

Comments
 (0)