aboutsummaryrefslogtreecommitdiffstats
path: root/lib/dialyzer/test/generator.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dialyzer/test/generator.erl')
-rw-r--r--lib/dialyzer/test/generator.erl198
1 files changed, 198 insertions, 0 deletions
diff --git a/lib/dialyzer/test/generator.erl b/lib/dialyzer/test/generator.erl
new file mode 100644
index 0000000000..f49083963f
--- /dev/null
+++ b/lib/dialyzer/test/generator.erl
@@ -0,0 +1,198 @@
+%%% File : dialyzer_test_suite_generator.erl
+%%% Author : Stavros Aronis <stavros@enjoy>
+%%% Description : Generator for simple dialyzer test suites (some options,
+%%% some input files or directories and the relevant results).
+%%% Created : 11 Jun 2010 by Stavros Aronis <stavros@enjoy>
+
+-module(generator).
+
+-export([suite/1]).
+
+-include_lib("kernel/include/file.hrl").
+
+-define(suite_suffix, "_tests_SUITE").
+-define(data_folder, "_data").
+-define(erlang_extension, ".erl").
+-define(output_file_mode, write).
+-define(dialyzer_option_file, "dialyzer_options").
+-define(input_files_directory, "src").
+-define(result_files_directory, "result").
+
+-record(suite, {suitename :: string(),
+ outputfile :: file:io_device(),
+ options :: options(),
+ testcases :: [testcase()]}).
+
+-record(options, {time_limit = 1 :: integer(),
+ dialyzer_options = [] :: [term()]}).
+
+-type options() :: #options{}.
+-type testcase() :: {atom(), 'file' | 'dir'}.
+
+-spec suite(string()) -> 'ok'.
+
+suite(SuiteName) ->
+ {ok, Cwd} = file:get_cwd(),
+ SuiteDirN = generate_suite_dir_from_name(Cwd, SuiteName),
+ OutputFile = generate_suite_file(Cwd, SuiteName),
+ {OptionsFileN, InputDirN} = check_neccessary_files(SuiteDirN),
+ generate_suite(SuiteName, OutputFile, OptionsFileN, InputDirN).
+
+generate_suite_dir_from_name(Cwd, SuiteName) ->
+ filename:join(Cwd, SuiteName ++ ?suite_suffix ++ ?data_folder).
+
+generate_suite_file(Cwd, SuiteName) ->
+ OutputFilename =
+ filename:join(Cwd, SuiteName ++ ?suite_suffix ++ ?erlang_extension),
+ case file:open(OutputFilename, [?output_file_mode]) of
+ {ok, IoDevice} -> IoDevice;
+ {error, _} = E -> exit(E)
+ end.
+
+check_neccessary_files(SuiteDirN) ->
+ InputDirN = filename:join(SuiteDirN, ?input_files_directory),
+ check_file_exists(InputDirN, directory),
+ OptionsFileN = filename:join(SuiteDirN, ?dialyzer_option_file),
+ check_file_exists(OptionsFileN, regular),
+ {OptionsFileN, InputDirN}.
+
+check_file_exists(Filename, Type) ->
+ case file:read_file_info(Filename) of
+ {ok, FileInfo} ->
+ case FileInfo#file_info.type of
+ Type -> ok;
+ Else -> exit({error, {wrong_input_file_type, Else}})
+ end;
+ {error, _} = E -> exit(E)
+ end.
+
+generate_suite(SuiteName, OutputFile, OptionsFileN, InputDirN) ->
+ Options = read_options(OptionsFileN),
+ TestCases = list_testcases(InputDirN),
+ Suite = #suite{suitename = SuiteName, outputfile = OutputFile,
+ options = Options, testcases = TestCases},
+ write_suite(Suite),
+ file:close(OutputFile).
+
+read_options(OptionsFileN) ->
+ case file:consult(OptionsFileN) of
+ {ok, Opts} -> read_options(Opts, #options{});
+ _ = E -> exit({error, {incorrect_options_file, E}})
+ end.
+
+read_options([List], Options) when is_list(List) ->
+ read_options(List, Options);
+read_options([], Options) ->
+ Options;
+read_options([{time_limit, TimeLimit}|Opts], Options) ->
+ read_options(Opts, Options#options{time_limit = TimeLimit});
+read_options([{dialyzer_options, DialyzerOptions}|Opts], Options) ->
+ read_options(Opts, Options#options{dialyzer_options = DialyzerOptions}).
+
+list_testcases(InputDirN) ->
+ {ok, PartialFilenames} = file:list_dir(InputDirN),
+ Filenames = [filename:join(InputDirN, F) || F <- PartialFilenames],
+ SafeFilenames = [F || F <- Filenames, safe_extension(F)],
+ lists:sort(lists:map(fun(X) -> map_testcase(X) end, SafeFilenames)).
+
+safe_extension(Filename) ->
+ Extension = filename:extension(Filename),
+ Extension =:= ".erl" orelse Extension =:= "".
+
+map_testcase(Filename) ->
+ TestCase = list_to_atom(filename:basename(Filename, ?erlang_extension)),
+ {ok, FileInfo} = file:read_file_info(Filename),
+ case FileInfo#file_info.type of
+ directory -> {TestCase, dir};
+ regular -> {TestCase, file}
+ end.
+
+write_suite(Suite) ->
+ write_header(Suite),
+ write_testcases(Suite),
+ write_footer(Suite).
+
+write_header(#suite{suitename = SuiteName, outputfile = OutputFile,
+ options = Options, testcases = TestCases}) ->
+ TestCaseNames = [N || {N, _} <- TestCases],
+ Exports = format_export(TestCaseNames),
+ TimeLimit = Options#options.time_limit,
+ DialyzerOptions = Options#options.dialyzer_options,
+ io:format(OutputFile,
+ "-module(~s).\n\n"
+ "-include_lib(\"test_server/include/test_server.hrl\").\n\n"
+ "-export([all/0, groups/0, init_per_group/2, end_per_group/2,\n"
+ " init_per_testcase/2, fin_per_testcase/2]).\n\n"
+ "~s\n\n"
+ "-define(default_timeout, ?t:minutes(~p)).\n"
+ "-define(dialyzer_options, ?config(dialyzer_options, Config)).\n"
+ "-define(datadir, ?config(data_dir, Config)).\n"
+ "-define(privdir, ?config(priv_dir, Config)).\n\n"
+ "groups() -> [].\n\n"
+ "init_per_group(_GroupName, Config) -> Config.\n\n"
+ "end_per_group(_GroupName, Config) -> Config.\n\n"
+ "init_per_testcase(_Case, Config) ->\n"
+ " ?line Dog = ?t:timetrap(?default_timeout),\n"
+ " [{dialyzer_options, ~p}, {watchdog, Dog} | Config].\n\n"
+ "fin_per_testcase(_Case, _Config) ->\n"
+ " Dog = ?config(watchdog, _Config),\n"
+ " ?t:timetrap_cancel(Dog),\n"
+ " ok.\n\n"
+ "all() ->\n"
+ " ~p.\n\n"
+ ,[SuiteName ++ ?suite_suffix, Exports, TimeLimit,
+ DialyzerOptions, TestCaseNames]).
+
+format_export(TestCaseNames) ->
+ TestCaseNamesArity = [list_to_atom(atom_to_list(N)++"/1") ||
+ N <- TestCaseNames],
+ TestCaseString = io_lib:format("-export(~p).", [TestCaseNamesArity]),
+ strip_quotes(lists:flatten(TestCaseString),[]).
+
+strip_quotes([], Result) ->
+ lists:reverse(Result);
+strip_quotes([$' |Rest], Result) ->
+ strip_quotes(Rest, Result);
+strip_quotes([$\, |Rest], Result) ->
+ strip_quotes(Rest, [$\ , $\, |Result]);
+strip_quotes([C|Rest], Result) ->
+ strip_quotes(Rest, [C|Result]).
+
+write_testcases(#suite{outputfile = OutputFile, testcases = TestCases}) ->
+ write_testcases(OutputFile, TestCases).
+
+write_testcases(OutputFile, [{TestCase, Kind}|TestCases]) ->
+ io:format(OutputFile,
+ "~p(Config) when is_list(Config) ->\n"
+ " ?line run(Config, {~p, ~p}),\n"
+ " ok.\n\n"
+ ,[TestCase, TestCase, Kind]),
+ write_testcases(OutputFile, TestCases);
+write_testcases(_OutputFile, []) ->
+ ok.
+
+write_footer(#suite{outputfile = OutputFile}) ->
+ io:format(OutputFile,
+ "run(Config, TestCase) ->\n"
+ " case run_test(Config, TestCase) of\n"
+ " ok -> ok;\n"
+ " {fail, Reason} ->\n"
+ " ?t:format(\"~~s\",[Reason]),\n"
+ " fail()\n"
+ " end.\n\n"
+ "run_test(Config, {TestCase, Kind}) ->\n"
+ " Dog = ?config(watchdog, Config),\n"
+ " Options = ?dialyzer_options,\n"
+ " Dir = ?datadir,\n"
+ " OutDir = ?privdir,\n"
+ " case dialyzer_test:dialyzer_test(Options, TestCase, Kind,\n"
+ " Dir, OutDir, Dog) of\n"
+ " same -> ok;\n"
+ " {differ, DiffList} ->\n"
+ " {fail,\n"
+ " io_lib:format(\"\\nTest ~~p failed:\\n~~p\\n\",\n"
+ " [TestCase, DiffList])}\n"
+ " end.\n\n"
+ "fail() ->\n"
+ " io:format(\"failed\\n\"),\n"
+ " ?t:fail().\n",[]).