diff options
Diffstat (limited to 'erts/test/erlc_SUITE.erl')
-rw-r--r-- | erts/test/erlc_SUITE.erl | 695 |
1 files changed, 678 insertions, 17 deletions
diff --git a/erts/test/erlc_SUITE.erl b/erts/test/erlc_SUITE.erl index f5a5f8bf5c..449aedf301 100644 --- a/erts/test/erlc_SUITE.erl +++ b/erts/test/erlc_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2021. All Rights Reserved. +%% Copyright Ericsson AB 1997-2022. 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. @@ -21,28 +21,56 @@ %% Tests the erlc command by compiling various types of files. --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, compile_erl/1, +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, + init_per_testcase/2, end_per_testcase/2, + compile_erl/1, compile_yecc/1, compile_script/1, compile_mib/1, good_citizen/1, deep_cwd/1, arg_overflow/1, - make_dep_options/1]). + make_dep_options/1, + features_erlc_describe/1, + features_erlc_unknown/1, + features_directives/1, + features_atom_warnings/1, + features_macros/1, + features_disable/1, + features_all/1, + features_load/1, + features_runtime/1, + features_include/1, + features/1]). -include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. -all() -> - [{group,with_server},{group,without_server}]. +all() -> + [{group,with_server},{group,without_server}, + {group,features_with_server}]. groups() -> Tests = tests(), [{with_server,[],Tests}, - {without_server,[],Tests}]. + {features_with_server,[],feature_tests()}, + {without_server,[],Tests ++ feature_tests()}]. tests() -> [compile_erl, compile_yecc, compile_script, compile_mib, good_citizen, deep_cwd, arg_overflow, make_dep_options]. +feature_tests() -> + [features_erlc_describe, + features_erlc_unknown, + features_directives, + features_atom_warnings, + features_macros, + features_disable, + features_all, + features_load, + features_runtime, + features_include, + features]. + init_per_suite(Config) -> Config. @@ -52,6 +80,10 @@ end_per_suite(_Config) -> init_per_group(with_server, Config) -> os:putenv("ERLC_USE_SERVER", "yes"), Config; +init_per_group(features_with_server, Config) -> + os:putenv("ERLC_USE_SERVER", "yes"), + timer:sleep(12 * 1000), + Config; init_per_group(without_server, Config) -> os:putenv("ERLC_USE_SERVER", "no"), Config; @@ -60,8 +92,28 @@ init_per_group(_, Config) -> end_per_group(_GroupName, Config) -> os:unsetenv("ERLC_USE_SERVER"), + os:unsetenv("OTP_TEST_FEATURES"), + Config. + +init_per_testcase(TestCase, Config) -> + case lists:member(TestCase, feature_tests()) of + true -> + os:putenv("OTP_TEST_FEATURES", "true"); + false -> + ok + end, Config. +end_per_testcase(TestCase, Config) -> + case lists:member(TestCase, feature_tests()) of + true -> + os:putenv("OTP_TEST_FEATURES", "true"); + false -> + ok + end, + Config. + + %% Copy from erlc_SUITE_data/include/erl_test.hrl. -record(person, {name, shoe_size}). @@ -150,7 +202,7 @@ compile_mib(Config) when is_list(Config) -> %% Try -W option and more verbose. ok = file:delete(Output), - case test_server:os_type() of + case os:type() of {unix,_} -> run(Config, Cmd, FileName, "-W +'{verbosity,info}'", ["\\[GOOD-MIB[.]mib\\]\\[INF\\]: No accessfunction for 'sysDescr' => using default", @@ -435,6 +487,590 @@ make_dep_options(Config) -> false = exists(BeamFileName), ok. + +%%% Tests related to the features mechanism +%% Support macros and functions +-define(OK(Lines), Lines ++ ["_OK_"]). +-define(NOTOK(Lines), Lines ++ ["_ERROR_"]). + +flatfmt(FStr, Args) -> + lists:flatten(io_lib:format(FStr, Args)). + +defopt(Name) -> flatfmt("-D~w", [Name]). +defopt(Name, Value) -> flatfmt("-D~w=~w", [Name, Value]). + +longopt(enable, Ftr) -> flatfmt("-enable-feature ~w", [Ftr]); +longopt(disable, Ftr) -> flatfmt("-disable-feature ~w", [Ftr]). + +plusopt(enable, Ftr) -> flatfmt("+\"{feature, ~w, enable}\"", [Ftr]); +plusopt(disable, Ftr) -> flatfmt("+\"{feature, ~w, disable}\"", [Ftr]). + +options(Opts) -> lists:flatten(lists:join(" ", Opts)). + +peer(Args) -> + {ok, Peer, Node} = + ?CT_PEER(#{args => Args, + connection => 0}), + {Peer, Node}. + +%% Error messages to expect +nosingle(Ftr) -> flatfmt("the feature '~w' does not exist", [Ftr]). + +nomultiple(Ftrs) -> + Q = fun(A) -> flatfmt("'~w'", [A]) end, + flatfmt("the features ~s and '~w' do not exist", + [lists:join(", ", + lists:map(Q, lists:droplast(Ftrs))), + lists:last(Ftrs)]). + +not_config(Ftr) -> flatfmt("the feature '~w' is not configurable", [Ftr]). + +syntax(Offender) -> flatfmt("syntax error before: ~w", [Offender]). + +misplaced_directive() -> + "feature directive not allowed after exports or record definitions". + +atom_warning(Atom, Ftr) -> + flatfmt("atom '~w' is reserved in the experimental feature '~w'", + [Atom, Ftr]). + +compile_fun(Config) -> + {SrcDir, OutDir, Cmd} = get_cmd(Config), + Compile = fun(FName, Opts, Expected) -> + Path = case FName of + "" -> ""; + FName -> filename:join(SrcDir, FName) + end, + run(Config, Cmd, Path, Opts, Expected) + end, + {Compile, SrcDir, OutDir}. + +%% Tests +features_erlc_describe(Config) when is_list(Config) -> + {Compile, _, _} = compile_fun(Config), + + Compile("", "-list-features", + ?NOTOK(["Available features:", + "approved_ftr_1", + "approved_ftr_2", + "experimental_ftr_1", + "experimental_ftr_2"])), + + Compile("", "-describe-feature experimental_ftr_1", + ?NOTOK(["experimental_ftr_1", + "Type", + "Status", + "Keywords", + skip_lines])), + + Compile("", "-describe-feature loop_macro", + ?NOTOK(["Unknown feature: loop_macro"])). + +features_erlc_unknown(Config) when is_list(Config) -> + {Compile, _, _} = compile_fun(Config), + + %% Test for single invalid feature, using long and +options + Compile("nofile.erl", longopt(enable, no_ftr), + ?NOTOK([nosingle(no_ftr)])), + Compile("nofile.erl", plusopt(enable, no_ftr), + ?NOTOK([nosingle(no_ftr)])), + Compile("nofile.erl", longopt(disable, no_ftr), + ?NOTOK([nosingle(no_ftr)])), + Compile("nofile.erl", plusopt(disable, no_ftr), + ?NOTOK([nosingle(no_ftr)])), + + %% Test for multiple invalid features + Compile("nofile.erl", options([longopt(enable, no_ftr), + longopt(enable, un_ftr)]), + ?NOTOK([nomultiple([no_ftr, un_ftr])])), + Compile("nofile.erl", options([longopt(enable, no_ftr), + longopt(enable, un_ftr), + longopt(disable, mis_ftr)]), + ?NOTOK([nomultiple([no_ftr, un_ftr, mis_ftr])])), + Compile("nofile.erl", options([plusopt(enable, no_ftr), + plusopt(enable, un_ftr)]), + ?NOTOK([nomultiple([no_ftr, un_ftr])])), + Compile("nofile.erl", options([plusopt(enable, no_ftr), + plusopt(enable, un_ftr), + plusopt(disable, mis_ftr)]), + ?NOTOK([nomultiple([no_ftr, un_ftr, mis_ftr])])), + + Compile("nofile.erl", options([longopt(enable, no_ftr), + plusopt(enable, un_ftr)]), + ?NOTOK([nomultiple([no_ftr, un_ftr])])), + Compile("nofile.erl", options([plusopt(enable, no_ftr), + longopt(enable, un_ftr)]), + ?NOTOK([nomultiple([no_ftr, un_ftr])])), + + Compile("nofile.erl", options([longopt(enable, no_ftr), + longopt(enable, un_ftr), + plusopt(disable, mis_ftr)]), + ?NOTOK([nomultiple([no_ftr, un_ftr, mis_ftr])])), + Compile("nofile.erl", options([longopt(enable, no_ftr), + plusopt(enable, un_ftr), + plusopt(disable, mis_ftr)]), + ?NOTOK([nomultiple([no_ftr, un_ftr, mis_ftr])])), + + Compile("nofile.erl", options([longopt(enable, no_ftr), + plusopt(enable, un_ftr), + longopt(disable, mis_ftr)]), + ?NOTOK([nomultiple([no_ftr, un_ftr, mis_ftr])])), + Compile("nofile.erl", options([plusopt(enable, no_ftr), + plusopt(enable, un_ftr), + longopt(disable, mis_ftr)]), + ?NOTOK([nomultiple([no_ftr, un_ftr, mis_ftr])])), + + %% Rejected and permanent features can not be configured. + %% Rejected feature + Compile("nofile.erl", + options([longopt(enable, rejected_ftr)]), + ?NOTOK([not_config(rejected_ftr), + skip_lines])), + + %% Permanent feature + Compile("nofile.erl", + options([longopt(enable, permanent_ftr)]), + ?NOTOK([not_config(permanent_ftr), + skip_lines])). + +features_directives(Config) when is_list(Config) -> + {Compile, _, _} = compile_fun(Config), + + %% Test for misplaced feature directive (enable) + Compile("f_directives.erl", + options([defopt(misplaced_enable)]), + ?NOTOK([misplaced_directive(), + "[0-9]+\| -feature.*misplaced", + skip_lines])), + %% Test for misplaced feature directive (disable) + Compile("f_directives.erl", + options([defopt(misplaced_disable)]), + ?NOTOK([misplaced_directive(), + "[0-9]+\| -feature.*misplaced", + skip_lines])), + + %% Test for unknown feature in directive (enable) + Compile("f_directives.erl", + options([defopt(enable_unknown)]), + ?NOTOK([nosingle(unlesser), + "[0-9]+\| -feature", + skip_lines])), + %% Test for unknown feature in directive (disable) + Compile("f_directives.erl", + options([defopt(disable_unknown)]), + ?NOTOK([nosingle(unlesser), + "[0-9]+\| -feature", + skip_lines])), + + %% Enable a feature on the command line and disable the same in a + %% directive + Compile("f_directives.erl", + options([longopt(enable, experimental_ftr_2), + defopt(disable_exp2), + defopt(no_dir_enable_exp2)]), + ?OK([])). + +features_atom_warnings(Config) when is_list(Config) -> + {Compile, _, _} = compile_fun(Config), + + %% Check for keyword warnings. Not all are checked. + Compile("ignorant.erl", "+warn_keywords", + ?OK([atom_warning(until, experimental_ftr_2), + skip_lines, + atom_warning(ifn, experimental_ftr_1), + skip_lines, + atom_warning(while, experimental_ftr_2), + skip_lines])), + + %% Check for keyword warnings, resulting in error. + %% Not all warnings are checked. + Compile("ignorant.erl", "+warn_keywords -Werror", + ?NOTOK([skip_lines, + atom_warning(until, experimental_ftr_2), + skip_lines, + atom_warning(ifn, experimental_ftr_1), + skip_lines, + atom_warning(while, experimental_ftr_2), + skip_lines])), + + %% Check for keyword warnings. Not all warnings are checked. + %% This file has a -compile attribute for keyword warnings. + Compile("ignorant_directive.erl", "", + ?OK([atom_warning(ifn, experimental_ftr_1), + skip_lines, + atom_warning(while, experimental_ftr_2), + skip_lines, + atom_warning(until, experimental_ftr_2), + skip_lines])), + + %% Override warning attribute inside file + Compile("ignorant_directive.erl", "+nowarn_keywords", + ?OK([])), + + %% File has quoted atoms which are keywords in experimental_ftr_2. + %% We should see no warnings. + Compile("foo.erl", options([longopt(enable, experimental_ftr_2), + "+warn_keywords"]), + ?OK([])). + +features(Config) when is_list(Config) -> + {Compile, _, _} = compile_fun(Config), + + %% Simple compile - baseline for this file. Really needed? + %% It uses atoms that are keywords in experimental features. + Compile("ignorant.erl", + [], + ?OK([])), + + %% Enable and disable the same feature on the command line to + %% check that the ordering semantics is honoured. + Compile("ignorant.erl", + options([longopt(enable, experimental_ftr_1), + longopt(disable, experimental_ftr_1)]), + ?OK([])), + + %% As above, with +options + Compile("ignorant.erl", + options([plusopt(enable, experimental_ftr_1), + plusopt(disable, experimental_ftr_1)]), + ?OK([])), + + %% Mixed options + Compile("ignorant.erl", + options([longopt(enable, experimental_ftr_1), + plusopt(disable, experimental_ftr_1)]), + ?OK([])), + + %% Mixed options + Compile("ignorant.erl", + options([plusopt(enable, experimental_ftr_1), + longopt(disable, experimental_ftr_1)]), + ?OK([])), + + %% Enable a feature and see errors + Compile("ignorant.erl", + options([longopt(enable, experimental_ftr_1)]), + ?NOTOK([syntax(ifn), + skip_lines, + syntax(ifn), + skip_lines])), + + %% Enable another feature and see other errors + Compile("ignorant.erl", + options([longopt(enable, experimental_ftr_2)]), + ?NOTOK([syntax(until), + skip_lines, + syntax(until), + skip_lines, + syntax(while), + skip_lines, + syntax(while), + skip_lines, + syntax(until), + skip_lines])), + + %% Enable both features and see even more errors + Compile("ignorant.erl", + options([longopt(enable, experimental_ftr_1), + longopt(enable, experimental_ftr_2)]), + ?NOTOK([syntax(until), + skip_lines, + syntax(until), + skip_lines, + syntax(ifn), + skip_lines, + syntax(while), + skip_lines, + syntax(until), + skip_lines, + syntax(ifn), + skip_lines])), + + ok. + +features_macros(Config) when is_list(Config) -> + {Compile, _, OutDir} = compile_fun(Config), + + Compile("f_macros.erl", "", ?OK([])), + + {Peer0, Node0} = peer(["-pa", OutDir]), + Call = fun(Node, Fun) -> + erpc:call(Node, f_macros, Fun, []) + end, + + {module, f_macros} = + erpc:call(Node0, code, load_file, [f_macros]), + + true = Call(Node0, has_experimental), + false = Call(Node0, has_hindley_milner), + false = Call(Node0, with_hm), + false = Call(Node0, uses_experimental), + false = Call(Node0, uses_exp2), + + peer:stop(Peer0), + + Compile("f_macros.erl", longopt(enable, experimental_ftr_1), + ?OK([])), + + {Peer1, Node1} = peer(["-pa", OutDir]), + + true = erpc:call(Node1, erlang, module_loaded, [erl_features]), + + %% We can't load this due to experimental_ftr_1 not being enabled + %% in the runtime + {error, {features_not_allowed, [experimental_ftr_1]}} = + erpc:call(Node1, code, load_file, [f_macros]), + %% Check features enabled during compilation + [approved_ftr_1, approved_ftr_2, experimental_ftr_1] = + erpc:call(Node1, erl_features, used, [f_macros]), + + peer:stop(Peer1), + + %% Restart with feature enabled in runtime + {Peer2, Node2} = peer(["-pa", OutDir, + "-enable-feature","experimental_ftr_1"]), + %% Now we can load it + {module, f_macros} = + erpc:call(Node2, code, load_file, [f_macros]), + + true = Call(Node2, uses_experimental), + false = Call(Node2, uses_exp2), + peer:stop(Peer2), + + Compile("f_macros.erl", longopt(enable, experimental_ftr_2), + ?OK([])), + {Peer3, Node3} = peer(["-pa", OutDir, + "-enable-feature","experimental_ftr_2"]), + false = Call(Node3, uses_experimental), + true = Call(Node3, uses_exp2), + peer:stop(Peer3), + + Compile("f_macros.erl", options([longopt(enable, experimental_ftr_1), + longopt(enable, experimental_ftr_2)]), + ?OK([])), + {Peer4, Node4} = peer(["-pa", OutDir, + "-enable-feature","experimental_ftr_1", + "-enable-feature","experimental_ftr_2"]), + true = Call(Node4, uses_experimental), + true = Call(Node4, uses_exp2), + peer:stop(Peer4), + + ok. + +features_disable(Config) when is_list(Config) -> + {Compile, _, OutDir} = compile_fun(Config), + + Call = fun(Node, Fun) -> + erpc:call(Node, f_disable, Fun, []) + end, + + Compile("f_disable.erl", options([longopt(enable, experimental_ftr_1), + longopt(enable, experimental_ftr_2)]), + ?OK([])), + + {Peer, Node} = peer(["-pa", OutDir, + "-enable-feature","experimental_ftr_1", + "-enable-feature","experimental_ftr_2"]), + %% Check features enabled during compilation + [approved_ftr_2] = + erpc:call(Node, erl_features, used, [f_disable]), + + no_experimental = Call(Node, no_experimental), + no_exp2 = Call(Node, no_ftrs), + peer:stop(Peer), + + ok. + +features_all(Config) when is_list(Config) -> + {Compile, _, OutDir} = compile_fun(Config), + + Compile("foo.erl", longopt(enable, all), + ?OK([])), + + {Peer0, Node0} = peer(["-pa", OutDir]), + %% Check features enabled during compilation + [approved_ftr_1,approved_ftr_2,experimental_ftr_1,experimental_ftr_2] = + erpc:call(Node0, erl_features, used, [foo]), + peer:stop(Peer0), + + Compile("foo.erl", longopt(disable, all), + ?OK([])), + + {Peer1, Node1} = peer(["-pa", OutDir]), + %% Check features enabled during compilation + [] = erpc:call(Node1, erl_features, used, [foo]), + {module, foo} = erpc:call(Node1, code, load_file, [foo]), + peer:stop(Peer1), + + Compile("foo.erl", options([longopt(disable, all), + longopt(enable, approved_ftr_2)]), + ?OK([])), + + {Peer2, Node2} = peer(["-pa", OutDir, + "-disable-feature", "all"]), + %% Check features enabled during compilation + [approved_ftr_2] = erpc:call(Node2, erl_features, used, [foo]), + {error, {features_not_allowed, [approved_ftr_2]}} = + erpc:call(Node2, code, load_file, [foo]), + peer:stop(Peer2), + + ok. + +features_load(Config) when is_list(Config) -> + {_Compile, SrcDir, _OutDir} = compile_fun(Config), + + %% Note that we put SrcDir in the load path as there is where we + %% have the precompiled beam file. + {Peer0, Node0} = peer(["-pa", SrcDir]), + + %% For a file compiled with an older version, i.e., with no Meta + %% chunk, we should see no used features. + [] = erpc:call(Node0, erl_features, used, [older]), + %% .. and we should be able to load it. + {module,older} = erpc:call(Node0, code, load_file, [older]), + + [] = erpc:call(Node0, erl_features, used, + [filename:join(SrcDir, "older.beam")]), + + %% Behaviour for non existent modules + not_found = erpc:call(Node0, erl_features, used, [none]), + not_found = erpc:call(Node0, erl_features, used, ["none.beam"]), + peer:stop(Peer0), + + ok. + +features_runtime(Config) when is_list(Config) -> + AllFtrs = [approved_ftr_1, + approved_ftr_2, + experimental_ftr_1, + experimental_ftr_2, + permanent_ftr, + rejected_ftr], + ConfigFtrs = [approved_ftr_1, + approved_ftr_2, + experimental_ftr_1, + experimental_ftr_2], + Approved = [approved_ftr_2, + approved_ftr_1], + + {Compile, _SrcDir, _OutDir} = compile_fun(Config), + + {Peer0, Node0} = peer([]), + + %% Get all known features + AllFtrs = erpc:call(Node0, erl_features, all, []), + ConfigFtrs = erpc:call(Node0, erl_features, configurable, []), + Approved = erpc:call(Node0, erl_features, enabled, []), + + %% Keywords from enabled (here the approved) features + %% (does not need to be quoted since it comes from the peer node) + [unless] = erpc:call(Node0, erl_features, keywords, []), + + Info = erpc:call(Node0, erl_features, info, [permanent_ftr]), + true = is_map(Info), + true = lists:all(fun(K) -> is_map_key(K, Info) end, + [status,type,description,short,experimental]), + + %% Try to get info for unknown feature - raises error + try + erpc:call(Node0, erl_features, info, [unknown_feature]) of + Value -> + ct:fail({value_returned_for_unknown_feature, Value}) + catch + error:{exception, invalid_feature, _} -> + ok; + Class:Reason -> + ct:fail({unexpected_exception, {Class, Reason}}) + end, + + peer:stop(Peer0), + + {Peer1, Node1} = peer(["-enable-feature", "experimental_ftr_2"]), + [experimental_ftr_2, approved_ftr_2, approved_ftr_1] = + erpc:call(Node1, erl_features, enabled, []), + [while, until, unless] = erpc:call(Node1, erl_features, keywords, []), + + peer:stop(Peer1), + + {Peer2, Node2} = peer(["-disable-feature", "all"]), + [] = erpc:call(Node2, erl_features, enabled, []), + [] = erpc:call(Node2, erl_features, keywords, []), + + peer:stop(Peer2), + ok. + +features_include(Config) when is_list(Config) -> + {Compile, _SrcDir, OutDir} = compile_fun(Config), + + %% Ensure that the feature experimental_ftr_1 is enabled in the + %% include file and generates an error + Compile("f_include_1.erl", [], + ?NOTOK([syntax(ifn), + skip_lines])), + + %% This will disable a feature after a record has been defined. + %% Error expected + Compile("f_include_1.erl", defopt(end_include), + ?NOTOK([misplaced_directive(), + skip_lines])), + + %% Ensure that the macro knows that experimental_ftr_1 is enabled + %% in the include file. + Compile("f_include_2.erl", defopt(end_include), + ?OK([])), + + {Peer0, Node0} = peer(["-pa", OutDir, + "-enable-feature", "experimental_ftr_1"]), + + {module, f_include_2} = erpc:call(Node0, code, load_file, [f_include_2]), + active = erpc:call(Node0, f_include_2, foo, [2]), + peer:stop(Peer0), + + Compile("f_include_3.erl", [], + ?OK([])), + + {Peer1, Node1} = peer(["-pa", OutDir, + "-enable-feature", "all"]), + exp2_enabled = erpc:call(Node1, f_include_3, foo, [1]), + peer:stop(Peer1), + + Compile("f_include_3.erl", defopt(end_prefix), + ?NOTOK([misplaced_directive(), + "experimental_ftr_2", + skip_lines])), + + Compile("f_include_exp2.erl", defopt(enable_exp_2, 0), + ?OK([])), + {Peer2, Node2} = peer(["-pa", OutDir]), + {conditional, on, until} = erpc:call(Node2, f_include_exp2, foo, []), + peer:stop(Peer2), + + Compile("f_include_exp2.erl", + options([defopt(enable_exp_2, 0), + longopt(enable, experimental_ftr_2)]), + ?NOTOK([syntax(until), + skip_lines])), + + Compile("f_include_exp2.erl", + options([defopt(enable_exp_2, 1)]), + ?NOTOK([syntax(until), + skip_lines])), + + Compile("f_include_exp2.erl", + options([defopt(enable_exp_2, 2), + longopt(enable, experimental_ftr_2)]), + ?OK([])), + + {Peer3, Node3} = peer(["-pa", OutDir, + "-enable-feature", "experimental_ftr_2"]), + {conditional, off, none} = erpc:call(Node3, f_include_exp2, foo, []), + + [approved_ftr_1, approved_ftr_2, experimental_ftr_2] = + erpc:call(Node3, erl_features, used, [f_include_exp2]), + peer:stop(Peer3), + + ok. + %% Runs a command. run(Config, Cmd0, Name, Options, Expect) -> @@ -447,7 +1083,7 @@ verify_result(Result, Expect) -> Messages = split(Result, [], []), io:format("Result: ~p", [Messages]), io:format("Expected: ~p", [Expect]), - match_messages(Messages, Expect). + match_messages_x(Messages, Expect). %% insert What before Item, crash if Item is not found insert_before(Item, What, [Item|List]) -> @@ -466,15 +1102,27 @@ split([], [], Lines) -> split([], Current, Lines) -> split([], [], [lists:reverse(Current)|Lines]). -match_messages([Msg|Rest1], [Regexp|Rest2]) -> - case re:run(Msg, Regexp, [{capture,none}, unicode]) of +match_messages_x(Msgs0, Regexps0) -> + Msgs = lists:droplast(Msgs0), + Regexps = lists:droplast(Regexps0), + Return = lists:last(Msgs0), + ExpRet = lists:last(Regexps0), + match_messages(Msgs, Regexps), + match_one(Return, ExpRet). + +match_messages(_, [skip_lines]) -> + ok; +match_messages([_Msg|Rest1], [skip_one|Rest2]) -> + match_messages(Rest1, Rest2); +match_messages([Msg|Rest1], [skip_lines, Regexp|Rest2]) -> + case match(Msg, Regexp) of match -> - ok; + match_messages(Rest1, Rest2); nomatch -> - io:format("Not matching: ~s\n", [Msg]), - io:format("Regexp : ~s\n", [Regexp]), - ct:fail(message_mismatch) - end, + match_messages(Rest1, [skip_lines, Regexp|Rest2]) + end; +match_messages([Msg|Rest1], [Regexp|Rest2]) -> + match_one(Msg, Regexp), match_messages(Rest1, Rest2); match_messages([], [Expect|Rest]) -> ct:fail({too_few_messages, [Expect|Rest]}); @@ -483,6 +1131,19 @@ match_messages([Msg|Rest], []) -> match_messages([], []) -> ok. +match_one(Msg, Regexp) -> + case match(Msg, Regexp) of + match -> + ok; + nomatch -> + io:format("Not matching: ~s\n", [Msg]), + io:format("Regexp : ~s\n", [Regexp]), + ct:fail(message_mismatch) + end. + +match(Msg, Regexp) -> + re:run(Msg, Regexp, [{capture,none}, unicode]). + get_cmd(Cfg) -> {SrcDir, IncDir, OutDir} = get_dirs(Cfg), Cmd = erlc() ++ " -I" ++ IncDir ++ " -o" ++ OutDir ++ " ", @@ -509,7 +1170,7 @@ run_command(Config, Cmd) -> TmpDir = filename:join(proplists:get_value(priv_dir, Config), "tmp"), file:make_dir(TmpDir), {RunFile, Run, Script} = run_command(TmpDir, os:type(), Cmd), - ok = file:write_file(filename:join(TmpDir, RunFile), unicode:characters_to_binary(Script)), + ok = file:write_file(RunFile, unicode:characters_to_binary(Script)), os:cmd(Run). run_command(Dir, {win32, _}, Cmd) -> |