From 93202f2de3dc67da8b48b62cc87c2923faf355f2 Mon Sep 17 00:00:00 2001 From: Juan Facorro Date: Tue, 1 Sep 2015 18:21:33 -0300 Subject: [PATCH 1/6] [#74] ktn_os:command/[1,2] --- src/ktn_os.erl | 45 ++++++++++++++++++++++++++++++++ test/katana.coverspec | 1 + test/ktn_os_SUITE.erl | 60 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 src/ktn_os.erl create mode 100644 test/ktn_os_SUITE.erl diff --git a/src/ktn_os.erl b/src/ktn_os.erl new file mode 100644 index 0000000..f850c8c --- /dev/null +++ b/src/ktn_os.erl @@ -0,0 +1,45 @@ +%% @doc Utility functions for +-module(ktn_os). + +-export([command/1, command/2]). + +-type opts() :: #{log_fun => function()}. +-type exit_status() :: integer(). + +-spec command(iodata()) -> {exit_status(), string()}. +command(Cmd) -> + Opts = #{log_fun => fun error_logger:info_msg/1}, + command(Cmd, Opts). + +-spec command(iodata(), opts()) -> {exit_status(), string()}. +command(Cmd, Opts) -> + PortOpts = [stream, exit_status, eof], + Port = open_port({spawn, shell_cmd()}, PortOpts), + erlang:port_command(Port, make_cmd(Cmd)), + get_data(Port, Opts, []). + +-spec get_data(port(), opts(), [string()]) -> {exit_status(), string()}. +get_data(Port, Opts, Data) -> + receive + {Port, {data, NewData}} -> + case maps:get(log_fun, Opts, undefined) of + Fun when is_function(Fun) -> Fun(NewData); + undefined -> ok + end, + get_data(Port, Opts, [NewData | Data]); + {Port, eof} -> + port_close(Port), + receive + {Port, {exit_status, ExitStatus}} -> + {ExitStatus, lists:flatten(lists:reverse(Data))} + end + end. + +-spec make_cmd(string()) -> iodata(). +make_cmd(Cmd) -> + %% We insert a new line after the command, in case the command + %% contains a comment character. + [$(, unicode:characters_to_binary(Cmd), "\n) string(). +shell_cmd() -> "sh -s unix:cmd 2>&1". diff --git a/test/katana.coverspec b/test/katana.coverspec index d18ceb6..5e6a35e 100644 --- a/test/katana.coverspec +++ b/test/katana.coverspec @@ -9,6 +9,7 @@ ktn_lists, ktn_maps, ktn_numbers, + ktn_os, ktn_random, ktn_recipe, ktn_recipe_verify, diff --git a/test/ktn_os_SUITE.erl b/test/ktn_os_SUITE.erl new file mode 100644 index 0000000..679cd99 --- /dev/null +++ b/test/ktn_os_SUITE.erl @@ -0,0 +1,60 @@ +-module(ktn_os_SUITE). + +-export([ + all/0, + init_per_suite/1, + end_per_suite/1 + ]). + +-export([ + command/1 + ]). + +-define(EXCLUDED_FUNS, + [ + module_info, + all, + test, + init_per_suite, + end_per_suite + ]). + +-type config() :: [{atom(), term()}]. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Common test +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-spec all() -> [atom()]. +all() -> + Exports = ?MODULE:module_info(exports), + [F || {F, _} <- Exports, not lists:member(F, ?EXCLUDED_FUNS)]. + +-spec init_per_suite(config()) -> config(). +init_per_suite(Config) -> + Config. + +-spec end_per_suite(config()) -> config(). +end_per_suite(Config) -> + Config. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Test Cases +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-spec command(config()) -> ok. +command(_Config) -> + Opts = #{log_fun => fun(_) -> ok end}, + + {0, "/\n"} = ktn_os:command("cd /; pwd", Opts), + + {ok, Cwd} = file:get_cwd(), + Result = Cwd ++ "\n", + {0, Result} = ktn_os:command("pwd", Opts), + + {1, _} = ktn_os:command("pwd; ls w4th3v3r", Opts), + + Result2 = Result ++ "Hi\n", + {0, Result2} = ktn_os:command("pwd; echo Hi", #{}), + + {0, "/\n"} = ktn_os:command("cd /; pwd"). From aade5aef2b22603457742fa196db0a3bff3a608e Mon Sep 17 00:00:00 2001 From: Juan Facorro Date: Tue, 1 Sep 2015 18:46:14 -0300 Subject: [PATCH 2/6] [#74] Add tests for ktn_code --- test/ktn_code_SUITE.erl | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/test/ktn_code_SUITE.erl b/test/ktn_code_SUITE.erl index a7edba1..d9da80e 100644 --- a/test/ktn_code_SUITE.erl +++ b/test/ktn_code_SUITE.erl @@ -8,7 +8,8 @@ -export([ consult/1, - beam_to_string/1 + beam_to_string/1, + parse_tree/1 ]). -define(EXCLUDED_FUNS, @@ -57,5 +58,17 @@ consult(_Config) -> -spec beam_to_string(config()) -> ok. beam_to_string(_Config) -> - {error, beam_lib, _} = ktn_code:beam_to_string(bla), - {ok, _} = ktn_code:beam_to_string("../../ebin/ktn_code.beam"). + {error, beam_lib, _} = ktn_code:beam_to_string(bla), + {ok, _} = ktn_code:beam_to_string("../../ebin/ktn_code.beam"). + +parse_tree(_Config) -> + ModuleNode = #{type => module, + attrs => #{location => {1, 2}, + text => "module", + value => x}}, + + #{type := root, + content := _} = ktn_code:parse_tree("-module(x)."), + + #{type := root, + content := [ModuleNode]} = ktn_code:parse_tree("-module(x)."). From c0b34d74aa200b9d36735e1b13cdee0774a04bd7 Mon Sep 17 00:00:00 2001 From: Juan Facorro Date: Wed, 2 Sep 2015 10:59:26 -0300 Subject: [PATCH 3/6] [#74] Removed unused functions from test, added timeout option --- src/ktn_os.erl | 6 +++++- test/ktn_os_SUITE.erl | 18 ++---------------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/src/ktn_os.erl b/src/ktn_os.erl index f850c8c..1f8cee5 100644 --- a/src/ktn_os.erl +++ b/src/ktn_os.erl @@ -1,4 +1,4 @@ -%% @doc Utility functions for +%% @doc Utility functions to run commands in the underlying OS. -module(ktn_os). -export([command/1, command/2]). @@ -20,6 +20,8 @@ command(Cmd, Opts) -> -spec get_data(port(), opts(), [string()]) -> {exit_status(), string()}. get_data(Port, Opts, Data) -> + %% Get timeout option or an hour if undefined. + Timeout = maps:get(timeout, Opts, 600000), receive {Port, {data, NewData}} -> case maps:get(log_fun, Opts, undefined) of @@ -33,6 +35,8 @@ get_data(Port, Opts, Data) -> {Port, {exit_status, ExitStatus}} -> {ExitStatus, lists:flatten(lists:reverse(Data))} end + after + Timeout -> throw(timeout) end. -spec make_cmd(string()) -> iodata(). diff --git a/test/ktn_os_SUITE.erl b/test/ktn_os_SUITE.erl index 679cd99..33b2302 100644 --- a/test/ktn_os_SUITE.erl +++ b/test/ktn_os_SUITE.erl @@ -1,14 +1,8 @@ -module(ktn_os_SUITE). --export([ - all/0, - init_per_suite/1, - end_per_suite/1 - ]). +-export([all/0]). --export([ - command/1 - ]). +-export([command/1]). -define(EXCLUDED_FUNS, [ @@ -30,14 +24,6 @@ all() -> Exports = ?MODULE:module_info(exports), [F || {F, _} <- Exports, not lists:member(F, ?EXCLUDED_FUNS)]. --spec init_per_suite(config()) -> config(). -init_per_suite(Config) -> - Config. - --spec end_per_suite(config()) -> config(). -end_per_suite(Config) -> - Config. - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Test Cases %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% From 8762966f4466dd1d37f98506f4fe460716765ecc Mon Sep 17 00:00:00 2001 From: Juan Facorro Date: Wed, 2 Sep 2015 11:50:52 -0300 Subject: [PATCH 4/6] [#74] Test output function works --- test/ktn_os_SUITE.erl | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/test/ktn_os_SUITE.erl b/test/ktn_os_SUITE.erl index 33b2302..d904986 100644 --- a/test/ktn_os_SUITE.erl +++ b/test/ktn_os_SUITE.erl @@ -43,4 +43,29 @@ command(_Config) -> Result2 = Result ++ "Hi\n", {0, Result2} = ktn_os:command("pwd; echo Hi", #{}), - {0, "/\n"} = ktn_os:command("cd /; pwd"). + {0, "/\n"} = ktn_os:command("cd /; pwd"), + + ok = try ktn_os:command("sleep 5", #{timeout => 1000}) + catch _:timeout -> ok end, + + Fun = fun() -> ktn_os:command("cd /; pwd") end, + FilterFun = + fun(Line) -> + case re:run(Line, "=INFO REPORT==== .* ===") of + nomatch -> false; + {match, _}-> true + end + end, + check_some_line_output(Fun, FilterFun). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Helper functions +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +check_some_line_output(Fun, FilterFun) -> + ct:capture_start(), + Fun(), + ct:capture_stop(), + Lines = ct:capture_get([]), + ListFun = fun(Line) -> FilterFun(Line) end, + [_ | _] = lists:filter(ListFun, Lines). From cea5bdb81561f21b7e20da7d81867b1a950e4261 Mon Sep 17 00:00:00 2001 From: Juan Facorro Date: Wed, 2 Sep 2015 12:08:58 -0300 Subject: [PATCH 5/6] [#74] Test when process is killed. Improve type spec --- src/ktn_os.erl | 2 +- test/ktn_os_SUITE.erl | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/ktn_os.erl b/src/ktn_os.erl index 1f8cee5..ae0c703 100644 --- a/src/ktn_os.erl +++ b/src/ktn_os.erl @@ -3,7 +3,7 @@ -export([command/1, command/2]). --type opts() :: #{log_fun => function()}. +-type opts() :: #{log_fun => fun((iodata()) -> any())}. -type exit_status() :: integer(). -spec command(iodata()) -> {exit_status(), string()}. diff --git a/test/ktn_os_SUITE.erl b/test/ktn_os_SUITE.erl index d904986..ade3147 100644 --- a/test/ktn_os_SUITE.erl +++ b/test/ktn_os_SUITE.erl @@ -56,7 +56,19 @@ command(_Config) -> {match, _}-> true end end, - check_some_line_output(Fun, FilterFun). + check_some_line_output(Fun, FilterFun), + + ct:comment("Check result when process is killed"), + Self = self(), + YesFun = fun() -> + case ktn_os:command("yes > /dev/null") of + {ExitStatus, _} when ExitStatus =/= 0 -> Self ! ok; + _ -> Self ! error + end + end, + erlang:spawn_link(YesFun), + os:cmd("pkill yes"), + ok = receive X -> X after 1000 -> error end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Helper functions From bf0fb05d2f4756d8b65ede835b416b43fcccfe49 Mon Sep 17 00:00:00 2001 From: Juan Facorro Date: Wed, 2 Sep 2015 12:09:37 -0300 Subject: [PATCH 6/6] [#74] Add timeout option in spec --- src/ktn_os.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ktn_os.erl b/src/ktn_os.erl index ae0c703..777604a 100644 --- a/src/ktn_os.erl +++ b/src/ktn_os.erl @@ -3,7 +3,7 @@ -export([command/1, command/2]). --type opts() :: #{log_fun => fun((iodata()) -> any())}. +-type opts() :: #{log_fun => fun((iodata()) -> any()), timeout => integer()}. -type exit_status() :: integer(). -spec command(iodata()) -> {exit_status(), string()}.