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