1// Copyright 2018 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <mdns/mdns.h> 6 7#include <errno.h> 8#include <unittest/unittest.h> 9 10// Test values. 11const uint8_t kRdata[4] = {0xA, 0xB, 0xC}; 12const char kRrName[] = "test_rr"; 13 14// Test state manages sample values for testing. It is always initialized the 15// same way. 16// 17// TODO(kjharland): Rector existing tests to use this and reduce boilerplate. 18struct test_data { 19 bool reset() { 20 // Init message. 21 mdns_init_message(&message); 22 23 // Init rr. 24 strcpy(rr.name, kRrName); 25 rr.type = RR_TYPE_AAAA; 26 rr.clazz = RR_CLASS_IN; 27 rr.rdata = const_cast<uint8_t*>(kRdata); 28 rr.rdlength = sizeof(kRdata); 29 rr.ttl = 42; 30 return true; 31 } 32 33 mdns_message message; 34 mdns_rr rr; 35}; 36 37// Returns true iff the given question has the given expected properties. 38static bool verify_question(mdns_question* q, char* domain, uint16_t qtype, uint16_t qclass) { 39 ASSERT_STR_EQ(q->domain, domain, "question has incorrect domain"); 40 ASSERT_EQ(q->qtype, qtype, "question has incorrect type"); 41 ASSERT_EQ(q->qclass, qclass, "question has incorrect class"); 42 return true; 43} 44 45// Returns true iff the given resource record has the given expected properties. 46static bool verify_rr(mdns_rr* rr, 47 char* name, 48 uint16_t type, 49 uint16_t clazz, 50 uint8_t* rdata, 51 uint16_t rdlength, 52 uint32_t ttl) { 53 ASSERT_STR_EQ(rr->name, name, "rr has incorrect name"); 54 ASSERT_EQ(rr->type, type, "rr has incorrect type"); 55 ASSERT_EQ(rr->clazz, clazz, "rr has incorrect class"); 56 ASSERT_EQ(rr->rdlength, rdlength, "rr has incorrect rdlength"); 57 ASSERT_EQ(rr->ttl, ttl, "rr has incorrect ttl"); 58 return true; 59} 60 61// Returns true iff the given message is zeroed out. 62static bool verify_message_is_zeroed(mdns_message* message) { 63 ASSERT_EQ(message->header.id, 0, "id should be zero"); 64 ASSERT_EQ(message->header.flags, 0, "flags should be zero"); 65 ASSERT_EQ(message->header.qd_count, 0, "question count should be zero"); 66 ASSERT_EQ(message->header.an_count, 0, "answer count should be zero"); 67 ASSERT_EQ(message->header.ns_count, 0, "name server count should be zero"); 68 ASSERT_EQ(message->header.ar_count, 0, "addition resource count should be zero"); 69 ASSERT_NULL(message->questions, "questions should be null"); 70 ASSERT_NULL(message->answers, "answers should be null"); 71 ASSERT_NULL(message->authorities, "authorities should be null"); 72 return true; 73} 74 75static bool test_mdns_init_message(void) { 76 BEGIN_TEST; 77 78 mdns_message message; 79 mdns_init_message(&message); 80 EXPECT_TRUE(verify_message_is_zeroed(&message), "message was not zeroed"); 81 mdns_free_message(&message); 82 83 END_TEST; 84} 85 86static bool test_mdns_add_first_question(void) { 87 BEGIN_TEST; 88 89 mdns_message message; 90 mdns_init_message(&message); 91 EXPECT_EQ(message.header.qd_count, 0, "question count should be zero"); 92 EXPECT_NULL(message.questions, "questions should be null"); 93 94 char domain[20] = "https://fuchsia.com"; 95 uint16_t qtype = 0x1234; 96 uint16_t qclass = 0xABCD; 97 98 int retval = mdns_add_question(&message, domain, qtype, qclass); 99 EXPECT_EQ(retval, 0, "should return zero if no error"); 100 EXPECT_EQ(message.header.qd_count, 1, "question count should be one"); 101 EXPECT_TRUE(verify_question(message.questions, domain, qtype, qclass)); 102 EXPECT_NULL(message.questions->next, "last question next ptr should be NULL"); 103 104 mdns_free_message(&message); 105 106 END_TEST; 107} 108 109static bool test_mdns_add_nth_question(void) { 110 BEGIN_TEST; 111 112 mdns_message message; 113 mdns_init_message(&message); 114 EXPECT_EQ(message.header.qd_count, 0, "question count should be zero"); 115 EXPECT_NULL(message.questions, "questions should be null"); 116 117 char domain[20] = "https://fuchsia.com"; 118 uint16_t qtypeA = 0x1234; 119 uint16_t qclassA = 0xABCD; 120 121 int retval = mdns_add_question(&message, domain, qtypeA, qclassA); 122 EXPECT_EQ(retval, 0, "should return zero if no error"); 123 124 message.header.qd_count = 4; // Fiddle with header to ensure it's reset. 125 uint16_t qtypeB = 0x1235; 126 uint16_t qclassB = 0xABCE; 127 retval = mdns_add_question(&message, domain, qtypeB, qclassB); 128 EXPECT_EQ(retval, 0, "should return zero if no error"); 129 130 EXPECT_EQ(message.header.qd_count, 2, "question count should be two"); 131 EXPECT_TRUE(verify_question(message.questions, domain, qtypeA, qclassA)); 132 EXPECT_NONNULL(message.questions->next, "non-last question next ptr should not be NULL"); 133 EXPECT_TRUE(verify_question(message.questions->next, domain, qtypeB, qclassB)); 134 EXPECT_NULL(message.questions->next->next, "last question next ptr should be NULL"); 135 136 mdns_free_message(&message); 137 138 END_TEST; 139} 140 141static bool test_mdns_add_domain_too_long(void) { 142 BEGIN_TEST; 143 144 mdns_message message; 145 mdns_init_message(&message); 146 147 uint16_t qtype = 0x1234; 148 uint16_t qclass = 0xABCD; 149 150 char domain[MAX_DOMAIN_LENGTH + 1]; 151 memset(domain, 1, MAX_DOMAIN_LENGTH + 1); 152 153 int retval = mdns_add_question(&message, domain, qtype, qclass); 154 EXPECT_EQ(retval, -1, "should return -1 on error"); 155 EXPECT_EQ(errno, ENAMETOOLONG, "errno should be ENAMETOOLONG"); 156 EXPECT_NULL(message.questions, "question should not have been added on error"); 157 158 mdns_free_message(&message); 159 160 END_TEST; 161} 162 163static bool test_mdns_add_first_answer(void) { 164 BEGIN_TEST; 165 166 test_data t; 167 t.reset(); 168 169 int retval = mdns_add_answer(&t.message, t.rr.name, t.rr.type, t.rr.clazz, 170 t.rr.rdata, t.rr.rdlength, t.rr.ttl); 171 EXPECT_EQ(retval, 0, "should return zero if no error"); 172 EXPECT_NONNULL(t.message.answers, "answer was not added"); 173 EXPECT_EQ(t.message.header.an_count, 1, "answer count should be one"); 174 EXPECT_TRUE(verify_rr(t.message.answers, t.rr.name, t.rr.type, t.rr.clazz, 175 t.rr.rdata, t.rr.rdlength, t.rr.ttl)); 176 177 END_TEST; 178} 179 180static bool test_mdns_add_nth_answer(void) { 181 BEGIN_TEST; 182 183 test_data t; 184 t.reset(); 185 186 int retval = mdns_add_answer(&t.message, t.rr.name, t.rr.type, t.rr.clazz, 187 t.rr.rdata, t.rr.rdlength, t.rr.ttl); 188 EXPECT_EQ(retval, 0, "should return zero if no error"); 189 190 char other_name[] = "other name"; 191 uint16_t other_type = RR_TYPE_A; 192 uint16_t other_clazz = RR_CLASS_IN; 193 uint8_t other_rdata[] = {t.rr.rdata[0]}; 194 uint16_t other_rdlength = sizeof(other_rdata) / sizeof(uint8_t); 195 uint32_t other_ttl = t.rr.ttl + 1; 196 retval = mdns_add_answer(&t.message, other_name, other_type, other_clazz, 197 other_rdata, other_rdlength, other_ttl); 198 EXPECT_NONNULL(t.message.answers, "answer was not added"); 199 EXPECT_EQ(t.message.header.an_count, 2, "answer count should be two"); 200 201 EXPECT_TRUE(verify_rr(t.message.answers, t.rr.name, t.rr.type, t.rr.clazz, 202 t.rr.rdata, t.rr.rdlength, t.rr.ttl)); 203 204 EXPECT_NONNULL(t.message.answers->next, "second answer was not added"); 205 EXPECT_TRUE(verify_rr(t.message.answers->next, other_name, other_type, 206 other_clazz, other_rdata, other_rdlength, other_ttl)); 207 EXPECT_NULL(t.message.answers->next->next, 208 "second answer nextptr should be null"); 209 210 END_TEST; 211} 212 213static bool test_mdns_add_answer_bad_rr_type(void) { 214 BEGIN_TEST; 215 216 test_data t; 217 t.reset(); 218 t.rr.type = (uint16_t)(RR_TYPE_A + 1); // Unsupported record type. 219 int retval = mdns_add_answer(&t.message, t.rr.name, t.rr.type, t.rr.clazz, 220 t.rr.rdata, t.rr.rdlength, t.rr.ttl); 221 EXPECT_EQ(errno, EINVAL, "errno should be EINVAL when given bad rr type"); 222 EXPECT_EQ(retval, -1, "should return value < zero on error"); 223 EXPECT_NULL(t.message.answers, "should not have added answer to message"); 224 EXPECT_EQ(t.message.header.an_count, 0, "answer count should be zero"); 225 226 END_TEST; 227} 228 229static bool test_mdns_add_answer_bad_rr_class(void) { 230 BEGIN_TEST; 231 232 test_data t; 233 t.reset(); 234 t.rr.clazz = (uint16_t)(RR_CLASS_IN + 1); // Unsupported record class. 235 int retval = mdns_add_answer(&t.message, t.rr.name, t.rr.type, t.rr.clazz, 236 t.rr.rdata, t.rr.rdlength, t.rr.ttl); 237 EXPECT_EQ(errno, EINVAL, "errno should be EINVAL when given bad rr class"); 238 EXPECT_EQ(retval, -1, "should return value < zero on error"); 239 EXPECT_NULL(t.message.answers, "should not have added answer to message"); 240 EXPECT_EQ(t.message.header.an_count, 0, "answer count should be zero"); 241 242 END_TEST; 243} 244 245static bool test_mdns_add_first_authority(void) { 246 BEGIN_TEST; 247 248 test_data t; 249 t.reset(); 250 251 int retval = mdns_add_authority(&t.message, t.rr.name, t.rr.type, t.rr.clazz, 252 t.rr.rdata, t.rr.rdlength, t.rr.ttl); 253 EXPECT_EQ(retval, 0, "should return zero if no error"); 254 EXPECT_NONNULL(t.message.authorities, "authority was not added"); 255 EXPECT_EQ(t.message.header.ns_count, 1, "authority count should be one"); 256 EXPECT_TRUE(verify_rr(t.message.authorities, t.rr.name, t.rr.type, t.rr.clazz, 257 t.rr.rdata, t.rr.rdlength, t.rr.ttl)); 258 259 END_TEST; 260} 261 262static bool test_mdns_add_nth_authority(void) { 263 BEGIN_TEST; 264 265 test_data t; 266 t.reset(); 267 268 int retval = mdns_add_authority(&t.message, t.rr.name, t.rr.type, t.rr.clazz, 269 t.rr.rdata, t.rr.rdlength, t.rr.ttl); 270 EXPECT_EQ(retval, 0, "should return zero if no error"); 271 272 char other_name[] = "other name"; 273 uint16_t other_type = RR_TYPE_A; 274 uint16_t other_clazz = RR_CLASS_IN; 275 uint8_t other_rdata[] = {t.rr.rdata[0]}; 276 uint16_t other_rdlength = sizeof(other_rdata) / sizeof(uint8_t); 277 uint32_t other_ttl = t.rr.ttl + 1; 278 retval = mdns_add_authority(&t.message, other_name, other_type, other_clazz, 279 other_rdata, other_rdlength, other_ttl); 280 EXPECT_NONNULL(t.message.authorities, "authority was not added"); 281 EXPECT_EQ(t.message.header.ns_count, 2, "authority count should be two"); 282 283 EXPECT_TRUE(verify_rr(t.message.authorities, t.rr.name, t.rr.type, t.rr.clazz, 284 t.rr.rdata, t.rr.rdlength, t.rr.ttl)); 285 286 EXPECT_NONNULL(t.message.authorities->next, "second authority was not added"); 287 EXPECT_TRUE(verify_rr(t.message.authorities->next, other_name, other_type, 288 other_clazz, other_rdata, other_rdlength, other_ttl)); 289 EXPECT_NULL(t.message.authorities->next->next, 290 "second authority nextptr should be null"); 291 292 END_TEST; 293} 294 295static bool test_mdns_add_authority_bad_rr_type(void) { 296 BEGIN_TEST; 297 298 test_data t; 299 t.reset(); 300 t.rr.type = (uint16_t)(RR_TYPE_A + 1); // Unsupported record type. 301 int retval = mdns_add_authority(&t.message, t.rr.name, t.rr.type, t.rr.clazz, 302 t.rr.rdata, t.rr.rdlength, t.rr.ttl); 303 EXPECT_EQ(errno, EINVAL, "errno should be EINVAL when given bad rr type"); 304 EXPECT_EQ(retval, -1, "should return value < zero on error"); 305 EXPECT_NULL(t.message.authorities, "should not have added authority to message"); 306 EXPECT_EQ(t.message.header.ns_count, 0, "authority count should be zero"); 307 308 END_TEST; 309} 310 311static bool test_mdns_add_authority_bad_rr_class(void) { 312 BEGIN_TEST; 313 314 test_data t; 315 t.reset(); 316 t.rr.clazz = (uint16_t)(RR_CLASS_IN + 1); // Unsupported record class. 317 int retval = mdns_add_authority(&t.message, t.rr.name, t.rr.type, t.rr.clazz, 318 t.rr.rdata, t.rr.rdlength, t.rr.ttl); 319 EXPECT_EQ(errno, EINVAL, "errno should be EINVAL when given bad rr class"); 320 EXPECT_EQ(retval, -1, "should return value < zero on error"); 321 EXPECT_NULL(t.message.authorities, "should not have added authority to message"); 322 EXPECT_EQ(t.message.header.ns_count, 0, "authority count should be zero"); 323 324 END_TEST; 325} 326 327static bool test_mdns_add_first_additional(void) { 328 BEGIN_TEST; 329 330 test_data t; 331 t.reset(); 332 333 int retval = mdns_add_additional(&t.message, t.rr.name, t.rr.type, t.rr.clazz, 334 t.rr.rdata, t.rr.rdlength, t.rr.ttl); 335 EXPECT_EQ(retval, 0, "should return zero if no error"); 336 EXPECT_NONNULL(t.message.additionals, "additional was not added"); 337 EXPECT_EQ(t.message.header.ar_count, 1, "additional count should be one"); 338 EXPECT_TRUE(verify_rr(t.message.additionals, t.rr.name, t.rr.type, t.rr.clazz, 339 t.rr.rdata, t.rr.rdlength, t.rr.ttl)); 340 341 END_TEST; 342} 343 344static bool test_mdns_add_nth_additional(void) { 345 BEGIN_TEST; 346 347 test_data t; 348 t.reset(); 349 350 int retval = mdns_add_additional(&t.message, t.rr.name, t.rr.type, t.rr.clazz, 351 t.rr.rdata, t.rr.rdlength, t.rr.ttl); 352 EXPECT_EQ(retval, 0, "should return zero if no error"); 353 354 char other_name[] = "other name"; 355 uint16_t other_type = RR_TYPE_A; 356 uint16_t other_clazz = RR_CLASS_IN; 357 uint8_t other_rdata[] = {t.rr.rdata[0]}; 358 uint16_t other_rdlength = sizeof(other_rdata) / sizeof(uint8_t); 359 uint32_t other_ttl = t.rr.ttl + 1; 360 retval = mdns_add_additional(&t.message, other_name, other_type, other_clazz, 361 other_rdata, other_rdlength, other_ttl); 362 EXPECT_NONNULL(t.message.additionals, "additional was not added"); 363 EXPECT_EQ(t.message.header.ar_count, 2, "additional count should be two"); 364 365 EXPECT_TRUE(verify_rr(t.message.additionals, t.rr.name, t.rr.type, t.rr.clazz, 366 t.rr.rdata, t.rr.rdlength, t.rr.ttl)); 367 368 EXPECT_NONNULL(t.message.additionals->next, "second additional was not added"); 369 EXPECT_TRUE(verify_rr(t.message.additionals->next, other_name, other_type, 370 other_clazz, other_rdata, other_rdlength, other_ttl)); 371 EXPECT_NULL(t.message.additionals->next->next, 372 "second additional nextptr should be null"); 373 374 END_TEST; 375} 376 377static bool test_mdns_add_additional_bad_rr_type(void) { 378 BEGIN_TEST; 379 380 test_data t; 381 t.reset(); 382 t.rr.type = (uint16_t)(RR_TYPE_A + 1); // Unsupported record type. 383 int retval = mdns_add_additional(&t.message, t.rr.name, t.rr.type, t.rr.clazz, 384 t.rr.rdata, t.rr.rdlength, t.rr.ttl); 385 EXPECT_EQ(errno, EINVAL, "errno should be EINVAL when given bad rr type"); 386 EXPECT_EQ(retval, -1, "should return value < zero on error"); 387 EXPECT_NULL(t.message.additionals, "should not have added additional to message"); 388 EXPECT_EQ(t.message.header.ar_count, 0, "additional count should be zero"); 389 390 END_TEST; 391} 392 393static bool test_mdns_add_additional_bad_rr_class(void) { 394 BEGIN_TEST; 395 396 test_data t; 397 t.reset(); 398 t.rr.clazz = (uint16_t)(RR_CLASS_IN + 1); // Unsupported record class. 399 int retval = mdns_add_additional(&t.message, t.rr.name, t.rr.type, t.rr.clazz, 400 t.rr.rdata, t.rr.rdlength, t.rr.ttl); 401 EXPECT_EQ(errno, EINVAL, "errno should be EINVAL when given bad rr class"); 402 EXPECT_EQ(retval, -1, "should return value < zero on error"); 403 EXPECT_NULL(t.message.additionals, "should not have added additional to message"); 404 EXPECT_EQ(t.message.header.ar_count, 0, "additional count should be zero"); 405 406 END_TEST; 407} 408 409static bool test_mdns_free_message(void) { 410 BEGIN_TEST; 411 412 mdns_message message; 413 mdns_init_message(&message); 414 415 char domain[20] = "https://fuchsia.com"; 416 int retval = mdns_add_question(&message, domain, 0, 0); 417 EXPECT_EQ(retval, 0, "should return zero if no error"); 418 retval = mdns_add_question(&message, domain, 0, 0); 419 EXPECT_EQ(retval, 0, "should return zero if no error"); 420 421 // Double check questions were successfully added. 422 EXPECT_NONNULL(message.questions, "first question was not added"); 423 EXPECT_NONNULL(message.questions->next, "second question was not added"); 424 425 mdns_free_message(&message); 426 EXPECT_TRUE(verify_message_is_zeroed(&message), "message was not zeroed"); 427 428 END_TEST; 429} 430 431static bool test_mdns_unmarshal_incomplete_header(void) { 432 BEGIN_TEST; 433 434 mdns_message message; 435 uint16_t encoded_message[] = {0}; 436 437 // pass buf_len value smaller than MDNS_HEADER_SIZE to indicate the full 438 // message did not fit into the provided buffer. 439 int retval = mdns_unmarshal(encoded_message, MDNS_HEADER_SIZE - 1, &message); 440 EXPECT_LT(retval, 0, "should have returned a negative value on error"); 441 EXPECT_TRUE(verify_message_is_zeroed(&message), 442 "message was mutated even though decoding failed"); 443 444 retval = mdns_unmarshal(encoded_message, MDNS_HEADER_SIZE - 5, &message); 445 EXPECT_LT(retval, 0, "should have returned a negative value on error"); 446 EXPECT_TRUE(verify_message_is_zeroed(&message), 447 "message was mutated even though decoding failed"); 448 449 retval = mdns_unmarshal(encoded_message, 0, &message); 450 EXPECT_LT(retval, 0, "should have returned a negative value on error"); 451 EXPECT_TRUE(verify_message_is_zeroed(&message), 452 "message was mutated even though decoding failed"); 453 454 END_TEST; 455} 456 457static bool test_mdns_unmarshal_empty_message(void) { 458 BEGIN_TEST; 459 460 mdns_message message; 461 462 // Completely empty message. 463 uint16_t encoded_message_1[] = { 464 // Header section 465 0, // ID 466 0, // Flags section 467 0, // Question count 468 0, // Answer count 469 0, // Authority count 470 0, // Additionals count 471 // No message content. 472 }; 473 474 int retval = mdns_unmarshal(encoded_message_1, MDNS_HEADER_SIZE, &message); 475 EXPECT_EQ(retval, MDNS_HEADER_SIZE, "should have read 12 bytes"); 476 EXPECT_TRUE(verify_message_is_zeroed(&message), 477 "message is not zeroed even though input data was empty"); 478 479 // Message with ID and flags but still "empty" because no questions or 480 // records are inside. 481 uint16_t encoded_message_2[] = { 482 // Header section 483 0xABCD, // ID 484 0xCDEF, // Flags section 485 0, // Question count 486 0, // Answer count 487 0, // Authority count 488 0, // Additionals count 489 // No message content. 490 }; 491 492 retval = mdns_unmarshal(encoded_message_2, MDNS_HEADER_SIZE, &message); 493 EXPECT_EQ(retval, MDNS_HEADER_SIZE, "should have read 12 bytes"); 494 EXPECT_EQ(message.header.id, 0xABCD, "ID should be 0xABCD (171)"); 495 EXPECT_EQ(message.header.flags, 0xCDEF, "flags should be 0xCDEF (205)"); 496 EXPECT_EQ(message.header.qd_count, 0, "question count should be 0"); 497 EXPECT_EQ(message.header.qd_count, 0, "answer count should be 0"); 498 EXPECT_EQ(message.header.qd_count, 0, "authority count should be 0"); 499 EXPECT_EQ(message.header.qd_count, 0, "additionals count should be 0"); 500 EXPECT_NULL(message.questions, "questions should be null"); 501 EXPECT_NULL(message.answers, "answers should be null"); 502 EXPECT_NULL(message.authorities, "authorities should be null"); 503 EXPECT_NULL(message.additionals, "additionals should be null"); 504 END_TEST; 505} 506 507BEGIN_TEST_CASE(mdns_free_message) 508RUN_TEST(test_mdns_free_message) 509END_TEST_CASE(mdns_free_message) 510 511BEGIN_TEST_CASE(mdns_init_message) 512RUN_TEST(test_mdns_init_message) 513END_TEST_CASE(mdns_init_message) 514 515BEGIN_TEST_CASE(mdns_add_question) 516RUN_TEST(test_mdns_add_first_question) 517RUN_TEST(test_mdns_add_nth_question) 518RUN_TEST(test_mdns_add_domain_too_long) 519END_TEST_CASE(mdns_add_question) 520 521BEGIN_TEST_CASE(mdns_add_answer) 522RUN_TEST(test_mdns_add_first_answer) 523RUN_TEST(test_mdns_add_nth_answer) 524RUN_TEST(test_mdns_add_answer_bad_rr_type) 525RUN_TEST(test_mdns_add_answer_bad_rr_class) 526END_TEST_CASE(mdns_add_answer) 527 528BEGIN_TEST_CASE(mdns_add_authority) 529RUN_TEST(test_mdns_add_first_authority) 530RUN_TEST(test_mdns_add_nth_authority) 531RUN_TEST(test_mdns_add_authority_bad_rr_type) 532RUN_TEST(test_mdns_add_authority_bad_rr_class) 533END_TEST_CASE(mdns_add_authority) 534 535BEGIN_TEST_CASE(mdns_add_additional) 536RUN_TEST(test_mdns_add_first_additional) 537RUN_TEST(test_mdns_add_nth_additional) 538RUN_TEST(test_mdns_add_additional_bad_rr_type) 539RUN_TEST(test_mdns_add_additional_bad_rr_class) 540END_TEST_CASE(mdns_add_additional) 541 542BEGIN_TEST_CASE(test_mdns_unmarshal) 543RUN_TEST(test_mdns_unmarshal_incomplete_header) 544RUN_TEST(test_mdns_unmarshal_empty_message) 545END_TEST_CASE(test_mdns_unmarshal) 546 547int main(int argc, char* argv[]) { 548 return unittest_run_all_tests(argc, argv) ? 0 : -1; 549} 550