/work/auto_tests/conference_test.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Auto Tests: Conferences. |
2 | | */ |
3 | | |
4 | | #include <stdlib.h> |
5 | | #include <string.h> |
6 | | #include <time.h> |
7 | | #include <stdint.h> |
8 | | |
9 | | #include "../toxcore/os_random.h" |
10 | | #include "../toxcore/util.h" |
11 | | |
12 | | #include "check_compat.h" |
13 | | |
14 | 1.82k | #define NUM_GROUP_TOX 16 |
15 | 18 | #define NUM_DISCONNECT 8 |
16 | 48 | #define GROUP_MESSAGE "Install Gentoo" |
17 | | |
18 | 288 | #define NAMELEN 9 |
19 | 16 | #define NAME_FORMAT_STR "Tox #%4u" |
20 | 0 | #define NEW_NAME_FORMAT_STR "New #%4u" |
21 | | |
22 | | typedef struct State { |
23 | | bool invited_next; |
24 | | } State; |
25 | | |
26 | | #include "auto_test_support.h" |
27 | | |
28 | | static void handle_self_connection_status( |
29 | | Tox *tox, Tox_Connection connection_status, void *user_data) |
30 | 50 | { |
31 | 50 | const AutoTox *autotox = (AutoTox *)user_data; |
32 | | |
33 | 50 | if (connection_status != TOX_CONNECTION_NONE) { |
34 | 25 | printf("tox #%u: is now connected\n", autotox->index); |
35 | 25 | } else { |
36 | 25 | printf("tox #%u: is now disconnected\n", autotox->index); |
37 | 25 | } |
38 | 50 | } |
39 | | |
40 | | static void handle_friend_connection_status( |
41 | | Tox *tox, uint32_t friendnumber, Tox_Connection connection_status, void *user_data) |
42 | 64 | { |
43 | 64 | const AutoTox *autotox = (AutoTox *)user_data; |
44 | | |
45 | 64 | if (connection_status != TOX_CONNECTION_NONE) { |
46 | 32 | printf("tox #%u: is now connected to friend %u\n", autotox->index, friendnumber); |
47 | 32 | } else { |
48 | 32 | printf("tox #%u: is now disconnected from friend %u\n", autotox->index, friendnumber); |
49 | 32 | } |
50 | 64 | } |
51 | | |
52 | | static void handle_conference_invite( |
53 | | Tox *tox, uint32_t friendnumber, Tox_Conference_Type type, |
54 | | const uint8_t *data, size_t length, void *user_data) |
55 | 15 | { |
56 | 15 | const AutoTox *autotox = (AutoTox *)user_data; |
57 | 15 | ck_assert_msg(type == TOX_CONFERENCE_TYPE_TEXT, "tox #%u: wrong conference type: %u", autotox->index, type); |
58 | | |
59 | 15 | Tox_Err_Conference_Join err; |
60 | 15 | uint32_t g_num = tox_conference_join(autotox->tox, friendnumber, data, length, &err); |
61 | | |
62 | 15 | ck_assert_msg(err == TOX_ERR_CONFERENCE_JOIN_OK, "tox #%u: error joining group: %u", autotox->index, err); |
63 | 15 | ck_assert_msg(g_num == 0, "tox #%u: group number was not 0", autotox->index); |
64 | | |
65 | | // Try joining again. We should only be allowed to join once. |
66 | 15 | tox_conference_join(autotox->tox, friendnumber, data, length, &err); |
67 | 15 | ck_assert_msg(err != TOX_ERR_CONFERENCE_JOIN_OK, |
68 | 15 | "tox #%u: joining groupchat twice should be impossible.", autotox->index); |
69 | 15 | } |
70 | | |
71 | | static void handle_conference_connected( |
72 | | Tox *tox, uint32_t conference_number, void *user_data) |
73 | 30 | { |
74 | 30 | const AutoTox *autotox = (AutoTox *)user_data; |
75 | 30 | State *state = (State *)autotox->state; |
76 | | |
77 | 30 | if (state->invited_next || tox_self_get_friend_list_size(autotox->tox) <= 1) { |
78 | 2 | return; |
79 | 2 | } |
80 | | |
81 | 28 | Tox_Err_Conference_Invite err; |
82 | 28 | tox_conference_invite(autotox->tox, 1, 0, &err); |
83 | 28 | ck_assert_msg(err == TOX_ERR_CONFERENCE_INVITE_OK, "tox #%u failed to invite next friend: err = %u", autotox->index, |
84 | 28 | err); |
85 | 28 | printf("tox #%u: invited next friend\n", autotox->index); |
86 | 28 | state->invited_next = true; |
87 | 28 | } |
88 | | |
89 | | static uint32_t num_recv; |
90 | | |
91 | | static void handle_conference_message( |
92 | | Tox *tox, uint32_t groupnumber, uint32_t peernumber, Tox_Message_Type type, |
93 | | const uint8_t *message, size_t length, void *user_data) |
94 | 16 | { |
95 | 16 | if (length == (sizeof(GROUP_MESSAGE) - 1) && memcmp(message, GROUP_MESSAGE, sizeof(GROUP_MESSAGE) - 1) == 0) { |
96 | 16 | ++num_recv; |
97 | 16 | } |
98 | 16 | } |
99 | | |
100 | | static bool toxes_are_disconnected_from_group(uint32_t tox_count, AutoTox *autotoxes, |
101 | | const bool *disconnected) |
102 | 232 | { |
103 | 232 | uint32_t num_disconnected = 0; |
104 | | |
105 | 3.94k | for (uint32_t i = 0; i < tox_count; ++i) { |
106 | 3.71k | num_disconnected += disconnected[i]; |
107 | 3.71k | } |
108 | | |
109 | 470 | for (uint32_t i = 0; i < tox_count; i++) { |
110 | 466 | if (disconnected[i]) { |
111 | 214 | continue; |
112 | 214 | } |
113 | | |
114 | 252 | if (tox_conference_peer_count(autotoxes[i].tox, 0, nullptr) > tox_count - num_disconnected) { |
115 | 228 | return false; |
116 | 228 | } |
117 | 252 | } |
118 | | |
119 | 4 | return true; |
120 | 232 | } |
121 | | |
122 | | static void disconnect_toxes(uint32_t tox_count, AutoTox *autotoxes, |
123 | | const bool *disconnect, const bool *exclude) |
124 | 2 | { |
125 | | /* Fake a network outage for a set of peers D by iterating only the other |
126 | | * peers D' until the connections time out according to D', then iterating |
127 | | * only D until the connections time out according to D. */ |
128 | | |
129 | 2 | VLA(bool, disconnect_now, tox_count); |
130 | 2 | bool invert = false; |
131 | | |
132 | 4 | do { |
133 | 68 | for (uint32_t i = 0; i < tox_count; ++i) { |
134 | 64 | disconnect_now[i] = exclude[i] || (invert ^ disconnect[i]); |
135 | 64 | } |
136 | | |
137 | 232 | do { |
138 | 3.94k | for (uint32_t i = 0; i < tox_count; ++i) { |
139 | 3.71k | if (!disconnect_now[i]) { |
140 | 1.39k | tox_iterate(autotoxes[i].tox, &autotoxes[i]); |
141 | 1.39k | autotoxes[i].clock += 1000; |
142 | 1.39k | } |
143 | 3.71k | } |
144 | | |
145 | 232 | c_sleep(20); |
146 | 232 | } while (!toxes_are_disconnected_from_group(tox_count, autotoxes, disconnect_now)); |
147 | | |
148 | 4 | invert = !invert; |
149 | 4 | } while (invert); |
150 | 2 | } |
151 | | |
152 | | static bool all_connected_to_group(uint32_t tox_count, AutoTox *autotoxes) |
153 | 126 | { |
154 | 175 | for (uint32_t i = 0; i < tox_count; i++) { |
155 | 173 | if (tox_conference_peer_count(autotoxes[i].tox, 0, nullptr) < tox_count) { |
156 | 124 | return false; |
157 | 124 | } |
158 | 173 | } |
159 | | |
160 | 2 | return true; |
161 | 126 | } |
162 | | |
163 | | static bool names_propagated(uint32_t tox_count, AutoTox *autotoxes) |
164 | 1 | { |
165 | 17 | for (uint32_t i = 0; i < tox_count; ++i) { |
166 | 272 | for (uint32_t j = 0; j < tox_count; ++j) { |
167 | 256 | const size_t len = tox_conference_peer_get_name_size(autotoxes[i].tox, 0, j, nullptr); |
168 | | |
169 | 256 | if (len != NAMELEN) { |
170 | 0 | return false; |
171 | 0 | } |
172 | 256 | } |
173 | 16 | } |
174 | | |
175 | 1 | return true; |
176 | 1 | } |
177 | | |
178 | | /** |
179 | | * returns a random index at which a list of booleans is false |
180 | | * (some such index is required to exist) |
181 | | */ |
182 | | static uint32_t random_false_index(const Random *rng, const bool *list, const uint32_t length) |
183 | 24 | { |
184 | 24 | uint32_t index; |
185 | | |
186 | 33 | do { |
187 | 33 | index = random_u32(rng) % length; |
188 | 33 | } while (list[index]); |
189 | | |
190 | 24 | return index; |
191 | 24 | } |
192 | | |
193 | | static void run_conference_tests(AutoTox *autotoxes) |
194 | 1 | { |
195 | 1 | const Random *rng = os_random(); |
196 | 1 | ck_assert(rng != nullptr); |
197 | | /* disabling name change propagation check for now, as it occasionally |
198 | | * fails due to disconnections too short to trigger freezing */ |
199 | 1 | const bool check_name_change_propagation = false; |
200 | | |
201 | | /* each peer should freeze at least its two friends, but freezing more |
202 | | * should not be necessary */ |
203 | 1 | const uint32_t max_frozen = max_u32(2, NUM_DISCONNECT / 2); |
204 | 1 | printf("restricting number of frozen peers to %u\n", max_frozen); |
205 | | |
206 | 17 | for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) { |
207 | 16 | Tox_Err_Conference_Set_Max_Offline err; |
208 | 16 | tox_conference_set_max_offline(autotoxes[i].tox, 0, max_frozen, &err); |
209 | 16 | ck_assert_msg(err == TOX_ERR_CONFERENCE_SET_MAX_OFFLINE_OK, |
210 | 16 | "tox #%u failed to set max offline: err = %u", autotoxes[i].index, err); |
211 | 16 | } |
212 | | |
213 | 1 | printf("letting random toxes timeout\n"); |
214 | 1 | bool disconnected[NUM_GROUP_TOX] = {0}; |
215 | 1 | bool restarting[NUM_GROUP_TOX] = {0}; |
216 | | |
217 | 1 | ck_assert(NUM_DISCONNECT < NUM_GROUP_TOX); |
218 | | |
219 | 9 | for (uint32_t i = 0; i < NUM_DISCONNECT; ++i) { |
220 | 8 | uint32_t disconnect = random_false_index(rng, disconnected, NUM_GROUP_TOX); |
221 | 8 | disconnected[disconnect] = true; |
222 | | |
223 | 8 | if (i < NUM_DISCONNECT / 2) { |
224 | 4 | restarting[disconnect] = true; |
225 | 4 | printf("Restarting #%u\n", autotoxes[disconnect].index); |
226 | 4 | } else { |
227 | 4 | printf("Disconnecting #%u\n", autotoxes[disconnect].index); |
228 | 4 | } |
229 | 8 | } |
230 | | |
231 | 1 | uint8_t *save[NUM_GROUP_TOX]; |
232 | 1 | size_t save_size[NUM_GROUP_TOX]; |
233 | | |
234 | 17 | for (uint32_t i = 0; i < NUM_GROUP_TOX; ++i) { |
235 | 16 | if (restarting[i]) { |
236 | 4 | save_size[i] = tox_get_savedata_size(autotoxes[i].tox); |
237 | 4 | ck_assert_msg(save_size[i] != 0, "save is invalid size %u", (unsigned)save_size[i]); |
238 | 4 | save[i] = (uint8_t *)malloc(save_size[i]); |
239 | 4 | ck_assert_msg(save[i] != nullptr, "malloc failed"); |
240 | 4 | tox_get_savedata(autotoxes[i].tox, save[i]); |
241 | 4 | tox_kill(autotoxes[i].tox); |
242 | 4 | } |
243 | 16 | } |
244 | | |
245 | 1 | disconnect_toxes(NUM_GROUP_TOX, autotoxes, disconnected, restarting); |
246 | | |
247 | 17 | for (uint32_t i = 0; i < NUM_GROUP_TOX; ++i) { |
248 | 16 | if (restarting[i]) { |
249 | 4 | struct Tox_Options *const options = tox_options_new(nullptr); |
250 | 4 | ck_assert(options != nullptr); |
251 | 4 | tox_options_set_savedata_type(options, TOX_SAVEDATA_TYPE_TOX_SAVE); |
252 | 4 | tox_options_set_savedata_data(options, save[i], save_size[i]); |
253 | 4 | autotoxes[i].tox = tox_new_log(options, nullptr, &autotoxes[i].index); |
254 | 4 | ck_assert(autotoxes[i].tox != nullptr); |
255 | 4 | tox_options_free(options); |
256 | 4 | free(save[i]); |
257 | | |
258 | 4 | set_mono_time_callback(&autotoxes[i]); |
259 | 4 | tox_conference_set_max_offline(autotoxes[i].tox, 0, max_frozen, nullptr); |
260 | 4 | } |
261 | 16 | } |
262 | | |
263 | 1 | if (check_name_change_propagation) { |
264 | 0 | printf("changing names\n"); |
265 | |
|
266 | 0 | for (uint32_t i = 0; i < NUM_GROUP_TOX; ++i) { |
267 | 0 | char name[NAMELEN + 1]; |
268 | 0 | snprintf(name, NAMELEN + 1, NEW_NAME_FORMAT_STR, autotoxes[i].index); |
269 | 0 | tox_self_set_name(autotoxes[i].tox, (const uint8_t *)name, NAMELEN, nullptr); |
270 | 0 | } |
271 | 0 | } |
272 | | |
273 | 17 | for (uint16_t i = 0; i < NUM_GROUP_TOX; ++i) { |
274 | 16 | const uint32_t num_frozen = tox_conference_offline_peer_count(autotoxes[i].tox, 0, nullptr); |
275 | 16 | ck_assert_msg(num_frozen <= max_frozen, |
276 | 16 | "tox #%u has too many offline peers: %u\n", |
277 | 16 | autotoxes[i].index, num_frozen); |
278 | 16 | } |
279 | | |
280 | 1 | printf("reconnecting toxes\n"); |
281 | | |
282 | 65 | do { |
283 | 65 | iterate_all_wait(autotoxes, NUM_GROUP_TOX, ITERATION_INTERVAL); |
284 | 65 | } while (!all_connected_to_group(NUM_GROUP_TOX, autotoxes)); |
285 | | |
286 | 1 | printf("running conference tests\n"); |
287 | | |
288 | 17 | for (uint32_t i = 0; i < NUM_GROUP_TOX; ++i) { |
289 | 16 | tox_callback_conference_message(autotoxes[i].tox, &handle_conference_message); |
290 | | |
291 | 16 | iterate_all_wait(autotoxes, NUM_GROUP_TOX, ITERATION_INTERVAL); |
292 | 16 | } |
293 | | |
294 | 1 | Tox_Err_Conference_Send_Message err; |
295 | 1 | ck_assert_msg( |
296 | 1 | tox_conference_send_message( |
297 | 1 | autotoxes[random_u32(rng) % NUM_GROUP_TOX].tox, 0, TOX_MESSAGE_TYPE_NORMAL, (const uint8_t *)GROUP_MESSAGE, |
298 | 1 | sizeof(GROUP_MESSAGE) - 1, &err) != 0, "failed to send group message"); |
299 | 1 | ck_assert_msg( |
300 | 1 | err == TOX_ERR_CONFERENCE_SEND_MESSAGE_OK, "failed to send group message"); |
301 | 1 | num_recv = 0; |
302 | | |
303 | 33 | for (uint8_t j = 0; j < NUM_GROUP_TOX * 2; ++j) { |
304 | 32 | iterate_all_wait(autotoxes, NUM_GROUP_TOX, ITERATION_INTERVAL); |
305 | 32 | } |
306 | | |
307 | 1 | ck_assert_msg(num_recv == NUM_GROUP_TOX, "failed to recv group messages"); |
308 | | |
309 | 1 | if (check_name_change_propagation) { |
310 | 0 | for (uint32_t i = 0; i < NUM_GROUP_TOX; ++i) { |
311 | 0 | for (uint32_t j = 0; j < NUM_GROUP_TOX; ++j) { |
312 | 0 | uint8_t name[NAMELEN]; |
313 | 0 | tox_conference_peer_get_name(autotoxes[i].tox, 0, j, name, nullptr); |
314 | | /* Note the toxes will have been reordered */ |
315 | 0 | ck_assert_msg(memcmp(name, "New", 3) == 0, |
316 | 0 | "name of #%u according to #%u not updated", autotoxes[j].index, autotoxes[i].index); |
317 | 0 | } |
318 | 0 | } |
319 | 0 | } |
320 | | |
321 | 17 | for (uint32_t k = NUM_GROUP_TOX; k != 0 ; --k) { |
322 | 16 | tox_conference_delete(autotoxes[k - 1].tox, 0, nullptr); |
323 | | |
324 | 272 | for (uint8_t j = 0; j < 10 || j < NUM_GROUP_TOX; ++j) { |
325 | 256 | iterate_all_wait(autotoxes, NUM_GROUP_TOX, ITERATION_INTERVAL); |
326 | 256 | } |
327 | | |
328 | 136 | for (uint32_t i = 0; i < k - 1; ++i) { |
329 | 120 | uint32_t peer_count = tox_conference_peer_count(autotoxes[i].tox, 0, nullptr); |
330 | 120 | ck_assert_msg(peer_count == (k - 1), "\n\tBad number of group peers (post check)." |
331 | 120 | "\n\t\t\tExpected: %u but tox_instance(%u) only has: %u\n\n", |
332 | 120 | k - 1, i, (unsigned)peer_count); |
333 | 120 | } |
334 | 16 | } |
335 | 1 | } |
336 | | |
337 | | static void test_many_group(AutoTox *autotoxes) |
338 | 1 | { |
339 | 1 | const time_t test_start_time = time(nullptr); |
340 | | |
341 | 17 | for (uint32_t i = 0; i < NUM_GROUP_TOX; ++i) { |
342 | 16 | tox_callback_self_connection_status(autotoxes[i].tox, &handle_self_connection_status); |
343 | 16 | tox_callback_friend_connection_status(autotoxes[i].tox, &handle_friend_connection_status); |
344 | 16 | tox_callback_conference_invite(autotoxes[i].tox, &handle_conference_invite); |
345 | 16 | tox_callback_conference_connected(autotoxes[i].tox, &handle_conference_connected); |
346 | | |
347 | 16 | char name[NAMELEN + 1]; |
348 | 16 | snprintf(name, NAMELEN + 1, NAME_FORMAT_STR, autotoxes[i].index); |
349 | 16 | tox_self_set_name(autotoxes[i].tox, (const uint8_t *)name, NAMELEN, nullptr); |
350 | 16 | } |
351 | | |
352 | 1 | ck_assert_msg(tox_conference_new(autotoxes[0].tox, nullptr) != UINT32_MAX, "failed to create group"); |
353 | 1 | printf("tox #%u: inviting its first friend\n", autotoxes[0].index); |
354 | 1 | ck_assert_msg(tox_conference_invite(autotoxes[0].tox, 0, 0, nullptr) != 0, "failed to invite friend"); |
355 | 1 | ((State *)autotoxes[0].state)->invited_next = true; |
356 | 1 | ck_assert_msg(tox_conference_set_title(autotoxes[0].tox, 0, (const uint8_t *)"Gentoo", sizeof("Gentoo") - 1, |
357 | 1 | nullptr) != 0, |
358 | 1 | "failed to set group title"); |
359 | | |
360 | 1 | printf("waiting for invitations to be made\n"); |
361 | 1 | uint32_t invited_count = 0; |
362 | | |
363 | 15 | do { |
364 | 15 | iterate_all_wait(autotoxes, NUM_GROUP_TOX, ITERATION_INTERVAL); |
365 | | |
366 | 15 | invited_count = 0; |
367 | | |
368 | 255 | for (uint32_t i = 0; i < NUM_GROUP_TOX; ++i) { |
369 | 240 | invited_count += ((State *)autotoxes[i].state)->invited_next; |
370 | 240 | } |
371 | 15 | } while (invited_count != NUM_GROUP_TOX - 1); |
372 | | |
373 | 1 | uint64_t pregroup_clock = autotoxes[0].clock; |
374 | 1 | printf("waiting for all toxes to be in the group\n"); |
375 | 1 | uint32_t fully_connected_count = 0; |
376 | | |
377 | 8 | do { |
378 | 8 | fully_connected_count = 0; |
379 | 8 | printf("current peer counts: ["); |
380 | | |
381 | 8 | iterate_all_wait(autotoxes, NUM_GROUP_TOX, ITERATION_INTERVAL); |
382 | | |
383 | 136 | for (uint32_t i = 0; i < NUM_GROUP_TOX; ++i) { |
384 | 128 | Tox_Err_Conference_Peer_Query err; |
385 | 128 | uint32_t peer_count = tox_conference_peer_count(autotoxes[i].tox, 0, &err); |
386 | | |
387 | 128 | if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) { |
388 | 0 | peer_count = 0; |
389 | 0 | } |
390 | | |
391 | 128 | fully_connected_count += peer_count == NUM_GROUP_TOX; |
392 | | |
393 | 128 | if (i != 0) { |
394 | 120 | printf(", "); |
395 | 120 | } |
396 | | |
397 | 128 | printf("%u", peer_count); |
398 | 128 | } |
399 | | |
400 | 8 | printf("]\n"); |
401 | 8 | fflush(stdout); |
402 | 8 | } while (fully_connected_count != NUM_GROUP_TOX); |
403 | | |
404 | 17 | for (uint32_t i = 0; i < NUM_GROUP_TOX; ++i) { |
405 | 16 | uint32_t peer_count = tox_conference_peer_count(autotoxes[i].tox, 0, nullptr); |
406 | | |
407 | 16 | ck_assert_msg(peer_count == NUM_GROUP_TOX, "\n\tBad number of group peers (pre check)." |
408 | 16 | "\n\t\t\tExpected: %d but tox_instance(%u) only has: %u\n\n", |
409 | 16 | NUM_GROUP_TOX, i, (unsigned)peer_count); |
410 | | |
411 | 16 | uint8_t title[2048]; |
412 | 16 | size_t ret = tox_conference_get_title_size(autotoxes[i].tox, 0, nullptr); |
413 | 16 | ck_assert_msg(ret == sizeof("Gentoo") - 1, "Wrong title length"); |
414 | 16 | tox_conference_get_title(autotoxes[i].tox, 0, title, nullptr); |
415 | 16 | ck_assert_msg(memcmp("Gentoo", title, ret) == 0, "Wrong title"); |
416 | 16 | } |
417 | | |
418 | 1 | printf("waiting for names to propagate\n"); |
419 | | |
420 | 1 | do { |
421 | 1 | iterate_all_wait(autotoxes, NUM_GROUP_TOX, ITERATION_INTERVAL); |
422 | 1 | } while (!names_propagated(NUM_GROUP_TOX, autotoxes)); |
423 | | |
424 | 1 | printf("group connected, took %d seconds\n", (int)((autotoxes[0].clock - pregroup_clock) / 1000)); |
425 | | |
426 | 1 | run_conference_tests(autotoxes); |
427 | | |
428 | 1 | printf("test_many_group succeeded, took %d seconds\n", (int)(time(nullptr) - test_start_time)); |
429 | 1 | } |
430 | | |
431 | | int main(void) |
432 | 545 | { |
433 | 545 | setvbuf(stdout, nullptr, _IONBF, 0); |
434 | | |
435 | 545 | Run_Auto_Options options = default_run_auto_options(); |
436 | 545 | options.graph = GRAPH_LINEAR; |
437 | 545 | options.events = false; |
438 | | |
439 | 545 | run_auto_test(nullptr, NUM_GROUP_TOX, test_many_group, sizeof(State), &options); |
440 | 545 | return 0; |
441 | 545 | } |