Coverage Report

Created: 2026-10-24 08:16

/work/auto_tests/scenarios/scenario_conference_av_test.c
Line
Count
Source (jump to first uncovered line)
1
#include "framework/framework.h"
2
#include "../../toxav/toxav.h"
3
#include <stdio.h>
4
#include <string.h>
5
#include <stdlib.h>
6
7
39
#define NUM_NODES 4
8
9
typedef struct {
10
    bool invited_next;
11
    uint32_t audio_received_mask;
12
    uint32_t group_number;
13
    bool joined;
14
} State;
15
16
static void audio_callback(void *tox, uint32_t group_number, uint32_t peer_number, const int16_t *pcm,
17
                           unsigned int samples, uint8_t channels, uint32_t sample_rate, void *user_data)
18
452
{
19
452
    (void)tox;
20
452
    (void)group_number;
21
452
    (void)sample_rate;
22
452
    (void)channels;
23
452
    ToxNode *self = (ToxNode *)user_data;
24
452
    State *state = (State *)tox_node_get_script_ctx(self);
25
452
    if (samples > 0) {
26
452
        state->audio_received_mask |= (1 << peer_number);
27
452
    }
28
452
}
29
30
static void on_conference_invite(const Tox_Event_Conference_Invite *event, void *user_data)
31
3
{
32
3
    ToxNode *self = (ToxNode *)user_data;
33
3
    State *state = (State *)tox_node_get_script_ctx(self);
34
3
    uint32_t friend_number = tox_event_conference_invite_get_friend_number(event);
35
36
3
    if (state->joined) {
37
0
        return;
38
0
    }
39
40
3
    const uint8_t *cookie = tox_event_conference_invite_get_cookie(event);
41
3
    size_t length = tox_event_conference_invite_get_cookie_length(event);
42
43
3
    tox_node_log(self, "Received conference invite from friend %u", friend_number);
44
3
    state->group_number = toxav_join_av_groupchat(tox_node_get_tox(self), friend_number, cookie, length, audio_callback, self);
45
3
    ck_assert(state->group_number != (uint32_t) -1);
46
3
    state->joined = true;
47
3
}
48
49
static void on_conference_connected(const Tox_Event_Conference_Connected *event, void *user_data)
50
3
{
51
3
    ToxNode *self = (ToxNode *)user_data;
52
3
    State *state = (State *)tox_node_get_script_ctx(self);
53
3
    uint32_t group_number = tox_event_conference_connected_get_conference_number(event);
54
55
3
    if (state->invited_next) {
56
0
        return;
57
0
    }
58
59
    // In a linear graph, friend 0 is i-1, friend 1 is i+1 (if exists)
60
    // We want to invite the next peer.
61
3
    uint32_t friend_count = tox_self_get_friend_list_size(tox_node_get_tox(self));
62
3
    if (friend_count > 1 || tox_node_get_index(self) == 0) {
63
2
        uint32_t friend_to_invite = (tox_node_get_index(self) == 0) ? 0 : 1;
64
2
        if (friend_to_invite < friend_count) {
65
2
            Tox_Err_Conference_Invite err;
66
2
            tox_conference_invite(tox_node_get_tox(self), friend_to_invite, group_number, &err);
67
2
            if (err == TOX_ERR_CONFERENCE_INVITE_OK) {
68
2
                tox_node_log(self, "Invited next friend (%u)", friend_to_invite);
69
2
                state->invited_next = true;
70
2
            }
71
2
        }
72
2
    }
73
3
}
74
75
static void peer_script(ToxNode *self, void *ctx)
76
4
{
77
4
    State *state = (State *)ctx;
78
4
    Tox *tox = tox_node_get_tox(self);
79
4
    Tox_Dispatch *dispatch = tox_node_get_dispatch(self);
80
81
4
    tox_events_callback_conference_invite(dispatch, on_conference_invite);
82
4
    tox_events_callback_conference_connected(dispatch, on_conference_connected);
83
84
4
    WAIT_UNTIL(tox_node_is_self_connected(self));
85
86
4
    if (tox_node_get_index(self) == 0) {
87
1
        tox_node_log(self, "Creating AV group...");
88
1
        state->group_number = toxav_add_av_groupchat(tox, audio_callback, self);
89
1
        ck_assert(state->group_number != (uint32_t) -1);
90
1
        state->joined = true;
91
92
        // Peer 0 has only one friend (Peer 1)
93
1
        WAIT_UNTIL(tox_node_is_friend_connected(self, 0));
94
1
        tox_conference_invite(tox, 0, state->group_number, nullptr);
95
1
        state->invited_next = true;
96
1
    }
97
98
    // Wait until joined and everyone is in the group
99
4
    WAIT_UNTIL(state->joined);
100
101
    // Invite and friend connection order can be unreliable, so we add a fallback here.
102
4
    if (tox_node_get_index(self) < NUM_NODES - 1 && !state->invited_next) {
103
2
        WAIT_UNTIL(tox_node_is_friend_connected(self, 1));
104
2
        Tox_Err_Conference_Invite err;
105
2
        tox_conference_invite(tox_node_get_tox(self), 1, state->group_number, &err);
106
2
        state->invited_next = (err == TOX_ERR_CONFERENCE_INVITE_OK);
107
2
    }
108
109
22
    while (tox_node_get_conference_peer_count(self, state->group_number) < (uint32_t)NUM_NODES) {
110
18
        tox_scenario_yield(self);
111
18
    }
112
4
    tox_node_log(self, "All %u peers in group!", (uint32_t)NUM_NODES);
113
114
    // Send audio for a bit
115
4
    const int16_t pcm[960] = {0};
116
164
    for (int i = 0; i < 40; i++) {
117
160
        toxav_group_send_audio(tox, state->group_number, pcm, 960, 1, 48000);
118
160
        tox_scenario_yield(self);
119
160
    }
120
121
4
    tox_node_log(self, "Audio sent. Received audio mask: %u", state->audio_received_mask);
122
4
}
123
124
int main(int argc, char *argv[])
125
1
{
126
1
    ToxScenario *s = tox_scenario_new(argc, argv, 60000);
127
1
    State states[NUM_NODES] = {0};
128
1
    ToxNode *nodes[NUM_NODES];
129
130
5
    for (uint32_t i = 0; i < NUM_NODES; i++) {
131
4
        char name[32];
132
4
        snprintf(name, sizeof(name), "Peer-%u", i);
133
4
        nodes[i] = tox_scenario_add_node(s, name, peer_script, &states[i], sizeof(State));
134
4
    }
135
136
    // Linear graph
137
4
    for (uint32_t i = 0; i < NUM_NODES - 1; i++) {
138
3
        tox_node_bootstrap(nodes[i + 1], nodes[i]);
139
3
        tox_node_friend_add(nodes[i], nodes[i + 1]);
140
3
        tox_node_friend_add(nodes[i + 1], nodes[i]);
141
3
    }
142
143
1
    ToxScenarioStatus res = tox_scenario_run(s);
144
1
    tox_scenario_free(s);
145
1
    return (res == TOX_SCENARIO_DONE) ? 0 : 1;
146
1
}