1% Licensed under the Apache License, Version 2.0 (the "License"); you may not 2% use this file except in compliance with the License. You may obtain a copy of 3% the License at 4% 5% http://www.apache.org/licenses/LICENSE-2.0 6% 7% Unless required by applicable law or agreed to in writing, software 8% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10% License for the specific language governing permissions and limitations under 11% the License. 12 13-module(couch_task_status_tests). 14 15-include_lib("couch/include/couch_eunit.hrl"). 16-include_lib("couch/include/couch_db.hrl"). 17 18-define(TIMEOUT, 1000). 19 20 21setup() -> 22 Ctx = test_util:start(?MODULE, [couch_log], [{dont_mock, [config]}]), 23 {ok, TaskStatusPid} = couch_task_status:start_link(), 24 TaskUpdaterPid = spawn(fun() -> loop() end), 25 {TaskStatusPid, TaskUpdaterPid, Ctx}. 26 27 28teardown({TaskStatusPid, _, Ctx})-> 29 test_util:stop_sync_throw(TaskStatusPid, fun() -> 30 couch_task_status:stop() 31 end, timeout_error, ?TIMEOUT), 32 test_util:stop(Ctx). 33 34 35couch_task_status_test_() -> 36 { 37 "CouchDB task status updates", 38 { 39 foreach, 40 fun setup/0, fun teardown/1, 41 [ 42 fun should_register_task/1, 43 fun should_set_task_startup_time/1, 44 fun should_have_update_time_as_startup_before_any_progress/1, 45 fun should_set_task_type/1, 46 fun should_not_register_multiple_tasks_for_same_pid/1, 47 fun should_set_task_progress/1, 48 fun should_update_task_progress/1, 49 fun should_update_time_changes_on_task_progress/1, 50 %% fun should_control_update_frequency/1, 51 fun should_reset_control_update_frequency/1, 52 fun should_track_multiple_tasks/1, 53 fun should_finish_task/1 54 55 ] 56 } 57 }. 58 59 60should_register_task({_, Pid, _Ctx}) -> 61 ok = call(Pid, add, [{type, replication}, {progress, 0}]), 62 ?_assertEqual(1, length(couch_task_status:all())). 63 64should_set_task_startup_time({_, Pid, _Ctx}) -> 65 ok = call(Pid, add, [{type, replication}, {progress, 0}]), 66 ?_assert(is_integer(get_task_prop(Pid, started_on))). 67 68should_have_update_time_as_startup_before_any_progress({_, Pid, _Ctx}) -> 69 ok = call(Pid, add, [{type, replication}, {progress, 0}]), 70 StartTime = get_task_prop(Pid, started_on), 71 ?_assertEqual(StartTime, get_task_prop(Pid, updated_on)). 72 73should_set_task_type({_, Pid, _Ctx}) -> 74 ok = call(Pid, add, [{type, replication}, {progress, 0}]), 75 ?_assertEqual(replication, get_task_prop(Pid, type)). 76 77should_not_register_multiple_tasks_for_same_pid({_, Pid, _Ctx}) -> 78 ok = call(Pid, add, [{type, replication}, {progress, 0}]), 79 ?_assertEqual({add_task_error, already_registered}, 80 call(Pid, add, [{type, compaction}, {progress, 0}])). 81 82should_set_task_progress({_, Pid, _Ctx}) -> 83 ok = call(Pid, add, [{type, replication}, {progress, 0}]), 84 ?_assertEqual(0, get_task_prop(Pid, progress)). 85 86should_update_task_progress({_, Pid, _Ctx}) -> 87 ok = call(Pid, add, [{type, replication}, {progress, 0}]), 88 call(Pid, update, [{progress, 25}]), 89 ?_assertEqual(25, get_task_prop(Pid, progress)). 90 91should_update_time_changes_on_task_progress({_, Pid, _Ctx}) -> 92 ?_assert( 93 begin 94 ok = call(Pid, add, [{type, replication}, {progress, 0}]), 95 ok = timer:sleep(1000), % sleep awhile to customize update time 96 call(Pid, update, [{progress, 25}]), 97 get_task_prop(Pid, updated_on) > get_task_prop(Pid, started_on) 98 end). 99 100%%should_control_update_frequency({_, Pid, _Ctx}) -> 101%% ?_assertEqual(66, 102%% begin 103%% ok = call(Pid, add, [{type, replication}, {progress, 0}]), 104%% call(Pid, update, [{progress, 50}]), 105%% call(Pid, update_frequency, 500), 106%% call(Pid, update, [{progress, 66}]), 107%% call(Pid, update, [{progress, 77}]), 108%% get_task_prop(Pid, progress) 109%% end). 110 111should_reset_control_update_frequency({_, Pid, _Ctx}) -> 112 ?_assertEqual(87, 113 begin 114 ok = call(Pid, add, [{type, replication}, {progress, 0}]), 115 call(Pid, update, [{progress, 50}]), 116 call(Pid, update_frequency, 500), 117 call(Pid, update, [{progress, 66}]), 118 call(Pid, update, [{progress, 77}]), 119 call(Pid, update_frequency, 0), 120 call(Pid, update, [{progress, 87}]), 121 get_task_prop(Pid, progress) 122 end). 123 124should_track_multiple_tasks(_) -> 125 ?_assert(run_multiple_tasks()). 126 127should_finish_task({_, Pid, _Ctx}) -> 128 ok = call(Pid, add, [{type, replication}, {progress, 0}]), 129 ?assertEqual(1, length(couch_task_status:all())), 130 ok = call(Pid, done), 131 ?_assertEqual(0, length(couch_task_status:all())). 132 133 134run_multiple_tasks() -> 135 Pid1 = spawn(fun() -> loop() end), 136 Pid2 = spawn(fun() -> loop() end), 137 Pid3 = spawn(fun() -> loop() end), 138 call(Pid1, add, [{type, replication}, {progress, 0}]), 139 call(Pid2, add, [{type, compaction}, {progress, 0}]), 140 call(Pid3, add, [{type, indexer}, {progress, 0}]), 141 142 ?assertEqual(3, length(couch_task_status:all())), 143 ?assertEqual(replication, get_task_prop(Pid1, type)), 144 ?assertEqual(compaction, get_task_prop(Pid2, type)), 145 ?assertEqual(indexer, get_task_prop(Pid3, type)), 146 147 call(Pid2, update, [{progress, 33}]), 148 call(Pid3, update, [{progress, 42}]), 149 call(Pid1, update, [{progress, 11}]), 150 ?assertEqual(42, get_task_prop(Pid3, progress)), 151 call(Pid1, update, [{progress, 72}]), 152 ?assertEqual(72, get_task_prop(Pid1, progress)), 153 ?assertEqual(33, get_task_prop(Pid2, progress)), 154 155 call(Pid1, done), 156 ?assertEqual(2, length(couch_task_status:all())), 157 call(Pid3, done), 158 ?assertEqual(1, length(couch_task_status:all())), 159 call(Pid2, done), 160 ?assertEqual(0, length(couch_task_status:all())), 161 162 true. 163 164 165loop() -> 166 receive 167 {add, Props, From} -> 168 Resp = couch_task_status:add_task(Props), 169 From ! {ok, self(), Resp}, 170 loop(); 171 {update, Props, From} -> 172 Resp = couch_task_status:update(Props), 173 From ! {ok, self(), Resp}, 174 loop(); 175 {update_frequency, Msecs, From} -> 176 Resp = couch_task_status:set_update_frequency(Msecs), 177 From ! {ok, self(), Resp}, 178 loop(); 179 {done, From} -> 180 From ! {ok, self(), ok} 181 end. 182 183call(Pid, done) -> 184 Ref = erlang:monitor(process, Pid), 185 Pid ! {done, self()}, 186 Res = wait(Pid), 187 receive 188 {'DOWN', Ref, _Type, Pid, _Info} -> 189 Res 190 after ?TIMEOUT -> 191 throw(timeout_error) 192 end; 193call(Pid, Command) -> 194 Pid ! {Command, self()}, 195 wait(Pid). 196 197call(Pid, Command, Arg) -> 198 Pid ! {Command, Arg, self()}, 199 wait(Pid). 200 201wait(Pid) -> 202 receive 203 {ok, Pid, Msg} -> 204 Msg 205 after ?TIMEOUT -> 206 throw(timeout_error) 207 end. 208 209get_task_prop(Pid, Prop) -> 210 From = list_to_binary(pid_to_list(Pid)), 211 Element = lists:foldl( 212 fun(PropList, Acc) -> 213 case couch_util:get_value(pid, PropList) of 214 From -> 215 [PropList | Acc]; 216 _ -> 217 Acc 218 end 219 end, 220 [], couch_task_status:all() 221 ), 222 case couch_util:get_value(Prop, hd(Element), nil) of 223 nil -> 224 erlang:error({assertion_failed, 225 [{module, ?MODULE}, 226 {line, ?LINE}, 227 {reason, "Could not get property '" 228 ++ couch_util:to_list(Prop) 229 ++ "' for task " 230 ++ pid_to_list(Pid)}]}); 231 Value -> 232 Value 233 end. 234