%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% -------------------------------------------------- %% This file is provided 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. %% -------------------------------------------------- %% %% @author Ulf Wiger %% -module(gproc_tests). -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). -include_lib("stdlib/include/qlc.hrl"). -define(T_NAME, {n, l, {?MODULE, ?LINE, erlang:timestamp()}}). conf_test_() -> {foreach, fun() -> application:stop(gproc), application:unload(gproc) end, fun(_) -> application:stop(gproc) end, [?_test(t_server_opts()), ?_test(t_ets_opts())]}. t_server_opts() -> H = 10000, application:set_env(gproc, server_options, [{min_heap_size, H}]), ?assertMatch(ok, application:start(gproc)), {min_heap_size, H1} = process_info(whereis(gproc), min_heap_size), ?assert(is_integer(H1) andalso H1 > H). t_ets_opts() -> %% Cannot inspect the write_concurrency attribute on an ets table in %% any easy way, so trace on the ets:new/2 call and check the arguments. application:set_env(gproc, ets_options, [{write_concurrency, false}]), erlang:trace_pattern({ets,new, 2}, [{[gproc,'_'],[],[]}], [global]), erlang:trace(new, true, [call]), ?assert(ok == application:start(gproc)), erlang:trace(new, false, [call]), receive {trace,_,call,{ets,new,[gproc,Opts]}} -> ?assertMatch({write_concurrency, false}, lists:keyfind(write_concurrency,1,Opts)) after 3000 -> error(timeout) end. reg_test_() -> {setup, fun() -> application:start(gproc), application:start(mnesia) end, fun(_) -> application:stop(gproc), application:stop(mnesia) end, [ {spawn, ?_test(?debugVal(t_simple_reg()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_simple_reg_other()))} , ?_test(t_is_clean()) , ?_test(?debugVal(t_simple_ensure())) , ?_test(t_is_clean()) , ?_test(?debugVal(t_simple_ensure_other())) , ?_test(t_is_clean()) , ?_test(?debugVal(t_simple_ensure_prop())) , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_simple_reg_or_locate()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_reg_or_locate2()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_reg_or_locate3()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_simple_counter()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_simple_aggr_counter()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_awaited_aggr_counter()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_simple_resource_count()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_awaited_resource_count()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_resource_count_on_zero_send()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_update_counters()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_simple_prop()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_await()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_await_self()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_await_crash()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_simple_mreg()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_mreg_props()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_gproc_crash()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_cancel_wait_and_register()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_give_away_to_pid()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_give_away_to_self()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_give_away_badarg()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_give_away_to_unknown()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_give_away_and_back()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_select()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_select_count()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_qlc()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_qlc_dead()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_get_env()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_get_set_env()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_set_env()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_get_env_inherit()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_monitor()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_monitor_give_away()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_monitor_standby()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_monitor_follow()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_two_monitoring_processes_one_dies()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_monitor_demonitor()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_subscribe()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_gproc_info()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_simple_pool()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_pool_add_worker_size_1_no_auto_size()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_pool_add_worker_size_2_no_auto_size()))} , ?_test(t_is_clean()) , {spawn, ?_test(?debugVal(t_pool_round_robin_disconnect_worker()))} , ?_test(t_is_clean()) ]}. t_simple_reg() -> ?assert(gproc:reg({n,l,name}) =:= true), ?assert(gproc:where({n,l,name}) =:= self()), ?assert(gproc:unreg({n,l,name}) =:= true), ?assert(gproc:where({n,l,name}) =:= undefined). t_simple_reg_other() -> P = self(), P1 = spawn_link(fun() -> receive {P, goodbye} -> ok end end), Ref = erlang:monitor(process, P1), ?assert(gproc:reg_other({n,l,name}, P1) =:= true), ?assert(gproc:where({n,l,name}) =:= P1), ?assert(gproc:unreg_other({n,l,name}, P1) =:= true), ?assert(gproc:where({n,l,name}) =:= undefined), P1 ! {self(), goodbye}, receive {'DOWN', Ref, _, _, _} -> ok end. t_simple_ensure() -> P = self(), Key = {n,l,name}, ?assert(gproc:reg(Key) =:= true), ?assert(gproc:where(Key) =:= P), ?assert(gproc:ensure_reg(Key, new_val, [{a,1}]) =:= updated), ?assert(gproc:get_attributes(Key) =:= [{a,1}]), ?assert(gproc:unreg(Key) =:= true), ?assert(gproc:where(Key) =:= undefined). t_simple_ensure_other() -> P = self(), Key = {n,l,name}, P1 = spawn_link(fun() -> receive {P, goodbye} -> ok end end), Ref = erlang:monitor(process, P1), ?assert(gproc:reg_other(Key, P1) =:= true), ?assert(gproc:where(Key) =:= P1), ?assert(gproc:ensure_reg_other(Key, P1, new_val, [{a,1}]) =:= updated), ?assert(gproc:get_value(Key, P1) =:= new_val), ?assert(gproc:get_attributes(Key, P1) =:= [{a,1}]), ?assert(gproc:unreg_other(Key, P1) =:= true), ?assert(gproc:where({n,l,name}) =:= undefined), P1 ! {self(), goodbye}, receive {'DOWN', Ref, _, _, _} -> ok end. t_simple_ensure_prop() -> Key = {p,l,?LINE}, P = self(), Select = fun() -> gproc:select({l,p}, [{ {Key,'_','_'},[],['$_'] }]) end, ?assertMatch(new, gproc:ensure_reg(Key, first_val, [])), ?assertMatch([{Key,P,first_val}], Select()), ?assertMatch(updated, gproc:ensure_reg(Key, new_val, [{a,1}])), ?assertMatch([{Key,P,new_val}], Select()), ?assertMatch([{a,1}], gproc:get_attributes(Key)), ?assertMatch(true, gproc:unreg(Key)), ?assertMatch([], Select()). t_simple_reg_or_locate() -> P = self(), ?assertMatch({P, undefined}, gproc:reg_or_locate({n,l,name})), ?assertMatch(P, gproc:where({n,l,name})), ?assertMatch({P, my_val}, gproc:reg_or_locate({n,l,name2}, my_val)), ?assertMatch(my_val, gproc:get_value({n,l,name2})). t_reg_or_locate2() -> P = self(), {P1,R1} = spawn_monitor(fun() -> Ref = erlang:monitor(process, P), gproc:reg({n,l,foo}, the_value), P ! {self(), ok}, receive {'DOWN',Ref,_,_,_} -> ok end end), receive {P1, ok} -> ok end, ?assertMatch({P1, the_value}, gproc:reg_or_locate({n,l,foo})), exit(P1, kill), receive {'DOWN',R1,_,_,_} -> ok end. t_reg_or_locate3() -> P = self(), {P1, Value} = gproc:reg_or_locate( {n,l,foo}, the_value, fun() -> P ! {self(), ok}, receive {'DOWN',_Ref,_,_,_} -> ok end end), ?assert(P =/= P1), ?assert(Value =:= the_value), _Ref = erlang:monitor(process, P1), receive {P1, ok} -> ok; {'DOWN', _Ref, _, _, _Reason} -> ?assert(process_died_unexpectedly) end, ?assertMatch({P1, the_value}, gproc:reg_or_locate({n,l,foo})), exit(P1, kill), receive {'DOWN',_R1,_,_,_} -> ok end. t_simple_counter() -> ?assert(gproc:reg({c,l,c1}, 3) =:= true), ?assert(gproc:get_value({c,l,c1}) =:= 3), ?assert(gproc:update_counter({c,l,c1}, 4) =:= 7), ?assert(gproc:reset_counter({c,l,c1}) =:= {7, 3}). t_simple_aggr_counter() -> ?assert(gproc:reg({c,l,c1}, 3) =:= true), ?assert(gproc:reg({a,l,c1}) =:= true), ?assert(gproc:get_value({a,l,c1}) =:= 3), P = self(), P1 = spawn_link(fun() -> gproc:reg({c,l,c1}, 5), P ! {self(), ok}, receive {P, goodbye} -> ok end end), receive {P1, ok} -> ok end, ?assert(gproc:get_value({a,l,c1}) =:= 8), ?assert(gproc:update_counter({c,l,c1}, 4) =:= 7), ?assert(gproc:get_value({a,l,c1}) =:= 12), P1 ! {self(), goodbye}, R = erlang:monitor(process, P1), receive {'DOWN', R, _, _, _} -> gproc:audit_process(P1) end, ?assert(gproc:get_value({a,l,c1}) =:= 7). t_awaited_aggr_counter() -> ?assert(gproc:reg({c,l,c1}, 3) =:= true), gproc:nb_wait({a,l,c1}), ?assert(gproc:reg({a,l,c1}) =:= true), receive {gproc,_,registered,{{a,l,c1},_,_}} -> ok after 1000 -> error(timeout) end, ?assertMatch(3, gproc:get_value({a,l,c1})). t_simple_resource_count() -> ?assert(gproc:reg({r,l,r1}, 1) =:= true), ?assert(gproc:reg({rc,l,r1}) =:= true), ?assert(gproc:get_value({rc,l,r1}) =:= 1), P = self(), P1 = spawn_link(fun() -> gproc:reg({r,l,r1}, 1), P ! {self(), ok}, receive {P, goodbye} -> ok end end), receive {P1, ok} -> ok end, ?assert(gproc:get_value({rc,l,r1}) =:= 2), P1 ! {self(), goodbye}, R = erlang:monitor(process, P1), receive {'DOWN', R, _, _, _} -> gproc:audit_process(P1) end, ?assert(gproc:get_value({rc,l,r1}) =:= 1). t_awaited_resource_count() -> ?assert(gproc:reg({r,l,r1}, 3) =:= true), ?assert(gproc:reg({r,l,r2}, 3) =:= true), ?assert(gproc:reg({r,l,r3}, 3) =:= true), gproc:nb_wait({rc,l,r1}), ?assert(gproc:reg({rc,l,r1}) =:= true), receive {gproc,_,registered,{{rc,l,r1},_,_}} -> ok after 1000 -> error(timeout) end, ?assertMatch(1, gproc:get_value({rc,l,r1})). t_resource_count_on_zero_send() -> Me = self(), ?assertMatch(true, gproc:reg({p,l,myp})), ?assertMatch(true, gproc:reg({r,l,r1})), ?assertMatch(true, gproc:reg({rc,l,r1}, 1, [{on_zero, [{send, {p,l,myp}}]}])), ?assertMatch(1, gproc:get_value({rc,l,r1})), ?assertMatch(true, gproc:unreg({r,l,r1})), ?assertMatch(0, gproc:get_value({rc,l,r1})), receive {gproc, resource_on_zero, l, r1, Me} -> ok after 1000 -> error(timeout) end. t_update_counters() -> ?assert(gproc:reg({c,l,c1}, 3) =:= true), ?assert(gproc:reg({a,l,c1}) =:= true), ?assert(gproc:get_value({a,l,c1}) =:= 3), P = self(), P1 = spawn_link(fun() -> gproc:reg({c,l,c1}, 5), P ! {self(), ok}, receive {P, goodbye} -> ok end end), receive {P1, ok} -> ok end, ?assert(gproc:get_value({a,l,c1}) =:= 8), Me = self(), ?assertEqual([{{c,l,c1},Me,7}, {{c,l,c1},P1,8}], gproc:update_counters(l, [{{c,l,c1}, Me, 4}, {{c,l,c1}, P1, 3}])), ?assert(gproc:get_value({a,l,c1}) =:= 15), P1 ! {self(), goodbye}, R = erlang:monitor(process, P1), receive {'DOWN', R, _, _, _} -> gproc:audit_process(P1) end, ?assert(gproc:get_value({a,l,c1}) =:= 7). t_simple_prop() -> ?assert(gproc:reg({p,l,prop}) =:= true), ?assert(t_other_proc(fun() -> ?assert(gproc:reg({p,l,prop}) =:= true) end) =:= ok), ?assert(gproc:unreg({p,l,prop}) =:= true). t_other_proc(F) -> {_Pid,Ref} = spawn_monitor(fun() -> exit(F()) end), receive {'DOWN',Ref,_,_,R} -> R after 10000 -> erlang:error(timeout) end. t_await() -> Me = self(), Name = {n,l,t_await}, {_Pid,Ref} = spawn_monitor( fun() -> exit(?assert( gproc:await(Name) =:= {Me,val})) end), ?assert(gproc:reg(Name, val) =:= true), receive {'DOWN', Ref, _, _, R} -> ?assertEqual(R, ok) after 10000 -> erlang:error(timeout) end, ?assertMatch(Me, gproc:where(Name)), ok. t_await_self() -> Me = self(), Ref = gproc:nb_wait({n, l, t_await_self}), ?assert(gproc:reg({n, l, t_await_self}, some_value) =:= true), ?assertEqual(true, receive {gproc, Ref, R, Wh} -> {registered, {{n, l, t_await_self}, Me, some_value}} = {R, Wh}, true after 10000 -> timeout end). t_await_crash() -> Name = {n,l,{dummy,?LINE}}, {Pid,_} = spawn_regger(Name), ?assertEqual({Pid,undefined}, gproc:await(Name, 1000)), exit(Pid, kill), {NewPid,MRef} = spawn_regger(Name), ?assertEqual(false, is_process_alive(Pid)), ?assertEqual({NewPid,undefined}, gproc:await(Name, 1000)), exit(NewPid, kill), receive {'DOWN', MRef, _, _, _} -> ok end. spawn_regger(Name) -> spawn_monitor(fun() -> gproc:reg(Name), timer:sleep(60000) end). t_is_clean() -> sys:get_status(gproc), % in order to synch sys:get_status(gproc_monitor), T = ets:tab2list(gproc), Tm = ets:tab2list(gproc_monitor), ?assertMatch([], Tm), ?assertMatch([], T -- [{{whereis(gproc_monitor), l}}, {{self(), l}}]). t_simple_mreg() -> P = self(), ?assertEqual(true, gproc:mreg(n, l, [{foo, foo_val}, {bar, bar_val}])), ?assertEqual(P, gproc:where({n,l,foo})), ?assertEqual(P, gproc:where({n,l,bar})), ?assertEqual(true, gproc:munreg(n, l, [foo, bar])). t_mreg_props() -> P = self(), ?assertEqual(true, gproc:mreg(p, l, [{p, v}])), ?assertEqual(v, gproc:get_value({p,l,p})), %% Force a context switch, since gproc:monitor_me() is asynchronous _ = sys:get_status(gproc), {monitors, Mons} = process_info(whereis(gproc), monitors), ?assertEqual(true, lists:keymember(P, 2, Mons)). t_gproc_crash() -> P = spawn_helper(), ?assert(gproc:where({n,l,P}) =:= P), exit(whereis(gproc), kill), give_gproc_some_time(100), ?assert(whereis(gproc) =/= undefined), %% %% Check that the registration is still there using an ets:lookup(), %% Once we've killed the process, gproc will always return undefined %% if the process is not alive, regardless of whether the registration %% is still there. So, here, the lookup should find something... %% ?assert(ets:lookup(gproc,{{n,l,P},n}) =/= []), ?assert(gproc:where({n,l,P}) =:= P), exit(P, kill), %% ...and here, it shouldn't. %% (sleep for a while first to let gproc handle the EXIT give_gproc_some_time(10), ?assert(ets:lookup(gproc,{{n,l,P},n}) =:= []). t_cancel_wait_and_register() -> Alias = {n, l, foo}, Me = self(), P = spawn(fun() -> {'EXIT',_} = (catch gproc:await(Alias, 100)), ?assert(element(1,sys:get_status(gproc)) == status), Me ! {self(), go_ahead}, timer:sleep(infinity) end), receive {P, go_ahead} -> ?assertEqual(gproc:reg(Alias, undefined), true), exit(P, kill), timer:sleep(500), ?assert(element(1,sys:get_status(gproc)) == status) end. t_give_away_to_pid() -> From = {n, l, foo}, Me = self(), P = spawn_link(fun t_loop/0), ?assertEqual(true, gproc:reg(From, undefined)), ?assertEqual(Me, gproc:where(From)), ?assertEqual(P, gproc:give_away(From, P)), ?assertEqual(P, gproc:where(From)), ?assertEqual(ok, t_lcall(P, die)). t_give_away_to_self() -> From = {n, l, foo}, Me = self(), ?assertEqual(true, gproc:reg(From, undefined)), ?assertEqual(Me, gproc:where(From)), ?assertEqual(Me, gproc:give_away(From, Me)), ?assertEqual(Me, gproc:where(From)), ?assertEqual(true, gproc:unreg(From)). t_give_away_badarg() -> From = {n, l, foo}, Me = self(), ?assertEqual(undefined, gproc:where(From)), ?assertError(badarg, gproc:give_away(From, Me)). t_give_away_to_unknown() -> From = {n, l, foo}, Unknown = {n, l, unknown}, Me = self(), ?assertEqual(true, gproc:reg(From, undefined)), ?assertEqual(Me, gproc:where(From)), ?assertEqual(undefined, gproc:where(Unknown)), ?assertEqual(undefined, gproc:give_away(From, Unknown)), ?assertEqual(undefined, gproc:where(From)). t_give_away_and_back() -> From = {n, l, foo}, Me = self(), P = spawn_link(fun t_loop/0), ?assertEqual(true, gproc:reg(From, undefined)), ?assertEqual(Me, gproc:where(From)), ?assertEqual(P, gproc:give_away(From, P)), ?assertEqual(P, gproc:where(From)), ?assertEqual(ok, t_lcall(P, {give_away, From})), ?assertEqual(Me, gproc:where(From)), ?assertEqual(ok, t_lcall(P, die)). t_select() -> ?assertEqual(true, gproc:reg({n, l, {n,1}}, x)), ?assertEqual(true, gproc:reg({n, l, {n,2}}, y)), ?assertEqual(true, gproc:reg({p, l, {p,1}}, x)), ?assertEqual(true, gproc:reg({p, l, {p,2}}, y)), ?assertEqual(true, gproc:reg({c, l, {c,1}}, 1)), ?assertEqual(true, gproc:reg({a, l, {c,1}}, undefined)), %% local names ?assertEqual( [{{n,l,{n,1}},self(),x}, {{n,l,{n,2}},self(),y}], gproc:select( {local,names}, [{{{n,l,'_'},'_','_'},[],['$_']}])), %% mactch local names on value ?assertEqual( [{{n,l,{n,1}},self(),x}], gproc:select( {local,names}, [{{{n,l,'_'},'_',x},[],['$_']}])), %% match all on value ?assertEqual( [{{n,l,{n,1}},self(),x}, {{p,l,{p,1}},self(),x}], gproc:select( {all,all}, [{{{'_',l,'_'},'_',x},[],['$_']}])), %% match all on pid ?assertEqual( [{{a,l,{c,1}},self(),1}, {{c,l,{c,1}},self(),1}, {{n,l,{n,1}},self(),x}, {{n,l,{n,2}},self(),y}, {{p,l,{p,1}},self(),x}, {{p,l,{p,2}},self(),y} ], gproc:select( {all,all}, [{{'_',self(),'_'},[],['$_']}])). t_select_count() -> ?assertEqual(true, gproc:reg({n, l, {n,1}}, x)), ?assertEqual(true, gproc:reg({n, l, {n,2}}, y)), ?assertEqual(true, gproc:reg({p, l, {p,1}}, x)), ?assertEqual(true, gproc:reg({p, l, {p,2}}, y)), ?assertEqual(true, gproc:reg({c, l, {c,1}}, 1)), ?assertEqual(true, gproc:reg({a, l, {c,1}}, undefined)), %% local names ?assertEqual(2, gproc:select_count( {local,names}, [{{{n,l,'_'},'_','_'},[],[true]}])), %% mactch local names on value ?assertEqual(1, gproc:select_count( {local,names}, [{{{n,l,'_'},'_',x},[],[true]}])), %% match all on value ?assertEqual(2, gproc:select_count( {all,all}, [{{{'_',l,'_'},'_',x},[],[true]}])), %% match all on pid ?assertEqual(6, gproc:select_count( {all,all}, [{{'_',self(),'_'},[],[true]}])). t_qlc() -> ?assertEqual(true, gproc:reg({n, l, {n,1}}, x)), ?assertEqual(true, gproc:reg({n, l, {n,2}}, y)), ?assertEqual(true, gproc:reg({p, l, {p,1}}, x)), ?assertEqual(true, gproc:reg({p, l, {p,2}}, y)), ?assertEqual(true, gproc:reg({c, l, {c,1}}, 1)), ?assertEqual(true, gproc:reg({a, l, {c,1}}, undefined)), %% local names Exp1 = [{{n,l,{n,1}},self(),x}, {{n,l,{n,2}},self(),y}], ?assertEqual(Exp1, qlc:e(qlc:q([N || N <- gproc:table(names)]))), ?assertEqual(Exp1, qlc:e(qlc:q([N || {{n,l,_},_,_} = N <- gproc:table(names)]))), %% mactch local names on value Exp2 = [{{n,l,{n,1}},self(),x}], ?assertEqual(Exp2, qlc:e(qlc:q([N || {{n,l,_},_,x} = N <- gproc:table(names)]))), %% match all on value Exp3 = [{{n,l,{n,1}},self(),x}, {{p,l,{p,1}},self(),x}], ?assertEqual(Exp3, qlc:e(qlc:q([N || {_,_,x} = N <- gproc:table(all)]))), %% match all Exp4 = [{{a,l,{c,1}},self(),1}, {{c,l,{c,1}},self(),1}, {{n,l,{n,1}},self(),x}, {{n,l,{n,2}},self(),y}, {{p,l,{p,1}},self(),x}, {{p,l,{p,2}},self(),y} ], ?assertEqual(Exp4, qlc:e(qlc:q([X || X <- gproc:table(all)]))), %% match on pid ?assertEqual(Exp4, qlc:e(qlc:q([{K,P,V} || {K,P,V} <- gproc:table(all), P =:= self()]))), ?assertEqual(Exp4, qlc:e(qlc:q([{K,P,V} || {K,P,V} <- gproc:table(all), P == self()]))). t_qlc_dead() -> P0 = self(), ?assertEqual(true, gproc:reg({n, l, {n,1}}, x)), ?assertEqual(true, gproc:reg({p, l, {p,1}}, x)), P1 = spawn(fun() -> Ref = erlang:monitor(process, P0), gproc:reg({n, l, {n,2}}, y), gproc:reg({p, l, {p,2}}, y), P0 ! {self(), ok}, receive {_P, goodbye} -> ok; {'DOWN', Ref, _, _, _} -> ok end end), receive {P1, ok} -> ok end, %% now, suspend gproc so it doesn't do cleanup try sys:suspend(gproc), exit(P1, kill), %% local names Exp1 = [{{n,l,{n,1}},self(),x}], ?assertEqual(Exp1, qlc:e(qlc:q([N || N <- gproc:table(names, [check_pids])]))), ?assertEqual(Exp1, qlc:e(qlc:q([N || {{n,l,_},_,_} = N <- gproc:table(names, [check_pids])]))), %% match local names on value Exp2 = [{{n,l,{n,1}},self(),x}], ?assertEqual(Exp2, qlc:e(qlc:q([N || {{n,l,_},_,x} = N <- gproc:table(names, [check_pids])]))), ?assertEqual([], qlc:e(qlc:q([N || {{n,l,_},_,y} = N <- gproc:table(names, [check_pids])]))), %% match all on value Exp3 = [{{n,l,{n,1}},self(),x}, {{p,l,{p,1}},self(),x}], ?assertEqual(Exp3, qlc:e(qlc:q([N || {_,_,x} = N <- gproc:table(all, [check_pids])]))), ?assertEqual([], qlc:e(qlc:q([N || {_,_,y} = N <- gproc:table(all, [check_pids])]))), Exp3b = [{{n,l,{n,2}},P1,y}, {{p,l,{p,2}},P1,y}], ?assertEqual(Exp3b, qlc:e(qlc:q([N || {_,_,y} = N <- gproc:table(all)]))), %% match all Exp4 = [{{n,l,{n,1}},self(),x}, {{p,l,{p,1}},self(),x}], ?assertEqual(Exp4, qlc:e(qlc:q([X || X <- gproc:table(all, [check_pids])]))), %% match on pid ?assertEqual(Exp4, qlc:e(qlc:q([{K,P,V} || {K,P,V} <- gproc:table(all, [check_pids]), P =:= self()]))), ?assertEqual([], qlc:e(qlc:q([{K,P,V} || {K,P,V} <- gproc:table(all, [check_pids]), P =:= P1]))), Exp4b = [{{n,l,{n,2}},P1,y}, {{p,l,{p,2}},P1,y}], ?assertEqual(Exp4b, qlc:e(qlc:q([{K,P,V} || {K,P,V} <- gproc:table(all), P =:= P1]))) after sys:resume(gproc) end. t_get_env() -> ?assertEqual(ok, application:set_env(gproc, ssss, "s1")), ?assertEqual(true, os:putenv("SSSS", "s2")), ?assertEqual(true, os:putenv("TTTT", "s3")), ?assertEqual(ok, application:set_env(gproc, aaaa, a)), ?assertEqual(undefined, gproc:get_env(l, gproc, ssss, [])), %% ?assertEqual("s1", gproc:get_env(l, gproc, ssss, [app_env])), ?assertEqual("s2", gproc:get_env(l, gproc, ssss, [os_env])), ?assertEqual("s1", gproc:get_env(l, gproc, ssss, [app_env, os_env])), ?assertEqual("s3", gproc:get_env(l, gproc, ssss, [{os_env,"TTTT"}])), ?assertEqual("s4", gproc:get_env(l, gproc, ssss, [{default,"s4"}])), %% ?assertEqual({atomic,ok}, mnesia:create_table(t, [{ram_copies, [node()]}])), ?assertEqual(ok, mnesia:dirty_write({t, foo, bar})), ?assertEqual(bar, gproc:get_env(l, gproc, some_env, [{mnesia,transaction, {t, foo}, 3}])), ?assertEqual("erl", gproc:get_env(l, gproc, progname, [init_arg])). t_get_set_env() -> ?assertEqual(ok, application:set_env(gproc, aaaa, a)), ?assertEqual(a, gproc:get_set_env(l, gproc, aaaa, [app_env])), ?assertEqual(ok, application:set_env(gproc, aaaa, undefined)), ?assertEqual(a, gproc:get_env(l, gproc, aaaa, [error])). t_set_env() -> ?assertEqual(ok, application:set_env(gproc, aaaa, a)), ?assertEqual(a, gproc:get_set_env(l, gproc, aaaa, [app_env])), ?assertEqual(ok, application:set_env(gproc, aaaa, undefined)), ?assertEqual(b, gproc:set_env(l, gproc, aaaa, b, [app_env])), ?assertEqual({ok,b}, application:get_env(gproc, aaaa)), %% ?assertEqual(true, os:putenv("SSSS", "s0")), ?assertEqual("s0", gproc:get_env(l, gproc, ssss, [os_env])), ?assertEqual("s1", gproc:set_env(l, gproc, ssss, "s1", [os_env])), ?assertEqual("s1", os:getenv("SSSS")), ?assertEqual(true, os:putenv("SSSS", "s0")), ?assertEqual([{self(),"s1"}], gproc:lookup_values({p,l,{gproc_env,gproc,ssss}})), %% ?assertEqual({atomic,ok}, mnesia:create_table(t_set_env, [{ram_copies,[node()]}])), ?assertEqual(ok, mnesia:dirty_write({t_set_env, a, 1})), ?assertEqual(2, gproc:set_env(l, gproc, a, 2, [{mnesia,async_dirty, {t_set_env,a},3}])), ?assertEqual([{t_set_env,a,2}], mnesia:dirty_read({t_set_env,a})), %% non-existing mnesia obj ?assertEqual(3, gproc:set_env(l, gproc, b, 3, [{mnesia,async_dirty, {t_set_env,b},3}])), ?assertEqual([{t_set_env,b,3}], mnesia:dirty_read({t_set_env,b})). t_get_env_inherit() -> P = spawn_link(fun() -> ?assertEqual(bar, gproc:set_env(l,gproc,foo,bar,[])), gproc:reg({n,l,get_env_p}), t_loop() end), ?assertEqual({P,undefined}, gproc:await({n,l,get_env_p},1000)), ?assertEqual(bar, gproc:get_env(l, gproc, foo, [{inherit, P}])), ?assertEqual(bar, gproc:get_env(l, gproc, foo, [{inherit, {n,l,get_env_p}}])), ?assertEqual(ok, t_lcall(P, die)). %% What we test here is that we return the same current_function as the %% process_info() BIF. As we parse the backtrace dump, we check with some %% weirdly named functions. t_gproc_info() -> {A,B} = '-t1-'(), ?assertEqual(A,B), {C,D} = '\'t2'(), ?assertEqual(C,D), {E,F} = '\'t3\''(), ?assertEqual(E,F), {G,H} = t4(), ?assertEqual(G,H). '-t1-'() -> {_, I0} = process_info(self(), current_function), {_, I} = gproc:info(self(), current_function), {I0, I}. '\'t2'() -> {_, I0} = process_info(self(), current_function), {_, I} = gproc:info(self(), current_function), {I0, I}. '\'t3\''() -> {_, I0} = process_info(self(), current_function), {_, I} = gproc:info(self(), current_function), {I0, I}. t4() -> {_, I0} = process_info(self(), current_function), {_, I} = gproc:info(self(), current_function), {I0, I}. t_monitor() -> Me = self(), P = spawn_link(fun() -> gproc:reg({n,l,a}), Me ! continue, t_loop() end), receive continue -> ok end, Ref = gproc:monitor({n,l,a}), ?assertEqual(ok, t_lcall(P, die)), receive M -> ?assertEqual({gproc,unreg,Ref,{n,l,a}}, M) end. t_monitor_give_away() -> Me = self(), P = spawn_link(fun() -> gproc:reg({n,l,a}), Me ! continue, t_loop() end), receive continue -> ok end, Ref = gproc:monitor({n,l,a}), ?assertEqual(ok, t_lcall(P, {give_away, {n,l,a}})), receive M -> ?assertEqual({gproc,{migrated,Me},Ref,{n,l,a}}, M) end, ?assertEqual(ok, t_lcall(P, die)). t_monitor_standby() -> Me = self(), P = spawn(fun() -> gproc:reg({n,l,a}), Me ! continue, t_loop() end), receive continue -> ok end, Ref = gproc:monitor({n,l,a}, standby), exit(P, kill), receive M -> ?assertEqual({gproc,{failover,Me},Ref,{n,l,a}}, M) end, gproc:unreg({n,l,a}), ok. t_monitor_follow() -> Name = ?T_NAME, P1 = t_spawn(_Selective = true), Ref = t_call(P1, {apply, gproc, monitor, [Name, follow]}), {gproc,unreg,Ref,Name} = got_msg(P1), %% gproc_lib:dbg([gproc,gproc_lib]), P2 = t_spawn_reg(Name), {gproc,registered,Ref,Name} = got_msg(P1), exit(P2, kill), {gproc,unreg,Ref,Name} = got_msg(P1), P3 = t_spawn(true), Ref3 = t_call(P3, {apply, gproc, monitor, [Name, standby]}), {gproc,{failover,P3},Ref,Name} = got_msg(P1), {gproc,{failover,P3},Ref3,Name} = got_msg(P3), [exit(P,kill) || P <- [P1,P3]], ok. t_two_monitoring_processes_one_dies() -> Name = ?T_NAME, P = t_spawn_reg(Name), Ref = gproc:monitor(Name), {P2, R2} = spawn_monitor(fun() -> gproc:monitor(Name) end), receive {'DOWN', R2, process, P2, normal} -> ok end, exit(P, kill), receive M -> ?assertEqual({gproc, unreg, Ref, Name}, M) after 1000 -> error(timeout) end . t_monitor_demonitor() -> Name = ?T_NAME, P1 = t_spawn(Selective = true), Ref = t_call(P1, {apply, gproc, monitor, [Name, follow]}), {gproc, unreg, Ref, Name} = got_msg(P1), ok = t_call(P1, {apply, gproc, demonitor, [Name, Ref]}), P2 = t_spawn(Selective), Ref2 = t_call(P2, {apply, gproc, monitor, [Name, follow]}), {gproc, unreg, Ref2, Name} = got_msg(P2), P3 = t_spawn_reg(Name), {gproc, registered, Ref2, Name} = got_msg(P2), ok = gproc_test_lib:no_msg(P1, 300), [exit(P, kill) || P <- [P1, P2, P3]], ok. t_subscribe() -> Key = {n,l,a}, ?assertEqual(ok, gproc_monitor:subscribe(Key)), ?assertEqual({gproc_monitor, Key, undefined}, get_msg()), P = spawn_link(fun() -> gproc:reg({n,l,a}), t_loop() end), ?assertEqual({gproc_monitor, Key, P}, get_msg()), ?assertEqual(ok, t_lcall(P, {give_away, Key})), ?assertEqual({gproc_monitor, Key, {migrated,self()}}, get_msg()), gproc:give_away(Key, P), ?assertEqual({gproc_monitor, Key, {migrated,P}}, get_msg()), ?assertEqual(ok, t_lcall(P, die)), ?assertEqual({gproc_monitor, Key, undefined}, get_msg()), ?assertEqual(ok, gproc_monitor:unsubscribe(Key)). t_simple_pool()-> Key = p1w1, From = {n,l,Key}, _P = t_spawn_reg(From), %% create a new pool ?assertEqual(gproc_pool:new(p1), ok), %% add a worker to it ?assertEqual(gproc_pool:add_worker(p1,Key) , 1 ), ?assertEqual( length(gproc_pool:worker_pool(p1) ), 1), %% but it should not be active as yet ?assertEqual( length( gproc_pool:active_workers(p1)), 0), ?assert( gproc_pool:pick(p1) =:= false ), %% connect to make the worker active ?assertEqual(gproc_pool:connect_worker(p1,Key) , true ), %% it should be active now ?assertEqual( length( gproc_pool:active_workers(p1)), 1), ?assertEqual( gproc_pool:pick(p1) , {n,l,[gproc_pool,p1,1,Key]}), Ref = erlang:make_ref(), gproc:send(From, {self(), Ref, die}), receive {_, Ref, Returned} -> ?assertEqual(Returned, ok) after 1000 -> %% the next 3 tests should fail if the worker is still alive ok end, %% disconnect the worker from the pool. ?assertEqual(gproc_pool:disconnect_worker(p1,Key), true), %% there should be no active workers now ?assertEqual( length( gproc_pool:active_workers(p1)), 0), %% remove the worker from the pool ?assertEqual(gproc_pool:remove_worker(p1,Key), true), %% there should be no workers now %% NOTE: value of worker_pool seems to vary after removing workers %% sometimes [1,2] , sometimes [1], and then [] %% so relying on defined_workers ?assertEqual( length(gproc_pool:defined_workers(p1)), 0 ), ?assertEqual( length(gproc_pool:worker_pool(p1)), 0 ), ?assertEqual( false, gproc_test_lib:t_pool_contains_atleast(p1,1) ), %% should be able to delete the pool now ?assertEqual( gproc_pool:delete(p1), ok). %% verifying #167 - Removing a worker from a pool does not make room %% for a new worker (size was erroneously adjusted down, though auto_size = false) t_pool_add_worker_size_1_no_auto_size() -> ok = gproc_pool:new(p2, direct, [{size, 1}, {auto_size, false}]), [] = gproc_pool:defined_workers(p2), Pos1 = gproc_pool:add_worker(p2, worker1), [{worker1, Pos1, 0}] = gproc_pool:defined_workers(p2), io:fwrite("G = ~p~n", [ets:tab2list(gproc)]), true = gproc_pool:remove_worker(p2, worker1), [] = gproc_pool:defined_workers(p2), %% the pool seems to be empty Pos2 = gproc_pool:add_worker(p2, worker2), %% throws error:pool_full true = is_integer(Pos2), true = gproc_pool:force_delete(p2). t_pool_add_worker_size_2_no_auto_size() -> gproc_pool:new(p3, direct, [{size, 2}, {auto_size, false}]), gproc_pool:add_worker(p3, worker1), gproc_pool:add_worker(p3, worker2), gproc_pool:remove_worker(p3, worker2), gproc_pool:add_worker(p3, worker3), gproc_pool:force_delete(p3). t_pool_round_robin_disconnect_worker() -> gproc_pool:new(p), gproc_pool:add_worker(p, w1), gproc_pool:add_worker(p, w2), gproc_pool:add_worker(p, w3), Work = fun(W) -> spawn(fun() -> gproc_pool:connect_worker(p, W), fun F() -> F() end() end) end, %% disc first W1 = Work(w1), W2 = Work(w2), W3 = Work(w3), timer:sleep(10), ?assertEqual([W1, W2, W3], [ P || {_, P} <- gproc_pool:active_workers(p)]), ?assertEqual([W1, W2, W3, W1, W2, W3, W1, W2, W3], [gproc_pool:pick_worker(p) || _ <- lists:seq(1, 9)]), exit(W1, die), timer:sleep(10), ?assertEqual([W2, W3], [ P || {_, P} <- gproc_pool:active_workers(p)]), ?assertEqual([W2, W3, W2, W3, W2, W3, W2, W3, W2], [gproc_pool:pick_worker(p) || _ <- lists:seq(1, 9)]), %% disc middle W11 = Work(w1), timer:sleep(10), ?assertEqual([W11, W2, W3], [ P || {_, P} <- gproc_pool:active_workers(p)]), ?assertEqual([W3, W11, W2, W3, W11, W2, W3, W11, W2], [gproc_pool:pick_worker(p) || _ <- lists:seq(1, 9)]), exit(W2, die), timer:sleep(10), ?assertEqual([W11, W3], [ P || {_, P} <- gproc_pool:active_workers(p)]), ?assertEqual([W3, W11, W3, W11, W3, W11, W3, W11, W3], [gproc_pool:pick_worker(p) || _ <- lists:seq(1, 9)]), %% disc last W22 = Work(w2), timer:sleep(10), ?assertEqual([W11, W22, W3], [ P || {_, P} <- gproc_pool:active_workers(p)]), ?assertEqual([W11, W22, W3, W11, W22, W3, W11, W22, W3], [gproc_pool:pick_worker(p) || _ <- lists:seq(1, 9)]), exit(W3, die), timer:sleep(10), ?assertEqual([W11, W22], [ P || {_, P} <- gproc_pool:active_workers(p)]), ?assertEqual([W11, W22, W11, W22, W11, W22, W11, W22, W11], [gproc_pool:pick_worker(p) || _ <- lists:seq(1, 9)]), %% restore W33 = Work(w3), timer:sleep(10), ?assertEqual([W11, W22, W33], [ P || {_, P} <- gproc_pool:active_workers(p)]), ?assertEqual([W22, W33, W11, W22, W33, W11, W22, W33, W11], [gproc_pool:pick_worker(p) || _ <- lists:seq(1, 9)]), gproc_pool:force_delete(p). get_msg() -> receive M -> M after 1000 -> timeout end. %% t_spawn() -> gproc_test_lib:t_spawn(node()). t_spawn(Sel) -> gproc_test_lib:t_spawn(node(), Sel). t_spawn_reg(N) -> gproc_test_lib:t_spawn_reg(node(), N). t_call(P, Req) -> gproc_test_lib:t_call(P, Req). %% got_msg(P, M) -> gproc_test_lib:got_msg(P, M). got_msg(P) -> gproc_test_lib:got_msg(P). t_loop() -> receive {From, {give_away, Key}} -> ?assertEqual(From, gproc:give_away(Key, From)), From ! {self(), ok}, t_loop(); {From, die} -> From ! {self(), ok}; {From, {reg, Name}} -> From ! {self(), gproc:reg(Name,undefined)}, t_loop(); {From, {unreg, Name}} -> From ! {self(), gproc:unreg(Name)}, t_loop() end. t_lcall(P, Msg) -> P ! {self(), Msg}, receive {P, Reply} -> Reply end. spawn_helper() -> Parent = self(), P = spawn(fun() -> ?assert(gproc:reg({n,l,self()}) =:= true), Ref = erlang:monitor(process, Parent), Parent ! {ok,self()}, receive {'DOWN', Ref, _, _, _} -> ok end end), receive {ok,P} -> P end. give_gproc_some_time(T) -> timer:sleep(T), sys:get_status(gproc). -endif.