syn_groups_SUITE.erl 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710
  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_groups_SUITE).
  27. %% callbacks
  28. -export([all/0]).
  29. -export([init_per_suite/1, end_per_suite/1]).
  30. -export([groups/0, init_per_group/2, end_per_group/2]).
  31. -export([init_per_testcase/2, end_per_testcase/2]).
  32. %% tests
  33. -export([
  34. three_nodes_discover_default_scope/1,
  35. three_nodes_discover_custom_scope/1,
  36. three_nodes_join_leave_and_monitor_default_scope/1,
  37. three_nodes_join_leave_and_monitor_custom_scope/1
  38. ]).
  39. %% include
  40. -include_lib("common_test/include/ct.hrl").
  41. -include_lib("syn/src/syn.hrl").
  42. %% ===================================================================
  43. %% Callbacks
  44. %% ===================================================================
  45. %% -------------------------------------------------------------------
  46. %% Function: all() -> GroupsAndTestCases | {skip,Reason}
  47. %% GroupsAndTestCases = [{group,GroupName} | TestCase]
  48. %% GroupName = atom()
  49. %% TestCase = atom()
  50. %% Reason = any()
  51. %% -------------------------------------------------------------------
  52. all() ->
  53. [
  54. {group, three_nodes_groups}
  55. ].
  56. %% -------------------------------------------------------------------
  57. %% Function: groups() -> [Group]
  58. %% Group = {GroupName,Properties,GroupsAndTestCases}
  59. %% GroupName = atom()
  60. %% Properties = [parallel | sequence | Shuffle | {RepeatType,N}]
  61. %% GroupsAndTestCases = [Group | {group,GroupName} | TestCase]
  62. %% TestCase = atom()
  63. %% Shuffle = shuffle | {shuffle,{integer(),integer(),integer()}}
  64. %% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |
  65. %% repeat_until_any_ok | repeat_until_any_fail
  66. %% N = integer() | forever
  67. %% -------------------------------------------------------------------
  68. groups() ->
  69. [
  70. {three_nodes_groups, [shuffle], [
  71. three_nodes_discover_default_scope,
  72. three_nodes_discover_custom_scope,
  73. three_nodes_join_leave_and_monitor_default_scope,
  74. three_nodes_join_leave_and_monitor_custom_scope
  75. ]}
  76. ].
  77. %% -------------------------------------------------------------------
  78. %% Function: init_per_suite(Config0) ->
  79. %% Config1 | {skip,Reason} |
  80. %% {skip_and_save,Reason,Config1}
  81. %% Config0 = Config1 = [tuple()]
  82. %% Reason = any()
  83. %% -------------------------------------------------------------------
  84. init_per_suite(Config) ->
  85. Config.
  86. %% -------------------------------------------------------------------
  87. %% Function: end_per_suite(Config0) -> void() | {save_config,Config1}
  88. %% Config0 = Config1 = [tuple()]
  89. %% -------------------------------------------------------------------
  90. end_per_suite(_Config) ->
  91. ok.
  92. %% -------------------------------------------------------------------
  93. %% Function: init_per_group(GroupName, Config0) ->
  94. %% Config1 | {skip,Reason} |
  95. %% {skip_and_save,Reason,Config1}
  96. %% GroupName = atom()
  97. %% Config0 = Config1 = [tuple()]
  98. %% Reason = any()
  99. %% -------------------------------------------------------------------
  100. init_per_group(three_nodes_groups, Config) ->
  101. %% start slave
  102. {ok, SlaveNode1} = syn_test_suite_helper:start_slave(syn_slave_1),
  103. {ok, SlaveNode2} = syn_test_suite_helper:start_slave(syn_slave_2),
  104. syn_test_suite_helper:connect_node(SlaveNode1),
  105. syn_test_suite_helper:connect_node(SlaveNode2),
  106. rpc:call(SlaveNode1, syn_test_suite_helper, connect_node, [SlaveNode2]),
  107. %% wait full cluster
  108. case syn_test_suite_helper:wait_cluster_mesh_connected([node(), SlaveNode1, SlaveNode2]) of
  109. ok ->
  110. %% config
  111. [{slave_node_1, SlaveNode1}, {slave_node_2, SlaveNode2} | Config];
  112. Other ->
  113. ct:pal("*********** Could not get full cluster, skipping"),
  114. end_per_group(three_nodes_groups, Config),
  115. {skip, Other}
  116. end;
  117. init_per_group(_GroupName, Config) ->
  118. Config.
  119. %% -------------------------------------------------------------------
  120. %% Function: end_per_group(GroupName, Config0) ->
  121. %% void() | {save_config,Config1}
  122. %% GroupName = atom()
  123. %% Config0 = Config1 = [tuple()]
  124. %% -------------------------------------------------------------------
  125. end_per_group(three_nodes_groups, Config) ->
  126. SlaveNode1 = proplists:get_value(slave_node_1, Config),
  127. syn_test_suite_helper:connect_node(SlaveNode1),
  128. SlaveNode2 = proplists:get_value(slave_node_2, Config),
  129. syn_test_suite_helper:connect_node(SlaveNode2),
  130. syn_test_suite_helper:clean_after_test(),
  131. syn_test_suite_helper:stop_slave(syn_slave_1),
  132. syn_test_suite_helper:stop_slave(syn_slave_2),
  133. timer:sleep(1000);
  134. end_per_group(_GroupName, _Config) ->
  135. syn_test_suite_helper:clean_after_test().
  136. %% -------------------------------------------------------------------
  137. %% Function: init_per_testcase(TestCase, Config0) ->
  138. %% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
  139. %% TestCase = atom()
  140. %% Config0 = Config1 = [tuple()]
  141. %% Reason = any()
  142. %% -------------------------------------------------------------------
  143. init_per_testcase(TestCase, Config) ->
  144. ct:pal("Starting test: ~p", [TestCase]),
  145. Config.
  146. %% -------------------------------------------------------------------
  147. %% Function: end_per_testcase(TestCase, Config0) ->
  148. %% void() | {save_config,Config1} | {fail,Reason}
  149. %% TestCase = atom()
  150. %% Config0 = Config1 = [tuple()]
  151. %% Reason = any()
  152. %% -------------------------------------------------------------------
  153. end_per_testcase(_, _Config) ->
  154. syn_test_suite_helper:clean_after_test().
  155. %% ===================================================================
  156. %% Tests
  157. %% ===================================================================
  158. three_nodes_discover_default_scope(Config) ->
  159. %% get slaves
  160. SlaveNode1 = proplists:get_value(slave_node_1, Config),
  161. SlaveNode2 = proplists:get_value(slave_node_2, Config),
  162. %% start syn on nodes
  163. ok = syn:start(),
  164. ok = rpc:call(SlaveNode1, syn, start, []),
  165. ok = rpc:call(SlaveNode2, syn, start, []),
  166. %% check
  167. syn_test_suite_helper:assert_groups_scope_subcluster(node(), default, [SlaveNode1, SlaveNode2]),
  168. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode1, default, [node(), SlaveNode2]),
  169. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode2, default, [node(), SlaveNode1]),
  170. %% simulate full netsplit
  171. rpc:call(SlaveNode1, syn_test_suite_helper, disconnect_node, [SlaveNode2]),
  172. syn_test_suite_helper:disconnect_node(SlaveNode1),
  173. syn_test_suite_helper:disconnect_node(SlaveNode2),
  174. syn_test_suite_helper:assert_cluster(node(), []),
  175. %% check
  176. syn_test_suite_helper:assert_groups_scope_subcluster(node(), default, []),
  177. %% reconnect slave 1
  178. syn_test_suite_helper:connect_node(SlaveNode1),
  179. syn_test_suite_helper:assert_cluster(node(), [SlaveNode1]),
  180. syn_test_suite_helper:assert_cluster(SlaveNode1, [node()]),
  181. %% check
  182. syn_test_suite_helper:assert_groups_scope_subcluster(node(), default, [SlaveNode1]),
  183. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode1, default, [node()]),
  184. %% reconnect all
  185. syn_test_suite_helper:connect_node(SlaveNode2),
  186. rpc:call(SlaveNode1, syn_test_suite_helper, connect_node, [SlaveNode2]),
  187. syn_test_suite_helper:assert_cluster(node(), [SlaveNode1, SlaveNode2]),
  188. syn_test_suite_helper:assert_cluster(SlaveNode1, [node(), SlaveNode2]),
  189. syn_test_suite_helper:assert_cluster(SlaveNode2, [node(), SlaveNode1]),
  190. %% check
  191. syn_test_suite_helper:assert_groups_scope_subcluster(node(), default, [SlaveNode1, SlaveNode2]),
  192. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode1, default, [node(), SlaveNode2]),
  193. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode2, default, [node(), SlaveNode1]),
  194. %% simulate full netsplit, again
  195. rpc:call(SlaveNode1, syn_test_suite_helper, disconnect_node, [SlaveNode2]),
  196. syn_test_suite_helper:disconnect_node(SlaveNode1),
  197. syn_test_suite_helper:disconnect_node(SlaveNode2),
  198. syn_test_suite_helper:assert_cluster(node(), []),
  199. %% check
  200. syn_test_suite_helper:assert_groups_scope_subcluster(node(), default, []),
  201. %% reconnect all, again
  202. syn_test_suite_helper:connect_node(SlaveNode2),
  203. rpc:call(SlaveNode1, syn_test_suite_helper, connect_node, [SlaveNode2]),
  204. syn_test_suite_helper:assert_cluster(node(), [SlaveNode1, SlaveNode2]),
  205. syn_test_suite_helper:assert_cluster(SlaveNode1, [node(), SlaveNode2]),
  206. syn_test_suite_helper:assert_cluster(SlaveNode2, [node(), SlaveNode1]),
  207. %% check
  208. syn_test_suite_helper:assert_groups_scope_subcluster(node(), default, [SlaveNode1, SlaveNode2]),
  209. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode1, default, [node(), SlaveNode2]),
  210. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode2, default, [node(), SlaveNode1]),
  211. %% crash the scope process on local
  212. syn_test_suite_helper:kill_process(syn_registry_default),
  213. syn_test_suite_helper:wait_process_name_ready(syn_registry_default),
  214. %% check, it should have rebuilt after supervisor restarts it
  215. syn_test_suite_helper:assert_groups_scope_subcluster(node(), default, [SlaveNode1, SlaveNode2]),
  216. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode1, default, [node(), SlaveNode2]),
  217. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode2, default, [node(), SlaveNode1]),
  218. %% crash scopes supervisor on local
  219. syn_test_suite_helper:kill_process(syn_scopes_sup),
  220. syn_test_suite_helper:wait_process_name_ready(syn_registry_default),
  221. %% check
  222. syn_test_suite_helper:assert_groups_scope_subcluster(node(), default, [SlaveNode1, SlaveNode2]),
  223. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode1, default, [node(), SlaveNode2]),
  224. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode2, default, [node(), SlaveNode1]).
  225. three_nodes_discover_custom_scope(Config) ->
  226. %% get slaves
  227. SlaveNode1 = proplists:get_value(slave_node_1, Config),
  228. SlaveNode2 = proplists:get_value(slave_node_2, Config),
  229. %% start syn on nodes
  230. ok = syn:start(),
  231. ok = rpc:call(SlaveNode1, syn, start, []),
  232. ok = rpc:call(SlaveNode2, syn, start, []),
  233. %% add custom scopes
  234. ok = syn:add_node_to_scope(custom_scope_ab),
  235. ok = syn:add_node_to_scope(custom_scope_all),
  236. ok = rpc:call(SlaveNode1, syn, add_node_to_scopes, [[custom_scope_ab, custom_scope_bc, custom_scope_all]]),
  237. ok = rpc:call(SlaveNode2, syn, add_node_to_scopes, [[custom_scope_bc, custom_scope_c, custom_scope_all]]),
  238. %% get_subcluster_nodes should return invalid errors
  239. {'EXIT', {{invalid_scope, custom_abcdef}, _}} = catch syn_registry:get_subcluster_nodes(custom_abcdef),
  240. %% check
  241. syn_test_suite_helper:assert_groups_scope_subcluster(node(), custom_scope_ab, [SlaveNode1]),
  242. syn_test_suite_helper:assert_groups_scope_subcluster(node(), custom_scope_all, [SlaveNode1, SlaveNode2]),
  243. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode1, custom_scope_ab, [node()]),
  244. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode1, custom_scope_bc, [SlaveNode2]),
  245. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode1, custom_scope_all, [node(), SlaveNode2]),
  246. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode2, custom_scope_bc, [SlaveNode1]),
  247. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode2, custom_scope_c, []),
  248. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode2, custom_scope_all, [node(), SlaveNode1]),
  249. %% check default
  250. syn_test_suite_helper:assert_groups_scope_subcluster(node(), default, [SlaveNode1, SlaveNode2]),
  251. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode1, default, [node(), SlaveNode2]),
  252. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode2, default, [node(), SlaveNode1]),
  253. %% disconnect node 2 (node 1 can still see node 2)
  254. syn_test_suite_helper:disconnect_node(SlaveNode2),
  255. syn_test_suite_helper:assert_cluster(node(), [SlaveNode1]),
  256. syn_test_suite_helper:assert_cluster(SlaveNode1, [node(), SlaveNode2]),
  257. %% check
  258. syn_test_suite_helper:assert_groups_scope_subcluster(node(), custom_scope_ab, [SlaveNode1]),
  259. syn_test_suite_helper:assert_groups_scope_subcluster(node(), custom_scope_all, [SlaveNode1]),
  260. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode1, custom_scope_ab, [node()]),
  261. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode1, custom_scope_bc, [SlaveNode2]),
  262. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode1, custom_scope_all, [node(), SlaveNode2]),
  263. %% reconnect node 2
  264. syn_test_suite_helper:connect_node(SlaveNode2),
  265. syn_test_suite_helper:assert_cluster(node(), [SlaveNode1, SlaveNode2]),
  266. syn_test_suite_helper:assert_cluster(SlaveNode1, [node(), SlaveNode2]),
  267. syn_test_suite_helper:assert_cluster(SlaveNode2, [node(), SlaveNode1]),
  268. %% check
  269. syn_test_suite_helper:assert_groups_scope_subcluster(node(), custom_scope_ab, [SlaveNode1]),
  270. syn_test_suite_helper:assert_groups_scope_subcluster(node(), custom_scope_all, [SlaveNode1, SlaveNode2]),
  271. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode1, custom_scope_ab, [node()]),
  272. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode1, custom_scope_bc, [SlaveNode2]),
  273. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode1, custom_scope_all, [node(), SlaveNode2]),
  274. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode2, custom_scope_bc, [SlaveNode1]),
  275. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode2, custom_scope_c, []),
  276. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode2, custom_scope_all, [node(), SlaveNode1]),
  277. %% crash a scope process on 2
  278. rpc:call(SlaveNode2, syn_test_suite_helper, kill_process, [syn_registry_custom_scope_bc]),
  279. rpc:call(SlaveNode2, syn_test_suite_helper, wait_process_name_ready, [syn_registry_default]),
  280. %% check
  281. syn_test_suite_helper:assert_groups_scope_subcluster(node(), custom_scope_ab, [SlaveNode1]),
  282. syn_test_suite_helper:assert_groups_scope_subcluster(node(), custom_scope_all, [SlaveNode1, SlaveNode2]),
  283. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode1, custom_scope_ab, [node()]),
  284. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode1, custom_scope_bc, [SlaveNode2]),
  285. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode1, custom_scope_all, [node(), SlaveNode2]),
  286. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode2, custom_scope_bc, [SlaveNode1]),
  287. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode2, custom_scope_c, []),
  288. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode2, custom_scope_all, [node(), SlaveNode1]),
  289. %% crash scopes supervisor on local
  290. syn_test_suite_helper:kill_process(syn_scopes_sup),
  291. syn_test_suite_helper:wait_process_name_ready(syn_registry_default),
  292. %% check
  293. syn_test_suite_helper:assert_groups_scope_subcluster(node(), custom_scope_ab, [SlaveNode1]),
  294. syn_test_suite_helper:assert_groups_scope_subcluster(node(), custom_scope_all, [SlaveNode1, SlaveNode2]),
  295. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode1, custom_scope_ab, [node()]),
  296. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode1, custom_scope_bc, [SlaveNode2]),
  297. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode1, custom_scope_all, [node(), SlaveNode2]),
  298. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode2, custom_scope_bc, [SlaveNode1]),
  299. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode2, custom_scope_c, []),
  300. syn_test_suite_helper:assert_groups_scope_subcluster(SlaveNode2, custom_scope_all, [node(), SlaveNode1]).
  301. three_nodes_join_leave_and_monitor_default_scope(Config) ->
  302. %% get slaves
  303. SlaveNode1 = proplists:get_value(slave_node_1, Config),
  304. SlaveNode2 = proplists:get_value(slave_node_2, Config),
  305. %% start syn on nodes
  306. ok = syn:start(),
  307. ok = rpc:call(SlaveNode1, syn, start, []),
  308. ok = rpc:call(SlaveNode2, syn, start, []),
  309. %% start processes
  310. Pid = syn_test_suite_helper:start_process(),
  311. PidWithMeta = syn_test_suite_helper:start_process(),
  312. PidRemoteOn1 = syn_test_suite_helper:start_process(SlaveNode1),
  313. %% check
  314. [] = syn:get_members({group, "one"}),
  315. [] = rpc:call(SlaveNode1, syn, get_members, [{group, "one"}]),
  316. [] = rpc:call(SlaveNode2, syn, get_members, [{group, "one"}]),
  317. [] = syn:get_members({group, "two"}),
  318. [] = rpc:call(SlaveNode1, syn, get_members, [{group, "two"}]),
  319. [] = rpc:call(SlaveNode2, syn, get_members, [{group, "two"}]),
  320. 0 = syn:groups_count(default),
  321. 0 = syn:groups_count(default, node()),
  322. 0 = syn:groups_count(default, SlaveNode1),
  323. 0 = syn:groups_count(default, SlaveNode2),
  324. %% join
  325. ok = syn:join({group, "one"}, Pid),
  326. ok = syn:join({group, "one"}, PidWithMeta, <<"with meta">>),
  327. ok = syn:join({group, "one"}, PidRemoteOn1),
  328. ok = syn:join({group, "two"}, Pid),
  329. ok = syn:join({group, "two"}, PidWithMeta, "with-meta-2"),
  330. %% errors
  331. {error, not_alive} = syn:join({"pid not alive"}, list_to_pid("<0.9999.0>")),
  332. {error, not_in_group} = syn:leave({group, "three"}, Pid),
  333. %% retrieve
  334. syn_test_suite_helper:assert_wait(
  335. lists:sort([{Pid, undefined}, {PidWithMeta, <<"with meta">>}, {PidRemoteOn1, undefined}]),
  336. fun() -> lists:sort(syn:get_members({group, "one"})) end
  337. ),
  338. syn_test_suite_helper:assert_wait(
  339. lists:sort([{Pid, undefined}, {PidWithMeta, <<"with meta">>}, {PidRemoteOn1, undefined}]),
  340. fun() -> lists:sort(rpc:call(SlaveNode1, syn, get_members, [{group, "one"}])) end
  341. ),
  342. syn_test_suite_helper:assert_wait(
  343. lists:sort([{Pid, undefined}, {PidWithMeta, <<"with meta">>}, {PidRemoteOn1, undefined}]),
  344. fun() -> lists:sort(rpc:call(SlaveNode2, syn, get_members, [{group, "one"}])) end
  345. ),
  346. syn_test_suite_helper:assert_wait(
  347. lists:sort([{Pid, undefined}, {PidWithMeta, "with-meta-2"}]),
  348. fun() -> lists:sort(syn:get_members({group, "two"})) end
  349. ),
  350. syn_test_suite_helper:assert_wait(
  351. lists:sort([{Pid, undefined}, {PidWithMeta, "with-meta-2"}]),
  352. fun() -> lists:sort(rpc:call(SlaveNode1, syn, get_members, [{group, "two"}])) end
  353. ),
  354. syn_test_suite_helper:assert_wait(
  355. lists:sort([{Pid, undefined}, {PidWithMeta, "with-meta-2"}]),
  356. fun() -> lists:sort(rpc:call(SlaveNode2, syn, get_members, [{group, "two"}])) end
  357. ),
  358. 2 = syn:groups_count(default),
  359. 2 = syn:groups_count(default, node()),
  360. 1 = syn:groups_count(default, SlaveNode1),
  361. 0 = syn:groups_count(default, SlaveNode2),
  362. %% re-join to edit meta
  363. ok = syn:join({group, "one"}, PidWithMeta, <<"with updated meta">>),
  364. ok = rpc:call(SlaveNode2, syn, join, [{group, "one"}, PidRemoteOn1, added_meta]), %% updated on slave 2
  365. %% retrieve
  366. syn_test_suite_helper:assert_wait(
  367. lists:sort([{Pid, undefined}, {PidWithMeta, <<"with updated meta">>}, {PidRemoteOn1, added_meta}]),
  368. fun() -> lists:sort(syn:get_members({group, "one"})) end
  369. ),
  370. syn_test_suite_helper:assert_wait(
  371. lists:sort([{Pid, undefined}, {PidWithMeta, <<"with updated meta">>}, {PidRemoteOn1, added_meta}]),
  372. fun() -> lists:sort(rpc:call(SlaveNode1, syn, get_members, [{group, "one"}])) end
  373. ),
  374. syn_test_suite_helper:assert_wait(
  375. lists:sort([{Pid, undefined}, {PidWithMeta, <<"with updated meta">>}, {PidRemoteOn1, added_meta}]),
  376. fun() -> lists:sort(rpc:call(SlaveNode2, syn, get_members, [{group, "one"}])) end
  377. ),
  378. syn_test_suite_helper:assert_wait(
  379. lists:sort([{Pid, undefined}, {PidWithMeta, "with-meta-2"}]),
  380. fun() -> lists:sort(syn:get_members({group, "two"})) end
  381. ),
  382. syn_test_suite_helper:assert_wait(
  383. lists:sort([{Pid, undefined}, {PidWithMeta, "with-meta-2"}]),
  384. fun() -> lists:sort(rpc:call(SlaveNode1, syn, get_members, [{group, "two"}])) end
  385. ),
  386. syn_test_suite_helper:assert_wait(
  387. lists:sort([{Pid, undefined}, {PidWithMeta, "with-meta-2"}]),
  388. fun() -> lists:sort(rpc:call(SlaveNode2, syn, get_members, [{group, "two"}])) end
  389. ),
  390. 2 = syn:groups_count(default),
  391. 2 = syn:groups_count(default, node()),
  392. 1 = syn:groups_count(default, SlaveNode1),
  393. 0 = syn:groups_count(default, SlaveNode2),
  394. %% crash scope process to ensure that monitors get recreated
  395. exit(whereis(syn_groups_default), kill),
  396. syn_test_suite_helper:wait_process_name_ready(syn_groups_default),
  397. %% kill process
  398. syn_test_suite_helper:kill_process(Pid),
  399. syn_test_suite_helper:kill_process(PidRemoteOn1),
  400. %% leave
  401. ok = syn:leave({group, "one"}, PidWithMeta),
  402. %% retrieve
  403. syn_test_suite_helper:assert_wait(
  404. [],
  405. fun() -> lists:sort(syn:get_members({group, "one"})) end
  406. ),
  407. syn_test_suite_helper:assert_wait(
  408. [],
  409. fun() -> lists:sort(rpc:call(SlaveNode1, syn, get_members, [{group, "one"}])) end
  410. ),
  411. syn_test_suite_helper:assert_wait(
  412. [],
  413. fun() -> lists:sort(rpc:call(SlaveNode2, syn, get_members, [{group, "one"}])) end
  414. ),
  415. syn_test_suite_helper:assert_wait(
  416. [{PidWithMeta, "with-meta-2"}],
  417. fun() -> lists:sort(syn:get_members({group, "two"})) end
  418. ),
  419. syn_test_suite_helper:assert_wait(
  420. [{PidWithMeta, "with-meta-2"}],
  421. fun() -> lists:sort(rpc:call(SlaveNode1, syn, get_members, [{group, "two"}])) end
  422. ),
  423. syn_test_suite_helper:assert_wait(
  424. [{PidWithMeta, "with-meta-2"}],
  425. fun() -> lists:sort(rpc:call(SlaveNode2, syn, get_members, [{group, "two"}])) end
  426. ),
  427. 1 = syn:groups_count(default),
  428. 1 = syn:groups_count(default, node()),
  429. 0 = syn:groups_count(default, SlaveNode1),
  430. 0 = syn:groups_count(default, SlaveNode2),
  431. %% errors
  432. {error, not_in_group} = syn:leave({group, "one"}, PidWithMeta).
  433. three_nodes_join_leave_and_monitor_custom_scope(Config) ->
  434. %% get slaves
  435. SlaveNode1 = proplists:get_value(slave_node_1, Config),
  436. SlaveNode2 = proplists:get_value(slave_node_2, Config),
  437. %% start syn on nodes
  438. ok = syn:start(),
  439. ok = rpc:call(SlaveNode1, syn, start, []),
  440. ok = rpc:call(SlaveNode2, syn, start, []),
  441. %% add custom scopes
  442. ok = syn:add_node_to_scope(custom_scope_ab),
  443. ok = rpc:call(SlaveNode1, syn, add_node_to_scopes, [[custom_scope_ab, custom_scope_bc]]),
  444. ok = rpc:call(SlaveNode2, syn, add_node_to_scopes, [[custom_scope_bc]]),
  445. %% start processes
  446. Pid = syn_test_suite_helper:start_process(),
  447. PidWithMeta = syn_test_suite_helper:start_process(),
  448. PidRemoteOn1 = syn_test_suite_helper:start_process(SlaveNode1),
  449. PidRemoteOn2 = syn_test_suite_helper:start_process(SlaveNode2),
  450. %% check
  451. [] = syn:get_members(custom_scope_ab, {group, "one"}),
  452. [] = rpc:call(SlaveNode1, syn, get_members, [custom_scope_ab, {group, "one"}]),
  453. {badrpc, {'EXIT', {{invalid_scope, custom_scope_ab}, _}}} = catch rpc:call(SlaveNode2, syn, get_members, [custom_scope_ab, {group, "one"}]),
  454. {'EXIT', {{invalid_scope, custom_scope_bc}, _}} = catch syn:get_members(custom_scope_bc, {group, "one"}),
  455. [] = rpc:call(SlaveNode1, syn, get_members, [custom_scope_bc, {group, "one"}]),
  456. [] = rpc:call(SlaveNode2, syn, get_members, [custom_scope_bc, {group, "one"}]),
  457. [] = syn:get_members(custom_scope_ab, {group, "two"}),
  458. [] = rpc:call(SlaveNode1, syn, get_members, [custom_scope_ab, {group, "two"}]),
  459. {badrpc, {'EXIT', {{invalid_scope, custom_scope_ab}, _}}} = catch rpc:call(SlaveNode2, syn, get_members, [custom_scope_ab, {group, "two"}]),
  460. {'EXIT', {{invalid_scope, custom_scope_bc}, _}} = catch syn:get_members(custom_scope_bc, {group, "two"}),
  461. [] = rpc:call(SlaveNode1, syn, get_members, [custom_scope_bc, {group, "two"}]),
  462. [] = rpc:call(SlaveNode2, syn, get_members, [custom_scope_bc, {group, "two"}]),
  463. %% join
  464. ok = syn:join(custom_scope_ab, {group, "one"}, Pid),
  465. ok = syn:join(custom_scope_ab, {group, "one"}, PidWithMeta, <<"with meta">>),
  466. ok = rpc:call(SlaveNode1, syn, join, [custom_scope_bc, {group, "two"}, PidRemoteOn1]),
  467. ok = syn:join(custom_scope_ab, {group, "two"}, Pid),
  468. ok = syn:join(custom_scope_ab, {group, "two"}, PidWithMeta, "with-meta-2"),
  469. %% errors
  470. {error, not_alive} = syn:join({"pid not alive"}, list_to_pid("<0.9999.0>")),
  471. {error, not_in_group} = syn:leave({group, "three"}, Pid),
  472. {'EXIT', {{invalid_scope, custom_scope_ab}, _}} = catch syn:join(custom_scope_ab, {group, "one"}, PidRemoteOn2),
  473. %% retrieve
  474. syn_test_suite_helper:assert_wait(
  475. lists:sort([{Pid, undefined}, {PidWithMeta, <<"with meta">>}]),
  476. fun() -> lists:sort(syn:get_members(custom_scope_ab, {group, "one"})) end
  477. ),
  478. syn_test_suite_helper:assert_wait(
  479. lists:sort([{Pid, undefined}, {PidWithMeta, <<"with meta">>}]),
  480. fun() -> lists:sort(rpc:call(SlaveNode1, syn, get_members, [custom_scope_ab, {group, "one"}])) end
  481. ),
  482. {badrpc, {'EXIT', {{invalid_scope, custom_scope_ab}, _}}} = catch rpc:call(SlaveNode2, syn, get_members, [custom_scope_ab, {group, "one"}]),
  483. syn_test_suite_helper:assert_wait(
  484. lists:sort([{Pid, undefined}, {PidWithMeta, "with-meta-2"}]),
  485. fun() -> lists:sort(syn:get_members(custom_scope_ab, {group, "two"})) end
  486. ),
  487. syn_test_suite_helper:assert_wait(
  488. lists:sort([{Pid, undefined}, {PidWithMeta, "with-meta-2"}]),
  489. fun() -> lists:sort(rpc:call(SlaveNode1, syn, get_members, [custom_scope_ab, {group, "two"}])) end
  490. ),
  491. {badrpc, {'EXIT', {{invalid_scope, custom_scope_ab}, _}}} = catch rpc:call(SlaveNode2, syn, get_members, [custom_scope_ab, {group, "two"}]),
  492. {'EXIT', {{invalid_scope, custom_scope_bc}, _}} = catch syn:get_members(custom_scope_bc, {group, "two"}),
  493. syn_test_suite_helper:assert_wait(
  494. [{PidRemoteOn1, undefined}],
  495. fun() -> lists:sort(rpc:call(SlaveNode1, syn, get_members, [custom_scope_bc, {group, "two"}])) end
  496. ),
  497. syn_test_suite_helper:assert_wait(
  498. [{PidRemoteOn1, undefined}],
  499. fun() -> lists:sort(rpc:call(SlaveNode2, syn, get_members, [custom_scope_bc, {group, "two"}])) end
  500. ),
  501. 2 = syn:groups_count(custom_scope_ab),
  502. 2 = syn:groups_count(custom_scope_ab, node()),
  503. 0 = syn:groups_count(custom_scope_ab, SlaveNode1),
  504. 0 = syn:groups_count(custom_scope_ab, SlaveNode2),
  505. {'EXIT', {{invalid_scope, custom_scope_bc}, _}} = catch syn:groups_count(custom_scope_bc),
  506. {'EXIT', {{invalid_scope, custom_scope_bc}, _}} = catch syn:groups_count(custom_scope_bc, node()),
  507. {'EXIT', {{invalid_scope, custom_scope_bc}, _}} = catch syn:groups_count(custom_scope_bc, SlaveNode1),
  508. {'EXIT', {{invalid_scope, custom_scope_bc}, _}} = catch syn:groups_count(custom_scope_bc, SlaveNode2),
  509. 2 = rpc:call(SlaveNode1, syn, groups_count, [custom_scope_ab]),
  510. 2 = rpc:call(SlaveNode1, syn, groups_count, [custom_scope_ab, node()]),
  511. 0 = rpc:call(SlaveNode1, syn, groups_count, [custom_scope_ab, SlaveNode1]),
  512. 0 = rpc:call(SlaveNode1, syn, groups_count, [custom_scope_ab, SlaveNode2]),
  513. 1 = rpc:call(SlaveNode1, syn, groups_count, [custom_scope_bc]),
  514. 0 = rpc:call(SlaveNode1, syn, groups_count, [custom_scope_bc, node()]),
  515. 1 = rpc:call(SlaveNode1, syn, groups_count, [custom_scope_bc, SlaveNode1]),
  516. 0 = rpc:call(SlaveNode1, syn, groups_count, [custom_scope_bc, SlaveNode2]),
  517. {badrpc, {'EXIT', {{invalid_scope, custom_scope_ab}, _}}} = catch rpc:call(SlaveNode2, syn, groups_count, [custom_scope_ab]),
  518. {badrpc, {'EXIT', {{invalid_scope, custom_scope_ab}, _}}} = catch rpc:call(SlaveNode2, syn, groups_count, [custom_scope_ab, node()]),
  519. {badrpc, {'EXIT', {{invalid_scope, custom_scope_ab}, _}}} = catch rpc:call(SlaveNode2, syn, groups_count, [custom_scope_ab, SlaveNode1]),
  520. {badrpc, {'EXIT', {{invalid_scope, custom_scope_ab}, _}}} = catch rpc:call(SlaveNode2, syn, groups_count, [custom_scope_ab, SlaveNode2]),
  521. 1 = rpc:call(SlaveNode2, syn, groups_count, [custom_scope_bc]),
  522. 0 = rpc:call(SlaveNode2, syn, groups_count, [custom_scope_bc, node()]),
  523. 1 = rpc:call(SlaveNode2, syn, groups_count, [custom_scope_bc, SlaveNode1]),
  524. 0 = rpc:call(SlaveNode2, syn, groups_count, [custom_scope_bc, SlaveNode2]),
  525. %% re-join to edit meta
  526. ok = syn:join(custom_scope_ab, {group, "one"}, PidWithMeta, <<"with updated meta">>),
  527. ok = rpc:call(SlaveNode2, syn, join, [custom_scope_bc, {group, "two"}, PidRemoteOn1, added_meta]), %% updated on slave 2
  528. %% retrieve
  529. syn_test_suite_helper:assert_wait(
  530. lists:sort([{Pid, undefined}, {PidWithMeta, <<"with updated meta">>}]),
  531. fun() -> lists:sort(syn:get_members(custom_scope_ab, {group, "one"})) end
  532. ),
  533. syn_test_suite_helper:assert_wait(
  534. lists:sort([{Pid, undefined}, {PidWithMeta, <<"with updated meta">>}]),
  535. fun() -> lists:sort(rpc:call(SlaveNode1, syn, get_members, [custom_scope_ab, {group, "one"}])) end
  536. ),
  537. {badrpc, {'EXIT', {{invalid_scope, custom_scope_ab}, _}}} = catch rpc:call(SlaveNode2, syn, get_members, [custom_scope_ab, {group, "one"}]),
  538. syn_test_suite_helper:assert_wait(
  539. lists:sort([{Pid, undefined}, {PidWithMeta, "with-meta-2"}]),
  540. fun() -> lists:sort(syn:get_members(custom_scope_ab, {group, "two"})) end
  541. ),
  542. syn_test_suite_helper:assert_wait(
  543. lists:sort([{Pid, undefined}, {PidWithMeta, "with-meta-2"}]),
  544. fun() -> lists:sort(rpc:call(SlaveNode1, syn, get_members, [custom_scope_ab, {group, "two"}])) end
  545. ),
  546. {badrpc, {'EXIT', {{invalid_scope, custom_scope_ab}, _}}} = catch rpc:call(SlaveNode2, syn, get_members, [custom_scope_ab, {group, "two"}]),
  547. {'EXIT', {{invalid_scope, custom_scope_bc}, _}} = catch syn:get_members(custom_scope_bc, {group, "two"}),
  548. syn_test_suite_helper:assert_wait(
  549. [{PidRemoteOn1, added_meta}],
  550. fun() -> lists:sort(rpc:call(SlaveNode1, syn, get_members, [custom_scope_bc, {group, "two"}])) end
  551. ),
  552. syn_test_suite_helper:assert_wait(
  553. [{PidRemoteOn1, added_meta}],
  554. fun() -> lists:sort(rpc:call(SlaveNode2, syn, get_members, [custom_scope_bc, {group, "two"}])) end
  555. ),
  556. 2 = syn:groups_count(custom_scope_ab),
  557. 2 = syn:groups_count(custom_scope_ab, node()),
  558. 0 = syn:groups_count(custom_scope_ab, SlaveNode1),
  559. 0 = syn:groups_count(custom_scope_ab, SlaveNode2),
  560. {'EXIT', {{invalid_scope, custom_scope_bc}, _}} = catch syn:groups_count(custom_scope_bc),
  561. {'EXIT', {{invalid_scope, custom_scope_bc}, _}} = catch syn:groups_count(custom_scope_bc, node()),
  562. {'EXIT', {{invalid_scope, custom_scope_bc}, _}} = catch syn:groups_count(custom_scope_bc, SlaveNode1),
  563. {'EXIT', {{invalid_scope, custom_scope_bc}, _}} = catch syn:groups_count(custom_scope_bc, SlaveNode2),
  564. 2 = rpc:call(SlaveNode1, syn, groups_count, [custom_scope_ab]),
  565. 2 = rpc:call(SlaveNode1, syn, groups_count, [custom_scope_ab, node()]),
  566. 0 = rpc:call(SlaveNode1, syn, groups_count, [custom_scope_ab, SlaveNode1]),
  567. 0 = rpc:call(SlaveNode1, syn, groups_count, [custom_scope_ab, SlaveNode2]),
  568. 1 = rpc:call(SlaveNode1, syn, groups_count, [custom_scope_bc]),
  569. 0 = rpc:call(SlaveNode1, syn, groups_count, [custom_scope_bc, node()]),
  570. 1 = rpc:call(SlaveNode1, syn, groups_count, [custom_scope_bc, SlaveNode1]),
  571. 0 = rpc:call(SlaveNode1, syn, groups_count, [custom_scope_bc, SlaveNode2]),
  572. {badrpc, {'EXIT', {{invalid_scope, custom_scope_ab}, _}}} = catch rpc:call(SlaveNode2, syn, groups_count, [custom_scope_ab]),
  573. {badrpc, {'EXIT', {{invalid_scope, custom_scope_ab}, _}}} = catch rpc:call(SlaveNode2, syn, groups_count, [custom_scope_ab, node()]),
  574. {badrpc, {'EXIT', {{invalid_scope, custom_scope_ab}, _}}} = catch rpc:call(SlaveNode2, syn, groups_count, [custom_scope_ab, SlaveNode1]),
  575. {badrpc, {'EXIT', {{invalid_scope, custom_scope_ab}, _}}} = catch rpc:call(SlaveNode2, syn, groups_count, [custom_scope_ab, SlaveNode2]),
  576. 1 = rpc:call(SlaveNode2, syn, groups_count, [custom_scope_bc]),
  577. 0 = rpc:call(SlaveNode2, syn, groups_count, [custom_scope_bc, node()]),
  578. 1 = rpc:call(SlaveNode2, syn, groups_count, [custom_scope_bc, SlaveNode1]),
  579. 0 = rpc:call(SlaveNode2, syn, groups_count, [custom_scope_bc, SlaveNode2]),
  580. %% crash scope process to ensure that monitors get recreated
  581. exit(whereis(syn_groups_custom_scope_ab), kill),
  582. syn_test_suite_helper:wait_process_name_ready(syn_groups_custom_scope_ab),
  583. %% kill process
  584. syn_test_suite_helper:kill_process(Pid),
  585. syn_test_suite_helper:kill_process(PidRemoteOn1),
  586. %% leave
  587. ok = syn:leave(custom_scope_ab, {group, "one"}, PidWithMeta),
  588. %% retrieve
  589. syn_test_suite_helper:assert_wait(
  590. [],
  591. fun() -> lists:sort(syn:get_members(custom_scope_ab, {group, "one"})) end
  592. ),
  593. syn_test_suite_helper:assert_wait(
  594. [],
  595. fun() -> lists:sort(rpc:call(SlaveNode1, syn, get_members, [custom_scope_ab, {group, "one"}])) end
  596. ),
  597. {badrpc, {'EXIT', {{invalid_scope, custom_scope_ab}, _}}} = catch rpc:call(SlaveNode2, syn, get_members, [custom_scope_ab, {group, "one"}]),
  598. syn_test_suite_helper:assert_wait(
  599. [{PidWithMeta, "with-meta-2"}],
  600. fun() -> lists:sort(syn:get_members(custom_scope_ab, {group, "two"})) end
  601. ),
  602. syn_test_suite_helper:assert_wait(
  603. [{PidWithMeta, "with-meta-2"}],
  604. fun() -> lists:sort(rpc:call(SlaveNode1, syn, get_members, [custom_scope_ab, {group, "two"}])) end
  605. ),
  606. {badrpc, {'EXIT', {{invalid_scope, custom_scope_ab}, _}}} = catch rpc:call(SlaveNode2, syn, get_members, [custom_scope_ab, {group, "two"}]),
  607. {'EXIT', {{invalid_scope, custom_scope_bc}, _}} = catch syn:get_members(custom_scope_bc, {group, "two"}),
  608. syn_test_suite_helper:assert_wait(
  609. [],
  610. fun() -> lists:sort(rpc:call(SlaveNode1, syn, get_members, [custom_scope_bc, {group, "two"}])) end
  611. ),
  612. syn_test_suite_helper:assert_wait(
  613. [], fun() -> lists:sort(rpc:call(SlaveNode2, syn, get_members, [custom_scope_bc, {group, "two"}])) end
  614. ),
  615. 1 = syn:groups_count(custom_scope_ab),
  616. 1 = syn:groups_count(custom_scope_ab, node()),
  617. 0 = syn:groups_count(custom_scope_ab, SlaveNode1),
  618. 0 = syn:groups_count(custom_scope_ab, SlaveNode2),
  619. {'EXIT', {{invalid_scope, custom_scope_bc}, _}} = catch syn:groups_count(custom_scope_bc),
  620. {'EXIT', {{invalid_scope, custom_scope_bc}, _}} = catch syn:groups_count(custom_scope_bc, node()),
  621. {'EXIT', {{invalid_scope, custom_scope_bc}, _}} = catch syn:groups_count(custom_scope_bc, SlaveNode1),
  622. {'EXIT', {{invalid_scope, custom_scope_bc}, _}} = catch syn:groups_count(custom_scope_bc, SlaveNode2),
  623. 1 = rpc:call(SlaveNode1, syn, groups_count, [custom_scope_ab]),
  624. 1 = rpc:call(SlaveNode1, syn, groups_count, [custom_scope_ab, node()]),
  625. 0 = rpc:call(SlaveNode1, syn, groups_count, [custom_scope_ab, SlaveNode1]),
  626. 0 = rpc:call(SlaveNode1, syn, groups_count, [custom_scope_ab, SlaveNode2]),
  627. 0 = rpc:call(SlaveNode1, syn, groups_count, [custom_scope_bc]),
  628. 0 = rpc:call(SlaveNode1, syn, groups_count, [custom_scope_bc, node()]),
  629. 0 = rpc:call(SlaveNode1, syn, groups_count, [custom_scope_bc, SlaveNode1]),
  630. 0 = rpc:call(SlaveNode1, syn, groups_count, [custom_scope_bc, SlaveNode2]),
  631. {badrpc, {'EXIT', {{invalid_scope, custom_scope_ab}, _}}} = catch rpc:call(SlaveNode2, syn, groups_count, [custom_scope_ab]),
  632. {badrpc, {'EXIT', {{invalid_scope, custom_scope_ab}, _}}} = catch rpc:call(SlaveNode2, syn, groups_count, [custom_scope_ab, node()]),
  633. {badrpc, {'EXIT', {{invalid_scope, custom_scope_ab}, _}}} = catch rpc:call(SlaveNode2, syn, groups_count, [custom_scope_ab, SlaveNode1]),
  634. {badrpc, {'EXIT', {{invalid_scope, custom_scope_ab}, _}}} = catch rpc:call(SlaveNode2, syn, groups_count, [custom_scope_ab, SlaveNode2]),
  635. 0 = rpc:call(SlaveNode2, syn, groups_count, [custom_scope_bc]),
  636. 0 = rpc:call(SlaveNode2, syn, groups_count, [custom_scope_bc, node()]),
  637. 0 = rpc:call(SlaveNode2, syn, groups_count, [custom_scope_bc, SlaveNode1]),
  638. 0 = rpc:call(SlaveNode2, syn, groups_count, [custom_scope_bc, SlaveNode2]),
  639. %% errors
  640. {error, not_in_group} = syn:leave(custom_scope_ab, {group, "one"}, PidWithMeta).