blob: a7bde6c8850bd9afc9c40f017a28eb653f827f61 [file] [log] [blame]
#!/usr/bin/env escript
%% -*- erlang -*-
% 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.
main(_) ->
test_util:init_code_path(),
etap:plan(55),
case (catch test()) of
ok ->
etap:end_tests();
Other ->
etap:diag(io_lib:format("Test died abnormally: ~p", [Other])),
etap:bail(Other)
end,
ok.
test() ->
couch_server_sup:start_link(test_util:config_files()),
ibrowse:start(),
test_pool_full(),
test_worker_dead_pool_non_full(),
test_worker_dead_pool_full(),
couch_server_sup:stop(),
ok.
test_pool_full() ->
Pool = spawn_pool(),
Client1 = spawn_client(Pool),
Client2 = spawn_client(Pool),
Client3 = spawn_client(Pool),
etap:diag("Check that we can spawn the max number of connections."),
etap:is(ping_client(Client1), ok, "Client 1 started ok."),
etap:is(ping_client(Client2), ok, "Client 2 started ok."),
etap:is(ping_client(Client3), ok, "Client 3 started ok."),
Worker1 = get_client_worker(Client1, "1"),
Worker2 = get_client_worker(Client2, "2"),
Worker3 = get_client_worker(Client3, "3"),
etap:is(is_process_alive(Worker1), true, "Client's 1 worker is alive."),
etap:is(is_process_alive(Worker2), true, "Client's 2 worker is alive."),
etap:is(is_process_alive(Worker3), true, "Client's 3 worker is alive."),
etap:isnt(Worker1, Worker2, "Clients 1 and 2 got different workers."),
etap:isnt(Worker2, Worker3, "Clients 2 and 3 got different workers."),
etap:isnt(Worker1, Worker3, "Clients 1 and 3 got different workers."),
etap:diag("Check that client 4 blocks waiting for a worker."),
Client4 = spawn_client(Pool),
etap:is(ping_client(Client4), timeout, "Client 4 blocked while waiting."),
etap:diag("Check that stopping a client gives up its worker."),
etap:is(stop_client(Client1), ok, "First client stopped."),
etap:diag("And check that our blocked client has been unblocked."),
etap:is(ping_client(Client4), ok, "Client 4 was unblocked."),
Worker4 = get_client_worker(Client4, "4"),
etap:is(is_process_alive(Worker4), true, "Client's 4 worker is alive."),
etap:is(Worker4, Worker1, "Client 4 got worker that client 1 got before."),
lists:foreach(fun(C) -> ok = stop_client(C) end, [Client2, Client3, Client4]),
stop_pool(Pool).
test_worker_dead_pool_non_full() ->
Pool = spawn_pool(),
Client1 = spawn_client(Pool),
etap:is(ping_client(Client1), ok, "Client 1 started ok."),
Worker1 = get_client_worker(Client1, "1"),
etap:is(is_process_alive(Worker1), true, "Client's 1 worker is alive."),
etap:diag("Kill client's 1 worker."),
etap:is(kill_client_worker(Client1), ok, "Killed client's 1 worker."),
etap:is(is_process_alive(Worker1), false, "Client's 1 worker process is dead."),
etap:is(stop_client(Client1), ok, "First client stopped and released its worker."),
Client2 = spawn_client(Pool),
etap:is(ping_client(Client2), ok, "Client 2 started ok."),
Worker2 = get_client_worker(Client2, "2"),
etap:isnt(Worker2, Worker1, "Client 2 got a different worker from client 1"),
etap:is(is_process_alive(Worker2), true, "Client's 2 worker is alive."),
etap:is(stop_client(Client2), ok, "Second client stopped."),
stop_pool(Pool).
test_worker_dead_pool_full() ->
Pool = spawn_pool(),
Client1 = spawn_client(Pool),
Client2 = spawn_client(Pool),
Client3 = spawn_client(Pool),
etap:diag("Check that we can spawn the max number of connections."),
etap:is(ping_client(Client1), ok, "Client 1 started ok."),
etap:is(ping_client(Client2), ok, "Client 2 started ok."),
etap:is(ping_client(Client3), ok, "Client 3 started ok."),
Worker1 = get_client_worker(Client1, "1"),
Worker2 = get_client_worker(Client2, "2"),
Worker3 = get_client_worker(Client3, "3"),
etap:is(is_process_alive(Worker1), true, "Client's 1 worker is alive."),
etap:is(is_process_alive(Worker2), true, "Client's 2 worker is alive."),
etap:is(is_process_alive(Worker3), true, "Client's 3 worker is alive."),
etap:isnt(Worker1, Worker2, "Clients 1 and 2 got different workers."),
etap:isnt(Worker2, Worker3, "Clients 2 and 3 got different workers."),
etap:isnt(Worker1, Worker3, "Clients 1 and 3 got different workers."),
etap:diag("Check that client 4 blocks waiting for a worker."),
Client4 = spawn_client(Pool),
etap:is(ping_client(Client4), timeout, "Client 4 blocked while waiting."),
etap:diag("Kill client's 1 worker."),
etap:is(kill_client_worker(Client1), ok, "Killed client's 1 worker."),
etap:is(is_process_alive(Worker1), false, "Client's 1 worker process is dead."),
etap:diag("Check client 4 got unblocked after first worker's death"),
etap:is(ping_client(Client4), ok, "Client 4 not blocked anymore."),
Worker4 = get_client_worker(Client4, "4"),
etap:is(is_process_alive(Worker4), true, "Client's 4 worker is alive."),
etap:isnt(Worker4, Worker1, "Client 4 got a worker different from client 1."),
etap:isnt(Worker4, Worker2, "Client 4 got a worker different from client 2."),
etap:isnt(Worker4, Worker3, "Client 4 got a worker different from client 3."),
etap:diag("Check that stopping client 1 is a noop."),
etap:is(stop_client(Client1), ok, "First client stopped."),
etap:is(is_process_alive(Worker2), true, "Client's 2 worker still alive."),
etap:is(is_process_alive(Worker3), true, "Client's 3 worker still alive."),
etap:is(is_process_alive(Worker4), true, "Client's 4 worker still alive."),
etap:diag("Check that client 5 blocks waiting for a worker."),
Client5 = spawn_client(Pool),
etap:is(ping_client(Client5), timeout, "Client 5 blocked while waiting."),
etap:diag("Check that stopping client 2 gives up its worker."),
etap:is(stop_client(Client2), ok, "Second client stopped."),
etap:diag("Now check that client 5 has been unblocked."),
etap:is(ping_client(Client5), ok, "Client 5 was unblocked."),
Worker5 = get_client_worker(Client5, "5"),
etap:is(is_process_alive(Worker5), true, "Client's 5 worker is alive."),
etap:isnt(Worker5, Worker1, "Client 5 got a worker different from client 1."),
etap:is(Worker5, Worker2, "Client 5 got same worker as client 2."),
etap:isnt(Worker5, Worker3, "Client 5 got a worker different from client 3."),
etap:isnt(Worker5, Worker4, "Client 5 got a worker different from client 4."),
etap:is(is_process_alive(Worker3), true, "Client's 3 worker still alive."),
etap:is(is_process_alive(Worker4), true, "Client's 4 worker still alive."),
etap:is(is_process_alive(Worker5), true, "Client's 5 worker still alive."),
lists:foreach(fun(C) -> ok = stop_client(C) end, [Client3, Client4, Client5]),
stop_pool(Pool).
spawn_client(Pool) ->
Parent = self(),
Ref = make_ref(),
Pid = spawn(fun() ->
{ok, Worker} = couch_replicator_httpc_pool:get_worker(Pool),
loop(Parent, Ref, Worker, Pool)
end),
{Pid, Ref}.
ping_client({Pid, Ref}) ->
Pid ! ping,
receive
{pong, Ref} ->
ok
after 3000 ->
timeout
end.
get_client_worker({Pid, Ref}, ClientName) ->
Pid ! get_worker,
receive
{worker, Ref, Worker} ->
Worker
after 3000 ->
etap:bail("Timeout getting client " ++ ClientName ++ " worker.")
end.
stop_client({Pid, Ref}) ->
Pid ! stop,
receive
{stop, Ref} ->
ok
after 3000 ->
timeout
end.
kill_client_worker({Pid, Ref}) ->
Pid ! get_worker,
receive
{worker, Ref, Worker} ->
exit(Worker, kill),
ok
after 3000 ->
timeout
end.
loop(Parent, Ref, Worker, Pool) ->
receive
ping ->
Parent ! {pong, Ref},
loop(Parent, Ref, Worker, Pool);
get_worker ->
Parent ! {worker, Ref, Worker},
loop(Parent, Ref, Worker, Pool);
stop ->
couch_replicator_httpc_pool:release_worker(Pool, Worker),
Parent ! {stop, Ref}
end.
spawn_pool() ->
Host = couch_config:get("httpd", "bind_address", "127.0.0.1"),
Port = couch_config:get("httpd", "port", "5984"),
{ok, Pool} = couch_replicator_httpc_pool:start_link(
"http://" ++ Host ++ ":5984", [{max_connections, 3}]),
Pool.
stop_pool(Pool) ->
ok = couch_replicator_httpc_pool:stop(Pool).