summaryrefslogtreecommitdiff
path: root/erts/test/erlc_SUITE.erl
diff options
context:
space:
mode:
Diffstat (limited to 'erts/test/erlc_SUITE.erl')
-rw-r--r--erts/test/erlc_SUITE.erl695
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) ->