/work/auto_tests/save_load_test.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Tests that we can save and load Tox data. |
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/tox_struct.h" |
13 | | #include "../toxcore/util.h" |
14 | | #include "auto_test_support.h" |
15 | | #include "check_compat.h" |
16 | | |
17 | | #ifndef USE_IPV6 |
18 | | #define USE_IPV6 1 |
19 | | #endif |
20 | | |
21 | | #ifdef TOX_LOCALHOST |
22 | | #undef TOX_LOCALHOST |
23 | | #endif |
24 | | #if USE_IPV6 |
25 | | #define TOX_LOCALHOST "::1" |
26 | | #else |
27 | 1 | #define TOX_LOCALHOST "127.0.0.1" |
28 | | #endif |
29 | | |
30 | | #ifdef TCP_RELAY_PORT |
31 | | #undef TCP_RELAY_PORT |
32 | | #endif |
33 | 2 | #define TCP_RELAY_PORT 33431 |
34 | | |
35 | | static void accept_friend_request(const Tox_Event_Friend_Request *event, void *userdata) |
36 | 53 | { |
37 | 53 | Tox *tox = (Tox *)userdata; |
38 | | |
39 | 53 | const uint8_t *public_key = tox_event_friend_request_get_public_key(event); |
40 | 53 | const uint8_t *message = tox_event_friend_request_get_message(event); |
41 | 53 | uint32_t message_length = tox_event_friend_request_get_message_length(event); |
42 | | |
43 | 53 | if (message_length == 7 && memcmp("Gentoo", message, 7) == 0) { |
44 | 53 | tox_friend_add_norequest(tox, public_key, nullptr); |
45 | 53 | } |
46 | 53 | } |
47 | | |
48 | | static unsigned int connected_t1; |
49 | | static void tox_connection_status(const Tox_Event_Self_Connection_Status *event, void *user_data) |
50 | 1 | { |
51 | 1 | const Tox_Connection connection_status = tox_event_self_connection_status_get_connection_status(event); |
52 | | |
53 | 1 | if (connected_t1 && !connection_status) { |
54 | 0 | ck_abort_msg("Tox went offline"); |
55 | 0 | } |
56 | | |
57 | 1 | ck_assert_msg(connection_status != TOX_CONNECTION_NONE, "wrong status %u", connection_status); |
58 | | |
59 | 1 | connected_t1 = connection_status; |
60 | 1 | } |
61 | | |
62 | | /* validate that: |
63 | | * a) saving stays within the confined space |
64 | | * b) a saved state can be loaded back successfully |
65 | | * c) a second save is of equal size |
66 | | * d) the second save is of equal content */ |
67 | | static void reload_tox(Tox **tox, struct Tox_Options *const in_opts, void *user_data) |
68 | 2 | { |
69 | 2 | const size_t extra = 64; |
70 | 2 | const size_t save_size1 = tox_get_savedata_size(*tox); |
71 | 2 | ck_assert_msg(save_size1 != 0, "save is invalid size %u", (unsigned)save_size1); |
72 | 2 | printf("%u\n", (unsigned)save_size1); |
73 | | |
74 | 2 | uint8_t *buffer = (uint8_t *)malloc(save_size1 + 2 * extra); |
75 | 2 | ck_assert_msg(buffer != nullptr, "malloc failed"); |
76 | 2 | memset(buffer, 0xCD, extra); |
77 | 2 | memset(buffer + extra + save_size1, 0xCD, extra); |
78 | 2 | tox_get_savedata(*tox, buffer + extra); |
79 | 2 | tox_kill(*tox); |
80 | | |
81 | 130 | for (size_t i = 0; i < extra; ++i) { |
82 | 128 | ck_assert_msg(buffer[i] == 0xCD, "Buffer underwritten from tox_get_savedata() @%u", (unsigned)i); |
83 | 128 | ck_assert_msg(buffer[extra + save_size1 + i] == 0xCD, "Buffer overwritten from tox_get_savedata() @%u", (unsigned)i); |
84 | 128 | } |
85 | | |
86 | 2 | struct Tox_Options *const options = (in_opts == nullptr) ? tox_options_new(nullptr) : in_opts; |
87 | 2 | tox_options_set_ipv6_enabled(options, USE_IPV6); |
88 | | |
89 | 2 | tox_options_set_savedata_type(options, TOX_SAVEDATA_TYPE_TOX_SAVE); |
90 | | |
91 | 2 | tox_options_set_savedata_data(options, buffer + extra, save_size1); |
92 | | |
93 | 2 | *tox = tox_new_log(options, nullptr, user_data); |
94 | | |
95 | 2 | if (in_opts == nullptr) { |
96 | 0 | tox_options_free(options); |
97 | 0 | } |
98 | | |
99 | 2 | ck_assert_msg(*tox != nullptr, "Failed to load back stored buffer"); |
100 | | |
101 | 2 | const size_t save_size2 = tox_get_savedata_size(*tox); |
102 | | |
103 | 2 | ck_assert_msg(save_size1 == save_size2, "Tox save data changed in size from a store/load cycle: %u -> %u", |
104 | 2 | (unsigned)save_size1, (unsigned)save_size2); |
105 | | |
106 | 2 | uint8_t *buffer2 = (uint8_t *)malloc(save_size2); |
107 | | |
108 | 2 | ck_assert_msg(buffer2 != nullptr, "malloc failed"); |
109 | | |
110 | 2 | tox_get_savedata(*tox, buffer2); |
111 | | |
112 | 2 | ck_assert_msg(!memcmp(buffer + extra, buffer2, save_size2), "Tox state changed by store/load/store cycle"); |
113 | | |
114 | 2 | free(buffer2); |
115 | | |
116 | 2 | free(buffer); |
117 | 2 | } |
118 | | |
119 | | typedef struct Time_Data { |
120 | | pthread_mutex_t lock; |
121 | | uint64_t clock; |
122 | | } Time_Data; |
123 | | |
124 | | static uint64_t get_state_clock_callback(void *user_data) |
125 | 442k | { |
126 | 442k | Time_Data *time_data = (Time_Data *)user_data; |
127 | 442k | pthread_mutex_lock(&time_data->lock); |
128 | 442k | uint64_t clock = time_data->clock; |
129 | 442k | pthread_mutex_unlock(&time_data->lock); |
130 | 442k | return clock; |
131 | 442k | } |
132 | | |
133 | | static void increment_clock(Time_Data *time_data, uint64_t count) |
134 | 2.01k | { |
135 | 2.01k | pthread_mutex_lock(&time_data->lock); |
136 | 2.01k | time_data->clock += count; |
137 | 2.01k | pthread_mutex_unlock(&time_data->lock); |
138 | 2.01k | } |
139 | | |
140 | | static void set_current_time_callback(Tox *tox, Time_Data *time_data) |
141 | 8 | { |
142 | 8 | Mono_Time *mono_time = tox->mono_time; |
143 | 8 | mono_time_set_current_time_callback(mono_time, get_state_clock_callback, time_data); |
144 | 8 | } |
145 | | |
146 | | static void test_few_clients(void) |
147 | 1 | { |
148 | 1 | uint32_t index[] = { 1, 2, 3 }; |
149 | 1 | time_t con_time = 0, cur_time = time(nullptr); |
150 | | |
151 | 1 | struct Tox_Options *opts1 = tox_options_new(nullptr); |
152 | 1 | tox_options_set_ipv6_enabled(opts1, USE_IPV6); |
153 | 1 | tox_options_set_tcp_port(opts1, TCP_RELAY_PORT); |
154 | 1 | Tox_Err_New t_n_error; |
155 | 1 | Tox *tox1 = tox_new_log(opts1, &t_n_error, &index[0]); |
156 | 1 | ck_assert_msg(t_n_error == TOX_ERR_NEW_OK, "Failed to create tox instance: %u", t_n_error); |
157 | 1 | tox_options_free(opts1); |
158 | 1 | tox_events_init(tox1); |
159 | 1 | Tox_Dispatch *dispatch1 = tox_dispatch_new(nullptr); |
160 | 1 | ck_assert(dispatch1 != nullptr); |
161 | | |
162 | 1 | struct Tox_Options *opts2 = tox_options_new(nullptr); |
163 | 1 | tox_options_set_ipv6_enabled(opts2, USE_IPV6); |
164 | 1 | tox_options_set_udp_enabled(opts2, false); |
165 | 1 | tox_options_set_local_discovery_enabled(opts2, false); |
166 | 1 | Tox *tox2 = tox_new_log(opts2, &t_n_error, &index[1]); |
167 | 1 | ck_assert_msg(t_n_error == TOX_ERR_NEW_OK, "Failed to create tox instance: %u", t_n_error); |
168 | 1 | tox_events_init(tox2); |
169 | 1 | Tox_Dispatch *dispatch2 = tox_dispatch_new(nullptr); |
170 | 1 | ck_assert(dispatch2 != nullptr); |
171 | | |
172 | 1 | struct Tox_Options *opts3 = tox_options_new(nullptr); |
173 | 1 | tox_options_set_ipv6_enabled(opts3, USE_IPV6); |
174 | 1 | tox_options_set_local_discovery_enabled(opts3, false); |
175 | 1 | Tox *tox3 = tox_new_log(opts3, &t_n_error, &index[2]); |
176 | 1 | ck_assert_msg(t_n_error == TOX_ERR_NEW_OK, "Failed to create tox instance: %u", t_n_error); |
177 | | |
178 | 1 | ck_assert_msg(tox1 && tox2 && tox3, "Failed to create 3 tox instances"); |
179 | | |
180 | 1 | Time_Data time_data; |
181 | 1 | ck_assert_msg(pthread_mutex_init(&time_data.lock, nullptr) == 0, "Failed to init time_data mutex"); |
182 | 1 | time_data.clock = current_time_monotonic(tox1->mono_time); |
183 | 1 | set_current_time_callback(tox1, &time_data); |
184 | 1 | set_current_time_callback(tox2, &time_data); |
185 | 1 | set_current_time_callback(tox3, &time_data); |
186 | | |
187 | 1 | uint8_t dht_key[TOX_PUBLIC_KEY_SIZE]; |
188 | 1 | tox_self_get_dht_id(tox1, dht_key); |
189 | 1 | const uint16_t dht_port = tox_self_get_udp_port(tox1, nullptr); |
190 | | |
191 | 1 | printf("using tox1 as tcp relay for tox2\n"); |
192 | 1 | tox_add_tcp_relay(tox2, TOX_LOCALHOST, TCP_RELAY_PORT, dht_key, nullptr); |
193 | | |
194 | 1 | printf("bootstrapping toxes off tox1\n"); |
195 | 1 | tox_bootstrap(tox2, "localhost", dht_port, dht_key, nullptr); |
196 | 1 | tox_bootstrap(tox3, "localhost", dht_port, dht_key, nullptr); |
197 | | |
198 | 1 | connected_t1 = 0; |
199 | 1 | tox_events_callback_self_connection_status(dispatch1, tox_connection_status); |
200 | 1 | tox_events_callback_friend_request(dispatch2, accept_friend_request); |
201 | 1 | uint8_t address[TOX_ADDRESS_SIZE]; |
202 | 1 | tox_self_get_address(tox2, address); |
203 | 1 | uint32_t test = tox_friend_add(tox3, address, (const uint8_t *)"Gentoo", 7, nullptr); |
204 | 1 | ck_assert_msg(test == 0, "Failed to add friend error code: %u", test); |
205 | | |
206 | 1 | uint8_t off = 1; |
207 | | |
208 | 78 | while (true) { |
209 | 78 | { |
210 | 78 | Tox_Err_Events_Iterate err = TOX_ERR_EVENTS_ITERATE_OK; |
211 | 78 | Tox_Events *events = tox_events_iterate(tox1, true, &err); |
212 | 78 | ck_assert(err == TOX_ERR_EVENTS_ITERATE_OK); |
213 | 78 | tox_dispatch_invoke(dispatch1, events, tox1); |
214 | 78 | tox_events_free(events); |
215 | 78 | } |
216 | 0 | { |
217 | 78 | Tox_Err_Events_Iterate err = TOX_ERR_EVENTS_ITERATE_OK; |
218 | 78 | Tox_Events *events = tox_events_iterate(tox2, true, &err); |
219 | 78 | ck_assert(err == TOX_ERR_EVENTS_ITERATE_OK); |
220 | 78 | tox_dispatch_invoke(dispatch2, events, tox2); |
221 | 78 | tox_events_free(events); |
222 | 78 | } |
223 | 78 | tox_iterate(tox3, nullptr); |
224 | | |
225 | 78 | if (tox_self_get_connection_status(tox1) && tox_self_get_connection_status(tox2) |
226 | 78 | && tox_self_get_connection_status(tox3)) { |
227 | 38 | if (off) { |
228 | 1 | printf("Toxes are online, took %lu seconds\n", (unsigned long)(time(nullptr) - cur_time)); |
229 | 1 | con_time = time(nullptr); |
230 | 1 | off = 0; |
231 | 1 | } |
232 | | |
233 | 38 | if (tox_friend_get_connection_status(tox2, 0, nullptr) == TOX_CONNECTION_TCP |
234 | 38 | && tox_friend_get_connection_status(tox3, 0, nullptr) == TOX_CONNECTION_TCP) { |
235 | 1 | break; |
236 | 1 | } |
237 | 38 | } |
238 | | |
239 | 77 | increment_clock(&time_data, 200); |
240 | 77 | c_sleep(5); |
241 | 77 | } |
242 | | |
243 | 1 | ck_assert_msg(connected_t1, "Tox1 isn't connected. %u", connected_t1); |
244 | 1 | printf("tox clients connected took %lu seconds\n", (unsigned long)(time(nullptr) - con_time)); |
245 | | |
246 | | // We're done with this callback, so unset it to ensure we don't fail the |
247 | | // test if tox1 goes offline while tox2 and 3 are reloaded. |
248 | 1 | tox_events_callback_self_connection_status(dispatch1, nullptr); |
249 | | |
250 | 1 | reload_tox(&tox2, opts2, &index[1]); |
251 | 1 | tox_events_init(tox2); |
252 | | |
253 | 1 | reload_tox(&tox3, opts3, &index[2]); |
254 | | |
255 | 1 | cur_time = time(nullptr); |
256 | | |
257 | 1 | off = 1; |
258 | | |
259 | 1.55k | while (true) { |
260 | 1.55k | { |
261 | 1.55k | Tox_Err_Events_Iterate err = TOX_ERR_EVENTS_ITERATE_OK; |
262 | 1.55k | Tox_Events *events = tox_events_iterate(tox1, true, &err); |
263 | 1.55k | ck_assert(err == TOX_ERR_EVENTS_ITERATE_OK); |
264 | 1.55k | tox_dispatch_invoke(dispatch1, events, tox1); |
265 | 1.55k | tox_events_free(events); |
266 | 1.55k | } |
267 | 0 | { |
268 | 1.55k | Tox_Err_Events_Iterate err = TOX_ERR_EVENTS_ITERATE_OK; |
269 | 1.55k | Tox_Events *events = tox_events_iterate(tox2, true, &err); |
270 | 1.55k | ck_assert(err == TOX_ERR_EVENTS_ITERATE_OK); |
271 | 1.55k | tox_dispatch_invoke(dispatch2, events, tox2); |
272 | 1.55k | tox_events_free(events); |
273 | 1.55k | } |
274 | 1.55k | tox_iterate(tox3, nullptr); |
275 | | |
276 | 1.55k | if (tox_self_get_connection_status(tox1) && tox_self_get_connection_status(tox2) |
277 | 1.55k | && tox_self_get_connection_status(tox3)) { |
278 | 193 | if (off) { |
279 | 1 | printf("Toxes are online again after reloading, took %lu seconds\n", (unsigned long)(time(nullptr) - cur_time)); |
280 | 1 | con_time = time(nullptr); |
281 | 1 | off = 0; |
282 | 1 | } |
283 | | |
284 | 193 | if (tox_friend_get_connection_status(tox2, 0, nullptr) == TOX_CONNECTION_TCP |
285 | 193 | && tox_friend_get_connection_status(tox3, 0, nullptr) == TOX_CONNECTION_TCP) { |
286 | 1 | break; |
287 | 1 | } |
288 | 193 | } |
289 | | |
290 | 1.55k | increment_clock(&time_data, 100); |
291 | 1.55k | c_sleep(5); |
292 | 1.55k | } |
293 | | |
294 | 1 | printf("tox clients connected took %lu seconds\n", (unsigned long)(time(nullptr) - con_time)); |
295 | | |
296 | 1 | printf("test_few_clients succeeded, took %lu seconds\n", (unsigned long)(time(nullptr) - cur_time)); |
297 | | |
298 | 1 | tox_dispatch_free(dispatch1); |
299 | 1 | tox_dispatch_free(dispatch2); |
300 | | |
301 | 1 | tox_kill(tox1); |
302 | 1 | tox_kill(tox2); |
303 | 1 | tox_kill(tox3); |
304 | | |
305 | 1 | tox_options_free(opts2); |
306 | 1 | tox_options_free(opts3); |
307 | 1 | } |
308 | | |
309 | | int main(void) |
310 | 545 | { |
311 | 545 | setvbuf(stdout, nullptr, _IONBF, 0); |
312 | | |
313 | 545 | test_few_clients(); |
314 | 545 | return 0; |
315 | 545 | } |