syn_groups_SUITE.erl 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693
  1. %% ==========================================================================================================
  2. %% Syn - A global Process Registry and Process Group manager.
  3. %%
  4. %% The MIT License (MIT)
  5. %%
  6. %% Copyright (c) 2016 Roberto Ostinelli <roberto@ostinelli.net> and Neato Robotics, Inc.
  7. %%
  8. %% Permission is hereby granted, free of charge, to any person obtaining a copy
  9. %% of this software and associated documentation files (the "Software"), to deal
  10. %% in the Software without restriction, including without limitation the rights
  11. %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12. %% copies of the Software, and to permit persons to whom the Software is
  13. %% furnished to do so, subject to the following conditions:
  14. %%
  15. %% The above copyright notice and this permission notice shall be included in
  16. %% all copies or substantial portions of the Software.
  17. %%
  18. %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21. %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24. %% THE SOFTWARE.
  25. %% ==========================================================================================================
  26. -module(syn_groups_SUITE).
  27. %% callbacks
  28. -export([all/0]).
  29. -export([init_per_suite/1, end_per_suite/1]).
  30. -export([groups/0, init_per_group/2, end_per_group/2]).
  31. -export([init_per_testcase/2, end_per_testcase/2]).
  32. %% tests
  33. -export([
  34. single_node_join/1,
  35. single_node_leave/1,
  36. single_node_kill/1,
  37. single_node_leave_and_kill_multi_groups/1,
  38. single_node_publish/1,
  39. single_node_multi_call/1,
  40. single_node_multi_call_when_recipient_crashes/1,
  41. single_node_meta/1,
  42. single_node_callback_on_process_exit/1,
  43. single_node_all_keys/1
  44. ]).
  45. -export([
  46. two_nodes_kill/1,
  47. two_nodes_publish/1,
  48. two_nodes_multi_call/1,
  49. two_nodes_local_members/1,
  50. two_nodes_local_publish/1
  51. ]).
  52. %% internals
  53. -export([recipient_loop/1]).
  54. -export([called_loop/1, called_loop_that_crashes/1]).
  55. -export([process_groups_process_exit_callback_dummy/4]).
  56. %% include
  57. -include_lib("common_test/include/ct.hrl").
  58. %% ===================================================================
  59. %% Callbacks
  60. %% ===================================================================
  61. %% -------------------------------------------------------------------
  62. %% Function: all() -> GroupsAndTestCases | {skip,Reason}
  63. %% GroupsAndTestCases = [{group,GroupName} | TestCase]
  64. %% GroupName = atom()
  65. %% TestCase = atom()
  66. %% Reason = term()
  67. %% -------------------------------------------------------------------
  68. all() ->
  69. [
  70. {group, single_node_process_groups},
  71. {group, two_nodes_process_groups}
  72. ].
  73. %% -------------------------------------------------------------------
  74. %% Function: groups() -> [Group]
  75. %% Group = {GroupName,Properties,GroupsAndTestCases}
  76. %% GroupName = atom()
  77. %% Properties = [parallel | sequence | Shuffle | {RepeatType,N}]
  78. %% GroupsAndTestCases = [Group | {group,GroupName} | TestCase]
  79. %% TestCase = atom()
  80. %% Shuffle = shuffle | {shuffle,{integer(),integer(),integer()}}
  81. %% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |
  82. %% repeat_until_any_ok | repeat_until_any_fail
  83. %% N = integer() | forever
  84. %% -------------------------------------------------------------------
  85. groups() ->
  86. [
  87. {single_node_process_groups, [shuffle], [
  88. single_node_join,
  89. single_node_leave,
  90. single_node_kill,
  91. single_node_leave_and_kill_multi_groups,
  92. single_node_publish,
  93. single_node_multi_call,
  94. single_node_multi_call_when_recipient_crashes,
  95. single_node_meta,
  96. single_node_callback_on_process_exit,
  97. single_node_all_keys
  98. ]},
  99. {two_nodes_process_groups, [shuffle], [
  100. two_nodes_kill,
  101. two_nodes_publish,
  102. two_nodes_multi_call,
  103. two_nodes_local_members,
  104. two_nodes_local_publish
  105. ]}
  106. ].
  107. %% -------------------------------------------------------------------
  108. %% Function: init_per_suite(Config0) ->
  109. %% Config1 | {skip,Reason} |
  110. %% {skip_and_save,Reason,Config1}
  111. %% Config0 = Config1 = [tuple()]
  112. %% Reason = term()
  113. %% -------------------------------------------------------------------
  114. init_per_suite(Config) ->
  115. %% config
  116. [
  117. {slave_node_short_name, syn_slave}
  118. | Config
  119. ].
  120. %% -------------------------------------------------------------------
  121. %% Function: end_per_suite(Config0) -> void() | {save_config,Config1}
  122. %% Config0 = Config1 = [tuple()]
  123. %% -------------------------------------------------------------------
  124. end_per_suite(_Config) -> ok.
  125. %% -------------------------------------------------------------------
  126. %% Function: init_per_group(GroupName, Config0) ->
  127. %% Config1 | {skip,Reason} |
  128. %% {skip_and_save,Reason,Config1}
  129. %% GroupName = atom()
  130. %% Config0 = Config1 = [tuple()]
  131. %% Reason = term()
  132. %% -------------------------------------------------------------------
  133. init_per_group(two_nodes_process_groups, Config) ->
  134. %% start slave
  135. SlaveNodeShortName = proplists:get_value(slave_node_short_name, Config),
  136. {ok, SlaveNode} = syn_test_suite_helper:start_slave(SlaveNodeShortName),
  137. %% config
  138. [
  139. {slave_node, SlaveNode}
  140. | Config
  141. ];
  142. init_per_group(_GroupName, Config) -> Config.
  143. %% -------------------------------------------------------------------
  144. %% Function: end_per_group(GroupName, Config0) ->
  145. %% void() | {save_config,Config1}
  146. %% GroupName = atom()
  147. %% Config0 = Config1 = [tuple()]
  148. %% -------------------------------------------------------------------
  149. end_per_group(two_nodes_process_groups, Config) ->
  150. %% get slave node name
  151. SlaveNodeShortName = proplists:get_value(slave_node_short_name, Config),
  152. %% stop slave
  153. syn_test_suite_helper:stop_slave(SlaveNodeShortName);
  154. end_per_group(_GroupName, _Config) ->
  155. ok.
  156. % ----------------------------------------------------------------------------------------------------------
  157. % Function: init_per_testcase(TestCase, Config0) ->
  158. % Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
  159. % TestCase = atom()
  160. % Config0 = Config1 = [tuple()]
  161. % Reason = term()
  162. % ----------------------------------------------------------------------------------------------------------
  163. init_per_testcase(_TestCase, Config) ->
  164. Config.
  165. % ----------------------------------------------------------------------------------------------------------
  166. % Function: end_per_testcase(TestCase, Config0) ->
  167. % void() | {save_config,Config1} | {fail,Reason}
  168. % TestCase = atom()
  169. % Config0 = Config1 = [tuple()]
  170. % Reason = term()
  171. % ----------------------------------------------------------------------------------------------------------
  172. end_per_testcase(_TestCase, Config) ->
  173. %% get slave
  174. SlaveNode = proplists:get_value(slave_node, Config),
  175. syn_test_suite_helper:clean_after_test(SlaveNode).
  176. %% ===================================================================
  177. %% Tests
  178. %% ===================================================================
  179. single_node_join(_Config) ->
  180. %% set schema location
  181. application:set_env(mnesia, schema_location, ram),
  182. %% start
  183. ok = syn:start(),
  184. ok = syn:init(),
  185. %% start process
  186. Pid = syn_test_suite_helper:start_process(),
  187. %% retrieve
  188. [] = syn:get_members(<<"my group">>),
  189. false = syn:member(Pid, <<"my group">>),
  190. %% join
  191. ok = syn:join(<<"my group">>, Pid),
  192. %% allow to rejoin
  193. ok = syn:join(<<"my group">>, Pid),
  194. %% retrieve
  195. [Pid] = syn:get_members(<<"my group">>),
  196. true = syn:member(Pid, <<"my group">>),
  197. %% kill process
  198. syn_test_suite_helper:kill_process(Pid).
  199. single_node_leave(_Config) ->
  200. %% set schema location
  201. application:set_env(mnesia, schema_location, ram),
  202. %% start
  203. ok = syn:start(),
  204. ok = syn:init(),
  205. %% start process
  206. Pid = syn_test_suite_helper:start_process(),
  207. %% retrieve
  208. [] = syn:get_members(<<"my group">>),
  209. false = syn:member(Pid, <<"my group">>),
  210. %% leave before join
  211. {error, pid_not_in_group} = syn:leave(<<"my group">>, Pid),
  212. %% join
  213. ok = syn:join(<<"my group">>, Pid),
  214. %% retrieve
  215. [Pid] = syn:get_members(<<"my group">>),
  216. true = syn:member(Pid, <<"my group">>),
  217. %% leave
  218. ok = syn:leave(<<"my group">>, Pid),
  219. %% retrieve
  220. [] = syn:get_members(<<"my group">>),
  221. false = syn:member(Pid, <<"my group">>),
  222. %% kill process
  223. syn_test_suite_helper:kill_process(Pid).
  224. single_node_kill(_Config) ->
  225. %% set schema location
  226. application:set_env(mnesia, schema_location, ram),
  227. %% start
  228. ok = syn:start(),
  229. ok = syn:init(),
  230. %% start process
  231. Pid = syn_test_suite_helper:start_process(),
  232. %% retrieve
  233. [] = syn:get_members(<<"my group 1">>),
  234. [] = syn:get_members(<<"my group 2">>),
  235. false = syn:member(Pid, <<"my group 1">>),
  236. false = syn:member(Pid, <<"my group 2">>),
  237. %% join
  238. ok = syn:join(<<"my group 1">>, Pid),
  239. ok = syn:join(<<"my group 2">>, Pid),
  240. %% retrieve
  241. [Pid] = syn:get_members(<<"my group 1">>),
  242. [Pid] = syn:get_members(<<"my group 2">>),
  243. true = syn:member(Pid, <<"my group 1">>),
  244. true = syn:member(Pid, <<"my group 2">>),
  245. %% kill process
  246. syn_test_suite_helper:kill_process(Pid),
  247. timer:sleep(100),
  248. %% retrieve
  249. [] = syn:get_members(<<"my group 1">>),
  250. [] = syn:get_members(<<"my group 2">>),
  251. false = syn:member(Pid, <<"my group 1">>),
  252. false = syn:member(Pid, <<"my group 2">>).
  253. single_node_leave_and_kill_multi_groups(_Config) ->
  254. %% set schema location
  255. application:set_env(mnesia, schema_location, ram),
  256. %% start
  257. ok = syn:start(),
  258. ok = syn:init(),
  259. %% start process
  260. Pid = syn_test_suite_helper:start_process(),
  261. %% retrieve
  262. [] = syn:get_members(<<"my group 1">>),
  263. [] = syn:get_members(<<"my group 2">>),
  264. false = syn:member(Pid, <<"my group 1">>),
  265. false = syn:member(Pid, <<"my group 2">>),
  266. %% join
  267. ok = syn:join(<<"my group 1">>, Pid),
  268. ok = syn:join(<<"my group 2">>, Pid),
  269. %% retrieve
  270. [Pid] = syn:get_members(<<"my group 1">>),
  271. [Pid] = syn:get_members(<<"my group 2">>),
  272. true = syn:member(Pid, <<"my group 1">>),
  273. true = syn:member(Pid, <<"my group 2">>),
  274. %% leave group 1
  275. ok = syn:leave(<<"my group 1">>, Pid),
  276. %% retrieve
  277. [] = syn:get_members(<<"my group 1">>),
  278. [Pid] = syn:get_members(<<"my group 2">>),
  279. false = syn:member(Pid, <<"my group 1">>),
  280. true = syn:member(Pid, <<"my group 2">>),
  281. %% kill process
  282. syn_test_suite_helper:kill_process(Pid),
  283. timer:sleep(100),
  284. %% retrieve
  285. [] = syn:get_members(<<"my group 1">>),
  286. [] = syn:get_members(<<"my group 2">>),
  287. false = syn:member(Pid, <<"my group 1">>),
  288. false = syn:member(Pid, <<"my group 2">>).
  289. single_node_publish(_Config) ->
  290. %% set schema location
  291. application:set_env(mnesia, schema_location, ram),
  292. %% start
  293. ok = syn:start(),
  294. ok = syn:init(),
  295. %% start processes
  296. ResultPid = self(),
  297. F = fun() -> recipient_loop(ResultPid) end,
  298. Pid1 = syn_test_suite_helper:start_process(F),
  299. Pid2 = syn_test_suite_helper:start_process(F),
  300. %% join
  301. ok = syn:join(<<"my group">>, Pid1),
  302. ok = syn:join(<<"my group">>, Pid2),
  303. %% publish
  304. {ok, 2} = syn:publish(<<"my group">>, {test, message}),
  305. %% check publish was received
  306. receive
  307. {received, Pid1, {test, message}} -> ok
  308. after 2000 ->
  309. ok = published_message_was_not_received_by_pid1
  310. end,
  311. receive
  312. {received, Pid2, {test, message}} -> ok
  313. after 2000 ->
  314. ok = published_message_was_not_received_by_pid2
  315. end,
  316. %% kill processes
  317. syn_test_suite_helper:kill_process(Pid1),
  318. syn_test_suite_helper:kill_process(Pid2).
  319. single_node_multi_call(_Config) ->
  320. %% set schema location
  321. application:set_env(mnesia, schema_location, ram),
  322. %% start
  323. ok = syn:start(),
  324. ok = syn:init(),
  325. %% start processes
  326. Pid1 = syn_test_suite_helper:start_process(fun() -> called_loop(pid1) end),
  327. Pid2 = syn_test_suite_helper:start_process(fun() -> called_loop(pid2) end),
  328. PidUnresponsive = syn_test_suite_helper:start_process(),
  329. %% register
  330. ok = syn:join(<<"my group">>, Pid1),
  331. ok = syn:join(<<"my group">>, Pid2),
  332. ok = syn:join(<<"my group">>, PidUnresponsive),
  333. %% call
  334. {Replies, BadPids} = syn:multi_call(<<"my group">>, get_pid_name),
  335. %% check responses
  336. 2 = length(Replies),
  337. pid1 = proplists:get_value(Pid1, Replies),
  338. pid2 = proplists:get_value(Pid2, Replies),
  339. [PidUnresponsive] = BadPids,
  340. %% kill processes
  341. syn_test_suite_helper:kill_process(Pid1),
  342. syn_test_suite_helper:kill_process(Pid2),
  343. syn_test_suite_helper:kill_process(PidUnresponsive).
  344. single_node_multi_call_when_recipient_crashes(_Config) ->
  345. %% set schema location
  346. application:set_env(mnesia, schema_location, ram),
  347. %% start
  348. ok = syn:start(),
  349. ok = syn:init(),
  350. %% start processes
  351. Pid1 = syn_test_suite_helper:start_process(fun() -> called_loop(pid1) end),
  352. Pid2 = syn_test_suite_helper:start_process(fun() -> called_loop(pid2) end),
  353. PidCrashes = syn_test_suite_helper:start_process(fun() -> called_loop_that_crashes(pid_crashes) end),
  354. %% register
  355. ok = syn:join(<<"my group">>, Pid1),
  356. ok = syn:join(<<"my group">>, Pid2),
  357. ok = syn:join(<<"my group">>, PidCrashes),
  358. %% call
  359. {Time, {Replies, BadPids}} = timer:tc(syn, multi_call, [<<"my group">>, get_pid_name]),
  360. %% check that pid2 was monitored, no need to wait for timeout
  361. true = Time / 1000 < 1000,
  362. %% check responses
  363. 2 = length(Replies),
  364. pid1 = proplists:get_value(Pid1, Replies),
  365. pid2 = proplists:get_value(Pid2, Replies),
  366. [PidCrashes] = BadPids,
  367. %% kill processes
  368. syn_test_suite_helper:kill_process(Pid1),
  369. syn_test_suite_helper:kill_process(Pid2).
  370. single_node_meta(_Config) ->
  371. %% set schema location
  372. application:set_env(mnesia, schema_location, ram),
  373. %% start
  374. ok = syn:start(),
  375. ok = syn:init(),
  376. %% start process
  377. Pid = syn_test_suite_helper:start_process(),
  378. %% retrieve
  379. [] = syn:get_members(<<"my group">>, with_meta),
  380. false = syn:member(Pid, <<"my group">>),
  381. %% join
  382. ok = syn:join(<<"my group">>, Pid, {some, meta}),
  383. %% retrieve
  384. [{Pid, {some, meta}}] = syn:get_members(<<"my group">>, with_meta),
  385. %% allow to rejoin to update meta
  386. ok = syn:join(<<"my group">>, Pid, {updated, meta}),
  387. %% retrieve
  388. [{Pid, {updated, meta}}] = syn:get_members(<<"my group">>, with_meta),
  389. %% leave
  390. ok = syn:leave(<<"my group">>, Pid),
  391. %% retrieve
  392. [] = syn:get_members(<<"my group">>),
  393. false = syn:member(Pid, <<"my group">>),
  394. %% kill process
  395. syn_test_suite_helper:kill_process(Pid).
  396. single_node_callback_on_process_exit(_Config) ->
  397. CurrentNode = node(),
  398. %% set schema location
  399. application:set_env(mnesia, schema_location, ram),
  400. %% load configuration variables from syn-test.config => this defines the callback
  401. syn_test_suite_helper:set_environment_variables(),
  402. %% start
  403. ok = syn:start(),
  404. ok = syn:init(),
  405. %% register global process
  406. ResultPid = self(),
  407. global:register_name(syn_process_groups_SUITE_result, ResultPid),
  408. %% start process
  409. Pid = syn_test_suite_helper:start_process(),
  410. %% register
  411. ok = syn:join(<<"my group">>, Pid, {some, meta, 1}),
  412. ok = syn:join(<<"my other group">>, Pid, {some, meta, 2}),
  413. %% kill process
  414. syn_test_suite_helper:kill_process(Pid),
  415. %% check callback were triggered
  416. receive
  417. {exited, CurrentNode, <<"my group">>, Pid, {some, meta, 1}, killed} -> ok
  418. after 2000 ->
  419. ok = process_groups_exit_callback_was_not_called_from_local_node
  420. end,
  421. receive
  422. {exited, CurrentNode, <<"my other group">>, Pid, {some, meta, 2}, killed} -> ok
  423. after 2000 ->
  424. ok = process_groups_exit_callback_was_not_called_from_local_node
  425. end,
  426. %% unregister
  427. global:unregister_name(syn_process_groups_SUITE_result).
  428. %% covering bug <https://github.com/ostinelli/syn/issues/32>
  429. single_node_all_keys(_Config) ->
  430. %% set schema location
  431. application:set_env(mnesia, schema_location, ram),
  432. %% start
  433. ok = syn:start(),
  434. ok = syn:init(),
  435. %% start process
  436. Pid = syn_test_suite_helper:start_process(),
  437. %% join with complex names
  438. GroupNames = [
  439. "mygroup",
  440. <<"mygroup">>,
  441. mygroup,
  442. {mygroup},
  443. {mygroup, 12345},
  444. {mygroup, {other, <<"mygroup">>}},
  445. [mygroup, {other, <<"mygroup">>}],
  446. {mygroup, {other, <<"mygroup">>}, [mygroup, {other, <<"mygroup">>}]}
  447. ],
  448. F = fun(GroupName) ->
  449. ok = syn:join(GroupName, Pid),
  450. %% retrieve
  451. [Pid] = syn:get_members(GroupName),
  452. true = syn:member(Pid, GroupName)
  453. end,
  454. lists:foreach(F, GroupNames),
  455. %% kill process
  456. syn_test_suite_helper:kill_process(Pid).
  457. two_nodes_kill(Config) ->
  458. %% get slave
  459. SlaveNode = proplists:get_value(slave_node, Config),
  460. %% set schema location
  461. application:set_env(mnesia, schema_location, ram),
  462. rpc:call(SlaveNode, mnesia, schema_location, [ram]),
  463. %% start
  464. ok = syn:start(),
  465. ok = syn:init(),
  466. ok = rpc:call(SlaveNode, syn, start, []),
  467. ok = rpc:call(SlaveNode, syn, init, []),
  468. timer:sleep(100),
  469. %% start processes
  470. PidLocal = syn_test_suite_helper:start_process(),
  471. PidSlave = syn_test_suite_helper:start_process(SlaveNode),
  472. %% retrieve
  473. [] = syn:get_members(<<"my group">>),
  474. false = syn:member(PidLocal, <<"my group">>),
  475. false = syn:member(PidSlave, <<"my group">>),
  476. [] = rpc:call(SlaveNode, syn, get_members, [<<"my group">>]),
  477. false = rpc:call(SlaveNode, syn, member, [PidLocal, <<"my group">>]),
  478. false = rpc:call(SlaveNode, syn, member, [PidSlave, <<"my group">>]),
  479. %% register
  480. ok = syn:join(<<"my group">>, PidSlave),
  481. ok = rpc:call(SlaveNode, syn, join, [<<"my group">>, PidLocal]),
  482. %% retrieve, pid should have the same order in all nodes
  483. [PidSlave, PidLocal] = syn:get_members(<<"my group">>),
  484. [PidSlave, PidLocal] = rpc:call(SlaveNode, syn, get_members, [<<"my group">>]),
  485. %% kill processes
  486. syn_test_suite_helper:kill_process(PidLocal),
  487. syn_test_suite_helper:kill_process(PidSlave),
  488. timer:sleep(100),
  489. %% retrieve
  490. [] = syn:get_members(<<"my group">>),
  491. false = syn:member(PidLocal, <<"my group">>),
  492. false = syn:member(PidSlave, <<"my group">>),
  493. [] = rpc:call(SlaveNode, syn, get_members, [<<"my group">>]),
  494. false = rpc:call(SlaveNode, syn, member, [PidLocal, <<"my group">>]),
  495. false = rpc:call(SlaveNode, syn, member, [PidSlave, <<"my group">>]).
  496. two_nodes_publish(Config) ->
  497. %% get slave
  498. SlaveNode = proplists:get_value(slave_node, Config),
  499. %% set schema location
  500. application:set_env(mnesia, schema_location, ram),
  501. rpc:call(SlaveNode, mnesia, schema_location, [ram]),
  502. %% start
  503. ok = syn:start(),
  504. ok = syn:init(),
  505. ok = rpc:call(SlaveNode, syn, start, []),
  506. ok = rpc:call(SlaveNode, syn, init, []),
  507. timer:sleep(100),
  508. %% start process
  509. ResultPid = self(),
  510. F = fun() -> recipient_loop(ResultPid) end,
  511. PidLocal = syn_test_suite_helper:start_process(F),
  512. PidSlave = syn_test_suite_helper:start_process(SlaveNode, F),
  513. %% register
  514. ok = syn:join(<<"my group">>, PidSlave),
  515. ok = rpc:call(SlaveNode, syn, join, [<<"my group">>, PidLocal]),
  516. %% publish
  517. syn:publish(<<"my group">>, {test, message}),
  518. %% check publish was received
  519. receive
  520. {received, PidLocal, {test, message}} -> ok
  521. after 2000 ->
  522. ok = published_message_was_not_received_by_pidlocal
  523. end,
  524. receive
  525. {received, PidSlave, {test, message}} -> ok
  526. after 2000 ->
  527. ok = published_message_was_not_received_by_pidslave
  528. end,
  529. %% kill processes
  530. syn_test_suite_helper:kill_process(PidLocal),
  531. syn_test_suite_helper:kill_process(PidSlave).
  532. two_nodes_multi_call(Config) ->
  533. %% get slave
  534. SlaveNode = proplists:get_value(slave_node, Config),
  535. %% set schema location
  536. application:set_env(mnesia, schema_location, ram),
  537. rpc:call(SlaveNode, mnesia, schema_location, [ram]),
  538. %% start
  539. ok = syn:start(),
  540. ok = syn:init(),
  541. ok = rpc:call(SlaveNode, syn, start, []),
  542. ok = rpc:call(SlaveNode, syn, init, []),
  543. timer:sleep(100),
  544. %% start processes
  545. PidLocal = syn_test_suite_helper:start_process(fun() -> called_loop(pid1) end),
  546. PidSlave = syn_test_suite_helper:start_process(SlaveNode, fun() -> called_loop(pid2) end),
  547. PidUnresponsive = syn_test_suite_helper:start_process(),
  548. %% join
  549. ok = syn:join(<<"my group">>, PidLocal),
  550. ok = syn:join(<<"my group">>, PidSlave),
  551. ok = syn:join(<<"my group">>, PidUnresponsive),
  552. timer:sleep(100),
  553. %% call
  554. {Replies, BadPids} = syn:multi_call(<<"my group">>, get_pid_name, 3000),
  555. %% check responses
  556. 2 = length(Replies),
  557. pid1 = proplists:get_value(PidLocal, Replies),
  558. pid2 = proplists:get_value(PidSlave, Replies),
  559. [PidUnresponsive] = BadPids,
  560. %% kill processes
  561. syn_test_suite_helper:kill_process(PidLocal),
  562. syn_test_suite_helper:kill_process(PidSlave),
  563. syn_test_suite_helper:kill_process(PidUnresponsive).
  564. two_nodes_local_members(Config) ->
  565. %% get slave
  566. SlaveNode = proplists:get_value(slave_node, Config),
  567. %% set schema location
  568. application:set_env(mnesia, schema_location, ram),
  569. rpc:call(SlaveNode, mnesia, schema_location, [ram]),
  570. %% start
  571. ok = syn:start(),
  572. ok = syn:init(),
  573. ok = rpc:call(SlaveNode, syn, start, []),
  574. ok = rpc:call(SlaveNode, syn, init, []),
  575. timer:sleep(100),
  576. %% start processes
  577. PidLocal1 = syn_test_suite_helper:start_process(),
  578. PidLocal2 = syn_test_suite_helper:start_process(),
  579. PidSlave = syn_test_suite_helper:start_process(SlaveNode),
  580. %% join
  581. ok = syn:join(<<"my group">>, PidLocal1, {meta, pid_local_1}),
  582. ok = syn:join(<<"my group">>, PidLocal2, {meta, pid_local_2}),
  583. ok = syn:join(<<"my group">>, PidSlave, {meta, pid_slave}),
  584. timer:sleep(100),
  585. %% retrieve, pid should have the same order in all nodes
  586. [PidLocal1, PidLocal2] = syn:get_local_members(<<"my group">>),
  587. [
  588. {PidLocal1, {meta, pid_local_1}},
  589. {PidLocal2, {meta, pid_local_2}}
  590. ] = syn:get_local_members(<<"my group">>, with_meta),
  591. %% local pids leave
  592. ok = syn:leave(<<"my group">>, PidLocal1),
  593. ok = syn:leave(<<"my group">>, PidLocal2),
  594. %% retrieve, no more local pids
  595. [] = syn:get_local_members(<<"my group">>),
  596. %% kill processes
  597. syn_test_suite_helper:kill_process(PidLocal1),
  598. syn_test_suite_helper:kill_process(PidLocal2),
  599. syn_test_suite_helper:kill_process(PidSlave).
  600. two_nodes_local_publish(Config) ->
  601. %% get slave
  602. SlaveNode = proplists:get_value(slave_node, Config),
  603. %% set schema location
  604. application:set_env(mnesia, schema_location, ram),
  605. rpc:call(SlaveNode, mnesia, schema_location, [ram]),
  606. %% start
  607. ok = syn:start(),
  608. ok = syn:init(),
  609. ok = rpc:call(SlaveNode, syn, start, []),
  610. ok = rpc:call(SlaveNode, syn, init, []),
  611. timer:sleep(100),
  612. %% start processes
  613. ResultPid = self(),
  614. F = fun() -> recipient_loop(ResultPid) end,
  615. PidLocal1 = syn_test_suite_helper:start_process(F),
  616. PidLocal2 = syn_test_suite_helper:start_process(F),
  617. PidSlave = syn_test_suite_helper:start_process(SlaveNode, F),
  618. %% join
  619. ok = syn:join(<<"my group">>, PidLocal1, {meta, pid_local_1}),
  620. ok = syn:join(<<"my group">>, PidLocal2, {meta, pid_local_2}),
  621. ok = syn:join(<<"my group">>, PidSlave, {meta, pid_slave}),
  622. %% publish
  623. {ok, 2} = syn:publish_to_local(<<"my group">>, {test, message}),
  624. %% check publish was received by local pids
  625. receive
  626. {received, PidLocal1, {test, message}} -> ok
  627. after 2000 ->
  628. ok = published_message_was_not_received_by_pid_local_1
  629. end,
  630. receive
  631. {received, PidLocal2, {test, message}} -> ok
  632. after 2000 ->
  633. ok = published_message_was_not_received_by_pid_local_2
  634. end,
  635. receive
  636. {received, PidSlave, {test, message}} ->
  637. ko = published_message_was_received_by_pid_slave
  638. after 1000 ->
  639. ok
  640. end,
  641. %% kill processes
  642. syn_test_suite_helper:kill_process(PidLocal1),
  643. syn_test_suite_helper:kill_process(PidLocal2),
  644. syn_test_suite_helper:kill_process(PidSlave).
  645. %% ===================================================================
  646. %% Internal
  647. %% ===================================================================
  648. recipient_loop(Pid) ->
  649. receive
  650. Message -> Pid ! {received, self(), Message}
  651. end.
  652. called_loop(PidName) ->
  653. receive
  654. {syn_multi_call, CallerPid, get_pid_name} -> syn:multi_call_reply(CallerPid, PidName)
  655. end.
  656. called_loop_that_crashes(_PidName) ->
  657. receive
  658. {syn_multi_call, _CallerPid, get_pid_name} -> exit(recipient_crashed_on_purpose)
  659. end.
  660. process_groups_process_exit_callback_dummy(Name, Pid, Meta, Reason) ->
  661. global:send(syn_process_groups_SUITE_result, {exited, node(), Name, Pid, Meta, Reason}).