Coverage Report

Created: 2025-10-08 19:34

/work/auto_tests/toxav_many_test.c
Line
Count
Source (jump to first uncovered line)
1
#include <stdint.h>
2
#include <stdio.h>
3
#include <stdlib.h>
4
#include <string.h>
5
#include <sys/types.h>
6
#include <time.h>
7
8
#if !defined(_WIN32) && !defined(__WIN32__) && !defined(WIN32)
9
#include <pthread.h>
10
#endif
11
12
#include <vpx/vpx_image.h>
13
14
#include "../testing/misc_tools.h"
15
#include "../toxav/toxav.h"
16
#include "../toxcore/crypto_core.h"
17
#include "../toxcore/logger.h"
18
#include "../toxcore/tox.h"
19
#include "../toxcore/tox_struct.h"
20
#include "../toxcore/util.h"
21
#include "auto_test_support.h"
22
#include "check_compat.h"
23
24
typedef struct CallControl {
25
    bool incoming;
26
    uint32_t state;
27
} CallControl;
28
29
typedef struct Thread_Data {
30
    ToxAV *alice_av;
31
    ToxAV *bob_av;
32
    CallControl *alice_cc;
33
    CallControl *bob_cc;
34
    uint32_t friend_number;
35
} Thread_Data;
36
37
/**
38
 * Callbacks
39
 */
40
static void t_toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data)
41
11
{
42
11
    printf("Handling CALL callback\n");
43
11
    ((CallControl *)user_data)[friend_number].incoming = true;
44
11
}
45
46
static void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data)
47
33
{
48
33
    printf("Handling CALL STATE callback: %u %p\n", state, (void *)av);
49
33
    ((CallControl *)user_data)[friend_number].state = state;
50
33
}
51
52
static void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number,
53
        uint16_t width, uint16_t height,
54
        uint8_t const *y, uint8_t const *u, uint8_t const *v,
55
        int32_t ystride, int32_t ustride, int32_t vstride,
56
        void *user_data)
57
94
{
58
94
}
59
60
static void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number,
61
        int16_t const *pcm,
62
        size_t sample_count,
63
        uint8_t channels,
64
        uint32_t sampling_rate,
65
        void *user_data)
66
98
{
67
98
}
68
69
static void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length,
70
                                       void *userdata)
71
4
{
72
4
    if (length == 7 && memcmp("gentoo", data, 7) == 0) {
73
4
        ck_assert(tox_friend_add_norequest(m, public_key, nullptr) != (uint32_t) -1);
74
4
    }
75
4
}
76
77
/**
78
 * Iterate helper
79
 */
80
static ToxAV *setup_av_instance(Tox *tox, CallControl *cc)
81
4
{
82
4
    Toxav_Err_New error;
83
84
4
    ToxAV *av = toxav_new(tox, &error);
85
4
    ck_assert(error == TOXAV_ERR_NEW_OK);
86
87
4
    toxav_callback_call(av, t_toxav_call_cb, cc);
88
4
    toxav_callback_call_state(av, t_toxav_call_state_cb, cc);
89
4
    toxav_callback_video_receive_frame(av, t_toxav_receive_video_frame_cb, cc);
90
4
    toxav_callback_audio_receive_frame(av, t_toxav_receive_audio_frame_cb, cc);
91
92
4
    return av;
93
4
}
94
95
static void *call_thread(void *pd)
96
3
{
97
3
    ToxAV *alice_av = ((Thread_Data *) pd)->alice_av;
98
3
    ToxAV *bob_av = ((Thread_Data *) pd)->bob_av;
99
3
    uint32_t friend_number = ((Thread_Data *) pd)->friend_number;
100
101
3
    int16_t *pcm = (int16_t *)calloc(960, sizeof(int16_t));
102
3
    uint8_t *video_y = (uint8_t *)calloc(800 * 600, sizeof(uint8_t));
103
3
    uint8_t *video_u = (uint8_t *)calloc(800 * 600 / 4, sizeof(uint8_t));
104
3
    uint8_t *video_v = (uint8_t *)calloc(800 * 600 / 4, sizeof(uint8_t));
105
106
3
    time_t start_time = time(nullptr);
107
108
504
    do {
109
504
        toxav_iterate(alice_av);
110
504
        toxav_iterate(bob_av);
111
112
504
        toxav_audio_send_frame(alice_av, friend_number, pcm, 960, 1, 48000, nullptr);
113
504
        toxav_audio_send_frame(bob_av, 0, pcm, 960, 1, 48000, nullptr);
114
115
504
        toxav_video_send_frame(alice_av, friend_number, 800, 600, video_y, video_u, video_v, nullptr);
116
504
        toxav_video_send_frame(bob_av, 0, 800, 600, video_y, video_u, video_v, nullptr);
117
118
504
        c_sleep(10);
119
504
    } while (time(nullptr) - start_time < 4);
120
121
3
    free(pcm);
122
3
    free(video_y);
123
3
    free(video_u);
124
3
    free(video_v);
125
126
3
    printf("Closing thread\n");
127
3
    pthread_exit(nullptr);
128
129
0
    return nullptr;
130
3
}
131
132
typedef struct Time_Data {
133
    pthread_mutex_t lock;
134
    uint64_t clock;
135
} Time_Data;
136
137
static uint64_t get_state_clock_callback(void *user_data)
138
442k
{
139
442k
    Time_Data *time_data = (Time_Data *)user_data;
140
442k
    pthread_mutex_lock(&time_data->lock);
141
442k
    uint64_t clock = time_data->clock;
142
442k
    pthread_mutex_unlock(&time_data->lock);
143
442k
    return clock;
144
442k
}
145
146
static void increment_clock(Time_Data *time_data, uint64_t count)
147
2.01k
{
148
2.01k
    pthread_mutex_lock(&time_data->lock);
149
2.01k
    time_data->clock += count;
150
2.01k
    pthread_mutex_unlock(&time_data->lock);
151
2.01k
}
152
153
static void set_current_time_callback(Tox *tox, Time_Data *time_data)
154
8
{
155
8
    Mono_Time *mono_time = tox->mono_time;
156
8
    mono_time_set_current_time_callback(mono_time, get_state_clock_callback, time_data);
157
8
}
158
159
static void test_av_three_calls(void)
160
1
{
161
1
    uint32_t index[] = { 1, 2, 3, 4, 5 };
162
1
    Tox *alice, *bootstrap, *bobs[3];
163
1
    ToxAV *alice_av, *bobs_av[3];
164
1
    void *retval;
165
166
1
    CallControl alice_cc[3], bobs_cc[3];
167
168
1
    Time_Data time_data;
169
1
    pthread_mutex_init(&time_data.lock, nullptr);
170
1
    {
171
1
        Tox_Options *opts = tox_options_new(nullptr);
172
1
        ck_assert(opts != nullptr);
173
1
        tox_options_set_experimental_thread_safety(opts, true);
174
1
        Tox_Err_New error;
175
176
1
        bootstrap = tox_new_log(opts, &error, &index[0]);
177
1
        ck_assert(error == TOX_ERR_NEW_OK);
178
179
1
        time_data.clock = current_time_monotonic(bootstrap->mono_time);
180
1
        set_current_time_callback(bootstrap, &time_data);
181
182
1
        alice = tox_new_log(opts, &error, &index[1]);
183
1
        ck_assert(error == TOX_ERR_NEW_OK);
184
1
        set_current_time_callback(alice, &time_data);
185
186
1
        bobs[0] = tox_new_log(opts, &error, &index[2]);
187
1
        ck_assert(error == TOX_ERR_NEW_OK);
188
1
        set_current_time_callback(bobs[0], &time_data);
189
190
1
        bobs[1] = tox_new_log(opts, &error, &index[3]);
191
1
        ck_assert(error == TOX_ERR_NEW_OK);
192
1
        set_current_time_callback(bobs[1], &time_data);
193
194
1
        bobs[2] = tox_new_log(opts, &error, &index[4]);
195
1
        ck_assert(error == TOX_ERR_NEW_OK);
196
1
        set_current_time_callback(bobs[2], &time_data);
197
1
        tox_options_free(opts);
198
1
    }
199
200
0
    printf("Created 5 instances of Tox\n");
201
1
    printf("Preparing network...\n");
202
1
    time_t cur_time = time(nullptr);
203
204
1
    uint8_t address[TOX_ADDRESS_SIZE];
205
206
1
    tox_callback_friend_request(alice, t_accept_friend_request_cb);
207
1
    tox_self_get_address(alice, address);
208
209
1
    printf("bootstrapping Alice and the %u Bobs off a third bootstrap node\n",
210
1
           (unsigned)(sizeof(bobs) / sizeof(bobs[0])));
211
1
    uint8_t dht_key[TOX_PUBLIC_KEY_SIZE];
212
1
    tox_self_get_dht_id(bootstrap, dht_key);
213
1
    const uint16_t dht_port = tox_self_get_udp_port(bootstrap, nullptr);
214
215
1
    tox_bootstrap(alice, "localhost", dht_port, dht_key, nullptr);
216
1
    tox_bootstrap(bobs[0], "localhost", dht_port, dht_key, nullptr);
217
1
    tox_bootstrap(bobs[1], "localhost", dht_port, dht_key, nullptr);
218
1
    tox_bootstrap(bobs[2], "localhost", dht_port, dht_key, nullptr);
219
220
1
    ck_assert(tox_friend_add(bobs[0], address, (const uint8_t *)"gentoo", 7, nullptr) != (uint32_t) -1);
221
1
    ck_assert(tox_friend_add(bobs[1], address, (const uint8_t *)"gentoo", 7, nullptr) != (uint32_t) -1);
222
1
    ck_assert(tox_friend_add(bobs[2], address, (const uint8_t *)"gentoo", 7, nullptr) != (uint32_t) -1);
223
224
1
    uint8_t off = 1;
225
226
50
    while (true) {
227
50
        tox_iterate(bootstrap, nullptr);
228
50
        tox_iterate(alice, nullptr);
229
50
        tox_iterate(bobs[0], nullptr);
230
50
        tox_iterate(bobs[1], nullptr);
231
50
        tox_iterate(bobs[2], nullptr);
232
233
50
        if (tox_self_get_connection_status(bootstrap) &&
234
50
                tox_self_get_connection_status(alice) &&
235
50
                tox_self_get_connection_status(bobs[0]) &&
236
50
                tox_self_get_connection_status(bobs[1]) &&
237
50
                tox_self_get_connection_status(bobs[2]) && off) {
238
1
            printf("Toxes are online, took %lu seconds\n", (unsigned long)(time(nullptr) - cur_time));
239
1
            off = 0;
240
1
        }
241
242
50
        if (tox_friend_get_connection_status(alice, 0, nullptr) == TOX_CONNECTION_UDP &&
243
50
                tox_friend_get_connection_status(alice, 1, nullptr) == TOX_CONNECTION_UDP &&
244
50
                tox_friend_get_connection_status(alice, 2, nullptr) == TOX_CONNECTION_UDP &&
245
50
                tox_friend_get_connection_status(bobs[0], 0, nullptr) == TOX_CONNECTION_UDP &&
246
50
                tox_friend_get_connection_status(bobs[1], 0, nullptr) == TOX_CONNECTION_UDP &&
247
50
                tox_friend_get_connection_status(bobs[2], 0, nullptr) == TOX_CONNECTION_UDP) {
248
1
            break;
249
1
        }
250
251
49
        increment_clock(&time_data, 200);
252
49
        c_sleep(5);
253
49
    }
254
255
1
    alice_av = setup_av_instance(alice, alice_cc);
256
1
    bobs_av[0] = setup_av_instance(bobs[0], &bobs_cc[0]);
257
1
    bobs_av[1] = setup_av_instance(bobs[1], &bobs_cc[1]);
258
1
    bobs_av[2] = setup_av_instance(bobs[2], &bobs_cc[2]);
259
260
1
    printf("Created 4 instances of ToxAV\n");
261
1
    printf("All set after %lu seconds!\n", (unsigned long)(time(nullptr) - cur_time));
262
263
1
    Thread_Data tds[3];
264
265
4
    for (size_t i = 0; i < 3; i++) {
266
3
        tds[i].alice_av = alice_av;
267
3
        tds[i].bob_av = bobs_av[i];
268
3
        tds[i].alice_cc = &alice_cc[i];
269
3
        tds[i].bob_cc = &bobs_cc[i];
270
3
        tds[i].friend_number = i;
271
3
        memset(tds[i].alice_cc, 0, sizeof(CallControl));
272
3
        memset(tds[i].bob_cc, 0, sizeof(CallControl));
273
3
    }
274
275
1
    pthread_t tids[3];
276
277
4
    for (size_t i = 0; i < 3; i++) {
278
3
        (void) pthread_create(&tids[i], nullptr, call_thread, &tds[i]);
279
3
    }
280
281
1
    time_t start_time = time(nullptr);
282
283
56
    do {
284
56
        tox_iterate(bootstrap, nullptr);
285
56
        tox_iterate(alice, nullptr);
286
56
        tox_iterate(bobs[0], nullptr);
287
56
        tox_iterate(bobs[1], nullptr);
288
56
        tox_iterate(bobs[2], nullptr);
289
290
56
        increment_clock(&time_data, 100);
291
56
        c_sleep(5);
292
56
    } while (time(nullptr) - start_time < 1);
293
294
    /* Call */
295
4
    for (size_t i = 0; i < 3; i++) {
296
3
        Toxav_Err_Call rc;
297
3
        toxav_call(alice_av, tds[i].friend_number, 48, 3000, &rc);
298
299
3
        if (rc != TOXAV_ERR_CALL_OK) {
300
0
            printf("toxav_call failed: %u\n", rc);
301
0
            ck_assert(0);
302
0
        }
303
3
    }
304
305
117
    do {
306
117
        tox_iterate(bootstrap, nullptr);
307
117
        tox_iterate(alice, nullptr);
308
117
        tox_iterate(bobs[0], nullptr);
309
117
        tox_iterate(bobs[1], nullptr);
310
117
        tox_iterate(bobs[2], nullptr);
311
312
468
        for (size_t i = 0; i < 3; i++) {
313
351
            if (bobs_cc[i].incoming) {
314
                /* Answer */
315
3
                Toxav_Err_Answer rc;
316
3
                toxav_answer(bobs_av[i], 0, 8, 500, &rc);
317
318
3
                if (rc != TOXAV_ERR_ANSWER_OK) {
319
0
                    printf("toxav_answer failed: %u\n", rc);
320
0
                    ck_assert(0);
321
0
                }
322
323
3
                bobs_cc[i].incoming = false;
324
3
            }
325
351
        }
326
327
117
        increment_clock(&time_data, 100);
328
117
        c_sleep(5);
329
117
    } while (time(nullptr) - start_time < 3);
330
331
    /* Hangup */
332
4
    for (size_t i = 0; i < 3; i++) {
333
3
        Toxav_Err_Call_Control rc;
334
3
        toxav_call_control(alice_av, i, TOXAV_CALL_CONTROL_CANCEL, &rc);
335
336
3
        if (rc != TOXAV_ERR_CALL_CONTROL_OK) {
337
0
            printf("toxav_call_control failed: %u %p %p\n", rc, (void *)alice_av, (void *)&bobs_av[i]);
338
0
        }
339
3
    }
340
341
162
    do {
342
162
        tox_iterate(bootstrap, nullptr);
343
162
        tox_iterate(alice, nullptr);
344
162
        tox_iterate(bobs[0], nullptr);
345
162
        tox_iterate(bobs[1], nullptr);
346
162
        tox_iterate(bobs[2], nullptr);
347
348
162
        increment_clock(&time_data, 100);
349
162
        c_sleep(5);
350
162
    } while (time(nullptr) - start_time < 5);
351
352
1
    ck_assert(pthread_join(tids[0], &retval) == 0);
353
1
    ck_assert(retval == nullptr);
354
355
1
    ck_assert(pthread_join(tids[1], &retval) == 0);
356
1
    ck_assert(retval == nullptr);
357
358
1
    ck_assert(pthread_join(tids[2], &retval) == 0);
359
1
    ck_assert(retval == nullptr);
360
361
1
    printf("Killing all instances\n");
362
1
    toxav_kill(bobs_av[2]);
363
1
    toxav_kill(bobs_av[1]);
364
1
    toxav_kill(bobs_av[0]);
365
1
    toxav_kill(alice_av);
366
1
    tox_kill(bobs[2]);
367
1
    tox_kill(bobs[1]);
368
1
    tox_kill(bobs[0]);
369
1
    tox_kill(alice);
370
1
    tox_kill(bootstrap);
371
372
1
    pthread_mutex_destroy(&time_data.lock);
373
374
1
    printf("\nTest successful!\n");
375
1
}
376
377
int main(void)
378
545
{
379
545
    setvbuf(stdout, nullptr, _IONBF, 0);
380
381
545
    test_av_three_calls();
382
545
    return 0;
383
545
}