Merge branch 'maint-0.4.8'
[tor.git] / src / test / test_confparse.c
blob391f6241dacc01a43b0bfe1da19b59d05706eab7
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 */
6 /*
7 * Tests for confmgt.c module that we use to parse various
8 * configuration/state file types.
9 */
11 #define CONFMGT_PRIVATE
12 #include "orconfig.h"
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"
21 #include "lib/confmgt/unitparse.h"
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;
46 config_line_t *extra_lines;
47 } test_struct_t;
49 static test_struct_t test_struct_t_dummy;
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)
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"),
80 .member = { .name = "routerset",
81 .type = CONFIG_TYPE_EXTENDED,
82 .type_def = &ROUTERSET_type_defn,
83 .offset = offsetof(test_struct_t, routerset),
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),
91 END_OF_CONFIG_VARS,
94 static config_abbrev_t test_abbrevs[] = {
95 { "uint", "pos", 0, 0 },
96 { "float", "dbl", 0, 1 },
97 { NULL, NULL, 0, 0 }
100 static config_deprecation_t test_deprecation_notes[] = {
101 { "deprecated_int", "This integer is deprecated." },
102 { NULL, NULL }
105 static int
106 test_validate_cb(const void *old_options, void *options, char **msg)
108 (void)old_options;
109 (void)msg;
110 test_struct_t *ts = options;
112 if (ts->i == 0xbad) {
113 *msg = tor_strdup("bad value for i");
114 return -1;
116 return 0;
119 #define TEST_MAGIC 0x1337
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),
128 .abbrevs = test_abbrevs,
129 .deprecations = test_deprecation_notes,
130 .vars = test_vars,
131 .legacy_validate_fn = test_validate_cb,
134 /* Make sure that config_init sets everything to the right defaults. */
135 static void
136 test_confparse_init(void *arg)
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);
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);
165 done:
166 config_free(mgr, tst);
167 config_mgr_free(mgr);
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";
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)
198 test_struct_t *result = NULL;
199 test_struct_t *tst = config_new(mgr);
200 config_line_t *lines = NULL;
201 char *msg = NULL;
203 config_init(mgr, tst);
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);
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;
220 /* Make sure that config_assign can parse things. */
221 static void
222 test_confparse_assign_simple(void *arg)
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);
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);
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);
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);
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);
275 tt_assert(config_check_ok(mgr, tst, LOG_ERR));
277 done:
278 config_free(mgr, tst);
279 config_mgr_free(mgr);
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)
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;
293 config_init(mgr, tst);
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");
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);
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)
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;
324 config_init(mgr, tst);
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.");
335 tt_int_op(tst->deprecated_int, OP_EQ, 7);
337 tt_assert(config_check_ok(mgr, tst, LOG_ERR));
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);
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)
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;
359 config_init(mgr, tst);
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.");
369 tt_double_op(tst->dbl, OP_GT, 999.999);
370 tt_double_op(tst->dbl, OP_LT, 1000.001);
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);
380 /* Try to set a linelist value with no option. */
381 static void
382 test_confparse_assign_emptystring(void *arg)
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;
391 config_init(mgr, tst);
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");
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);
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)
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;
420 config_init(mgr, tst);
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");
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);
439 typedef struct badval_test_t {
440 const char *cfg;
441 const char *expect_msg;
442 } badval_test_t;
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)
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;
456 config_init(mgr, tst);
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));
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);
476 /* Various arguments for badval test.
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.
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" };
510 /* Try config_dump(), and make sure it behaves correctly */
511 static void
512 test_confparse_dump(void *arg)
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;
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");
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");
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");
595 done:
596 config_free(mgr, tst);
597 tor_free(dumped);
598 config_mgr_free(mgr);
601 /* Try confparse_reset_line(), and make sure it behaves correctly */
602 static void
603 test_confparse_reset(void *arg)
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);
610 config_reset_line(mgr, tst, "interval", 0);
611 tt_int_op(tst->interval, OP_EQ, 0);
613 config_reset_line(mgr, tst, "interval", 1);
614 tt_int_op(tst->interval, OP_EQ, 10);
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);
620 done:
621 config_free(mgr, tst);
622 config_mgr_free(mgr);
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)
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;
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);
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");
660 rs = routerset_to_string(tst->routerset);
661 tt_str_op(rs, OP_EQ, "127.0.0.1");
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);
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);
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);
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)
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;
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);
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);
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);
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);
735 done:
736 config_free(mgr, tst);
737 config_free_lines(lines);
738 tor_free(msg);
739 config_mgr_free(mgr);
742 /* Test out confparse_get_assigned(). */
743 static void
744 test_confparse_get_assigned(void *arg)
746 (void)arg;
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;
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);
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);
767 lines = config_get_assigned_option(mgr, tst, "obsolete", 1);
768 tt_assert(!lines);
770 lines = config_get_assigned_option(mgr, tst, "nonesuch", 1);
771 tt_assert(!lines);
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);
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);
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);
799 done:
800 config_free(mgr, tst);
801 config_free_lines(lines);
802 config_mgr_free(mgr);
805 /* Another variant, which accepts and stores unrecognized lines.*/
806 #define ETEST_MAGIC 13371337
808 static struct_member_t extra = {
809 .name = "__extra",
810 .type = CONFIG_TYPE_LINELIST,
811 .offset = offsetof(test_struct_t, extra_lines),
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),
821 .abbrevs = test_abbrevs,
822 .deprecations = test_deprecation_notes,
823 .vars = test_vars,
824 .legacy_validate_fn = test_validate_cb,
825 .extra = &extra,
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)
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;
840 config_init(mgr, tst);
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);
851 tt_assert(tst->extra_lines);
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");
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);
867 static void
868 test_confparse_unitparse(void *args)
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);
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);
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);
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);
914 /* negative float */
915 tt_u64_op(config_parse_memunit("-1.5 GB", &ok), OP_EQ, 0);
916 tt_assert(!ok);
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);
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);
933 done:
937 static void
938 test_confparse_check_ok_fail(void *arg)
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));
947 done:
948 config_free(mgr, tst);
949 config_mgr_free(mgr);
952 static void
953 test_confparse_list_vars(void *arg)
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;
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");
993 done:
994 tor_free(joined);
995 smartlist_free(varnames);
996 smartlist_free(vars);
997 config_mgr_free(mgr);
1000 static void
1001 test_confparse_list_deprecated(void *arg)
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;
1008 tt_assert(vars);
1009 smartlist_sort_strings(vars);
1010 joined = smartlist_join_strings(vars, "::", 0, NULL);
1012 tt_str_op(joined, OP_EQ, "deprecated_int");
1014 done:
1015 tor_free(joined);
1016 smartlist_free(vars);
1017 config_mgr_free(mgr);
1020 static void
1021 test_confparse_find_option_name(void *arg)
1023 (void)arg;
1024 config_mgr_t *mgr = config_mgr_new(&test_fmt);
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);
1039 done:
1040 config_mgr_free(mgr);
1043 #ifndef COCCI
1044 #define CONFPARSE_TEST(name, flags) \
1045 { #name, test_confparse_ ## name, flags, NULL, NULL }
1047 #define BADVAL_TEST(name) \
1048 { "badval_" #name, test_confparse_assign_badval, 0, \
1049 &passthrough_setup, (void*)&bv_ ## name }
1050 #endif /* !defined(COCCI) */
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