Posted at

今時のErlangプロジェクトの作り方(3/3)

More than 1 year has passed since last update.


rebarでテスト

テストを作って実行してみます。

まずはフォルダを作ります。

mkdir -p test

eunitによるテストを追加します。


test/test_myapp_hello.erl

-module(test_myapp_hello).

-include_lib("eunit/include/eunit.hrl").
-define(MOD, myapp_hello).

setup() ->
{ok, Started} = application:ensure_all_started(myapp),
Started.

cleanup(Started) ->
[application:stop(App) || App <- Started].

all_test_() ->
{setup,
fun setup/0,
fun cleanup/1,
[
fun test1/0,
fun test2/0
]
}.

test1() ->
?assertEqual(ok, gen_server:call(?MOD, hi)),
?assert(is_alive_process()).

test2() ->
?assert(is_alive_process()),
?assertEqual(ok, gen_server:call(?MOD, stop)),
?assert(not is_alive_process()).

is_alive_process() ->
Alives = lists:filter(fun(X) -> X =:= ?MOD end, registered()),
length(Alives) =:= 1.


テストを実行します。

% rebar3 eunit

===> Verifying dependencies...
===> Compiling myapp
===> Performing EUnit tests...
- start_link()
- init([])
hello world- handle_call(hi, {<0.103.0>,#Ref<0.0.1.233>}, {})
- handle_call(stop, {<0.103.0>,#Ref<0.0.1.236>}, {})
.normal terminate {}
.

Top 2 slowest tests (0.001 seconds, 1.8% of total time):
test_myapp_hello:all_test_/0
0.001 seconds
test_myapp_hello:all_test_/0
0.000 seconds

Finished in 0.056 seconds
2 tests, 0 failures

=INFO REPORT==== 5-Dec-2016::15:13:02 ===
application: myapp
exited: stopped
type: temporary

テストが通りました!


rebarで依存関係解決

ログフレームワークのlagerを例に依存関係の解決をしてみます。

起動時に立ち上げるプロセスとしてlagerを追加します。


apps/myapp/src/myapp.app.src

{application, myapp,

[{description, "An OTP application"},
{vsn, "0.1.0"},
{registered, []},
{mod, { myapp_app, []}},
{applications,
[kernel,
stdlib,
lager
]},
{env,[]},
{modules, []},

{maintainers, []},
{licenses, []},
{links, []}
]}.


io:formatからlager:debugに変更します。


apps/myapp/src/myapp_hello.erl

-module(myapp_hello).

-behavior(gen_server).

-define(DELAY, 1000).

-export([
start_link/0,
stop/0,
init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
code_change/3,
terminate/2
]).

start_link() ->
lager:debug("- start_link() ~n"),
gen_server:start_link({local, myapp_hello}, ?MODULE, [], []).

stop() ->
lager:debug("- stop() ~n"),
gen_server:call(?MODULE, stop).

init(Args) ->
lager:debug("- init(~p) ~n", [Args]),
lager:debug("hello world"),
{ok, {}}.

handle_call(stop, From, State) ->
lager:debug("- handle_call(stop, ~p, ~p) ~n", [From, State]),
{stop, normal, ok, State};
handle_call(Message, From, State) ->
lager:debug("- handle_call(~p, ~p, ~p) ~n", [Message, From, State]),
{reply, ok, State, ?DELAY}.

handle_cast(Message, State) ->
lager:debug("- handle_cast(~p, ~p) ~n", [Message, State]),
{noreply, State, ?DELAY}.

handle_info(timeout, State) ->
lager:debug("- handle_info(timeout, ~p) ~n", [State]),
{noreply, State, ?DELAY};
handle_info(Message, State) ->
lager:debug("- handle_info(~p, ~p) ~n", [Message, State]),
{noreply, State, ?DELAY}.

code_change(OldVsn, State, Extra) ->
lager:debug("- code_change(~p, ~p, ~p) ~n", [OldVsn, State, Extra]),
{ok, State}.

terminate(Reason, State) ->
lager:debug("~p terminate ~p~n", [Reason, State]).


rebar.configのdepsにlagerを追加します。

またlagerはコンパイルオプション{parse_transform, lager_transform}が必要なのでそれも追加します。


rebar.config

{erl_opts, [

debug_info,
{parse_transform, lager_transform}
]}.

{deps, [
{lager, {git, "git://github.com/basho/lager", {tag, "3.2.4"}}}
]}.

{relx, [{release, { myapp, "0.1.0" },
[
lager,
myapp,
sasl
]},

{sys_config, "./config/sys.config"},
{vm_args, "./config/vm.args"},

{dev_mode, true},
{include_erts, false},

{extended_start_script, true}]
}.

{profiles, [{prod, [{relx, [{dev_mode, false},
{include_erts, true}]}]
}]
}.


この状態でビルドしてみます。

初回ビルド時にgitリポジトリからlagerがダウンロードされ、さらにlagerが依存するgoldrushもダウンロードされています。

% rebar3 release

===> Verifying dependencies...
===> Fetching lager ({git,"git://github.com/basho/lager",
{ref,"81eaef0ce98fdbf64ab95665e3bc2ec4b24c7dac"}})
===> Fetching goldrush ({git,"https://github.com/basho/goldrush.git",
{ref,
"8f1b715d36b650ec1e1f5612c00e28af6ab0de82"}})
===> Compiling goldrush
===> Compiling lager
===> Compiling myapp
===> Starting relx build process ...
===> Resolving OTP Applications from directories:
/home/tadokoro/git/erlang_start/myapp/_build/default/lib
/home/tadokoro/git/erlang_start/myapp/apps
/usr/lib/erlang/lib
===> Resolved myapp-0.1.0
===> Dev mode enabled, release will be symlinked
===> release successfully created!

最後に実行してみます。

出力がio:formatからlagerに置き換わっています。

% ./_build/default/rel/myapp/bin/myapp foreground

Exec: ~bin/erlang/19.1/erts-8.1/bin/erlexec -noshell -noinput +Bd -boot ~/myapp/_build/default/rel/myapp/releases/0.1.0/myapp -mode embedded -config ~/myapp/_build/default/rel/myapp/releases/0.1.0/sys.config -boot_var ERTS_LIB_DIR ~bin/erlang/19.1/erts-8.1/../lib -args_file ~/myapp/_build/default/rel/myapp/releases/0.1.0/vm.args -- foreground
Root: ~/myapp/_build/default/rel/myapp
10:24:44.886 [debug] Lager installed handler {lager_file_backend,"error.log"} into lager_event
10:24:44.886 [debug] Lager installed handler {lager_file_backend,"console.log"} into lager_event
10:24:44.886 [debug] Lager installed handler error_logger_lager_h into error_logger
10:24:44.887 [debug] Supervisor gr_param_sup started gr_param:start_link(gr_lager_default_tracer_params) at pid <0.332.0>
10:24:44.887 [debug] Supervisor gr_counter_sup started gr_counter:start_link(gr_lager_default_tracer_counters) at pid <0.333.0>
10:24:44.887 [debug] Supervisor gr_manager_sup started gr_manager:start_link(gr_lager_default_tracer_params_mgr, gr_lager_default_tracer_params, []) at pid <0.334.0>
10:24:44.887 [debug] Supervisor gr_manager_sup started gr_manager:start_link(gr_lager_default_tracer_counters_mgr, gr_lager_default_tracer_counters, [{input,0},{filter,0},{output,0},{job_input,0},{job_run,0},{job_time,0},{job_error,0}]) at pid <0.335.0>
10:24:44.892 [info] Application lager started on node myapp@tvb
10:24:44.892 [debug] - start_link() ~n
10:24:44.892 [debug] - init([])
10:24:44.892 [debug] hello world
10:24:44.892 [debug] Supervisor myapp_sup started myapp_hello:start_link() at pid <0.341.0>
10:24:44.893 [info] Application myapp started on node myapp@tvb

=PROGRESS REPORT==== 6-Dec-2016::10:24:44 ===
supervisor: {local,sasl_safe_sup}
started: [{pid,<0.347.0>},
{id,alarm_handler},
{mfargs,{alarm_handler,start_link,[]}},
{restart_type,permanent},
{shutdown,2000},
{child_type,worker}]
10:24:44.893 [debug] Supervisor sasl_safe_sup started alarm_handler:start_link() at pid <0.347.0>

=PROGRESS REPORT==== 6-Dec-2016::10:24:44 ===
supervisor: {local,sasl_sup}
started: [{pid,<0.346.0>},
{id,sasl_safe_sup},
{mfargs,
{supervisor,start_link,
[{local,sasl_safe_sup},sasl,safe]}},
{restart_type,permanent},
{shutdown,infinity},
{child_type,supervisor}]
10:24:44.893 [debug] Supervisor sasl_sup started supervisor:start_link({local,sasl_safe_sup}, sasl, safe) at pid <0.346.0>

=PROGRESS REPORT==== 6-Dec-2016::10:24:44 ===
supervisor: {local,sasl_sup}
started: [{pid,<0.348.0>},
{id,release_handler},
{mfargs,{release_handler,start_link,[]}},
{restart_type,permanent},
{shutdown,2000},
{child_type,worker}]

=PROGRESS REPORT==== 6-Dec-2016::10:24:44 ===
application: sasl
started_at: myapp@tvb
10:24:44.894 [debug] Supervisor sasl_sup started release_handler:start_link() at pid <0.348.0>
10:24:44.894 [info] Application sasl started on node myapp@tvb
10:24:45.387 [debug] Lager installed handler lager_backend_throttle into lager_event


まとめ

rebarを使うとテストも依存関係の解決も簡単に行えるのでオススメです。