/work/auto_tests/group_moderation_test.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Tests group moderation functionality. |
3 | | * |
4 | | * Note that making the peer count too high will break things. This test should not be relied on |
5 | | * for general group/syncing functionality. |
6 | | */ |
7 | | |
8 | | #include <stdbool.h> |
9 | | #include <stdint.h> |
10 | | #include <string.h> |
11 | | |
12 | | #include "auto_test_support.h" |
13 | | #include "check_compat.h" |
14 | | |
15 | | #include "../toxcore/tox.h" |
16 | | |
17 | 1.58k | #define NUM_GROUP_TOXES 5 |
18 | 2 | #define GROUP_NAME "NASA Headquarters" |
19 | 1 | #define GROUP_NAME_LEN (sizeof(GROUP_NAME) - 1) |
20 | | |
21 | | typedef struct Peer { |
22 | | char name[TOX_MAX_NAME_LENGTH + 1]; |
23 | | size_t name_length; |
24 | | uint32_t peer_id; |
25 | | } Peer; |
26 | | |
27 | | typedef struct State { |
28 | | char self_name[TOX_MAX_NAME_LENGTH + 1]; |
29 | | size_t self_name_length; |
30 | | |
31 | | uint32_t group_number; |
32 | | |
33 | | uint32_t num_peers; |
34 | | Peer peers[NUM_GROUP_TOXES - 1]; |
35 | | |
36 | | bool mod_check; |
37 | | size_t mod_event_count; |
38 | | char mod_name1[TOX_MAX_NAME_LENGTH]; |
39 | | char mod_name2[TOX_MAX_NAME_LENGTH]; |
40 | | |
41 | | |
42 | | bool observer_check; |
43 | | size_t observer_event_count; |
44 | | char observer_name1[TOX_MAX_NAME_LENGTH]; |
45 | | char observer_name2[TOX_MAX_NAME_LENGTH]; |
46 | | |
47 | | bool user_check; |
48 | | size_t user_event_count; |
49 | | |
50 | | bool kick_check; // mod gets kicked |
51 | | } State; |
52 | | |
53 | | static bool all_peers_connected(AutoTox *autotoxes) |
54 | 159 | { |
55 | 313 | for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) { |
56 | 312 | const State *state = (const State *)autotoxes[i].state; |
57 | | |
58 | 312 | if (state->num_peers != NUM_GROUP_TOXES - 1) { |
59 | 158 | return false; |
60 | 158 | } |
61 | | |
62 | 154 | if (!tox_group_is_connected(autotoxes[i].tox, state->group_number, nullptr)) { |
63 | 0 | return false; |
64 | 0 | } |
65 | 154 | } |
66 | | |
67 | 1 | return true; |
68 | 159 | } |
69 | | |
70 | | /* |
71 | | * Waits for all peers to receive the mod event. |
72 | | */ |
73 | | static void check_mod_event(AutoTox *autotoxes, size_t num_peers, Tox_Group_Mod_Event event) |
74 | 7 | { |
75 | 7 | uint32_t peers_recv_changes = 0; |
76 | | |
77 | 7 | do { |
78 | 7 | peers_recv_changes = 0; |
79 | | |
80 | 7 | iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL); |
81 | | |
82 | 42 | for (size_t i = 0; i < num_peers; ++i) { |
83 | 35 | State *state = (State *)autotoxes[i].state; |
84 | 35 | bool check = false; |
85 | | |
86 | 35 | switch (event) { |
87 | 10 | case TOX_GROUP_MOD_EVENT_MODERATOR: { |
88 | 10 | if (state->mod_check) { |
89 | 10 | check = true; |
90 | 10 | state->mod_check = false; |
91 | 10 | } |
92 | | |
93 | 10 | break; |
94 | 0 | } |
95 | | |
96 | 10 | case TOX_GROUP_MOD_EVENT_OBSERVER: { |
97 | 10 | if (state->observer_check) { |
98 | 10 | check = true; |
99 | 10 | state->observer_check = false; |
100 | 10 | } |
101 | | |
102 | 10 | break; |
103 | 0 | } |
104 | | |
105 | 10 | case TOX_GROUP_MOD_EVENT_USER: { |
106 | 10 | if (state->user_check) { |
107 | 10 | check = true; |
108 | 10 | state->user_check = false; |
109 | 10 | } |
110 | | |
111 | 10 | break; |
112 | 0 | } |
113 | | |
114 | 5 | case TOX_GROUP_MOD_EVENT_KICK: { |
115 | 5 | check = state->kick_check; |
116 | 5 | break; |
117 | 0 | } |
118 | | |
119 | 0 | default: { |
120 | 0 | ck_assert(0); |
121 | 0 | } |
122 | 35 | } |
123 | | |
124 | 35 | if (check) { |
125 | 35 | ++peers_recv_changes; |
126 | 35 | } |
127 | 35 | } |
128 | 7 | } while (peers_recv_changes < num_peers - 1); |
129 | 7 | } |
130 | | |
131 | | static uint32_t get_peer_id_by_nick(const Peer *peers, uint32_t num_peers, const char *name) |
132 | 2 | { |
133 | 2 | ck_assert(name != nullptr); |
134 | | |
135 | 3 | for (uint32_t i = 0; i < num_peers; ++i) { |
136 | 3 | if (memcmp(peers[i].name, name, peers[i].name_length) == 0) { |
137 | 2 | return peers[i].peer_id; |
138 | 2 | } |
139 | 3 | } |
140 | | |
141 | 0 | ck_assert_msg(0, "Failed to find peer id"); |
142 | 0 | } |
143 | | |
144 | | static size_t get_state_index_by_nick(const AutoTox *autotoxes, size_t num_peers, const char *name, size_t name_length) |
145 | 1 | { |
146 | 1 | ck_assert(name != nullptr && name_length <= TOX_MAX_NAME_LENGTH); |
147 | | |
148 | 2 | for (size_t i = 0; i < num_peers; ++i) { |
149 | 2 | const State *state = (const State *)autotoxes[i].state; |
150 | | |
151 | 2 | if (memcmp(state->self_name, name, name_length) == 0) { |
152 | 1 | return i; |
153 | 1 | } |
154 | 2 | } |
155 | | |
156 | 0 | ck_assert_msg(0, "Failed to find index"); |
157 | 0 | } |
158 | | |
159 | | static void group_join_fail_handler(Tox *tox, const Tox_Event_Group_Join_Fail *event, void *user_data) |
160 | 0 | { |
161 | 0 | const Tox_Group_Join_Fail fail_type = tox_event_group_join_fail_get_fail_type(event); |
162 | 0 | fprintf(stderr, "Failed to join group: %d", fail_type); |
163 | 0 | } |
164 | | |
165 | | static void group_peer_join_handler(Tox *tox, const Tox_Event_Group_Peer_Join *event, void *user_data) |
166 | 20 | { |
167 | 20 | AutoTox *autotox = (AutoTox *)user_data; |
168 | 20 | ck_assert(autotox != nullptr); |
169 | | |
170 | 20 | State *state = (State *)autotox->state; |
171 | | |
172 | 20 | const uint32_t group_number = tox_event_group_peer_join_get_group_number(event); |
173 | 20 | const uint32_t peer_id = tox_event_group_peer_join_get_peer_id(event); |
174 | | |
175 | 20 | ck_assert(state->group_number == group_number); |
176 | | |
177 | 20 | char peer_name[TOX_MAX_NAME_LENGTH + 1]; |
178 | | |
179 | 20 | Tox_Err_Group_Peer_Query q_err; |
180 | 20 | size_t peer_name_len = tox_group_peer_get_name_size(tox, group_number, peer_id, &q_err); |
181 | | |
182 | 20 | ck_assert(q_err == TOX_ERR_GROUP_PEER_QUERY_OK); |
183 | 20 | ck_assert(peer_name_len <= TOX_MAX_NAME_LENGTH); |
184 | | |
185 | 20 | tox_group_peer_get_name(tox, group_number, peer_id, (uint8_t *) peer_name, &q_err); |
186 | 20 | peer_name[peer_name_len] = 0; |
187 | 20 | ck_assert(q_err == TOX_ERR_GROUP_PEER_QUERY_OK); |
188 | | |
189 | 20 | Peer *peer = &state->peers[state->num_peers]; |
190 | | |
191 | 20 | peer->peer_id = peer_id; |
192 | 20 | memcpy(peer->name, peer_name, peer_name_len); |
193 | 20 | peer->name_length = peer_name_len; |
194 | | |
195 | 20 | ++state->num_peers; |
196 | | |
197 | 20 | ck_assert(state->num_peers < NUM_GROUP_TOXES); |
198 | 20 | } |
199 | | |
200 | | static void handle_mod(State *state, const char *peer_name, size_t peer_name_len) |
201 | 8 | { |
202 | 8 | if (state->mod_event_count == 0) { |
203 | 4 | ck_assert(memcmp(peer_name, state->mod_name1, peer_name_len) == 0); |
204 | 4 | } else if (state->mod_event_count == 1) { |
205 | 4 | ck_assert(memcmp(peer_name, state->mod_name2, peer_name_len) == 0); |
206 | 4 | } else { |
207 | 0 | ck_assert(false); |
208 | 0 | } |
209 | | |
210 | 8 | ++state->mod_event_count; |
211 | 8 | state->mod_check = true; |
212 | 8 | } |
213 | | |
214 | | static void handle_observer(State *state, const char *peer_name, size_t peer_name_len) |
215 | 8 | { |
216 | 8 | if (state->observer_event_count == 0) { |
217 | 4 | ck_assert(memcmp(peer_name, state->observer_name1, peer_name_len) == 0); |
218 | 4 | } else if (state->observer_event_count == 1) { |
219 | 4 | ck_assert(memcmp(peer_name, state->observer_name2, peer_name_len) == 0); |
220 | 4 | } else { |
221 | 0 | ck_assert(false); |
222 | 0 | } |
223 | | |
224 | 8 | ++state->observer_event_count; |
225 | 8 | state->observer_check = true; |
226 | 8 | } |
227 | | |
228 | | static void handle_user(State *state, const char *peer_name, size_t peer_name_len) |
229 | 15 | { |
230 | | // event 1: observer1 gets promoted back to user |
231 | | // event 2: observer2 gets promoted to moderator |
232 | | // event 3: moderator 1 gets kicked |
233 | | // event 4: moderator 2 gets demoted to moderator |
234 | 15 | if (state->user_event_count == 0) { |
235 | 4 | ck_assert(memcmp(peer_name, state->observer_name1, peer_name_len) == 0); |
236 | 11 | } else if (state->user_event_count == 1) { |
237 | 4 | ck_assert(memcmp(peer_name, state->observer_name2, peer_name_len) == 0); |
238 | 7 | } else if (state->user_event_count == 2) { |
239 | 4 | ck_assert(memcmp(peer_name, state->mod_name1, peer_name_len) == 0); |
240 | 4 | } else if (state->user_event_count == 3) { |
241 | 3 | ck_assert(memcmp(peer_name, state->mod_name2, peer_name_len) == 0); |
242 | 3 | } else { |
243 | 0 | ck_assert(false); |
244 | 0 | } |
245 | | |
246 | 15 | ++state->user_event_count; |
247 | 15 | state->user_check = true; |
248 | 15 | } |
249 | | |
250 | | static void group_mod_event_handler(Tox *tox, const Tox_Event_Group_Moderation *event, void *user_data) |
251 | 43 | { |
252 | 43 | AutoTox *autotox = (AutoTox *)user_data; |
253 | 43 | ck_assert(autotox != nullptr); |
254 | | |
255 | 43 | State *state = (State *)autotox->state; |
256 | | |
257 | 43 | const uint32_t group_number = tox_event_group_moderation_get_group_number(event); |
258 | 43 | const uint32_t target_peer_id = tox_event_group_moderation_get_target_peer_id(event); |
259 | 43 | const Tox_Group_Mod_Event mod_type = tox_event_group_moderation_get_mod_type(event); |
260 | | |
261 | 43 | ck_assert(state->group_number == group_number); |
262 | | |
263 | 43 | char peer_name[TOX_MAX_NAME_LENGTH + 1]; |
264 | | |
265 | 43 | Tox_Err_Group_Peer_Query q_err; |
266 | 43 | size_t peer_name_len = tox_group_peer_get_name_size(tox, group_number, target_peer_id, &q_err); |
267 | | |
268 | 43 | if (q_err == TOX_ERR_GROUP_PEER_QUERY_PEER_NOT_FOUND) { // may occurr on sync attempts |
269 | 8 | return; |
270 | 8 | } |
271 | | |
272 | 35 | ck_assert_msg(q_err == TOX_ERR_GROUP_PEER_QUERY_OK, "error %d", q_err); |
273 | 35 | ck_assert(peer_name_len <= TOX_MAX_NAME_LENGTH); |
274 | | |
275 | 35 | tox_group_peer_get_name(tox, group_number, target_peer_id, (uint8_t *) peer_name, &q_err); |
276 | 35 | peer_name[peer_name_len] = 0; |
277 | 35 | ck_assert(q_err == TOX_ERR_GROUP_PEER_QUERY_OK); |
278 | | |
279 | 35 | Tox_Group_Role role = tox_group_peer_get_role(tox, group_number, target_peer_id, &q_err); |
280 | 35 | ck_assert(q_err == TOX_ERR_GROUP_PEER_QUERY_OK); |
281 | 35 | ck_assert(role <= TOX_GROUP_ROLE_OBSERVER); |
282 | | |
283 | 35 | fprintf(stderr, "tox%u: got moderator event %d (%s), role = %s\n", |
284 | 35 | autotox->index, mod_type, tox_group_mod_event_to_string(mod_type), |
285 | 35 | tox_group_role_to_string(role)); |
286 | | |
287 | 35 | switch (mod_type) { |
288 | 8 | case TOX_GROUP_MOD_EVENT_MODERATOR: { |
289 | 8 | handle_mod(state, peer_name, peer_name_len); |
290 | 8 | break; |
291 | 0 | } |
292 | | |
293 | 8 | case TOX_GROUP_MOD_EVENT_OBSERVER: { |
294 | 8 | handle_observer(state, peer_name, peer_name_len); |
295 | 8 | break; |
296 | 0 | } |
297 | | |
298 | 15 | case TOX_GROUP_MOD_EVENT_USER: { |
299 | 15 | handle_user(state, peer_name, peer_name_len); |
300 | 15 | break; |
301 | 0 | } |
302 | | |
303 | 4 | case TOX_GROUP_MOD_EVENT_KICK: { |
304 | 4 | ck_assert(memcmp(peer_name, state->mod_name1, peer_name_len) == 0); |
305 | 4 | state->kick_check = true; |
306 | 4 | break; |
307 | 4 | } |
308 | | |
309 | 0 | default: { |
310 | 0 | ck_assert_msg(0, "Got invalid moderator event %d", mod_type); |
311 | 0 | return; |
312 | 0 | } |
313 | 35 | } |
314 | 35 | } |
315 | | |
316 | | /* Checks that `peer_id` sees itself with the role `role`. */ |
317 | | static void check_self_role(AutoTox *autotoxes, uint32_t peer_id, Tox_Group_Role role) |
318 | 4 | { |
319 | 4 | Tox_Err_Group_Self_Query sq_err; |
320 | | |
321 | 24 | for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) { |
322 | 20 | const State *state = (const State *)autotoxes[i].state; |
323 | | |
324 | 20 | uint32_t self_peer_id = tox_group_self_get_peer_id(autotoxes[i].tox, state->group_number, &sq_err); |
325 | 20 | ck_assert(sq_err == TOX_ERR_GROUP_SELF_QUERY_OK); |
326 | | |
327 | 20 | if (self_peer_id == peer_id) { |
328 | 0 | Tox_Group_Role self_role = tox_group_self_get_role(autotoxes[i].tox, state->group_number, &sq_err); |
329 | 0 | ck_assert(sq_err == TOX_ERR_GROUP_SELF_QUERY_OK); |
330 | 0 | ck_assert(self_role == role); |
331 | 0 | return; |
332 | 0 | } |
333 | 20 | } |
334 | 4 | } |
335 | | |
336 | | /* Makes sure that a peer's role respects the voice state */ |
337 | | static void voice_state_message_test(AutoTox *autotox, Tox_Group_Voice_State voice_state) |
338 | 15 | { |
339 | 15 | const State *state = (State *)autotox->state; |
340 | | |
341 | 15 | Tox_Err_Group_Self_Query sq_err; |
342 | 15 | Tox_Group_Role self_role = tox_group_self_get_role(autotox->tox, state->group_number, &sq_err); |
343 | 15 | ck_assert(sq_err == TOX_ERR_GROUP_SELF_QUERY_OK); |
344 | | |
345 | 15 | Tox_Err_Group_Send_Message msg_err; |
346 | 15 | tox_group_send_message(autotox->tox, state->group_number, TOX_MESSAGE_TYPE_NORMAL, |
347 | 15 | (const uint8_t *)"test", 4, &msg_err); |
348 | | |
349 | 15 | switch (self_role) { |
350 | 6 | case TOX_GROUP_ROLE_OBSERVER: { |
351 | 6 | ck_assert(msg_err == TOX_ERR_GROUP_SEND_MESSAGE_PERMISSIONS); |
352 | 6 | break; |
353 | 6 | } |
354 | | |
355 | 6 | case TOX_GROUP_ROLE_USER: { |
356 | 3 | if (voice_state != TOX_GROUP_VOICE_STATE_ALL) { |
357 | 2 | ck_assert_msg(msg_err == TOX_ERR_GROUP_SEND_MESSAGE_PERMISSIONS, |
358 | 2 | "%d", msg_err); |
359 | 2 | } else { |
360 | 1 | ck_assert(msg_err == TOX_ERR_GROUP_SEND_MESSAGE_OK); |
361 | 1 | } |
362 | | |
363 | 3 | break; |
364 | 3 | } |
365 | | |
366 | 3 | case TOX_GROUP_ROLE_MODERATOR: { |
367 | 3 | if (voice_state != TOX_GROUP_VOICE_STATE_FOUNDER) { |
368 | 2 | ck_assert(msg_err == TOX_ERR_GROUP_SEND_MESSAGE_OK); |
369 | 2 | } else { |
370 | 1 | ck_assert(msg_err == TOX_ERR_GROUP_SEND_MESSAGE_PERMISSIONS); |
371 | 1 | } |
372 | | |
373 | 3 | break; |
374 | 3 | } |
375 | | |
376 | 3 | case TOX_GROUP_ROLE_FOUNDER: { |
377 | 3 | ck_assert(msg_err == TOX_ERR_GROUP_SEND_MESSAGE_OK); |
378 | 3 | break; |
379 | 3 | } |
380 | 15 | } |
381 | 15 | } |
382 | | |
383 | | static bool all_peers_got_voice_state_change(AutoTox *autotoxes, uint32_t num_toxes, |
384 | | Tox_Group_Voice_State expected_voice_state) |
385 | 15 | { |
386 | 15 | Tox_Err_Group_State_Queries query_err; |
387 | | |
388 | 90 | for (uint32_t i = 0; i < num_toxes; ++i) { |
389 | 75 | const State *state = (State *)autotoxes[i].state; |
390 | | |
391 | 75 | Tox_Group_Voice_State voice_state = tox_group_get_voice_state(autotoxes[i].tox, state->group_number, &query_err); |
392 | 75 | ck_assert(query_err == TOX_ERR_GROUP_STATE_QUERIES_OK); |
393 | | |
394 | 75 | if (voice_state != expected_voice_state) { |
395 | 0 | return false; |
396 | 0 | } |
397 | 75 | } |
398 | | |
399 | 15 | return true; |
400 | 15 | } |
401 | | |
402 | | static void check_voice_state(AutoTox *autotoxes, uint32_t num_toxes) |
403 | 1 | { |
404 | | // founder sets voice state to Moderator |
405 | 1 | const State *state = (State *)autotoxes[0].state; |
406 | 1 | Tox_Err_Group_Founder_Set_Voice_State voice_set_err; |
407 | 1 | tox_group_founder_set_voice_state(autotoxes[0].tox, state->group_number, TOX_GROUP_VOICE_STATE_MODERATOR, |
408 | 1 | &voice_set_err); |
409 | 1 | ck_assert(voice_set_err == TOX_ERR_GROUP_FOUNDER_SET_VOICE_STATE_OK); |
410 | | |
411 | 6 | for (uint32_t i = 0; i < num_toxes; ++i) { |
412 | 5 | do { |
413 | 5 | iterate_all_wait(autotoxes, num_toxes, ITERATION_INTERVAL); |
414 | 5 | } while (!all_peers_got_voice_state_change(autotoxes, num_toxes, TOX_GROUP_VOICE_STATE_MODERATOR)); |
415 | | |
416 | 5 | voice_state_message_test(&autotoxes[i], TOX_GROUP_VOICE_STATE_MODERATOR); |
417 | 5 | } |
418 | | |
419 | 1 | tox_group_founder_set_voice_state(autotoxes[0].tox, state->group_number, TOX_GROUP_VOICE_STATE_FOUNDER, &voice_set_err); |
420 | 1 | ck_assert(voice_set_err == TOX_ERR_GROUP_FOUNDER_SET_VOICE_STATE_OK); |
421 | | |
422 | 6 | for (uint32_t i = 0; i < num_toxes; ++i) { |
423 | 5 | do { |
424 | 5 | iterate_all_wait(autotoxes, num_toxes, ITERATION_INTERVAL); |
425 | 5 | } while (!all_peers_got_voice_state_change(autotoxes, num_toxes, TOX_GROUP_VOICE_STATE_FOUNDER)); |
426 | | |
427 | 5 | voice_state_message_test(&autotoxes[i], TOX_GROUP_VOICE_STATE_FOUNDER); |
428 | 5 | } |
429 | | |
430 | 1 | tox_group_founder_set_voice_state(autotoxes[0].tox, state->group_number, TOX_GROUP_VOICE_STATE_ALL, &voice_set_err); |
431 | 1 | ck_assert(voice_set_err == TOX_ERR_GROUP_FOUNDER_SET_VOICE_STATE_OK); |
432 | | |
433 | 6 | for (uint32_t i = 0; i < num_toxes; ++i) { |
434 | 5 | do { |
435 | 5 | iterate_all_wait(autotoxes, num_toxes, ITERATION_INTERVAL); |
436 | 5 | } while (!all_peers_got_voice_state_change(autotoxes, num_toxes, TOX_GROUP_VOICE_STATE_ALL)); |
437 | | |
438 | 5 | voice_state_message_test(&autotoxes[i], TOX_GROUP_VOICE_STATE_ALL); |
439 | 5 | } |
440 | 1 | } |
441 | | |
442 | | static void group_moderation_test(AutoTox *autotoxes) |
443 | 1 | { |
444 | 1 | ck_assert_msg(NUM_GROUP_TOXES >= 4, "NUM_GROUP_TOXES is too small: %d", NUM_GROUP_TOXES); |
445 | 1 | ck_assert_msg(NUM_GROUP_TOXES < 10, "NUM_GROUP_TOXES is too big: %d", NUM_GROUP_TOXES); |
446 | | |
447 | 1 | uint16_t name_length = 6; |
448 | | |
449 | 6 | for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) { |
450 | 5 | State *state = (State *)autotoxes[i].state; |
451 | 5 | state->self_name_length = name_length; |
452 | 5 | snprintf(state->self_name, sizeof(state->self_name), "peer_%zu", i); |
453 | 5 | state->self_name[name_length] = 0; |
454 | | |
455 | 5 | tox_events_callback_group_join_fail(autotoxes[i].dispatch, group_join_fail_handler); |
456 | 5 | tox_events_callback_group_peer_join(autotoxes[i].dispatch, group_peer_join_handler); |
457 | 5 | tox_events_callback_group_moderation(autotoxes[i].dispatch, group_mod_event_handler); |
458 | 5 | } |
459 | | |
460 | 1 | iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL); |
461 | | |
462 | 1 | fprintf(stderr, "Creating new group\n"); |
463 | | |
464 | | /* Founder makes new group */ |
465 | 1 | State *state0 = (State *)autotoxes[0].state; |
466 | 1 | Tox *tox0 = autotoxes[0].tox; |
467 | | |
468 | 1 | Tox_Err_Group_New err_new; |
469 | 1 | state0->group_number = tox_group_new(tox0, TOX_GROUP_PRIVACY_STATE_PUBLIC, (const uint8_t *)GROUP_NAME, |
470 | 1 | GROUP_NAME_LEN, (const uint8_t *)state0->self_name, state0->self_name_length, |
471 | 1 | &err_new); |
472 | | |
473 | 1 | ck_assert_msg(err_new == TOX_ERR_GROUP_NEW_OK, "Failed to create group. error: %d\n", err_new); |
474 | | |
475 | | /* Founder gets chat ID */ |
476 | 1 | Tox_Err_Group_State_Queries id_err; |
477 | 1 | uint8_t chat_id[TOX_GROUP_CHAT_ID_SIZE]; |
478 | 1 | tox_group_get_chat_id(tox0, state0->group_number, chat_id, &id_err); |
479 | | |
480 | 1 | ck_assert_msg(id_err == TOX_ERR_GROUP_STATE_QUERIES_OK, "Failed to get chat ID. error: %d", id_err); |
481 | | |
482 | 1 | fprintf(stderr, "Peers attemping to join DHT group via the chat ID\n"); |
483 | | |
484 | 5 | for (size_t i = 1; i < NUM_GROUP_TOXES; ++i) { |
485 | 4 | iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL); |
486 | | |
487 | 4 | State *state = (State *)autotoxes[i].state; |
488 | 4 | Tox_Err_Group_Join join_err; |
489 | 4 | state->group_number = tox_group_join(autotoxes[i].tox, chat_id, (const uint8_t *)state->self_name, |
490 | 4 | state->self_name_length, |
491 | 4 | nullptr, 0, &join_err); |
492 | 4 | ck_assert_msg(join_err == TOX_ERR_GROUP_JOIN_OK, "Peer %s (%zu) failed to join group. error %d", |
493 | 4 | state->self_name, i, join_err); |
494 | | |
495 | 4 | c_sleep(100); |
496 | 4 | } |
497 | | |
498 | | // make sure every peer sees every other peer before we continue |
499 | 159 | do { |
500 | 159 | iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL); |
501 | 159 | } while (!all_peers_connected(autotoxes)); |
502 | | |
503 | | /* manually tell the other peers the names of the peers that will be assigned new roles */ |
504 | 6 | for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) { |
505 | 5 | State *state = (State *)autotoxes[i].state; |
506 | 5 | memcpy(state->mod_name1, state0->peers[0].name, sizeof(state->mod_name1)); |
507 | 5 | memcpy(state->mod_name2, state0->peers[2].name, sizeof(state->mod_name2)); |
508 | 5 | memcpy(state->observer_name1, state0->peers[1].name, sizeof(state->observer_name1)); |
509 | 5 | memcpy(state->observer_name2, state0->peers[2].name, sizeof(state->observer_name2)); |
510 | 5 | } |
511 | | |
512 | | /* founder checks his own role */ |
513 | 1 | Tox_Err_Group_Self_Query sq_err; |
514 | 1 | Tox_Group_Role self_role = tox_group_self_get_role(tox0, state0->group_number, &sq_err); |
515 | 1 | ck_assert(sq_err == TOX_ERR_GROUP_SELF_QUERY_OK); |
516 | 1 | ck_assert(self_role == TOX_GROUP_ROLE_FOUNDER); |
517 | | |
518 | | /* all peers should be user role except founder */ |
519 | 5 | for (size_t i = 1; i < NUM_GROUP_TOXES; ++i) { |
520 | 4 | const State *state = (const State *)autotoxes[i].state; |
521 | 4 | self_role = tox_group_self_get_role(autotoxes[i].tox, state->group_number, &sq_err); |
522 | 4 | ck_assert(sq_err == TOX_ERR_GROUP_SELF_QUERY_OK); |
523 | 4 | ck_assert(self_role == TOX_GROUP_ROLE_USER); |
524 | 4 | } |
525 | | |
526 | | /* founder sets first peer to moderator */ |
527 | 1 | fprintf(stderr, "Founder setting %s to moderator\n", state0->peers[0].name); |
528 | | |
529 | 1 | Tox_Err_Group_Mod_Set_Role role_err; |
530 | 1 | tox_group_mod_set_role(tox0, state0->group_number, state0->peers[0].peer_id, TOX_GROUP_ROLE_MODERATOR, &role_err); |
531 | 1 | ck_assert_msg(role_err == TOX_ERR_GROUP_MOD_SET_ROLE_OK, "Failed to set moderator. error: %d", role_err); |
532 | | |
533 | | // manually flag the role setter because they don't get a callback |
534 | 1 | state0->mod_check = true; |
535 | 1 | ++state0->mod_event_count; |
536 | 1 | check_mod_event(autotoxes, NUM_GROUP_TOXES, TOX_GROUP_MOD_EVENT_MODERATOR); |
537 | | |
538 | 1 | check_self_role(autotoxes, state0->peers[0].peer_id, TOX_GROUP_ROLE_MODERATOR); |
539 | | |
540 | 1 | fprintf(stderr, "All peers successfully received mod event\n"); |
541 | | |
542 | | /* founder sets second and third peer to observer */ |
543 | 1 | fprintf(stderr, "Founder setting %s to observer\n", state0->peers[1].name); |
544 | | |
545 | 1 | tox_group_mod_set_role(tox0, state0->group_number, state0->peers[1].peer_id, TOX_GROUP_ROLE_OBSERVER, &role_err); |
546 | 1 | ck_assert_msg(role_err == TOX_ERR_GROUP_MOD_SET_ROLE_OK, "Failed to set observer. error: %d", role_err); |
547 | | |
548 | 1 | state0->observer_check = true; |
549 | 1 | ++state0->observer_event_count; |
550 | 1 | check_mod_event(autotoxes, NUM_GROUP_TOXES, TOX_GROUP_MOD_EVENT_OBSERVER); |
551 | | |
552 | 1 | fprintf(stderr, "All peers successfully received observer event 1\n"); |
553 | | |
554 | 1 | fprintf(stderr, "Founder setting %s to observer\n", state0->peers[2].name); |
555 | | |
556 | 1 | tox_group_mod_set_role(tox0, state0->group_number, state0->peers[2].peer_id, TOX_GROUP_ROLE_OBSERVER, &role_err); |
557 | 1 | ck_assert_msg(role_err == TOX_ERR_GROUP_MOD_SET_ROLE_OK, "Failed to set observer. error: %d", role_err); |
558 | | |
559 | 1 | state0->observer_check = true; |
560 | 1 | ++state0->observer_event_count; |
561 | 1 | check_mod_event(autotoxes, NUM_GROUP_TOXES, TOX_GROUP_MOD_EVENT_OBSERVER); |
562 | | |
563 | 1 | check_self_role(autotoxes, state0->peers[1].peer_id, TOX_GROUP_ROLE_OBSERVER); |
564 | | |
565 | 1 | fprintf(stderr, "All peers successfully received observer event 2\n"); |
566 | | |
567 | | /* do voice state test here since we have at least one peer of each role */ |
568 | 1 | check_voice_state(autotoxes, NUM_GROUP_TOXES); |
569 | | |
570 | 1 | fprintf(stderr, "Voice state respected by all peers\n"); |
571 | | |
572 | | /* New moderator promotes second peer back to user */ |
573 | 1 | const uint32_t idx = get_state_index_by_nick(autotoxes, NUM_GROUP_TOXES, state0->peers[0].name, |
574 | 1 | state0->peers[0].name_length); |
575 | 1 | State *state1 = (State *)autotoxes[idx].state; |
576 | 1 | Tox *tox1 = autotoxes[idx].tox; |
577 | | |
578 | 1 | const uint32_t obs_peer_id = get_peer_id_by_nick(state1->peers, NUM_GROUP_TOXES - 1, state1->observer_name1); |
579 | | |
580 | 1 | fprintf(stderr, "%s is promoting %s back to user\n", state1->self_name, state0->peers[1].name); |
581 | | |
582 | 1 | tox_group_mod_set_role(tox1, state1->group_number, obs_peer_id, TOX_GROUP_ROLE_USER, &role_err); |
583 | 1 | ck_assert_msg(role_err == TOX_ERR_GROUP_MOD_SET_ROLE_OK, "Failed to promote observer back to user. error: %d", |
584 | 1 | role_err); |
585 | | |
586 | 1 | state1->user_check = true; |
587 | 1 | ++state1->user_event_count; |
588 | 1 | check_mod_event(autotoxes, NUM_GROUP_TOXES, TOX_GROUP_MOD_EVENT_USER); |
589 | | |
590 | 1 | fprintf(stderr, "All peers successfully received user event\n"); |
591 | | |
592 | | /* founder assigns third peer to moderator (this triggers two events: user and moderator) */ |
593 | 1 | fprintf(stderr, "Founder setting %s to moderator\n", state0->peers[2].name); |
594 | | |
595 | 1 | tox_group_mod_set_role(tox0, state0->group_number, state0->peers[2].peer_id, TOX_GROUP_ROLE_MODERATOR, &role_err); |
596 | 1 | ck_assert_msg(role_err == TOX_ERR_GROUP_MOD_SET_ROLE_OK, "Failed to set moderator. error: %d", role_err); |
597 | | |
598 | 1 | state0->mod_check = true; |
599 | 1 | ++state0->mod_event_count; |
600 | 1 | check_mod_event(autotoxes, NUM_GROUP_TOXES, TOX_GROUP_MOD_EVENT_MODERATOR); |
601 | | |
602 | 1 | check_self_role(autotoxes, state0->peers[2].peer_id, TOX_GROUP_ROLE_MODERATOR); |
603 | | |
604 | 1 | fprintf(stderr, "All peers successfully received moderator event\n"); |
605 | | |
606 | | /* moderator attempts to demote and kick founder */ |
607 | 1 | uint32_t founder_peer_id = get_peer_id_by_nick(state1->peers, NUM_GROUP_TOXES - 1, state0->self_name); |
608 | 1 | tox_group_mod_set_role(tox1, state1->group_number, founder_peer_id, TOX_GROUP_ROLE_OBSERVER, &role_err); |
609 | 1 | ck_assert_msg(role_err != TOX_ERR_GROUP_MOD_SET_ROLE_OK, "Mod set founder to observer"); |
610 | | |
611 | 1 | Tox_Err_Group_Mod_Kick_Peer k_err; |
612 | 1 | tox_group_mod_kick_peer(tox1, state1->group_number, founder_peer_id, &k_err); |
613 | 1 | ck_assert_msg(k_err != TOX_ERR_GROUP_MOD_KICK_PEER_OK, "Mod kicked founder"); |
614 | | |
615 | | /* the moderator about to be kicked changes the topic to trigger the founder to |
616 | | * re-sign and redistribute it after the kick. |
617 | | */ |
618 | 1 | const State *state_x = (const State *)autotoxes[idx].state; |
619 | 1 | Tox *tox_x = autotoxes[idx].tox; |
620 | 1 | Tox_Err_Group_Topic_Set topic_err; |
621 | 1 | tox_group_set_topic(tox_x, state_x->group_number, nullptr, 0, &topic_err); |
622 | 1 | ck_assert(topic_err == TOX_ERR_GROUP_TOPIC_SET_OK); |
623 | | |
624 | | /* founder kicks moderator (this triggers two events: user and kick) */ |
625 | 1 | fprintf(stderr, "Founder is kicking %s\n", state0->peers[0].name); |
626 | | |
627 | 1 | tox_group_mod_kick_peer(tox0, state0->group_number, state0->peers[0].peer_id, &k_err); |
628 | 1 | ck_assert_msg(k_err == TOX_ERR_GROUP_MOD_KICK_PEER_OK, "Failed to kick peer. error: %d", k_err); |
629 | | |
630 | 1 | state0->kick_check = true; |
631 | 1 | check_mod_event(autotoxes, NUM_GROUP_TOXES, TOX_GROUP_MOD_EVENT_KICK); |
632 | | |
633 | 1 | fprintf(stderr, "All peers successfully received kick event\n"); |
634 | | |
635 | 1 | fprintf(stderr, "Founder is demoting moderator to user\n"); |
636 | 1 | tox_group_mod_set_role(tox0, state0->group_number, state0->peers[2].peer_id, TOX_GROUP_ROLE_USER, &role_err); |
637 | 1 | ck_assert_msg(role_err == TOX_ERR_GROUP_MOD_SET_ROLE_OK, "Failed to demote peer 3 to User. error: %d", role_err); |
638 | | |
639 | 1 | state0->user_check = true; |
640 | 1 | ++state0->user_event_count; |
641 | | |
642 | 1 | check_mod_event(autotoxes, NUM_GROUP_TOXES, TOX_GROUP_MOD_EVENT_USER); |
643 | 1 | check_self_role(autotoxes, state0->peers[2].peer_id, TOX_GROUP_ROLE_USER); |
644 | | |
645 | 6 | for (size_t i = 0; i < NUM_GROUP_TOXES; i++) { |
646 | 5 | const State *state = (const State *)autotoxes[i].state; |
647 | 5 | Tox_Err_Group_Leave err_exit; |
648 | 5 | tox_group_leave(autotoxes[i].tox, state->group_number, nullptr, 0, &err_exit); |
649 | 5 | ck_assert(err_exit == TOX_ERR_GROUP_LEAVE_OK); |
650 | 5 | } |
651 | | |
652 | 1 | fprintf(stderr, "All tests passed!\n"); |
653 | 1 | } |
654 | | |
655 | | int main(void) |
656 | 721 | { |
657 | 721 | setvbuf(stdout, nullptr, _IONBF, 0); |
658 | | |
659 | 721 | Run_Auto_Options options = default_run_auto_options(); |
660 | 721 | options.graph = GRAPH_COMPLETE; |
661 | | |
662 | 721 | run_auto_test(nullptr, NUM_GROUP_TOXES, group_moderation_test, sizeof(State), &options); |
663 | 721 | return 0; |
664 | 721 | } |
665 | | |
666 | | #undef NUM_GROUP_TOXES |
667 | | #undef GROUP_NAME |
668 | | #undef GROUP_NAME_LEN |