/work/auto_tests/file_transfer_test.c
Line | Count | Source |
1 | | /* File transfer test. |
2 | | */ |
3 | | |
4 | | #include <stdio.h> |
5 | | #include <stdlib.h> |
6 | | #include <string.h> |
7 | | #include <time.h> |
8 | | |
9 | | #include "../testing/misc_tools.h" |
10 | | #include "../toxcore/ccompat.h" |
11 | | #include "../toxcore/tox.h" |
12 | | #include "../toxcore/util.h" |
13 | | #include "auto_test_support.h" |
14 | | #include "check_compat.h" |
15 | | |
16 | | #ifndef USE_IPV6 |
17 | | #define USE_IPV6 1 |
18 | | #endif |
19 | | |
20 | | #ifdef TOX_LOCALHOST |
21 | | #undef TOX_LOCALHOST |
22 | | #endif |
23 | | #if USE_IPV6 |
24 | | #define TOX_LOCALHOST "::1" |
25 | | #else |
26 | 2 | #define TOX_LOCALHOST "127.0.0.1" |
27 | | #endif |
28 | | |
29 | | static void accept_friend_request(Tox *m, const Tox_Event_Friend_Request *event, void *userdata) |
30 | 53 | { |
31 | 53 | const uint8_t *public_key = tox_event_friend_request_get_public_key(event); |
32 | 53 | const uint8_t *data = tox_event_friend_request_get_message(event); |
33 | 53 | const size_t length = tox_event_friend_request_get_message_length(event); |
34 | | |
35 | 53 | if (length == 7 && memcmp("Gentoo", data, 7) == 0) { |
36 | 53 | tox_friend_add_norequest(m, public_key, nullptr); |
37 | 53 | } |
38 | 53 | } |
39 | | |
40 | | static uint64_t size_recv; |
41 | | static uint64_t sending_pos; |
42 | | |
43 | | static uint8_t file_cmp_id[TOX_FILE_ID_LENGTH]; |
44 | | static uint32_t file_accepted; |
45 | | static uint64_t file_size; |
46 | | static void tox_file_receive(Tox *tox, const Tox_Event_File_Recv *event, void *userdata) |
47 | 3 | { |
48 | 3 | const uint32_t friend_number = tox_event_file_recv_get_friend_number(event); |
49 | 3 | const uint32_t file_number = tox_event_file_recv_get_file_number(event); |
50 | 3 | const uint32_t kind = tox_event_file_recv_get_kind(event); |
51 | 3 | const uint64_t filesize = tox_event_file_recv_get_file_size(event); |
52 | 3 | const uint8_t *filename = tox_event_file_recv_get_filename(event); |
53 | 3 | const size_t filename_length = tox_event_file_recv_get_filename_length(event); |
54 | | |
55 | 3 | ck_assert_msg(kind == TOX_FILE_KIND_DATA, "bad kind"); |
56 | | |
57 | 3 | ck_assert_msg(filename_length == sizeof("Gentoo.exe") |
58 | 3 | && memcmp(filename, "Gentoo.exe", sizeof("Gentoo.exe")) == 0, "bad filename"); |
59 | | |
60 | 3 | uint8_t file_id[TOX_FILE_ID_LENGTH]; |
61 | | |
62 | 3 | ck_assert_msg(tox_file_get_file_id(tox, friend_number, file_number, file_id, nullptr), "tox_file_get_file_id error"); |
63 | | |
64 | 3 | ck_assert_msg(memcmp(file_id, file_cmp_id, TOX_FILE_ID_LENGTH) == 0, "bad file_id"); |
65 | | |
66 | 3 | const uint8_t empty[TOX_FILE_ID_LENGTH] = {0}; |
67 | | |
68 | 3 | ck_assert_msg(memcmp(empty, file_cmp_id, TOX_FILE_ID_LENGTH) != 0, "empty file_id"); |
69 | | |
70 | 3 | file_size = filesize; |
71 | | |
72 | 3 | if (filesize) { |
73 | 2 | sending_pos = size_recv = 1337; |
74 | | |
75 | 2 | Tox_Err_File_Seek err_s; |
76 | | |
77 | 2 | ck_assert_msg(tox_file_seek(tox, friend_number, file_number, 1337, &err_s), "tox_file_seek error"); |
78 | | |
79 | 2 | ck_assert_msg(err_s == TOX_ERR_FILE_SEEK_OK, "tox_file_seek wrong error"); |
80 | | |
81 | 2 | } else { |
82 | 1 | sending_pos = size_recv = 0; |
83 | 1 | } |
84 | | |
85 | 3 | Tox_Err_File_Control error; |
86 | | |
87 | 3 | ck_assert_msg(tox_file_control(tox, friend_number, file_number, TOX_FILE_CONTROL_RESUME, &error), |
88 | 3 | "tox_file_control failed. %i", error); |
89 | 3 | ++file_accepted; |
90 | | |
91 | 3 | Tox_Err_File_Seek err_s; |
92 | | |
93 | 3 | ck_assert_msg(!tox_file_seek(tox, friend_number, file_number, 1234, &err_s), "tox_file_seek no error"); |
94 | | |
95 | 3 | ck_assert_msg(err_s == TOX_ERR_FILE_SEEK_DENIED, "tox_file_seek wrong error"); |
96 | 3 | } |
97 | | |
98 | | static uint32_t sendf_ok; |
99 | | static void file_print_control(Tox *tox, const Tox_Event_File_Recv_Control *event, |
100 | | void *userdata) |
101 | 3 | { |
102 | 3 | const uint32_t file_number = tox_event_file_recv_control_get_file_number(event); |
103 | 3 | const Tox_File_Control control = tox_event_file_recv_control_get_control(event); |
104 | | |
105 | | /* First send file num is 0.*/ |
106 | 3 | if (file_number == 0 && control == TOX_FILE_CONTROL_RESUME) { |
107 | 3 | sendf_ok = 1; |
108 | 3 | } |
109 | 3 | } |
110 | | |
111 | | static uint64_t max_sending; |
112 | | static bool m_send_reached; |
113 | | static uint8_t sending_num; |
114 | | static bool file_sending_done; |
115 | | static void tox_file_chunk_request(Tox *tox, const Tox_Event_File_Chunk_Request *event, void *user_data) |
116 | 76.5k | { |
117 | 76.5k | const uint32_t friend_number = tox_event_file_chunk_request_get_friend_number(event); |
118 | 76.5k | const uint32_t file_number = tox_event_file_chunk_request_get_file_number(event); |
119 | 76.5k | const uint64_t position = tox_event_file_chunk_request_get_position(event); |
120 | 76.5k | size_t length = tox_event_file_chunk_request_get_length(event); |
121 | | |
122 | 76.5k | ck_assert_msg(sendf_ok, "didn't get resume control"); |
123 | | |
124 | 76.5k | ck_assert_msg(sending_pos == position, "bad position %lu (should be %lu)", (unsigned long)position, (unsigned long)sending_pos); |
125 | | |
126 | 76.5k | if (length == 0) { |
127 | 3 | ck_assert_msg(!file_sending_done, "file sending already done"); |
128 | | |
129 | 3 | file_sending_done = 1; |
130 | 3 | return; |
131 | 3 | } |
132 | | |
133 | 76.5k | if (position + length > max_sending) { |
134 | 1 | ck_assert_msg(!m_send_reached, "requested done file transfer"); |
135 | | |
136 | 1 | length = max_sending - position; |
137 | 1 | m_send_reached = 1; |
138 | 1 | } |
139 | | |
140 | 76.5k | VLA(uint8_t, f_data, length); |
141 | 76.5k | memset(f_data, sending_num, length); |
142 | | |
143 | 76.5k | Tox_Err_File_Send_Chunk error; |
144 | 76.5k | tox_file_send_chunk(tox, friend_number, file_number, position, f_data, length, &error); |
145 | | |
146 | 76.5k | ck_assert_msg(error == TOX_ERR_FILE_SEND_CHUNK_OK, |
147 | 76.5k | "could not send chunk, error num=%d pos=%d len=%d", (int)error, (int)position, (int)length); |
148 | | |
149 | 76.5k | ++sending_num; |
150 | 76.5k | sending_pos += length; |
151 | 76.5k | } |
152 | | |
153 | | |
154 | | static uint8_t num; |
155 | | static bool file_recv; |
156 | | static void write_file(Tox *tox, const Tox_Event_File_Recv_Chunk *event, void *user_data) |
157 | 76.5k | { |
158 | 76.5k | const uint64_t position = tox_event_file_recv_chunk_get_position(event); |
159 | 76.5k | const uint8_t *data = tox_event_file_recv_chunk_get_data(event); |
160 | 76.5k | const size_t length = tox_event_file_recv_chunk_get_data_length(event); |
161 | | |
162 | 76.5k | ck_assert_msg(size_recv == position, "bad position"); |
163 | | |
164 | 76.5k | if (length == 0) { |
165 | 3 | file_recv = 1; |
166 | 3 | return; |
167 | 3 | } |
168 | | |
169 | 76.5k | VLA(uint8_t, f_data, length); |
170 | 76.5k | memset(f_data, num, length); |
171 | 76.5k | ++num; |
172 | | |
173 | 76.5k | ck_assert_msg(memcmp(f_data, data, length) == 0, "FILE_CORRUPTED"); |
174 | | |
175 | 76.5k | size_recv += length; |
176 | 76.5k | } |
177 | | |
178 | | static void iterate_and_dispatch(const Tox_Dispatch *dispatch, Tox *tox) |
179 | 24.1k | { |
180 | 24.1k | Tox_Err_Events_Iterate err; |
181 | 24.1k | Tox_Events *events; |
182 | | |
183 | 24.1k | events = tox_events_iterate(tox, true, &err); |
184 | 24.1k | ck_assert(err == TOX_ERR_EVENTS_ITERATE_OK); |
185 | 24.1k | tox_dispatch_invoke(dispatch, events, tox, nullptr); |
186 | 24.1k | tox_events_free(events); |
187 | 24.1k | } |
188 | | |
189 | | static void file_transfer_test(void) |
190 | 1 | { |
191 | 1 | printf("Starting test: few_clients\n"); |
192 | 1 | uint32_t index[] = { 1, 2, 3 }; |
193 | 1 | long long unsigned int cur_time = time(nullptr); |
194 | 1 | Tox_Err_New t_n_error; |
195 | 1 | Tox *tox1 = tox_new_log(nullptr, &t_n_error, &index[0]); |
196 | 1 | ck_assert_msg(t_n_error == TOX_ERR_NEW_OK, "wrong error"); |
197 | 1 | Tox *tox2 = tox_new_log(nullptr, &t_n_error, &index[1]); |
198 | 1 | ck_assert_msg(t_n_error == TOX_ERR_NEW_OK, "wrong error"); |
199 | 1 | Tox *tox3 = tox_new_log(nullptr, &t_n_error, &index[2]); |
200 | 1 | ck_assert_msg(t_n_error == TOX_ERR_NEW_OK, "wrong error"); |
201 | | |
202 | 1 | ck_assert_msg(tox1 && tox2 && tox3, "Failed to create 3 tox instances"); |
203 | | |
204 | 1 | tox_events_init(tox1); |
205 | 1 | tox_events_init(tox2); |
206 | 1 | tox_events_init(tox3); |
207 | | |
208 | 1 | Tox_Dispatch *dispatch1 = tox_dispatch_new(nullptr); |
209 | 1 | ck_assert(dispatch1 != nullptr); |
210 | 1 | Tox_Dispatch *dispatch2 = tox_dispatch_new(nullptr); |
211 | 1 | ck_assert(dispatch2 != nullptr); |
212 | 1 | Tox_Dispatch *dispatch3 = tox_dispatch_new(nullptr); |
213 | 1 | ck_assert(dispatch3 != nullptr); |
214 | | |
215 | 1 | tox_events_callback_friend_request(dispatch2, accept_friend_request); |
216 | | |
217 | 1 | uint8_t address[TOX_ADDRESS_SIZE]; |
218 | 1 | tox_self_get_address(tox2, address); |
219 | 1 | uint32_t test = tox_friend_add(tox3, address, (const uint8_t *)"Gentoo", 7, nullptr); |
220 | 1 | ck_assert_msg(test == 0, "Failed to add friend error code: %u", test); |
221 | | |
222 | 1 | uint8_t dht_key[TOX_PUBLIC_KEY_SIZE]; |
223 | 1 | tox_self_get_dht_id(tox1, dht_key); |
224 | 1 | uint16_t dht_port = tox_self_get_udp_port(tox1, nullptr); |
225 | | |
226 | 1 | tox_bootstrap(tox2, TOX_LOCALHOST, dht_port, dht_key, nullptr); |
227 | 1 | tox_bootstrap(tox3, TOX_LOCALHOST, dht_port, dht_key, nullptr); |
228 | | |
229 | 1 | printf("Waiting for toxes to come online\n"); |
230 | | |
231 | 55 | do { |
232 | 55 | iterate_and_dispatch(dispatch1, tox1); |
233 | 55 | iterate_and_dispatch(dispatch2, tox2); |
234 | 55 | iterate_and_dispatch(dispatch3, tox3); |
235 | | |
236 | 55 | printf("Connections: self (%d, %d, %d), friends (%d, %d)\n", |
237 | 55 | tox_self_get_connection_status(tox1), |
238 | 55 | tox_self_get_connection_status(tox2), |
239 | 55 | tox_self_get_connection_status(tox3), |
240 | 55 | tox_friend_get_connection_status(tox2, 0, nullptr), |
241 | 55 | tox_friend_get_connection_status(tox3, 0, nullptr)); |
242 | 55 | c_sleep(ITERATION_INTERVAL); |
243 | 55 | } while (tox_self_get_connection_status(tox1) == TOX_CONNECTION_NONE || |
244 | 55 | tox_self_get_connection_status(tox2) == TOX_CONNECTION_NONE || |
245 | 55 | tox_self_get_connection_status(tox3) == TOX_CONNECTION_NONE || |
246 | 55 | tox_friend_get_connection_status(tox2, 0, nullptr) == TOX_CONNECTION_NONE || |
247 | 55 | tox_friend_get_connection_status(tox3, 0, nullptr) == TOX_CONNECTION_NONE); |
248 | | |
249 | 1 | printf("Starting file transfer test: 100MiB file.\n"); |
250 | | |
251 | 1 | file_accepted = file_size = sendf_ok = size_recv = 0; |
252 | 1 | file_recv = 0; |
253 | 1 | max_sending = UINT64_MAX; |
254 | 1 | uint64_t f_time = time(nullptr); |
255 | 1 | tox_events_callback_file_recv_chunk(dispatch3, write_file); |
256 | 1 | tox_events_callback_file_recv_control(dispatch2, file_print_control); |
257 | 1 | tox_events_callback_file_chunk_request(dispatch2, tox_file_chunk_request); |
258 | 1 | tox_events_callback_file_recv_control(dispatch3, file_print_control); |
259 | 1 | tox_events_callback_file_recv(dispatch3, tox_file_receive); |
260 | 1 | uint64_t totalf_size = 100 * 1024 * 1024; |
261 | 1 | uint32_t fnum = tox_file_send(tox2, 0, TOX_FILE_KIND_DATA, totalf_size, nullptr, (const uint8_t *)"Gentoo.exe", |
262 | 1 | sizeof("Gentoo.exe"), nullptr); |
263 | 1 | ck_assert_msg(fnum != UINT32_MAX, "tox_new_file_sender fail"); |
264 | | |
265 | 1 | Tox_Err_File_Get gfierr; |
266 | 1 | ck_assert_msg(!tox_file_get_file_id(tox2, 1, fnum, file_cmp_id, &gfierr), "tox_file_get_file_id didn't fail"); |
267 | 1 | ck_assert_msg(gfierr == TOX_ERR_FILE_GET_FRIEND_NOT_FOUND, "wrong error"); |
268 | 1 | ck_assert_msg(!tox_file_get_file_id(tox2, 0, fnum + 1, file_cmp_id, &gfierr), "tox_file_get_file_id didn't fail"); |
269 | 1 | ck_assert_msg(gfierr == TOX_ERR_FILE_GET_NOT_FOUND, "wrong error"); |
270 | 1 | ck_assert_msg(tox_file_get_file_id(tox2, 0, fnum, file_cmp_id, &gfierr), "tox_file_get_file_id failed"); |
271 | 1 | ck_assert_msg(gfierr == TOX_ERR_FILE_GET_OK, "wrong error"); |
272 | | |
273 | 1 | const size_t max_iterations = INT16_MAX; |
274 | | |
275 | 7.41k | for (size_t i = 0; i < max_iterations; i++) { |
276 | 7.41k | iterate_and_dispatch(dispatch1, tox1); |
277 | 7.41k | iterate_and_dispatch(dispatch2, tox2); |
278 | 7.41k | iterate_and_dispatch(dispatch3, tox3); |
279 | | |
280 | 7.41k | if (file_sending_done) { |
281 | 1 | ck_assert_msg(sendf_ok && file_recv && totalf_size == file_size && size_recv == file_size && sending_pos == size_recv |
282 | 1 | && file_accepted == 1, |
283 | 1 | "Something went wrong in file transfer %u %u %u %u %u %u %lu %lu %lu", |
284 | 1 | sendf_ok, file_recv, totalf_size == file_size, size_recv == file_size, sending_pos == size_recv, |
285 | 1 | file_accepted == 1, (unsigned long)totalf_size, (unsigned long)size_recv, |
286 | 1 | (unsigned long)sending_pos); |
287 | 1 | break; |
288 | 1 | } |
289 | | |
290 | 7.41k | uint32_t tox1_interval = tox_iteration_interval(tox1); |
291 | 7.41k | uint32_t tox2_interval = tox_iteration_interval(tox2); |
292 | 7.41k | uint32_t tox3_interval = tox_iteration_interval(tox3); |
293 | | |
294 | 7.41k | if ((i + 1) % 500 == 0) { |
295 | 14 | printf("after %u iterations: %.2fMiB done\n", (unsigned int)i + 1, (double)size_recv / 1024 / 1024); |
296 | 14 | } |
297 | | |
298 | 7.41k | c_sleep(min_u32(tox1_interval, min_u32(tox2_interval, tox3_interval))); |
299 | 7.41k | } |
300 | | |
301 | 1 | ck_assert_msg(file_sending_done, "file sending did not complete after %u iterations: sendf_ok:%u file_recv:%u " |
302 | 1 | "totalf_size==file_size:%u size_recv==file_size:%u sending_pos==size_recv:%u file_accepted:%u " |
303 | 1 | "totalf_size:%lu size_recv:%lu sending_pos:%lu", |
304 | 1 | (unsigned int)max_iterations, sendf_ok, file_recv, |
305 | 1 | totalf_size == file_size, size_recv == file_size, sending_pos == size_recv, file_accepted == 1, |
306 | 1 | (unsigned long)totalf_size, (unsigned long)size_recv, |
307 | 1 | (unsigned long)sending_pos); |
308 | | |
309 | 1 | printf("100MiB file sent in %lu seconds\n", (unsigned long)(time(nullptr) - f_time)); |
310 | | |
311 | 1 | printf("starting file 0 transfer test.\n"); |
312 | | |
313 | 1 | file_sending_done = 0; |
314 | 1 | file_accepted = 0; |
315 | 1 | file_size = 0; |
316 | 1 | sendf_ok = 0; |
317 | 1 | size_recv = 0; |
318 | 1 | file_recv = 0; |
319 | 1 | tox_events_callback_file_recv_chunk(dispatch3, write_file); |
320 | 1 | tox_events_callback_file_recv_control(dispatch2, file_print_control); |
321 | 1 | tox_events_callback_file_chunk_request(dispatch2, tox_file_chunk_request); |
322 | 1 | tox_events_callback_file_recv_control(dispatch3, file_print_control); |
323 | 1 | tox_events_callback_file_recv(dispatch3, tox_file_receive); |
324 | 1 | totalf_size = 0; |
325 | 1 | fnum = tox_file_send(tox2, 0, TOX_FILE_KIND_DATA, totalf_size, nullptr, |
326 | 1 | (const uint8_t *)"Gentoo.exe", sizeof("Gentoo.exe"), nullptr); |
327 | 1 | ck_assert_msg(fnum != UINT32_MAX, "tox_new_file_sender fail"); |
328 | | |
329 | 1 | ck_assert_msg(!tox_file_get_file_id(tox2, 1, fnum, file_cmp_id, &gfierr), "tox_file_get_file_id didn't fail"); |
330 | 1 | ck_assert_msg(gfierr == TOX_ERR_FILE_GET_FRIEND_NOT_FOUND, "wrong error"); |
331 | 1 | ck_assert_msg(!tox_file_get_file_id(tox2, 0, fnum + 1, file_cmp_id, &gfierr), "tox_file_get_file_id didn't fail"); |
332 | 1 | ck_assert_msg(gfierr == TOX_ERR_FILE_GET_NOT_FOUND, "wrong error"); |
333 | 1 | ck_assert_msg(tox_file_get_file_id(tox2, 0, fnum, file_cmp_id, &gfierr), "tox_file_get_file_id failed"); |
334 | 1 | ck_assert_msg(gfierr == TOX_ERR_FILE_GET_OK, "wrong error"); |
335 | | |
336 | 569 | do { |
337 | 569 | uint32_t tox1_interval = tox_iteration_interval(tox1); |
338 | 569 | uint32_t tox2_interval = tox_iteration_interval(tox2); |
339 | 569 | uint32_t tox3_interval = tox_iteration_interval(tox3); |
340 | | |
341 | 569 | c_sleep(min_u32(tox1_interval, min_u32(tox2_interval, tox3_interval))); |
342 | | |
343 | 569 | iterate_and_dispatch(dispatch1, tox1); |
344 | 569 | iterate_and_dispatch(dispatch2, tox2); |
345 | 569 | iterate_and_dispatch(dispatch3, tox3); |
346 | 569 | } while (!file_sending_done); |
347 | | |
348 | 1 | ck_assert_msg(sendf_ok && file_recv && totalf_size == file_size && size_recv == file_size |
349 | 1 | && sending_pos == size_recv && file_accepted == 1, |
350 | 1 | "something went wrong in file transfer %u %u %u %u %u %u %llu %llu %llu", sendf_ok, file_recv, |
351 | 1 | totalf_size == file_size, size_recv == file_size, sending_pos == size_recv, file_accepted == 1, |
352 | 1 | (unsigned long long)totalf_size, (unsigned long long)size_recv, |
353 | 1 | (unsigned long long)sending_pos); |
354 | | |
355 | 1 | printf("file_transfer_test succeeded, took %llu seconds\n", time(nullptr) - cur_time); |
356 | | |
357 | 1 | tox_dispatch_free(dispatch3); |
358 | 1 | tox_dispatch_free(dispatch2); |
359 | 1 | tox_dispatch_free(dispatch1); |
360 | 1 | tox_kill(tox3); |
361 | 1 | tox_kill(tox2); |
362 | 1 | tox_kill(tox1); |
363 | 1 | } |
364 | | |
365 | | int main(void) |
366 | 721 | { |
367 | 721 | setvbuf(stdout, nullptr, _IONBF, 0); |
368 | 721 | file_transfer_test(); |
369 | 721 | return 0; |
370 | 721 | } |