tor

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

test_pubsub_build.c (15375B)


      1 /* Copyright (c) 2018-2021, The Tor Project, Inc. */
      2 /* See LICENSE for licensing information */
      3 
      4 #define DISPATCH_PRIVATE
      5 #define PUBSUB_PRIVATE
      6 
      7 #include "test/test.h"
      8 
      9 #include "lib/cc/torint.h"
     10 #include "lib/dispatch/dispatch.h"
     11 #include "lib/dispatch/dispatch_naming.h"
     12 #include "lib/dispatch/dispatch_st.h"
     13 #include "lib/dispatch/msgtypes.h"
     14 #include "lib/pubsub/pubsub_macros.h"
     15 #include "lib/pubsub/pubsub_build.h"
     16 #include "lib/pubsub/pubsub_builder_st.h"
     17 
     18 #include "lib/log/escape.h"
     19 #include "lib/malloc/malloc.h"
     20 #include "lib/string/printf.h"
     21 
     22 #include "test/log_test_helpers.h"
     23 
     24 #include <stdio.h>
     25 #include <string.h>
     26 
     27 static char *
     28 ex_int_fmt(msg_aux_data_t aux)
     29 {
     30  int val = (int) aux.u64;
     31  char *r=NULL;
     32  tor_asprintf(&r, "%d", val);
     33  return r;
     34 }
     35 
     36 static char *
     37 ex_str_fmt(msg_aux_data_t aux)
     38 {
     39  return esc_for_log(aux.ptr);
     40 }
     41 
     42 static void
     43 ex_str_free(msg_aux_data_t aux)
     44 {
     45  tor_free_(aux.ptr);
     46 }
     47 
     48 static dispatch_typefns_t intfns = {
     49  .fmt_fn = ex_int_fmt
     50 };
     51 
     52 static dispatch_typefns_t stringfns = {
     53  .free_fn = ex_str_free,
     54  .fmt_fn = ex_str_fmt
     55 };
     56 
     57 DECLARE_MESSAGE_INT(bunch_of_coconuts, int, int);
     58 DECLARE_PUBLISH(bunch_of_coconuts);
     59 DECLARE_SUBSCRIBE(bunch_of_coconuts, coconut_recipient_cb);
     60 
     61 DECLARE_MESSAGE(yes_we_have_no, string, char *);
     62 DECLARE_PUBLISH(yes_we_have_no);
     63 DECLARE_SUBSCRIBE(yes_we_have_no, absent_item_cb);
     64 
     65 static void
     66 coconut_recipient_cb(const msg_t *m, int n_coconuts)
     67 {
     68  (void)m;
     69  (void)n_coconuts;
     70 }
     71 
     72 static void
     73 absent_item_cb(const msg_t *m, const char *fruitname)
     74 {
     75  (void)m;
     76  (void)fruitname;
     77 }
     78 
     79 #define FLAG_SKIP 99999
     80 
     81 static void
     82 seed_dispatch_builder(pubsub_builder_t *b,
     83                      unsigned fl1, unsigned fl2, unsigned fl3, unsigned fl4)
     84 {
     85  pubsub_connector_t *c = NULL;
     86 
     87  {
     88    c = pubsub_connector_for_subsystem(b, get_subsys_id("sys1"));
     89    DISPATCH_REGISTER_TYPE(c, int, &intfns);
     90    if (fl1 != FLAG_SKIP)
     91      DISPATCH_ADD_PUB_(c, main, bunch_of_coconuts, fl1);
     92    if (fl2 != FLAG_SKIP)
     93      DISPATCH_ADD_SUB_(c, main, yes_we_have_no, fl2);
     94    pubsub_connector_free(c);
     95  }
     96 
     97  {
     98    c = pubsub_connector_for_subsystem(b, get_subsys_id("sys2"));
     99    DISPATCH_REGISTER_TYPE(c, string, &stringfns);
    100    if (fl3 != FLAG_SKIP)
    101      DISPATCH_ADD_PUB_(c, main, yes_we_have_no, fl3);
    102    if (fl4 != FLAG_SKIP)
    103      DISPATCH_ADD_SUB_(c, main, bunch_of_coconuts, fl4);
    104    pubsub_connector_free(c);
    105  }
    106 }
    107 
    108 static void
    109 seed_pubsub_builder_basic(pubsub_builder_t *b)
    110 {
    111  seed_dispatch_builder(b, 0, 0, 0, 0);
    112 }
    113 
    114 /* Regular builder with valid types and messages.
    115 */
    116 static void
    117 test_pubsub_build_types_ok(void *arg)
    118 {
    119  (void)arg;
    120  pubsub_builder_t *b = NULL;
    121  dispatch_t *dispatcher = NULL;
    122  pubsub_connector_t *c = NULL;
    123  pubsub_items_t *items = NULL;
    124 
    125  b = pubsub_builder_new();
    126  seed_pubsub_builder_basic(b);
    127 
    128  dispatcher = pubsub_builder_finalize(b, &items);
    129  b = NULL;
    130  tt_assert(dispatcher);
    131  tt_assert(items);
    132  tt_int_op(smartlist_len(items->items), OP_EQ, 4);
    133 
    134  // Make sure that the bindings got build correctly.
    135  SMARTLIST_FOREACH_BEGIN(items->items, pubsub_cfg_t *, item) {
    136    if (item->is_publish) {
    137      tt_assert(item->pub_binding);
    138      tt_ptr_op(item->pub_binding->dispatch_ptr, OP_EQ, dispatcher);
    139    }
    140  } SMARTLIST_FOREACH_END(item);
    141 
    142  tt_int_op(dispatcher->n_types, OP_GE, 2);
    143  tt_assert(dispatcher->typefns);
    144 
    145  tt_assert(dispatcher->typefns[get_msg_type_id("int")].fmt_fn == ex_int_fmt);
    146  tt_assert(dispatcher->typefns[get_msg_type_id("string")].fmt_fn ==
    147            ex_str_fmt);
    148 
    149  // Now clear the bindings, like we would do before freeing the
    150  // the dispatcher.
    151  pubsub_items_clear_bindings(items);
    152  SMARTLIST_FOREACH_BEGIN(items->items, pubsub_cfg_t *, item) {
    153    if (item->is_publish) {
    154      tt_assert(item->pub_binding);
    155      tt_ptr_op(item->pub_binding->dispatch_ptr, OP_EQ, NULL);
    156    }
    157  } SMARTLIST_FOREACH_END(item);
    158 
    159 done:
    160  pubsub_connector_free(c);
    161  pubsub_builder_free(b);
    162  dispatch_free(dispatcher);
    163  pubsub_items_free(items);
    164 }
    165 
    166 /* We fail if the same type is defined in two places with different functions.
    167 */
    168 static void
    169 test_pubsub_build_types_decls_conflict(void *arg)
    170 {
    171  (void)arg;
    172  pubsub_builder_t *b = NULL;
    173  dispatch_t *dispatcher = NULL;
    174  pubsub_connector_t *c = NULL;
    175 
    176  b = pubsub_builder_new();
    177  seed_pubsub_builder_basic(b);
    178  {
    179    c = pubsub_connector_for_subsystem(b, get_subsys_id("sys3"));
    180    // Extra declaration of int: we don't allow this.
    181    DISPATCH_REGISTER_TYPE(c, int, &stringfns);
    182    pubsub_connector_free(c);
    183  }
    184 
    185  setup_full_capture_of_logs(LOG_WARN);
    186  dispatcher = pubsub_builder_finalize(b, NULL);
    187  b = NULL;
    188  tt_assert(dispatcher == NULL);
    189  // expect_log_msg_containing("(int) declared twice"); // XXXX
    190 
    191 done:
    192  pubsub_connector_free(c);
    193  pubsub_builder_free(b);
    194  dispatch_free(dispatcher);
    195  teardown_capture_of_logs();
    196 }
    197 
    198 /* If a message ID exists but nobody is publishing or subscribing to it,
    199 * that's okay. */
    200 static void
    201 test_pubsub_build_unused_message(void *arg)
    202 {
    203  (void)arg;
    204  pubsub_builder_t *b = NULL;
    205  dispatch_t *dispatcher = NULL;
    206 
    207  b = pubsub_builder_new();
    208  seed_pubsub_builder_basic(b);
    209 
    210  // This message isn't actually generated by anyone, but that will be fine:
    211  // we just log it at info.
    212  get_message_id("unused");
    213  setup_capture_of_logs(LOG_INFO);
    214 
    215  dispatcher = pubsub_builder_finalize(b, NULL);
    216  b = NULL;
    217  tt_assert(dispatcher);
    218  expect_log_msg_containing(
    219     "Nobody is publishing or subscribing to message");
    220 
    221 done:
    222  pubsub_builder_free(b);
    223  dispatch_free(dispatcher);
    224  teardown_capture_of_logs();
    225 }
    226 
    227 /* Publishing or subscribing to a message with no subscribers / publishers
    228 * should fail and warn. */
    229 static void
    230 test_pubsub_build_missing_pubsub(void *arg)
    231 {
    232  (void)arg;
    233  pubsub_builder_t *b = NULL;
    234  dispatch_t *dispatcher = NULL;
    235 
    236  b = pubsub_builder_new();
    237  seed_dispatch_builder(b, 0, 0, FLAG_SKIP, FLAG_SKIP);
    238 
    239  setup_full_capture_of_logs(LOG_WARN);
    240  dispatcher = pubsub_builder_finalize(b, NULL);
    241  b = NULL;
    242  tt_assert(dispatcher == NULL);
    243 
    244  expect_log_msg_containing(
    245       "Message \"bunch_of_coconuts\" has publishers, but no subscribers.");
    246  expect_log_msg_containing(
    247       "Message \"yes_we_have_no\" has subscribers, but no publishers.");
    248 
    249 done:
    250  pubsub_builder_free(b);
    251  dispatch_free(dispatcher);
    252  teardown_capture_of_logs();
    253 }
    254 
    255 /* Make sure that a stub publisher or subscriber prevents an error from
    256 * happening even if there are no other publishers/subscribers for a message
    257 */
    258 static void
    259 test_pubsub_build_stub_pubsub(void *arg)
    260 {
    261  (void)arg;
    262  pubsub_builder_t *b = NULL;
    263  dispatch_t *dispatcher = NULL;
    264 
    265  b = pubsub_builder_new();
    266  seed_dispatch_builder(b, 0, 0, DISP_FLAG_STUB, DISP_FLAG_STUB);
    267 
    268  dispatcher = pubsub_builder_finalize(b, NULL);
    269  b = NULL;
    270  tt_assert(dispatcher);
    271 
    272  // 1 subscriber.
    273  tt_int_op(1, OP_EQ,
    274            dispatcher->table[get_message_id("yes_we_have_no")]->n_enabled);
    275  // no subscribers
    276  tt_ptr_op(NULL, OP_EQ,
    277            dispatcher->table[get_message_id("bunch_of_coconuts")]);
    278 
    279 done:
    280  pubsub_builder_free(b);
    281  dispatch_free(dispatcher);
    282 }
    283 
    284 /* Only one channel per msg id. */
    285 static void
    286 test_pubsub_build_channels_conflict(void *arg)
    287 {
    288  (void)arg;
    289  pubsub_builder_t *b = NULL;
    290  dispatch_t *dispatcher = NULL;
    291  pubsub_connector_t *c = NULL;
    292 
    293  b = pubsub_builder_new();
    294  seed_pubsub_builder_basic(b);
    295  pub_binding_t btmp;
    296 
    297  {
    298    c = pubsub_connector_for_subsystem(b, get_subsys_id("problems"));
    299    /* Usually the DISPATCH_ADD_PUB macro would keep us from using
    300     * the wrong channel */
    301    pubsub_add_pub_(c, &btmp, get_channel_id("hithere"),
    302                    get_message_id("bunch_of_coconuts"),
    303                    get_msg_type_id("int"),
    304                    0 /* flags */,
    305                    "somewhere.c", 22);
    306    pubsub_connector_free(c);
    307  };
    308 
    309  setup_full_capture_of_logs(LOG_WARN);
    310  dispatcher = pubsub_builder_finalize(b, NULL);
    311  b = NULL;
    312  tt_assert(dispatcher == NULL);
    313 
    314  expect_log_msg_containing("Message \"bunch_of_coconuts\" is associated "
    315                            "with multiple inconsistent channels.");
    316 
    317 done:
    318  pubsub_builder_free(b);
    319  dispatch_free(dispatcher);
    320  teardown_capture_of_logs();
    321 }
    322 
    323 /* Only one type per msg id. */
    324 static void
    325 test_pubsub_build_types_conflict(void *arg)
    326 {
    327  (void)arg;
    328  pubsub_builder_t *b = NULL;
    329  dispatch_t *dispatcher = NULL;
    330  pubsub_connector_t *c = NULL;
    331 
    332  b = pubsub_builder_new();
    333  seed_pubsub_builder_basic(b);
    334  pub_binding_t btmp;
    335 
    336  {
    337    c = pubsub_connector_for_subsystem(b, get_subsys_id("problems"));
    338    /* Usually the DISPATCH_ADD_PUB macro would keep us from using
    339     * the wrong channel */
    340    pubsub_add_pub_(c, &btmp, get_channel_id("hithere"),
    341                    get_message_id("bunch_of_coconuts"),
    342                    get_msg_type_id("string"),
    343                    0 /* flags */,
    344                    "somewhere.c", 22);
    345    pubsub_connector_free(c);
    346  };
    347 
    348  setup_full_capture_of_logs(LOG_WARN);
    349  dispatcher = pubsub_builder_finalize(b, NULL);
    350  b = NULL;
    351  tt_assert(dispatcher == NULL);
    352 
    353  expect_log_msg_containing("Message \"bunch_of_coconuts\" is associated "
    354                            "with multiple inconsistent message types.");
    355 
    356 done:
    357  pubsub_builder_free(b);
    358  dispatch_free(dispatcher);
    359  teardown_capture_of_logs();
    360 }
    361 
    362 /* The same module can't publish and subscribe the same message */
    363 static void
    364 test_pubsub_build_pubsub_same(void *arg)
    365 {
    366  (void)arg;
    367  pubsub_builder_t *b = NULL;
    368  dispatch_t *dispatcher = NULL;
    369  pubsub_connector_t *c = NULL;
    370 
    371  b = pubsub_builder_new();
    372  seed_pubsub_builder_basic(b);
    373 
    374  {
    375    c = pubsub_connector_for_subsystem(b, get_subsys_id("sys1"));
    376    // already publishing this.
    377    DISPATCH_ADD_SUB(c, main, bunch_of_coconuts);
    378    pubsub_connector_free(c);
    379  };
    380 
    381  setup_full_capture_of_logs(LOG_WARN);
    382  dispatcher = pubsub_builder_finalize(b, NULL);
    383  b = NULL;
    384  tt_assert(dispatcher == NULL);
    385 
    386  expect_log_msg_containing("Message \"bunch_of_coconuts\" is published "
    387                            "and subscribed by the same subsystem \"sys1\".");
    388 
    389 done:
    390  pubsub_builder_free(b);
    391  dispatch_free(dispatcher);
    392  teardown_capture_of_logs();
    393 }
    394 
    395 /* More than one subsystem may publish or subscribe, and that's okay. */
    396 static void
    397 test_pubsub_build_pubsub_multi(void *arg)
    398 {
    399  (void)arg;
    400  pubsub_builder_t *b = NULL;
    401  dispatch_t *dispatcher = NULL;
    402  pubsub_connector_t *c = NULL;
    403 
    404  b = pubsub_builder_new();
    405  seed_pubsub_builder_basic(b);
    406  pub_binding_t btmp;
    407 
    408  {
    409    c = pubsub_connector_for_subsystem(b, get_subsys_id("sys3"));
    410    DISPATCH_ADD_SUB(c, main, bunch_of_coconuts);
    411    pubsub_add_pub_(c, &btmp, get_channel_id("main"),
    412                    get_message_id("yes_we_have_no"),
    413                    get_msg_type_id("string"),
    414                    0 /* flags */,
    415                    "somewhere.c", 22);
    416    pubsub_connector_free(c);
    417  };
    418 
    419  dispatcher = pubsub_builder_finalize(b, NULL);
    420  b = NULL;
    421  tt_assert(dispatcher);
    422 
    423  // 1 subscribers
    424  tt_int_op(1, OP_EQ,
    425            dispatcher->table[get_message_id("yes_we_have_no")]->n_enabled);
    426  // 2 subscribers.
    427  dtbl_entry_t *ent =
    428    dispatcher->table[get_message_id("bunch_of_coconuts")];
    429  tt_int_op(2, OP_EQ, ent->n_enabled);
    430  tt_int_op(2, OP_EQ, ent->n_fns);
    431  tt_ptr_op(ent->rcv[0].fn, OP_EQ, recv_fn__bunch_of_coconuts);
    432  tt_ptr_op(ent->rcv[1].fn, OP_EQ, recv_fn__bunch_of_coconuts);
    433 
    434 done:
    435  pubsub_builder_free(b);
    436  dispatch_free(dispatcher);
    437 }
    438 
    439 static void
    440 some_other_coconut_hook(const msg_t *m)
    441 {
    442  (void)m;
    443 }
    444 
    445 /* Subscribe hooks should be build correctly when there are a bunch of
    446 * them. */
    447 static void
    448 test_pubsub_build_sub_many(void *arg)
    449 {
    450  (void)arg;
    451  pubsub_builder_t *b = NULL;
    452  dispatch_t *dispatcher = NULL;
    453  pubsub_connector_t *c = NULL;
    454  char *sysname = NULL;
    455  b = pubsub_builder_new();
    456  seed_pubsub_builder_basic(b);
    457 
    458  int i;
    459  for (i = 1; i < 100; ++i) {
    460    tor_asprintf(&sysname, "system%d",i);
    461    c = pubsub_connector_for_subsystem(b, get_subsys_id(sysname));
    462    if (i % 7) {
    463      DISPATCH_ADD_SUB(c, main, bunch_of_coconuts);
    464    } else {
    465      pubsub_add_sub_(c, some_other_coconut_hook,
    466                      get_channel_id("main"),
    467                      get_message_id("bunch_of_coconuts"),
    468                      get_msg_type_id("int"),
    469                      0 /* flags */,
    470                      "somewhere.c", 22);
    471    }
    472    pubsub_connector_free(c);
    473    tor_free(sysname);
    474  };
    475 
    476  dispatcher = pubsub_builder_finalize(b, NULL);
    477  b = NULL;
    478  tt_assert(dispatcher);
    479 
    480  dtbl_entry_t *ent =
    481    dispatcher->table[get_message_id("bunch_of_coconuts")];
    482  tt_int_op(100, OP_EQ, ent->n_enabled);
    483  tt_int_op(100, OP_EQ, ent->n_fns);
    484  tt_ptr_op(ent->rcv[0].fn, OP_EQ, recv_fn__bunch_of_coconuts);
    485  tt_ptr_op(ent->rcv[1].fn, OP_EQ, recv_fn__bunch_of_coconuts);
    486  tt_ptr_op(ent->rcv[76].fn, OP_EQ, recv_fn__bunch_of_coconuts);
    487  tt_ptr_op(ent->rcv[77].fn, OP_EQ, some_other_coconut_hook);
    488  tt_ptr_op(ent->rcv[78].fn, OP_EQ, recv_fn__bunch_of_coconuts);
    489 
    490 done:
    491  pubsub_builder_free(b);
    492  dispatch_free(dispatcher);
    493  tor_free(sysname);
    494 }
    495 
    496 /* It's fine to declare the excl flag. */
    497 static void
    498 test_pubsub_build_excl_ok(void *arg)
    499 {
    500  (void)arg;
    501  pubsub_builder_t *b = NULL;
    502  dispatch_t *dispatcher = NULL;
    503 
    504  b = pubsub_builder_new();
    505  // Try one excl/excl pair and one excl/non pair.
    506  seed_dispatch_builder(b, DISP_FLAG_EXCL, 0,
    507                        DISP_FLAG_EXCL, DISP_FLAG_EXCL);
    508 
    509  dispatcher = pubsub_builder_finalize(b, NULL);
    510  b = NULL;
    511  tt_assert(dispatcher);
    512 
    513  // 1 subscribers
    514  tt_int_op(1, OP_EQ,
    515            dispatcher->table[get_message_id("yes_we_have_no")]->n_enabled);
    516  // 1 subscriber.
    517  tt_int_op(1, OP_EQ,
    518            dispatcher->table[get_message_id("bunch_of_coconuts")]->n_enabled);
    519 
    520 done:
    521  pubsub_builder_free(b);
    522  dispatch_free(dispatcher);
    523 }
    524 
    525 /* but if you declare the excl flag, you need to mean it. */
    526 static void
    527 test_pubsub_build_excl_bad(void *arg)
    528 {
    529  (void)arg;
    530  pubsub_builder_t *b = NULL;
    531  dispatch_t *dispatcher = NULL;
    532  pubsub_connector_t *c = NULL;
    533 
    534  b = pubsub_builder_new();
    535  seed_dispatch_builder(b, DISP_FLAG_EXCL, DISP_FLAG_EXCL,
    536                        0, 0);
    537 
    538  {
    539    c = pubsub_connector_for_subsystem(b, get_subsys_id("sys3"));
    540    DISPATCH_ADD_PUB_(c, main, bunch_of_coconuts, 0);
    541    DISPATCH_ADD_SUB_(c, main, yes_we_have_no, 0);
    542    pubsub_connector_free(c);
    543  };
    544 
    545  setup_full_capture_of_logs(LOG_WARN);
    546  dispatcher = pubsub_builder_finalize(b, NULL);
    547  b = NULL;
    548  tt_assert(dispatcher == NULL);
    549 
    550  expect_log_msg_containing("has multiple publishers, but at least one is "
    551                            "marked as exclusive.");
    552  expect_log_msg_containing("has multiple subscribers, but at least one is "
    553                            "marked as exclusive.");
    554 
    555 done:
    556  pubsub_builder_free(b);
    557  dispatch_free(dispatcher);
    558  teardown_capture_of_logs();
    559 }
    560 
    561 #define T(name, flags)                                          \
    562  { #name, test_pubsub_build_ ## name , (flags), NULL, NULL }
    563 
    564 struct testcase_t pubsub_build_tests[] = {
    565  T(types_ok, TT_FORK),
    566  T(types_decls_conflict, TT_FORK),
    567  T(unused_message, TT_FORK),
    568  T(missing_pubsub, TT_FORK),
    569  T(stub_pubsub, TT_FORK),
    570  T(channels_conflict, TT_FORK),
    571  T(types_conflict, TT_FORK),
    572  T(pubsub_same, TT_FORK),
    573  T(pubsub_multi, TT_FORK),
    574  T(sub_many, TT_FORK),
    575  T(excl_ok, TT_FORK),
    576  T(excl_bad, TT_FORK),
    577  END_OF_TESTCASES
    578 };