Coverage Report

Created: 2025-10-08 19:34

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