test_pubsub_build.c 15 KB

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