LCOV - code coverage report
Current view: top level - test - test_confmgr.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 206 209 98.6 %
Date: 2021-11-24 03:28:48 Functions: 13 13 100.0 %

          Line data    Source code
       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's features that support multiple configuration
       8             :  * formats and configuration objects.
       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 "lib/confmgt/confmgt.h"
      17             : #include "test/test.h"
      18             : #include "test/log_test_helpers.h"
      19             : 
      20             : /*
      21             :  * Set up a few objects: a pasture_cfg is toplevel; it has a llama_cfg and an
      22             :  * alpaca_cfg.
      23             :  */
      24             : 
      25             : typedef struct {
      26             :   uint32_t magic;
      27             :   char *address;
      28             :   int opentopublic;
      29             :   config_suite_t *subobjs;
      30             : } pasture_cfg_t;
      31             : 
      32             : typedef struct {
      33             :   char *llamaname;
      34             :   int cuteness;
      35             :   uint32_t magic;
      36             :   int eats_meat; /* deprecated; llamas are never carnivorous. */
      37             : 
      38             :   char *description; // derived from other fields.
      39             : } llama_cfg_t;
      40             : 
      41             : typedef struct {
      42             :   uint32_t magic;
      43             :   int fuzziness;
      44             :   char *alpacaname;
      45             :   int n_wings; /* deprecated; alpacas don't have wings. */
      46             : 
      47             :   int square_fuzziness; /* Derived from fuzziness. */
      48             : } alpaca_cfg_t;
      49             : 
      50             : /*
      51             :  * Make the above into configuration objects.
      52             :  */
      53             : 
      54             : static pasture_cfg_t pasture_cfg_t_dummy;
      55             : static llama_cfg_t llama_cfg_t_dummy;
      56             : static alpaca_cfg_t alpaca_cfg_t_dummy;
      57             : 
      58             : #define PV(name, type, dflt) \
      59             :   CONFIG_VAR_ETYPE(pasture_cfg_t, #name, type, name, 0, dflt)
      60             : #define LV(name, type, dflt) \
      61             :   CONFIG_VAR_ETYPE(llama_cfg_t, #name, type, name, 0, dflt)
      62             : #define AV(name, type, dflt) \
      63             :   CONFIG_VAR_ETYPE(alpaca_cfg_t, #name, type, name, 0, dflt)
      64             : static const config_var_t pasture_vars[] = {
      65             :   PV(address, STRING, NULL),
      66             :   PV(opentopublic, BOOL, "1"),
      67             :   END_OF_CONFIG_VARS
      68             : };
      69             : static const config_var_t llama_vars[] =
      70             : {
      71             :   LV(llamaname, STRING, NULL),
      72             :   LV(eats_meat, BOOL, NULL),
      73             :   LV(cuteness, POSINT, "100"),
      74             :   END_OF_CONFIG_VARS
      75             : };
      76             : static const config_var_t alpaca_vars[] =
      77             : {
      78             :   AV(alpacaname, STRING, NULL),
      79             :   AV(fuzziness, POSINT, "50"),
      80             :   AV(n_wings, POSINT, "0"),
      81             :   END_OF_CONFIG_VARS
      82             : };
      83             : 
      84             : static config_deprecation_t llama_deprecations[] = {
      85             :   { "eats_meat", "Llamas are herbivores." },
      86             :   {NULL,NULL}
      87             : };
      88             : 
      89             : static config_deprecation_t alpaca_deprecations[] = {
      90             :   { "n_wings", "Alpacas are quadrupeds." },
      91             :   {NULL,NULL}
      92             : };
      93             : 
      94             : static int clear_llama_cfg_called = 0;
      95             : static void
      96          12 : clear_llama_cfg(const config_mgr_t *mgr, void *llamacfg)
      97             : {
      98          12 :   (void)mgr;
      99          12 :   llama_cfg_t *lc = llamacfg;
     100          12 :   tor_free(lc->description);
     101          12 :   ++clear_llama_cfg_called;
     102          12 : }
     103             : 
     104             : static config_abbrev_t llama_abbrevs[] = {
     105             :   { "gracia", "cuteness", 0, 0 },
     106             :   { "gentillesse", "cuteness", 0, 0 },
     107             :   { NULL, NULL, 0, 0 },
     108             : };
     109             : 
     110             : static int
     111           3 : legacy_validate_pasture(const void *old_, void *obj, char **msg_out)
     112             : {
     113           3 :   const pasture_cfg_t *old = old_;
     114           3 :   pasture_cfg_t *p = obj;
     115             : 
     116             :   // llamas can't find their way home if the letters are lowercase.
     117           3 :   if (p->address)
     118           3 :     tor_strupper(p->address);
     119             : 
     120           3 :   if (old && old->address &&
     121           2 :       (!p->address || strcmp(old->address, p->address))) {
     122           1 :     *msg_out = tor_strdup("You can't move a pasture.");
     123           1 :     return -1;
     124             :   }
     125             : 
     126             :   return 0;
     127             : }
     128             : 
     129             : static int
     130           6 : validate_llama(const void *obj, char **msg_out)
     131             : {
     132           6 :   const llama_cfg_t *llama = obj;
     133           6 :   tor_assert(llama->magic == 0x11aa11);
     134             : 
     135           6 :   if (! llama->llamaname || strlen(llama->llamaname) == 0) {
     136           1 :     *msg_out = tor_strdup("A llama has no name!?");
     137           1 :     return -1;
     138             :   }
     139             : 
     140           5 :   if (strspn(llama->llamaname, "0123456789") == strlen(llama->llamaname)) {
     141           1 :     *msg_out = tor_strdup("It is not a number; it is a free llama!");
     142           1 :     return -1;
     143             :   }
     144             : 
     145             :   return 0;
     146             : }
     147             : 
     148             : static int
     149           3 : check_transition_alpaca(const void *old_, const void *new_, char **msg_out)
     150             : {
     151           3 :   const alpaca_cfg_t *old_alpaca = old_;
     152           3 :   const alpaca_cfg_t *new_alpaca = new_;
     153             : 
     154           3 :   tor_assert(old_alpaca && new_alpaca);
     155           3 :   tor_assert(old_alpaca->magic == 0xa15aca);
     156           3 :   tor_assert(new_alpaca->magic == 0xa15aca);
     157             : 
     158           3 :   if (old_alpaca->fuzziness > new_alpaca->fuzziness) {
     159           1 :     *msg_out = tor_strdup("An alpaca only becomes more fuzzy over time.");
     160           1 :     return -1;
     161             :   }
     162             : 
     163             :   return 0;
     164             : }
     165             : 
     166             : static int
     167           4 : post_normalize_llama(void *obj, char **msg_out)
     168             : {
     169           4 :   (void)msg_out;
     170           4 :   llama_cfg_t *llama = obj;
     171           4 :   tor_assert(llama->magic == 0x11aa11);
     172           4 :   tor_assert(llama->llamaname); // we have already checked for a NULL name.
     173           4 :   tor_free(llama->description);
     174           4 :   tor_asprintf(&llama->description, "A llama called %s.", llama->llamaname);
     175           4 :   return 0;
     176             : }
     177             : 
     178             : static int
     179           4 : pre_normalize_alpaca(void *obj, char **msg_out)
     180             : {
     181           4 :   (void)msg_out;
     182           4 :   alpaca_cfg_t *alpaca = obj;
     183           4 :   tor_assert(alpaca->magic == 0xa15aca);
     184           4 :   alpaca->square_fuzziness = alpaca->fuzziness * alpaca->fuzziness;
     185           4 :   return 0;
     186             : }
     187             : 
     188             : static const config_format_t pasture_fmt = {
     189             :   sizeof(pasture_cfg_t),
     190             :   {
     191             :     "pasture_cfg_t",
     192             :     8989,
     193             :     offsetof(pasture_cfg_t, magic)
     194             :   },
     195             :   .vars = pasture_vars,
     196             :   .has_config_suite = true,
     197             :   .config_suite_offset = offsetof(pasture_cfg_t, subobjs),
     198             :   .legacy_validate_fn = legacy_validate_pasture,
     199             : };
     200             : 
     201             : static const config_format_t llama_fmt = {
     202             :   sizeof(llama_cfg_t),
     203             :   {
     204             :     "llama_cfg_t",
     205             :     0x11aa11,
     206             :     offsetof(llama_cfg_t, magic)
     207             :   },
     208             :   .vars = llama_vars,
     209             :   .deprecations = llama_deprecations,
     210             :   .abbrevs = llama_abbrevs,
     211             :   .clear_fn = clear_llama_cfg,
     212             :   .validate_fn = validate_llama,
     213             :   .post_normalize_fn = post_normalize_llama,
     214             : };
     215             : 
     216             : static const config_format_t alpaca_fmt = {
     217             :   sizeof(alpaca_cfg_t),
     218             :   {
     219             :     "alpaca_cfg_t",
     220             :     0xa15aca,
     221             :     offsetof(alpaca_cfg_t, magic)
     222             :   },
     223             :   .vars = alpaca_vars,
     224             :   .deprecations = alpaca_deprecations,
     225             :   .pre_normalize_fn = pre_normalize_alpaca,
     226             :   .check_transition_fn = check_transition_alpaca,
     227             : };
     228             : 
     229             : #define LLAMA_IDX 0
     230             : #define ALPACA_IDX 1
     231             : 
     232             : static config_mgr_t *
     233           7 : get_mgr(bool freeze)
     234             : {
     235           7 :   config_mgr_t *mgr = config_mgr_new(&pasture_fmt);
     236           7 :   tt_int_op(LLAMA_IDX, OP_EQ, config_mgr_add_format(mgr, &llama_fmt));
     237           7 :   tt_int_op(ALPACA_IDX, OP_EQ, config_mgr_add_format(mgr, &alpaca_fmt));
     238           7 :   if (freeze)
     239           7 :     config_mgr_freeze(mgr);
     240             :   return mgr;
     241             : 
     242           0 :  done:
     243           0 :   config_mgr_free(mgr);
     244           0 :   return NULL;
     245             : }
     246             : 
     247             : static void
     248           1 : test_confmgr_init(void *arg)
     249             : {
     250           1 :   (void)arg;
     251           1 :   config_mgr_t *mgr = get_mgr(true);
     252           1 :   smartlist_t *vars = NULL;
     253           1 :   tt_ptr_op(mgr, OP_NE, NULL);
     254             : 
     255           1 :   vars = config_mgr_list_vars(mgr);
     256           1 :   tt_int_op(smartlist_len(vars), OP_EQ, 8); // 8 vars total.
     257             : 
     258           1 :   tt_str_op("cuteness", OP_EQ, config_find_option_name(mgr, "CUTENESS"));
     259           1 :   tt_str_op("cuteness", OP_EQ, config_find_option_name(mgr, "GRACIA"));
     260           1 :   smartlist_free(vars);
     261             : 
     262           1 :   vars = config_mgr_list_deprecated_vars(mgr); // 2 deprecated vars.
     263           1 :   tt_int_op(smartlist_len(vars), OP_EQ, 2);
     264           1 :   tt_assert(smartlist_contains_string(vars, "eats_meat"));
     265           1 :   tt_assert(smartlist_contains_string(vars, "n_wings"));
     266             : 
     267           1 :   tt_str_op("Llamas are herbivores.", OP_EQ,
     268             :             config_find_deprecation(mgr, "EATS_MEAT"));
     269           1 :   tt_str_op("Alpacas are quadrupeds.", OP_EQ,
     270             :             config_find_deprecation(mgr, "N_WINGS"));
     271             : 
     272           1 :  done:
     273           1 :   smartlist_free(vars);
     274           1 :   config_mgr_free(mgr);
     275           1 : }
     276             : 
     277             : static void
     278           1 : test_confmgr_magic(void *args)
     279             : {
     280           1 :   (void)args;
     281             :   // Every time we build a manager, it is supposed to get a different magic
     282             :   // number.  Let's test that.
     283           1 :   config_mgr_t *mgr1 = get_mgr(true);
     284           1 :   config_mgr_t *mgr2 = get_mgr(true);
     285           1 :   config_mgr_t *mgr3 = get_mgr(true);
     286             : 
     287           1 :   pasture_cfg_t *p1 = NULL, *p2 = NULL, *p3 = NULL;
     288             : 
     289           1 :   tt_assert(mgr1);
     290           1 :   tt_assert(mgr2);
     291           1 :   tt_assert(mgr3);
     292             : 
     293           1 :   p1 = config_new(mgr1);
     294           1 :   p2 = config_new(mgr2);
     295           1 :   p3 = config_new(mgr3);
     296             : 
     297           1 :   tt_assert(p1);
     298           1 :   tt_assert(p2);
     299           1 :   tt_assert(p3);
     300             : 
     301             :   // By chance, two managers get the same magic with P=2^-32.  Let's
     302             :   // make sure that at least two of them are different, so that our
     303             :   // odds of a false positive are 1/2^-64.
     304           1 :   tt_assert((p1->magic != p2->magic) || (p2->magic != p3->magic));
     305             : 
     306           1 :  done:
     307           1 :   config_free(mgr1, p1);
     308           1 :   config_free(mgr2, p2);
     309           1 :   config_free(mgr3, p3);
     310             : 
     311           1 :   config_mgr_free(mgr1);
     312           1 :   config_mgr_free(mgr2);
     313           1 :   config_mgr_free(mgr3);
     314           1 : }
     315             : 
     316             : static const char *simple_pasture =
     317             :   "LLamaname hugo\n"
     318             :   "Alpacaname daphne\n"
     319             :   "gentillesse 42\n"
     320             :   "address 123 Camelid ave\n";
     321             : 
     322             : static void
     323           1 : test_confmgr_parse(void *arg)
     324             : {
     325           1 :   (void)arg;
     326           1 :   config_mgr_t *mgr = get_mgr(true);
     327           1 :   pasture_cfg_t *p = config_new(mgr);
     328           1 :   config_line_t *lines = NULL;
     329           1 :   char *msg = NULL;
     330             : 
     331           1 :   config_init(mgr, p); // set defaults.
     332             : 
     333           1 :   int r = config_get_lines(simple_pasture, &lines, 0);
     334           1 :   tt_int_op(r, OP_EQ, 0);
     335           1 :   r = config_assign(mgr, p, lines, 0, &msg);
     336           1 :   tt_int_op(r, OP_EQ, 0);
     337             : 
     338           1 :   tt_int_op(p->opentopublic, OP_EQ, 1);
     339           1 :   tt_str_op(p->address, OP_EQ, "123 Camelid ave");
     340             : 
     341             :   // We are using this API directly; modules outside confparse will, in the
     342             :   // future, not.
     343           1 :   const alpaca_cfg_t *ac = config_mgr_get_obj(mgr, p, ALPACA_IDX);
     344           1 :   const llama_cfg_t *lc = config_mgr_get_obj(mgr, p, LLAMA_IDX);
     345           1 :   tt_str_op(lc->llamaname, OP_EQ, "hugo");
     346           1 :   tt_str_op(ac->alpacaname, OP_EQ, "daphne");
     347           1 :   tt_int_op(lc->cuteness, OP_EQ, 42);
     348           1 :   tt_int_op(ac->fuzziness, OP_EQ, 50);
     349             : 
     350             :   // We set the description for the llama here, so that the clear function
     351             :   // can clear it.  (Later we can do this in a verification function.)
     352           1 :   clear_llama_cfg_called = 0;
     353           1 :   llama_cfg_t *mut_lc = config_mgr_get_obj_mutable(mgr, p, LLAMA_IDX);
     354           1 :   mut_lc->description = tor_strdup("A llama named Hugo.");
     355           1 :   config_free(mgr, p);
     356           1 :   tt_int_op(clear_llama_cfg_called, OP_EQ, 1);
     357             : 
     358           1 :  done:
     359           1 :   config_free_lines(lines);
     360           1 :   config_free(mgr, p);
     361           1 :   config_mgr_free(mgr);
     362           1 :   tor_free(msg);
     363           1 : }
     364             : 
     365             : static void
     366           1 : test_confmgr_dump(void *arg)
     367             : {
     368           1 :   (void)arg;
     369           1 :   config_mgr_t *mgr = get_mgr(true);
     370           1 :   pasture_cfg_t *p = config_new(mgr);
     371           1 :   pasture_cfg_t *defaults = config_new(mgr);
     372           1 :   config_line_t *lines = NULL;
     373           1 :   char *msg = NULL;
     374           1 :   char *s = NULL;
     375             : 
     376           1 :   config_init(mgr, p); // set defaults.
     377           1 :   config_init(mgr, defaults); // set defaults.
     378             : 
     379           1 :   int r = config_get_lines(simple_pasture, &lines, 0);
     380           1 :   tt_int_op(r, OP_EQ, 0);
     381           1 :   r = config_assign(mgr, p, lines, 0, &msg);
     382           1 :   tt_int_op(r, OP_EQ, 0);
     383             : 
     384           1 :   s = config_dump(mgr, defaults, p, 1, 0);
     385           1 :   tt_str_op("address 123 Camelid ave\n"
     386             :             "alpacaname daphne\n"
     387             :             "cuteness 42\n"
     388             :             "llamaname hugo\n", OP_EQ, s);
     389             : 
     390           1 :  done:
     391           1 :   config_free_lines(lines);
     392           1 :   config_free(mgr, p);
     393           1 :   config_free(mgr, defaults);
     394           1 :   config_mgr_free(mgr);
     395             : 
     396           1 :   tor_free(msg);
     397           1 :   tor_free(s);
     398           1 : }
     399             : 
     400             : static pasture_cfg_t *
     401           6 : parse_and_validate(config_mgr_t *mgr,
     402             :                    const char *inp, const pasture_cfg_t *old, char **msg_out)
     403             : {
     404           6 :   pasture_cfg_t *p = config_new(mgr);
     405           6 :   pasture_cfg_t *result = NULL;
     406           6 :   config_line_t *lines = NULL;
     407             : 
     408           6 :   config_init(mgr, p); // set defaults.
     409           6 :   int r = config_get_lines(inp, &lines, 0);
     410           6 :   tt_int_op(r, OP_EQ, 0);
     411           6 :   r = config_assign(mgr, p, lines, 0, msg_out);
     412           6 :   tt_int_op(r, OP_EQ, 0);
     413           6 :   tor_free(*msg_out); // sets it to NULL
     414           6 :   r = config_validate(mgr, old, p, msg_out);
     415           6 :   if (r < 0)
     416           4 :     goto done;
     417             : 
     418           2 :   tt_ptr_op(*msg_out, OP_EQ, NULL);
     419             :   result = p;
     420             :   p = NULL; // prevent free
     421           6 :  done:
     422           6 :   config_free(mgr, p);
     423           6 :   config_free_lines(lines);
     424           6 :   return result;
     425             : }
     426             : 
     427             : static void
     428           1 : test_confmgr_validate(void *arg)
     429             : {
     430           1 :   (void)arg;
     431           1 :   char *msg = NULL;
     432           1 :   config_mgr_t *mgr = get_mgr(true);
     433           1 :   pasture_cfg_t *p_orig, *p=NULL;
     434             : 
     435           1 :   p_orig = parse_and_validate(mgr, "Llamaname Quest\n"
     436             :                                    "Address 99 camelid way\n"
     437             :                                    "Fuzziness 8\n", NULL, &msg);
     438           1 :   tt_assert(p_orig);
     439             : 
     440             :   // Make sure normalization code was run.
     441           1 :   const alpaca_cfg_t *ac0 = config_mgr_get_obj(mgr, p_orig, ALPACA_IDX);
     442           1 :   const llama_cfg_t *lc0 = config_mgr_get_obj(mgr, p_orig, LLAMA_IDX);
     443           1 :   tt_int_op(ac0->fuzziness, OP_EQ, 8);
     444           1 :   tt_int_op(ac0->square_fuzziness, OP_EQ, 64);
     445           1 :   tt_str_op(lc0->description, OP_EQ, "A llama called Quest.");
     446           1 :   tt_str_op(p_orig->address, OP_EQ, "99 CAMELID WAY");
     447             : 
     448             :   // try a bad llamaname.
     449           1 :   p = parse_and_validate(mgr, "llamaname 123", p_orig, &msg);
     450           1 :   tt_assert(!p);
     451           1 :   tt_str_op(msg, OP_EQ, "It is not a number; it is a free llama!");
     452           1 :   tor_free(msg);
     453             : 
     454             :   // try a llamaname that would crash the post_normalize step, if it ran.
     455           1 :   p = parse_and_validate(mgr, "", p_orig, &msg);
     456           1 :   tt_assert(!p);
     457           1 :   tt_str_op(msg, OP_EQ, "A llama has no name!?");
     458           1 :   tor_free(msg);
     459             : 
     460             :   // Verify that a transition to a less fuzzy alpaca fails.
     461           1 :   p = parse_and_validate(mgr, "Llamaname Quest\n"
     462             :                               "Address 99 camelid way\n"
     463             :                               "Fuzziness 4\n", p_orig, &msg);
     464           1 :   tt_assert(!p);
     465           1 :   tt_str_op(msg, OP_EQ, "An alpaca only becomes more fuzzy over time.");
     466           1 :   tor_free(msg);
     467             : 
     468             :   // Try a transition to a more fuzzy alpaca; it should work fine.
     469           1 :   p = parse_and_validate(mgr, "Llamaname Mercutio\n"
     470             :                               // the default fuzziness is 50
     471             :                               "Address 99 camelid way\n", p_orig, &msg);
     472           1 :   tt_assert(p);
     473           1 :   config_free(mgr, p);
     474             : 
     475             :   // Verify that we can't move the pasture.
     476           1 :   p = parse_and_validate(mgr, "Llamaname Montague\n"
     477             :                               // the default fuzziness is 50
     478             :                               "Address 99 ungulate st\n", p_orig, &msg);
     479           1 :   tt_assert(!p);
     480           1 :   tt_str_op(msg, OP_EQ, "You can't move a pasture.");
     481             : 
     482           1 :  done:
     483           1 :   config_free(mgr, p);
     484           1 :   config_free(mgr, p_orig);
     485           1 :   config_mgr_free(mgr);
     486           1 :   tor_free(msg);
     487           1 : }
     488             : 
     489             : #define CONFMGR_TEST(name, flags)                       \
     490             :   { #name, test_confmgr_ ## name, flags, NULL, NULL }
     491             : 
     492             : struct testcase_t confmgr_tests[] = {
     493             :   CONFMGR_TEST(init, 0),
     494             :   CONFMGR_TEST(magic, 0),
     495             :   CONFMGR_TEST(parse, 0),
     496             :   CONFMGR_TEST(dump, 0),
     497             :   CONFMGR_TEST(validate, 0),
     498             :   END_OF_TESTCASES
     499             : };

Generated by: LCOV version 1.14