%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%
-module(beam_types_prop).
-compile([export_all, nowarn_export_all]).
%% This module has only supports proper, as we don't have an eqc license to
%% test with.
-proptest([proper]).
-ifdef(PROPER).
-include_lib("compiler/src/beam_types.hrl").
-include_lib("proper/include/proper.hrl").
-define(MOD_eqc,proper).
consistency() ->
?FORALL({TypeA, TypeB},
?LET(TypeA, type(),
?LET(TypeB, type(), {TypeA, TypeB})),
consistency_check(TypeA, TypeB)).
consistency_check(A, B) ->
consistency_check_1(A, B),
consistency_check_1(B, A),
true.
consistency_check_1(A, B) ->
A = beam_types:meet(A, beam_types:join(A, B)),
A = beam_types:join(A, beam_types:meet(A, B)),
ok.
commutativity() ->
?FORALL({TypeA, TypeB},
?LET(TypeA, type(),
?LET(TypeB, type(), {TypeA, TypeB})),
commutativity_check(TypeA, TypeB)).
commutativity_check(A, B) ->
true = beam_types:meet(A, B) =:= beam_types:meet(B, A),
true = beam_types:join(A, B) =:= beam_types:join(B, A),
true.
%%%
%%% Generators
%%%
type() ->
type(0).
type(Depth) ->
oneof(nested_types(Depth) ++
numerical_types() ++
list_types() ++
other_types()).
other_types() ->
[any, gen_atom(), gen_binary(), none].
list_types() ->
[cons, list, nil].
numerical_types() ->
[gen_integer(), float, number].
nested_types(Depth) when Depth >= 3 -> [];
nested_types(Depth) -> [#t_map{}, gen_tuple(Depth + 1)].
gen_atom() ->
?LET(Size, range(0, ?ATOM_SET_SIZE),
case Size of
0 ->
#t_atom{};
_ ->
?LET(Set, sized_list(Size, atom()),
begin
#t_atom{elements=ordsets:from_list(Set)}
end)
end).
gen_binary() ->
?SHRINK(#t_bitstring{unit=range(1, 128)},
[#t_bitstring{unit=[1, 2, 3, 5, 7, 8, 16, 32, 64]}]).
gen_integer() ->
oneof([gen_integer_bounded(), #t_integer{}]).
gen_integer_bounded() ->
?LET(A, integer(),
?LET(B, integer(),
begin
#t_integer{elements={min(A,B), max(A,B)}}
end)).
gen_tuple(Depth) ->
?SIZED(Size,
?LET(Exact, oneof([true, false]),
?LET(Elements, gen_tuple_elements(Size, Depth),
begin
#t_tuple{exact=Exact,
size=Size,
elements=Elements}
end))).
gen_tuple_elements(Size, Depth) ->
?LET(Types, sized_list(rand:uniform(Size div 4 + 1), gen_element(Depth)),
maps:from_list([{rand:uniform(Size), T} || T <- Types])).
gen_element(Depth) ->
?LAZY(?SUCHTHAT(Type, type(Depth),
case Type of
any -> false;
none -> false;
_ -> true
end)).
sized_list(0, _Gen) -> [];
sized_list(N, Gen) -> [Gen | sized_list(N - 1, Gen)].
-endif.