Coverage Report

Created: 2024-01-26 01:52

/work/auto_tests/group_topic_test.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Tests that we can successfully change the group topic, that all peers receive topic changes
3
 * and that the topic lock works as intended.
4
 */
5
6
#include <string.h>
7
#include <stdio.h>
8
#include <stdlib.h>
9
#include <stdint.h>
10
11
#include "auto_test_support.h"
12
#include "check_compat.h"
13
14
#include "../toxcore/tox.h"
15
#include "../toxcore/group_chats.h"
16
17
2.00k
#define NUM_GROUP_TOXES 3
18
19
4
#define TOPIC "They're waiting for you Gordon...in the test chamber"
20
2
#define TOPIC_LEN (sizeof(TOPIC) - 1)
21
22
6
#define TOPIC2 "They're waiting for you Gordon...in the test chamber 2.0"
23
3
#define TOPIC_LEN2 (sizeof(TOPIC2) - 1)
24
25
3
#define GROUP_NAME "The Test Chamber"
26
2
#define GROUP_NAME_LEN (sizeof(GROUP_NAME) - 1)
27
28
2
#define PEER0_NICK "Koresh"
29
1
#define PEER0_NICK_LEN  (sizeof(PEER0_NICK) - 1)
30
31
typedef struct State {
32
    uint32_t peer_id;  // the id of the peer we set to observer
33
} State;
34
35
static bool all_group_peers_connected(const AutoTox *autotoxes, uint32_t tox_count, uint32_t groupnumber,
36
                                      size_t name_length, uint32_t peer_limit)
37
1
{
38
2
    for (uint32_t i = 0; i < tox_count; ++i) {
39
        // make sure we got an invite
40
2
        if (tox_group_get_name_size(autotoxes[i].tox, groupnumber, nullptr) != name_length) {
41
1
            return false;
42
1
        }
43
44
        // make sure we got a sync response
45
1
        if (peer_limit != 0 && tox_group_get_peer_limit(autotoxes[i].tox, groupnumber, nullptr) != peer_limit) {
46
0
            return false;
47
0
        }
48
49
        // make sure we're actually connected
50
1
        if (!tox_group_is_connected(autotoxes[i].tox, groupnumber, nullptr)) {
51
0
            return false;
52
0
        }
53
1
    }
54
55
0
    return true;
56
1
}
57
58
static void group_peer_join_handler(Tox *tox, const Tox_Event_Group_Peer_Join *event, void *user_data)
59
151
{
60
    //const uint32_t group_number = tox_event_group_peer_join_get_group_number(event);
61
151
    const uint32_t peer_id = tox_event_group_peer_join_get_peer_id(event);
62
63
151
    AutoTox *autotox = (AutoTox *)user_data;
64
151
    ck_assert(autotox != nullptr);
65
66
151
    State *state = (State *)autotox->state;
67
68
151
    state->peer_id = peer_id;
69
151
}
70
71
static void group_topic_handler(Tox *tox, const Tox_Event_Group_Topic *event, void *user_data)
72
12
{
73
12
    const uint32_t group_number = tox_event_group_topic_get_group_number(event);
74
    //const uint32_t peer_id = tox_event_group_topic_get_peer_id(event);
75
12
    const uint8_t *topic = tox_event_group_topic_get_topic(event);
76
12
    const uint32_t topic_length = tox_event_group_topic_get_topic_length(event);
77
78
12
    ck_assert(topic_length <= TOX_GROUP_MAX_TOPIC_LENGTH);
79
80
12
    Tox_Err_Group_State_Queries query_err;
81
12
    uint8_t topic2[TOX_GROUP_MAX_TOPIC_LENGTH];
82
12
    tox_group_get_topic(tox, group_number, topic2, &query_err);
83
12
    ck_assert(query_err == TOX_ERR_GROUP_STATE_QUERIES_OK);
84
85
12
    size_t topic_length_getter = tox_group_get_topic_size(tox, group_number, &query_err);
86
12
    ck_assert(query_err == TOX_ERR_GROUP_STATE_QUERIES_OK);
87
12
    ck_assert_msg(topic_length_getter == topic_length && memcmp(topic, topic2, topic_length) == 0,
88
12
                  "topic differs in callback: %s, %s", topic, topic2);
89
12
}
90
91
static void group_topic_lock_handler(Tox *tox, const Tox_Event_Group_Topic_Lock *event, void *user_data)
92
8
{
93
8
    const uint32_t group_number = tox_event_group_topic_lock_get_group_number(event);
94
8
    const Tox_Group_Topic_Lock topic_lock = tox_event_group_topic_lock_get_topic_lock(event);
95
96
8
    Tox_Err_Group_State_Queries err;
97
8
    Tox_Group_Topic_Lock current_lock = tox_group_get_topic_lock(tox, group_number, &err);
98
99
8
    ck_assert(err == TOX_ERR_GROUP_STATE_QUERIES_OK);
100
8
    ck_assert_msg(topic_lock == current_lock, "topic locks differ in callback");
101
8
}
102
103
/* Sets group topic.
104
 *
105
 * Return true on success.
106
 */
107
static bool set_topic(Tox *tox, uint32_t groupnumber, const char *topic, size_t length)
108
10
{
109
10
    Tox_Err_Group_Topic_Set err;
110
10
    tox_group_set_topic(tox, groupnumber, (const uint8_t *)topic, length, &err);
111
112
10
    return err == TOX_ERR_GROUP_TOPIC_SET_OK;
113
10
}
114
115
/* Returns 0 if group topic matches expected topic.
116
 * Returns a value < 0 on failure.
117
 */
118
static int check_topic(const Tox *tox, uint32_t groupnumber, const char *expected_topic, size_t expected_length)
119
342
{
120
342
    Tox_Err_Group_State_Queries query_err;
121
342
    size_t topic_length = tox_group_get_topic_size(tox, groupnumber, &query_err);
122
123
342
    if (query_err != TOX_ERR_GROUP_STATE_QUERIES_OK) {
124
0
        return -1;
125
0
    }
126
127
342
    if (expected_length != topic_length) {
128
14
        return -2;
129
14
    }
130
131
328
    uint8_t topic[TOX_GROUP_MAX_TOPIC_LENGTH];
132
328
    tox_group_get_topic(tox, groupnumber, topic, &query_err);
133
134
328
    if (query_err != TOX_ERR_GROUP_STATE_QUERIES_OK) {
135
0
        return -3;
136
0
    }
137
138
328
    if (memcmp(expected_topic, (const char *)topic, topic_length) != 0) {
139
97
        return -4;
140
97
    }
141
142
231
    return 0;
143
328
}
144
145
static void wait_topic_lock(AutoTox *autotoxes, uint32_t groupnumber, Tox_Group_Topic_Lock expected_lock)
146
2
{
147
97
    while (1) {
148
97
        iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
149
150
97
        uint32_t count = 0;
151
152
388
        for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) {
153
291
            Tox_Err_Group_State_Queries err;
154
291
            Tox_Group_Topic_Lock topic_lock = tox_group_get_topic_lock(autotoxes[i].tox, groupnumber, &err);
155
291
            ck_assert(err == TOX_ERR_GROUP_STATE_QUERIES_OK);
156
157
291
            if (topic_lock == expected_lock) {
158
196
                ++count;
159
196
            }
160
291
        }
161
162
97
        if (count == NUM_GROUP_TOXES) {
163
2
            break;
164
2
        }
165
97
    }
166
2
}
167
168
/* Waits for all peers in group to see the same topic */
169
static void wait_state_topic(AutoTox *autotoxes, uint32_t groupnumber, const char *topic, size_t length)
170
8
{
171
114
    while (1) {
172
114
        iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
173
174
114
        uint32_t count = 0;
175
176
456
        for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) {
177
342
            const int c_ret = check_topic(autotoxes[i].tox, groupnumber, topic, length);
178
179
342
            if (c_ret == 0) {
180
231
                ++count;
181
231
            }
182
342
        }
183
184
114
        if (count == NUM_GROUP_TOXES) {
185
8
            break;
186
8
        }
187
114
    }
188
8
}
189
190
/* All peers attempt to set the topic.
191
 *
192
 * Returns the number of peers who succeeeded.
193
 */
194
static uint32_t set_topic_all_peers(const Random *rng, AutoTox *autotoxes, size_t num_peers, uint32_t groupnumber)
195
3
{
196
3
    uint32_t change_count = 0;
197
198
11
    for (size_t i = 0; i < num_peers; ++i) {
199
8
        char new_topic[TOX_GROUP_MAX_TOPIC_LENGTH];
200
8
        snprintf(new_topic, sizeof(new_topic), "peer %zu changes topic %u", i, random_u32(rng));
201
8
        size_t length = strlen(new_topic);
202
203
8
        if (set_topic(autotoxes[i].tox, groupnumber, new_topic, length)) {
204
5
            wait_state_topic(autotoxes, groupnumber, new_topic, length);
205
5
            ++change_count;
206
5
        } else {
207
3
            fprintf(stderr, "Peer %zu couldn't set the topic\n", i);
208
3
        }
209
8
    }
210
211
3
    return change_count;
212
3
}
213
214
static void group_topic_test(AutoTox *autotoxes)
215
1
{
216
1
    ck_assert_msg(NUM_GROUP_TOXES >= 3, "NUM_GROUP_TOXES is too small: %d", NUM_GROUP_TOXES);
217
218
1
    const Random *rng = os_random();
219
1
    ck_assert(rng != nullptr);
220
221
1
    Tox *tox0 = autotoxes[0].tox;
222
1
    Tox_Dispatch *dispatch0 = autotoxes[0].dispatch;
223
1
    const State *state0 = (const State *)autotoxes[0].state;
224
225
1
    tox_events_callback_group_peer_join(dispatch0, group_peer_join_handler);
226
227
4
    for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) {
228
3
        tox_events_callback_group_topic(autotoxes[i].dispatch, group_topic_handler);
229
3
        tox_events_callback_group_topic_lock(autotoxes[i].dispatch, group_topic_lock_handler);
230
3
    }
231
232
    /* Tox1 creates a group and is the founder of a newly created group */
233
1
    Tox_Err_Group_New new_err;
234
1
    const uint32_t groupnumber = tox_group_new(tox0, TOX_GROUP_PRIVACY_STATE_PUBLIC, (const uint8_t *)GROUP_NAME,
235
1
                                 GROUP_NAME_LEN,
236
1
                                 (const uint8_t *)PEER0_NICK, PEER0_NICK_LEN, &new_err);
237
238
1
    ck_assert_msg(new_err == TOX_ERR_GROUP_NEW_OK, "tox_group_new failed: %d", new_err);
239
240
1
    iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
241
242
    /* Founder sets group topic before anyone else joins */
243
1
    const bool s_ret = set_topic(tox0, groupnumber, TOPIC, TOPIC_LEN);
244
1
    ck_assert_msg(s_ret, "Founder failed to set topic");
245
246
    /* Founder gets the Chat ID and implicitly shares it publicly */
247
1
    Tox_Err_Group_State_Queries id_err;
248
1
    uint8_t chat_id[TOX_GROUP_CHAT_ID_SIZE];
249
1
    tox_group_get_chat_id(tox0, groupnumber, chat_id, &id_err);
250
251
1
    ck_assert_msg(id_err == TOX_ERR_GROUP_STATE_QUERIES_OK, "tox_group_get_chat_id failed %d", id_err);
252
253
    /* All other peers join the group using the Chat ID */
254
3
    for (size_t i = 1; i < NUM_GROUP_TOXES; ++i) {
255
2
        iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
256
257
2
        Tox_Err_Group_Join join_err;
258
2
        tox_group_join(autotoxes[i].tox, chat_id, (const uint8_t *)"Test", 4, nullptr, 0, &join_err);
259
2
        ck_assert_msg(join_err == TOX_ERR_GROUP_JOIN_OK, "tox_group_join failed: %d", join_err);
260
261
2
        c_sleep(100);
262
2
    }
263
264
1
    fprintf(stderr, "Peers attempting to join group\n");
265
266
1
    all_group_peers_connected(autotoxes, NUM_GROUP_TOXES, groupnumber, GROUP_NAME_LEN, MAX_GC_PEERS_DEFAULT);
267
268
1
    wait_state_topic(autotoxes, groupnumber, TOPIC, TOPIC_LEN);
269
270
    /* Founder disables topic lock */
271
1
    Tox_Err_Group_Founder_Set_Topic_Lock lock_set_err;
272
1
    tox_group_founder_set_topic_lock(tox0, groupnumber, TOX_GROUP_TOPIC_LOCK_DISABLED, &lock_set_err);
273
1
    ck_assert_msg(lock_set_err == TOX_ERR_GROUP_FOUNDER_SET_TOPIC_LOCK_OK, "failed to disable topic lock: %d",
274
1
                  lock_set_err);
275
276
1
    fprintf(stderr, "Topic lock disabled\n");
277
278
    /* make sure every peer sees the topic lock state change */
279
1
    wait_topic_lock(autotoxes, groupnumber, TOX_GROUP_TOPIC_LOCK_DISABLED);
280
281
    /* All peers should be able to change the topic now */
282
1
    uint32_t change_count = set_topic_all_peers(rng, autotoxes, NUM_GROUP_TOXES, groupnumber);
283
284
1
    ck_assert_msg(change_count == NUM_GROUP_TOXES, "%u peers changed the topic with topic lock disabled", change_count);
285
286
    /* founder silences the last peer he saw join */
287
1
    Tox_Err_Group_Mod_Set_Role merr;
288
1
    tox_group_mod_set_role(tox0, groupnumber, state0->peer_id, TOX_GROUP_ROLE_OBSERVER, &merr);
289
1
    ck_assert_msg(merr == TOX_ERR_GROUP_MOD_SET_ROLE_OK, "Failed to set %u to observer role: %d", state0->peer_id, merr);
290
291
1
    fprintf(stderr, "Random peer is set to observer\n");
292
293
1
    iterate_all_wait(autotoxes, NUM_GROUP_TOXES, ITERATION_INTERVAL);
294
295
    /* All peers except one should now be able to change the topic */
296
1
    change_count = set_topic_all_peers(rng, autotoxes, NUM_GROUP_TOXES, groupnumber);
297
298
1
    ck_assert_msg(change_count == NUM_GROUP_TOXES - 1, "%u peers changed the topic with a silenced peer", change_count);
299
300
    /* Founder enables topic lock and sets topic back to original */
301
1
    tox_group_founder_set_topic_lock(tox0, groupnumber, TOX_GROUP_TOPIC_LOCK_ENABLED, &lock_set_err);
302
1
    ck_assert_msg(lock_set_err == TOX_ERR_GROUP_FOUNDER_SET_TOPIC_LOCK_OK, "failed to enable topic lock: %d",
303
1
                  lock_set_err);
304
305
1
    fprintf(stderr, "Topic lock enabled\n");
306
307
    /* Wait for all peers to get topic lock state change */
308
1
    wait_topic_lock(autotoxes, groupnumber, TOX_GROUP_TOPIC_LOCK_ENABLED);
309
310
1
    const bool s3_ret = set_topic(tox0, groupnumber, TOPIC2, TOPIC_LEN2);
311
1
    ck_assert_msg(s3_ret, "Founder failed to set topic second time");
312
313
1
    wait_state_topic(autotoxes, groupnumber, TOPIC2, TOPIC_LEN2);
314
315
    /* No peer excluding the founder should be able to set the topic */
316
317
1
    change_count = set_topic_all_peers(rng, &autotoxes[1], NUM_GROUP_TOXES - 1, groupnumber);
318
319
1
    ck_assert_msg(change_count == 0, "%u peers changed the topic with topic lock enabled", change_count);
320
321
    /* A final check that the topic is unchanged */
322
1
    wait_state_topic(autotoxes, groupnumber, TOPIC2, TOPIC_LEN2);
323
324
4
    for (size_t i = 0; i < NUM_GROUP_TOXES; ++i) {
325
3
        Tox_Err_Group_Leave err_exit;
326
3
        tox_group_leave(autotoxes[i].tox, groupnumber, nullptr, 0, &err_exit);
327
3
        ck_assert_msg(err_exit == TOX_ERR_GROUP_LEAVE_OK, "%d", err_exit);
328
3
    }
329
330
1
    fprintf(stderr, "All tests passed!\n");
331
1
}
332
333
int main(void)
334
721
{
335
721
    setvbuf(stdout, nullptr, _IONBF, 0);
336
337
721
    Run_Auto_Options autotest_opts = default_run_auto_options();
338
721
    autotest_opts.graph = GRAPH_COMPLETE;
339
340
721
    run_auto_test(nullptr, NUM_GROUP_TOXES, group_topic_test, sizeof(State), &autotest_opts);
341
342
721
    return 0;
343
721
}
344
345
#undef TOPIC
346
#undef TOPIC_LEN
347
#undef TOPIC2
348
#undef TOPIC_LEN2
349
#undef NUM_GROUP_TOXES
350
#undef GROUP_NAME
351
#undef GROUP_NAME_LEN
352
#undef PEER0_NICK
353
#undef PEER0_NICK_LEN