Coverage Report

Created: 2026-10-24 08:16

/work/auto_tests/scenarios/scenario_group_tcp_test.c
Line
Count
Source (jump to first uncovered line)
1
#include "framework/framework.h"
2
#include <stdio.h>
3
#include <string.h>
4
#include <stdlib.h>
5
6
10
#define CODEWORD "RONALD MCDONALD"
7
8
#define CODEWORD_LEN (sizeof(CODEWORD) - 1)
8
1
#define RELAY_TCP_PORT 33811
9
10
typedef struct {
11
    uint8_t chat_id[TOX_GROUP_CHAT_ID_SIZE];
12
    uint32_t group_number;
13
    uint32_t peer_id;
14
    bool joined;
15
    bool got_private_message;
16
    bool got_group_message;
17
    bool got_invite;
18
} State;
19
20
static void on_group_invite(const Tox_Event_Group_Invite *event, void *user_data)
21
1
{
22
1
    ToxNode *self = (ToxNode *)user_data;
23
1
    State *state = (State *)tox_node_get_script_ctx(self);
24
25
1
    const uint32_t friend_number = tox_event_group_invite_get_friend_number(event);
26
1
    const uint8_t *invite_data = tox_event_group_invite_get_invite_data(event);
27
1
    const size_t length = tox_event_group_invite_get_invite_data_length(event);
28
29
1
    Tox_Err_Group_Invite_Accept err_accept;
30
1
    state->group_number = tox_group_invite_accept(tox_node_get_tox(self), friend_number, invite_data, length, (const uint8_t *)"test", 4,
31
1
                          nullptr, 0, &err_accept);
32
1
    if (err_accept == TOX_ERR_GROUP_INVITE_ACCEPT_OK) {
33
1
        state->got_invite = true;
34
1
    }
35
1
}
36
37
static void on_group_peer_join(const Tox_Event_Group_Peer_Join *event, void *user_data)
38
25
{
39
25
    ToxNode *self = (ToxNode *)user_data;
40
25
    State *state = (State *)tox_node_get_script_ctx(self);
41
25
    state->peer_id = tox_event_group_peer_join_get_peer_id(event);
42
25
    state->joined = true;
43
25
    tox_node_log(self, "Peer %u joined group", state->peer_id);
44
25
}
45
46
static void on_group_private_message(const Tox_Event_Group_Private_Message *event, void *user_data)
47
2
{
48
2
    ToxNode *self = (ToxNode *)user_data;
49
2
    State *state = (State *)tox_node_get_script_ctx(self);
50
2
    const uint8_t *message = tox_event_group_private_message_get_message(event);
51
2
    const size_t length = tox_event_group_private_message_get_message_length(event);
52
53
2
    if (length == CODEWORD_LEN && memcmp(CODEWORD, message, length) == 0) {
54
2
        state->got_private_message = true;
55
2
    }
56
2
}
57
58
static void on_group_message(const Tox_Event_Group_Message *event, void *user_data)
59
1
{
60
1
    ToxNode *self = (ToxNode *)user_data;
61
1
    State *state = (State *)tox_node_get_script_ctx(self);
62
1
    const uint8_t *message = tox_event_group_message_get_message(event);
63
1
    const size_t length = tox_event_group_message_get_message_length(event);
64
65
1
    if (length == CODEWORD_LEN && memcmp(CODEWORD, message, length) == 0) {
66
1
        state->got_group_message = true;
67
1
    }
68
1
}
69
70
static void alice_script(ToxNode *self, void *ctx)
71
1
{
72
1
    State *state = (State *)ctx;
73
1
    Tox *tox = tox_node_get_tox(self);
74
75
1
    tox_events_callback_group_peer_join(tox_node_get_dispatch(self), on_group_peer_join);
76
77
1
    Tox_Err_Group_New new_err;
78
1
    state->group_number = tox_group_new(tox, TOX_GROUP_PRIVACY_STATE_PUBLIC, (const uint8_t *)"test", 4,
79
1
                                        (const uint8_t *)"test", 4, &new_err);
80
1
    ck_assert(new_err == TOX_ERR_GROUP_NEW_OK);
81
82
1
    Tox_Err_Group_State_Query id_err;
83
1
    tox_group_get_chat_id(tox, state->group_number, state->chat_id, &id_err);
84
1
    ck_assert(id_err == TOX_ERR_GROUP_STATE_QUERY_OK);
85
86
1
    char chat_id_str[TOX_GROUP_CHAT_ID_SIZE * 2 + 1];
87
33
    for (int i = 0; i < TOX_GROUP_CHAT_ID_SIZE; ++i) {
88
32
        snprintf(chat_id_str + i * 2, sizeof(chat_id_str) - i * 2, "%02X", state->chat_id[i]);
89
32
    }
90
1
    tox_node_log(self, "Created group with chat_id: %s", chat_id_str);
91
92
1
    tox_scenario_barrier_wait(self); // Barrier 1: chat_id is ready for Bob
93
94
1
    tox_node_log(self, "Waiting for Bob to join group...");
95
1
    WAIT_UNTIL(state->joined);
96
1
    if (!state->joined) {
97
0
        return;
98
0
    }
99
100
1
    Tox_Err_Group_Send_Private_Message perr;
101
1
    tox_group_send_private_message(tox, state->group_number, state->peer_id,
102
1
                                   TOX_MESSAGE_TYPE_NORMAL,
103
1
                                   (const uint8_t *)CODEWORD, CODEWORD_LEN, &perr);
104
1
    ck_assert(perr == TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_OK);
105
106
1
    tox_scenario_barrier_wait(self); // Barrier 2: PM sent, Bob leaving
107
1
    tox_scenario_barrier_wait(self); // Barrier 3: Bob left
108
1
    state->joined = false;
109
110
1
    tox_node_log(self, "Inviting Bob back via friend invite...");
111
1
    Tox_Err_Group_Invite_Friend err_invite;
112
1
    tox_group_invite_friend(tox, state->group_number, 0, &err_invite);
113
1
    ck_assert(err_invite == TOX_ERR_GROUP_INVITE_FRIEND_OK);
114
115
1
    WAIT_UNTIL(state->joined);
116
1
    if (!state->joined) {
117
0
        return;
118
0
    }
119
120
1
    Tox_Err_Group_Send_Message merr;
121
1
    tox_group_send_message(tox, state->group_number, TOX_MESSAGE_TYPE_NORMAL,
122
1
                           (const uint8_t *)CODEWORD, CODEWORD_LEN, &merr);
123
1
    ck_assert(merr == TOX_ERR_GROUP_SEND_MESSAGE_OK);
124
1
}
125
126
static void bob_script(ToxNode *self, void *ctx)
127
1
{
128
1
    State *state = (State *)ctx;
129
1
    Tox *tox = tox_node_get_tox(self);
130
131
1
    tox_events_callback_group_private_message(tox_node_get_dispatch(self), on_group_private_message);
132
1
    tox_events_callback_group_message(tox_node_get_dispatch(self), on_group_message);
133
1
    tox_events_callback_group_invite(tox_node_get_dispatch(self), on_group_invite);
134
135
1
    tox_node_log(self, "Waiting for TCP connection...");
136
1
    WAIT_UNTIL(tox_node_get_connection_status(self) == TOX_CONNECTION_TCP);
137
1
    tox_node_log(self, "Connected. Waiting for Alice...");
138
1
    WAIT_UNTIL(tox_node_is_friend_connected(self, 0));
139
1
    if (!tox_node_is_friend_connected(self, 0)) {
140
0
        tox_node_log(self, "TIMEOUT waiting for Alice.");
141
0
        return;
142
0
    }
143
1
    tox_node_log(self, "Alice connected.");
144
145
1
    tox_scenario_barrier_wait(self); // Barrier 1: Alice has chat_id
146
147
1
    ToxScenario *s = tox_node_get_scenario(self);
148
1
    ToxNode *alice_node = tox_scenario_get_node(s, 0);
149
1
    State *alice_state = (State *)tox_node_get_script_ctx(alice_node);
150
151
1
    tox_node_log(self, "Joining group via chat_id...");
152
1
    Tox_Err_Group_Join jerr;
153
1
    state->group_number = tox_group_join(tox, alice_state->chat_id, (const uint8_t *)"test", 4, nullptr, 0, &jerr);
154
1
    ck_assert(jerr == TOX_ERR_GROUP_JOIN_OK);
155
156
1
    WAIT_UNTIL(state->got_private_message);
157
1
    if (!state->got_private_message) {
158
0
        return;
159
0
    }
160
1
    tox_scenario_barrier_wait(self); // Barrier 2: PM received, now leaving
161
162
1
    Tox_Err_Group_Leave err_exit;
163
1
    tox_group_leave(tox, state->group_number, nullptr, 0, &err_exit);
164
1
    ck_assert(err_exit == TOX_ERR_GROUP_LEAVE_OK);
165
166
1
    state->got_invite = false;
167
1
    tox_scenario_barrier_wait(self); // Barrier 3: Left, waiting for invite
168
1
    WAIT_UNTIL(state->got_invite);
169
1
    WAIT_UNTIL(state->got_group_message);
170
1
}
171
172
static void relay_script(ToxNode *self, void *ctx)
173
1
{
174
1
    (void)ctx;
175
1
    tox_scenario_barrier_wait(self); // Barrier 1
176
1
    tox_scenario_barrier_wait(self); // Barrier 2
177
1
    tox_scenario_barrier_wait(self); // Barrier 3
178
1
}
179
180
int main(int argc, char **argv)
181
1
{
182
1
    ToxScenario *s = tox_scenario_new(argc, argv, 120000);
183
184
1
    struct Tox_Options *options_alice = tox_options_new(nullptr);
185
1
    tox_options_set_udp_enabled(options_alice, false);
186
187
1
    struct Tox_Options *options_bob = tox_options_new(nullptr);
188
1
    tox_options_set_udp_enabled(options_bob, false);
189
190
1
    struct Tox_Options *options_relay = tox_options_new(nullptr);
191
1
    tox_options_set_tcp_port(options_relay, RELAY_TCP_PORT);
192
193
1
    State *alice_state = (State *)calloc(1, sizeof(State));
194
1
    State *bob_state = (State *)calloc(1, sizeof(State));
195
196
1
    ToxNode *alice = tox_scenario_add_node_ex(s, "Alice", alice_script, alice_state, sizeof(State), options_alice);
197
1
    ToxNode *bob = tox_scenario_add_node_ex(s, "Bob", bob_script, bob_state, sizeof(State), options_bob);
198
1
    ToxNode *relay = tox_scenario_add_node_ex(s, "Relay", relay_script, nullptr, 0, options_relay);
199
200
1
    tox_options_free(options_alice);
201
1
    tox_options_free(options_bob);
202
1
    tox_options_free(options_relay);
203
204
1
    if (!alice || !bob || !relay) {
205
0
        fprintf(stderr, "Failed to create nodes\n");
206
0
        return 1;
207
0
    }
208
209
1
    uint8_t relay_dht_id[TOX_PUBLIC_KEY_SIZE];
210
1
    tox_self_get_dht_id(tox_node_get_tox(relay), relay_dht_id);
211
212
1
    uint16_t relay_tcp_port = tox_self_get_tcp_port(tox_node_get_tox(relay), nullptr);
213
214
1
    Tox_Err_Bootstrap berr;
215
216
    // Both Alice and Bob use the Relay for TCP and DHT bootstrapping
217
1
    tox_add_tcp_relay(tox_node_get_tox(alice), "127.0.0.1", relay_tcp_port, relay_dht_id, &berr);
218
1
    tox_add_tcp_relay(tox_node_get_tox(bob), "127.0.0.1", relay_tcp_port, relay_dht_id, &berr);
219
220
1
    tox_node_bootstrap(alice, relay);
221
1
    tox_node_bootstrap(bob, relay);
222
223
1
    tox_node_friend_add(alice, bob);
224
1
    tox_node_friend_add(bob, alice);
225
226
1
    ToxScenarioStatus res = tox_scenario_run(s);
227
1
    tox_scenario_free(s);
228
1
    free(alice_state);
229
1
    free(bob_state);
230
231
1
    return (res == TOX_SCENARIO_DONE) ? 0 : 1;
232
1
}
233
234
#undef RELAY_TCP_PORT