syn_groups_SUITE.erl 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068
  1. %% ==========================================================================================================
  2. %% Syn - A global Process Registry and Process Group manager.
  3. %%
  4. %% The MIT License (MIT)
  5. %%
  6. %% Copyright (c) 2015-2019 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_and_monitor/1,
  35. single_node_join_and_leave/1,
  36. single_node_join_errors/1,
  37. single_node_publish/1,
  38. single_node_multicall/1,
  39. single_node_multicall_with_custom_timeout/1,
  40. single_node_callback_on_process_exit/1,
  41. single_node_monitor_after_group_crash/1
  42. ]).
  43. -export([
  44. two_nodes_join_monitor_and_unregister/1,
  45. two_nodes_local_members/1,
  46. two_nodes_publish/1,
  47. two_nodes_local_publish/1,
  48. two_nodes_multicall/1,
  49. two_nodes_groups_wait_for_syn_up/1
  50. ]).
  51. -export([
  52. three_nodes_partial_netsplit_consistency/1,
  53. three_nodes_full_netsplit_consistency/1
  54. ]).
  55. %% include
  56. -include_lib("common_test/include/ct.hrl").
  57. %% ===================================================================
  58. %% Callbacks
  59. %% ===================================================================
  60. %% -------------------------------------------------------------------
  61. %% Function: all() -> GroupsAndTestCases | {skip,Reason}
  62. %% GroupsAndTestCases = [{group,GroupName} | TestCase]
  63. %% GroupName = atom()
  64. %% TestCase = atom()
  65. %% Reason = any()
  66. %% -------------------------------------------------------------------
  67. all() ->
  68. [
  69. {group, single_node_groups},
  70. {group, two_nodes_groups},
  71. {group, three_nodes_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_groups, [shuffle], [
  88. single_node_join_and_monitor,
  89. single_node_join_and_leave,
  90. single_node_join_errors,
  91. single_node_publish,
  92. single_node_multicall,
  93. single_node_multicall_with_custom_timeout,
  94. single_node_callback_on_process_exit,
  95. single_node_monitor_after_group_crash
  96. ]},
  97. {two_nodes_groups, [shuffle], [
  98. two_nodes_join_monitor_and_unregister,
  99. two_nodes_local_members,
  100. two_nodes_publish,
  101. two_nodes_local_publish,
  102. two_nodes_multicall,
  103. two_nodes_groups_wait_for_syn_up
  104. ]},
  105. {three_nodes_groups, [shuffle], [
  106. three_nodes_partial_netsplit_consistency,
  107. three_nodes_full_netsplit_consistency
  108. ]}
  109. ].
  110. %% -------------------------------------------------------------------
  111. %% Function: init_per_suite(Config0) ->
  112. %% Config1 | {skip,Reason} |
  113. %% {skip_and_save,Reason,Config1}
  114. %% Config0 = Config1 = [tuple()]
  115. %% Reason = any()
  116. %% -------------------------------------------------------------------
  117. init_per_suite(Config) ->
  118. Config.
  119. %% -------------------------------------------------------------------
  120. %% Function: end_per_suite(Config0) -> void() | {save_config,Config1}
  121. %% Config0 = Config1 = [tuple()]
  122. %% -------------------------------------------------------------------
  123. end_per_suite(_Config) ->
  124. 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 = any()
  132. %% -------------------------------------------------------------------
  133. init_per_group(two_nodes_groups, Config) ->
  134. %% start slave
  135. {ok, SlaveNode} = syn_test_suite_helper:start_slave(syn_slave),
  136. %% config
  137. [{slave_node, SlaveNode} | Config];
  138. init_per_group(three_nodes_groups, Config) ->
  139. %% start slave
  140. {ok, SlaveNode1} = syn_test_suite_helper:start_slave(syn_slave_1),
  141. {ok, SlaveNode2} = syn_test_suite_helper:start_slave(syn_slave_2),
  142. %% config
  143. [{slave_node_1, SlaveNode1}, {slave_node_2, SlaveNode2} | Config];
  144. init_per_group(_GroupName, Config) ->
  145. Config.
  146. %% -------------------------------------------------------------------
  147. %% Function: end_per_group(GroupName, Config0) ->
  148. %% void() | {save_config,Config1}
  149. %% GroupName = atom()
  150. %% Config0 = Config1 = [tuple()]
  151. %% -------------------------------------------------------------------
  152. end_per_group(two_nodes_groups, Config) ->
  153. SlaveNode = proplists:get_value(slave_node, Config),
  154. syn_test_suite_helper:connect_node(SlaveNode),
  155. syn_test_suite_helper:clean_after_test(),
  156. syn_test_suite_helper:stop_slave(syn_slave),
  157. timer:sleep(1000);
  158. end_per_group(three_nodes_groups, Config) ->
  159. SlaveNode1 = proplists:get_value(slave_node_1, Config),
  160. syn_test_suite_helper:connect_node(SlaveNode1),
  161. SlaveNode2 = proplists:get_value(slave_node_2, Config),
  162. syn_test_suite_helper:connect_node(SlaveNode2),
  163. syn_test_suite_helper:clean_after_test(),
  164. syn_test_suite_helper:stop_slave(syn_slave_1),
  165. syn_test_suite_helper:stop_slave(syn_slave_2),
  166. timer:sleep(1000);
  167. end_per_group(_GroupName, _Config) ->
  168. syn_test_suite_helper:clean_after_test().
  169. %% -------------------------------------------------------------------
  170. %% Function: init_per_testcase(TestCase, Config0) ->
  171. %% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
  172. %% TestCase = atom()
  173. %% Config0 = Config1 = [tuple()]
  174. %% Reason = any()
  175. %% -------------------------------------------------------------------
  176. init_per_testcase(_TestCase, Config) ->
  177. Config.
  178. %% -------------------------------------------------------------------
  179. %% Function: end_per_testcase(TestCase, Config0) ->
  180. %% void() | {save_config,Config1} | {fail,Reason}
  181. %% TestCase = atom()
  182. %% Config0 = Config1 = [tuple()]
  183. %% Reason = any()
  184. %% -------------------------------------------------------------------
  185. end_per_testcase(_, _Config) ->
  186. syn_test_suite_helper:clean_after_test().
  187. %% ===================================================================
  188. %% Tests
  189. %% ===================================================================
  190. single_node_join_and_monitor(_Config) ->
  191. GroupName = "my group",
  192. %% start
  193. ok = syn:start(),
  194. %% start processes
  195. Pid = syn_test_suite_helper:start_process(),
  196. PidWithMeta = syn_test_suite_helper:start_process(),
  197. PidOther = syn_test_suite_helper:start_process(),
  198. %% retrieve
  199. [] = syn:get_members(GroupName),
  200. [] = syn:get_members(GroupName, with_meta),
  201. false = syn:member(Pid, GroupName),
  202. false = syn:member(PidWithMeta, GroupName),
  203. false = syn:member(PidOther, GroupName),
  204. %% join
  205. ok = syn:join(GroupName, Pid),
  206. ok = syn:join(GroupName, PidWithMeta, {with, meta}),
  207. ok = syn:join("other-group", PidOther),
  208. %% retrieve
  209. true = syn:member(Pid, GroupName),
  210. true = syn:member(PidWithMeta, GroupName),
  211. false = syn:member(PidOther, GroupName),
  212. true = lists:sort([Pid, PidWithMeta]) =:= lists:sort(syn:get_members(GroupName)),
  213. true = lists:sort([{Pid, undefined}, {PidWithMeta, {with, meta}}])
  214. =:= lists:sort(syn:get_members(GroupName, with_meta)),
  215. %% re-join
  216. ok = syn:join(GroupName, PidWithMeta, {with2, meta2}),
  217. true = lists:sort([{Pid, undefined}, {PidWithMeta, {with2, meta2}}])
  218. =:= lists:sort(syn:get_members(GroupName, with_meta)),
  219. %% kill process
  220. syn_test_suite_helper:kill_process(Pid),
  221. syn_test_suite_helper:kill_process(PidWithMeta),
  222. syn_test_suite_helper:kill_process(PidOther),
  223. timer:sleep(100),
  224. %% retrieve
  225. [] = syn:get_members(GroupName),
  226. [] = syn:get_members(GroupName, with_meta),
  227. false = syn:member(Pid, GroupName),
  228. false = syn:member(PidWithMeta, GroupName).
  229. single_node_join_and_leave(_Config) ->
  230. GroupName = "my group",
  231. %% start
  232. ok = syn:start(),
  233. %% start processes
  234. Pid = syn_test_suite_helper:start_process(),
  235. PidWithMeta = syn_test_suite_helper:start_process(),
  236. %% retrieve
  237. [] = syn:get_members(GroupName),
  238. [] = syn:get_members(GroupName, with_meta),
  239. false = syn:member(Pid, GroupName),
  240. false = syn:member(PidWithMeta, GroupName),
  241. %% join
  242. ok = syn:join(GroupName, Pid),
  243. ok = syn:join(GroupName, PidWithMeta, {with, meta}),
  244. %% retrieve
  245. true = syn:member(Pid, GroupName),
  246. true = syn:member(PidWithMeta, GroupName),
  247. true = lists:sort([Pid, PidWithMeta]) =:= lists:sort(syn:get_members(GroupName)),
  248. true = lists:sort([{Pid, undefined}, {PidWithMeta, {with, meta}}])
  249. =:= lists:sort(syn:get_members(GroupName, with_meta)),
  250. %% leave
  251. ok = syn:leave(GroupName, Pid),
  252. ok = syn:leave(GroupName, PidWithMeta),
  253. timer:sleep(100),
  254. %% retrieve
  255. [] = syn:get_members(GroupName),
  256. [] = syn:get_members(GroupName, with_meta),
  257. false = syn:member(Pid, GroupName),
  258. false = syn:member(PidWithMeta, GroupName).
  259. single_node_join_errors(_Config) ->
  260. GroupName = "my group",
  261. %% start
  262. ok = syn:start(),
  263. %% start processes
  264. Pid = syn_test_suite_helper:start_process(),
  265. Pid2 = syn_test_suite_helper:start_process(),
  266. %% join
  267. ok = syn:join(GroupName, Pid),
  268. ok = syn:join(GroupName, Pid2),
  269. true = syn:member(Pid, GroupName),
  270. true = syn:member(Pid2, GroupName),
  271. %% leave
  272. ok = syn:leave(GroupName, Pid),
  273. {error, not_in_group} = syn:leave(GroupName, Pid),
  274. %% kill
  275. syn_test_suite_helper:kill_process(Pid2),
  276. timer:sleep(200),
  277. {error, not_in_group} = syn:leave(GroupName, Pid2),
  278. {error, not_alive} = syn:join(GroupName, Pid2).
  279. single_node_publish(_Config) ->
  280. GroupName = "my group",
  281. Message = {test, message},
  282. %% start
  283. ok = syn:start(),
  284. %% start processes
  285. ResultPid = self(),
  286. F = fun() ->
  287. receive
  288. Message -> ResultPid ! {received, self(), Message}
  289. end
  290. end,
  291. Pid = syn_test_suite_helper:start_process(F),
  292. Pid2 = syn_test_suite_helper:start_process(F),
  293. _OtherPid = syn_test_suite_helper:start_process(F),
  294. %% join
  295. ok = syn:join(GroupName, Pid),
  296. ok = syn:join(GroupName, Pid2),
  297. true = syn:member(Pid, GroupName),
  298. true = syn:member(Pid2, GroupName),
  299. %% send
  300. {ok, 2} = syn:publish(GroupName, Message),
  301. %% check
  302. receive
  303. {received, Pid, Message} -> ok
  304. after 2000 ->
  305. ok = published_message_was_not_received_by_pid_1
  306. end,
  307. receive
  308. {received, Pid2, Message} -> ok
  309. after 2000 ->
  310. ok = published_message_was_not_received_by_pid_2
  311. end.
  312. single_node_multicall(_Config) ->
  313. GroupName = <<"my group">>,
  314. %% start
  315. ok = syn:start(),
  316. %% start processes
  317. F = fun() ->
  318. receive
  319. {syn_multi_call, RequestorPid, get_pid_name} ->
  320. syn:multi_call_reply(RequestorPid, {pong, self()})
  321. end
  322. end,
  323. Pid1 = syn_test_suite_helper:start_process(F),
  324. Pid2 = syn_test_suite_helper:start_process(F),
  325. PidUnresponsive = syn_test_suite_helper:start_process(),
  326. %% register
  327. ok = syn:join(GroupName, Pid1),
  328. ok = syn:join(GroupName, Pid2),
  329. ok = syn:join(GroupName, PidUnresponsive),
  330. %% call
  331. {Replies, BadPids} = syn:multi_call(GroupName, get_pid_name),
  332. %% check responses
  333. true = lists:sort([
  334. {Pid1, {pong, Pid1}},
  335. {Pid2, {pong, Pid2}}
  336. ]) =:= lists:sort(Replies),
  337. [PidUnresponsive] = BadPids.
  338. single_node_multicall_with_custom_timeout(_Config) ->
  339. GroupName = <<"my group">>,
  340. %% start
  341. ok = syn:start(),
  342. %% start processes
  343. F1 = fun() ->
  344. receive
  345. {syn_multi_call, RequestorPid, get_pid_name} ->
  346. syn:multi_call_reply(RequestorPid, {pong, self()})
  347. end
  348. end,
  349. Pid1 = syn_test_suite_helper:start_process(F1),
  350. F2 = fun() ->
  351. receive
  352. {syn_multi_call, RequestorPid, get_pid_name} ->
  353. timer:sleep(5000),
  354. syn:multi_call_reply(RequestorPid, {pong, self()})
  355. end
  356. end,
  357. PidTakesLong = syn_test_suite_helper:start_process(F2),
  358. PidUnresponsive = syn_test_suite_helper:start_process(),
  359. %% register
  360. ok = syn:join(GroupName, Pid1),
  361. ok = syn:join(GroupName, PidTakesLong),
  362. ok = syn:join(GroupName, PidUnresponsive),
  363. %% call
  364. {Replies, BadPids} = syn:multi_call(GroupName, get_pid_name, 2000),
  365. %% check responses
  366. [{Pid1, {pong, Pid1}}] = Replies,
  367. true = lists:sort([PidTakesLong, PidUnresponsive]) =:= lists:sort(BadPids).
  368. single_node_callback_on_process_exit(_Config) ->
  369. %% use custom handler
  370. syn_test_suite_helper:use_custom_handler(),
  371. %% start
  372. ok = syn:start(),
  373. %% start processes
  374. Pid = syn_test_suite_helper:start_process(),
  375. Pid2 = syn_test_suite_helper:start_process(),
  376. %% join
  377. TestPid = self(),
  378. ok = syn:join(group_1, Pid, {pid_group_1, TestPid}),
  379. ok = syn:join(group_2, Pid, {pid_group_2, TestPid}),
  380. ok = syn:join(group_1, Pid2, {pid2, TestPid}),
  381. %% kill 1
  382. syn_test_suite_helper:kill_process(Pid),
  383. receive
  384. {received_event_on, pid_group_1} ->
  385. ok;
  386. {received_event_on, pid2} ->
  387. ok = callback_on_process_exit_was_received_by_pid2
  388. after 1000 ->
  389. ok = callback_on_process_exit_was_not_received_by_pid
  390. end,
  391. receive
  392. {received_event_on, pid_group_2} ->
  393. ok;
  394. {received_event_on, pid2} ->
  395. ok = callback_on_process_exit_was_received_by_pid2
  396. after 1000 ->
  397. ok = callback_on_process_exit_was_not_received_by_pid
  398. end,
  399. %% unregister & kill 2
  400. ok = syn:leave(group_1, Pid2),
  401. syn_test_suite_helper:kill_process(Pid2),
  402. receive
  403. {received_event_on, pid2} ->
  404. ok = callback_on_process_exit_was_received_by_pid2
  405. after 1000 ->
  406. ok
  407. end.
  408. single_node_monitor_after_group_crash(_Config) ->
  409. GroupName = "my group",
  410. %% start
  411. ok = syn:start(),
  412. %% start processes
  413. Pid = syn_test_suite_helper:start_process(),
  414. %% join
  415. ok = syn:join(GroupName, Pid),
  416. %% kill groups
  417. exit(whereis(syn_groups), kill),
  418. timer:sleep(200),
  419. %% retrieve
  420. true = syn:member(Pid, GroupName),
  421. [Pid] = syn:get_members(GroupName),
  422. %% kill process
  423. syn_test_suite_helper:kill_process(Pid),
  424. timer:sleep(200),
  425. %% retrieve
  426. false = syn:member(Pid, GroupName),
  427. [] = syn:get_members(GroupName).
  428. two_nodes_join_monitor_and_unregister(Config) ->
  429. GroupName = "my group",
  430. %% get slave
  431. SlaveNode = proplists:get_value(slave_node, Config),
  432. %% start
  433. ok = syn:start(),
  434. ok = rpc:call(SlaveNode, syn, start, []),
  435. timer:sleep(100),
  436. %% start processes
  437. LocalPid = syn_test_suite_helper:start_process(),
  438. RemotePid = syn_test_suite_helper:start_process(SlaveNode),
  439. RemotePidJoinRemote = syn_test_suite_helper:start_process(SlaveNode),
  440. OtherPid = syn_test_suite_helper:start_process(),
  441. %% retrieve
  442. [] = syn:get_members("group-1"),
  443. [] = syn:get_members(GroupName),
  444. [] = syn:get_members(GroupName, with_meta),
  445. false = syn:member(LocalPid, GroupName),
  446. false = syn:member(RemotePid, GroupName),
  447. false = syn:member(RemotePidJoinRemote, GroupName),
  448. false = syn:member(OtherPid, GroupName),
  449. [] = rpc:call(SlaveNode, syn, get_members, [GroupName]),
  450. [] = rpc:call(SlaveNode, syn, get_members, [GroupName, with_meta]),
  451. false = rpc:call(SlaveNode, syn, member, [LocalPid, GroupName]),
  452. false = rpc:call(SlaveNode, syn, member, [RemotePid, GroupName]),
  453. false = rpc:call(SlaveNode, syn, member, [RemotePidJoinRemote, GroupName]),
  454. false = rpc:call(SlaveNode, syn, member, [OtherPid, GroupName]),
  455. %% join
  456. ok = syn:join(GroupName, LocalPid),
  457. ok = syn:join(GroupName, RemotePid, {with_meta}),
  458. ok = rpc:call(SlaveNode, syn, join, [GroupName, RemotePidJoinRemote]),
  459. ok = syn:join("other-group", OtherPid),
  460. timer:sleep(200),
  461. %% retrieve local
  462. true = lists:sort([LocalPid, RemotePid, RemotePidJoinRemote]) =:= lists:sort(syn:get_members(GroupName)),
  463. true = lists:sort([{LocalPid, undefined}, {RemotePid, {with_meta}}, {RemotePidJoinRemote, undefined}])
  464. =:= lists:sort(syn:get_members(GroupName, with_meta)),
  465. true = syn:member(LocalPid, GroupName),
  466. true = syn:member(RemotePid, GroupName),
  467. true = syn:member(RemotePidJoinRemote, GroupName),
  468. false = syn:member(OtherPid, GroupName),
  469. %% retrieve remote
  470. true = lists:sort([LocalPid, RemotePid, RemotePidJoinRemote])
  471. =:= lists:sort(rpc:call(SlaveNode, syn, get_members, [GroupName])),
  472. true = lists:sort([{LocalPid, undefined}, {RemotePid, {with_meta}}, {RemotePidJoinRemote, undefined}])
  473. =:= lists:sort(rpc:call(SlaveNode, syn, get_members, [GroupName, with_meta])),
  474. true = rpc:call(SlaveNode, syn, member, [LocalPid, GroupName]),
  475. true = rpc:call(SlaveNode, syn, member, [RemotePid, GroupName]),
  476. true = rpc:call(SlaveNode, syn, member, [RemotePidJoinRemote, GroupName]),
  477. false = rpc:call(SlaveNode, syn, member, [OtherPid, GroupName]),
  478. %% leave & kill
  479. ok = rpc:call(SlaveNode, syn, leave, [GroupName, LocalPid]),
  480. ok = syn:leave(GroupName, RemotePid),
  481. syn_test_suite_helper:kill_process(RemotePidJoinRemote),
  482. syn_test_suite_helper:kill_process(OtherPid),
  483. timer:sleep(200),
  484. %% retrieve
  485. [] = syn:get_members("group-1"),
  486. [] = syn:get_members(GroupName),
  487. [] = syn:get_members(GroupName, with_meta),
  488. false = syn:member(LocalPid, GroupName),
  489. false = syn:member(RemotePid, GroupName),
  490. false = syn:member(RemotePidJoinRemote, GroupName),
  491. false = syn:member(OtherPid, GroupName),
  492. [] = rpc:call(SlaveNode, syn, get_members, [GroupName]),
  493. [] = rpc:call(SlaveNode, syn, get_members, [GroupName, with_meta]),
  494. false = rpc:call(SlaveNode, syn, member, [LocalPid, GroupName]),
  495. false = rpc:call(SlaveNode, syn, member, [RemotePid, GroupName]),
  496. false = rpc:call(SlaveNode, syn, member, [RemotePidJoinRemote, GroupName]),
  497. false = rpc:call(SlaveNode, syn, member, [OtherPid, GroupName]).
  498. two_nodes_local_members(Config) ->
  499. GroupName = "my group",
  500. %% get slave
  501. SlaveNode = proplists:get_value(slave_node, Config),
  502. %% start
  503. ok = syn:start(),
  504. ok = rpc:call(SlaveNode, syn, start, []),
  505. timer:sleep(100),
  506. %% start processes
  507. LocalPid = syn_test_suite_helper:start_process(),
  508. RemotePid = syn_test_suite_helper:start_process(SlaveNode),
  509. RemotePidJoinRemote = syn_test_suite_helper:start_process(SlaveNode),
  510. OtherPid = syn_test_suite_helper:start_process(),
  511. %% local members
  512. [] = syn:get_local_members(GroupName),
  513. [] = syn:get_local_members(GroupName, with_meta),
  514. false = syn:local_member(LocalPid, GroupName),
  515. false = syn:local_member(RemotePid, GroupName),
  516. false = syn:local_member(RemotePidJoinRemote, GroupName),
  517. false = syn:local_member(OtherPid, GroupName),
  518. %% remote members
  519. [] = rpc:call(SlaveNode, syn, get_local_members, [GroupName]),
  520. [] = rpc:call(SlaveNode, syn, get_local_members, [GroupName, with_meta]),
  521. false = rpc:call(SlaveNode, syn, local_member, [LocalPid, GroupName]),
  522. false = rpc:call(SlaveNode, syn, local_member, [RemotePid, GroupName]),
  523. false = rpc:call(SlaveNode, syn, local_member, [RemotePidJoinRemote, GroupName]),
  524. false = rpc:call(SlaveNode, syn, local_member, [OtherPid, GroupName]),
  525. %% join
  526. ok = syn:join(GroupName, LocalPid),
  527. ok = syn:join(GroupName, RemotePid, {meta, 2}),
  528. ok = rpc:call(SlaveNode, syn, join, [GroupName, RemotePidJoinRemote]),
  529. ok = syn:join({"other-group"}, OtherPid),
  530. timer:sleep(200),
  531. %% local members
  532. [LocalPid] = syn:get_local_members(GroupName),
  533. [{LocalPid, undefined}] = syn:get_local_members(GroupName, with_meta),
  534. [OtherPid] = syn:get_local_members({"other-group"}),
  535. true = syn:local_member(LocalPid, GroupName),
  536. false = syn:local_member(RemotePid, GroupName),
  537. false = syn:local_member(RemotePidJoinRemote, GroupName),
  538. false = syn:local_member(OtherPid, GroupName),
  539. true = syn:local_member(OtherPid, {"other-group"}),
  540. %% remote members
  541. true = lists:sort([RemotePid, RemotePidJoinRemote])
  542. =:= lists:sort(rpc:call(SlaveNode, syn, get_local_members, [GroupName])),
  543. true = lists:sort([{RemotePid, {meta, 2}}, {RemotePidJoinRemote, undefined}])
  544. =:= lists:sort(rpc:call(SlaveNode, syn, get_local_members, [GroupName, with_meta])),
  545. false = rpc:call(SlaveNode, syn, local_member, [LocalPid, GroupName]),
  546. true = rpc:call(SlaveNode, syn, local_member, [RemotePid, GroupName]),
  547. true = rpc:call(SlaveNode, syn, local_member, [RemotePidJoinRemote, GroupName]),
  548. false = rpc:call(SlaveNode, syn, local_member, [OtherPid, GroupName]),
  549. %% leave & kill
  550. ok = rpc:call(SlaveNode, syn, leave, [GroupName, LocalPid]),
  551. ok = syn:leave(GroupName, RemotePid),
  552. syn_test_suite_helper:kill_process(RemotePidJoinRemote),
  553. syn_test_suite_helper:kill_process(OtherPid),
  554. timer:sleep(200),
  555. %% local members
  556. [] = syn:get_local_members(GroupName),
  557. [] = syn:get_local_members(GroupName, with_meta),
  558. %% remote members
  559. [] = rpc:call(SlaveNode, syn, get_local_members, [GroupName]),
  560. [] = rpc:call(SlaveNode, syn, get_local_members, [GroupName, with_meta]).
  561. two_nodes_publish(Config) ->
  562. GroupName = "my group",
  563. Message = {test, message},
  564. %% get slave
  565. SlaveNode = proplists:get_value(slave_node, Config),
  566. %% start
  567. ok = syn:start(),
  568. ok = rpc:call(SlaveNode, syn, start, []),
  569. timer:sleep(100),
  570. %% start processes
  571. ResultPid = self(),
  572. F = fun() ->
  573. receive
  574. Message -> ResultPid ! {received, self(), Message}
  575. end
  576. end,
  577. LocalPid = syn_test_suite_helper:start_process(F),
  578. LocalPid2 = syn_test_suite_helper:start_process(F),
  579. RemotePid = syn_test_suite_helper:start_process(SlaveNode, F),
  580. RemotePid2 = syn_test_suite_helper:start_process(SlaveNode, F),
  581. OtherPid = syn_test_suite_helper:start_process(F),
  582. %% join
  583. ok = syn:join(GroupName, LocalPid),
  584. ok = syn:join(GroupName, LocalPid2),
  585. ok = syn:join(GroupName, RemotePid),
  586. ok = syn:join(GroupName, RemotePid2),
  587. timer:sleep(200),
  588. %% send
  589. {ok, 4} = syn:publish(GroupName, Message),
  590. %% check
  591. receive
  592. {received, LocalPid, Message} -> ok
  593. after 2000 ->
  594. ok = published_message_was_not_received_by_local_pid
  595. end,
  596. receive
  597. {received, LocalPid2, Message} -> ok
  598. after 2000 ->
  599. ok = published_message_was_not_received_by_local_pid_2
  600. end,
  601. receive
  602. {received, RemotePid, Message} -> ok
  603. after 2000 ->
  604. ok = published_message_was_not_received_by_remote_pid
  605. end,
  606. receive
  607. {received, RemotePid2, Message} -> ok
  608. after 2000 ->
  609. ok = published_message_was_not_received_by_remote_pid_2
  610. end,
  611. receive
  612. {received, OtherPid, Message} ->
  613. ok = published_message_was_received_by_other_pid
  614. after 250 ->
  615. ok
  616. end.
  617. two_nodes_local_publish(Config) ->
  618. GroupName = "my group",
  619. Message = {test, message},
  620. %% get slave
  621. SlaveNode = proplists:get_value(slave_node, Config),
  622. %% start
  623. ok = syn:start(),
  624. ok = rpc:call(SlaveNode, syn, start, []),
  625. timer:sleep(100),
  626. %% start processes
  627. ResultPid = self(),
  628. F = fun() ->
  629. receive
  630. Message -> ResultPid ! {received, self(), Message}
  631. end
  632. end,
  633. LocalPid = syn_test_suite_helper:start_process(F),
  634. LocalPid2 = syn_test_suite_helper:start_process(F),
  635. RemotePid = syn_test_suite_helper:start_process(SlaveNode, F),
  636. RemotePid2 = syn_test_suite_helper:start_process(SlaveNode, F),
  637. OtherPid = syn_test_suite_helper:start_process(F),
  638. %% join
  639. ok = syn:join(GroupName, LocalPid),
  640. ok = syn:join(GroupName, LocalPid2),
  641. ok = syn:join(GroupName, RemotePid),
  642. ok = syn:join(GroupName, RemotePid2),
  643. timer:sleep(200),
  644. %% send
  645. {ok, 2} = syn:publish_to_local(GroupName, Message),
  646. %% check
  647. receive
  648. {received, LocalPid, Message} -> ok
  649. after 2000 ->
  650. ok = published_message_was_not_received_by_local_pid
  651. end,
  652. receive
  653. {received, LocalPid2, Message} -> ok
  654. after 2000 ->
  655. ok = published_message_was_not_received_by_local_pid_2
  656. end,
  657. receive
  658. {received, RemotePid, Message} ->
  659. ok = published_message_was_received_by_remote_pid
  660. after 250 ->
  661. ok
  662. end,
  663. receive
  664. {received, RemotePid, Message} ->
  665. ok = published_message_was_received_by_remote_pid_2
  666. after 250 ->
  667. ok
  668. end,
  669. receive
  670. {received, OtherPid, Message} ->
  671. ok = published_message_was_received_by_other_pid
  672. after 250 ->
  673. ok
  674. end.
  675. two_nodes_multicall(Config) ->
  676. GroupName = <<"my group">>,
  677. %% get slave
  678. SlaveNode = proplists:get_value(slave_node, Config),
  679. %% start
  680. ok = syn:start(),
  681. ok = rpc:call(SlaveNode, syn, start, []),
  682. timer:sleep(100),
  683. %% start processes
  684. F = fun() ->
  685. receive
  686. {syn_multi_call, RequestorPid, get_pid_name} ->
  687. syn:multi_call_reply(RequestorPid, {pong, self()})
  688. end
  689. end,
  690. Pid1 = syn_test_suite_helper:start_process(F),
  691. Pid2 = syn_test_suite_helper:start_process(SlaveNode, F),
  692. PidUnresponsive = syn_test_suite_helper:start_process(),
  693. %% register
  694. ok = syn:join(GroupName, Pid1),
  695. ok = syn:join(GroupName, Pid2),
  696. ok = syn:join(GroupName, PidUnresponsive),
  697. timer:sleep(500),
  698. %% call
  699. {Replies, BadPids} = syn:multi_call(GroupName, get_pid_name),
  700. %% check responses
  701. true = lists:sort([
  702. {Pid1, {pong, Pid1}},
  703. {Pid2, {pong, Pid2}}
  704. ]) =:= lists:sort(Replies),
  705. [PidUnresponsive] = BadPids.
  706. two_nodes_groups_wait_for_syn_up(_Config) ->
  707. %% stop slave
  708. syn_test_suite_helper:stop_slave(syn_slave),
  709. %% start syn on local node
  710. ok = syn:start(),
  711. %% start process
  712. Pid = syn_test_suite_helper:start_process(),
  713. %% join
  714. ok = syn:join(<<"group">>, Pid),
  715. %% start remote node and syn
  716. {ok, SlaveNode} = syn_test_suite_helper:start_slave(syn_slave),
  717. ok = rpc:call(SlaveNode, syn, start, []),
  718. timer:sleep(1000),
  719. %% check
  720. [Pid] = syn:get_members(<<"group">>),
  721. [Pid] = rpc:call(SlaveNode, syn, get_members, [<<"group">>]).
  722. three_nodes_partial_netsplit_consistency(Config) ->
  723. GroupName = "my group",
  724. %% get slaves
  725. SlaveNode1 = proplists:get_value(slave_node_1, Config),
  726. SlaveNode2 = proplists:get_value(slave_node_2, Config),
  727. %% start syn on nodes
  728. ok = syn:start(),
  729. ok = rpc:call(SlaveNode1, syn, start, []),
  730. ok = rpc:call(SlaveNode2, syn, start, []),
  731. timer:sleep(100),
  732. %% start processes
  733. Pid0 = syn_test_suite_helper:start_process(),
  734. Pid0Changed = syn_test_suite_helper:start_process(),
  735. Pid1 = syn_test_suite_helper:start_process(SlaveNode1),
  736. Pid2 = syn_test_suite_helper:start_process(SlaveNode2),
  737. OtherPid = syn_test_suite_helper:start_process(),
  738. timer:sleep(100),
  739. %% retrieve local
  740. [] = syn:get_members("group-1"),
  741. [] = syn:get_members(GroupName),
  742. [] = syn:get_members(GroupName, with_meta),
  743. false = syn:member(Pid0, GroupName),
  744. false = syn:member(Pid0Changed, GroupName),
  745. false = syn:member(Pid1, GroupName),
  746. false = syn:member(Pid2, GroupName),
  747. %% retrieve slave 1
  748. [] = rpc:call(SlaveNode1, syn, get_members, [GroupName]),
  749. [] = rpc:call(SlaveNode1, syn, get_members, [GroupName, with_meta]),
  750. false = rpc:call(SlaveNode1, syn, member, [Pid0, GroupName]),
  751. false = rpc:call(SlaveNode1, syn, member, [Pid0Changed, GroupName]),
  752. false = rpc:call(SlaveNode1, syn, member, [Pid1, GroupName]),
  753. false = rpc:call(SlaveNode1, syn, member, [Pid2, GroupName]),
  754. %% retrieve slave 2
  755. [] = rpc:call(SlaveNode2, syn, get_members, [GroupName]),
  756. [] = rpc:call(SlaveNode2, syn, get_members, [GroupName, with_meta]),
  757. false = rpc:call(SlaveNode2, syn, member, [Pid0, GroupName]),
  758. false = rpc:call(SlaveNode2, syn, member, [Pid0Changed, GroupName]),
  759. false = rpc:call(SlaveNode2, syn, member, [Pid1, GroupName]),
  760. false = rpc:call(SlaveNode2, syn, member, [Pid2, GroupName]),
  761. %% join
  762. ok = syn:join(GroupName, Pid0),
  763. ok = syn:join(GroupName, Pid0Changed, {meta, changed}),
  764. ok = rpc:call(SlaveNode1, syn, join, [GroupName, Pid1]),
  765. ok = rpc:call(SlaveNode2, syn, join, [GroupName, Pid2, {meta, 2}]),
  766. ok = syn:join("other-group", OtherPid),
  767. timer:sleep(200),
  768. %% retrieve local
  769. true = lists:sort([Pid0, Pid0Changed, Pid1, Pid2]) =:= lists:sort(syn:get_members(GroupName)),
  770. true = lists:sort([
  771. {Pid0, undefined},
  772. {Pid0Changed, {meta, changed}},
  773. {Pid1, undefined},
  774. {Pid2, {meta, 2}}
  775. ]) =:= lists:sort(syn:get_members(GroupName, with_meta)),
  776. true = syn:member(Pid0, GroupName),
  777. true = syn:member(Pid0Changed, GroupName),
  778. true = syn:member(Pid1, GroupName),
  779. true = syn:member(Pid2, GroupName),
  780. false = syn:member(OtherPid, GroupName),
  781. %% retrieve slave 1
  782. true = lists:sort([Pid0, Pid0Changed, Pid1, Pid2])
  783. =:= lists:sort(rpc:call(SlaveNode1, syn, get_members, [GroupName])),
  784. true = lists:sort([
  785. {Pid0, undefined},
  786. {Pid0Changed, {meta, changed}},
  787. {Pid1, undefined},
  788. {Pid2, {meta, 2}}
  789. ]) =:= lists:sort(rpc:call(SlaveNode1, syn, get_members, [GroupName, with_meta])),
  790. true = rpc:call(SlaveNode1, syn, member, [Pid0, GroupName]),
  791. true = rpc:call(SlaveNode1, syn, member, [Pid0Changed, GroupName]),
  792. true = rpc:call(SlaveNode1, syn, member, [Pid1, GroupName]),
  793. true = rpc:call(SlaveNode1, syn, member, [Pid2, GroupName]),
  794. false = rpc:call(SlaveNode1, syn, member, [OtherPid, GroupName]),
  795. %% retrieve slave 2
  796. true = lists:sort([Pid0, Pid0Changed, Pid1, Pid2])
  797. =:= lists:sort(rpc:call(SlaveNode2, syn, get_members, [GroupName])),
  798. true = lists:sort([
  799. {Pid0, undefined},
  800. {Pid0Changed, {meta, changed}},
  801. {Pid1, undefined},
  802. {Pid2, {meta, 2}}
  803. ]) =:= lists:sort(rpc:call(SlaveNode2, syn, get_members, [GroupName, with_meta])),
  804. true = rpc:call(SlaveNode2, syn, member, [Pid0, GroupName]),
  805. true = rpc:call(SlaveNode2, syn, member, [Pid0Changed, GroupName]),
  806. true = rpc:call(SlaveNode2, syn, member, [Pid1, GroupName]),
  807. true = rpc:call(SlaveNode2, syn, member, [Pid2, GroupName]),
  808. false = rpc:call(SlaveNode2, syn, member, [OtherPid, GroupName]),
  809. %% disconnect slave 2 from main (slave 1 can still see slave 2)
  810. syn_test_suite_helper:disconnect_node(SlaveNode2),
  811. timer:sleep(500),
  812. %% retrieve local
  813. true = lists:sort([Pid0, Pid0Changed, Pid1]) =:= lists:sort(syn:get_members(GroupName)),
  814. true = lists:sort([
  815. {Pid0, undefined},
  816. {Pid0Changed, {meta, changed}},
  817. {Pid1, undefined}
  818. ]) =:= lists:sort(syn:get_members(GroupName, with_meta)),
  819. true = syn:member(Pid0, GroupName),
  820. true = syn:member(Pid0Changed, GroupName),
  821. true = syn:member(Pid1, GroupName),
  822. false = syn:member(Pid2, GroupName),
  823. false = syn:member(OtherPid, GroupName),
  824. %% retrieve slave 1
  825. true = lists:sort([Pid0, Pid0Changed, Pid1, Pid2])
  826. =:= lists:sort(rpc:call(SlaveNode1, syn, get_members, [GroupName])),
  827. true = lists:sort([
  828. {Pid0, undefined},
  829. {Pid0Changed, {meta, changed}},
  830. {Pid1, undefined},
  831. {Pid2, {meta, 2}}
  832. ]) =:= lists:sort(rpc:call(SlaveNode1, syn, get_members, [GroupName, with_meta])),
  833. true = rpc:call(SlaveNode1, syn, member, [Pid0, GroupName]),
  834. true = rpc:call(SlaveNode1, syn, member, [Pid0Changed, GroupName]),
  835. true = rpc:call(SlaveNode1, syn, member, [Pid1, GroupName]),
  836. true = rpc:call(SlaveNode1, syn, member, [Pid2, GroupName]),
  837. false = rpc:call(SlaveNode1, syn, member, [OtherPid, GroupName]),
  838. %% disconnect slave 1
  839. syn_test_suite_helper:disconnect_node(SlaveNode1),
  840. timer:sleep(500),
  841. %% leave 0Changed
  842. ok = syn:leave(GroupName, Pid0Changed),
  843. %% retrieve local
  844. true = lists:sort([Pid0]) =:= lists:sort(syn:get_members(GroupName)),
  845. true = lists:sort([
  846. {Pid0, undefined}
  847. ]) =:= lists:sort(syn:get_members(GroupName, with_meta)),
  848. true = syn:member(Pid0, GroupName),
  849. false = syn:member(Pid0Changed, GroupName),
  850. false = syn:member(Pid1, GroupName),
  851. false = syn:member(Pid2, GroupName),
  852. false = syn:member(OtherPid, GroupName),
  853. %% reconnect all
  854. syn_test_suite_helper:connect_node(SlaveNode1),
  855. syn_test_suite_helper:connect_node(SlaveNode2),
  856. timer:sleep(5000),
  857. %% retrieve local
  858. true = lists:sort([Pid0, Pid1, Pid2]) =:= lists:sort(syn:get_members(GroupName)),
  859. true = lists:sort([
  860. {Pid0, undefined},
  861. {Pid1, undefined},
  862. {Pid2, {meta, 2}}
  863. ]) =:= lists:sort(syn:get_members(GroupName, with_meta)),
  864. true = syn:member(Pid0, GroupName),
  865. false = syn:member(Pid0Changed, GroupName),
  866. true = syn:member(Pid1, GroupName),
  867. true = syn:member(Pid2, GroupName),
  868. false = syn:member(OtherPid, GroupName),
  869. %% retrieve slave 1
  870. true = lists:sort([Pid0, Pid1, Pid2])
  871. =:= lists:sort(rpc:call(SlaveNode1, syn, get_members, [GroupName])),
  872. true = lists:sort([
  873. {Pid0, undefined},
  874. {Pid1, undefined},
  875. {Pid2, {meta, 2}}
  876. ]) =:= lists:sort(rpc:call(SlaveNode1, syn, get_members, [GroupName, with_meta])),
  877. true = rpc:call(SlaveNode1, syn, member, [Pid0, GroupName]),
  878. false = rpc:call(SlaveNode1, syn, member, [Pid0Changed, GroupName]),
  879. true = rpc:call(SlaveNode1, syn, member, [Pid1, GroupName]),
  880. true = rpc:call(SlaveNode1, syn, member, [Pid2, GroupName]),
  881. false = rpc:call(SlaveNode1, syn, member, [OtherPid, GroupName]),
  882. %% retrieve slave 2
  883. true = lists:sort([Pid0, Pid1, Pid2])
  884. =:= lists:sort(rpc:call(SlaveNode2, syn, get_members, [GroupName])),
  885. true = lists:sort([
  886. {Pid0, undefined},
  887. {Pid1, undefined},
  888. {Pid2, {meta, 2}}
  889. ]) =:= lists:sort(rpc:call(SlaveNode2, syn, get_members, [GroupName, with_meta])),
  890. true = rpc:call(SlaveNode2, syn, member, [Pid0, GroupName]),
  891. false = rpc:call(SlaveNode2, syn, member, [Pid0Changed, GroupName]),
  892. true = rpc:call(SlaveNode2, syn, member, [Pid1, GroupName]),
  893. true = rpc:call(SlaveNode2, syn, member, [Pid2, GroupName]),
  894. false = rpc:call(SlaveNode2, syn, member, [OtherPid, GroupName]).
  895. three_nodes_full_netsplit_consistency(Config) ->
  896. GroupName = "my group",
  897. %% get slaves
  898. SlaveNode1 = proplists:get_value(slave_node_1, Config),
  899. SlaveNode2 = proplists:get_value(slave_node_2, Config),
  900. %% start syn on nodes
  901. ok = syn:start(),
  902. ok = rpc:call(SlaveNode1, syn, start, []),
  903. ok = rpc:call(SlaveNode2, syn, start, []),
  904. timer:sleep(100),
  905. %% start processes
  906. Pid0 = syn_test_suite_helper:start_process(),
  907. Pid0Changed = syn_test_suite_helper:start_process(),
  908. Pid1 = syn_test_suite_helper:start_process(SlaveNode1),
  909. Pid2 = syn_test_suite_helper:start_process(SlaveNode2),
  910. OtherPid = syn_test_suite_helper:start_process(),
  911. timer:sleep(100),
  912. %% retrieve local
  913. [] = syn:get_members("group-1"),
  914. [] = syn:get_members(GroupName),
  915. [] = syn:get_members(GroupName, with_meta),
  916. false = syn:member(Pid0, GroupName),
  917. false = syn:member(Pid0Changed, GroupName),
  918. false = syn:member(Pid1, GroupName),
  919. false = syn:member(Pid2, GroupName),
  920. %% retrieve slave 1
  921. [] = rpc:call(SlaveNode1, syn, get_members, [GroupName]),
  922. [] = rpc:call(SlaveNode1, syn, get_members, [GroupName, with_meta]),
  923. false = rpc:call(SlaveNode1, syn, member, [Pid0, GroupName]),
  924. false = rpc:call(SlaveNode1, syn, member, [Pid0Changed, GroupName]),
  925. false = rpc:call(SlaveNode1, syn, member, [Pid1, GroupName]),
  926. false = rpc:call(SlaveNode1, syn, member, [Pid2, GroupName]),
  927. %% retrieve slave 2
  928. [] = rpc:call(SlaveNode2, syn, get_members, [GroupName]),
  929. [] = rpc:call(SlaveNode2, syn, get_members, [GroupName, with_meta]),
  930. false = rpc:call(SlaveNode2, syn, member, [Pid0, GroupName]),
  931. false = rpc:call(SlaveNode2, syn, member, [Pid0Changed, GroupName]),
  932. false = rpc:call(SlaveNode2, syn, member, [Pid1, GroupName]),
  933. false = rpc:call(SlaveNode2, syn, member, [Pid2, GroupName]),
  934. %% join
  935. ok = syn:join(GroupName, Pid0),
  936. ok = syn:join(GroupName, Pid0Changed, {meta, changed}),
  937. ok = rpc:call(SlaveNode1, syn, join, [GroupName, Pid1]),
  938. ok = rpc:call(SlaveNode2, syn, join, [GroupName, Pid2, {meta, 2}]),
  939. ok = syn:join("other-group", OtherPid),
  940. timer:sleep(200),
  941. %% retrieve local
  942. true = lists:sort([Pid0, Pid0Changed, Pid1, Pid2]) =:= lists:sort(syn:get_members(GroupName)),
  943. true = lists:sort([
  944. {Pid0, undefined},
  945. {Pid0Changed, {meta, changed}},
  946. {Pid1, undefined},
  947. {Pid2, {meta, 2}}
  948. ]) =:= lists:sort(syn:get_members(GroupName, with_meta)),
  949. true = syn:member(Pid0, GroupName),
  950. true = syn:member(Pid0Changed, GroupName),
  951. true = syn:member(Pid1, GroupName),
  952. true = syn:member(Pid2, GroupName),
  953. false = syn:member(OtherPid, GroupName),
  954. %% retrieve slave 1
  955. true = lists:sort([Pid0, Pid0Changed, Pid1, Pid2])
  956. =:= lists:sort(rpc:call(SlaveNode1, syn, get_members, [GroupName])),
  957. true = lists:sort([
  958. {Pid0, undefined},
  959. {Pid0Changed, {meta, changed}},
  960. {Pid1, undefined},
  961. {Pid2, {meta, 2}}
  962. ]) =:= lists:sort(rpc:call(SlaveNode1, syn, get_members, [GroupName, with_meta])),
  963. true = rpc:call(SlaveNode1, syn, member, [Pid0, GroupName]),
  964. true = rpc:call(SlaveNode1, syn, member, [Pid0Changed, GroupName]),
  965. true = rpc:call(SlaveNode1, syn, member, [Pid1, GroupName]),
  966. true = rpc:call(SlaveNode1, syn, member, [Pid2, GroupName]),
  967. false = rpc:call(SlaveNode1, syn, member, [OtherPid, GroupName]),
  968. %% retrieve slave 2
  969. true = lists:sort([Pid0, Pid0Changed, Pid1, Pid2])
  970. =:= lists:sort(rpc:call(SlaveNode2, syn, get_members, [GroupName])),
  971. true = lists:sort([
  972. {Pid0, undefined},
  973. {Pid0Changed, {meta, changed}},
  974. {Pid1, undefined},
  975. {Pid2, {meta, 2}}
  976. ]) =:= lists:sort(rpc:call(SlaveNode2, syn, get_members, [GroupName, with_meta])),
  977. true = rpc:call(SlaveNode2, syn, member, [Pid0, GroupName]),
  978. true = rpc:call(SlaveNode2, syn, member, [Pid0Changed, GroupName]),
  979. true = rpc:call(SlaveNode2, syn, member, [Pid1, GroupName]),
  980. true = rpc:call(SlaveNode2, syn, member, [Pid2, GroupName]),
  981. false = rpc:call(SlaveNode2, syn, member, [OtherPid, GroupName]),
  982. %% disconnect everyone
  983. rpc:call(SlaveNode1, syn_test_suite_helper, disconnect_node, [SlaveNode2]),
  984. syn_test_suite_helper:disconnect_node(SlaveNode1),
  985. syn_test_suite_helper:disconnect_node(SlaveNode2),
  986. timer:sleep(1000),
  987. %% leave 0Changed
  988. ok = syn:leave(GroupName, Pid0Changed),
  989. %% retrieve local
  990. true = lists:sort([Pid0]) =:= lists:sort(syn:get_members(GroupName)),
  991. true = lists:sort([
  992. {Pid0, undefined}
  993. ]) =:= lists:sort(syn:get_members(GroupName, with_meta)),
  994. true = syn:member(Pid0, GroupName),
  995. false = syn:member(Pid0Changed, GroupName),
  996. false = syn:member(Pid1, GroupName),
  997. false = syn:member(Pid2, GroupName),
  998. false = syn:member(OtherPid, GroupName),
  999. %% reconnect all
  1000. syn_test_suite_helper:connect_node(SlaveNode1),
  1001. syn_test_suite_helper:connect_node(SlaveNode2),
  1002. rpc:call(SlaveNode1, syn_test_suite_helper, connect_node, [SlaveNode2]),
  1003. timer:sleep(1500),
  1004. %% retrieve local
  1005. true = lists:sort([Pid0, Pid1, Pid2]) =:= lists:sort(syn:get_members(GroupName)),
  1006. true = lists:sort([
  1007. {Pid0, undefined},
  1008. {Pid1, undefined},
  1009. {Pid2, {meta, 2}}
  1010. ]) =:= lists:sort(syn:get_members(GroupName, with_meta)),
  1011. true = syn:member(Pid0, GroupName),
  1012. false = syn:member(Pid0Changed, GroupName),
  1013. true = syn:member(Pid1, GroupName),
  1014. true = syn:member(Pid2, GroupName),
  1015. false = syn:member(OtherPid, GroupName),
  1016. %% retrieve slave 1
  1017. true = lists:sort([Pid0, Pid1, Pid2])
  1018. =:= lists:sort(rpc:call(SlaveNode1, syn, get_members, [GroupName])),
  1019. true = lists:sort([
  1020. {Pid0, undefined},
  1021. {Pid1, undefined},
  1022. {Pid2, {meta, 2}}
  1023. ]) =:= lists:sort(rpc:call(SlaveNode1, syn, get_members, [GroupName, with_meta])),
  1024. true = rpc:call(SlaveNode1, syn, member, [Pid0, GroupName]),
  1025. false = rpc:call(SlaveNode1, syn, member, [Pid0Changed, GroupName]),
  1026. true = rpc:call(SlaveNode1, syn, member, [Pid1, GroupName]),
  1027. true = rpc:call(SlaveNode1, syn, member, [Pid2, GroupName]),
  1028. false = rpc:call(SlaveNode1, syn, member, [OtherPid, GroupName]),
  1029. %% retrieve slave 2
  1030. true = lists:sort([Pid0, Pid1, Pid2])
  1031. =:= lists:sort(rpc:call(SlaveNode2, syn, get_members, [GroupName])),
  1032. true = lists:sort([
  1033. {Pid0, undefined},
  1034. {Pid1, undefined},
  1035. {Pid2, {meta, 2}}
  1036. ]) =:= lists:sort(rpc:call(SlaveNode2, syn, get_members, [GroupName, with_meta])),
  1037. true = rpc:call(SlaveNode2, syn, member, [Pid0, GroupName]),
  1038. false = rpc:call(SlaveNode2, syn, member, [Pid0Changed, GroupName]),
  1039. true = rpc:call(SlaveNode2, syn, member, [Pid1, GroupName]),
  1040. true = rpc:call(SlaveNode2, syn, member, [Pid2, GroupName]),
  1041. false = rpc:call(SlaveNode2, syn, member, [OtherPid, GroupName]).