1 /* unit tests for /core/Messenger.c
2 * Design:
3 * Just call every non-static function in Messenger.c, checking that
4 * they return as they should with check calls. "Bad" calls of the type
5 * function(bad_data, good_length) are _not_ checked for, this type
6 * of call is the fault of the client code.
7 *
8 * Note:
9 * None of the functions here test things that rely on the network, i.e.
10 * checking that status changes are received, messages can be sent, etc.
11 * All of that is done in a separate test, with two local clients running. */
12
13 #ifdef HAVE_CONFIG_H
14 #include "config.h"
15 #endif
16
17 #include <string.h>
18
19 #ifdef VANILLA_NACL
20 #include <crypto_box.h> // crypto_box_PUBLICKEYBYTES and other defines.
21 #else
22 #include <sodium.h>
23 #endif
24
25 #include "check_compat.h"
26 #include "../testing/misc_tools.h"
27 #include "../toxcore/Messenger.h"
28
29 #define REALLY_BIG_NUMBER ((1) << (sizeof(uint16_t) * 7))
30
31 static bool enable_broken_tests = false;
32
33 static const char *friend_id_str = "e4b3d5030bc99494605aecc33ceec8875640c1d74aa32790e821b17e98771c4a00000000f1db";
34 static const char *good_id_str = "d3f14b6d384d8f5f2a66cff637e69f28f539c5de61bc29744785291fa4ef4d64";
35 static const char *bad_id_str = "9B569D14ff637e69f2";
36
37 static uint8_t *friend_id = nullptr;
38 static uint8_t *good_id = nullptr;
39 static uint8_t *bad_id = nullptr;
40
41 static int friend_id_num = 0;
42
43 static Messenger *m;
44
START_TEST(test_m_sendmesage)45 START_TEST(test_m_sendmesage)
46 {
47 const char *message = "h-hi :3";
48 int good_len = strlen(message);
49 int bad_len = MAX_CRYPTO_PACKET_SIZE;
50
51
52 ck_assert(m_send_message_generic(
53 m, -1, MESSAGE_NORMAL, (const uint8_t *)message, good_len, nullptr) == -1);
54 ck_assert(m_send_message_generic(
55 m, REALLY_BIG_NUMBER, MESSAGE_NORMAL, (const uint8_t *)message, good_len, nullptr) == -1);
56 ck_assert(m_send_message_generic(
57 m, 17, MESSAGE_NORMAL, (const uint8_t *)message, good_len, nullptr) == -1);
58 ck_assert(m_send_message_generic(
59 m, friend_id_num, MESSAGE_NORMAL, (const uint8_t *)message, bad_len, nullptr) == -2);
60 }
61 END_TEST
62
START_TEST(test_m_get_userstatus_size)63 START_TEST(test_m_get_userstatus_size)
64 {
65 int rc = 0;
66 ck_assert_msg((m_get_statusmessage_size(m, -1) == -1),
67 "m_get_statusmessage_size did NOT catch an argument of -1");
68 ck_assert_msg((m_get_statusmessage_size(m, REALLY_BIG_NUMBER) == -1),
69 "m_get_statusmessage_size did NOT catch the following argument: %d\n",
70 REALLY_BIG_NUMBER);
71 rc = m_get_statusmessage_size(m, friend_id_num);
72
73 /* this WILL error if the original m_addfriend_norequest() failed */
74 ck_assert_msg((rc >= 0 && rc <= MAX_STATUSMESSAGE_LENGTH),
75 "m_get_statusmessage_size is returning out of range values! (%i)\n"
76 "(this can be caused by the error of m_addfriend_norequest"
77 " in the beginning of the suite)\n", rc);
78 }
79 END_TEST
80
START_TEST(test_m_set_userstatus)81 START_TEST(test_m_set_userstatus)
82 {
83 const char *status = "online!";
84 uint16_t good_length = strlen(status);
85 uint16_t bad_length = REALLY_BIG_NUMBER;
86
87 ck_assert_msg((m_set_statusmessage(m, (const uint8_t *)status, bad_length) == -1),
88 "m_set_userstatus did NOT catch the following length: %d\n",
89 REALLY_BIG_NUMBER);
90
91 ck_assert_msg((m_set_statusmessage(m, (const uint8_t *)status, good_length) == 0),
92 "m_set_userstatus did NOT return 0 on the following length: %d\n"
93 "MAX_STATUSMESSAGE_LENGTH: %d\n", good_length, MAX_STATUSMESSAGE_LENGTH);
94 }
95 END_TEST
96
START_TEST(test_m_get_friend_connectionstatus)97 START_TEST(test_m_get_friend_connectionstatus)
98 {
99 ck_assert_msg((m_get_friend_connectionstatus(m, -1) == -1),
100 "m_get_friend_connectionstatus did NOT catch an argument of -1.\n");
101 ck_assert_msg((m_get_friend_connectionstatus(m, REALLY_BIG_NUMBER) == -1),
102 "m_get_friend_connectionstatus did NOT catch an argument of %d.\n",
103 REALLY_BIG_NUMBER);
104 }
105 END_TEST
106
START_TEST(test_m_friend_exists)107 START_TEST(test_m_friend_exists)
108 {
109 ck_assert_msg((m_friend_exists(m, -1) == 0),
110 "m_friend_exists did NOT catch an argument of -1.\n");
111 ck_assert_msg((m_friend_exists(m, REALLY_BIG_NUMBER) == 0),
112 "m_friend_exists did NOT catch an argument of %d.\n",
113 REALLY_BIG_NUMBER);
114 }
115 END_TEST
116
START_TEST(test_m_delfriend)117 START_TEST(test_m_delfriend)
118 {
119 ck_assert_msg((m_delfriend(m, -1) == -1),
120 "m_delfriend did NOT catch an argument of -1\n");
121 ck_assert_msg((m_delfriend(m, REALLY_BIG_NUMBER) == -1),
122 "m_delfriend did NOT catch the following number: %d\n",
123 REALLY_BIG_NUMBER);
124 }
125 END_TEST
126
START_TEST(test_m_addfriend)127 START_TEST(test_m_addfriend)
128 {
129 const char *good_data = "test";
130 const char *bad_data = "";
131
132 int good_len = strlen(good_data);
133 int bad_len = strlen(bad_data);
134 int really_bad_len = (MAX_CRYPTO_PACKET_SIZE - crypto_box_PUBLICKEYBYTES
135 - crypto_box_NONCEBYTES - crypto_box_BOXZEROBYTES
136 + crypto_box_ZEROBYTES + 100);
137
138 /* TODO(irungentoo): Update this properly to latest master */
139 if (m_addfriend(m, friend_id, (const uint8_t *)good_data, really_bad_len) != FAERR_TOOLONG) {
140 ck_abort_msg("m_addfriend did NOT catch the following length: %d\n", really_bad_len);
141 }
142
143 /* this will return an error if the original m_addfriend_norequest() failed */
144 if (m_addfriend(m, friend_id, (const uint8_t *)good_data, good_len) != FAERR_ALREADYSENT) {
145 ck_abort_msg("m_addfriend did NOT catch adding a friend we already have.\n"
146 "(this can be caused by the error of m_addfriend_norequest in"
147 " the beginning of the suite)\n");
148 }
149
150 if (m_addfriend(m, good_id, (const uint8_t *)bad_data, bad_len) != FAERR_NOMESSAGE) {
151 ck_abort_msg("m_addfriend did NOT catch the following length: %d\n", bad_len);
152 }
153
154 /* this should REALLY return an error */
155 /* TODO(irungentoo): validate client_id in m_addfriend? */
156 if (m_addfriend(m, bad_id, (const uint8_t *)good_data, good_len) >= 0) {
157 ck_abort_msg("The following ID passed through "
158 "m_addfriend without an error:\n'%s'\n", bad_id_str);
159 }
160 }
161 END_TEST
162
START_TEST(test_setname)163 START_TEST(test_setname)
164 {
165 const char *good_name = "consensualCorn";
166 int good_length = strlen(good_name);
167 int bad_length = REALLY_BIG_NUMBER;
168
169 ck_assert_msg((setname(m, (const uint8_t *)good_name, bad_length) == -1),
170 "setname() did NOT error on %d as a length argument!\n", bad_length);
171
172 ck_assert_msg((setname(m, (const uint8_t *)good_name, good_length) == 0),
173 "setname() did NOT return 0 on good arguments!\n");
174 }
175 END_TEST
176
START_TEST(test_getself_name)177 START_TEST(test_getself_name)
178 {
179 const char *nickname = "testGallop";
180 size_t len = strlen(nickname);
181 char *nick_check = (char *)calloc(len + 1, 1);
182
183 setname(m, (const uint8_t *)nickname, len);
184 getself_name(m, (uint8_t *)nick_check);
185
186 ck_assert_msg((memcmp(nickname, nick_check, len) == 0),
187 "getself_name failed to return the known name!\n"
188 "known name: %s\nreturned: %s\n", nickname, nick_check);
189 free(nick_check);
190 }
191 END_TEST
192
193 /* this test is excluded for now, due to lack of a way
194 * to set a friend's status for now.
195 * ideas:
196 * if we have access to the friends list, we could
197 * just add a status manually ourselves. */
198 #if 0
199 START_TEST(test_m_copy_userstatus)
200 {
201 assert(m_copy_userstatus(-1, buf, MAX_USERSTATUS_LENGTH) == -1);
202 assert(m_copy_userstatus(REALLY_BIG_NUMBER, buf, MAX_USERSTATUS_LENGTH) == -1);
203 m_copy_userstatus(friend_id_num, buf, MAX_USERSTATUS_LENGTH + 6);
204
205 assert(strcmp(name_buf, friend_id_status) == 0);
206 }
207 END_TEST
208 #endif
209
START_TEST(test_getname)210 START_TEST(test_getname)
211 {
212 uint8_t name_buf[MAX_NAME_LENGTH];
213 uint8_t test_name[] = {'f', 'o', 'o'};
214
215 ck_assert(getname(m, -1, name_buf) == -1);
216 ck_assert(getname(m, REALLY_BIG_NUMBER, name_buf) == -1);
217
218 memcpy(m->friendlist[0].name, &test_name[0], 3);
219 m->friendlist[0].name_length = 4;
220 ck_assert(getname(m, 0, &name_buf[0]) == 4);
221
222 ck_assert(strcmp((char *)&name_buf[0], "foo") == 0);
223 }
224 END_TEST
225
START_TEST(test_dht_state_saveloadsave)226 START_TEST(test_dht_state_saveloadsave)
227 {
228 /* validate that:
229 * a) saving stays within the confined space
230 * b) a save()d state can be load()ed back successfully
231 * c) a second save() is of equal size
232 * d) the second save() is of equal content */
233 const size_t extra = 64;
234 const size_t size = dht_size(m->dht);
235 VLA(uint8_t, buffer, size + 2 * extra);
236 memset(buffer, 0xCD, extra);
237 memset(buffer + extra + size, 0xCD, extra);
238 dht_save(m->dht, buffer + extra);
239
240 for (size_t i = 0; i < extra; i++) {
241 ck_assert_msg(buffer[i] == 0xCD, "Buffer underwritten from dht_save() @%u", (unsigned)i);
242 ck_assert_msg(buffer[extra + size + i] == 0xCD, "Buffer overwritten from dht_save() @%u", (unsigned)i);
243 }
244
245 const int res = dht_load(m->dht, buffer + extra, size);
246
247 if (res == -1) {
248 ck_assert_msg(res == 0, "Failed to load back stored buffer: res == -1");
249 } else {
250 const size_t offset = res >> 4;
251 const uint8_t *ptr = buffer + extra + offset;
252 ck_assert_msg(res == 0, "Failed to load back stored buffer: 0x%02x%02x%02x%02x%02x%02x%02x%02x @%u/%u, code %d",
253 ptr[-2], ptr[-1], ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5],
254 (unsigned)offset, (unsigned)size, res & 0x0F);
255 }
256
257 const size_t size2 = dht_size(m->dht);
258 ck_assert_msg(size == size2, "Messenger \"grew\" in size from a store/load cycle: %u -> %u", (unsigned)size,
259 (unsigned)size2);
260
261 VLA(uint8_t, buffer2, size2);
262 dht_save(m->dht, buffer2);
263
264 ck_assert_msg(!memcmp(buffer + extra, buffer2, size), "DHT state changed by store/load/store cycle");
265 }
266 END_TEST
267
messenger_suite(void)268 static Suite *messenger_suite(void)
269 {
270 Suite *s = suite_create("Messenger");
271
272 DEFTESTCASE(dht_state_saveloadsave);
273
274 DEFTESTCASE(getself_name);
275 DEFTESTCASE(m_get_userstatus_size);
276 DEFTESTCASE(m_set_userstatus);
277
278 if (enable_broken_tests) {
279 DEFTESTCASE(m_addfriend);
280 }
281
282 DEFTESTCASE(m_friend_exists);
283 DEFTESTCASE(m_get_friend_connectionstatus);
284 DEFTESTCASE(m_delfriend);
285
286 DEFTESTCASE(setname);
287 DEFTESTCASE(getname);
288 DEFTESTCASE(m_sendmesage);
289
290 return s;
291 }
292
main(void)293 int main(void)
294 {
295 setvbuf(stdout, nullptr, _IONBF, 0);
296
297 friend_id = hex_string_to_bin(friend_id_str);
298 good_id = hex_string_to_bin(good_id_str);
299 bad_id = hex_string_to_bin(bad_id_str);
300
301 Mono_Time *mono_time = mono_time_new();
302
303 /* IPv6 status from global define */
304 Messenger_Options options = {0};
305 options.ipv6enabled = TOX_ENABLE_IPV6_DEFAULT;
306 options.port_range[0] = 41234;
307 options.port_range[1] = 44234;
308 options.log_callback = (logger_cb *)print_debug_log;
309 m = new_messenger(mono_time, &options, nullptr);
310
311 /* setup a default friend and friendnum */
312 if (m_addfriend_norequest(m, friend_id) < 0) {
313 fputs("m_addfriend_norequest() failed on a valid ID!\n"
314 "this was CRITICAL to the test, and the build WILL fail.\n"
315 "the tests will continue now...\n\n", stderr);
316 }
317
318 if ((friend_id_num = getfriend_id(m, friend_id)) < 0) {
319 fputs("getfriend_id() failed on a valid ID!\n"
320 "this was CRITICAL to the test, and the build WILL fail.\n"
321 "the tests will continue now...\n\n", stderr);
322 }
323
324 Suite *messenger = messenger_suite();
325 SRunner *test_runner = srunner_create(messenger);
326 int number_failed = 0;
327
328 srunner_run_all(test_runner, CK_NORMAL);
329 number_failed = srunner_ntests_failed(test_runner);
330
331 srunner_free(test_runner);
332 free(friend_id);
333 free(good_id);
334 free(bad_id);
335
336 kill_messenger(m);
337 mono_time_free(mono_time);
338
339 return number_failed;
340 }
341