123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621 |
- /* Copyright (c) 2018, The Tor Project, Inc. */
- /* See LICENSE for licensing information */
- #define DISPATCH_PRIVATE
- #define PUBSUB_PRIVATE
- #include "test/test.h"
- #include "lib/cc/torint.h"
- #include "lib/dispatch/dispatch.h"
- #include "lib/dispatch/dispatch_naming.h"
- #include "lib/dispatch/dispatch_st.h"
- #include "lib/dispatch/msgtypes.h"
- #include "lib/pubsub/pubsub_macros.h"
- #include "lib/pubsub/pubsub_build.h"
- #include "lib/pubsub/pubsub_builder_st.h"
- #include "lib/log/escape.h"
- #include "lib/malloc/malloc.h"
- #include "lib/string/printf.h"
- #include "test/log_test_helpers.h"
- #include <stdio.h>
- #include <string.h>
- static char *
- ex_int_fmt(msg_aux_data_t aux)
- {
- int val = (int) aux.u64;
- char *r=NULL;
- tor_asprintf(&r, "%d", val);
- return r;
- }
- static char *
- ex_str_fmt(msg_aux_data_t aux)
- {
- return esc_for_log(aux.ptr);
- }
- static void
- ex_str_free(msg_aux_data_t aux)
- {
- tor_free_(aux.ptr);
- }
- static dispatch_typefns_t intfns = {
- .fmt_fn = ex_int_fmt
- };
- static dispatch_typefns_t stringfns = {
- .free_fn = ex_str_free,
- .fmt_fn = ex_str_fmt
- };
- DECLARE_MESSAGE_INT(bunch_of_coconuts, int, int);
- DECLARE_PUBLISH(bunch_of_coconuts);
- DECLARE_SUBSCRIBE(bunch_of_coconuts, coconut_recipient_cb);
- DECLARE_MESSAGE(yes_we_have_no, string, char *);
- DECLARE_PUBLISH(yes_we_have_no);
- DECLARE_SUBSCRIBE(yes_we_have_no, absent_item_cb);
- static void
- coconut_recipient_cb(const msg_t *m, int n_coconuts)
- {
- (void)m;
- (void)n_coconuts;
- }
- static void
- absent_item_cb(const msg_t *m, const char *fruitname)
- {
- (void)m;
- (void)fruitname;
- }
- #define FLAG_SKIP 99999
- static void
- seed_dispatch_builder(pubsub_builder_t *b,
- unsigned fl1, unsigned fl2, unsigned fl3, unsigned fl4)
- {
- pubsub_connector_t *c = NULL;
- {
- c = pubsub_connector_for_subsystem(b, get_subsys_id("sys1"));
- DISPATCH_REGISTER_TYPE(c, int, &intfns);
- if (fl1 != FLAG_SKIP)
- DISPATCH_ADD_PUB_(c, main, bunch_of_coconuts, fl1);
- if (fl2 != FLAG_SKIP)
- DISPATCH_ADD_SUB_(c, main, yes_we_have_no, fl2);
- pubsub_connector_free(c);
- }
- {
- c = pubsub_connector_for_subsystem(b, get_subsys_id("sys2"));
- DISPATCH_REGISTER_TYPE(c, string, &stringfns);
- if (fl3 != FLAG_SKIP)
- DISPATCH_ADD_PUB_(c, main, yes_we_have_no, fl3);
- if (fl4 != FLAG_SKIP)
- DISPATCH_ADD_SUB_(c, main, bunch_of_coconuts, fl4);
- pubsub_connector_free(c);
- }
- }
- static void
- seed_pubsub_builder_basic(pubsub_builder_t *b)
- {
- seed_dispatch_builder(b, 0, 0, 0, 0);
- }
- /* Regular builder with valid types and messages.
- */
- static void
- test_pubsub_build_types_ok(void *arg)
- {
- (void)arg;
- pubsub_builder_t *b = NULL;
- dispatch_t *dispatcher = NULL;
- pubsub_connector_t *c = NULL;
- pubsub_items_t *items = NULL;
- b = pubsub_builder_new();
- seed_pubsub_builder_basic(b);
- dispatcher = pubsub_builder_finalize(b, &items);
- b = NULL;
- tt_assert(dispatcher);
- tt_assert(items);
- tt_int_op(smartlist_len(items->items), OP_EQ, 4);
- // Make sure that the bindings got build correctly.
- SMARTLIST_FOREACH_BEGIN(items->items, pubsub_cfg_t *, item) {
- if (item->is_publish) {
- tt_assert(item->pub_binding);
- tt_ptr_op(item->pub_binding->dispatch_ptr, OP_EQ, dispatcher);
- }
- } SMARTLIST_FOREACH_END(item);
- tt_int_op(dispatcher->n_types, OP_GE, 2);
- tt_assert(dispatcher->typefns);
- tt_assert(dispatcher->typefns[get_msg_type_id("int")].fmt_fn == ex_int_fmt);
- tt_assert(dispatcher->typefns[get_msg_type_id("string")].fmt_fn ==
- ex_str_fmt);
- // Now clear the bindings, like we would do before freeing the
- // the dispatcher.
- pubsub_items_clear_bindings(items);
- SMARTLIST_FOREACH_BEGIN(items->items, pubsub_cfg_t *, item) {
- if (item->is_publish) {
- tt_assert(item->pub_binding);
- tt_ptr_op(item->pub_binding->dispatch_ptr, OP_EQ, NULL);
- }
- } SMARTLIST_FOREACH_END(item);
- done:
- pubsub_connector_free(c);
- pubsub_builder_free(b);
- dispatch_free(dispatcher);
- pubsub_items_free(items);
- }
- /* We fail if the same type is defined in two places with different functions.
- */
- static void
- test_pubsub_build_types_decls_conflict(void *arg)
- {
- (void)arg;
- pubsub_builder_t *b = NULL;
- dispatch_t *dispatcher = NULL;
- pubsub_connector_t *c = NULL;
- b = pubsub_builder_new();
- seed_pubsub_builder_basic(b);
- {
- c = pubsub_connector_for_subsystem(b, get_subsys_id("sys3"));
- // Extra declaration of int: we don't allow this.
- DISPATCH_REGISTER_TYPE(c, int, &stringfns);
- pubsub_connector_free(c);
- }
- setup_full_capture_of_logs(LOG_WARN);
- dispatcher = pubsub_builder_finalize(b, NULL);
- b = NULL;
- tt_assert(dispatcher == NULL);
- // expect_log_msg_containing("(int) declared twice"); // XXXX
- done:
- pubsub_connector_free(c);
- pubsub_builder_free(b);
- dispatch_free(dispatcher);
- teardown_capture_of_logs();
- }
- /* If a message ID exists but nobody is publishing or subscribing to it,
- * that's okay. */
- static void
- test_pubsub_build_unused_message(void *arg)
- {
- (void)arg;
- pubsub_builder_t *b = NULL;
- dispatch_t *dispatcher = NULL;
- b = pubsub_builder_new();
- seed_pubsub_builder_basic(b);
- // This message isn't actually generated by anyone, but that will be fine:
- // we just log it at info.
- get_message_id("unused");
- setup_capture_of_logs(LOG_INFO);
- dispatcher = pubsub_builder_finalize(b, NULL);
- b = NULL;
- tt_assert(dispatcher);
- expect_log_msg_containing(
- "Nobody is publishing or subscribing to message");
- done:
- pubsub_builder_free(b);
- dispatch_free(dispatcher);
- teardown_capture_of_logs();
- }
- /* Publishing or subscribing to a message with no subscribers / publishers
- * should fail and warn. */
- static void
- test_pubsub_build_missing_pubsub(void *arg)
- {
- (void)arg;
- pubsub_builder_t *b = NULL;
- dispatch_t *dispatcher = NULL;
- b = pubsub_builder_new();
- seed_dispatch_builder(b, 0, 0, FLAG_SKIP, FLAG_SKIP);
- setup_full_capture_of_logs(LOG_WARN);
- dispatcher = pubsub_builder_finalize(b, NULL);
- b = NULL;
- tt_assert(dispatcher == NULL);
- expect_log_msg_containing(
- "Message \"bunch_of_coconuts\" has publishers, but no subscribers.");
- expect_log_msg_containing(
- "Message \"yes_we_have_no\" has subscribers, but no publishers.");
- done:
- pubsub_builder_free(b);
- dispatch_free(dispatcher);
- teardown_capture_of_logs();
- }
- /* Make sure that a stub publisher or subscriber prevents an error from
- * happening even if there are no other publishers/subscribers for a message
- */
- static void
- test_pubsub_build_stub_pubsub(void *arg)
- {
- (void)arg;
- pubsub_builder_t *b = NULL;
- dispatch_t *dispatcher = NULL;
- b = pubsub_builder_new();
- seed_dispatch_builder(b, 0, 0, DISP_FLAG_STUB, DISP_FLAG_STUB);
- dispatcher = pubsub_builder_finalize(b, NULL);
- b = NULL;
- tt_assert(dispatcher);
- // 1 subscriber.
- tt_int_op(1, OP_EQ,
- dispatcher->table[get_message_id("yes_we_have_no")]->n_enabled);
- // no subscribers
- tt_ptr_op(NULL, OP_EQ,
- dispatcher->table[get_message_id("bunch_of_coconuts")]);
- done:
- pubsub_builder_free(b);
- dispatch_free(dispatcher);
- }
- /* Only one channel per msg id. */
- static void
- test_pubsub_build_channels_conflict(void *arg)
- {
- (void)arg;
- pubsub_builder_t *b = NULL;
- dispatch_t *dispatcher = NULL;
- pubsub_connector_t *c = NULL;
- b = pubsub_builder_new();
- seed_pubsub_builder_basic(b);
- pub_binding_t btmp;
- {
- c = pubsub_connector_for_subsystem(b, get_subsys_id("problems"));
- /* Usually the DISPATCH_ADD_PUB macro would keep us from using
- * the wrong channel */
- pubsub_add_pub_(c, &btmp, get_channel_id("hithere"),
- get_message_id("bunch_of_coconuts"),
- get_msg_type_id("int"),
- 0 /* flags */,
- "somewhere.c", 22);
- pubsub_connector_free(c);
- };
- setup_full_capture_of_logs(LOG_WARN);
- dispatcher = pubsub_builder_finalize(b, NULL);
- b = NULL;
- tt_assert(dispatcher == NULL);
- expect_log_msg_containing("Message \"bunch_of_coconuts\" is associated "
- "with multiple inconsistent channels.");
- done:
- pubsub_builder_free(b);
- dispatch_free(dispatcher);
- teardown_capture_of_logs();
- }
- /* Only one type per msg id. */
- static void
- test_pubsub_build_types_conflict(void *arg)
- {
- (void)arg;
- pubsub_builder_t *b = NULL;
- dispatch_t *dispatcher = NULL;
- pubsub_connector_t *c = NULL;
- b = pubsub_builder_new();
- seed_pubsub_builder_basic(b);
- pub_binding_t btmp;
- {
- c = pubsub_connector_for_subsystem(b, get_subsys_id("problems"));
- /* Usually the DISPATCH_ADD_PUB macro would keep us from using
- * the wrong channel */
- pubsub_add_pub_(c, &btmp, get_channel_id("hithere"),
- get_message_id("bunch_of_coconuts"),
- get_msg_type_id("string"),
- 0 /* flags */,
- "somewhere.c", 22);
- pubsub_connector_free(c);
- };
- setup_full_capture_of_logs(LOG_WARN);
- dispatcher = pubsub_builder_finalize(b, NULL);
- b = NULL;
- tt_assert(dispatcher == NULL);
- expect_log_msg_containing("Message \"bunch_of_coconuts\" is associated "
- "with multiple inconsistent message types.");
- done:
- pubsub_builder_free(b);
- dispatch_free(dispatcher);
- teardown_capture_of_logs();
- }
- /* The same module can't publish and subscribe the same message */
- static void
- test_pubsub_build_pubsub_same(void *arg)
- {
- (void)arg;
- pubsub_builder_t *b = NULL;
- dispatch_t *dispatcher = NULL;
- pubsub_connector_t *c = NULL;
- b = pubsub_builder_new();
- seed_pubsub_builder_basic(b);
- {
- c = pubsub_connector_for_subsystem(b, get_subsys_id("sys1"));
- // already publishing this.
- DISPATCH_ADD_SUB(c, main, bunch_of_coconuts);
- pubsub_connector_free(c);
- };
- setup_full_capture_of_logs(LOG_WARN);
- dispatcher = pubsub_builder_finalize(b, NULL);
- b = NULL;
- tt_assert(dispatcher == NULL);
- expect_log_msg_containing("Message \"bunch_of_coconuts\" is published "
- "and subscribed by the same subsystem \"sys1\".");
- done:
- pubsub_builder_free(b);
- dispatch_free(dispatcher);
- teardown_capture_of_logs();
- }
- /* More than one subsystem may publish or subscribe, and that's okay. */
- static void
- test_pubsub_build_pubsub_multi(void *arg)
- {
- (void)arg;
- pubsub_builder_t *b = NULL;
- dispatch_t *dispatcher = NULL;
- pubsub_connector_t *c = NULL;
- b = pubsub_builder_new();
- seed_pubsub_builder_basic(b);
- pub_binding_t btmp;
- {
- c = pubsub_connector_for_subsystem(b, get_subsys_id("sys3"));
- DISPATCH_ADD_SUB(c, main, bunch_of_coconuts);
- pubsub_add_pub_(c, &btmp, get_channel_id("main"),
- get_message_id("yes_we_have_no"),
- get_msg_type_id("string"),
- 0 /* flags */,
- "somewhere.c", 22);
- pubsub_connector_free(c);
- };
- dispatcher = pubsub_builder_finalize(b, NULL);
- b = NULL;
- tt_assert(dispatcher);
- // 1 subscribers
- tt_int_op(1, OP_EQ,
- dispatcher->table[get_message_id("yes_we_have_no")]->n_enabled);
- // 2 subscribers.
- dtbl_entry_t *ent =
- dispatcher->table[get_message_id("bunch_of_coconuts")];
- tt_int_op(2, OP_EQ, ent->n_enabled);
- tt_int_op(2, OP_EQ, ent->n_fns);
- tt_ptr_op(ent->rcv[0].fn, OP_EQ, recv_fn__bunch_of_coconuts);
- tt_ptr_op(ent->rcv[1].fn, OP_EQ, recv_fn__bunch_of_coconuts);
- done:
- pubsub_builder_free(b);
- dispatch_free(dispatcher);
- }
- static void
- some_other_coconut_hook(const msg_t *m)
- {
- (void)m;
- }
- /* Subscribe hooks should be build correctly when there are a bunch of
- * them. */
- static void
- test_pubsub_build_sub_many(void *arg)
- {
- (void)arg;
- pubsub_builder_t *b = NULL;
- dispatch_t *dispatcher = NULL;
- pubsub_connector_t *c = NULL;
- char *sysname = NULL;
- b = pubsub_builder_new();
- seed_pubsub_builder_basic(b);
- int i;
- for (i = 1; i < 100; ++i) {
- tor_asprintf(&sysname, "system%d",i);
- c = pubsub_connector_for_subsystem(b, get_subsys_id(sysname));
- if (i % 7) {
- DISPATCH_ADD_SUB(c, main, bunch_of_coconuts);
- } else {
- pubsub_add_sub_(c, some_other_coconut_hook,
- get_channel_id("main"),
- get_message_id("bunch_of_coconuts"),
- get_msg_type_id("int"),
- 0 /* flags */,
- "somewhere.c", 22);
- }
- pubsub_connector_free(c);
- tor_free(sysname);
- };
- dispatcher = pubsub_builder_finalize(b, NULL);
- b = NULL;
- tt_assert(dispatcher);
- dtbl_entry_t *ent =
- dispatcher->table[get_message_id("bunch_of_coconuts")];
- tt_int_op(100, OP_EQ, ent->n_enabled);
- tt_int_op(100, OP_EQ, ent->n_fns);
- tt_ptr_op(ent->rcv[0].fn, OP_EQ, recv_fn__bunch_of_coconuts);
- tt_ptr_op(ent->rcv[1].fn, OP_EQ, recv_fn__bunch_of_coconuts);
- tt_ptr_op(ent->rcv[76].fn, OP_EQ, recv_fn__bunch_of_coconuts);
- tt_ptr_op(ent->rcv[77].fn, OP_EQ, some_other_coconut_hook);
- tt_ptr_op(ent->rcv[78].fn, OP_EQ, recv_fn__bunch_of_coconuts);
- done:
- pubsub_builder_free(b);
- dispatch_free(dispatcher);
- tor_free(sysname);
- }
- /* The same subsystem can only declare one publish or subscribe. */
- static void
- test_pubsub_build_pubsub_redundant(void *arg)
- {
- (void)arg;
- pubsub_builder_t *b = NULL;
- dispatch_t *dispatcher = NULL;
- pubsub_connector_t *c = NULL;
- b = pubsub_builder_new();
- seed_pubsub_builder_basic(b);
- pub_binding_t btmp;
- {
- c = pubsub_connector_for_subsystem(b, get_subsys_id("sys2"));
- DISPATCH_ADD_SUB(c, main, bunch_of_coconuts);
- pubsub_add_pub_(c, &btmp, get_channel_id("main"),
- get_message_id("yes_we_have_no"),
- get_msg_type_id("string"),
- 0 /* flags */,
- "somewhere.c", 22);
- pubsub_connector_free(c);
- };
- setup_full_capture_of_logs(LOG_WARN);
- dispatcher = pubsub_builder_finalize(b, NULL);
- b = NULL;
- tt_assert(dispatcher == NULL);
- expect_log_msg_containing(
- "Message \"yes_we_have_no\" is configured to be published by "
- "subsystem \"sys2\" more than once.");
- expect_log_msg_containing(
- "Message \"bunch_of_coconuts\" is configured to be subscribed by "
- "subsystem \"sys2\" more than once.");
- done:
- pubsub_builder_free(b);
- dispatch_free(dispatcher);
- teardown_capture_of_logs();
- }
- /* It's fine to declare the excl flag. */
- static void
- test_pubsub_build_excl_ok(void *arg)
- {
- (void)arg;
- pubsub_builder_t *b = NULL;
- dispatch_t *dispatcher = NULL;
- b = pubsub_builder_new();
- // Try one excl/excl pair and one excl/non pair.
- seed_dispatch_builder(b, DISP_FLAG_EXCL, 0,
- DISP_FLAG_EXCL, DISP_FLAG_EXCL);
- dispatcher = pubsub_builder_finalize(b, NULL);
- b = NULL;
- tt_assert(dispatcher);
- // 1 subscribers
- tt_int_op(1, OP_EQ,
- dispatcher->table[get_message_id("yes_we_have_no")]->n_enabled);
- // 1 subscriber.
- tt_int_op(1, OP_EQ,
- dispatcher->table[get_message_id("bunch_of_coconuts")]->n_enabled);
- done:
- pubsub_builder_free(b);
- dispatch_free(dispatcher);
- }
- /* but if you declare the excl flag, you need to mean it. */
- static void
- test_pubsub_build_excl_bad(void *arg)
- {
- (void)arg;
- pubsub_builder_t *b = NULL;
- dispatch_t *dispatcher = NULL;
- pubsub_connector_t *c = NULL;
- b = pubsub_builder_new();
- seed_dispatch_builder(b, DISP_FLAG_EXCL, DISP_FLAG_EXCL,
- 0, 0);
- {
- c = pubsub_connector_for_subsystem(b, get_subsys_id("sys3"));
- DISPATCH_ADD_PUB_(c, main, bunch_of_coconuts, 0);
- DISPATCH_ADD_SUB_(c, main, yes_we_have_no, 0);
- pubsub_connector_free(c);
- };
- setup_full_capture_of_logs(LOG_WARN);
- dispatcher = pubsub_builder_finalize(b, NULL);
- b = NULL;
- tt_assert(dispatcher == NULL);
- expect_log_msg_containing("has multiple publishers, but at least one is "
- "marked as exclusive.");
- expect_log_msg_containing("has multiple subscribers, but at least one is "
- "marked as exclusive.");
- done:
- pubsub_builder_free(b);
- dispatch_free(dispatcher);
- teardown_capture_of_logs();
- }
- #define T(name, flags) \
- { #name, test_pubsub_build_ ## name , (flags), NULL, NULL }
- struct testcase_t pubsub_build_tests[] = {
- T(types_ok, TT_FORK),
- T(types_decls_conflict, TT_FORK),
- T(unused_message, TT_FORK),
- T(missing_pubsub, TT_FORK),
- T(stub_pubsub, TT_FORK),
- T(channels_conflict, TT_FORK),
- T(types_conflict, TT_FORK),
- T(pubsub_same, TT_FORK),
- T(pubsub_multi, TT_FORK),
- T(sub_many, TT_FORK),
- T(pubsub_redundant, TT_FORK),
- T(excl_ok, TT_FORK),
- T(excl_bad, TT_FORK),
- END_OF_TESTCASES
- };
|