tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

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 };