Coverage Report

Created: 2025-10-08 19:34

/work/auto_tests/group_state_test.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Tests that we can successfully change the group state and that all peers in the group
3
 * receive the correct state changes.
4
 */
5
6
#include <string.h>
7
#include <stdio.h>
8
#include <stdlib.h>
9
#include <stdint.h>
10
#include <time.h>
11
12
#include "auto_test_support.h"
13
#include "check_compat.h"
14
15
2.92k
#define NUM_GROUP_TOXES 5
16
17
488
#define PEER_LIMIT_1 NUM_GROUP_TOXES
18
6
#define PEER_LIMIT_2 50
19
20
10
#define PASSWORD "dadada"
21
5
#define PASS_LEN (sizeof(PASSWORD) - 1)
22
23
499
#define GROUP_NAME "The Crystal Palace"
24
493
#define GROUP_NAME_LEN (sizeof(GROUP_NAME) - 1)
25
26
2
#define PEER0_NICK "David"
27
1
#define PEER0_NICK_LEN (sizeof(PEER0_NICK) - 1)
28
29
typedef struct State {
30
    size_t  num_peers;
31
} State;
32
33
static bool all_group_peers_connected(const AutoTox *autotoxes, uint32_t tox_count, uint32_t groupnumber,
34
                                      size_t name_length, uint32_t peer_limit)
35
487
{
36
890
    for (uint32_t i = 0; i < tox_count; ++i) {
37
        // make sure we got an invite response
38
889
        if (tox_group_get_name_size(autotoxes[i].tox, groupnumber, nullptr) != name_length) {
39
0
            return false;
40
0
        }
41
42
        // make sure we got a sync response
43
889
        if (tox_group_get_peer_limit(autotoxes[i].tox, groupnumber, nullptr) != peer_limit) {
44
0
            return false;
45
0
        }
46
47
        // make sure we're actually connected
48
889
        if (!tox_group_is_connected(autotoxes[i].tox, groupnumber, nullptr)) {
49
0
            return false;
50
0
        }
51
52
889
        const State *state = (const State *)autotoxes[i].state;
53
54
        // make sure all peers are connected to one another
55
889
        if (state->num_peers < NUM_GROUP_TOXES - 1) {
56
486
            return false;
57
486
        }
58
889
    }
59
60
1
    return true;
61
487
}
62
63
static void group_topic_lock_handler(const Tox_Event_Group_Topic_Lock *event,
64
                                     void *user_data)
65
8
{
66
8
    const AutoTox *autotox = (const AutoTox *)user_data;
67
8
    ck_assert(autotox != nullptr);
68
69
8
    const uint32_t groupnumber = tox_event_group_topic_lock_get_group_number(event);
70
8
    const Tox_Group_Topic_Lock topic_lock = tox_event_group_topic_lock_get_topic_lock(event);
71
72
8
    Tox_Err_Group_State_Query err;
73
8
    Tox_Group_Topic_Lock current_topic_lock = tox_group_get_topic_lock(autotox->tox, groupnumber, &err);
74
75
8
    ck_assert(err == TOX_ERR_GROUP_STATE_QUERY_OK);
76
8
    ck_assert_msg(current_topic_lock == topic_lock, "topic locks don't match in callback: %u %u",
77
8
                  topic_lock, current_topic_lock);
78
8
}
79
80
static void group_voice_state_handler(const Tox_Event_Group_Voice_State *event,
81
                                      void *user_data)
82
4
{
83
4
    const AutoTox *autotox = (const AutoTox *)user_data;
84
4
    ck_assert(autotox != nullptr);
85
86
4
    const uint32_t groupnumber = tox_event_group_voice_state_get_group_number(event);
87
4
    const Tox_Group_Voice_State voice_state = tox_event_group_voice_state_get_voice_state(event);
88
89
4
    Tox_Err_Group_State_Query err;
90
4
    Tox_Group_Voice_State current_voice_state = tox_group_get_voice_state(autotox->tox, groupnumber, &err);
91
92
4
    ck_assert(err == TOX_ERR_GROUP_STATE_QUERY_OK);
93
4
    ck_assert_msg(current_voice_state == voice_state, "voice states don't match in callback: %u %u",
94
4
                  voice_state, current_voice_state);
95
4
}
96
97
static void group_privacy_state_handler(const Tox_Event_Group_Privacy_State *event,
98
                                        void *user_data)
99
4
{
100
4
    const AutoTox *autotox = (const AutoTox *)user_data;
101
4
    ck_assert(autotox != nullptr);
102
103
4
    const uint32_t groupnumber = tox_event_group_privacy_state_get_group_number(event);
104
4
    const Tox_Group_Privacy_State privacy_state = tox_event_group_privacy_state_get_privacy_state(event);
105
106
4
    Tox_Err_Group_State_Query err;
107
4
    Tox_Group_Privacy_State current_pstate = tox_group_get_privacy_state(autotox->tox, groupnumber, &err);
108
109
4
    ck_assert(err == TOX_ERR_GROUP_STATE_QUERY_OK);
110
4
    ck_assert_msg(current_pstate == privacy_state, "privacy states don't match in callback");
111
4
}
112
113
static void group_peer_limit_handler(const Tox_Event_Group_Peer_Limit *event, void *user_data)
114
8
{
115
8
    const AutoTox *autotox = (const AutoTox *)user_data;
116
8
    ck_assert(autotox != nullptr);
117
118
8
    const uint32_t groupnumber = tox_event_group_peer_limit_get_group_number(event);
119
8
    const uint32_t peer_limit = tox_event_group_peer_limit_get_peer_limit(event);
120
121
8
    Tox_Err_Group_State_Query err;
122
8
    uint32_t current_plimit = tox_group_get_peer_limit(autotox->tox, groupnumber, &err);
123
124
8
    ck_assert(err == TOX_ERR_GROUP_STATE_QUERY_OK);
125
8
    ck_assert_msg(peer_limit == current_plimit,
126
8
                  "Peer limits don't match in callback: %u, %u\n", peer_limit, current_plimit);
127
8
}
128
129
static void group_password_handler(const Tox_Event_Group_Password *event,
130
                                   void *user_data)
131
4
{
132
4
    AutoTox *autotox = (AutoTox *)user_data;
133
4
    ck_assert(autotox != nullptr);
134
135
4
    const uint32_t groupnumber = tox_event_group_password_get_group_number(event);
136
4
    const uint8_t *password = tox_event_group_password_get_password(event);
137
4
    const size_t length = tox_event_group_password_get_password_length(event);
138
139
4
    Tox_Err_Group_State_Query err;
140
4
    size_t curr_pwlength = tox_group_get_password_size(autotox->tox, groupnumber, &err);
141
142
4
    ck_assert(err == TOX_ERR_GROUP_STATE_QUERY_OK);
143
4
    ck_assert(length == curr_pwlength);
144
145
4
    uint8_t current_password[TOX_GROUP_MAX_PASSWORD_SIZE];
146
4
    tox_group_get_password(autotox->tox, groupnumber, current_password, &err);
147
148
4
    ck_assert(err == TOX_ERR_GROUP_STATE_QUERY_OK);
149
4
    ck_assert_msg(memcmp(current_password, password, length) == 0,
150
4
                  "Passwords don't match: %s, %s", password, current_password);
151
4
}
152
153
static void group_peer_join_handler(const Tox_Event_Group_Peer_Join *event, void *user_data)
154
40
{
155
40
    AutoTox *autotox = (AutoTox *)user_data;
156
40
    ck_assert(autotox != nullptr);
157
158
40
    State *state = (State *)autotox->state;
159
160
40
    ++state->num_peers;
161
40
    ck_assert(state->num_peers < NUM_GROUP_TOXES);
162
40
}
163
164
/* Returns 0 if group state is equal to the state passed to this function.
165
 * Returns negative integer if state is invalid.
166
 */
167
static int check_group_state(const Tox *tox, uint32_t groupnumber, uint32_t peer_limit,
168
                             Tox_Group_Privacy_State priv_state, Tox_Group_Voice_State voice_state,
169
                             const uint8_t *password, size_t pass_len, Tox_Group_Topic_Lock topic_lock)
170
5
{
171
5
    Tox_Err_Group_State_Query query_err;
172
173
5
    Tox_Group_Privacy_State my_priv_state = tox_group_get_privacy_state(tox, groupnumber, &query_err);
174
5
    ck_assert_msg(query_err == TOX_ERR_GROUP_STATE_QUERY_OK, "Failed to get privacy state: %u", query_err);
175
176
5
    if (my_priv_state != priv_state) {
177
0
        return -1;
178
0
    }
179
180
5
    uint32_t my_peer_limit = tox_group_get_peer_limit(tox, groupnumber, &query_err);
181
5
    ck_assert_msg(query_err == TOX_ERR_GROUP_STATE_QUERY_OK, "Failed to get peer limit: %u", query_err);
182
183
5
    if (my_peer_limit != peer_limit) {
184
0
        return -2;
185
0
    }
186
187
5
    size_t my_pass_len = tox_group_get_password_size(tox, groupnumber, &query_err);
188
5
    ck_assert_msg(query_err == TOX_ERR_GROUP_STATE_QUERY_OK, "Failed to get password size: %u", query_err);
189
190
5
    if (my_pass_len != pass_len) {
191
0
        return -5;
192
0
    }
193
194
5
    if (password != nullptr && my_pass_len > 0) {
195
0
        ck_assert(my_pass_len <= TOX_GROUP_MAX_PASSWORD_SIZE);
196
197
0
        uint8_t my_pass[TOX_GROUP_MAX_PASSWORD_SIZE + 1];
198
0
        tox_group_get_password(tox, groupnumber, my_pass, &query_err);
199
0
        my_pass[my_pass_len] = 0;
200
0
        ck_assert_msg(query_err == TOX_ERR_GROUP_STATE_QUERY_OK, "Failed to get password: %u", query_err);
201
202
0
        if (memcmp(my_pass, password, my_pass_len) != 0) {
203
0
            return -6;
204
0
        }
205
0
    }
206
207
    /* Group name should never change */
208
5
    size_t my_gname_len = tox_group_get_name_size(tox, groupnumber, &query_err);
209
5
    ck_assert_msg(query_err == TOX_ERR_GROUP_STATE_QUERY_OK, "Failed to get group name size: %u", query_err);
210
211
5
    if (my_gname_len != GROUP_NAME_LEN) {
212
0
        return -7;
213
0
    }
214
215
5
    ck_assert(my_gname_len <= TOX_GROUP_MAX_GROUP_NAME_LENGTH);
216
217
5
    uint8_t my_gname[TOX_GROUP_MAX_GROUP_NAME_LENGTH + 1];
218
5
    tox_group_get_name(tox, groupnumber, my_gname, &query_err);
219
5
    my_gname[my_gname_len] = 0;
220
221
5
    if (memcmp(my_gname, (const uint8_t *)GROUP_NAME, my_gname_len) != 0) {
222
0
        return -8;
223
0
    }
224
225
5
    Tox_Group_Topic_Lock current_topic_lock = tox_group_get_topic_lock(tox, groupnumber, &query_err);
226
5
    ck_assert_msg(query_err == TOX_ERR_GROUP_STATE_QUERY_OK, "Failed to get topic lock: %u", query_err);
227
228
5
    if (current_topic_lock != topic_lock) {
229
0
        return -9;
230
0
    }
231
232
5
    Tox_Group_Voice_State current_voice_state = tox_group_get_voice_state(tox, groupnumber, &query_err);
233
5
    ck_assert_msg(query_err == TOX_ERR_GROUP_STATE_QUERY_OK, "Failed to get voice state: %u", query_err);
234
235
5
    if (current_voice_state != voice_state) {
236
0
        return -10;
237
0
    }
238
239
5
    return 0;
240
5
}
241
242
static void set_group_state(Tox *tox, uint32_t groupnumber, uint32_t peer_limit, Tox_Group_Privacy_State priv_state,
243
                            Tox_Group_Voice_State voice_state, const uint8_t *password, size_t pass_len,
244
                            Tox_Group_Topic_Lock topic_lock)
245
2
{
246
247
2
    Tox_Err_Group_Set_Peer_Limit limit_set_err;
248
2
    tox_group_set_peer_limit(tox, groupnumber, peer_limit, &limit_set_err);
249
2
    ck_assert_msg(limit_set_err == TOX_ERR_GROUP_SET_PEER_LIMIT_OK, "failed to set peer limit: %u", limit_set_err);
250
251
2
    Tox_Err_Group_Set_Privacy_State priv_err;
252
2
    tox_group_set_privacy_state(tox, groupnumber, priv_state, &priv_err);
253
2
    ck_assert_msg(priv_err == TOX_ERR_GROUP_SET_PRIVACY_STATE_OK, "failed to set privacy state: %u", priv_err);
254
255
2
    Tox_Err_Group_Set_Password pass_set_err;
256
2
    tox_group_set_password(tox, groupnumber, password, pass_len, &pass_set_err);
257
2
    ck_assert_msg(pass_set_err == TOX_ERR_GROUP_SET_PASSWORD_OK, "failed to set password: %u", pass_set_err);
258
259
2
    Tox_Err_Group_Set_Topic_Lock lock_set_err;
260
2
    tox_group_set_topic_lock(tox, groupnumber, topic_lock, &lock_set_err);
261
2
    ck_assert_msg(lock_set_err == TOX_ERR_GROUP_SET_TOPIC_LOCK_OK, "failed to set topic lock: %u",
262
2
                  lock_set_err);
263
264
2
    Tox_Err_Group_Set_Voice_State voice_set_err;
265
2
    tox_group_set_voice_state(tox, groupnumber, voice_state, &voice_set_err);
266
2
    ck_assert_msg(voice_set_err == TOX_ERR_GROUP_SET_VOICE_STATE_OK, "failed to set voice state: %u",
267
2
                  voice_set_err);
268
2
}
269
270
static void group_state_test(AutoTox *autotoxes)
271
1
{
272
1
    ck_assert_msg(NUM_GROUP_TOXES >= 3, "NUM_GROUP_TOXES is too small: %d", NUM_GROUP_TOXES);
273
274
6
    for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) {
275
5
        tox_events_callback_group_privacy_state(autotoxes[i].dispatch, group_privacy_state_handler);
276
5
        tox_events_callback_group_peer_limit(autotoxes[i].dispatch, group_peer_limit_handler);
277
5
        tox_events_callback_group_password(autotoxes[i].dispatch, group_password_handler);
278
5
        tox_events_callback_group_peer_join(autotoxes[i].dispatch, group_peer_join_handler);
279
5
        tox_events_callback_group_voice_state(autotoxes[i].dispatch, group_voice_state_handler);
280
5
        tox_events_callback_group_topic_lock(autotoxes[i].dispatch, group_topic_lock_handler);
281
5
    }
282
283
1
    Tox *tox0 = autotoxes[0].tox;
284
285
    /* Tox 0 creates a group and is the founder of a newly created group */
286
1
    Tox_Err_Group_New new_err;
287
1
    uint32_t groupnum = tox_group_new(tox0, TOX_GROUP_PRIVACY_STATE_PUBLIC, (const uint8_t *)GROUP_NAME, GROUP_NAME_LEN,
288
1
                                      (const uint8_t *)PEER0_NICK, PEER0_NICK_LEN, &new_err);
289
290
1
    ck_assert_msg(new_err == TOX_ERR_GROUP_NEW_OK, "tox_group_new failed: %u", new_err);
291
292
    /* Founder sets default group state before anyone else joins */
293
1
    set_group_state(tox0, groupnum, PEER_LIMIT_1, TOX_GROUP_PRIVACY_STATE_PUBLIC, TOX_GROUP_VOICE_STATE_ALL,
294
1
                    (const uint8_t *)PASSWORD, PASS_LEN, TOX_GROUP_TOPIC_LOCK_ENABLED);
295
296
    /* Founder gets the Chat ID and implicitly shares it publicly */
297
1
    Tox_Err_Group_State_Query id_err;
298
1
    uint8_t chat_id[TOX_GROUP_CHAT_ID_SIZE];
299
1
    tox_group_get_chat_id(tox0, groupnum, chat_id, &id_err);
300
301
1
    ck_assert_msg(id_err == TOX_ERR_GROUP_STATE_QUERY_OK, "tox_group_get_chat_id failed %u", id_err);
302
303
    /* All other peers join the group using the Chat ID and password */
304
5
    for (size_t i = 1; i < NUM_GROUP_TOXES; ++i) {
305
4
        iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
306
307
4
        Tox_Err_Group_Join join_err;
308
4
        tox_group_join(autotoxes[i].tox, chat_id, (const uint8_t *)"Test", 4, (const uint8_t *)PASSWORD, PASS_LEN,
309
4
                       &join_err);
310
4
        ck_assert_msg(join_err == TOX_ERR_GROUP_JOIN_OK, "tox_group_join failed: %u", join_err);
311
4
    }
312
313
1
    fprintf(stderr, "Peers attempting to join group\n");
314
315
    /* Keep checking if all instances have connected to the group until test times out */
316
487
    while (!all_group_peers_connected(autotoxes, NUM_GROUP_TOXES, groupnum, GROUP_NAME_LEN, PEER_LIMIT_1)) {
317
486
        iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
318
486
    }
319
320
    /* Change group state and check that all peers received the changes */
321
1
    set_group_state(tox0, groupnum, PEER_LIMIT_2, TOX_GROUP_PRIVACY_STATE_PRIVATE, TOX_GROUP_VOICE_STATE_MODERATOR,
322
1
                    nullptr, 0, TOX_GROUP_TOPIC_LOCK_DISABLED);
323
324
1
    fprintf(stderr, "Changing state\n");
325
326
1
    while (1) {
327
1
        iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
328
329
1
        uint32_t count = 0;
330
331
6
        for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) {
332
5
            if (check_group_state(autotoxes[i].tox, groupnum, PEER_LIMIT_2, TOX_GROUP_PRIVACY_STATE_PRIVATE,
333
5
                                  TOX_GROUP_VOICE_STATE_MODERATOR, nullptr, 0, TOX_GROUP_TOPIC_LOCK_DISABLED) == 0) {
334
5
                ++count;
335
5
            }
336
5
        }
337
338
1
        if (count == NUM_GROUP_TOXES) {
339
1
            fprintf(stderr, "%u peers successfully received state changes\n", count);
340
1
            break;
341
1
        }
342
1
    }
343
344
6
    for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) {
345
5
        Tox_Err_Group_Leave err_exit;
346
5
        tox_group_leave(autotoxes[i].tox, groupnum, nullptr, 0, &err_exit);
347
5
        ck_assert_msg(err_exit == TOX_ERR_GROUP_LEAVE_OK, "%u", err_exit);
348
5
    }
349
350
1
    fprintf(stderr, "All tests passed!\n");
351
1
}
352
353
int main(void)
354
545
{
355
545
    setvbuf(stdout, nullptr, _IONBF, 0);
356
357
545
    Run_Auto_Options autotest_opts = default_run_auto_options();
358
545
    autotest_opts.graph = GRAPH_COMPLETE;
359
360
545
    run_auto_test(nullptr, NUM_GROUP_TOXES, group_state_test, sizeof(State), &autotest_opts);
361
362
545
    return 0;
363
545
}
364
365
#undef PEER0_NICK
366
#undef PEER0_NICK_LEN
367
#undef GROUP_NAME_LEN
368
#undef GROUP_NAME
369
#undef PASS_LEN
370
#undef PASSWORD
371
#undef PEER_LIMIT_2
372
#undef PEER_LIMIT_1
373
#undef NUM_GROUP_TOXES