summaryrefslogtreecommitdiff
path: root/src/couch/test/eunit/couch_task_status_tests.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/couch/test/eunit/couch_task_status_tests.erl')
-rw-r--r--src/couch/test/eunit/couch_task_status_tests.erl233
1 files changed, 233 insertions, 0 deletions
diff --git a/src/couch/test/eunit/couch_task_status_tests.erl b/src/couch/test/eunit/couch_task_status_tests.erl
new file mode 100644
index 000000000..0ec03563b
--- /dev/null
+++ b/src/couch/test/eunit/couch_task_status_tests.erl
@@ -0,0 +1,233 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_task_status_tests).
+
+-include_lib("couch/include/couch_eunit.hrl").
+-include_lib("couch/include/couch_db.hrl").
+
+-define(TIMEOUT, 1000).
+
+
+setup() ->
+ Ctx = test_util:start(?MODULE, [couch_log], [{dont_mock, [config]}]),
+ {ok, TaskStatusPid} = couch_task_status:start_link(),
+ TaskUpdaterPid = spawn(fun() -> loop() end),
+ {TaskStatusPid, TaskUpdaterPid, Ctx}.
+
+
+teardown({TaskStatusPid, _, Ctx})->
+ test_util:stop_sync_throw(TaskStatusPid, fun() ->
+ couch_task_status:stop()
+ end, timeout_error, ?TIMEOUT),
+ test_util:stop(Ctx).
+
+
+couch_task_status_test_() ->
+ {
+ "CouchDB task status updates",
+ {
+ foreach,
+ fun setup/0, fun teardown/1,
+ [
+ fun should_register_task/1,
+ fun should_set_task_startup_time/1,
+ fun should_have_update_time_as_startup_before_any_progress/1,
+ fun should_set_task_type/1,
+ fun should_not_register_multiple_tasks_for_same_pid/1,
+ fun should_set_task_progress/1,
+ fun should_update_task_progress/1,
+ fun should_update_time_changes_on_task_progress/1,
+ %% fun should_control_update_frequency/1,
+ fun should_reset_control_update_frequency/1,
+ fun should_track_multiple_tasks/1,
+ fun should_finish_task/1
+
+ ]
+ }
+ }.
+
+
+should_register_task({_, Pid, _Ctx}) ->
+ ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+ ?_assertEqual(1, length(couch_task_status:all())).
+
+should_set_task_startup_time({_, Pid, _Ctx}) ->
+ ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+ ?_assert(is_integer(get_task_prop(Pid, started_on))).
+
+should_have_update_time_as_startup_before_any_progress({_, Pid, _Ctx}) ->
+ ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+ StartTime = get_task_prop(Pid, started_on),
+ ?_assertEqual(StartTime, get_task_prop(Pid, updated_on)).
+
+should_set_task_type({_, Pid, _Ctx}) ->
+ ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+ ?_assertEqual(replication, get_task_prop(Pid, type)).
+
+should_not_register_multiple_tasks_for_same_pid({_, Pid, _Ctx}) ->
+ ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+ ?_assertEqual({add_task_error, already_registered},
+ call(Pid, add, [{type, compaction}, {progress, 0}])).
+
+should_set_task_progress({_, Pid, _Ctx}) ->
+ ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+ ?_assertEqual(0, get_task_prop(Pid, progress)).
+
+should_update_task_progress({_, Pid, _Ctx}) ->
+ ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+ call(Pid, update, [{progress, 25}]),
+ ?_assertEqual(25, get_task_prop(Pid, progress)).
+
+should_update_time_changes_on_task_progress({_, Pid, _Ctx}) ->
+ ?_assert(
+ begin
+ ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+ ok = timer:sleep(1000), % sleep awhile to customize update time
+ call(Pid, update, [{progress, 25}]),
+ get_task_prop(Pid, updated_on) > get_task_prop(Pid, started_on)
+ end).
+
+%%should_control_update_frequency({_, Pid, _Ctx}) ->
+%% ?_assertEqual(66,
+%% begin
+%% ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+%% call(Pid, update, [{progress, 50}]),
+%% call(Pid, update_frequency, 500),
+%% call(Pid, update, [{progress, 66}]),
+%% call(Pid, update, [{progress, 77}]),
+%% get_task_prop(Pid, progress)
+%% end).
+
+should_reset_control_update_frequency({_, Pid, _Ctx}) ->
+ ?_assertEqual(87,
+ begin
+ ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+ call(Pid, update, [{progress, 50}]),
+ call(Pid, update_frequency, 500),
+ call(Pid, update, [{progress, 66}]),
+ call(Pid, update, [{progress, 77}]),
+ call(Pid, update_frequency, 0),
+ call(Pid, update, [{progress, 87}]),
+ get_task_prop(Pid, progress)
+ end).
+
+should_track_multiple_tasks(_) ->
+ ?_assert(run_multiple_tasks()).
+
+should_finish_task({_, Pid, _Ctx}) ->
+ ok = call(Pid, add, [{type, replication}, {progress, 0}]),
+ ?assertEqual(1, length(couch_task_status:all())),
+ ok = call(Pid, done),
+ ?_assertEqual(0, length(couch_task_status:all())).
+
+
+run_multiple_tasks() ->
+ Pid1 = spawn(fun() -> loop() end),
+ Pid2 = spawn(fun() -> loop() end),
+ Pid3 = spawn(fun() -> loop() end),
+ call(Pid1, add, [{type, replication}, {progress, 0}]),
+ call(Pid2, add, [{type, compaction}, {progress, 0}]),
+ call(Pid3, add, [{type, indexer}, {progress, 0}]),
+
+ ?assertEqual(3, length(couch_task_status:all())),
+ ?assertEqual(replication, get_task_prop(Pid1, type)),
+ ?assertEqual(compaction, get_task_prop(Pid2, type)),
+ ?assertEqual(indexer, get_task_prop(Pid3, type)),
+
+ call(Pid2, update, [{progress, 33}]),
+ call(Pid3, update, [{progress, 42}]),
+ call(Pid1, update, [{progress, 11}]),
+ ?assertEqual(42, get_task_prop(Pid3, progress)),
+ call(Pid1, update, [{progress, 72}]),
+ ?assertEqual(72, get_task_prop(Pid1, progress)),
+ ?assertEqual(33, get_task_prop(Pid2, progress)),
+
+ call(Pid1, done),
+ ?assertEqual(2, length(couch_task_status:all())),
+ call(Pid3, done),
+ ?assertEqual(1, length(couch_task_status:all())),
+ call(Pid2, done),
+ ?assertEqual(0, length(couch_task_status:all())),
+
+ true.
+
+
+loop() ->
+ receive
+ {add, Props, From} ->
+ Resp = couch_task_status:add_task(Props),
+ From ! {ok, self(), Resp},
+ loop();
+ {update, Props, From} ->
+ Resp = couch_task_status:update(Props),
+ 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, done) ->
+ Ref = erlang:monitor(process, Pid),
+ Pid ! {done, self()},
+ Res = wait(Pid),
+ receive
+ {'DOWN', Ref, _Type, Pid, _Info} ->
+ Res
+ after ?TIMEOUT ->
+ throw(timeout_error)
+ 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 ?TIMEOUT ->
+ throw(timeout_error)
+ end.
+
+get_task_prop(Pid, Prop) ->
+ From = list_to_binary(pid_to_list(Pid)),
+ Element = lists:foldl(
+ fun(PropList, Acc) ->
+ case couch_util:get_value(pid, PropList) of
+ From ->
+ [PropList | Acc];
+ _ ->
+ Acc
+ end
+ end,
+ [], couch_task_status:all()
+ ),
+ case couch_util:get_value(Prop, hd(Element), nil) of
+ nil ->
+ erlang:error({assertion_failed,
+ [{module, ?MODULE},
+ {line, ?LINE},
+ {reason, "Could not get property '"
+ ++ couch_util:to_list(Prop)
+ ++ "' for task "
+ ++ pid_to_list(Pid)}]});
+ Value ->
+ Value
+ end.