Coverage Report

Created: 2024-01-26 01:52

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