From c3437ff4648454607e17c7356536e857dfe87ae1 Mon Sep 17 00:00:00 2001 From: Paul Joseph Davis Date: Thu, 25 Jun 2009 03:59:47 +0000 Subject: Adding tests for couch_task_status.erl Thanks Bob Dionne git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@788246 13f79535-47bb-0310-9956-ffa450edef68 --- src/couchdb/couch_task_status.erl | 93 +++++++++++------- src/couchdb/couch_util.erl | 39 +++++--- test/etap/090-task-status.t | 197 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 282 insertions(+), 47 deletions(-) create mode 100755 test/etap/090-task-status.t diff --git a/src/couchdb/couch_task_status.erl b/src/couchdb/couch_task_status.erl index a0ef490a..1e481c7c 100644 --- a/src/couchdb/couch_task_status.erl +++ b/src/couchdb/couch_task_status.erl @@ -19,79 +19,106 @@ % it will be automatically removed the tracking. To get the tasks list, use the % all/0 function --export([start_link/0,init/1,terminate/2,handle_call/3,handle_cast/2,handle_info/2, - code_change/3,add_task/3,update/1,update/2,all/0,set_update_frequency/1]). +-export([start_link/0, stop/0]). +-export([all/0, add_task/3, update/1, update/2, set_update_frequency/1]). + +-export([init/1, terminate/2, code_change/3]). +-export([handle_call/3, handle_cast/2, handle_info/2]). + +-import(couch_util, [to_binary/1]). -include("couch_db.hrl"). - + + start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). -to_binary(L) when is_list(L) -> - ?l2b(L); -to_binary(B) when is_binary(B) -> - B. + +stop() -> + gen_server:cast(?MODULE, stop). + + +all() -> + gen_server:call(?MODULE, all). + add_task(Type, TaskName, StatusText) -> - put(task_status_update, {{0,0,0}, 0}), - gen_server:call(?MODULE, {add_task, to_binary(Type), - to_binary(TaskName), to_binary(StatusText)}). + put(task_status_update, {{0, 0, 0}, 0}), + Msg = { + add_task, + to_binary(Type), + to_binary(TaskName), + to_binary(StatusText) + }, + gen_server:call(?MODULE, Msg). + set_update_frequency(Msecs) -> - put(task_status_update, {{0,0,0}, Msecs * 1000}). + put(task_status_update, {{0, 0, 0}, Msecs * 1000}). + update(StatusText) -> update("~s", [StatusText]). update(Format, Data) -> {LastUpdateTime, Frequency} = get(task_status_update), - case timer:now_diff(Now = now(), LastUpdateTime) >= Frequency of true -> put(task_status_update, {Now, Frequency}), - gen_server:cast(?MODULE, {update_status, self(), ?l2b(io_lib:format(Format, Data))}); + Msg = ?l2b(io_lib:format(Format, Data)), + gen_server:cast(?MODULE, {update_status, self(), Msg}); false -> ok end. - -% returns a list of proplists. Each proplist describes a running task. -all() -> - [[{type,Type}, - {task,Task}, - {status,Status}, - {pid,?l2b(pid_to_list(Pid))}] || - {Pid, {Type,Task,Status}} <- ets:tab2list(tasks_by_pid)]. - + init([]) -> % read configuration settings and register for configuration changes - ets:new(tasks_by_pid, [ordered_set, protected, named_table]), + ets:new(?MODULE, [ordered_set, protected, named_table]), {ok, nil}. + terminate(_Reason,_State) -> ok. -handle_call({add_task,Type,TaskName,StatusText}, {From, _}, Server) -> - case ets:lookup(tasks_by_pid, From) of +handle_call({add_task, Type, TaskName, StatusText}, {From, _}, Server) -> + case ets:lookup(?MODULE, From) of [] -> - true = ets:insert(tasks_by_pid, {From,{Type,TaskName,StatusText}}), + true = ets:insert(?MODULE, {From, {Type, TaskName, StatusText}}), erlang:monitor(process, From), {reply, ok, Server}; [_] -> {reply, {add_task_error, already_registered}, Server} - end. - + end; +handle_call(all, _, Server) -> + All = [ + [ + {type, Type}, + {task, Task}, + {status, Status}, + {pid, ?l2b(pid_to_list(Pid))} + ] + || + {Pid, {Type, Task, Status}} <- ets:tab2list(?MODULE) + ], + {reply, All, Server}. + + handle_cast({update_status, Pid, StatusText}, Server) -> - [{Pid, {Type,TaskName,_StatusText}}] = ets:lookup(tasks_by_pid, Pid), - true = ets:insert(tasks_by_pid, {Pid, {Type,TaskName,StatusText}}), - {noreply, Server}. + [{Pid, {Type, TaskName, _StatusText}}] = ets:lookup(?MODULE, Pid), + true = ets:insert(?MODULE, {Pid, {Type, TaskName, StatusText}}), + {noreply, Server}; +handle_cast(stop, State) -> + {stop, normal, State}. + handle_info({'DOWN', _MonitorRef, _Type, Pid, _Info}, Server) -> - ets:delete(tasks_by_pid, Pid), + %% should we also erlang:demonitor(_MonitorRef), ? + ets:delete(?MODULE, Pid), {noreply, Server}. + code_change(_OldVsn, State, _Extra) -> {ok, State}. - diff --git a/src/couchdb/couch_util.erl b/src/couchdb/couch_util.erl index 0a715520..1a2929e4 100644 --- a/src/couchdb/couch_util.erl +++ b/src/couchdb/couch_util.erl @@ -13,11 +13,12 @@ -module(couch_util). -export([start_driver/1,terminate_linked/1]). --export([should_flush/0, should_flush/1, to_existing_atom/1, to_binary/1]). +-export([should_flush/0, should_flush/1, to_existing_atom/1]). -export([new_uuid/0, rand32/0, implode/2, collate/2, collate/3]). -export([abs_pathname/1,abs_pathname/2, trim/1, ascii_lower/1]). -export([encodeBase64/1, decodeBase64/1, to_hex/1,parse_term/1,dict_find/3]). -export([file_read_size/1]). +-export([to_binary/1, to_list/1]). -include("couch_db.hrl"). -include_lib("kernel/include/file.hrl"). @@ -67,19 +68,6 @@ to_digit(N) when N < 10 -> $0 + N; to_digit(N) -> $a + N-10. -to_binary(V) when is_binary(V) -> - V; -to_binary(V) when is_list(V) -> - try list_to_binary(V) - catch - _ -> list_to_binary(io_lib:format("~p", [V])) - end; -to_binary(V) when is_atom(V) -> - list_to_binary(atom_to_list(V)); -to_binary(V) -> - list_to_binary(io_lib:format("~p", [V])). - - parse_term(Bin) when is_binary(Bin)-> parse_term(binary_to_list(Bin)); parse_term(List) -> @@ -301,3 +289,26 @@ file_read_size(FileName) -> FileInfo#file_info.size; Error -> Error end. + +to_binary(V) when is_binary(V) -> + V; +to_binary(V) when is_list(V) -> + try + list_to_binary(V) + catch + _ -> + list_to_binary(io_lib:format("~p", [V])) + end; +to_binary(V) when is_atom(V) -> + list_to_binary(atom_to_list(V)); +to_binary(V) -> + list_to_binary(io_lib:format("~p", [V])). + +to_list(V) when is_list(V) -> + V; +to_list(V) when is_binary(V) -> + binary_to_list(V); +to_list(V) when is_atom(V) -> + atom_to_list(V); +to_list(V) -> + lists:flatten(io_lib:format("~p", [V])). diff --git a/test/etap/090-task-status.t b/test/etap/090-task-status.t new file mode 100755 index 00000000..d2df4f91 --- /dev/null +++ b/test/etap/090-task-status.t @@ -0,0 +1,197 @@ +#!/usr/bin/env escript +%% -*- erlang -*- + +main(_) -> + code:add_pathz("src/couchdb"), + etap:plan(16), + case (catch test()) of + ok -> + etap:end_tests(); + Other -> + etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), + etap:bail(Other) + end, + ok. + +check_status(Pid,ListPropLists) -> + From = list_to_binary(pid_to_list(Pid)), + Element = lists:foldl( + fun(PropList,Acc) -> + case proplists:get_value(pid,PropList) of + From -> + [PropList | Acc]; + _ -> + [] + end + end, + [], ListPropLists + ), + proplists:get_value(status,hd(Element)). + +loop() -> + receive + {add, From} -> + Resp = couch_task_status:add_task("type", "task", "init"), + From ! {ok, self(), Resp}, + loop(); + {update, Status, From} -> + Resp = couch_task_status:update(Status), + From ! {ok, self(), Resp}, + loop(); + {update_frequency, Msecs, From} -> + Resp = couch_task_status:set_update_frequency(Msecs), + From ! {ok, self(), Resp}, + loop(); + {done, From} -> + From ! {ok, self(), ok} + end. + +call(Pid, Command) -> + Pid ! {Command, self()}, + wait(Pid). + +call(Pid, Command, Arg) -> + Pid ! {Command, Arg, self()}, + wait(Pid). + +wait(Pid) -> + receive + {ok, Pid, Msg} -> Msg + after 1000 -> + throw(timeout_error) + end. + +test() -> + {ok, TaskStatusPid} = couch_task_status:start_link(), + + TaskUpdater = fun() -> loop() end, + % create three updaters + Pid1 = spawn(TaskUpdater), + Pid2 = spawn(TaskUpdater), + Pid3 = spawn(TaskUpdater), + + ok = call(Pid1, add), + etap:is( + length(couch_task_status:all()), + 1, + "Started a task" + ), + + etap:is( + call(Pid1, add), + {add_task_error, already_registered}, + "Unable to register multiple tasks for a single Pid." + ), + + etap:is( + check_status(Pid1, couch_task_status:all()), + <<"init">>, + "Task status was set to 'init'." + ), + + call(Pid1,update,"running"), + etap:is( + check_status(Pid1,couch_task_status:all()), + <<"running">>, + "Status updated to 'running'." + ), + + + call(Pid2,add), + etap:is( + length(couch_task_status:all()), + 2, + "Started a second task." + ), + + etap:is( + check_status(Pid2, couch_task_status:all()), + <<"init">>, + "Second tasks's status was set to 'init'." + ), + + call(Pid2, update, "running"), + etap:is( + check_status(Pid2, couch_task_status:all()), + <<"running">>, + "Second task's status updated to 'running'." + ), + + + call(Pid3, add), + etap:is( + length(couch_task_status:all()), + 3, + "Registered a third task." + ), + + etap:is( + check_status(Pid3, couch_task_status:all()), + <<"init">>, + "Third tasks's status was set to 'init'." + ), + + call(Pid3, update, "running"), + etap:is( + check_status(Pid3, couch_task_status:all()), + <<"running">>, + "Third task's status updated to 'running'." + ), + + + call(Pid3, update_frequency, 500), + call(Pid3, update, "still running"), + etap:is( + check_status(Pid3, couch_task_status:all()), + <<"still running">>, + "Third task's status updated to 'still running'." + ), + + call(Pid3, update, "skip this update"), + etap:is( + check_status(Pid3, couch_task_status:all()), + <<"still running">>, + "Status update dropped because of frequency limit." + ), + + call(Pid3, update_frequency, 0), + call(Pid3, update, "don't skip"), + etap:is( + check_status(Pid3, couch_task_status:all()), + <<"don't skip">>, + "Status updated after reseting frequency limit." + ), + + + call(Pid1, done), + etap:is( + length(couch_task_status:all()), + 2, + "First task finished." + ), + + call(Pid2, done), + etap:is( + length(couch_task_status:all()), + 1, + "Second task finished." + ), + + call(Pid3, done), + etap:is( + length(couch_task_status:all()), + 0, + "Third task finished." + ), + + erlang:monitor(process, TaskStatusPid), + couch_task_status:stop(), + receive + {'DOWN', _, _, TaskStatusPid, _} -> + ok + after + 1000 -> + throw(timeout_error) + end, + + ok. -- cgit v1.2.3