%% %% Licensed to the Apache Software Foundation (ASF) under one %% or more contributor license agreements. See the NOTICE file %% distributed with this work for additional information %% regarding copyright ownership. The ASF licenses this file %% to you 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. %% %% Tests the behavior of clients in the face of transport errors. %% Makes sure start, start_linked, and start_tethered work as expected. -module(test_tether). -compile(export_all). t() -> io:format("Beginning transport error test.~n"), Pid1 = erlang:spawn(?MODULE, t_sub, [2]), wait_for(Pid1), io:format("Beginning protocol error test.~n"), Pid2 = erlang:spawn(?MODULE, t_sub, [22]), wait_for(Pid2), ok. t_sub(Port) -> io:format("Starting.~n", []), register(tester, self()), Pid1 = erlang:spawn(?MODULE, test_start, [Port]), receive after 200 -> ok end, % Wait for completion. case is_up(Pid1) of true -> io:format("PASS. Unlinked owner still alive.~n"); false -> io:format("FAIL. Unlinked owner is dead.~n") end, Pid2 = erlang:spawn(?MODULE, test_linked, [Port]), receive after 200 -> ok end, % Wait for completion. case is_up(Pid2) of true -> io:format("FAIL. Linked owner still alive.~n"); false -> io:format("PASS. Linked owner is dead.~n") end, Pid3 = erlang:spawn(?MODULE, test_tethered, [Port]), receive after 200 -> ok end, % Wait for completion. case is_up(Pid3) of true -> io:format("PASS. Tethered owner still alive.~n"); false -> io:format("FAIL. Tethered owner is dead.~n") end, check_extras(3). is_up(Pid) -> MonitorRef = erlang:monitor(process, Pid), receive {'DOWN', MonitorRef, process, Pid, _Info} -> false after 50 -> erlang:demonitor(MonitorRef), true end. wait_for(Pid) -> MonitorRef = erlang:monitor(process, Pid), receive {'DOWN', MonitorRef, process, Pid, _Info} -> ok end. check_extras(0) -> ok; check_extras(N) -> receive {client, Type, Pid} -> case {Type, is_up(Pid)} of {unlinked, true} -> io:format("PASS. Unlinked client still alive.~n"); {unlinked, false} -> io:format("FAIL. Unlinked client dead.~n"); {linked, true} -> io:format("FAIL. Linked client still alive.~n"); {linked, false} -> io:format("PASS. Linked client dead.~n"); {tethered, true} -> io:format("FAIL. Tethered client still alive.~n"); {tethered, false} -> io:format("PASS. Tethered client dead.~n") end, check_extras(N-1) after 500 -> io:format("FAIL. Expected ~p more clients.~n", [N]) end. make_thrift_client(Opts) -> thrift_client:start(fun()->ok end, thriftTest_thrift, Opts). make_protocol_factory(Port) -> {ok, TransportFactory} = thrift_socket_transport:new_transport_factory( "127.0.0.1", Port, []), {ok, ProtocolFactory} = thrift_binary_protocol:new_protocol_factory( TransportFactory, []), ProtocolFactory. test_start(Port) -> {ok, Client1} = make_thrift_client([{connect, false}]), tester ! {client, unlinked, Client1}, {ok, Client2} = make_thrift_client([{connect, false}]), io:format("PASS. Unlinked clients created.~n"), try gen_server:call(Client2, {connect, make_protocol_factory(Port)}), thrift_client:call(Client2, testVoid, []), io:format("FAIL. Unlinked client connected and called.~n", []) catch Kind:Info -> io:format("PASS. Caught unlinked error. ~p:~p~n", [Kind, Info]) end, receive after 100 -> io:format("PASS. Still alive after unlinked death.~n"), %% Hang around a little longer so our parent can verify. receive after 200 -> ok end end, %% Exit abnormally to not kill our unlinked extra client. exit(die). test_linked(Port) -> {ok, Client1} = make_thrift_client([{connect, false}, {monitor, link}]), tester ! {client, linked, Client1}, {ok, Client2} = make_thrift_client([{connect, false}, {monitor, link}]), io:format("PASS. Linked clients created.~n"), try gen_server:call(Client2, {connect, make_protocol_factory(Port)}), thrift_client:call(Client2, testVoid, []), io:format("FAIL. Linked client connected and called.~n", []) catch Kind:Info -> io:format("FAIL. Caught linked error. ~p:~p~n", [Kind, Info]) end, receive after 100 -> io:format("FAIL. Still alive after linked death.~n"), % Hang around a little longer so our parent can verify. receive after 200 -> ok end end, %% Exit abnormally to kill our linked extra client. %% But we should never get here. exit(die). test_tethered(Port) -> {ok, Client1} = make_thrift_client([{connect, false}, {monitor, tether}]), tester ! {client, tethered, Client1}, {ok, Client2} = make_thrift_client([{connect, false}, {monitor, tether}]), io:format("PASS. Tethered clients created.~n"), try gen_server:call(Client2, {connect, make_protocol_factory(Port)}), thrift_client:call(Client2, testVoid, []), io:format("FAIL. Tethered client connected and called.~n", []) catch Kind:Info -> io:format("PASS. Caught tethered error. ~p:~p~n", [Kind, Info]) end, receive after 100 -> io:format("PASS. Still alive after tethered death.~n"), % Hang around a little longer so our parent can verify. receive after 200 -> ok end end, %% Exit abnormally to kill our tethered extra client. exit(die).