Coverage Report

Created: 2024-01-26 01:52

/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