syn_pg_SUITE.erl 82 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758
  1. %% ==========================================================================================================
  2. %% Syn - A global Process Registry and Process Group manager.
  3. %%
  4. %% The MIT License (MIT)
  5. %%
  6. %% Copyright (c) 2015-2021 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_pg_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. one_node_strict_mode/1
  35. ]).
  36. -export([
  37. three_nodes_discover/1,
  38. three_nodes_join_leave_and_monitor/1,
  39. three_nodes_join_filter_unknown_node/1,
  40. three_nodes_cluster_changes/1,
  41. three_nodes_custom_event_handler_joined_left/1,
  42. three_nodes_publish/1,
  43. three_nodes_multi_call/1,
  44. three_nodes_group_names/1
  45. ]).
  46. -export([
  47. four_nodes_concurrency/1
  48. ]).
  49. %% internals
  50. -export([
  51. subscriber_loop/2,
  52. recipient_loop/0
  53. ]).
  54. %% include
  55. -include_lib("common_test/include/ct.hrl").
  56. -include_lib("syn/src/syn.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, one_node_pg},
  70. {group, three_nodes_pg},
  71. {group, four_nodes_pg}
  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. {one_node_pg, [shuffle], [
  88. one_node_strict_mode
  89. ]},
  90. {three_nodes_pg, [shuffle], [
  91. three_nodes_discover,
  92. three_nodes_join_leave_and_monitor,
  93. three_nodes_join_filter_unknown_node,
  94. three_nodes_cluster_changes,
  95. three_nodes_custom_event_handler_joined_left,
  96. three_nodes_publish,
  97. three_nodes_multi_call,
  98. three_nodes_group_names
  99. ]},
  100. {four_nodes_pg, [shuffle], [
  101. four_nodes_concurrency
  102. ]}
  103. ].
  104. %% -------------------------------------------------------------------
  105. %% Function: init_per_suite(Config0) ->
  106. %% Config1 | {skip,Reason} |
  107. %% {skip_and_save,Reason,Config1}
  108. %% Config0 = Config1 = [tuple()]
  109. %% Reason = any()
  110. %% -------------------------------------------------------------------
  111. init_per_suite(Config) ->
  112. Config.
  113. %% -------------------------------------------------------------------
  114. %% Function: end_per_suite(Config0) -> void() | {save_config,Config1}
  115. %% Config0 = Config1 = [tuple()]
  116. %% -------------------------------------------------------------------
  117. end_per_suite(_Config) ->
  118. ok.
  119. %% -------------------------------------------------------------------
  120. %% Function: init_per_group(GroupName, Config0) ->
  121. %% Config1 | {skip,Reason} |
  122. %% {skip_and_save,Reason,Config1}
  123. %% GroupName = atom()
  124. %% Config0 = Config1 = [tuple()]
  125. %% Reason = any()
  126. %% -------------------------------------------------------------------
  127. init_per_group(three_nodes_pg, Config) ->
  128. case syn_test_suite_helper:init_cluster(3) of
  129. {error_initializing_cluster, Other} ->
  130. end_per_group(three_nodes_pg, Config),
  131. {skip, Other};
  132. NodesConfig ->
  133. NodesConfig ++ Config
  134. end;
  135. init_per_group(four_nodes_pg, Config) ->
  136. case syn_test_suite_helper:init_cluster(4) of
  137. {error_initializing_cluster, Other} ->
  138. end_per_group(four_nodes_pg, Config),
  139. {skip, Other};
  140. NodesConfig ->
  141. NodesConfig ++ Config
  142. end;
  143. init_per_group(_GroupName, Config) ->
  144. Config.
  145. %% -------------------------------------------------------------------
  146. %% Function: end_per_group(GroupName, Config0) ->
  147. %% void() | {save_config,Config1}
  148. %% GroupName = atom()
  149. %% Config0 = Config1 = [tuple()]
  150. %% -------------------------------------------------------------------
  151. end_per_group(three_nodes_pg, Config) ->
  152. syn_test_suite_helper:end_cluster(3, Config);
  153. end_per_group(four_nodes_pg, Config) ->
  154. syn_test_suite_helper:end_cluster(4, Config);
  155. end_per_group(_GroupName, _Config) ->
  156. syn_test_suite_helper:clean_after_test().
  157. %% -------------------------------------------------------------------
  158. %% Function: init_per_testcase(TestCase, Config0) ->
  159. %% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
  160. %% TestCase = atom()
  161. %% Config0 = Config1 = [tuple()]
  162. %% Reason = any()
  163. %% -------------------------------------------------------------------
  164. init_per_testcase(TestCase, Config) ->
  165. ct:pal("Starting test: ~p", [TestCase]),
  166. Config.
  167. %% -------------------------------------------------------------------
  168. %% Function: end_per_testcase(TestCase, Config0) ->
  169. %% void() | {save_config,Config1} | {fail,Reason}
  170. %% TestCase = atom()
  171. %% Config0 = Config1 = [tuple()]
  172. %% Reason = any()
  173. %% -------------------------------------------------------------------
  174. end_per_testcase(_, _Config) ->
  175. syn_test_suite_helper:clean_after_test().
  176. %% ===================================================================
  177. %% Tests
  178. %% ===================================================================
  179. one_node_strict_mode(_Config) ->
  180. %% start syn
  181. ok = syn:start(),
  182. syn:add_node_to_scopes([scope]),
  183. %% start process
  184. Pid = syn_test_suite_helper:start_process(),
  185. %% strict mode disabled
  186. ok = syn:join(scope, "strict-false", Pid, metadata),
  187. ok = syn:join(scope, "strict-false", Pid, metadata),
  188. {error, non_strict_update} = syn:join(scope, "strict-false", Pid, new_metadata),
  189. ok = syn:join(scope, "strict-false", Pid, metadata),
  190. [{Pid, metadata}] = syn:members(scope, "strict-false"),
  191. ok = syn:leave(scope, "strict-false", Pid),
  192. %% strict mode enabled
  193. application:set_env(syn, strict_mode, true),
  194. {error, not_self} = syn:join(scope, "strict-true", Pid, metadata),
  195. Self = self(),
  196. ok = syn:join(scope, "strict-true", Self, metadata),
  197. ok = syn:join(scope, "strict-true", Self, new_metadata),
  198. [{Self, new_metadata}] = syn:members(scope, "strict-true"),
  199. ok = syn:join(scope, "strict-true", Self),
  200. [{Self, undefined}] = syn:members(scope, "strict-true"),
  201. ok = syn:leave(scope, "strict-true", Self).
  202. three_nodes_discover(Config) ->
  203. %% get slaves
  204. SlaveNode1 = proplists:get_value(syn_slave_1, Config),
  205. SlaveNode2 = proplists:get_value(syn_slave_2, Config),
  206. %% start syn on nodes
  207. ok = syn:start(),
  208. ok = rpc:call(SlaveNode1, syn, start, []),
  209. ok = rpc:call(SlaveNode2, syn, start, []),
  210. %% add scopes
  211. ok = syn:add_node_to_scopes([scope_ab]),
  212. ok = syn:add_node_to_scopes([scope_all]),
  213. ok = rpc:call(SlaveNode1, syn, add_node_to_scopes, [[scope_ab, scope_bc, scope_all]]),
  214. ok = rpc:call(SlaveNode2, syn, add_node_to_scopes, [[scope_bc, scope_c, scope_all]]),
  215. %% subcluster_nodes should return invalid errors
  216. {'EXIT', {{invalid_scope, custom_abcdef}, _}} = (catch syn_registry:subcluster_nodes(custom_abcdef)),
  217. %% check
  218. syn_test_suite_helper:assert_pg_scope_subcluster(node(), scope_ab, [SlaveNode1]),
  219. syn_test_suite_helper:assert_pg_scope_subcluster(node(), scope_all, [SlaveNode1, SlaveNode2]),
  220. syn_test_suite_helper:assert_pg_scope_subcluster(SlaveNode1, scope_ab, [node()]),
  221. syn_test_suite_helper:assert_pg_scope_subcluster(SlaveNode1, scope_bc, [SlaveNode2]),
  222. syn_test_suite_helper:assert_pg_scope_subcluster(SlaveNode1, scope_all, [node(), SlaveNode2]),
  223. syn_test_suite_helper:assert_pg_scope_subcluster(SlaveNode2, scope_bc, [SlaveNode1]),
  224. syn_test_suite_helper:assert_pg_scope_subcluster(SlaveNode2, scope_c, []),
  225. syn_test_suite_helper:assert_pg_scope_subcluster(SlaveNode2, scope_all, [node(), SlaveNode1]),
  226. %% disconnect node 2 (node 1 can still see node 2)
  227. syn_test_suite_helper:disconnect_node(SlaveNode2),
  228. syn_test_suite_helper:assert_cluster(node(), [SlaveNode1]),
  229. syn_test_suite_helper:assert_cluster(SlaveNode1, [node(), SlaveNode2]),
  230. %% check
  231. syn_test_suite_helper:assert_pg_scope_subcluster(node(), scope_ab, [SlaveNode1]),
  232. syn_test_suite_helper:assert_pg_scope_subcluster(node(), scope_all, [SlaveNode1]),
  233. syn_test_suite_helper:assert_pg_scope_subcluster(SlaveNode1, scope_ab, [node()]),
  234. syn_test_suite_helper:assert_pg_scope_subcluster(SlaveNode1, scope_bc, [SlaveNode2]),
  235. syn_test_suite_helper:assert_pg_scope_subcluster(SlaveNode1, scope_all, [node(), SlaveNode2]),
  236. %% reconnect node 2
  237. syn_test_suite_helper:connect_node(SlaveNode2),
  238. syn_test_suite_helper:assert_cluster(node(), [SlaveNode1, SlaveNode2]),
  239. syn_test_suite_helper:assert_cluster(SlaveNode1, [node(), SlaveNode2]),
  240. syn_test_suite_helper:assert_cluster(SlaveNode2, [node(), SlaveNode1]),
  241. %% check
  242. syn_test_suite_helper:assert_pg_scope_subcluster(node(), scope_ab, [SlaveNode1]),
  243. syn_test_suite_helper:assert_pg_scope_subcluster(node(), scope_all, [SlaveNode1, SlaveNode2]),
  244. syn_test_suite_helper:assert_pg_scope_subcluster(SlaveNode1, scope_ab, [node()]),
  245. syn_test_suite_helper:assert_pg_scope_subcluster(SlaveNode1, scope_bc, [SlaveNode2]),
  246. syn_test_suite_helper:assert_pg_scope_subcluster(SlaveNode1, scope_all, [node(), SlaveNode2]),
  247. syn_test_suite_helper:assert_pg_scope_subcluster(SlaveNode2, scope_bc, [SlaveNode1]),
  248. syn_test_suite_helper:assert_pg_scope_subcluster(SlaveNode2, scope_c, []),
  249. syn_test_suite_helper:assert_pg_scope_subcluster(SlaveNode2, scope_all, [node(), SlaveNode1]),
  250. %% crash scope processes
  251. rpc:call(SlaveNode2, syn_test_suite_helper, kill_process, [syn_registry_scope_bc]),
  252. rpc:call(SlaveNode2, syn_test_suite_helper, wait_process_name_ready, [syn_registry_scope_bc]),
  253. %% check
  254. syn_test_suite_helper:assert_pg_scope_subcluster(node(), scope_ab, [SlaveNode1]),
  255. syn_test_suite_helper:assert_pg_scope_subcluster(node(), scope_all, [SlaveNode1, SlaveNode2]),
  256. syn_test_suite_helper:assert_pg_scope_subcluster(SlaveNode1, scope_ab, [node()]),
  257. syn_test_suite_helper:assert_pg_scope_subcluster(SlaveNode1, scope_bc, [SlaveNode2]),
  258. syn_test_suite_helper:assert_pg_scope_subcluster(SlaveNode1, scope_all, [node(), SlaveNode2]),
  259. syn_test_suite_helper:assert_pg_scope_subcluster(SlaveNode2, scope_bc, [SlaveNode1]),
  260. syn_test_suite_helper:assert_pg_scope_subcluster(SlaveNode2, scope_c, []),
  261. syn_test_suite_helper:assert_pg_scope_subcluster(SlaveNode2, scope_all, [node(), SlaveNode1]),
  262. %% crash scopes supervisor on local
  263. syn_test_suite_helper:kill_process(syn_scopes_sup),
  264. syn_test_suite_helper:wait_process_name_ready(syn_registry_scope_all),
  265. %% check
  266. syn_test_suite_helper:assert_pg_scope_subcluster(node(), scope_ab, [SlaveNode1]),
  267. syn_test_suite_helper:assert_pg_scope_subcluster(node(), scope_all, [SlaveNode1, SlaveNode2]),
  268. syn_test_suite_helper:assert_pg_scope_subcluster(SlaveNode1, scope_ab, [node()]),
  269. syn_test_suite_helper:assert_pg_scope_subcluster(SlaveNode1, scope_bc, [SlaveNode2]),
  270. syn_test_suite_helper:assert_pg_scope_subcluster(SlaveNode1, scope_all, [node(), SlaveNode2]),
  271. syn_test_suite_helper:assert_pg_scope_subcluster(SlaveNode2, scope_bc, [SlaveNode1]),
  272. syn_test_suite_helper:assert_pg_scope_subcluster(SlaveNode2, scope_c, []),
  273. syn_test_suite_helper:assert_pg_scope_subcluster(SlaveNode2, scope_all, [node(), SlaveNode1]).
  274. three_nodes_join_leave_and_monitor(Config) ->
  275. %% get slaves
  276. SlaveNode1 = proplists:get_value(syn_slave_1, Config),
  277. SlaveNode2 = proplists:get_value(syn_slave_2, Config),
  278. %% start syn on nodes
  279. ok = syn:start(),
  280. ok = rpc:call(SlaveNode1, syn, start, []),
  281. ok = rpc:call(SlaveNode2, syn, start, []),
  282. %% add scopes
  283. ok = syn:add_node_to_scopes([scope_ab]),
  284. ok = rpc:call(SlaveNode1, syn, add_node_to_scopes, [[scope_ab, scope_bc]]),
  285. ok = rpc:call(SlaveNode2, syn, add_node_to_scopes, [[scope_bc]]),
  286. %% start processes
  287. Pid = syn_test_suite_helper:start_process(),
  288. PidWithMeta = syn_test_suite_helper:start_process(),
  289. PidRemoteOn1 = syn_test_suite_helper:start_process(SlaveNode1),
  290. PidRemoteOn2 = syn_test_suite_helper:start_process(SlaveNode2),
  291. %% check
  292. [] = syn:members(scope_ab, {group, "one"}),
  293. [] = rpc:call(SlaveNode1, syn, members, [scope_ab, {group, "one"}]),
  294. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, members, [scope_ab, {group, "one"}])),
  295. false = syn:is_member(scope_ab, {group, "one"}, Pid),
  296. false = syn:is_member(scope_ab, {group, "one"}, PidWithMeta),
  297. false = syn:is_member(scope_ab, {group, "one"}, PidRemoteOn1),
  298. false = rpc:call(SlaveNode1, syn, is_member, [scope_ab, {group, "one"}, Pid]),
  299. false = rpc:call(SlaveNode1, syn, is_member, [scope_ab, {group, "one"}, PidWithMeta]),
  300. false = rpc:call(SlaveNode1, syn, is_member, [scope_ab, {group, "one"}, PidRemoteOn1]),
  301. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, is_member, [scope_ab, {group, "one"}, Pid])),
  302. [] = syn:local_members(scope_ab, {group, "one"}),
  303. [] = rpc:call(SlaveNode1, syn, local_members, [scope_ab, {group, "one"}]),
  304. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, local_members, [scope_ab, {group, "one"}])),
  305. false = syn:is_local_member(scope_ab, {group, "one"}, Pid),
  306. false = syn:is_local_member(scope_ab, {group, "one"}, PidWithMeta),
  307. false = syn:is_local_member(scope_ab, {group, "one"}, PidRemoteOn1),
  308. false = rpc:call(SlaveNode1, syn, is_local_member, [scope_ab, {group, "one"}, Pid]),
  309. false = rpc:call(SlaveNode1, syn, is_local_member, [scope_ab, {group, "one"}, PidWithMeta]),
  310. false = rpc:call(SlaveNode1, syn, is_local_member, [scope_ab, {group, "one"}, PidRemoteOn1]),
  311. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, is_local_member, [scope_ab, {group, "one"}, Pid])),
  312. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:members(scope_bc, {group, "one"})),
  313. [] = rpc:call(SlaveNode1, syn, members, [scope_bc, {group, "one"}]),
  314. [] = rpc:call(SlaveNode2, syn, members, [scope_bc, {group, "one"}]),
  315. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:local_members(scope_bc, {group, "one"})),
  316. [] = rpc:call(SlaveNode1, syn, local_members, [scope_bc, {group, "one"}]),
  317. [] = rpc:call(SlaveNode2, syn, local_members, [scope_bc, {group, "one"}]),
  318. [] = syn:members(scope_ab, {group, "two"}),
  319. [] = rpc:call(SlaveNode1, syn, members, [scope_ab, {group, "two"}]),
  320. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, members, [scope_ab, {group, "two"}])),
  321. false = syn:is_member(scope_ab, {group, "two"}, Pid),
  322. false = syn:is_member(scope_ab, {group, "two"}, PidWithMeta),
  323. false = syn:is_member(scope_ab, {group, "two"}, PidRemoteOn1),
  324. false = rpc:call(SlaveNode1, syn, is_member, [scope_ab, {group, "two"}, Pid]),
  325. false = rpc:call(SlaveNode1, syn, is_member, [scope_ab, {group, "two"}, PidWithMeta]),
  326. false = rpc:call(SlaveNode1, syn, is_member, [scope_ab, {group, "two"}, PidRemoteOn1]),
  327. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, is_member, [scope_ab, {group, "two"}, Pid])),
  328. [] = syn:local_members(scope_ab, {group, "two"}),
  329. [] = rpc:call(SlaveNode1, syn, local_members, [scope_ab, {group, "two"}]),
  330. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, local_members, [scope_ab, {group, "two"}])),
  331. false = syn:is_local_member(scope_ab, {group, "two"}, Pid),
  332. false = syn:is_local_member(scope_ab, {group, "two"}, PidWithMeta),
  333. false = syn:is_local_member(scope_ab, {group, "two"}, PidRemoteOn1),
  334. false = rpc:call(SlaveNode1, syn, is_local_member, [scope_ab, {group, "two"}, Pid]),
  335. false = rpc:call(SlaveNode1, syn, is_local_member, [scope_ab, {group, "two"}, PidWithMeta]),
  336. false = rpc:call(SlaveNode1, syn, is_local_member, [scope_ab, {group, "two"}, PidRemoteOn1]),
  337. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, is_local_member, [scope_ab, {group, "two"}, Pid])),
  338. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:members(scope_bc, {group, "two"})),
  339. [] = rpc:call(SlaveNode1, syn, members, [scope_bc, {group, "two"}]),
  340. [] = rpc:call(SlaveNode2, syn, members, [scope_bc, {group, "two"}]),
  341. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:local_members(scope_bc, {group, "two"})),
  342. [] = rpc:call(SlaveNode1, syn, local_members, [scope_bc, {group, "two"}]),
  343. [] = rpc:call(SlaveNode2, syn, local_members, [scope_bc, {group, "two"}]),
  344. %% join
  345. ok = syn:join(scope_ab, {group, "one"}, Pid),
  346. ok = syn:join(scope_ab, {group, "one"}, PidWithMeta, <<"with meta">>),
  347. ok = rpc:call(SlaveNode1, syn, join, [scope_bc, {group, "two"}, PidRemoteOn1]),
  348. ok = syn:join(scope_ab, {group, "two"}, Pid),
  349. ok = syn:join(scope_ab, {group, "two"}, PidWithMeta, "with-meta-2"),
  350. %% errors
  351. {error, not_alive} = syn:join(scope_ab, {"pid not alive"}, list_to_pid("<0.9999.0>")),
  352. {error, not_in_group} = syn:leave(scope_ab, {group, "three"}, Pid),
  353. {'EXIT', {{invalid_remote_scope, scope_ab, SlaveNode2}, _}} = (catch syn:join(scope_ab, {group, "one"}, PidRemoteOn2)),
  354. %% retrieve
  355. syn_test_suite_helper:assert_wait(
  356. lists:sort([{Pid, undefined}, {PidWithMeta, <<"with meta">>}]),
  357. fun() -> lists:sort(syn:members(scope_ab, {group, "one"})) end
  358. ),
  359. syn_test_suite_helper:assert_wait(
  360. lists:sort([{Pid, undefined}, {PidWithMeta, <<"with meta">>}]),
  361. fun() -> lists:sort(rpc:call(SlaveNode1, syn, members, [scope_ab, {group, "one"}])) end
  362. ),
  363. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, members, [scope_ab, {group, "one"}])),
  364. true = syn:is_member(scope_ab, {group, "one"}, Pid),
  365. true = syn:is_member(scope_ab, {group, "one"}, PidWithMeta),
  366. false = syn:is_member(scope_ab, {group, "one"}, PidRemoteOn1),
  367. true = rpc:call(SlaveNode1, syn, is_member, [scope_ab, {group, "one"}, Pid]),
  368. true = rpc:call(SlaveNode1, syn, is_member, [scope_ab, {group, "one"}, PidWithMeta]),
  369. false = rpc:call(SlaveNode1, syn, is_member, [scope_ab, {group, "one"}, PidRemoteOn1]),
  370. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, is_member, [scope_ab, {group, "one"}, Pid])),
  371. syn_test_suite_helper:assert_wait(
  372. lists:sort([{Pid, undefined}, {PidWithMeta, <<"with meta">>}]),
  373. fun() -> lists:sort(syn:local_members(scope_ab, {group, "one"})) end
  374. ),
  375. syn_test_suite_helper:assert_wait(
  376. [],
  377. fun() -> lists:sort(rpc:call(SlaveNode1, syn, local_members, [scope_ab, {group, "one"}])) end
  378. ),
  379. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, local_members, [scope_ab, {group, "one"}])),
  380. true = syn:is_local_member(scope_ab, {group, "one"}, Pid),
  381. true = syn:is_local_member(scope_ab, {group, "one"}, PidWithMeta),
  382. false = syn:is_local_member(scope_ab, {group, "one"}, PidRemoteOn1),
  383. false = rpc:call(SlaveNode1, syn, is_local_member, [scope_ab, {group, "one"}, Pid]),
  384. false = rpc:call(SlaveNode1, syn, is_local_member, [scope_ab, {group, "one"}, PidWithMeta]),
  385. false = rpc:call(SlaveNode1, syn, is_local_member, [scope_ab, {group, "one"}, PidRemoteOn1]),
  386. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, is_local_member, [scope_ab, {group, "one"}, Pid])),
  387. syn_test_suite_helper:assert_wait(
  388. lists:sort([{Pid, undefined}, {PidWithMeta, "with-meta-2"}]),
  389. fun() -> lists:sort(syn:members(scope_ab, {group, "two"})) end
  390. ),
  391. syn_test_suite_helper:assert_wait(
  392. lists:sort([{Pid, undefined}, {PidWithMeta, "with-meta-2"}]),
  393. fun() -> lists:sort(rpc:call(SlaveNode1, syn, members, [scope_ab, {group, "two"}])) end
  394. ),
  395. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, members, [scope_ab, {group, "two"}])),
  396. true = syn:is_member(scope_ab, {group, "two"}, Pid),
  397. true = syn:is_member(scope_ab, {group, "two"}, PidWithMeta),
  398. false = syn:is_member(scope_ab, {group, "two"}, PidRemoteOn1),
  399. true = rpc:call(SlaveNode1, syn, is_member, [scope_ab, {group, "two"}, Pid]),
  400. true = rpc:call(SlaveNode1, syn, is_member, [scope_ab, {group, "two"}, PidWithMeta]),
  401. false = rpc:call(SlaveNode1, syn, is_member, [scope_ab, {group, "two"}, PidRemoteOn1]),
  402. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, is_member, [scope_ab, {group, "two"}, Pid])),
  403. syn_test_suite_helper:assert_wait(
  404. lists:sort([{Pid, undefined}, {PidWithMeta, "with-meta-2"}]),
  405. fun() -> lists:sort(syn:local_members(scope_ab, {group, "two"})) end
  406. ),
  407. syn_test_suite_helper:assert_wait(
  408. [],
  409. fun() -> lists:sort(rpc:call(SlaveNode1, syn, local_members, [scope_ab, {group, "two"}])) end
  410. ),
  411. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, local_members, [scope_ab, {group, "two"}])),
  412. true = syn:is_local_member(scope_ab, {group, "two"}, Pid),
  413. true = syn:is_local_member(scope_ab, {group, "two"}, PidWithMeta),
  414. false = syn:is_local_member(scope_ab, {group, "two"}, PidRemoteOn1),
  415. false = rpc:call(SlaveNode1, syn, is_local_member, [scope_ab, {group, "two"}, Pid]),
  416. false = rpc:call(SlaveNode1, syn, is_local_member, [scope_ab, {group, "two"}, PidWithMeta]),
  417. false = rpc:call(SlaveNode1, syn, is_local_member, [scope_ab, {group, "two"}, PidRemoteOn1]),
  418. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, is_local_member, [scope_ab, {group, "two"}, Pid])),
  419. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:members(scope_bc, {group, "two"})),
  420. syn_test_suite_helper:assert_wait(
  421. [{PidRemoteOn1, undefined}],
  422. fun() -> lists:sort(rpc:call(SlaveNode1, syn, members, [scope_bc, {group, "two"}])) end
  423. ),
  424. syn_test_suite_helper:assert_wait(
  425. [{PidRemoteOn1, undefined}],
  426. fun() -> lists:sort(rpc:call(SlaveNode2, syn, members, [scope_bc, {group, "two"}])) end
  427. ),
  428. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:local_members(scope_bc, {group, "two"})),
  429. syn_test_suite_helper:assert_wait(
  430. [{PidRemoteOn1, undefined}],
  431. fun() -> lists:sort(rpc:call(SlaveNode1, syn, local_members, [scope_bc, {group, "two"}])) end
  432. ),
  433. syn_test_suite_helper:assert_wait(
  434. [],
  435. fun() -> lists:sort(rpc:call(SlaveNode2, syn, local_members, [scope_bc, {group, "two"}])) end
  436. ),
  437. 2 = syn:group_count(scope_ab),
  438. 2 = syn:group_count(scope_ab, node()),
  439. 0 = syn:group_count(scope_ab, SlaveNode1),
  440. 0 = syn:group_count(scope_ab, SlaveNode2),
  441. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:group_count(scope_bc)),
  442. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:group_count(scope_bc, node())),
  443. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:group_count(scope_bc, SlaveNode1)),
  444. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:group_count(scope_bc, SlaveNode2)),
  445. 2 = rpc:call(SlaveNode1, syn, group_count, [scope_ab]),
  446. 2 = rpc:call(SlaveNode1, syn, group_count, [scope_ab, node()]),
  447. 0 = rpc:call(SlaveNode1, syn, group_count, [scope_ab, SlaveNode1]),
  448. 0 = rpc:call(SlaveNode1, syn, group_count, [scope_ab, SlaveNode2]),
  449. 1 = rpc:call(SlaveNode1, syn, group_count, [scope_bc]),
  450. 0 = rpc:call(SlaveNode1, syn, group_count, [scope_bc, node()]),
  451. 1 = rpc:call(SlaveNode1, syn, group_count, [scope_bc, SlaveNode1]),
  452. 0 = rpc:call(SlaveNode1, syn, group_count, [scope_bc, SlaveNode2]),
  453. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, group_count, [scope_ab])),
  454. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, group_count, [scope_ab, node()])),
  455. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, group_count, [scope_ab, SlaveNode1])),
  456. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, group_count, [scope_ab, SlaveNode2])),
  457. 1 = rpc:call(SlaveNode2, syn, group_count, [scope_bc]),
  458. 0 = rpc:call(SlaveNode2, syn, group_count, [scope_bc, node()]),
  459. 1 = rpc:call(SlaveNode2, syn, group_count, [scope_bc, SlaveNode1]),
  460. 0 = rpc:call(SlaveNode2, syn, group_count, [scope_bc, SlaveNode2]),
  461. %% enable strict to allow for updates
  462. application:set_env(syn, strict_mode, true),
  463. rpc:call(SlaveNode1, application, set_env, [syn, strict_mode, true]),
  464. %% re-join to edit meta (strict is enabled, so send message)
  465. PidWithMeta ! {pg_update_meta, scope_ab, {group, "one"}, <<"with updated meta">>},
  466. PidRemoteOn1 ! {pg_update_meta, scope_bc, {group, "two"}, added_meta},
  467. %% retrieve
  468. syn_test_suite_helper:assert_wait(
  469. lists:sort([{Pid, undefined}, {PidWithMeta, <<"with updated meta">>}]),
  470. fun() -> lists:sort(syn:members(scope_ab, {group, "one"})) end
  471. ),
  472. syn_test_suite_helper:assert_wait(
  473. lists:sort([{Pid, undefined}, {PidWithMeta, <<"with updated meta">>}]),
  474. fun() -> lists:sort(rpc:call(SlaveNode1, syn, members, [scope_ab, {group, "one"}])) end
  475. ),
  476. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, members, [scope_ab, {group, "one"}])),
  477. syn_test_suite_helper:assert_wait(
  478. lists:sort([{Pid, undefined}, {PidWithMeta, <<"with updated meta">>}]),
  479. fun() -> lists:sort(syn:local_members(scope_ab, {group, "one"})) end
  480. ),
  481. syn_test_suite_helper:assert_wait(
  482. [],
  483. fun() -> lists:sort(rpc:call(SlaveNode1, syn, local_members, [scope_ab, {group, "one"}])) end
  484. ),
  485. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, local_members, [scope_ab, {group, "one"}])),
  486. syn_test_suite_helper:assert_wait(
  487. lists:sort([{Pid, undefined}, {PidWithMeta, "with-meta-2"}]),
  488. fun() -> lists:sort(syn:members(scope_ab, {group, "two"})) end
  489. ),
  490. syn_test_suite_helper:assert_wait(
  491. lists:sort([{Pid, undefined}, {PidWithMeta, "with-meta-2"}]),
  492. fun() -> lists:sort(rpc:call(SlaveNode1, syn, members, [scope_ab, {group, "two"}])) end
  493. ),
  494. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, members, [scope_ab, {group, "two"}])),
  495. syn_test_suite_helper:assert_wait(
  496. lists:sort([{Pid, undefined}, {PidWithMeta, "with-meta-2"}]),
  497. fun() -> lists:sort(syn:local_members(scope_ab, {group, "two"})) end
  498. ),
  499. syn_test_suite_helper:assert_wait(
  500. [],
  501. fun() -> lists:sort(rpc:call(SlaveNode1, syn, local_members, [scope_ab, {group, "two"}])) end
  502. ),
  503. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, local_members, [scope_ab, {group, "two"}])),
  504. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:members(scope_bc, {group, "two"})),
  505. syn_test_suite_helper:assert_wait(
  506. [{PidRemoteOn1, added_meta}],
  507. fun() -> lists:sort(rpc:call(SlaveNode1, syn, members, [scope_bc, {group, "two"}])) end
  508. ),
  509. syn_test_suite_helper:assert_wait(
  510. [{PidRemoteOn1, added_meta}],
  511. fun() -> lists:sort(rpc:call(SlaveNode2, syn, members, [scope_bc, {group, "two"}])) end
  512. ),
  513. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:local_members(scope_bc, {group, "two"})),
  514. syn_test_suite_helper:assert_wait(
  515. [{PidRemoteOn1, added_meta}],
  516. fun() -> lists:sort(rpc:call(SlaveNode1, syn, local_members, [scope_bc, {group, "two"}])) end
  517. ),
  518. syn_test_suite_helper:assert_wait(
  519. [],
  520. fun() -> lists:sort(rpc:call(SlaveNode2, syn, local_members, [scope_bc, {group, "two"}])) end
  521. ),
  522. 2 = syn:group_count(scope_ab),
  523. 2 = syn:group_count(scope_ab, node()),
  524. 0 = syn:group_count(scope_ab, SlaveNode1),
  525. 0 = syn:group_count(scope_ab, SlaveNode2),
  526. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:group_count(scope_bc)),
  527. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:group_count(scope_bc, node())),
  528. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:group_count(scope_bc, SlaveNode1)),
  529. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:group_count(scope_bc, SlaveNode2)),
  530. 2 = rpc:call(SlaveNode1, syn, group_count, [scope_ab]),
  531. 2 = rpc:call(SlaveNode1, syn, group_count, [scope_ab, node()]),
  532. 0 = rpc:call(SlaveNode1, syn, group_count, [scope_ab, SlaveNode1]),
  533. 0 = rpc:call(SlaveNode1, syn, group_count, [scope_ab, SlaveNode2]),
  534. 1 = rpc:call(SlaveNode1, syn, group_count, [scope_bc]),
  535. 0 = rpc:call(SlaveNode1, syn, group_count, [scope_bc, node()]),
  536. 1 = rpc:call(SlaveNode1, syn, group_count, [scope_bc, SlaveNode1]),
  537. 0 = rpc:call(SlaveNode1, syn, group_count, [scope_bc, SlaveNode2]),
  538. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, group_count, [scope_ab])),
  539. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, group_count, [scope_ab, node()])),
  540. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, group_count, [scope_ab, SlaveNode1])),
  541. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, group_count, [scope_ab, SlaveNode2])),
  542. 1 = rpc:call(SlaveNode2, syn, group_count, [scope_bc]),
  543. 0 = rpc:call(SlaveNode2, syn, group_count, [scope_bc, node()]),
  544. 1 = rpc:call(SlaveNode2, syn, group_count, [scope_bc, SlaveNode1]),
  545. 0 = rpc:call(SlaveNode2, syn, group_count, [scope_bc, SlaveNode2]),
  546. %% disable strict
  547. application:set_env(syn, strict_mode, false),
  548. rpc:call(SlaveNode1, application, set_env, [syn, strict_mode, false]),
  549. ok = syn:join(scope_ab, {group, "two"}, PidRemoteOn1),
  550. syn_test_suite_helper:assert_wait(
  551. lists:sort([{Pid, undefined}, {PidWithMeta, "with-meta-2"}, {PidRemoteOn1, undefined}]),
  552. fun() -> lists:sort(syn:members(scope_ab, {group, "two"})) end
  553. ),
  554. %% crash scope process to ensure that monitors get recreated
  555. exit(whereis(syn_pg_scope_ab), kill),
  556. syn_test_suite_helper:wait_process_name_ready(syn_pg_scope_ab),
  557. syn_test_suite_helper:assert_wait(
  558. lists:sort([{Pid, undefined}, {PidWithMeta, "with-meta-2"}, {PidRemoteOn1, undefined}]),
  559. fun() -> lists:sort(syn:members(scope_ab, {group, "two"})) end
  560. ),
  561. %% kill process
  562. syn_test_suite_helper:kill_process(Pid),
  563. syn_test_suite_helper:kill_process(PidRemoteOn1),
  564. %% leave
  565. ok = syn:leave(scope_ab, {group, "one"}, PidWithMeta),
  566. %% retrieve
  567. syn_test_suite_helper:assert_wait(
  568. [],
  569. fun() -> lists:sort(syn:members(scope_ab, {group, "one"})) end
  570. ),
  571. syn_test_suite_helper:assert_wait(
  572. [],
  573. fun() -> lists:sort(rpc:call(SlaveNode1, syn, members, [scope_ab, {group, "one"}])) end
  574. ),
  575. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, members, [scope_ab, {group, "one"}])),
  576. false = syn:is_member(scope_ab, {group, "one"}, Pid),
  577. false = syn:is_member(scope_ab, {group, "one"}, PidWithMeta),
  578. false = syn:is_member(scope_ab, {group, "one"}, PidRemoteOn1),
  579. false = rpc:call(SlaveNode1, syn, is_member, [scope_ab, {group, "one"}, Pid]),
  580. false = rpc:call(SlaveNode1, syn, is_member, [scope_ab, {group, "one"}, PidWithMeta]),
  581. false = rpc:call(SlaveNode1, syn, is_member, [scope_ab, {group, "one"}, PidRemoteOn1]),
  582. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, is_member, [scope_ab, {group, "one"}, Pid])),
  583. syn_test_suite_helper:assert_wait(
  584. [],
  585. fun() -> lists:sort(syn:local_members(scope_ab, {group, "one"})) end
  586. ),
  587. syn_test_suite_helper:assert_wait(
  588. [],
  589. fun() -> lists:sort(rpc:call(SlaveNode1, syn, local_members, [scope_ab, {group, "one"}])) end
  590. ),
  591. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, local_members, [scope_ab, {group, "one"}])),
  592. false = syn:is_local_member(scope_ab, {group, "one"}, Pid),
  593. false = syn:is_local_member(scope_ab, {group, "one"}, PidWithMeta),
  594. false = syn:is_local_member(scope_ab, {group, "one"}, PidRemoteOn1),
  595. false = rpc:call(SlaveNode1, syn, is_local_member, [scope_ab, {group, "one"}, Pid]),
  596. false = rpc:call(SlaveNode1, syn, is_local_member, [scope_ab, {group, "one"}, PidWithMeta]),
  597. false = rpc:call(SlaveNode1, syn, is_local_member, [scope_ab, {group, "one"}, PidRemoteOn1]),
  598. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, is_local_member, [scope_ab, {group, "one"}, Pid])),
  599. syn_test_suite_helper:assert_wait(
  600. [{PidWithMeta, "with-meta-2"}],
  601. fun() -> lists:sort(syn:members(scope_ab, {group, "two"})) end
  602. ),
  603. syn_test_suite_helper:assert_wait(
  604. [{PidWithMeta, "with-meta-2"}],
  605. fun() -> lists:sort(rpc:call(SlaveNode1, syn, members, [scope_ab, {group, "two"}])) end
  606. ),
  607. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, members, [scope_ab, {group, "two"}])),
  608. false = syn:is_member(scope_ab, {group, "two"}, Pid),
  609. true = syn:is_member(scope_ab, {group, "two"}, PidWithMeta),
  610. false = syn:is_member(scope_ab, {group, "two"}, PidRemoteOn1),
  611. false = rpc:call(SlaveNode1, syn, is_member, [scope_ab, {group, "two"}, Pid]),
  612. true = rpc:call(SlaveNode1, syn, is_member, [scope_ab, {group, "two"}, PidWithMeta]),
  613. false = rpc:call(SlaveNode1, syn, is_member, [scope_ab, {group, "two"}, PidRemoteOn1]),
  614. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, is_member, [scope_ab, {group, "two"}, Pid])),
  615. syn_test_suite_helper:assert_wait(
  616. [{PidWithMeta, "with-meta-2"}],
  617. fun() -> lists:sort(syn:local_members(scope_ab, {group, "two"})) end
  618. ),
  619. syn_test_suite_helper:assert_wait(
  620. [],
  621. fun() -> lists:sort(rpc:call(SlaveNode1, syn, local_members, [scope_ab, {group, "two"}])) end
  622. ),
  623. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, local_members, [scope_ab, {group, "two"}])),
  624. false = syn:is_local_member(scope_ab, {group, "two"}, Pid),
  625. true = syn:is_local_member(scope_ab, {group, "two"}, PidWithMeta),
  626. false = syn:is_local_member(scope_ab, {group, "two"}, PidRemoteOn1),
  627. false = rpc:call(SlaveNode1, syn, is_local_member, [scope_ab, {group, "two"}, Pid]),
  628. false = rpc:call(SlaveNode1, syn, is_local_member, [scope_ab, {group, "two"}, PidWithMeta]),
  629. false = rpc:call(SlaveNode1, syn, is_local_member, [scope_ab, {group, "two"}, PidRemoteOn1]),
  630. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, is_local_member, [scope_ab, {group, "two"}, Pid])),
  631. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:members(scope_bc, {group, "two"})),
  632. syn_test_suite_helper:assert_wait(
  633. [],
  634. fun() -> lists:sort(rpc:call(SlaveNode1, syn, members, [scope_bc, {group, "two"}])) end
  635. ),
  636. syn_test_suite_helper:assert_wait(
  637. [], fun() -> lists:sort(rpc:call(SlaveNode2, syn, members, [scope_bc, {group, "two"}])) end
  638. ),
  639. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:local_members(scope_bc, {group, "two"})),
  640. syn_test_suite_helper:assert_wait(
  641. [],
  642. fun() -> lists:sort(rpc:call(SlaveNode1, syn, local_members, [scope_bc, {group, "two"}])) end
  643. ),
  644. syn_test_suite_helper:assert_wait(
  645. [], fun() -> lists:sort(rpc:call(SlaveNode2, syn, local_members, [scope_bc, {group, "two"}])) end
  646. ),
  647. 1 = syn:group_count(scope_ab),
  648. 1 = syn:group_count(scope_ab, node()),
  649. 0 = syn:group_count(scope_ab, SlaveNode1),
  650. 0 = syn:group_count(scope_ab, SlaveNode2),
  651. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:group_count(scope_bc)),
  652. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:group_count(scope_bc, node())),
  653. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:group_count(scope_bc, SlaveNode1)),
  654. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:group_count(scope_bc, SlaveNode2)),
  655. 1 = rpc:call(SlaveNode1, syn, group_count, [scope_ab]),
  656. 1 = rpc:call(SlaveNode1, syn, group_count, [scope_ab, node()]),
  657. 0 = rpc:call(SlaveNode1, syn, group_count, [scope_ab, SlaveNode1]),
  658. 0 = rpc:call(SlaveNode1, syn, group_count, [scope_ab, SlaveNode2]),
  659. 0 = rpc:call(SlaveNode1, syn, group_count, [scope_bc]),
  660. 0 = rpc:call(SlaveNode1, syn, group_count, [scope_bc, node()]),
  661. 0 = rpc:call(SlaveNode1, syn, group_count, [scope_bc, SlaveNode1]),
  662. 0 = rpc:call(SlaveNode1, syn, group_count, [scope_bc, SlaveNode2]),
  663. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, group_count, [scope_ab])),
  664. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, group_count, [scope_ab, node()])),
  665. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, group_count, [scope_ab, SlaveNode1])),
  666. {badrpc, {'EXIT', {{invalid_scope, scope_ab}, _}}} = (catch rpc:call(SlaveNode2, syn, group_count, [scope_ab, SlaveNode2])),
  667. 0 = rpc:call(SlaveNode2, syn, group_count, [scope_bc]),
  668. 0 = rpc:call(SlaveNode2, syn, group_count, [scope_bc, node()]),
  669. 0 = rpc:call(SlaveNode2, syn, group_count, [scope_bc, SlaveNode1]),
  670. 0 = rpc:call(SlaveNode2, syn, group_count, [scope_bc, SlaveNode2]),
  671. %% errors
  672. {error, not_in_group} = syn:leave(scope_ab, {group, "one"}, PidWithMeta).
  673. three_nodes_join_filter_unknown_node(Config) ->
  674. %% get slaves
  675. SlaveNode1 = proplists:get_value(syn_slave_1, Config),
  676. SlaveNode2 = proplists:get_value(syn_slave_2, Config),
  677. %% start syn on 1 and 2
  678. ok = rpc:call(SlaveNode1, syn, start, []),
  679. ok = rpc:call(SlaveNode2, syn, start, []),
  680. %% add scopes
  681. ok = rpc:call(SlaveNode1, syn, add_node_to_scopes, [[scope_bc]]),
  682. ok = rpc:call(SlaveNode2, syn, add_node_to_scopes, [[scope_bc]]),
  683. %% send sync message from out of scope node
  684. InvalidPid = syn_test_suite_helper:start_process(),
  685. {syn_pg_scope_bc, SlaveNode1} ! {'3.0', sync_join, <<"group-name">>, InvalidPid, undefined, os:system_time(millisecond), normal},
  686. %% check
  687. false = rpc:call(SlaveNode1, syn, is_member, [scope_bc, <<"group-name">>, InvalidPid]).
  688. three_nodes_cluster_changes(Config) ->
  689. %% get slaves
  690. SlaveNode1 = proplists:get_value(syn_slave_1, Config),
  691. SlaveNode2 = proplists:get_value(syn_slave_2, Config),
  692. %% disconnect 1 from 2
  693. rpc:call(SlaveNode1, syn_test_suite_helper, disconnect_node, [SlaveNode2]),
  694. syn_test_suite_helper:assert_cluster(node(), [SlaveNode1, SlaveNode2]),
  695. syn_test_suite_helper:assert_cluster(SlaveNode1, [node()]),
  696. syn_test_suite_helper:assert_cluster(SlaveNode2, [node()]),
  697. %% start syn on 1 and 2, nodes don't know of each other
  698. ok = rpc:call(SlaveNode1, syn, start, []),
  699. ok = rpc:call(SlaveNode2, syn, start, []),
  700. %% add scopes
  701. ok = rpc:call(SlaveNode1, syn, add_node_to_scopes, [[scope_all, scope_bc]]),
  702. ok = rpc:call(SlaveNode2, syn, add_node_to_scopes, [[scope_all, scope_bc]]),
  703. %% start processes
  704. PidRemoteOn1 = syn_test_suite_helper:start_process(SlaveNode1),
  705. PidRemoteOn2 = syn_test_suite_helper:start_process(SlaveNode2),
  706. %% join
  707. ok = rpc:call(SlaveNode1, syn, join, [scope_all, <<"common-group">>, PidRemoteOn1, "meta-1"]),
  708. ok = rpc:call(SlaveNode2, syn, join, [scope_all, <<"common-group">>, PidRemoteOn2, "meta-2"]),
  709. ok = rpc:call(SlaveNode2, syn, join, [scope_all, <<"group-2">>, PidRemoteOn2, "other-meta"]),
  710. ok = rpc:call(SlaveNode1, syn, join, [scope_bc, <<"scoped-on-bc">>, PidRemoteOn1, "scoped-meta-1"]),
  711. ok = rpc:call(SlaveNode2, syn, join, [scope_bc, <<"scoped-on-bc">>, PidRemoteOn2, "scoped-meta-2"]),
  712. %% form full cluster
  713. ok = syn:start(),
  714. ok = syn:add_node_to_scopes([scope_all]),
  715. rpc:call(SlaveNode1, syn_test_suite_helper, connect_node, [SlaveNode2]),
  716. syn_test_suite_helper:assert_cluster(node(), [SlaveNode1, SlaveNode2]),
  717. syn_test_suite_helper:assert_cluster(SlaveNode1, [node(), SlaveNode2]),
  718. syn_test_suite_helper:assert_cluster(SlaveNode2, [node(), SlaveNode1]),
  719. syn_test_suite_helper:wait_process_name_ready(syn_pg_scope_all),
  720. %% retrieve
  721. syn_test_suite_helper:assert_wait(
  722. lists:sort([{PidRemoteOn1, "meta-1"}, {PidRemoteOn2, "meta-2"}]),
  723. fun() -> lists:sort(syn:members(scope_all, <<"common-group">>)) end
  724. ),
  725. syn_test_suite_helper:assert_wait(
  726. lists:sort([{PidRemoteOn1, "meta-1"}, {PidRemoteOn2, "meta-2"}]),
  727. fun() -> lists:sort(rpc:call(SlaveNode1, syn, members, [scope_all, <<"common-group">>])) end
  728. ),
  729. syn_test_suite_helper:assert_wait(
  730. lists:sort([{PidRemoteOn1, "meta-1"}, {PidRemoteOn2, "meta-2"}]),
  731. fun() -> lists:sort(rpc:call(SlaveNode2, syn, members, [scope_all, <<"common-group">>])) end
  732. ),
  733. syn_test_suite_helper:assert_wait(
  734. [],
  735. fun() -> lists:sort(syn:local_members(scope_all, <<"common-group">>)) end
  736. ),
  737. syn_test_suite_helper:assert_wait(
  738. [{PidRemoteOn1, "meta-1"}],
  739. fun() -> lists:sort(rpc:call(SlaveNode1, syn, local_members, [scope_all, <<"common-group">>])) end
  740. ),
  741. syn_test_suite_helper:assert_wait(
  742. [{PidRemoteOn2, "meta-2"}],
  743. fun() -> lists:sort(rpc:call(SlaveNode2, syn, local_members, [scope_all, <<"common-group">>])) end
  744. ),
  745. syn_test_suite_helper:assert_wait(
  746. [{PidRemoteOn2, "other-meta"}],
  747. fun() -> lists:sort(syn:members(scope_all, <<"group-2">>)) end
  748. ),
  749. syn_test_suite_helper:assert_wait(
  750. [{PidRemoteOn2, "other-meta"}],
  751. fun() -> lists:sort(rpc:call(SlaveNode1, syn, members, [scope_all, <<"group-2">>])) end
  752. ),
  753. syn_test_suite_helper:assert_wait(
  754. [{PidRemoteOn2, "other-meta"}],
  755. fun() -> lists:sort(rpc:call(SlaveNode2, syn, members, [scope_all, <<"group-2">>])) end
  756. ),
  757. syn_test_suite_helper:assert_wait(
  758. [],
  759. fun() -> lists:sort(syn:local_members(scope_all, <<"group-2">>)) end
  760. ),
  761. syn_test_suite_helper:assert_wait(
  762. [],
  763. fun() -> lists:sort(rpc:call(SlaveNode1, syn, local_members, [scope_all, <<"group-2">>])) end
  764. ),
  765. syn_test_suite_helper:assert_wait(
  766. [{PidRemoteOn2, "other-meta"}],
  767. fun() -> lists:sort(rpc:call(SlaveNode2, syn, local_members, [scope_all, <<"group-2">>])) end
  768. ),
  769. 2 = syn:group_count(scope_all),
  770. 0 = syn:group_count(scope_all, node()),
  771. 1 = syn:group_count(scope_all, SlaveNode1),
  772. 2 = syn:group_count(scope_all, SlaveNode2),
  773. 2 = rpc:call(SlaveNode1, syn, group_count, [scope_all]),
  774. 0 = rpc:call(SlaveNode1, syn, group_count, [scope_all, node()]),
  775. 1 = rpc:call(SlaveNode1, syn, group_count, [scope_all, SlaveNode1]),
  776. 2 = rpc:call(SlaveNode1, syn, group_count, [scope_all, SlaveNode2]),
  777. 2 = rpc:call(SlaveNode2, syn, group_count, [scope_all]),
  778. 0 = rpc:call(SlaveNode2, syn, group_count, [scope_all, node()]),
  779. 1 = rpc:call(SlaveNode2, syn, group_count, [scope_all, SlaveNode1]),
  780. 2 = rpc:call(SlaveNode2, syn, group_count, [scope_all, SlaveNode2]),
  781. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:members(scope_bc, <<"scoped-on-bc">>)),
  782. syn_test_suite_helper:assert_wait(
  783. lists:sort([{PidRemoteOn1, "scoped-meta-1"}, {PidRemoteOn2, "scoped-meta-2"}]),
  784. fun() -> lists:sort(rpc:call(SlaveNode1, syn, members, [scope_bc, <<"scoped-on-bc">>])) end
  785. ),
  786. syn_test_suite_helper:assert_wait(
  787. lists:sort([{PidRemoteOn1, "scoped-meta-1"}, {PidRemoteOn2, "scoped-meta-2"}]),
  788. fun() -> lists:sort(rpc:call(SlaveNode2, syn, members, [scope_bc, <<"scoped-on-bc">>])) end
  789. ),
  790. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:local_members(scope_bc, <<"scoped-on-bc">>)),
  791. syn_test_suite_helper:assert_wait(
  792. [{PidRemoteOn1, "scoped-meta-1"}],
  793. fun() -> lists:sort(rpc:call(SlaveNode1, syn, local_members, [scope_bc, <<"scoped-on-bc">>])) end
  794. ),
  795. syn_test_suite_helper:assert_wait(
  796. [{PidRemoteOn2, "scoped-meta-2"}],
  797. fun() -> lists:sort(rpc:call(SlaveNode2, syn, local_members, [scope_bc, <<"scoped-on-bc">>])) end
  798. ),
  799. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:group_count(scope_bc)),
  800. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:group_count(scope_bc, node())),
  801. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:group_count(scope_bc, SlaveNode1)),
  802. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:group_count(scope_bc, SlaveNode2)),
  803. 1 = rpc:call(SlaveNode1, syn, group_count, [scope_bc]),
  804. 0 = rpc:call(SlaveNode1, syn, group_count, [scope_bc, node()]),
  805. 1 = rpc:call(SlaveNode1, syn, group_count, [scope_bc, SlaveNode1]),
  806. 1 = rpc:call(SlaveNode1, syn, group_count, [scope_bc, SlaveNode2]),
  807. 1 = rpc:call(SlaveNode2, syn, group_count, [scope_bc]),
  808. 0 = rpc:call(SlaveNode2, syn, group_count, [scope_bc, node()]),
  809. 1 = rpc:call(SlaveNode2, syn, group_count, [scope_bc, SlaveNode1]),
  810. 1 = rpc:call(SlaveNode2, syn, group_count, [scope_bc, SlaveNode2]),
  811. %% partial netsplit (1 cannot see 2)
  812. rpc:call(SlaveNode1, syn_test_suite_helper, disconnect_node, [SlaveNode2]),
  813. syn_test_suite_helper:assert_cluster(node(), [SlaveNode1, SlaveNode2]),
  814. syn_test_suite_helper:assert_cluster(SlaveNode1, [node()]),
  815. syn_test_suite_helper:assert_cluster(SlaveNode2, [node()]),
  816. %% retrieve
  817. syn_test_suite_helper:assert_wait(
  818. lists:sort([{PidRemoteOn1, "meta-1"}, {PidRemoteOn2, "meta-2"}]),
  819. fun() -> lists:sort(syn:members(scope_all, <<"common-group">>)) end
  820. ),
  821. syn_test_suite_helper:assert_wait(
  822. [{PidRemoteOn1, "meta-1"}],
  823. fun() -> lists:sort(rpc:call(SlaveNode1, syn, members, [scope_all, <<"common-group">>])) end
  824. ),
  825. syn_test_suite_helper:assert_wait(
  826. [{PidRemoteOn2, "meta-2"}],
  827. fun() -> lists:sort(rpc:call(SlaveNode2, syn, members, [scope_all, <<"common-group">>])) end
  828. ),
  829. syn_test_suite_helper:assert_wait(
  830. [],
  831. fun() -> lists:sort(syn:local_members(scope_all, <<"common-group">>)) end
  832. ),
  833. syn_test_suite_helper:assert_wait(
  834. [{PidRemoteOn1, "meta-1"}],
  835. fun() -> lists:sort(rpc:call(SlaveNode1, syn, local_members, [scope_all, <<"common-group">>])) end
  836. ),
  837. syn_test_suite_helper:assert_wait(
  838. [{PidRemoteOn2, "meta-2"}],
  839. fun() -> lists:sort(rpc:call(SlaveNode2, syn, local_members, [scope_all, <<"common-group">>])) end
  840. ),
  841. syn_test_suite_helper:assert_wait(
  842. [{PidRemoteOn2, "other-meta"}],
  843. fun() -> lists:sort(syn:members(scope_all, <<"group-2">>)) end
  844. ),
  845. syn_test_suite_helper:assert_wait(
  846. [],
  847. fun() -> lists:sort(rpc:call(SlaveNode1, syn, members, [scope_all, <<"group-2">>])) end
  848. ),
  849. syn_test_suite_helper:assert_wait(
  850. [{PidRemoteOn2, "other-meta"}],
  851. fun() -> lists:sort(rpc:call(SlaveNode2, syn, members, [scope_all, <<"group-2">>])) end
  852. ),
  853. syn_test_suite_helper:assert_wait(
  854. [],
  855. fun() -> lists:sort(syn:local_members(scope_all, <<"group-2">>)) end
  856. ),
  857. syn_test_suite_helper:assert_wait(
  858. [],
  859. fun() -> lists:sort(rpc:call(SlaveNode1, syn, local_members, [scope_all, <<"group-2">>])) end
  860. ),
  861. syn_test_suite_helper:assert_wait(
  862. [{PidRemoteOn2, "other-meta"}],
  863. fun() -> lists:sort(rpc:call(SlaveNode2, syn, local_members, [scope_all, <<"group-2">>])) end
  864. ),
  865. 2 = syn:group_count(scope_all),
  866. 0 = syn:group_count(scope_all, node()),
  867. 1 = syn:group_count(scope_all, SlaveNode1),
  868. 2 = syn:group_count(scope_all, SlaveNode2),
  869. 1 = rpc:call(SlaveNode1, syn, group_count, [scope_all]),
  870. 0 = rpc:call(SlaveNode1, syn, group_count, [scope_all, node()]),
  871. 1 = rpc:call(SlaveNode1, syn, group_count, [scope_all, SlaveNode1]),
  872. 0 = rpc:call(SlaveNode1, syn, group_count, [scope_all, SlaveNode2]),
  873. 2 = rpc:call(SlaveNode2, syn, group_count, [scope_all]),
  874. 0 = rpc:call(SlaveNode2, syn, group_count, [scope_all, node()]),
  875. 0 = rpc:call(SlaveNode2, syn, group_count, [scope_all, SlaveNode1]),
  876. 2 = rpc:call(SlaveNode2, syn, group_count, [scope_all, SlaveNode2]),
  877. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:members(scope_bc, <<"scoped-on-bc">>)),
  878. syn_test_suite_helper:assert_wait(
  879. [{PidRemoteOn1, "scoped-meta-1"}],
  880. fun() -> lists:sort(rpc:call(SlaveNode1, syn, members, [scope_bc, <<"scoped-on-bc">>])) end
  881. ),
  882. syn_test_suite_helper:assert_wait(
  883. [{PidRemoteOn2, "scoped-meta-2"}],
  884. fun() -> lists:sort(rpc:call(SlaveNode2, syn, members, [scope_bc, <<"scoped-on-bc">>])) end
  885. ),
  886. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:local_members(scope_bc, <<"scoped-on-bc">>)),
  887. syn_test_suite_helper:assert_wait(
  888. [{PidRemoteOn1, "scoped-meta-1"}],
  889. fun() -> lists:sort(rpc:call(SlaveNode1, syn, local_members, [scope_bc, <<"scoped-on-bc">>])) end
  890. ),
  891. syn_test_suite_helper:assert_wait(
  892. [{PidRemoteOn2, "scoped-meta-2"}],
  893. fun() -> lists:sort(rpc:call(SlaveNode2, syn, local_members, [scope_bc, <<"scoped-on-bc">>])) end
  894. ),
  895. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:group_count(scope_bc)),
  896. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:group_count(scope_bc, node())),
  897. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:group_count(scope_bc, SlaveNode1)),
  898. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:group_count(scope_bc, SlaveNode2)),
  899. 1 = rpc:call(SlaveNode1, syn, group_count, [scope_bc]),
  900. 0 = rpc:call(SlaveNode1, syn, group_count, [scope_bc, node()]),
  901. 1 = rpc:call(SlaveNode1, syn, group_count, [scope_bc, SlaveNode1]),
  902. 0 = rpc:call(SlaveNode1, syn, group_count, [scope_bc, SlaveNode2]),
  903. 1 = rpc:call(SlaveNode2, syn, group_count, [scope_bc]),
  904. 0 = rpc:call(SlaveNode2, syn, group_count, [scope_bc, node()]),
  905. 0 = rpc:call(SlaveNode2, syn, group_count, [scope_bc, SlaveNode1]),
  906. 1 = rpc:call(SlaveNode2, syn, group_count, [scope_bc, SlaveNode2]),
  907. %% re-join
  908. rpc:call(SlaveNode1, syn_test_suite_helper, connect_node, [SlaveNode2]),
  909. syn_test_suite_helper:assert_cluster(node(), [SlaveNode1, SlaveNode2]),
  910. syn_test_suite_helper:assert_cluster(SlaveNode1, [node(), SlaveNode2]),
  911. syn_test_suite_helper:assert_cluster(SlaveNode2, [node(), SlaveNode1]),
  912. %% retrieve
  913. syn_test_suite_helper:assert_wait(
  914. lists:sort([{PidRemoteOn1, "meta-1"}, {PidRemoteOn2, "meta-2"}]),
  915. fun() -> lists:sort(syn:members(scope_all, <<"common-group">>)) end
  916. ),
  917. syn_test_suite_helper:assert_wait(
  918. lists:sort([{PidRemoteOn1, "meta-1"}, {PidRemoteOn2, "meta-2"}]),
  919. fun() -> lists:sort(rpc:call(SlaveNode1, syn, members, [scope_all, <<"common-group">>])) end
  920. ),
  921. syn_test_suite_helper:assert_wait(
  922. lists:sort([{PidRemoteOn1, "meta-1"}, {PidRemoteOn2, "meta-2"}]),
  923. fun() -> lists:sort(rpc:call(SlaveNode2, syn, members, [scope_all, <<"common-group">>])) end
  924. ),
  925. syn_test_suite_helper:assert_wait(
  926. [],
  927. fun() -> lists:sort(syn:local_members(scope_all, <<"common-group">>)) end
  928. ),
  929. syn_test_suite_helper:assert_wait(
  930. [{PidRemoteOn1, "meta-1"}],
  931. fun() -> lists:sort(rpc:call(SlaveNode1, syn, local_members, [scope_all, <<"common-group">>])) end
  932. ),
  933. syn_test_suite_helper:assert_wait(
  934. [{PidRemoteOn2, "meta-2"}],
  935. fun() -> lists:sort(rpc:call(SlaveNode2, syn, local_members, [scope_all, <<"common-group">>])) end
  936. ),
  937. syn_test_suite_helper:assert_wait(
  938. [{PidRemoteOn2, "other-meta"}],
  939. fun() -> lists:sort(syn:members(scope_all, <<"group-2">>)) end
  940. ),
  941. syn_test_suite_helper:assert_wait(
  942. [{PidRemoteOn2, "other-meta"}],
  943. fun() -> lists:sort(rpc:call(SlaveNode1, syn, members, [scope_all, <<"group-2">>])) end
  944. ),
  945. syn_test_suite_helper:assert_wait(
  946. [{PidRemoteOn2, "other-meta"}],
  947. fun() -> lists:sort(rpc:call(SlaveNode2, syn, members, [scope_all, <<"group-2">>])) end
  948. ),
  949. syn_test_suite_helper:assert_wait(
  950. [],
  951. fun() -> lists:sort(syn:local_members(scope_all, <<"group-2">>)) end
  952. ),
  953. syn_test_suite_helper:assert_wait(
  954. [],
  955. fun() -> lists:sort(rpc:call(SlaveNode1, syn, local_members, [scope_all, <<"group-2">>])) end
  956. ),
  957. syn_test_suite_helper:assert_wait(
  958. [{PidRemoteOn2, "other-meta"}],
  959. fun() -> lists:sort(rpc:call(SlaveNode2, syn, local_members, [scope_all, <<"group-2">>])) end
  960. ),
  961. 2 = syn:group_count(scope_all),
  962. 0 = syn:group_count(scope_all, node()),
  963. 1 = syn:group_count(scope_all, SlaveNode1),
  964. 2 = syn:group_count(scope_all, SlaveNode2),
  965. 2 = rpc:call(SlaveNode1, syn, group_count, [scope_all]),
  966. 0 = rpc:call(SlaveNode1, syn, group_count, [scope_all, node()]),
  967. 1 = rpc:call(SlaveNode1, syn, group_count, [scope_all, SlaveNode1]),
  968. 2 = rpc:call(SlaveNode1, syn, group_count, [scope_all, SlaveNode2]),
  969. 2 = rpc:call(SlaveNode2, syn, group_count, [scope_all]),
  970. 0 = rpc:call(SlaveNode2, syn, group_count, [scope_all, node()]),
  971. 1 = rpc:call(SlaveNode2, syn, group_count, [scope_all, SlaveNode1]),
  972. 2 = rpc:call(SlaveNode2, syn, group_count, [scope_all, SlaveNode2]),
  973. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:members(scope_bc, <<"scoped-on-bc">>)),
  974. syn_test_suite_helper:assert_wait(
  975. lists:sort([{PidRemoteOn1, "scoped-meta-1"}, {PidRemoteOn2, "scoped-meta-2"}]),
  976. fun() -> lists:sort(rpc:call(SlaveNode1, syn, members, [scope_bc, <<"scoped-on-bc">>])) end
  977. ),
  978. syn_test_suite_helper:assert_wait(
  979. lists:sort([{PidRemoteOn1, "scoped-meta-1"}, {PidRemoteOn2, "scoped-meta-2"}]),
  980. fun() -> lists:sort(rpc:call(SlaveNode2, syn, members, [scope_bc, <<"scoped-on-bc">>])) end
  981. ),
  982. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:local_members(scope_bc, <<"scoped-on-bc">>)),
  983. syn_test_suite_helper:assert_wait(
  984. [{PidRemoteOn1, "scoped-meta-1"}],
  985. fun() -> lists:sort(rpc:call(SlaveNode1, syn, local_members, [scope_bc, <<"scoped-on-bc">>])) end
  986. ),
  987. syn_test_suite_helper:assert_wait(
  988. [{PidRemoteOn2, "scoped-meta-2"}],
  989. fun() -> lists:sort(rpc:call(SlaveNode2, syn, local_members, [scope_bc, <<"scoped-on-bc">>])) end
  990. ),
  991. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:group_count(scope_bc)),
  992. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:group_count(scope_bc, node())),
  993. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:group_count(scope_bc, SlaveNode1)),
  994. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:group_count(scope_bc, SlaveNode2)),
  995. 1 = rpc:call(SlaveNode1, syn, group_count, [scope_bc]),
  996. 0 = rpc:call(SlaveNode1, syn, group_count, [scope_bc, node()]),
  997. 1 = rpc:call(SlaveNode1, syn, group_count, [scope_bc, SlaveNode1]),
  998. 1 = rpc:call(SlaveNode1, syn, group_count, [scope_bc, SlaveNode2]),
  999. 1 = rpc:call(SlaveNode2, syn, group_count, [scope_bc]),
  1000. 0 = rpc:call(SlaveNode2, syn, group_count, [scope_bc, node()]),
  1001. 1 = rpc:call(SlaveNode2, syn, group_count, [scope_bc, SlaveNode1]),
  1002. 1 = rpc:call(SlaveNode2, syn, group_count, [scope_bc, SlaveNode2]).
  1003. three_nodes_custom_event_handler_joined_left(Config) ->
  1004. %% get slaves
  1005. SlaveNode1 = proplists:get_value(syn_slave_1, Config),
  1006. SlaveNode2 = proplists:get_value(syn_slave_2, Config),
  1007. %% add custom handler for callbacks & scopes (using ENV)
  1008. rpc:call(SlaveNode2, application, set_env, [syn, scopes, [scope_all]]),
  1009. rpc:call(SlaveNode2, application, set_env, [syn, event_handler, syn_test_event_handler_callbacks]),
  1010. %% start syn on nodes
  1011. ok = syn:start(),
  1012. ok = rpc:call(SlaveNode1, syn, start, []),
  1013. ok = rpc:call(SlaveNode2, syn, start, []),
  1014. %% add custom handler for callbacks
  1015. syn:set_event_handler(syn_test_event_handler_callbacks),
  1016. rpc:call(SlaveNode1, syn, set_event_handler, [syn_test_event_handler_callbacks]),
  1017. %% add scopes
  1018. ok = syn:add_node_to_scopes([scope_all]),
  1019. ok = rpc:call(SlaveNode1, syn, add_node_to_scopes, [[scope_all]]),
  1020. %% init
  1021. TestPid = self(),
  1022. LocalNode = node(),
  1023. %% start process
  1024. Pid = syn_test_suite_helper:start_process(),
  1025. Pid2 = syn_test_suite_helper:start_process(),
  1026. %% ---> on join
  1027. ok = syn:join(scope_all, "my-group", Pid, {recipient, TestPid, <<"meta">>}),
  1028. %% check callbacks called
  1029. syn_test_suite_helper:assert_received_messages([
  1030. {on_process_joined, LocalNode, scope_all, "my-group", Pid, <<"meta">>, normal},
  1031. {on_process_joined, SlaveNode1, scope_all, "my-group", Pid, <<"meta">>, normal},
  1032. {on_process_joined, SlaveNode2, scope_all, "my-group", Pid, <<"meta">>, normal}
  1033. ]),
  1034. %% join from another node
  1035. ok = rpc:call(SlaveNode1, syn, join, [scope_all, "my-group", Pid2, {recipient, self(), <<"meta-for-2">>}]),
  1036. %% check callbacks called
  1037. syn_test_suite_helper:assert_received_messages([
  1038. {on_process_joined, LocalNode, scope_all, "my-group", Pid2, <<"meta-for-2">>, normal},
  1039. {on_process_joined, SlaveNode1, scope_all, "my-group", Pid2, <<"meta-for-2">>, normal},
  1040. {on_process_joined, SlaveNode2, scope_all, "my-group", Pid2, <<"meta-for-2">>, normal}
  1041. ]),
  1042. %% enable strict to allow for updates
  1043. application:set_env(syn, strict_mode, true),
  1044. %% ---> on meta update (strict is enabled, so send message)
  1045. Pid ! {pg_update_meta, scope_all, "my-group", {recipient, self(), <<"new-meta-0">>}},
  1046. %% check callbacks called
  1047. syn_test_suite_helper:assert_received_messages([
  1048. {on_group_process_updated, LocalNode, scope_all, "my-group", Pid, <<"new-meta-0">>, normal},
  1049. {on_group_process_updated, SlaveNode1, scope_all, "my-group", Pid, <<"new-meta-0">>, normal},
  1050. {on_group_process_updated, SlaveNode2, scope_all, "my-group", Pid, <<"new-meta-0">>, normal}
  1051. ]),
  1052. %% disable strict
  1053. application:set_env(syn, strict_mode, false),
  1054. %% ---> on left
  1055. ok = syn:leave(scope_all, "my-group", Pid),
  1056. %% check callbacks called
  1057. syn_test_suite_helper:assert_received_messages([
  1058. {on_process_left, LocalNode, scope_all, "my-group", Pid, <<"new-meta-0">>, normal},
  1059. {on_process_left, SlaveNode1, scope_all, "my-group", Pid, <<"new-meta-0">>, normal},
  1060. {on_process_left, SlaveNode2, scope_all, "my-group", Pid, <<"new-meta-0">>, normal}
  1061. ]),
  1062. %% leave from another node
  1063. ok = rpc:call(SlaveNode1, syn, leave, [scope_all, "my-group", Pid2]),
  1064. %% check callbacks called
  1065. syn_test_suite_helper:assert_received_messages([
  1066. {on_process_left, LocalNode, scope_all, "my-group", Pid2, <<"meta-for-2">>, normal},
  1067. {on_process_left, SlaveNode1, scope_all, "my-group", Pid2, <<"meta-for-2">>, normal},
  1068. {on_process_left, SlaveNode2, scope_all, "my-group", Pid2, <<"meta-for-2">>, normal}
  1069. ]),
  1070. %% clean & check (no callbacks since process has left)
  1071. syn_test_suite_helper:kill_process(Pid),
  1072. syn_test_suite_helper:assert_empty_queue(),
  1073. %% ---> after a netsplit
  1074. PidRemoteOn1 = syn_test_suite_helper:start_process(SlaveNode1),
  1075. ok = syn:join(scope_all, remote_on_1, PidRemoteOn1, {recipient, self(), <<"netsplit">>}),
  1076. %% check callbacks called
  1077. syn_test_suite_helper:assert_received_messages([
  1078. {on_process_joined, LocalNode, scope_all, remote_on_1, PidRemoteOn1, <<"netsplit">>, normal},
  1079. {on_process_joined, SlaveNode1, scope_all, remote_on_1, PidRemoteOn1, <<"netsplit">>, normal},
  1080. {on_process_joined, SlaveNode2, scope_all, remote_on_1, PidRemoteOn1, <<"netsplit">>, normal}
  1081. ]),
  1082. %% partial netsplit (1 cannot see 2)
  1083. rpc:call(SlaveNode1, syn_test_suite_helper, disconnect_node, [SlaveNode2]),
  1084. syn_test_suite_helper:assert_cluster(node(), [SlaveNode1, SlaveNode2]),
  1085. syn_test_suite_helper:assert_cluster(SlaveNode1, [node()]),
  1086. syn_test_suite_helper:assert_cluster(SlaveNode2, [node()]),
  1087. %% check callbacks called
  1088. syn_test_suite_helper:assert_received_messages([
  1089. {on_process_left, SlaveNode2, scope_all, remote_on_1, PidRemoteOn1, <<"netsplit">>, {syn_remote_scope_node_down, scope_all, SlaveNode1}}
  1090. ]),
  1091. %% ---> after a re-join
  1092. %% re-join
  1093. rpc:call(SlaveNode1, syn_test_suite_helper, connect_node, [SlaveNode2]),
  1094. syn_test_suite_helper:assert_cluster(node(), [SlaveNode1, SlaveNode2]),
  1095. syn_test_suite_helper:assert_cluster(SlaveNode1, [node(), SlaveNode2]),
  1096. syn_test_suite_helper:assert_cluster(SlaveNode2, [node(), SlaveNode1]),
  1097. %% check callbacks called
  1098. syn_test_suite_helper:assert_received_messages([
  1099. {on_process_joined, SlaveNode2, scope_all, remote_on_1, PidRemoteOn1, <<"netsplit">>, {syn_remote_scope_node_up, scope_all, SlaveNode1}}
  1100. ]),
  1101. %% clean
  1102. syn_test_suite_helper:kill_process(PidRemoteOn1),
  1103. %% check callbacks called
  1104. syn_test_suite_helper:assert_received_messages([
  1105. {on_process_left, LocalNode, scope_all, remote_on_1, PidRemoteOn1, <<"netsplit">>, killed},
  1106. {on_process_left, SlaveNode1, scope_all, remote_on_1, PidRemoteOn1, <<"netsplit">>, killed},
  1107. {on_process_left, SlaveNode2, scope_all, remote_on_1, PidRemoteOn1, <<"netsplit">>, killed}
  1108. ]),
  1109. %% ---> don't call on monitor rebuild
  1110. %% crash the scope process on local
  1111. syn_test_suite_helper:kill_process(syn_pg_scope_all),
  1112. syn_test_suite_helper:wait_process_name_ready(syn_pg_scope_all),
  1113. %% no messages
  1114. syn_test_suite_helper:assert_empty_queue(),
  1115. %% ---> call if process died during the scope process crash
  1116. TransientPid = syn_test_suite_helper:start_process(),
  1117. ok = syn:join(scope_all, "transient-group", TransientPid, {recipient, self(), "transient-meta"}),
  1118. %% check callbacks called
  1119. syn_test_suite_helper:assert_received_messages([
  1120. {on_process_joined, LocalNode, scope_all, "transient-group", TransientPid, "transient-meta", normal},
  1121. {on_process_joined, SlaveNode1, scope_all, "transient-group", TransientPid, "transient-meta", normal},
  1122. {on_process_joined, SlaveNode2, scope_all, "transient-group", TransientPid, "transient-meta", normal}
  1123. ]),
  1124. %% crash the scope process & fake a died process on local
  1125. InvalidPid = list_to_pid("<0.9999.0>"),
  1126. add_to_local_table(scope_all, "invalid-group", InvalidPid, {recipient, self(), "invalid-meta"}, 0, undefined),
  1127. syn_test_suite_helper:kill_process(syn_pg_scope_all),
  1128. %% check callbacks called
  1129. syn_test_suite_helper:assert_received_messages([
  1130. {on_process_left, LocalNode, scope_all, "invalid-group", InvalidPid, "invalid-meta", undefined},
  1131. {on_process_left, SlaveNode1, scope_all, "transient-group", TransientPid, "transient-meta", {syn_remote_scope_node_down, scope_all, LocalNode}},
  1132. {on_process_left, SlaveNode2, scope_all, "transient-group", TransientPid, "transient-meta", {syn_remote_scope_node_down, scope_all, LocalNode}},
  1133. {on_process_joined, SlaveNode1, scope_all, "transient-group", TransientPid, "transient-meta", {syn_remote_scope_node_up, scope_all, LocalNode}},
  1134. {on_process_joined, SlaveNode2, scope_all, "transient-group", TransientPid, "transient-meta", {syn_remote_scope_node_up, scope_all, LocalNode}}
  1135. ]).
  1136. three_nodes_publish(Config) ->
  1137. %% get slaves
  1138. SlaveNode1 = proplists:get_value(syn_slave_1, Config),
  1139. SlaveNode2 = proplists:get_value(syn_slave_2, Config),
  1140. %% start syn on nodes
  1141. ok = syn:start(),
  1142. ok = rpc:call(SlaveNode1, syn, start, []),
  1143. ok = rpc:call(SlaveNode2, syn, start, []),
  1144. %% add scopes
  1145. ok = syn:add_node_to_scopes([scope_ab]),
  1146. ok = rpc:call(SlaveNode1, syn, add_node_to_scopes, [[scope_ab, scope_bc]]),
  1147. ok = rpc:call(SlaveNode2, syn, add_node_to_scopes, [[scope_bc]]),
  1148. %% start processes
  1149. TestMessage = test_message,
  1150. TestPid = self(),
  1151. SubscriberLoop = fun() -> subscriber_loop(TestPid, TestMessage) end,
  1152. Pid = syn_test_suite_helper:start_process(SubscriberLoop),
  1153. Pid2 = syn_test_suite_helper:start_process(SubscriberLoop),
  1154. PidRemoteOn1 = syn_test_suite_helper:start_process(SlaveNode1, SubscriberLoop),
  1155. PidRemoteOn2 = syn_test_suite_helper:start_process(SlaveNode2, SubscriberLoop),
  1156. %% join
  1157. ok = syn:join(scope_ab, <<"subscribers">>, Pid),
  1158. ok = syn:join(scope_ab, <<"subscribers-2">>, Pid2),
  1159. ok = syn:join(scope_ab, <<"subscribers">>, PidRemoteOn1),
  1160. ok = rpc:call(SlaveNode1, syn, join, [scope_bc, <<"subscribers">>, PidRemoteOn1]),
  1161. ok = rpc:call(SlaveNode2, syn, join, [scope_bc, <<"subscribers">>, PidRemoteOn2]),
  1162. %% ---> publish
  1163. {ok, 2} = syn:publish(scope_ab, <<"subscribers">>, TestMessage),
  1164. syn_test_suite_helper:assert_received_messages([
  1165. {done, Pid},
  1166. {done, PidRemoteOn1}
  1167. ]),
  1168. %% errors
  1169. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:publish(scope_bc, <<"subscribers">>, TestMessage)),
  1170. %% on other scope
  1171. {ok, 2} = rpc:call(SlaveNode1, syn, publish, [scope_bc, <<"subscribers">>, TestMessage]),
  1172. syn_test_suite_helper:assert_received_messages([
  1173. {done, PidRemoteOn1},
  1174. {done, PidRemoteOn2}
  1175. ]),
  1176. %% non-existant
  1177. {ok, 0} = syn:publish(scope_ab, <<"non-existant">>, TestMessage),
  1178. %% no messages
  1179. syn_test_suite_helper:assert_empty_queue(),
  1180. %% ---> publish local
  1181. {ok, 1} = syn:local_publish(scope_ab, <<"subscribers">>, test_message),
  1182. syn_test_suite_helper:assert_received_messages([
  1183. {done, Pid}
  1184. ]),
  1185. %% non-existant
  1186. {ok, 0} = syn:local_publish(scope_ab, <<"non-existant">>, TestMessage),
  1187. %% no messages
  1188. syn_test_suite_helper:assert_empty_queue().
  1189. three_nodes_multi_call(Config) ->
  1190. %% get slaves
  1191. SlaveNode1 = proplists:get_value(syn_slave_1, Config),
  1192. SlaveNode2 = proplists:get_value(syn_slave_2, Config),
  1193. %% start syn on nodes
  1194. ok = syn:start(),
  1195. ok = rpc:call(SlaveNode1, syn, start, []),
  1196. ok = rpc:call(SlaveNode2, syn, start, []),
  1197. %% add scopes
  1198. ok = syn:add_node_to_scopes([scope_ab]),
  1199. ok = rpc:call(SlaveNode1, syn, add_node_to_scopes, [[scope_ab, scope_bc]]),
  1200. ok = rpc:call(SlaveNode2, syn, add_node_to_scopes, [[scope_bc]]),
  1201. %% start processes
  1202. RecipientLoopLate = fun() -> timer:sleep(200), recipient_loop() end,
  1203. Pid = syn_test_suite_helper:start_process(fun recipient_loop/0),
  1204. Pid2 = syn_test_suite_helper:start_process(RecipientLoopLate),
  1205. PidRemoteOn1 = syn_test_suite_helper:start_process(SlaveNode1, fun recipient_loop/0),
  1206. PidRemoteOn2 = syn_test_suite_helper:start_process(SlaveNode2, fun recipient_loop/0),
  1207. %% join
  1208. ok = syn:join(scope_ab, <<"recipients">>, Pid, "meta-1"),
  1209. ok = syn:join(scope_ab, <<"recipients">>, Pid2),
  1210. ok = syn:join(scope_ab, <<"recipients">>, PidRemoteOn1, "meta-on-ab-1"),
  1211. ok = rpc:call(SlaveNode1, syn, join, [scope_bc, <<"recipients">>, PidRemoteOn1, "meta-on-bc-1"]),
  1212. ok = rpc:call(SlaveNode2, syn, join, [scope_bc, <<"recipients">>, PidRemoteOn2, "meta-on-bc-2"]),
  1213. %% errors
  1214. {'EXIT', {{invalid_scope, scope_bc}, _}} = (catch syn:multi_call(scope_bc, <<"recipients">>, test_message, 100)),
  1215. %% ---> multi_call
  1216. {RepliesAB, BadRepliesAB} = syn:multi_call(scope_ab, <<"recipients">>, test_message_ab, 100),
  1217. RepliesABSorted = lists:sort(RepliesAB),
  1218. RepliesABSorted = lists:sort([
  1219. {{Pid, "meta-1"}, {reply, test_message_ab, Pid, "meta-1"}},
  1220. {{PidRemoteOn1, "meta-on-ab-1"}, {reply, test_message_ab, PidRemoteOn1, "meta-on-ab-1"}}
  1221. ]),
  1222. BadRepliesAB = [{Pid2, undefined}],
  1223. %% different scope
  1224. {RepliesBC, BadRepliesBC} = rpc:call(SlaveNode1, syn, multi_call, [scope_bc, <<"recipients">>, test_message_bc, 100]),
  1225. RepliesBCSorted = lists:sort(RepliesBC),
  1226. RepliesBCSorted = lists:sort([
  1227. {{PidRemoteOn1, "meta-on-bc-1"}, {reply, test_message_bc, PidRemoteOn1, "meta-on-bc-1"}},
  1228. {{PidRemoteOn2, "meta-on-bc-2"}, {reply, test_message_bc, PidRemoteOn2, "meta-on-bc-2"}}
  1229. ]),
  1230. BadRepliesBC = [].
  1231. three_nodes_group_names(Config) ->
  1232. %% get slaves
  1233. SlaveNode1 = proplists:get_value(syn_slave_1, Config),
  1234. SlaveNode2 = proplists:get_value(syn_slave_2, Config),
  1235. %% start syn on nodes
  1236. ok = syn:start(),
  1237. ok = rpc:call(SlaveNode1, syn, start, []),
  1238. ok = rpc:call(SlaveNode2, syn, start, []),
  1239. %% add scopes
  1240. ok = syn:add_node_to_scopes([scope_all]),
  1241. ok = rpc:call(SlaveNode1, syn, add_node_to_scopes, [[scope_all]]),
  1242. ok = rpc:call(SlaveNode2, syn, add_node_to_scopes, [[scope_all]]),
  1243. %% start processes
  1244. Pid = syn_test_suite_helper:start_process(),
  1245. Pid2 = syn_test_suite_helper:start_process(),
  1246. PidRemoteOn1 = syn_test_suite_helper:start_process(SlaveNode1),
  1247. PidRemoteOn2 = syn_test_suite_helper:start_process(SlaveNode2),
  1248. %% join
  1249. ok = syn:join(scope_all, <<"subscribers">>, Pid),
  1250. ok = syn:join(scope_all, <<"subscribers">>, Pid2),
  1251. ok = syn:join(scope_all, <<"subscribers-2">>, Pid),
  1252. ok = syn:join(scope_all, <<"subscribers-2">>, PidRemoteOn1),
  1253. ok = syn:join(scope_all, <<"subscribers-2">>, PidRemoteOn2),
  1254. ok = syn:join(scope_all, <<"subscribers-3">>, PidRemoteOn1),
  1255. %% retrieve
  1256. syn_test_suite_helper:assert_wait(
  1257. lists:sort([<<"subscribers">>, <<"subscribers-2">>, <<"subscribers-3">>]),
  1258. fun() -> lists:sort(syn:group_names(scope_all)) end
  1259. ),
  1260. syn_test_suite_helper:assert_wait(
  1261. lists:sort([<<"subscribers">>, <<"subscribers-2">>]),
  1262. fun() -> lists:sort(syn:group_names(scope_all, node())) end
  1263. ),
  1264. syn_test_suite_helper:assert_wait(
  1265. lists:sort([<<"subscribers-2">>, <<"subscribers-3">>]),
  1266. fun() -> lists:sort(syn:group_names(scope_all, SlaveNode1)) end
  1267. ),
  1268. syn_test_suite_helper:assert_wait(
  1269. [<<"subscribers-2">>],
  1270. fun() -> lists:sort(syn:group_names(scope_all, SlaveNode2)) end
  1271. ),
  1272. syn_test_suite_helper:assert_wait(
  1273. lists:sort([<<"subscribers">>, <<"subscribers-2">>, <<"subscribers-3">>]),
  1274. fun() -> lists:sort(rpc:call(SlaveNode1, syn, group_names, [scope_all])) end
  1275. ),
  1276. syn_test_suite_helper:assert_wait(
  1277. lists:sort([<<"subscribers">>, <<"subscribers-2">>]),
  1278. fun() -> lists:sort(rpc:call(SlaveNode1, syn, group_names, [scope_all, node()])) end
  1279. ),
  1280. syn_test_suite_helper:assert_wait(
  1281. lists:sort([<<"subscribers-2">>, <<"subscribers-3">>]),
  1282. fun() -> lists:sort(rpc:call(SlaveNode1, syn, group_names, [scope_all, SlaveNode1])) end
  1283. ),
  1284. syn_test_suite_helper:assert_wait(
  1285. [<<"subscribers-2">>],
  1286. fun() -> lists:sort(rpc:call(SlaveNode1, syn, group_names, [scope_all, SlaveNode2])) end
  1287. ),
  1288. syn_test_suite_helper:assert_wait(
  1289. lists:sort([<<"subscribers">>, <<"subscribers-2">>, <<"subscribers-3">>]),
  1290. fun() -> lists:sort(rpc:call(SlaveNode2, syn, group_names, [scope_all])) end
  1291. ),
  1292. syn_test_suite_helper:assert_wait(
  1293. lists:sort([<<"subscribers">>, <<"subscribers-2">>]),
  1294. fun() -> lists:sort(rpc:call(SlaveNode2, syn, group_names, [scope_all, node()])) end
  1295. ),
  1296. syn_test_suite_helper:assert_wait(
  1297. lists:sort([<<"subscribers-2">>, <<"subscribers-3">>]),
  1298. fun() -> lists:sort(rpc:call(SlaveNode2, syn, group_names, [scope_all, SlaveNode1])) end
  1299. ),
  1300. syn_test_suite_helper:assert_wait(
  1301. [<<"subscribers-2">>],
  1302. fun() -> lists:sort(rpc:call(SlaveNode2, syn, group_names, [scope_all, SlaveNode2])) end
  1303. ),
  1304. %% leave
  1305. ok = syn:leave(scope_all, <<"subscribers-2">>, Pid),
  1306. ok = syn:leave(scope_all, <<"subscribers-2">>, PidRemoteOn1),
  1307. ok = syn:leave(scope_all, <<"subscribers-2">>, PidRemoteOn2),
  1308. %% retrieve
  1309. syn_test_suite_helper:assert_wait(
  1310. lists:sort([<<"subscribers">>, <<"subscribers-3">>]),
  1311. fun() -> lists:sort(syn:group_names(scope_all)) end
  1312. ),
  1313. syn_test_suite_helper:assert_wait(
  1314. [<<"subscribers">>],
  1315. fun() -> lists:sort(syn:group_names(scope_all, node())) end
  1316. ),
  1317. syn_test_suite_helper:assert_wait(
  1318. [<<"subscribers-3">>],
  1319. fun() -> lists:sort(syn:group_names(scope_all, SlaveNode1)) end
  1320. ),
  1321. syn_test_suite_helper:assert_wait(
  1322. [],
  1323. fun() -> lists:sort(syn:group_names(scope_all, SlaveNode2)) end
  1324. ),
  1325. syn_test_suite_helper:assert_wait(
  1326. lists:sort([<<"subscribers">>, <<"subscribers-3">>]),
  1327. fun() -> lists:sort(rpc:call(SlaveNode1, syn, group_names, [scope_all])) end
  1328. ),
  1329. syn_test_suite_helper:assert_wait(
  1330. [<<"subscribers">>],
  1331. fun() -> lists:sort(rpc:call(SlaveNode1, syn, group_names, [scope_all, node()])) end
  1332. ),
  1333. syn_test_suite_helper:assert_wait(
  1334. [<<"subscribers-3">>],
  1335. fun() -> lists:sort(rpc:call(SlaveNode1, syn, group_names, [scope_all, SlaveNode1])) end
  1336. ),
  1337. syn_test_suite_helper:assert_wait(
  1338. [],
  1339. fun() -> lists:sort(rpc:call(SlaveNode1, syn, group_names, [scope_all, SlaveNode2])) end
  1340. ),
  1341. syn_test_suite_helper:assert_wait(
  1342. lists:sort([<<"subscribers">>, <<"subscribers-3">>]),
  1343. fun() -> lists:sort(rpc:call(SlaveNode2, syn, group_names, [scope_all])) end
  1344. ),
  1345. syn_test_suite_helper:assert_wait(
  1346. [<<"subscribers">>],
  1347. fun() -> lists:sort(rpc:call(SlaveNode2, syn, group_names, [scope_all, node()])) end
  1348. ),
  1349. syn_test_suite_helper:assert_wait(
  1350. [<<"subscribers-3">>],
  1351. fun() -> lists:sort(rpc:call(SlaveNode2, syn, group_names, [scope_all, SlaveNode1])) end
  1352. ),
  1353. syn_test_suite_helper:assert_wait(
  1354. [],
  1355. fun() -> lists:sort(rpc:call(SlaveNode2, syn, group_names, [scope_all, SlaveNode2])) end
  1356. ),
  1357. %% partial netsplit (1 cannot see 2)
  1358. rpc:call(SlaveNode1, syn_test_suite_helper, disconnect_node, [SlaveNode2]),
  1359. syn_test_suite_helper:assert_cluster(node(), [SlaveNode1, SlaveNode2]),
  1360. syn_test_suite_helper:assert_cluster(SlaveNode1, [node()]),
  1361. syn_test_suite_helper:assert_cluster(SlaveNode2, [node()]),
  1362. %% retrieve
  1363. syn_test_suite_helper:assert_wait(
  1364. lists:sort([<<"subscribers">>, <<"subscribers-3">>]),
  1365. fun() -> lists:sort(syn:group_names(scope_all)) end
  1366. ),
  1367. syn_test_suite_helper:assert_wait(
  1368. [<<"subscribers">>],
  1369. fun() -> lists:sort(syn:group_names(scope_all, node())) end
  1370. ),
  1371. syn_test_suite_helper:assert_wait(
  1372. [<<"subscribers-3">>],
  1373. fun() -> lists:sort(syn:group_names(scope_all, SlaveNode1)) end
  1374. ),
  1375. syn_test_suite_helper:assert_wait(
  1376. [],
  1377. fun() -> lists:sort(syn:group_names(scope_all, SlaveNode2)) end
  1378. ),
  1379. syn_test_suite_helper:assert_wait(
  1380. lists:sort([<<"subscribers">>, <<"subscribers-3">>]),
  1381. fun() -> lists:sort(rpc:call(SlaveNode1, syn, group_names, [scope_all])) end
  1382. ),
  1383. syn_test_suite_helper:assert_wait(
  1384. [<<"subscribers">>],
  1385. fun() -> lists:sort(rpc:call(SlaveNode1, syn, group_names, [scope_all, node()])) end
  1386. ),
  1387. syn_test_suite_helper:assert_wait(
  1388. [<<"subscribers-3">>],
  1389. fun() -> lists:sort(rpc:call(SlaveNode1, syn, group_names, [scope_all, SlaveNode1])) end
  1390. ),
  1391. syn_test_suite_helper:assert_wait(
  1392. [],
  1393. fun() -> lists:sort(rpc:call(SlaveNode1, syn, group_names, [scope_all, SlaveNode2])) end
  1394. ),
  1395. syn_test_suite_helper:assert_wait(
  1396. [<<"subscribers">>],
  1397. fun() -> lists:sort(rpc:call(SlaveNode2, syn, group_names, [scope_all])) end
  1398. ),
  1399. syn_test_suite_helper:assert_wait(
  1400. [<<"subscribers">>],
  1401. fun() -> lists:sort(rpc:call(SlaveNode2, syn, group_names, [scope_all, node()])) end
  1402. ),
  1403. syn_test_suite_helper:assert_wait(
  1404. [],
  1405. fun() -> lists:sort(rpc:call(SlaveNode2, syn, group_names, [scope_all, SlaveNode1])) end
  1406. ),
  1407. syn_test_suite_helper:assert_wait(
  1408. [],
  1409. fun() -> lists:sort(rpc:call(SlaveNode2, syn, group_names, [scope_all, SlaveNode2])) end
  1410. ),
  1411. %% re-join
  1412. rpc:call(SlaveNode1, syn_test_suite_helper, connect_node, [SlaveNode2]),
  1413. syn_test_suite_helper:assert_cluster(node(), [SlaveNode1, SlaveNode2]),
  1414. syn_test_suite_helper:assert_cluster(SlaveNode1, [node(), SlaveNode2]),
  1415. syn_test_suite_helper:assert_cluster(SlaveNode2, [node(), SlaveNode1]),
  1416. %% retrieve
  1417. syn_test_suite_helper:assert_wait(
  1418. lists:sort([<<"subscribers">>, <<"subscribers-3">>]),
  1419. fun() -> lists:sort(syn:group_names(scope_all)) end
  1420. ),
  1421. syn_test_suite_helper:assert_wait(
  1422. [<<"subscribers">>],
  1423. fun() -> lists:sort(syn:group_names(scope_all, node())) end
  1424. ),
  1425. syn_test_suite_helper:assert_wait(
  1426. [<<"subscribers-3">>],
  1427. fun() -> lists:sort(syn:group_names(scope_all, SlaveNode1)) end
  1428. ),
  1429. syn_test_suite_helper:assert_wait(
  1430. [],
  1431. fun() -> lists:sort(syn:group_names(scope_all, SlaveNode2)) end
  1432. ),
  1433. syn_test_suite_helper:assert_wait(
  1434. lists:sort([<<"subscribers">>, <<"subscribers-3">>]),
  1435. fun() -> lists:sort(rpc:call(SlaveNode1, syn, group_names, [scope_all])) end
  1436. ),
  1437. syn_test_suite_helper:assert_wait(
  1438. [<<"subscribers">>],
  1439. fun() -> lists:sort(rpc:call(SlaveNode1, syn, group_names, [scope_all, node()])) end
  1440. ),
  1441. syn_test_suite_helper:assert_wait(
  1442. [<<"subscribers-3">>],
  1443. fun() -> lists:sort(rpc:call(SlaveNode1, syn, group_names, [scope_all, SlaveNode1])) end
  1444. ),
  1445. syn_test_suite_helper:assert_wait(
  1446. [],
  1447. fun() -> lists:sort(rpc:call(SlaveNode1, syn, group_names, [scope_all, SlaveNode2])) end
  1448. ),
  1449. syn_test_suite_helper:assert_wait(
  1450. lists:sort([<<"subscribers">>, <<"subscribers-3">>]),
  1451. fun() -> lists:sort(rpc:call(SlaveNode2, syn, group_names, [scope_all])) end
  1452. ),
  1453. syn_test_suite_helper:assert_wait(
  1454. [<<"subscribers">>],
  1455. fun() -> lists:sort(rpc:call(SlaveNode2, syn, group_names, [scope_all, node()])) end
  1456. ),
  1457. syn_test_suite_helper:assert_wait(
  1458. [<<"subscribers-3">>],
  1459. fun() -> lists:sort(rpc:call(SlaveNode2, syn, group_names, [scope_all, SlaveNode1])) end
  1460. ),
  1461. syn_test_suite_helper:assert_wait(
  1462. [],
  1463. fun() -> lists:sort(rpc:call(SlaveNode2, syn, group_names, [scope_all, SlaveNode2])) end
  1464. ).
  1465. four_nodes_concurrency(Config) ->
  1466. %% get slaves
  1467. SlaveNode1 = proplists:get_value(syn_slave_1, Config),
  1468. SlaveNode2 = proplists:get_value(syn_slave_2, Config),
  1469. SlaveNode3 = proplists:get_value(syn_slave_3, Config),
  1470. %% start syn on nodes
  1471. ok = syn:start(),
  1472. ok = rpc:call(SlaveNode1, syn, start, []),
  1473. ok = rpc:call(SlaveNode2, syn, start, []),
  1474. ok = rpc:call(SlaveNode3, syn, start, []),
  1475. %% enable strict to allow for updates
  1476. application:set_env(syn, strict_mode, true),
  1477. rpc:call(SlaveNode1, application, set_env, [syn, strict_mode, true]),
  1478. rpc:call(SlaveNode2, application, set_env, [syn, strict_mode, true]),
  1479. rpc:call(SlaveNode3, application, set_env, [syn, strict_mode, true]),
  1480. %% add scopes
  1481. ok = syn:add_node_to_scopes([scope_all]),
  1482. ok = rpc:call(SlaveNode1, syn, add_node_to_scopes, [[scope_all]]),
  1483. ok = rpc:call(SlaveNode2, syn, add_node_to_scopes, [[scope_all]]),
  1484. ok = rpc:call(SlaveNode3, syn, add_node_to_scopes, [[scope_all]]),
  1485. %% ref
  1486. TestPid = self(),
  1487. Iterations = 250,
  1488. %% concurrent test
  1489. WorkerFun = fun() ->
  1490. Self = self(),
  1491. lists:foreach(fun(_) ->
  1492. %% loop
  1493. RandomMeta = rand:uniform(99999),
  1494. ok = syn:join(scope_all, <<"concurrent-scope">>, Self, RandomMeta),
  1495. %% random leave
  1496. case rand:uniform(5) of
  1497. 1 -> syn:leave(scope_all, <<"concurrent-scope">>, Self);
  1498. _ -> ok
  1499. end,
  1500. %% random sleep
  1501. RndTime = rand:uniform(30),
  1502. timer:sleep(RndTime)
  1503. end, lists:seq(1, Iterations)),
  1504. TestPid ! {done, node()}
  1505. end,
  1506. LocalNode = node(),
  1507. %% spawn concurrent
  1508. spawn(LocalNode, WorkerFun),
  1509. spawn(SlaveNode1, WorkerFun),
  1510. spawn(SlaveNode2, WorkerFun),
  1511. spawn(SlaveNode3, WorkerFun),
  1512. %% wait for workers done
  1513. syn_test_suite_helper:assert_received_messages([
  1514. {done, LocalNode},
  1515. {done, SlaveNode1},
  1516. {done, SlaveNode2},
  1517. {done, SlaveNode3}
  1518. ]),
  1519. %% check results are same across network
  1520. syn_test_suite_helper:assert_wait(
  1521. 1,
  1522. fun() ->
  1523. ResultPidLocal = lists:sort(syn:members(scope_all, <<"concurrent-scope">>)),
  1524. ResultPidOn1 = lists:sort(rpc:call(SlaveNode1, syn, members, [scope_all, <<"concurrent-scope">>])),
  1525. ResultPidOn2 = lists:sort(rpc:call(SlaveNode2, syn, members, [scope_all, <<"concurrent-scope">>])),
  1526. ResultPidOn3 = lists:sort(rpc:call(SlaveNode3, syn, members, [scope_all, <<"concurrent-scope">>])),
  1527. %% if unique set is of 1 element then they all contain the same result
  1528. Ordset = ordsets:from_list([ResultPidLocal, ResultPidOn1, ResultPidOn2, ResultPidOn3]),
  1529. ordsets:size(Ordset)
  1530. end
  1531. ).
  1532. %% ===================================================================
  1533. %% Internal
  1534. %% ===================================================================
  1535. add_to_local_table(Scope, GroupName, Pid, Meta, Time, MRef) ->
  1536. TableByName = syn_backbone:get_table_name(syn_pg_by_name, Scope),
  1537. TableByPid = syn_backbone:get_table_name(syn_pg_by_pid, Scope),
  1538. syn_pg:add_to_local_table(GroupName, Pid, Meta, Time, MRef, TableByName, TableByPid).
  1539. subscriber_loop(TestPid, TestMessage) ->
  1540. receive
  1541. TestMessage ->
  1542. TestPid ! {done, self()},
  1543. subscriber_loop(TestPid, TestMessage)
  1544. end.
  1545. recipient_loop() ->
  1546. receive
  1547. {syn_multi_call, TestMessage, Caller, Meta} ->
  1548. syn:multi_call_reply(Caller, {reply, TestMessage, self(), Meta}),
  1549. recipient_loop()
  1550. end.