%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2004-2016. 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%
%%
%%
%%% Purpose : Test records.
-module(record_SUITE).
-include_lib("common_test/include/ct.hrl").
-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
init_per_testcase/2,end_per_testcase/2,
init_per_suite/1,end_per_suite/1,
errors/1,record_test/1,eval_once/1]).
suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap,{minutes,1}}].
all() ->
cases().
groups() ->
[].
init_per_group(_GroupName, Config) ->
Config.
end_per_group(_GroupName, Config) ->
Config.
cases() ->
[errors, record_test, eval_once].
init_per_testcase(_Case, Config) ->
test_lib:interpret(?MODULE),
Config.
end_per_testcase(_Case, _Config) ->
ok.
init_per_suite(Config) when is_list(Config) ->
test_lib:interpret(?MODULE),
true = lists:member(?MODULE, int:interpreted()),
Config.
end_per_suite(Config) when is_list(Config) ->
ok.
-record(foo, {a,b,c,d}).
-record(bar, {a,b,c,d}).
-record(barf, {a,b,c,d,e}).
errors(Config) when is_list(Config) ->
Foo = #foo{a=1,b=2,c=3,d=4},
#foo{a=19,b=42,c=3,d=4} = update_foo(Foo, 19, 42),
{'EXIT',{{badrecord,bar},_}} = (catch update_foo_bar(Foo, 19)),
{'EXIT',{{badrecord,bar},_}} = (catch update_foo_bar(Foo, 19, 35)),
{'EXIT',{{badrecord,bar},_}} = (catch update_foo_bar(Foo, 19, 35, 17)),
{'EXIT',{{badrecord,bar},_}} = (catch update_foo_bar(Foo, 19, 35, 17, 42)),
{'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19)),
{'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19, 35)),
{'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19, 35, 17)),
{'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19, 35, 17, 42)),
{'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19,
35, 17, 42, -2)),
ok.
update_foo(#foo{}=R, A, B) ->
R#foo{a=A,b=B}.
update_foo_bar(#foo{}=R, A) ->
R#bar{a=A}.
update_foo_bar(#foo{}=R, A, _B) ->
R#bar{a=A,b=A}.
update_foo_bar(#foo{}=R, A, _B, C) ->
R#bar{a=A,b=A,c=C}.
update_foo_bar(#foo{}=R, A, _B, C, D) ->
R#bar{a=A,b=A,c=C,d=D}.
update_foo_barf(#foo{}=R, A) ->
R#barf{a=A}.
update_foo_barf(#foo{}=R, A, _B) ->
R#barf{a=A,b=A}.
update_foo_barf(#foo{}=R, A, _B, C) ->
R#barf{a=A,b=A,c=C}.
update_foo_barf(#foo{}=R, A, _B, C, D) ->
R#barf{a=A,b=A,c=C,d=D}.
update_foo_barf(#foo{}=R, A, _B, C, D, E) ->
R#barf{a=A,b=A,c=C,d=D,e=E}.
-define(TrueGuard(Expr), if Expr -> ok; true -> ct:fail(failed) end).
-define(FalseGuard(Expr), if Expr -> ct:fail(failed); true -> ok end).
record_test(Config) when is_list(Config) ->
true = is_record(#foo{}, foo),
false = is_record(#foo{}, barf),
false = is_record({foo}, foo),
true = erlang:is_record(#foo{}, foo),
false = erlang:is_record(#foo{}, barf),
false = erlang:is_record({foo}, foo),
false = is_record([], foo),
false = is_record(Config, foo),
?TrueGuard(is_record(#foo{}, foo)),
?FalseGuard(is_record(#foo{}, barf)),
?FalseGuard(is_record({foo}, foo)),
?TrueGuard(erlang:is_record(#foo{}, foo)),
?FalseGuard(erlang:is_record(#foo{}, barf)),
?FalseGuard(erlang:is_record({foo}, foo)),
?FalseGuard(is_record([], foo)),
?FalseGuard(is_record(Config, foo)),
%% 'not is_record/2' to test guard optimization.
?FalseGuard(not is_record(#foo{}, foo)),
?TrueGuard(not is_record(#foo{}, barf)),
?TrueGuard(not is_record({foo}, foo)),
?FalseGuard(not erlang:is_record(#foo{}, foo)),
?TrueGuard(not erlang:is_record(#foo{}, barf)),
?TrueGuard(not erlang:is_record({foo}, foo)),
Foo = id(#foo{}),
?FalseGuard(not erlang:is_record(Foo, foo)),
?TrueGuard(not erlang:is_record(Foo, barf)),
?TrueGuard(not is_record(Config, foo)),
?TrueGuard(not is_record(a, foo)),
?TrueGuard(not is_record([], foo)),
%% Pass non-literal first argument.
true = is_record(id(#foo{}), foo),
false = is_record(id(#foo{}), barf),
false = is_record(id({foo}), foo),
true = erlang:is_record(id(#foo{}), foo),
false = erlang:is_record(id(#foo{}), barf),
false = erlang:is_record(id({foo}), foo),
NoRec1 = id(blurf),
NoRec2 = id([]),
?TrueGuard(not is_record(NoRec1, foo)),
?TrueGuard(not is_record(NoRec2, foo)),
%% Force the use of guard bifs by using the 'xor' operation.
False = id(false),
?TrueGuard(is_record(#foo{}, foo) xor False),
?FalseGuard(is_record(#foo{}, barf) xor False),
?FalseGuard(is_record({foo}, foo) xor False ),
?TrueGuard(is_record(Foo, foo) xor False),
?FalseGuard(is_record(Foo, barf) xor False),
%% Implicit guards by using a list comprehension.
List = id([1,#foo{a=2},3,#bar{d=4},5,#foo{a=6},7]),
[#foo{a=2},#foo{a=6}] = [X || X <- List, is_record(X, foo)],
[#bar{d=4}] = [X || X <- List, is_record(X, bar)],
[1,#foo{a=2},3,5,#foo{a=6},7] =
[X || X <- List, not is_record(X, bar)],
[1,3,5,7] =
[X || X <- List, ((not is_record(X, bar)) and (not is_record(X, foo)))],
[#foo{a=2},#bar{d=4},#foo{a=6}] =
[X || X <- List, ((is_record(X, bar)) or (is_record(X, foo)))],
[1,3,#bar{d=4}] =
[X || X <- List, ((is_record(X, bar)) or (X < 5))],
MyList = [#foo{a=3},x,[],{a,b}],
[#foo{a=3}] = [X || X <- MyList, is_record(X, foo)],
[x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo)],
[#foo{a=3}] = [X || X <- MyList, begin is_record(X, foo) end],
[x,[],{a,b}] = [X || X <- MyList, begin not is_record(X, foo) end],
[#foo{a=3},x,[],{a,b}] = [X || X <- MyList, is_record(X, foo) or
not is_binary(X)],
[#foo{a=3},x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo) or
not is_binary(X)],
[#foo{a=3}] = [X || X <- MyList, is_record(X, foo) or is_reference(X)],
[x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo) or
is_reference(X)],
[#foo{a=3},x,[],{a,b}] = [X || X <- MyList,
begin is_record(X, foo) or
not is_binary(X) end],
[#foo{a=3},x,[],{a,b}] = [X || X <- MyList,
begin not is_record(X, foo) or
not is_binary(X) end],
[#foo{a=3}] = [X || X <- MyList,
begin is_record(X, foo) or is_reference(X) end],
[x,[],{a,b}] = [X || X <- MyList,
begin not is_record(X, foo) or
is_reference(X) end],
ok.
eval_once(Config) when is_list(Config) ->
once(fun(GetRec) ->
true = erlang:is_record(GetRec(), foo)
end, #foo{}),
once(fun(GetRec) ->
(GetRec())#foo{a=1}
end, #foo{}),
once(fun(GetRec) ->
(GetRec())#foo{a=1,b=2}
end, #foo{}),
once(fun(GetRec) ->
(GetRec())#foo{a=1,b=2,c=3}
end, #foo{}),
once(fun(GetRec) ->
(GetRec())#foo{a=1,b=2,c=3,d=4}
end, #foo{}),
ok.
once(Test, Record) ->
put(?MODULE, 0),
GetRec = fun() ->
put(?MODULE, 1+get(?MODULE)),
Record
end,
Result = Test(GetRec),
case get(?MODULE) of
1 -> ok;
N ->
io:format("Evaluated ~w times\n", [N]),
ct:fail(failed)
end,
Result.
id(I) -> I.