test_confparse.c (31744B)
1 /* Copyright (c) 2001-2004, Roger Dingledine. 2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. 3 * Copyright (c) 2007-2021, The Tor Project, Inc. */ 4 /* See LICENSE for licensing information */ 5 6 /* 7 * Tests for confmgt.c module that we use to parse various 8 * configuration/state file types. 9 */ 10 11 #define CONFMGT_PRIVATE 12 #include "orconfig.h" 13 14 #include "core/or/or.h" 15 #include "lib/encoding/confline.h" 16 #include "feature/nodelist/routerset.h" 17 #include "lib/confmgt/confmgt.h" 18 #include "test/test.h" 19 #include "test/log_test_helpers.h" 20 21 #include "lib/confmgt/unitparse.h" 22 23 typedef struct test_struct_t { 24 uint32_t magic; 25 char *s; 26 char *fn; 27 int pos; 28 int i; 29 int deprecated_int; 30 uint64_t u64; 31 int interval; 32 int msec_interval; 33 uint64_t mem; 34 double dbl; 35 int boolean; 36 int autobool; 37 time_t time; 38 smartlist_t *csv; 39 int csv_interval; 40 config_line_t *lines; 41 config_line_t *mixed_lines; 42 routerset_t *routerset; 43 int hidden_int; 44 config_line_t *mixed_hidden_lines; 45 46 config_line_t *extra_lines; 47 } test_struct_t; 48 49 static test_struct_t test_struct_t_dummy; 50 51 #define VAR(varname,conftype,member,initvalue) \ 52 CONFIG_VAR_ETYPE(test_struct_t, varname, conftype, member, 0, initvalue) 53 #define V(member,conftype,initvalue) \ 54 VAR(#member, conftype, member, initvalue) 55 #define OBSOLETE(varname) \ 56 CONFIG_VAR_OBSOLETE(varname) 57 58 static const config_var_t test_vars[] = { 59 V(s, STRING, "hello"), 60 V(fn, FILENAME, NULL), 61 V(pos, POSINT, NULL), 62 V(i, INT, "-10"), 63 V(deprecated_int, INT, "3"), 64 V(u64, UINT64, NULL), 65 V(interval, INTERVAL, "10 seconds"), 66 V(msec_interval, MSEC_INTERVAL, "150 msec"), 67 V(mem, MEMUNIT, "10 MB"), 68 V(dbl, DOUBLE, NULL), 69 V(boolean, BOOL, "0"), 70 V(autobool, AUTOBOOL, "auto"), 71 V(time, ISOTIME, NULL), 72 V(csv, CSV, NULL), 73 V(csv_interval, CSV_INTERVAL, "5 seconds"), 74 V(lines, LINELIST, NULL), 75 VAR("MixedLines", LINELIST_V, mixed_lines, NULL), 76 VAR("LineTypeA", LINELIST_S, mixed_lines, NULL), 77 VAR("LineTypeB", LINELIST_S, mixed_lines, NULL), 78 OBSOLETE("obsolete"), 79 { 80 .member = { .name = "routerset", 81 .type = CONFIG_TYPE_EXTENDED, 82 .type_def = &ROUTERSET_type_defn, 83 .offset = offsetof(test_struct_t, routerset), 84 }, 85 }, 86 VAR("__HiddenInt", POSINT, hidden_int, "0"), 87 VAR("MixedHiddenLines", LINELIST_V, mixed_hidden_lines, NULL), 88 VAR("__HiddenLineA", LINELIST_S, mixed_hidden_lines, NULL), 89 VAR("VisibleLineB", LINELIST_S, mixed_hidden_lines, NULL), 90 91 END_OF_CONFIG_VARS, 92 }; 93 94 static config_abbrev_t test_abbrevs[] = { 95 { "uint", "pos", 0, 0 }, 96 { "float", "dbl", 0, 1 }, 97 { NULL, NULL, 0, 0 } 98 }; 99 100 static config_deprecation_t test_deprecation_notes[] = { 101 { "deprecated_int", "This integer is deprecated." }, 102 { NULL, NULL } 103 }; 104 105 static int 106 test_validate_cb(const void *old_options, void *options, char **msg) 107 { 108 (void)old_options; 109 (void)msg; 110 test_struct_t *ts = options; 111 112 if (ts->i == 0xbad) { 113 *msg = tor_strdup("bad value for i"); 114 return -1; 115 } 116 return 0; 117 } 118 119 #define TEST_MAGIC 0x1337 120 121 static const config_format_t test_fmt = { 122 .size = sizeof(test_struct_t), 123 .magic = { 124 "test_struct_t", 125 TEST_MAGIC, 126 offsetof(test_struct_t, magic), 127 }, 128 .abbrevs = test_abbrevs, 129 .deprecations = test_deprecation_notes, 130 .vars = test_vars, 131 .legacy_validate_fn = test_validate_cb, 132 }; 133 134 /* Make sure that config_init sets everything to the right defaults. */ 135 static void 136 test_confparse_init(void *arg) 137 { 138 (void)arg; 139 config_mgr_t *mgr = config_mgr_new(&test_fmt); 140 config_mgr_freeze(mgr); 141 test_struct_t *tst = config_new(mgr); 142 config_init(mgr, tst); 143 144 // Make sure that options are initialized right. */ 145 tt_str_op(tst->s, OP_EQ, "hello"); 146 tt_ptr_op(tst->fn, OP_EQ, NULL); 147 tt_int_op(tst->pos, OP_EQ, 0); 148 tt_int_op(tst->i, OP_EQ, -10); 149 tt_int_op(tst->deprecated_int, OP_EQ, 3); 150 tt_u64_op(tst->u64, OP_EQ, 0); 151 tt_int_op(tst->interval, OP_EQ, 10); 152 tt_int_op(tst->msec_interval, OP_EQ, 150); 153 tt_u64_op(tst->mem, OP_EQ, 10 * 1024 * 1024); 154 tt_double_op(tst->dbl, OP_LT, .0000000001); 155 tt_double_op(tst->dbl, OP_GT, -0.0000000001); 156 tt_int_op(tst->boolean, OP_EQ, 0); 157 tt_int_op(tst->autobool, OP_EQ, -1); 158 tt_i64_op(tst->time, OP_EQ, 0); 159 tt_ptr_op(tst->csv, OP_EQ, NULL); 160 tt_int_op(tst->csv_interval, OP_EQ, 5); 161 tt_ptr_op(tst->lines, OP_EQ, NULL); 162 tt_ptr_op(tst->mixed_lines, OP_EQ, NULL); 163 tt_int_op(tst->hidden_int, OP_EQ, 0); 164 165 done: 166 config_free(mgr, tst); 167 config_mgr_free(mgr); 168 } 169 170 static const char simple_settings[] = 171 "s this is a \n" 172 "fn /simple/test of the\n" 173 "uint 77\n" // this is an abbrev 174 "i 3\n" 175 "u64 1000000000000 \n" 176 "interval 5 minutes \n" 177 "msec_interval 5 minutes \n" 178 "mem 10\n" 179 "dbl 6.060842\n" 180 "BOOLEAN 1\n" 181 "aUtObOOl 0\n" 182 "time 2019-06-14 13:58:51\n" 183 "csv configuration, parsing , system \n" 184 "csv_interval 10 seconds, 5 seconds, 10 hours\n" 185 "lines hello\n" 186 "LINES world\n" 187 "linetypea i d\n" 188 "linetypeb i c\n" 189 "routerset $FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\n" 190 "__hiddenint 11\n" 191 "__hiddenlineA XYZ\n" 192 "visiblelineB ABC\n"; 193 194 /* Return a configuration object set up from simple_settings above. */ 195 static test_struct_t * 196 get_simple_config(const config_mgr_t *mgr) 197 { 198 test_struct_t *result = NULL; 199 test_struct_t *tst = config_new(mgr); 200 config_line_t *lines = NULL; 201 char *msg = NULL; 202 203 config_init(mgr, tst); 204 205 int r = config_get_lines(simple_settings, &lines, 0); 206 tt_int_op(r, OP_EQ, 0); 207 r = config_assign(mgr, tst, lines, 0, &msg); 208 tt_int_op(r, OP_EQ, 0); 209 tt_ptr_op(msg, OP_EQ, NULL); 210 211 result = tst; 212 tst = NULL; // prevent free 213 done: 214 tor_free(msg); 215 config_free_lines(lines); 216 config_free(mgr, tst); 217 return result; 218 } 219 220 /* Make sure that config_assign can parse things. */ 221 static void 222 test_confparse_assign_simple(void *arg) 223 { 224 (void)arg; 225 config_mgr_t *mgr = config_mgr_new(&test_fmt); 226 config_mgr_freeze(mgr); 227 test_struct_t *tst = get_simple_config(mgr); 228 229 tt_str_op(tst->s, OP_EQ, "this is a"); 230 tt_str_op(tst->fn, OP_EQ, "/simple/test of the"); 231 tt_int_op(tst->pos, OP_EQ, 77); 232 tt_int_op(tst->i, OP_EQ, 3); 233 tt_int_op(tst->deprecated_int, OP_EQ, 3); 234 tt_u64_op(tst->u64, OP_EQ, UINT64_C(1000000000000)); 235 tt_int_op(tst->interval, OP_EQ, 5 * 60); 236 tt_int_op(tst->msec_interval, OP_EQ, 5 * 60 * 1000); 237 tt_u64_op(tst->mem, OP_EQ, 10); 238 tt_double_op(tst->dbl, OP_LT, 6.060843); 239 tt_double_op(tst->dbl, OP_GT, 6.060841); 240 tt_int_op(tst->boolean, OP_EQ, 1); 241 tt_int_op(tst->autobool, OP_EQ, 0); 242 tt_i64_op(tst->time, OP_EQ, 1560520731); 243 tt_ptr_op(tst->csv, OP_NE, NULL); 244 tt_int_op(smartlist_len(tst->csv), OP_EQ, 3); 245 tt_str_op(smartlist_get(tst->csv, 0), OP_EQ, "configuration"); 246 tt_str_op(smartlist_get(tst->csv, 1), OP_EQ, "parsing"); 247 tt_str_op(smartlist_get(tst->csv, 2), OP_EQ, "system"); 248 tt_int_op(tst->csv_interval, OP_EQ, 10); 249 tt_int_op(tst->hidden_int, OP_EQ, 11); 250 251 tt_assert(tst->lines); 252 tt_str_op(tst->lines->key, OP_EQ, "lines"); 253 tt_str_op(tst->lines->value, OP_EQ, "hello"); 254 tt_assert(tst->lines->next); 255 tt_str_op(tst->lines->next->key, OP_EQ, "lines"); 256 tt_str_op(tst->lines->next->value, OP_EQ, "world"); 257 tt_assert(!tst->lines->next->next); 258 259 tt_assert(tst->mixed_lines); 260 tt_str_op(tst->mixed_lines->key, OP_EQ, "LineTypeA"); 261 tt_str_op(tst->mixed_lines->value, OP_EQ, "i d"); 262 tt_assert(tst->mixed_lines->next); 263 tt_str_op(tst->mixed_lines->next->key, OP_EQ, "LineTypeB"); 264 tt_str_op(tst->mixed_lines->next->value, OP_EQ, "i c"); 265 tt_assert(!tst->mixed_lines->next->next); 266 267 tt_assert(tst->mixed_hidden_lines); 268 tt_str_op(tst->mixed_hidden_lines->key, OP_EQ, "__HiddenLineA"); 269 tt_str_op(tst->mixed_hidden_lines->value, OP_EQ, "XYZ"); 270 tt_assert(tst->mixed_hidden_lines->next); 271 tt_str_op(tst->mixed_hidden_lines->next->key, OP_EQ, "VisibleLineB"); 272 tt_str_op(tst->mixed_hidden_lines->next->value, OP_EQ, "ABC"); 273 tt_assert(!tst->mixed_hidden_lines->next->next); 274 275 tt_assert(config_check_ok(mgr, tst, LOG_ERR)); 276 277 done: 278 config_free(mgr, tst); 279 config_mgr_free(mgr); 280 } 281 282 /* Try to assign to an obsolete option, and make sure we get a warning. */ 283 static void 284 test_confparse_assign_obsolete(void *arg) 285 { 286 (void)arg; 287 config_mgr_t *mgr = config_mgr_new(&test_fmt); 288 config_mgr_freeze(mgr); 289 test_struct_t *tst = get_simple_config(mgr); 290 config_line_t *lines = NULL; 291 char *msg = NULL; 292 293 config_init(mgr, tst); 294 295 int r = config_get_lines("obsolete option here", 296 &lines, 0); 297 tt_int_op(r, OP_EQ, 0); 298 setup_capture_of_logs(LOG_WARN); 299 r = config_assign(mgr, tst, lines, 0, &msg); 300 tt_int_op(r, OP_EQ, 0); 301 tt_ptr_op(msg, OP_EQ, NULL); 302 expect_single_log_msg_containing("Skipping obsolete configuration option"); 303 304 done: 305 teardown_capture_of_logs(); 306 config_free(mgr, tst); 307 config_free_lines(lines); 308 tor_free(msg); 309 config_mgr_free(mgr); 310 } 311 312 /* Try to assign to an deprecated option, and make sure we get a warning 313 * but the assignment works anyway. */ 314 static void 315 test_confparse_assign_deprecated(void *arg) 316 { 317 (void)arg; 318 config_mgr_t *mgr = config_mgr_new(&test_fmt); 319 config_mgr_freeze(mgr); 320 test_struct_t *tst = get_simple_config(mgr); 321 config_line_t *lines = NULL; 322 char *msg = NULL; 323 324 config_init(mgr, tst); 325 326 int r = config_get_lines("deprecated_int 7", 327 &lines, 0); 328 tt_int_op(r, OP_EQ, 0); 329 setup_capture_of_logs(LOG_WARN); 330 r = config_assign(mgr, tst, lines, CAL_WARN_DEPRECATIONS, &msg); 331 tt_int_op(r, OP_EQ, 0); 332 tt_ptr_op(msg, OP_EQ, NULL); 333 expect_single_log_msg_containing("This integer is deprecated."); 334 335 tt_int_op(tst->deprecated_int, OP_EQ, 7); 336 337 tt_assert(config_check_ok(mgr, tst, LOG_ERR)); 338 339 done: 340 teardown_capture_of_logs(); 341 config_free(mgr, tst); 342 config_free_lines(lines); 343 tor_free(msg); 344 config_mgr_free(mgr); 345 } 346 347 /* Try to re-assign an option name that has been deprecated in favor of 348 * another. */ 349 static void 350 test_confparse_assign_replaced(void *arg) 351 { 352 (void)arg; 353 config_mgr_t *mgr = config_mgr_new(&test_fmt); 354 config_mgr_freeze(mgr); 355 test_struct_t *tst = get_simple_config(mgr); 356 config_line_t *lines = NULL; 357 char *msg = NULL; 358 359 config_init(mgr, tst); 360 361 int r = config_get_lines("float 1000\n", &lines, 0); 362 tt_int_op(r, OP_EQ, 0); 363 setup_capture_of_logs(LOG_WARN); 364 r = config_assign(mgr, tst, lines, CAL_WARN_DEPRECATIONS, &msg); 365 tt_int_op(r, OP_EQ, 0); 366 tt_ptr_op(msg, OP_EQ, NULL); 367 expect_single_log_msg_containing("use 'dbl' instead."); 368 369 tt_double_op(tst->dbl, OP_GT, 999.999); 370 tt_double_op(tst->dbl, OP_LT, 1000.001); 371 372 done: 373 teardown_capture_of_logs(); 374 config_free(mgr, tst); 375 config_free_lines(lines); 376 tor_free(msg); 377 config_mgr_free(mgr); 378 } 379 380 /* Try to set a linelist value with no option. */ 381 static void 382 test_confparse_assign_emptystring(void *arg) 383 { 384 (void)arg; 385 config_mgr_t *mgr = config_mgr_new(&test_fmt); 386 config_mgr_freeze(mgr); 387 test_struct_t *tst = get_simple_config(mgr); 388 config_line_t *lines = NULL; 389 char *msg = NULL; 390 391 config_init(mgr, tst); 392 393 int r = config_get_lines("lines\n", &lines, 0); 394 tt_int_op(r, OP_EQ, 0); 395 setup_capture_of_logs(LOG_WARN); 396 r = config_assign(mgr, tst, lines, 0, &msg); 397 tt_int_op(r, OP_EQ, 0); 398 tt_ptr_op(msg, OP_EQ, NULL); 399 expect_single_log_msg_containing("has no value"); 400 401 done: 402 teardown_capture_of_logs(); 403 config_free(mgr, tst); 404 config_free_lines(lines); 405 tor_free(msg); 406 config_mgr_free(mgr); 407 } 408 409 /* Try to set a the same option twice; make sure we get a warning. */ 410 static void 411 test_confparse_assign_twice(void *arg) 412 { 413 (void)arg; 414 config_mgr_t *mgr = config_mgr_new(&test_fmt); 415 config_mgr_freeze(mgr); 416 test_struct_t *tst = get_simple_config(mgr); 417 config_line_t *lines = NULL; 418 char *msg = NULL; 419 420 config_init(mgr, tst); 421 422 int r = config_get_lines("pos 10\n" 423 "pos 99\n", &lines, 0); 424 tt_int_op(r, OP_EQ, 0); 425 setup_capture_of_logs(LOG_WARN); 426 r = config_assign(mgr, tst, lines, 0, &msg); 427 tt_int_op(r, OP_EQ, 0); 428 tt_ptr_op(msg, OP_EQ, NULL); 429 expect_single_log_msg_containing("used more than once"); 430 431 done: 432 teardown_capture_of_logs(); 433 config_free(mgr, tst); 434 config_free_lines(lines); 435 tor_free(msg); 436 config_mgr_free(mgr); 437 } 438 439 typedef struct badval_test_t { 440 const char *cfg; 441 const char *expect_msg; 442 } badval_test_t; 443 444 /* Try to set an option and make sure that we get a failure and an expected 445 * warning. */ 446 static void 447 test_confparse_assign_badval(void *arg) 448 { 449 const badval_test_t *bt = arg; 450 config_mgr_t *mgr = config_mgr_new(&test_fmt); 451 config_mgr_freeze(mgr); 452 test_struct_t *tst = get_simple_config(mgr); 453 config_line_t *lines = NULL; 454 char *msg = NULL; 455 456 config_init(mgr, tst); 457 458 int r = config_get_lines(bt->cfg, &lines, 0); 459 tt_int_op(r, OP_EQ, 0); 460 setup_capture_of_logs(LOG_WARN); 461 r = config_assign(mgr, tst, lines, 0, &msg); 462 tt_int_op(r, OP_LT, 0); 463 tt_ptr_op(msg, OP_NE, NULL); 464 if (! strstr(msg, bt->expect_msg)) { 465 TT_DIE(("'%s' did not contain '%s'" , msg, bt->expect_msg)); 466 } 467 468 done: 469 teardown_capture_of_logs(); 470 config_free(mgr, tst); 471 config_free_lines(lines); 472 tor_free(msg); 473 config_mgr_free(mgr); 474 } 475 476 /* Various arguments for badval test. 477 * 478 * Note that the expected warnings here are _very_ truncated, since we 479 * are writing these tests before a refactoring that we expect will 480 * change them. 481 */ 482 static const badval_test_t bv_notint = { "pos X\n", "malformed" }; 483 static const badval_test_t bv_negint = { "pos -10\n", "out of bounds" }; 484 static const badval_test_t bv_badu64 = { "u64 u64\n", "malformed" }; 485 static const badval_test_t bv_dbl1 = { "dbl xxx\n", "Could not convert" }; 486 static const badval_test_t bv_dbl2 = { "dbl 1.0 xx\n", "Could not convert" }; 487 static const badval_test_t bv_dbl3 = { 488 "dbl 1e-10000\n", "too small to express" }; 489 static const badval_test_t bv_dbl4 = { 490 "dbl 1e1000\n", "too large to express" }; 491 static const badval_test_t bv_dbl5 = { 492 "dbl -1e-10000\n", "too small to express" }; 493 static const badval_test_t bv_dbl6 = { 494 "dbl -1e1000\n", "too large to express" }; 495 static const badval_test_t bv_badcsvi1 = 496 { "csv_interval 10 wl\n", "malformed" }; 497 static const badval_test_t bv_badcsvi2 = 498 { "csv_interval cl,10\n", "malformed" }; 499 static const badval_test_t bv_nonoption = { "fnord 10\n", "Unknown option" }; 500 static const badval_test_t bv_badmem = { "mem 3 trits\n", "malformed" }; 501 static const badval_test_t bv_badbool = { "boolean 7\n", "Unrecognized value"}; 502 static const badval_test_t bv_badabool = 503 { "autobool 7\n", "Unrecognized value" }; 504 static const badval_test_t bv_badtime = { "time lunchtime\n", "Invalid time" }; 505 static const badval_test_t bv_virt = { "MixedLines 7\n", "virtual option" }; 506 static const badval_test_t bv_rs = { "Routerset 2.2.2.2.2\n", "Invalid" }; 507 static const badval_test_t bv_big_interval = 508 { "interval 1000 months", "too large" }; 509 510 /* Try config_dump(), and make sure it behaves correctly */ 511 static void 512 test_confparse_dump(void *arg) 513 { 514 (void)arg; 515 config_mgr_t *mgr = config_mgr_new(&test_fmt); 516 config_mgr_freeze(mgr); 517 test_struct_t *tst = get_simple_config(mgr); 518 char *dumped = NULL; 519 520 /* Minimal version. */ 521 dumped = config_dump(mgr, NULL, tst, 1, 0); 522 tt_str_op(dumped, OP_EQ, 523 "autobool 0\n" 524 "boolean 1\n" 525 "csv configuration,parsing,system\n" 526 "csv_interval 10\n" 527 "dbl 6.060842\n" 528 "fn /simple/test of the\n" 529 "i 3\n" 530 "interval 300\n" 531 "lines hello\n" 532 "lines world\n" 533 "mem 10\n" 534 "VisibleLineB ABC\n" 535 "LineTypeA i d\n" 536 "LineTypeB i c\n" 537 "msec_interval 300000\n" 538 "pos 77\n" 539 "routerset $FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\n" 540 "s this is a\n" 541 "time 2019-06-14 13:58:51\n" 542 "u64 1000000000000\n"); 543 544 tor_free(dumped); 545 dumped = config_dump(mgr, NULL, tst, 0, 0); 546 tt_str_op(dumped, OP_EQ, 547 "autobool 0\n" 548 "boolean 1\n" 549 "csv configuration,parsing,system\n" 550 "csv_interval 10\n" 551 "dbl 6.060842\n" 552 "deprecated_int 3\n" 553 "fn /simple/test of the\n" 554 "i 3\n" 555 "interval 300\n" 556 "lines hello\n" 557 "lines world\n" 558 "mem 10\n" 559 "VisibleLineB ABC\n" 560 "LineTypeA i d\n" 561 "LineTypeB i c\n" 562 "msec_interval 300000\n" 563 "pos 77\n" 564 "routerset $FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\n" 565 "s this is a\n" 566 "time 2019-06-14 13:58:51\n" 567 "u64 1000000000000\n"); 568 569 /* commented */ 570 tor_free(dumped); 571 dumped = config_dump(mgr, NULL, tst, 0, 1); 572 tt_str_op(dumped, OP_EQ, 573 "autobool 0\n" 574 "boolean 1\n" 575 "csv configuration,parsing,system\n" 576 "csv_interval 10\n" 577 "dbl 6.060842\n" 578 "# deprecated_int 3\n" 579 "fn /simple/test of the\n" 580 "i 3\n" 581 "interval 300\n" 582 "lines hello\n" 583 "lines world\n" 584 "mem 10\n" 585 "VisibleLineB ABC\n" 586 "LineTypeA i d\n" 587 "LineTypeB i c\n" 588 "msec_interval 300000\n" 589 "pos 77\n" 590 "routerset $FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\n" 591 "s this is a\n" 592 "time 2019-06-14 13:58:51\n" 593 "u64 1000000000000\n"); 594 595 done: 596 config_free(mgr, tst); 597 tor_free(dumped); 598 config_mgr_free(mgr); 599 } 600 601 /* Try confparse_reset_line(), and make sure it behaves correctly */ 602 static void 603 test_confparse_reset(void *arg) 604 { 605 (void)arg; 606 config_mgr_t *mgr = config_mgr_new(&test_fmt); 607 config_mgr_freeze(mgr); 608 test_struct_t *tst = get_simple_config(mgr); 609 610 config_reset_line(mgr, tst, "interval", 0); 611 tt_int_op(tst->interval, OP_EQ, 0); 612 613 config_reset_line(mgr, tst, "interval", 1); 614 tt_int_op(tst->interval, OP_EQ, 10); 615 616 tt_ptr_op(tst->routerset, OP_NE, NULL); 617 config_reset_line(mgr, tst, "routerset", 0); 618 tt_ptr_op(tst->routerset, OP_EQ, NULL); 619 620 done: 621 config_free(mgr, tst); 622 config_mgr_free(mgr); 623 } 624 625 /* Try setting options a second time on a config object, and make sure 626 * it behaves correctly. */ 627 static void 628 test_confparse_reassign(void *arg) 629 { 630 (void)arg; 631 config_mgr_t *mgr = config_mgr_new(&test_fmt); 632 config_mgr_freeze(mgr); 633 test_struct_t *tst = get_simple_config(mgr); 634 config_line_t *lines = NULL; 635 char *msg = NULL, *rs = NULL; 636 637 int r = config_get_lines( 638 "s eleven\n" 639 "i 12\n" 640 "lines 13\n" 641 "csv 14,15\n" 642 "routerset 127.0.0.1\n", 643 &lines, 0); 644 r = config_assign(mgr, tst,lines, 0, &msg); 645 tt_int_op(r, OP_EQ, 0); 646 tt_ptr_op(msg, OP_EQ, NULL); 647 648 tt_str_op(tst->s, OP_EQ, "eleven"); 649 tt_str_op(tst->fn, OP_EQ, "/simple/test of the"); // unchanged 650 tt_int_op(tst->pos, OP_EQ, 77); // unchanged 651 tt_int_op(tst->i, OP_EQ, 12); 652 tt_ptr_op(tst->lines, OP_NE, NULL); 653 tt_str_op(tst->lines->key, OP_EQ, "lines"); 654 tt_str_op(tst->lines->value, OP_EQ, "13"); 655 tt_ptr_op(tst->lines->next, OP_EQ, NULL); 656 tt_int_op(smartlist_len(tst->csv), OP_EQ, 2); 657 tt_str_op(smartlist_get(tst->csv, 0), OP_EQ, "14"); 658 tt_str_op(smartlist_get(tst->csv, 1), OP_EQ, "15"); 659 660 rs = routerset_to_string(tst->routerset); 661 tt_str_op(rs, OP_EQ, "127.0.0.1"); 662 663 // Try again with the CLEAR_FIRST and USE_DEFAULTS flags 664 r = config_assign(mgr, tst, lines, 665 CAL_CLEAR_FIRST|CAL_USE_DEFAULTS, &msg); 666 tt_int_op(r, OP_EQ, 0); 667 668 tt_ptr_op(msg, OP_EQ, NULL); 669 tt_str_op(tst->s, OP_EQ, "eleven"); 670 // tt_ptr_op(tst->fn, OP_EQ, NULL); //XXXX why is this not cleared? 671 // tt_int_op(tst->pos, OP_EQ, 0); //XXXX why is this not cleared? 672 tt_int_op(tst->i, OP_EQ, 12); 673 674 done: 675 config_free(mgr, tst); 676 config_free_lines(lines); 677 tor_free(msg); 678 tor_free(rs); 679 config_mgr_free(mgr); 680 } 681 682 /* Try setting options a second time on a config object, using the +foo 683 * linelist-extending syntax. */ 684 static void 685 test_confparse_reassign_extend(void *arg) 686 { 687 (void)arg; 688 config_mgr_t *mgr = config_mgr_new(&test_fmt); 689 config_mgr_freeze(mgr); 690 test_struct_t *tst = get_simple_config(mgr); 691 config_line_t *lines = NULL; 692 char *msg = NULL; 693 694 int r = config_get_lines( 695 "+lines 13\n", 696 &lines, 1); // allow extended format. 697 tt_int_op(r, OP_EQ, 0); 698 r = config_assign(mgr, tst,lines, 0, &msg); 699 tt_int_op(r, OP_EQ, 0); 700 tt_ptr_op(msg, OP_EQ, NULL); 701 702 tt_assert(tst->lines); 703 tt_str_op(tst->lines->key, OP_EQ, "lines"); 704 tt_str_op(tst->lines->value, OP_EQ, "hello"); 705 tt_assert(tst->lines->next); 706 tt_str_op(tst->lines->next->key, OP_EQ, "lines"); 707 tt_str_op(tst->lines->next->value, OP_EQ, "world"); 708 tt_assert(tst->lines->next->next); 709 tt_str_op(tst->lines->next->next->key, OP_EQ, "lines"); 710 tt_str_op(tst->lines->next->next->value, OP_EQ, "13"); 711 tt_assert(tst->lines->next->next->next == NULL); 712 config_free_lines(lines); 713 714 r = config_get_lines( 715 "/lines\n", 716 &lines, 1); // allow extended format. 717 tt_int_op(r, OP_EQ, 0); 718 r = config_assign(mgr, tst, lines, 0, &msg); 719 tt_int_op(r, OP_EQ, 0); 720 tt_ptr_op(msg, OP_EQ, NULL); 721 tt_assert(tst->lines == NULL); 722 config_free_lines(lines); 723 724 config_free(mgr, tst); 725 tst = get_simple_config(mgr); 726 r = config_get_lines( 727 "/lines away!\n", 728 &lines, 1); // allow extended format. 729 tt_int_op(r, OP_EQ, 0); 730 r = config_assign(mgr, tst, lines, 0, &msg); 731 tt_int_op(r, OP_EQ, 0); 732 tt_ptr_op(msg, OP_EQ, NULL); 733 tt_assert(tst->lines == NULL); 734 735 done: 736 config_free(mgr, tst); 737 config_free_lines(lines); 738 tor_free(msg); 739 config_mgr_free(mgr); 740 } 741 742 /* Test out confparse_get_assigned(). */ 743 static void 744 test_confparse_get_assigned(void *arg) 745 { 746 (void)arg; 747 748 config_mgr_t *mgr = config_mgr_new(&test_fmt); 749 config_mgr_freeze(mgr); 750 test_struct_t *tst = get_simple_config(mgr); 751 config_line_t *lines = NULL; 752 753 lines = config_get_assigned_option(mgr, tst, "I", 1); 754 tt_assert(lines); 755 tt_str_op(lines->key, OP_EQ, "i"); 756 tt_str_op(lines->value, OP_EQ, "3"); 757 tt_assert(lines->next == NULL); 758 config_free_lines(lines); 759 760 lines = config_get_assigned_option(mgr, tst, "s", 1); 761 tt_assert(lines); 762 tt_str_op(lines->key, OP_EQ, "s"); 763 tt_str_op(lines->value, OP_EQ, "this is a"); 764 tt_assert(lines->next == NULL); 765 config_free_lines(lines); 766 767 lines = config_get_assigned_option(mgr, tst, "obsolete", 1); 768 tt_assert(!lines); 769 770 lines = config_get_assigned_option(mgr, tst, "nonesuch", 1); 771 tt_assert(!lines); 772 773 lines = config_get_assigned_option(mgr, tst, "mixedlines", 1); 774 tt_assert(lines); 775 tt_str_op(lines->key, OP_EQ, "LineTypeA"); 776 tt_str_op(lines->value, OP_EQ, "i d"); 777 tt_assert(lines->next); 778 tt_str_op(lines->next->key, OP_EQ, "LineTypeB"); 779 tt_str_op(lines->next->value, OP_EQ, "i c"); 780 tt_assert(lines->next->next == NULL); 781 config_free_lines(lines); 782 783 lines = config_get_assigned_option(mgr, tst, "linetypeb", 1); 784 tt_assert(lines); 785 tt_str_op(lines->key, OP_EQ, "LineTypeB"); 786 tt_str_op(lines->value, OP_EQ, "i c"); 787 tt_assert(lines->next == NULL); 788 config_free_lines(lines); 789 790 tor_free(tst->s); 791 tst->s = tor_strdup("Hello\nWorld"); 792 lines = config_get_assigned_option(mgr, tst, "s", 1); 793 tt_assert(lines); 794 tt_str_op(lines->key, OP_EQ, "s"); 795 tt_str_op(lines->value, OP_EQ, "\"Hello\\nWorld\""); 796 tt_assert(lines->next == NULL); 797 config_free_lines(lines); 798 799 done: 800 config_free(mgr, tst); 801 config_free_lines(lines); 802 config_mgr_free(mgr); 803 } 804 805 /* Another variant, which accepts and stores unrecognized lines.*/ 806 #define ETEST_MAGIC 13371337 807 808 static struct_member_t extra = { 809 .name = "__extra", 810 .type = CONFIG_TYPE_LINELIST, 811 .offset = offsetof(test_struct_t, extra_lines), 812 }; 813 814 static config_format_t etest_fmt = { 815 .size = sizeof(test_struct_t), 816 .magic = { 817 "test_struct_t (with extra lines)", 818 ETEST_MAGIC, 819 offsetof(test_struct_t, magic), 820 }, 821 .abbrevs = test_abbrevs, 822 .deprecations = test_deprecation_notes, 823 .vars = test_vars, 824 .legacy_validate_fn = test_validate_cb, 825 .extra = &extra, 826 }; 827 828 /* Try out the feature where we can store unrecognized lines and dump them 829 * again. (State files use this.) */ 830 static void 831 test_confparse_extra_lines(void *arg) 832 { 833 (void)arg; 834 config_mgr_t *mgr = config_mgr_new(&etest_fmt); 835 config_mgr_freeze(mgr); 836 test_struct_t *tst = config_new(mgr); 837 config_line_t *lines = NULL; 838 char *msg = NULL, *dump = NULL; 839 840 config_init(mgr, tst); 841 842 int r = config_get_lines( 843 "unknotty addita\n" 844 "pos 99\n" 845 "wombat knish\n", &lines, 0); 846 tt_int_op(r, OP_EQ, 0); 847 r = config_assign(mgr, tst, lines, 0, &msg); 848 tt_int_op(r, OP_EQ, 0); 849 tt_ptr_op(msg, OP_EQ, NULL); 850 851 tt_assert(tst->extra_lines); 852 853 dump = config_dump(mgr, NULL, tst, 1, 0); 854 tt_str_op(dump, OP_EQ, 855 "pos 99\n" 856 "unknotty addita\n" 857 "wombat knish\n"); 858 859 done: 860 tor_free(msg); 861 tor_free(dump); 862 config_free_lines(lines); 863 config_free(mgr, tst); 864 config_mgr_free(mgr); 865 } 866 867 static void 868 test_confparse_unitparse(void *args) 869 { 870 (void)args; 871 /* spot-check a few memunit values. */ 872 int ok = 3; 873 tt_u64_op(config_parse_memunit("100 MB", &ok), OP_EQ, 100<<20); 874 tt_assert(ok); 875 tt_u64_op(config_parse_memunit("100 TB", &ok), OP_EQ, UINT64_C(100)<<40); 876 tt_assert(ok); 877 // This is a floating-point value, but note that 1.5 can be represented 878 // precisely. 879 tt_u64_op(config_parse_memunit("1.5 MB", &ok), OP_EQ, 3<<19); 880 tt_assert(ok); 881 882 /* Try some good intervals and msec intervals */ 883 tt_int_op(config_parse_interval("2 days", &ok), OP_EQ, 48*3600); 884 tt_assert(ok); 885 tt_int_op(config_parse_interval("1.5 hour", &ok), OP_EQ, 5400); 886 tt_assert(ok); 887 tt_u64_op(config_parse_interval("1 minute", &ok), OP_EQ, 60); 888 tt_assert(ok); 889 tt_int_op(config_parse_msec_interval("2 days", &ok), OP_EQ, 48*3600*1000); 890 tt_assert(ok); 891 tt_int_op(config_parse_msec_interval("10 msec", &ok), OP_EQ, 10); 892 tt_assert(ok); 893 894 /* Try a couple of unitless values. */ 895 tt_int_op(config_parse_interval("10", &ok), OP_EQ, 10); 896 tt_assert(ok); 897 tt_u64_op(config_parse_interval("15.0", &ok), OP_EQ, 15); 898 tt_assert(ok); 899 900 /* u64 overflow */ 901 tt_u64_op(config_parse_memunit("20000000 TB", &ok), OP_EQ, 0); 902 tt_assert(!ok); 903 // This test fails the double check as the float representing 15000000.5 TB 904 // is greater than (double) INT64_MAX 905 tt_u64_op(config_parse_memunit("15000000.5 TB", &ok), OP_EQ, 0); 906 tt_assert(!ok); 907 // 8388608.1 TB passes double check because it falls in the same float 908 // value as (double)INT64_MAX (which is 2^63) due to precision. 909 // But will fail the int check because the unsigned representation of 910 // the float, which is 2^63, is strictly greater than INT64_MAX (2^63-1) 911 tt_u64_op(config_parse_memunit("8388608.1 TB", &ok), OP_EQ, 0); 912 tt_assert(!ok); 913 914 /* negative float */ 915 tt_u64_op(config_parse_memunit("-1.5 GB", &ok), OP_EQ, 0); 916 tt_assert(!ok); 917 918 /* i32 overflow */ 919 tt_int_op(config_parse_interval("1000 months", &ok), OP_EQ, -1); 920 tt_assert(!ok); 921 tt_int_op(config_parse_msec_interval("4 weeks", &ok), OP_EQ, -1); 922 tt_assert(!ok); 923 924 /* bad units */ 925 tt_u64_op(config_parse_memunit("7 nybbles", &ok), OP_EQ, 0); 926 tt_assert(!ok); 927 // XXXX these next two should return -1 according to the documentation. 928 tt_int_op(config_parse_interval("7 cowznofski", &ok), OP_EQ, 0); 929 tt_assert(!ok); 930 tt_int_op(config_parse_msec_interval("1 kalpa", &ok), OP_EQ, 0); 931 tt_assert(!ok); 932 933 done: 934 ; 935 } 936 937 static void 938 test_confparse_check_ok_fail(void *arg) 939 { 940 (void)arg; 941 config_mgr_t *mgr = config_mgr_new(&test_fmt); 942 config_mgr_freeze(mgr); 943 test_struct_t *tst = config_new(mgr); 944 tst->pos = -10; 945 tt_assert(! config_check_ok(mgr, tst, LOG_INFO)); 946 947 done: 948 config_free(mgr, tst); 949 config_mgr_free(mgr); 950 } 951 952 static void 953 test_confparse_list_vars(void *arg) 954 { 955 (void)arg; 956 config_mgr_t *mgr = config_mgr_new(&test_fmt); 957 smartlist_t *vars = config_mgr_list_vars(mgr); 958 smartlist_t *varnames = smartlist_new(); 959 char *joined = NULL; 960 961 tt_assert(vars); 962 SMARTLIST_FOREACH(vars, config_var_t *, cv, 963 smartlist_add(varnames, (void*)cv->member.name)); 964 smartlist_sort_strings(varnames); 965 joined = smartlist_join_strings(varnames, "::", 0, NULL); 966 tt_str_op(joined, OP_EQ, 967 "LineTypeA::" 968 "LineTypeB::" 969 "MixedHiddenLines::" 970 "MixedLines::" 971 "VisibleLineB::" 972 "__HiddenInt::" 973 "__HiddenLineA::" 974 "autobool::" 975 "boolean::" 976 "csv::" 977 "csv_interval::" 978 "dbl::" 979 "deprecated_int::" 980 "fn::" 981 "i::" 982 "interval::" 983 "lines::" 984 "mem::" 985 "msec_interval::" 986 "obsolete::" 987 "pos::" 988 "routerset::" 989 "s::" 990 "time::" 991 "u64"); 992 993 done: 994 tor_free(joined); 995 smartlist_free(varnames); 996 smartlist_free(vars); 997 config_mgr_free(mgr); 998 } 999 1000 static void 1001 test_confparse_list_deprecated(void *arg) 1002 { 1003 (void)arg; 1004 config_mgr_t *mgr = config_mgr_new(&test_fmt); 1005 smartlist_t *vars = config_mgr_list_deprecated_vars(mgr); 1006 char *joined = NULL; 1007 1008 tt_assert(vars); 1009 smartlist_sort_strings(vars); 1010 joined = smartlist_join_strings(vars, "::", 0, NULL); 1011 1012 tt_str_op(joined, OP_EQ, "deprecated_int"); 1013 1014 done: 1015 tor_free(joined); 1016 smartlist_free(vars); 1017 config_mgr_free(mgr); 1018 } 1019 1020 static void 1021 test_confparse_find_option_name(void *arg) 1022 { 1023 (void)arg; 1024 config_mgr_t *mgr = config_mgr_new(&test_fmt); 1025 1026 // exact match 1027 tt_str_op(config_find_option_name(mgr, "u64"), OP_EQ, "u64"); 1028 // case-insensitive match 1029 tt_str_op(config_find_option_name(mgr, "S"), OP_EQ, "s"); 1030 tt_str_op(config_find_option_name(mgr, "linetypea"), OP_EQ, "LineTypeA"); 1031 // prefix match 1032 tt_str_op(config_find_option_name(mgr, "deprec"), OP_EQ, "deprecated_int"); 1033 // explicit abbreviation 1034 tt_str_op(config_find_option_name(mgr, "uint"), OP_EQ, "pos"); 1035 tt_str_op(config_find_option_name(mgr, "UINT"), OP_EQ, "pos"); 1036 // no match 1037 tt_ptr_op(config_find_option_name(mgr, "absent"), OP_EQ, NULL); 1038 1039 done: 1040 config_mgr_free(mgr); 1041 } 1042 1043 #ifndef COCCI 1044 #define CONFPARSE_TEST(name, flags) \ 1045 { #name, test_confparse_ ## name, flags, NULL, NULL } 1046 1047 #define BADVAL_TEST(name) \ 1048 { "badval_" #name, test_confparse_assign_badval, 0, \ 1049 &passthrough_setup, (void*)&bv_ ## name } 1050 #endif /* !defined(COCCI) */ 1051 1052 struct testcase_t confparse_tests[] = { 1053 CONFPARSE_TEST(init, 0), 1054 CONFPARSE_TEST(assign_simple, 0), 1055 CONFPARSE_TEST(assign_obsolete, 0), 1056 CONFPARSE_TEST(assign_deprecated, 0), 1057 CONFPARSE_TEST(assign_replaced, 0), 1058 CONFPARSE_TEST(assign_emptystring, 0), 1059 CONFPARSE_TEST(assign_twice, 0), 1060 BADVAL_TEST(notint), 1061 BADVAL_TEST(negint), 1062 BADVAL_TEST(badu64), 1063 BADVAL_TEST(dbl1), 1064 BADVAL_TEST(dbl2), 1065 BADVAL_TEST(dbl3), 1066 BADVAL_TEST(dbl4), 1067 BADVAL_TEST(dbl5), 1068 BADVAL_TEST(dbl6), 1069 BADVAL_TEST(badcsvi1), 1070 BADVAL_TEST(badcsvi2), 1071 BADVAL_TEST(nonoption), 1072 BADVAL_TEST(badmem), 1073 BADVAL_TEST(badbool), 1074 BADVAL_TEST(badabool), 1075 BADVAL_TEST(badtime), 1076 BADVAL_TEST(virt), 1077 BADVAL_TEST(rs), 1078 BADVAL_TEST(big_interval), 1079 CONFPARSE_TEST(dump, 0), 1080 CONFPARSE_TEST(reset, 0), 1081 CONFPARSE_TEST(reassign, 0), 1082 CONFPARSE_TEST(reassign_extend, 0), 1083 CONFPARSE_TEST(get_assigned, 0), 1084 CONFPARSE_TEST(extra_lines, 0), 1085 CONFPARSE_TEST(unitparse, 0), 1086 CONFPARSE_TEST(check_ok_fail, 0), 1087 CONFPARSE_TEST(list_vars, 0), 1088 CONFPARSE_TEST(list_deprecated, 0), 1089 CONFPARSE_TEST(find_option_name, 0), 1090 END_OF_TESTCASES 1091 };