1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1997-2020. 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%%
22%%% Author: Hakan Mattsson  hakan@erix.ericsson.se
23%%% Purpose: Nice shortcuts intended for testing of Mnesia
24%%%
25%%% See the mnesia_SUITE module about the structure of
26%%% the test suite.
27%%%
28%%% See the mnesia_test_lib module about the test case execution.
29%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
30
31-module(mt).
32-author('hakan@erix.ericsson.se').
33-export([
34	 t/0, t/1, t/2, t/3,                % Run test cases
35	 loop/1, loop/2, loop/3,            % loop test cases
36	 doc/0, doc/1,                      % Generate test case doc
37	 struct/0, struct/1,                % View test suite struct
38	 shutdown/0, ping/0, start_nodes/0, % Node admin
39	 read_config/0, write_config/1      % Config admin
40	]).
41
42-include("mnesia_test_lib.hrl").
43
44%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
45%% Aliases for the (sub) test suites
46alias(all) -> mnesia_SUITE;
47alias(atomicity) -> mnesia_atomicity_test;
48alias(backup) -> mnesia_evil_backup;
49alias(config) -> mnesia_config_test;
50alias(consistency) -> mnesia_consistency_test;
51alias(dirty) -> mnesia_dirty_access_test;
52alias(durability) -> mnesia_durability_test;
53alias(evil) -> mnesia_evil_coverage_test;
54alias(qlc) -> mnesia_qlc_test;
55alias(examples) -> mnesia_examples_test;
56alias(frag) -> mnesia_frag_test;
57alias(heavy) -> {mnesia_SUITE, heavy};
58alias(install) -> mnesia_install_test;
59alias(isolation) -> mnesia_isolation_test;
60alias(light) -> {mnesia_SUITE, light};
61alias(majority) -> mnesia_majority_test;
62alias(measure) -> mnesia_measure_test;
63alias(medium) -> {mnesia_SUITE, medium};
64alias(nice) -> mnesia_nice_coverage_test;
65alias(recover) -> mnesia_recover_test;
66alias(recovery) -> mnesia_recovery_test;
67alias(registry) -> mnesia_registry_test;
68alias(suite) -> mnesia_SUITE;
69alias(trans) -> mnesia_trans_access_test;
70alias(ixp) -> mnesia_index_plugin_test;
71alias(Other) -> Other.
72
73%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
74%% Resolves the name of test suites and test cases
75%% according to the alias definitions. Single atoms
76%% are assumed to be the name of a test suite.
77resolve(Suite0) when is_atom(Suite0) ->
78    case alias(Suite0) of
79	Suite when is_atom(Suite) ->
80	    {Suite, all};
81	{Suite, Case} ->
82	    {Suite, is_group(Suite,Case)}
83    end;
84resolve({Suite0, {group, Case}}) ->
85    resolve({Suite0, Case});
86resolve({Suite0, Case}) when is_atom(Suite0), is_atom(Case) ->
87    case alias(Suite0) of
88	Suite when is_atom(Suite) ->
89	    {Suite, is_group(Suite,Case)};
90	{Suite, Case2} ->
91	    {Suite, is_group(Suite,Case2)}
92    end;
93resolve(List) when is_list(List) ->
94    [resolve(Case) || Case <- List].
95
96is_group(Mod, Case) ->
97    try {_,_,_} = lists:keyfind(Case, 1, Mod:groups()),
98	 {group, Case}
99    catch _:{badmatch,_} ->
100	    Case
101    end.
102
103%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
104%% Run one or more test cases
105
106%% Run the default test case with default config
107t() ->
108    t(read_test_case()).
109
110%% Resolve the test case name and run the test case
111%% The test case is noted as default test case
112%% and the outcome of the tests are written to
113%% to a file.
114t(silly) ->
115    mnesia_install_test:silly();
116t(diskless) ->
117    %% Run the default test case with default config,
118    %% but diskless
119    t(read_test_case(), diskless);
120t(Case) ->
121    %% Use the default config
122    t(Case, read_config()).
123
124t(Case, Config) when Config == diskless ->
125    %% Run the test case with default config, but diskless
126    Config2 = [{diskless, true} | read_config()],
127    t(Case, Config2);
128t(Mod, Fun) when is_atom(Mod), is_atom(Fun) ->
129    %% Run the test case with default config
130    t({Mod, Fun}, read_config());
131t(RawCase, Config) when is_list(Config) ->
132    %% Resolve the test case name and run the test case
133    Case = resolve(RawCase),
134    write_test_case(Case),
135    Res = mnesia_test_lib:test(Case, Config),
136    append_test_case_info(Case, Res).
137
138t(Mod, Fun, Config) when Config == diskless ->
139    t({Mod, Fun}, diskless).
140
141config_fname() ->
142    "mnesia_test_case_config".
143
144%% Read default config file
145read_config() ->
146    Fname = config_fname(),
147    mnesia_test_lib:log("Consulting file ~s...~n", [Fname]),
148    case file:consult(Fname) of
149        {ok, Config} ->
150	    mnesia_test_lib:log("Read config ~w~n", [Config]),
151            Config;
152        _Error ->
153	    Config = mnesia_test_lib:default_config(),
154            mnesia_test_lib:log("<>WARNING<> Using default config: ~w~n", [Config]),
155            Config
156    end.
157
158%% Write new default config file
159write_config(Config) when is_list(Config) ->
160    Fname = config_fname(),
161    {ok, Fd} = file:open(Fname, write),
162    write_list(Fd, Config),
163    file:close(Fd).
164
165write_list(Fd, [H | T]) ->
166    ok = io:format(Fd, "~p.~n",[H]),
167    write_list(Fd, T);
168write_list(_, []) ->
169    ok.
170
171test_case_fname() ->
172    "mnesia_test_case_info".
173
174%% Read name of test case
175read_test_case() ->
176    Fname = test_case_fname(),
177    case file:open(Fname, [read]) of
178	{ok, Fd} ->
179	    Res = io:read(Fd, []),
180	    file:close(Fd),
181	    case Res of
182		{ok, TestCase} ->
183		    mnesia_test_lib:log("Using test case ~w from file ~s~n",
184					[TestCase, Fname]),
185		    TestCase;
186		{error, _} ->
187		    default_test_case(Fname)
188	    end;
189	{error, _} ->
190	    default_test_case(Fname)
191    end.
192
193default_test_case(Fname) ->
194    TestCase = all,
195    mnesia_test_lib:log("<>WARNING<> Cannot read file ~s, "
196			"using default test case: ~w~n",
197			[Fname, TestCase]),
198    TestCase.
199
200write_test_case(TestCase) ->
201    Fname = test_case_fname(),
202    {ok, Fd} = file:open(Fname, write),
203    ok = io:format(Fd, "~p.~n",[TestCase]),
204    file:close(Fd).
205
206append_test_case_info(TestCase, TestCaseInfo) ->
207    Fname = test_case_fname(),
208    {ok, Fd} = file:open(Fname, [read, write]),
209    ok = io:format(Fd, "~p.~n",[TestCase]),
210    ok = io:format(Fd, "~p.~n",[TestCaseInfo]),
211    file:close(Fd),
212    TestCaseInfo.
213
214%% Generate HTML pages from the test case structure
215doc() ->
216    doc(suite).
217
218doc(Case) ->
219    mnesia_test_lib:doc(resolve(Case)).
220
221%% Display out the test case structure
222struct() ->
223    struct(suite).
224
225struct(Case) ->
226    mnesia_test_lib:struct([resolve(Case)]).
227
228%% Shutdown all nodes with erlang:halt/0
229shutdown() ->
230    mnesia_test_lib:shutdown().
231
232%% Ping all nodes in config spec
233ping() ->
234    Config = read_config(),
235    Nodes = mnesia_test_lib:select_nodes(all, Config, ?FILE, ?LINE),
236    [{N, net_adm:ping(N)} || N <- Nodes].
237
238%% Slave start all nodes in config spec
239start_nodes() ->
240    Config = read_config(),
241    Nodes = mnesia_test_lib:select_nodes(all, Config, ?FILE, ?LINE),
242    mnesia_test_lib:init_nodes(Nodes, ?FILE, ?LINE),
243    ping().
244
245%% loop one testcase /suite until it fails
246
247loop(Case) ->
248    loop_1(Case,-1,read_config()).
249
250loop(M,F) when is_atom(F) ->
251    loop_1({M,F},-1,read_config());
252loop(Case,N) when is_integer(N) ->
253    loop_1(Case, N,read_config()).
254
255loop(M,F,N) when is_integer(N) ->
256    loop_1({M,F},N,read_config()).
257
258loop_1(Case,N,Config) when N /= 0 ->
259    io:format("Loop test ~p ~n", [abs(N)]),
260    case ok_result(Res = t(Case,Config)) of
261	true ->
262	    loop_1(Case,N-1,Config);
263	error ->
264	    Res
265    end;
266loop_1(_,_,_) ->
267    ok.
268
269ok_result([{_T,{ok,_,_}}|R]) ->
270    ok_result(R);
271ok_result([{_T,{TC,List}}|R]) when is_tuple(TC), is_list(List) ->
272    ok_result(List) andalso ok_result(R);
273ok_result([]) -> true;
274ok_result(_) -> error.
275