ccsession_unittests.cc 51 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245
  1. // Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <config.h>
  15. #include <gtest/gtest.h>
  16. #include <config/tests/fake_session.h>
  17. #include <config/ccsession.h>
  18. #include <fstream>
  19. #include <config/tests/data_def_unittests_config.h>
  20. #include <log/logger_name.h>
  21. #include <boost/scoped_ptr.hpp>
  22. #include <boost/bind.hpp>
  23. using namespace isc::data;
  24. using namespace isc::config;
  25. using namespace isc::cc;
  26. using namespace std;
  27. namespace {
  28. std::string
  29. ccspecfile(const std::string& name) {
  30. return (std::string(TEST_DATA_PATH) + "/" + name);
  31. }
  32. ElementPtr
  33. el(const std::string& str) {
  34. return (Element::fromJSON(str));
  35. }
  36. class CCSessionTest : public ::testing::Test {
  37. protected:
  38. CCSessionTest() : session(el("[]"), el("[]"), el("[]")),
  39. root_name(isc::log::getRootLoggerName())
  40. {
  41. // upon creation of a ModuleCCSession, the class
  42. // sends its specification to the config manager.
  43. // it expects an ok answer back, so every time we
  44. // create a ModuleCCSession, we must set an initial
  45. // ok answer.
  46. session.getMessages()->add(createAnswer());
  47. }
  48. ConstElementPtr rpcCheck(const std::string& reply) {
  49. ModuleCCSession mccs(ccspecfile("spec1.spec"), session, NULL, NULL,
  50. false, false);
  51. // Prepare the answer beforehand, it'll block until it gets one
  52. const ConstElementPtr reply_el(el(reply));
  53. session.getMessages()->add(reply_el);
  54. const ConstElementPtr
  55. result(mccs.rpcCall("test", "Spec2",
  56. el("{\"param1\": \"Param 1\","
  57. "\"param2\": \"Param 2\"}")));
  58. const ConstElementPtr
  59. request(el("[\"Spec2\", \"*\", {"
  60. " \"command\": [\"test\", {"
  61. " \"param1\": \"Param 1\","
  62. " \"param2\": \"Param 2\""
  63. "}]}, -1, true]"));
  64. // The 0th one is from the initialization, to ConfigManager.
  65. // our is the 1st.
  66. EXPECT_TRUE(request->equals(*session.getMsgQueue()->get(1))) <<
  67. session.getMsgQueue()->get(1)->toWire();
  68. return (result);
  69. }
  70. ~CCSessionTest() {
  71. isc::log::setRootLoggerName(root_name);
  72. }
  73. FakeSession session;
  74. const std::string root_name;
  75. };
  76. void
  77. notificationCallback(std::vector<std::string>* called,
  78. const std::string& id, const std::string& notification,
  79. const ConstElementPtr& params)
  80. {
  81. called->push_back(id);
  82. EXPECT_EQ("event", notification);
  83. EXPECT_TRUE(el("{\"param\": true}")->equals(*params));
  84. }
  85. TEST_F(CCSessionTest, receiveNotification) {
  86. // Not subscribed to the group yet
  87. ModuleCCSession mccs(ccspecfile("spec1.spec"), session, NULL, NULL,
  88. false, false);
  89. EXPECT_FALSE(session.haveSubscription("notifications/group", "*"));
  90. std::vector<std::string> called;
  91. // Subscribe to the notification. Twice.
  92. const ModuleCCSession::NotificationID& first =
  93. mccs.subscribeNotification("group", boost::bind(&notificationCallback,
  94. &called, "first",
  95. _1, _2));
  96. const ModuleCCSession::NotificationID& second =
  97. mccs.subscribeNotification("group", boost::bind(&notificationCallback,
  98. &called, "second",
  99. _1, _2));
  100. EXPECT_TRUE(session.haveSubscription("notifications/group", "*"));
  101. EXPECT_TRUE(called.empty());
  102. // Send the notification
  103. session.getMessages()->add(el("{"
  104. " \"notification\": ["
  105. " \"event\", {"
  106. " \"param\": true"
  107. " }"
  108. " ]"
  109. " }"));
  110. mccs.checkCommand();
  111. ASSERT_EQ(2, called.size());
  112. EXPECT_EQ("first", called[0]);
  113. EXPECT_EQ("second", called[1]);
  114. called.clear();
  115. // Unsubscribe one of them
  116. mccs.unsubscribeNotification(first);
  117. // We are still subscribed to the group and handle the requests
  118. EXPECT_TRUE(session.haveSubscription("notifications/group", "*"));
  119. // Send the notification
  120. session.getMessages()->add(el("{"
  121. " \"notification\": ["
  122. " \"event\", {"
  123. " \"param\": true"
  124. " }"
  125. " ]"
  126. " }"));
  127. mccs.checkCommand();
  128. ASSERT_EQ(1, called.size());
  129. EXPECT_EQ("second", called[0]);
  130. // Try to unsubscribe again. That should fail.
  131. EXPECT_THROW(mccs.unsubscribeNotification(first), isc::InvalidParameter);
  132. EXPECT_TRUE(session.haveSubscription("notifications/group", "*"));
  133. // Unsubscribe the other one too. That should cancel the upstream
  134. // subscription
  135. mccs.unsubscribeNotification(second);
  136. EXPECT_FALSE(session.haveSubscription("notifications/group", "*"));
  137. }
  138. // Test we can send an RPC (command) and get an answer. The answer is success
  139. // in this case.
  140. TEST_F(CCSessionTest, rpcCallSuccess) {
  141. const ConstElementPtr result =
  142. rpcCheck("{\"result\": [0, {\"Hello\": \"a\"}]}");
  143. EXPECT_TRUE(el("{\"Hello\": \"a\"}")->equals(*result));
  144. }
  145. // Test success of RPC, but the answer is empty (eg. a void function on the
  146. // remote side).
  147. TEST_F(CCSessionTest, rpcCallSuccessNone) {
  148. EXPECT_FALSE(rpcCheck("{\"result\": [0]}"));
  149. }
  150. // Test it successfully raises CCSessionError if the answer is malformed.
  151. TEST_F(CCSessionTest, rpcCallMalformedAnswer) {
  152. EXPECT_THROW(rpcCheck("[\"Nonsense\"]"), CCSessionError);
  153. }
  154. // Test it raises exception when the remote side reports an error
  155. TEST_F(CCSessionTest, rpcCallError) {
  156. EXPECT_THROW(rpcCheck("{\"result\": [1, \"Error\"]}"), RPCError);
  157. }
  158. // Test it raises exception when the remote side doesn't exist
  159. TEST_F(CCSessionTest, rpcNoRecpt) {
  160. EXPECT_THROW(rpcCheck("{\"result\": [-1, \"Error\"]}"),
  161. RPCRecipientMissing);
  162. }
  163. // Test sending a notification
  164. TEST_F(CCSessionTest, notify) {
  165. ModuleCCSession mccs(ccspecfile("spec1.spec"), session, NULL, NULL, false,
  166. false);
  167. mccs.notify("group", "event", el("{\"param\": true}"));
  168. const ConstElementPtr notification(el(
  169. "["
  170. " \"notifications/group\","
  171. " \"*\","
  172. " {"
  173. " \"notification\": ["
  174. " \"event\", {"
  175. " \"param\": true"
  176. " }"
  177. " ]"
  178. " },"
  179. " -1"
  180. "]"));
  181. EXPECT_TRUE(notification->equals(*session.getMsgQueue()->get(1))) <<
  182. session.getMsgQueue()->get(1)->toWire();
  183. }
  184. // Test sending a notification
  185. TEST_F(CCSessionTest, notifyNoParams) {
  186. ModuleCCSession mccs(ccspecfile("spec1.spec"), session, NULL, NULL, false,
  187. false);
  188. mccs.notify("group", "event");
  189. const ConstElementPtr notification(el(
  190. "["
  191. " \"notifications/group\","
  192. " \"*\","
  193. " {"
  194. " \"notification\": [\"event\"]"
  195. " },"
  196. " -1"
  197. "]"));
  198. EXPECT_TRUE(notification->equals(*session.getMsgQueue()->get(1))) <<
  199. session.getMsgQueue()->get(1)->toWire();
  200. }
  201. // Try to subscribe and unsubscribe once again
  202. TEST_F(CCSessionTest, subscribe) {
  203. ModuleCCSession mccs(ccspecfile("spec1.spec"), session, NULL, NULL, false,
  204. false);
  205. EXPECT_FALSE(session.haveSubscription("A group", "*"));
  206. mccs.subscribe("A group");
  207. EXPECT_TRUE(session.haveSubscription("A group", "*"));
  208. mccs.unsubscribe("A group");
  209. EXPECT_FALSE(session.haveSubscription("A group", "*"));
  210. }
  211. TEST_F(CCSessionTest, createAnswer) {
  212. ConstElementPtr answer;
  213. answer = createAnswer();
  214. EXPECT_EQ("{ \"result\": [ 0 ] }", answer->str());
  215. answer = createAnswer(1, "error");
  216. EXPECT_EQ("{ \"result\": [ 1, \"error\" ] }", answer->str());
  217. EXPECT_THROW(createAnswer(1, ElementPtr()), CCSessionError);
  218. EXPECT_THROW(createAnswer(1, Element::create(1)), CCSessionError);
  219. ConstElementPtr arg = el("[ \"just\", \"some\", \"data\" ]");
  220. answer = createAnswer(0, arg);
  221. EXPECT_EQ("{ \"result\": [ 0, [ \"just\", \"some\", \"data\" ] ] }", answer->str());
  222. }
  223. TEST_F(CCSessionTest, parseAnswer) {
  224. ConstElementPtr answer;
  225. ConstElementPtr arg;
  226. int rcode;
  227. EXPECT_THROW(parseAnswer(rcode, ElementPtr()), CCSessionError);
  228. EXPECT_THROW(parseAnswer(rcode, el("1")), CCSessionError);
  229. EXPECT_THROW(parseAnswer(rcode, el("[]")), CCSessionError);
  230. EXPECT_THROW(parseAnswer(rcode, el("{ }")), CCSessionError);
  231. EXPECT_THROW(parseAnswer(rcode, el("{ \"something\": 1 }")), CCSessionError);
  232. EXPECT_THROW(parseAnswer(rcode, el("{ \"result\": 0 }")), CCSessionError);
  233. EXPECT_THROW(parseAnswer(rcode, el("{ \"result\": 1 }")), CCSessionError);
  234. EXPECT_THROW(parseAnswer(rcode, el("{ \"result\": [ 1 ] }")), CCSessionError);
  235. EXPECT_THROW(parseAnswer(rcode, el("{ \"result\": [ 1, 1 ] }")), CCSessionError);
  236. answer = el("{ \"result\": [ 0 ] }");
  237. arg = parseAnswer(rcode, answer);
  238. EXPECT_EQ(0, rcode);
  239. EXPECT_TRUE(isNull(arg));
  240. answer = el("{ \"result\": [ 1, \"error\"] }");
  241. arg = parseAnswer(rcode, answer);
  242. EXPECT_EQ(1, rcode);
  243. EXPECT_EQ("error", arg->stringValue());
  244. answer = el("{ \"result\": [ 0, [ \"just\", \"some\", \"data\" ] ] }");
  245. arg = parseAnswer(rcode, answer);
  246. EXPECT_EQ(0, rcode);
  247. EXPECT_EQ("[ \"just\", \"some\", \"data\" ]", arg->str());
  248. }
  249. TEST_F(CCSessionTest, createCommand) {
  250. ConstElementPtr command;
  251. ConstElementPtr arg;
  252. command = createCommand("my_command");
  253. ASSERT_EQ("{ \"command\": [ \"my_command\" ] }", command->str());
  254. arg = el("1");
  255. command = createCommand("my_command", arg);
  256. ASSERT_EQ("{ \"command\": [ \"my_command\", 1 ] }", command->str());
  257. arg = el("[ \"a\", \"b\" ]");
  258. command = createCommand("my_cmd", arg);
  259. ASSERT_EQ("{ \"command\": [ \"my_cmd\", [ \"a\", \"b\" ] ] }", command->str());
  260. arg = el("{ \"a\": \"map\" }");
  261. command = createCommand("foo", arg);
  262. ASSERT_EQ("{ \"command\": [ \"foo\", { \"a\": \"map\" } ] }", command->str());
  263. }
  264. TEST_F(CCSessionTest, parseCommand) {
  265. ConstElementPtr arg;
  266. std::string cmd;
  267. // should throw
  268. EXPECT_THROW(parseCommand(arg, ElementPtr()), CCSessionError);
  269. EXPECT_THROW(parseCommand(arg, el("1")), CCSessionError);
  270. EXPECT_THROW(parseCommand(arg, el("{ }")), CCSessionError);
  271. EXPECT_THROW(parseCommand(arg, el("{ \"not a command\": 1 }")), CCSessionError);
  272. EXPECT_THROW(parseCommand(arg, el("{ \"command\": 1 }")), CCSessionError);
  273. EXPECT_THROW(parseCommand(arg, el("{ \"command\": [] }")), CCSessionError);
  274. EXPECT_THROW(parseCommand(arg, el("{ \"command\": [ 1 ] }")), CCSessionError);
  275. cmd = parseCommand(arg, el("{ \"command\": [ \"my_command\" ] }"));
  276. EXPECT_EQ("my_command", cmd);
  277. EXPECT_EQ(*arg, *Element::createMap());
  278. cmd = parseCommand(arg, el("{ \"command\": [ \"my_command\", 1 ] }"));
  279. EXPECT_EQ("my_command", cmd);
  280. EXPECT_EQ("1", arg->str());
  281. parseCommand(arg, el("{ \"command\": [ \"my_command\", [ \"some\", \"argument\", \"list\" ] ] }"));
  282. EXPECT_EQ("my_command", cmd);
  283. EXPECT_EQ("[ \"some\", \"argument\", \"list\" ]", arg->str());
  284. }
  285. TEST_F(CCSessionTest, session1) {
  286. EXPECT_FALSE(session.haveSubscription("Spec1", "*"));
  287. ModuleCCSession mccs(ccspecfile("spec1.spec"), session, NULL, NULL,
  288. true, false);
  289. EXPECT_TRUE(session.haveSubscription("Spec1", "*"));
  290. EXPECT_EQ(1, session.getMsgQueue()->size());
  291. ConstElementPtr msg;
  292. std::string group, to;
  293. msg = session.getFirstMessage(group, to);
  294. EXPECT_EQ("{ \"command\": [ \"module_spec\", { \"module_name\": \"Spec1\" } ] }", msg->str());
  295. EXPECT_EQ("ConfigManager", group);
  296. EXPECT_EQ("*", to);
  297. EXPECT_EQ(0, session.getMsgQueue()->size());
  298. // with this argument, the session should not automatically
  299. // subscribe to logging config
  300. EXPECT_FALSE(session.haveSubscription("Logging", "*"));
  301. }
  302. TEST_F(CCSessionTest, session2) {
  303. EXPECT_FALSE(session.haveSubscription("Spec2", "*"));
  304. ModuleCCSession mccs(ccspecfile("spec2.spec"), session, NULL, NULL,
  305. true, false);
  306. EXPECT_TRUE(session.haveSubscription("Spec2", "*"));
  307. EXPECT_EQ(1, session.getMsgQueue()->size());
  308. ConstElementPtr msg;
  309. std::string group, to;
  310. msg = session.getFirstMessage(group, to);
  311. EXPECT_EQ("{ \"command\": [ \"module_spec\", { \"commands\": [ { \"command_args\": [ { \"item_default\": \"\", \"item_name\": \"message\", \"item_optional\": false, \"item_type\": \"string\" } ], \"command_description\": \"Print the given message to stdout\", \"command_name\": \"print_message\" }, { \"command_args\": [ ], \"command_description\": \"Shut down BIND 10\", \"command_name\": \"shutdown\" } ], \"config_data\": [ { \"item_default\": 1, \"item_name\": \"item1\", \"item_optional\": false, \"item_type\": \"integer\" }, { \"item_default\": 1.1, \"item_name\": \"item2\", \"item_optional\": false, \"item_type\": \"real\" }, { \"item_default\": true, \"item_name\": \"item3\", \"item_optional\": false, \"item_type\": \"boolean\" }, { \"item_default\": \"test\", \"item_name\": \"item4\", \"item_optional\": false, \"item_type\": \"string\" }, { \"item_default\": [ \"a\", \"b\" ], \"item_name\": \"item5\", \"item_optional\": false, \"item_type\": \"list\", \"list_item_spec\": { \"item_default\": \"\", \"item_name\": \"list_element\", \"item_optional\": false, \"item_type\": \"string\" } }, { \"item_default\": { }, \"item_name\": \"item6\", \"item_optional\": false, \"item_type\": \"map\", \"map_item_spec\": [ { \"item_default\": \"default\", \"item_name\": \"value1\", \"item_optional\": true, \"item_type\": \"string\" }, { \"item_name\": \"value2\", \"item_optional\": true, \"item_type\": \"integer\" } ] } ], \"module_name\": \"Spec2\", \"statistics\": [ { \"item_default\": \"1970-01-01T00:00:00Z\", \"item_description\": \"A dummy date time\", \"item_format\": \"date-time\", \"item_name\": \"dummy_time\", \"item_optional\": false, \"item_title\": \"Dummy Time\", \"item_type\": \"string\" } ] } ] }", msg->str());
  312. EXPECT_EQ("ConfigManager", group);
  313. EXPECT_EQ("*", to);
  314. EXPECT_EQ(0, session.getMsgQueue()->size());
  315. }
  316. TEST_F(CCSessionTest, session_close) {
  317. // Test whether ModuleCCSession automatically sends a 'stopping'
  318. // message when it is destroyed
  319. ConstElementPtr msg;
  320. std::string group, to;
  321. EXPECT_FALSE(session.haveSubscription("Spec2", "*"));
  322. boost::scoped_ptr<ModuleCCSession> mccs(new ModuleCCSession(
  323. ccspecfile("spec2.spec"),
  324. session, NULL, NULL,
  325. true, false));
  326. EXPECT_TRUE(session.haveSubscription("Spec2", "*"));
  327. // The initial message is irrelevant for this test
  328. // (see session2 test), drop it
  329. session.getFirstMessage(group, to);
  330. // Queue should now be empty
  331. ASSERT_EQ(0, session.getMsgQueue()->size());
  332. // Invoke the destructor
  333. mccs.reset();
  334. // Destructor should have caused a new message
  335. ASSERT_EQ(1, session.getMsgQueue()->size());
  336. msg = session.getFirstMessage(group, to);
  337. EXPECT_EQ("{ \"command\": [ \"stopping\", "
  338. "{ \"module_name\": \"Spec2\" } ] }", msg->str());
  339. EXPECT_EQ("ConfigManager", group);
  340. EXPECT_EQ("*", to);
  341. EXPECT_EQ(0, session.getMsgQueue()->size());
  342. }
  343. TEST_F(CCSessionTest, session_close_exception) {
  344. // Test whether an exception encountered during the destructor is
  345. // handled correctly
  346. ConstElementPtr msg;
  347. std::string group, to;
  348. EXPECT_FALSE(session.haveSubscription("Spec2", "*"));
  349. boost::scoped_ptr<ModuleCCSession> mccs(new ModuleCCSession(
  350. ccspecfile("spec2.spec"),
  351. session, NULL, NULL,
  352. true, false));
  353. EXPECT_TRUE(session.haveSubscription("Spec2", "*"));
  354. // The initial message is irrelevant for this test
  355. // (see session2 test), drop it
  356. session.getFirstMessage(group, to);
  357. // Queue should now be empty
  358. ASSERT_EQ(0, session.getMsgQueue()->size());
  359. // Set fake session to throw an exception
  360. session.setThrowOnSend(true);
  361. // Invoke the destructor
  362. mccs.reset();
  363. // Destructor should not have caused a new message (since fakesession
  364. // should have thrown an exception)
  365. ASSERT_EQ(0, session.getMsgQueue()->size());
  366. //EXPECT_EQ(0, session.getMsgQueue()->size());
  367. }
  368. ConstElementPtr my_config_handler(ConstElementPtr new_config) {
  369. if (new_config && new_config->contains("item1") &&
  370. new_config->get("item1")->intValue() == 5) {
  371. return (createAnswer(6, "I do not like the number 5"));
  372. }
  373. return (createAnswer());
  374. }
  375. ConstElementPtr my_command_handler(const std::string& command,
  376. ConstElementPtr arg)
  377. {
  378. if (command == "good_command") {
  379. return (createAnswer());
  380. } else if (command == "command_with_arg") {
  381. if (arg->contains("number")) {
  382. if (arg->get("number")->getType() == Element::integer) {
  383. return (createAnswer(0, el("2")));
  384. } else {
  385. return (createAnswer(1, "arg bad type"));
  386. }
  387. } else {
  388. return (createAnswer(1, "arg missing"));
  389. }
  390. } else {
  391. return (createAnswer(1, "bad command"));
  392. }
  393. }
  394. TEST_F(CCSessionTest, session3) {
  395. // client will ask for config
  396. session.getMessages()->add(createAnswer(0, el("{}")));
  397. EXPECT_FALSE(session.haveSubscription("Spec2", "*"));
  398. ModuleCCSession mccs(ccspecfile("spec2.spec"), session, my_config_handler,
  399. my_command_handler, true, false);
  400. EXPECT_TRUE(session.haveSubscription("Spec2", "*"));
  401. EXPECT_EQ(2, session.getMsgQueue()->size());
  402. ConstElementPtr msg;
  403. std::string group, to;
  404. msg = session.getFirstMessage(group, to);
  405. EXPECT_EQ("{ \"command\": [ \"module_spec\", { \"commands\": [ { \"command_args\": [ { \"item_default\": \"\", \"item_name\": \"message\", \"item_optional\": false, \"item_type\": \"string\" } ], \"command_description\": \"Print the given message to stdout\", \"command_name\": \"print_message\" }, { \"command_args\": [ ], \"command_description\": \"Shut down BIND 10\", \"command_name\": \"shutdown\" } ], \"config_data\": [ { \"item_default\": 1, \"item_name\": \"item1\", \"item_optional\": false, \"item_type\": \"integer\" }, { \"item_default\": 1.1, \"item_name\": \"item2\", \"item_optional\": false, \"item_type\": \"real\" }, { \"item_default\": true, \"item_name\": \"item3\", \"item_optional\": false, \"item_type\": \"boolean\" }, { \"item_default\": \"test\", \"item_name\": \"item4\", \"item_optional\": false, \"item_type\": \"string\" }, { \"item_default\": [ \"a\", \"b\" ], \"item_name\": \"item5\", \"item_optional\": false, \"item_type\": \"list\", \"list_item_spec\": { \"item_default\": \"\", \"item_name\": \"list_element\", \"item_optional\": false, \"item_type\": \"string\" } }, { \"item_default\": { }, \"item_name\": \"item6\", \"item_optional\": false, \"item_type\": \"map\", \"map_item_spec\": [ { \"item_default\": \"default\", \"item_name\": \"value1\", \"item_optional\": true, \"item_type\": \"string\" }, { \"item_name\": \"value2\", \"item_optional\": true, \"item_type\": \"integer\" } ] } ], \"module_name\": \"Spec2\", \"statistics\": [ { \"item_default\": \"1970-01-01T00:00:00Z\", \"item_description\": \"A dummy date time\", \"item_format\": \"date-time\", \"item_name\": \"dummy_time\", \"item_optional\": false, \"item_title\": \"Dummy Time\", \"item_type\": \"string\" } ] } ] }", msg->str());
  406. EXPECT_EQ("ConfigManager", group);
  407. EXPECT_EQ("*", to);
  408. EXPECT_EQ(1, session.getMsgQueue()->size());
  409. msg = session.getFirstMessage(group, to);
  410. EXPECT_EQ("{ \"command\": [ \"get_config\", { \"module_name\": \"Spec2\" } ] }", msg->str());
  411. EXPECT_EQ("ConfigManager", group);
  412. EXPECT_EQ("*", to);
  413. EXPECT_EQ(0, session.getMsgQueue()->size());
  414. }
  415. TEST_F(CCSessionTest, checkCommand) {
  416. // client will ask for config
  417. session.getMessages()->add(createAnswer(0, el("{}")));
  418. EXPECT_FALSE(session.haveSubscription("Spec29", "*"));
  419. ModuleCCSession mccs(ccspecfile("spec29.spec"), session, my_config_handler,
  420. my_command_handler, true, false);
  421. EXPECT_TRUE(session.haveSubscription("Spec29", "*"));
  422. EXPECT_EQ(2, session.getMsgQueue()->size());
  423. ConstElementPtr msg;
  424. std::string group, to;
  425. // checked above, drop em
  426. msg = session.getFirstMessage(group, to);
  427. msg = session.getFirstMessage(group, to);
  428. int result;
  429. result = mccs.checkCommand();
  430. EXPECT_EQ(0, result);
  431. // not a command, should be ignored
  432. session.addMessage(el("1"), "Spec29", "*");
  433. result = mccs.checkCommand();
  434. EXPECT_EQ(0, result);
  435. session.addMessage(el("{ \"command\": [ \"good_command\" ] }"), "Spec29",
  436. "*");
  437. result = mccs.checkCommand();
  438. EXPECT_EQ(1, session.getMsgQueue()->size());
  439. msg = session.getFirstMessage(group, to);
  440. EXPECT_EQ("{ \"result\": [ 0 ] }", msg->str());
  441. EXPECT_EQ(0, result);
  442. session.addMessage(el("{ \"command\": \"bad_command\" }"), "Spec29", "*");
  443. result = mccs.checkCommand();
  444. EXPECT_EQ(0, session.getMsgQueue()->size());
  445. EXPECT_EQ(0, result);
  446. session.addMessage(el("{ \"command\": [ \"bad_command\" ] }"),
  447. "Spec29", "*");
  448. result = mccs.checkCommand();
  449. EXPECT_EQ(1, session.getMsgQueue()->size());
  450. msg = session.getFirstMessage(group, to);
  451. EXPECT_EQ("{ \"result\": [ 1, \"bad command\" ] }", msg->str());
  452. EXPECT_EQ(0, result);
  453. session.addMessage(el("{ \"command\": [ \"command_with_arg\", {\"number\": 1} ] }"),
  454. "Spec29", "*");
  455. result = mccs.checkCommand();
  456. EXPECT_EQ(1, session.getMsgQueue()->size());
  457. msg = session.getFirstMessage(group, to);
  458. EXPECT_EQ("{ \"result\": [ 0, 2 ] }", msg->str());
  459. EXPECT_EQ(0, result);
  460. session.addMessage(el("{ \"command\": [ \"command_with_arg\" ] }"), "Spec29", "*");
  461. result = mccs.checkCommand();
  462. EXPECT_EQ(1, session.getMsgQueue()->size());
  463. msg = session.getFirstMessage(group, to);
  464. EXPECT_EQ("{ \"result\": [ 1, \"arg missing\" ] }", msg->str());
  465. EXPECT_EQ(0, result);
  466. session.addMessage(el("{ \"command\": [ \"command_with_arg\", \"asdf\" ] }"), "Spec29", "*");
  467. result = mccs.checkCommand();
  468. EXPECT_EQ(1, session.getMsgQueue()->size());
  469. msg = session.getFirstMessage(group, to);
  470. EXPECT_EQ("{ \"result\": [ 3, \"Error in command validation: args for command command_with_arg is not a map\" ] }", msg->str());
  471. EXPECT_EQ(0, result);
  472. mccs.setCommandHandler(NULL);
  473. session.addMessage(el("{ \"command\": [ \"whatever\" ] }"), "Spec29", "*");
  474. result = mccs.checkCommand();
  475. EXPECT_EQ(1, session.getMsgQueue()->size());
  476. msg = session.getFirstMessage(group, to);
  477. EXPECT_EQ("{ \"result\": [ 1, \"Command given but no command handler for module\" ] }", msg->str());
  478. EXPECT_EQ(0, result);
  479. }
  480. // A heuristic workaround for clang++: It doesn't seem to compile the whole
  481. // test, probably due to its length. Dividing the tests into two separate
  482. // test cases seems to work.
  483. TEST_F(CCSessionTest, checkCommand2) {
  484. session.getMessages()->add(createAnswer(0, el("{}")));
  485. EXPECT_FALSE(session.haveSubscription("Spec29", "*"));
  486. ModuleCCSession mccs(ccspecfile("spec29.spec"), session, my_config_handler,
  487. my_command_handler, true, false);
  488. EXPECT_TRUE(session.haveSubscription("Spec29", "*"));
  489. ConstElementPtr msg;
  490. std::string group, to;
  491. // checked above, drop em
  492. msg = session.getFirstMessage(group, to);
  493. msg = session.getFirstMessage(group, to);
  494. EXPECT_EQ(1, mccs.getValue("item1")->intValue());
  495. session.addMessage(el("{ \"command\": [ \"config_update\", { \"item1\": 2 } ] }"), "Spec29", "*");
  496. int result = mccs.checkCommand();
  497. EXPECT_EQ(1, session.getMsgQueue()->size());
  498. msg = session.getFirstMessage(group, to);
  499. EXPECT_EQ("{ \"result\": [ 0 ] }", msg->str());
  500. EXPECT_EQ(0, result);
  501. EXPECT_EQ(2, mccs.getValue("item1")->intValue());
  502. session.addMessage(el("{ \"command\": [ \"config_update\", { \"item1\": \"asdf\" } ] }"), "Spec29", "*");
  503. result = mccs.checkCommand();
  504. EXPECT_EQ(1, session.getMsgQueue()->size());
  505. msg = session.getFirstMessage(group, to);
  506. EXPECT_EQ("{ \"result\": [ 2, \"Error in config validation: Type mismatch\" ] }", msg->str());
  507. EXPECT_EQ(0, result);
  508. EXPECT_EQ(2, mccs.getValue("item1")->intValue());
  509. session.addMessage(el("{ \"command\": [ \"config_update\", { \"item1\": 5 } ] }"), "Spec29", "*");
  510. result = mccs.checkCommand();
  511. EXPECT_EQ(1, session.getMsgQueue()->size());
  512. msg = session.getFirstMessage(group, to);
  513. EXPECT_EQ("{ \"result\": [ 6, \"I do not like the number 5\" ] }", msg->str());
  514. EXPECT_EQ(0, result);
  515. EXPECT_EQ(2, mccs.getValue("item1")->intValue());
  516. }
  517. std::string remote_module_name;
  518. int remote_item1(0);
  519. ConstElementPtr remote_config;
  520. ModuleCCSession *remote_mccs(NULL);
  521. void remoteHandler(const std::string& module_name,
  522. ConstElementPtr config,
  523. const ConfigData&) {
  524. remote_module_name = module_name;
  525. remote_item1 = remote_mccs->getRemoteConfigValue("Spec2", "item1")->
  526. intValue();
  527. remote_config = config;
  528. }
  529. TEST_F(CCSessionTest, remoteConfig) {
  530. std::string module_name;
  531. int item1;
  532. ModuleCCSession mccs(ccspecfile("spec1.spec"), session, NULL, NULL,
  533. false, false);
  534. EXPECT_TRUE(session.haveSubscription("Spec1", "*"));
  535. // first simply connect, with no config values, and see we get
  536. // the default
  537. session.getMessages()->add(createAnswer(0, el("{}")));
  538. EXPECT_FALSE(session.haveSubscription("Spec2", "*"));
  539. module_name = mccs.addRemoteConfig(ccspecfile("spec2.spec"));
  540. EXPECT_EQ("Spec2", module_name);
  541. EXPECT_TRUE(session.haveSubscription("Spec2", "*"));
  542. item1 = mccs.getRemoteConfigValue(module_name, "item1")->intValue();
  543. EXPECT_EQ(1, item1);
  544. // Remove it and see we get an error asking for a config value
  545. mccs.removeRemoteConfig(module_name);
  546. EXPECT_FALSE(session.haveSubscription("Spec2", "*"));
  547. EXPECT_THROW(mccs.getRemoteConfigValue(module_name, "item1"), CCSessionError);
  548. // Now re-add it, with a specific config value, and see we get that
  549. session.getMessages()->add(createAnswer(0, el("{ \"item1\": 2 }")));
  550. module_name = mccs.addRemoteConfig(ccspecfile("spec2.spec"));
  551. item1 = mccs.getRemoteConfigValue(module_name, "item1")->intValue();
  552. EXPECT_EQ(2, item1);
  553. // Try a config_update command
  554. session.addMessage(el("{ \"command\": [ \"config_update\", { \"item1\": 3 } ] }"), module_name, "*");
  555. mccs.checkCommand();
  556. item1 = mccs.getRemoteConfigValue(module_name, "item1")->intValue();
  557. EXPECT_EQ(3, item1);
  558. // remove, re-add, now with a *bad* config request answer
  559. mccs.removeRemoteConfig(module_name);
  560. session.getMessages()->add(el("{}"));
  561. EXPECT_THROW(mccs.addRemoteConfig(ccspecfile("spec2.spec")), CCSessionError);
  562. session.getMessages()->add(createAnswer(1, "my_error"));
  563. EXPECT_THROW(mccs.addRemoteConfig(ccspecfile("spec2.spec")), CCSessionError);
  564. session.getMessages()->add(createAnswer());
  565. EXPECT_THROW(mccs.addRemoteConfig(ccspecfile("spec2.spec")), CCSessionError);
  566. {
  567. SCOPED_TRACE("With module name");
  568. // Try adding it with downloading the spec from config manager
  569. ModuleSpec spec(moduleSpecFromFile(ccspecfile("spec2.spec")));
  570. session.getMessages()->add(createAnswer(0, spec.getFullSpec()));
  571. session.getMessages()->add(createAnswer(0, el("{}")));
  572. EXPECT_NO_THROW(module_name = mccs.addRemoteConfig("Spec2", NULL,
  573. false));
  574. const size_t qsize(session.getMsgQueue()->size());
  575. EXPECT_TRUE(session.getMsgQueue()->get(qsize - 2)->equals(*el(
  576. "[ \"ConfigManager\", \"*\", { \"command\": ["
  577. "\"get_module_spec\", { \"module_name\": \"Spec2\" } ] }, -1 ]")));
  578. EXPECT_TRUE(session.getMsgQueue()->get(qsize - 1)->equals(*el(
  579. "[ \"ConfigManager\", \"*\", { \"command\": [ \"get_config\","
  580. "{ \"module_name\": \"Spec2\" } ] }, -1 ]")));
  581. EXPECT_EQ("Spec2", module_name);
  582. // Since we returned an empty local config above, the default value
  583. // for "item1", which is 1, should be used.
  584. EXPECT_NO_THROW(item1 =
  585. mccs.getRemoteConfigValue(module_name,
  586. "item1")->intValue());
  587. EXPECT_EQ(1, item1);
  588. mccs.removeRemoteConfig(module_name);
  589. }
  590. {
  591. SCOPED_TRACE("With bad module name");
  592. // It is almost the same as above, but we supply wrong module name.
  593. // It should fail.
  594. // Try adding it with downloading the spec from config manager
  595. ModuleSpec spec(moduleSpecFromFile(ccspecfile("spec2.spec")));
  596. session.getMessages()->add(createAnswer(0, spec.getFullSpec()));
  597. EXPECT_THROW(module_name = mccs.addRemoteConfig("Spec1", NULL, false),
  598. CCSessionError);
  599. }
  600. {
  601. // Try adding it with a handler.
  602. // Pass non-default value to see the handler is called after
  603. // downloading the configuration, not too soon.
  604. SCOPED_TRACE("With handler");
  605. session.getMessages()->add(createAnswer(0, el("{ \"item1\": 2 }")));
  606. remote_mccs = &mccs;
  607. module_name = mccs.addRemoteConfig(ccspecfile("spec2.spec"),
  608. remoteHandler);
  609. {
  610. SCOPED_TRACE("Before update");
  611. EXPECT_EQ("Spec2", module_name);
  612. EXPECT_TRUE(session.haveSubscription("Spec2", "*"));
  613. // Now check the parameters the remote handler stored
  614. // This also checks it was called
  615. EXPECT_EQ("Spec2", remote_module_name);
  616. remote_module_name = "";
  617. EXPECT_EQ(2, remote_item1);
  618. remote_item1 = 0;
  619. if (remote_config) {
  620. EXPECT_EQ(2, remote_config->get("item1")->intValue());
  621. } else {
  622. ADD_FAILURE() << "Remote config not set";
  623. }
  624. remote_config.reset();
  625. // Make sure normal way still works
  626. item1 = mccs.getRemoteConfigValue(module_name,
  627. "item1")->intValue();
  628. EXPECT_EQ(2, item1);
  629. }
  630. {
  631. SCOPED_TRACE("After update");
  632. session.addMessage(el("{ \"command\": [ \"config_update\", "
  633. "{ \"item1\": 3 } ] }"), module_name, "*");
  634. mccs.checkCommand();
  635. EXPECT_EQ("Spec2", remote_module_name);
  636. remote_module_name = "";
  637. EXPECT_EQ(3, remote_item1);
  638. remote_item1 = 0;
  639. if (remote_config) {
  640. EXPECT_EQ(3, remote_config->get("item1")->intValue());
  641. } else {
  642. ADD_FAILURE() << "Remote config not set";
  643. }
  644. remote_config.reset();
  645. // Make sure normal way still works
  646. item1 = mccs.getRemoteConfigValue(module_name,
  647. "item1")->intValue();
  648. EXPECT_EQ(3, item1);
  649. }
  650. remote_mccs = NULL;
  651. mccs.removeRemoteConfig(module_name);
  652. {
  653. SCOPED_TRACE("When removed");
  654. // Make sure nothing is called any more
  655. session.addMessage(el("{ \"command\": [ \"config_update\", "
  656. "{ \"item1\": 4 } ] }"), module_name, "*");
  657. EXPECT_EQ("", remote_module_name);
  658. EXPECT_EQ(0, remote_item1);
  659. EXPECT_FALSE(remote_config);
  660. }
  661. }
  662. }
  663. void
  664. callback(std::string* command, std::string* target, ConstElementPtr *params,
  665. const std::string& command_real, const std::string& target_real,
  666. const ConstElementPtr& params_real)
  667. {
  668. *command = command_real;
  669. *target = target_real;
  670. *params = params_real;
  671. }
  672. TEST_F(CCSessionTest, ignoreRemoteConfigCommands) {
  673. // client will ask for config
  674. session.getMessages()->add(createAnswer(0, el("{ }")));
  675. EXPECT_FALSE(session.haveSubscription("Spec29", "*"));
  676. ModuleCCSession mccs(ccspecfile("spec29.spec"), session, my_config_handler,
  677. my_command_handler, false, false);
  678. EXPECT_TRUE(session.haveSubscription("Spec29", "*"));
  679. EXPECT_EQ(2, session.getMsgQueue()->size());
  680. ConstElementPtr msg;
  681. std::string group, to;
  682. // drop the module_spec and config commands
  683. session.getFirstMessage(group, to);
  684. session.getFirstMessage(group, to);
  685. session.getMessages()->add(createAnswer(0, el("{ }")));
  686. mccs.addRemoteConfig(ccspecfile("spec1.spec"));
  687. EXPECT_EQ(1, session.getMsgQueue()->size());
  688. msg = session.getFirstMessage(group, to);
  689. // Check if commands for the module are handled
  690. session.addMessage(el("{ \"command\": [ \"good_command\" ] }"), "Spec29", "*");
  691. int result = mccs.checkCommand();
  692. EXPECT_EQ(1, session.getMsgQueue()->size());
  693. msg = session.getFirstMessage(group, to);
  694. EXPECT_EQ("{ \"result\": [ 0 ] }", msg->str());
  695. EXPECT_EQ(0, result);
  696. // Check if commands for the other module are ignored
  697. session.addMessage(el("{ \"command\": [ \"good_command\" ] }"), "Spec1", "*");
  698. EXPECT_EQ(1, session.getMsgQueue()->size());
  699. result = mccs.checkCommand();
  700. EXPECT_EQ(0, session.getMsgQueue()->size());
  701. EXPECT_EQ(0, result);
  702. // Check that we can get the ignored commands by registering a callback
  703. std::string command, target;
  704. ConstElementPtr params;
  705. mccs.setUnhandledCallback(boost::bind(&callback, &command, &target,
  706. &params, _1, _2, _3));
  707. session.addMessage(el("{ \"command\": [ \"good_command\","
  708. "{\"param\": true} ] }"), "Spec1", "*");
  709. EXPECT_EQ(1, session.getMsgQueue()->size());
  710. result = mccs.checkCommand();
  711. EXPECT_EQ(0, session.getMsgQueue()->size());
  712. EXPECT_EQ(0, result);
  713. EXPECT_EQ("good_command", command);
  714. EXPECT_EQ("Spec1", target);
  715. EXPECT_TRUE(params && el("{\"param\": true}")->equals(*params));
  716. }
  717. TEST_F(CCSessionTest, initializationFail) {
  718. // bad specification
  719. EXPECT_THROW(ModuleCCSession(ccspecfile("spec8.spec"), session,
  720. NULL, NULL), CCSessionInitError);
  721. // file that does not exist
  722. EXPECT_THROW(ModuleCCSession(ccspecfile("does_not_exist_spec"),
  723. session, NULL, NULL),
  724. CCSessionInitError);
  725. session.getMessages()->add(createAnswer(1, el("\"just an error\"")));
  726. EXPECT_FALSE(session.haveSubscription("Spec29", "*"));
  727. EXPECT_THROW(ModuleCCSession(ccspecfile("spec29.spec"), session,
  728. my_config_handler, my_command_handler),
  729. CCSessionInitError);
  730. EXPECT_TRUE(session.haveSubscription("Spec29", "*"));
  731. }
  732. // Test it throws when we try to start it twice (once from the constructor)
  733. TEST_F(CCSessionTest, doubleStartImplicit) {
  734. ModuleCCSession mccs(ccspecfile("spec29.spec"), session, NULL, NULL,
  735. true, false);
  736. EXPECT_THROW(mccs.start(), CCSessionError);
  737. }
  738. // The same, but both starts are explicit
  739. TEST_F(CCSessionTest, doubleStartExplicit) {
  740. ModuleCCSession mccs(ccspecfile("spec29.spec"), session, NULL, NULL,
  741. false, false);
  742. mccs.start();
  743. EXPECT_THROW(mccs.start(), CCSessionError);
  744. }
  745. // Test we can request synchronous receive before we start the session,
  746. // and check there's the mechanism if we do it after
  747. TEST_F(CCSessionTest, delayedStart) {
  748. ModuleCCSession mccs(ccspecfile("spec2.spec"), session, NULL, NULL,
  749. false, false);
  750. session.getMessages()->add(createAnswer());
  751. ConstElementPtr env, answer;
  752. EXPECT_NO_THROW(session.group_recvmsg(env, answer, false, 3));
  753. mccs.start();
  754. session.getMessages()->add(createAnswer());
  755. EXPECT_THROW(session.group_recvmsg(env, answer, false, 3),
  756. FakeSession::DoubleRead);
  757. }
  758. TEST_F(CCSessionTest, loggingStart) {
  759. // provide the logging module spec
  760. ConstElementPtr log_spec = moduleSpecFromFile(LOG_SPEC_FILE).getFullSpec();
  761. session.getMessages()->add(createAnswer(0, log_spec));
  762. // just give an empty config
  763. session.getMessages()->add(createAnswer(0, el("{}")));
  764. ModuleCCSession mccs(ccspecfile("spec2.spec"), session, NULL, NULL,
  765. true, true);
  766. EXPECT_TRUE(session.haveSubscription("Logging", "*"));
  767. }
  768. TEST_F(CCSessionTest, loggingStartBadSpec) {
  769. // provide the logging module spec
  770. session.getMessages()->add(createAnswer(0, el("{}")));
  771. // just give an empty config
  772. session.getMessages()->add(createAnswer(0, el("{}")));
  773. EXPECT_THROW(new ModuleCCSession(ccspecfile("spec2.spec"), session,
  774. NULL, NULL), ModuleSpecError);
  775. EXPECT_FALSE(session.haveSubscription("Logging", "*"));
  776. }
  777. // Similar to the above, but more implicitly by calling addRemoteConfig().
  778. // We should construct ModuleCCSession with start_immediately being false
  779. // if we need to call addRemoteConfig().
  780. // The correct cases are covered in remoteConfig test.
  781. TEST_F(CCSessionTest, doubleStartWithAddRemoteConfig) {
  782. ModuleCCSession mccs(ccspecfile("spec29.spec"), session, NULL, NULL,
  783. true, false);
  784. session.getMessages()->add(createAnswer(0, el("{}")));
  785. EXPECT_THROW(mccs.addRemoteConfig(ccspecfile("spec2.spec")),
  786. FakeSession::DoubleRead);
  787. }
  788. /// \brief Test fixture for asynchronous receiving of messages.
  789. ///
  790. /// This is an extension to the CCSessionTest. It would be possible to add
  791. /// the functionality to the CCSessionTest, but it is going to be used
  792. /// only by few tests and is non-trivial, so it is placed to a separate
  793. /// sub-class.
  794. class AsyncReceiveCCSessionTest : public CCSessionTest {
  795. protected:
  796. AsyncReceiveCCSessionTest() :
  797. mccs_(ccspecfile("spec29.spec"), session, NULL, NULL, false, false),
  798. msg_(el("{\"result\": [0]}")),
  799. next_flag_(0)
  800. {
  801. // This is just to make sure the messages get through the fake
  802. // session.
  803. session.subscribe("test group");
  804. session.subscribe("other group");
  805. session.subscribe("<ignored>");
  806. // Get rid of all unrelated stray messages
  807. while (session.getMsgQueue()->size() > 0) {
  808. session.getMsgQueue()->remove(0);
  809. }
  810. }
  811. /// \brief Convenience function to queue a request to get a command
  812. /// message.
  813. ModuleCCSession::AsyncRecvRequestID
  814. registerCommand(const string& recipient)
  815. {
  816. return (mccs_.groupRecvMsgAsync(
  817. boost::bind(&AsyncReceiveCCSessionTest::callback, this,
  818. next_flag_++, _1, _2, _3), false, -1,
  819. recipient));
  820. }
  821. /// \brief Convenience function to queue a request to get a reply
  822. /// message.
  823. ModuleCCSession::AsyncRecvRequestID
  824. registerReply(int seq)
  825. {
  826. return (mccs_.groupRecvMsgAsync(
  827. boost::bind(&AsyncReceiveCCSessionTest::callback, this,
  828. next_flag_++, _1, _2, _3), true, seq));
  829. }
  830. /// \brief Check the next called callback was with this flag
  831. void called(int flag) {
  832. ASSERT_FALSE(called_.empty());
  833. EXPECT_EQ(flag, *called_.begin());
  834. called_.pop_front();
  835. }
  836. /// \brief Checks that no more callbacks were called.
  837. void nothingCalled() {
  838. EXPECT_TRUE(called_.empty());
  839. }
  840. /// \brief The tested session.
  841. ModuleCCSession mccs_;
  842. /// \brief The value of message on the last called callback.
  843. ConstElementPtr last_msg_;
  844. /// \brief A message that can be used
  845. ConstElementPtr msg_;
  846. // Shared part of the simpleCommand and similar tests.
  847. void commandTest(const string& group) {
  848. // Push the message inside
  849. session.addMessage(msg_, "test group", "<unused>");
  850. EXPECT_TRUE(mccs_.hasQueuedMsgs());
  851. // Register the callback
  852. registerCommand(group);
  853. // But the callback should not be called yet
  854. // (even if the message is there).
  855. nothingCalled();
  856. // But when we call the checkCommand(), it should be called.
  857. mccs_.checkCommand();
  858. called(0);
  859. EXPECT_EQ(msg_, last_msg_);
  860. // But only once
  861. nothingCalled();
  862. // And the message should be eaten
  863. EXPECT_FALSE(mccs_.hasQueuedMsgs());
  864. // The callback should have been eaten as well, inserting another
  865. // message will not invoke it again
  866. session.addMessage(msg_, "test group", "<unused>");
  867. mccs_.checkCommand();
  868. nothingCalled();
  869. }
  870. /// \brief Shared part of the simpleResponse and wildcardResponse tests.
  871. void responseTest(int seq) {
  872. // Push the message inside
  873. session.addMessage(msg_, "<ignored>", "<unused>", 1);
  874. EXPECT_TRUE(mccs_.hasQueuedMsgs());
  875. // Register the callback
  876. registerReply(seq);
  877. // But the callback should not be called yet
  878. // (even if the message is there).
  879. nothingCalled();
  880. // But when we call the checkCommand(), it should be called.
  881. mccs_.checkCommand();
  882. called(0);
  883. EXPECT_EQ(msg_, last_msg_);
  884. // But only once
  885. nothingCalled();
  886. // And the message should be eaten
  887. EXPECT_FALSE(mccs_.hasQueuedMsgs());
  888. // The callback should have been eaten as well, inserting another
  889. // message will not invoke it again
  890. session.addMessage(msg_, "test group", "<unused>");
  891. mccs_.checkCommand();
  892. nothingCalled();
  893. }
  894. /// \brief Shared part of the noMatch* tests
  895. void noMatchTest(int seq, int wanted_seq, bool is_reply) {
  896. // Push the message inside
  897. session.addMessage(msg_, "other group", "<unused>", seq);
  898. EXPECT_TRUE(mccs_.hasQueuedMsgs());
  899. // Register the callback
  900. if (is_reply) {
  901. registerReply(wanted_seq);
  902. } else {
  903. registerCommand("test group");
  904. }
  905. // But the callback should not be called yet
  906. // (even if the message is there).
  907. nothingCalled();
  908. // And even not now, because it does not match.
  909. mccs_.checkCommand();
  910. nothingCalled();
  911. // And the message should be eaten by the checkCommand
  912. EXPECT_FALSE(mccs_.hasQueuedMsgs());
  913. }
  914. private:
  915. /// \brief The next flag to be handed out
  916. int next_flag_;
  917. /// \brief Flags of callbacks already called (as FIFO)
  918. list<int> called_;
  919. /// \brief This is the callback registered to the tested groupRecvMsgAsync
  920. /// function.
  921. void callback(int store_flag, const ConstElementPtr&,
  922. const ConstElementPtr& msg,
  923. const ModuleCCSession::AsyncRecvRequestID&)
  924. {
  925. called_.push_back(store_flag);
  926. last_msg_ = msg;
  927. }
  928. };
  929. // Test we can receive a command, without anything fancy yet
  930. TEST_F(AsyncReceiveCCSessionTest, simpleCommand) {
  931. commandTest("test group");
  932. }
  933. // Test we can receive a "wildcard" command - without specifying the
  934. // group to subscribe to. Very similar to simpleCommand test.
  935. TEST_F(AsyncReceiveCCSessionTest, wildcardCommand) {
  936. commandTest("");
  937. }
  938. // Very similar to simpleCommand, but with a response message
  939. TEST_F(AsyncReceiveCCSessionTest, simpleResponse) {
  940. responseTest(1);
  941. }
  942. // Matching a response message with wildcard
  943. TEST_F(AsyncReceiveCCSessionTest, wildcardResponse) {
  944. responseTest(-1);
  945. }
  946. // Check that a wrong command message is not matched
  947. TEST_F(AsyncReceiveCCSessionTest, noMatchCommand) {
  948. noMatchTest(-1, -1, false);
  949. }
  950. // Check that a wrong response message is not matched
  951. TEST_F(AsyncReceiveCCSessionTest, noMatchResponse) {
  952. noMatchTest(2, 3, true);
  953. }
  954. // Check that a command will not match on a reply check and vice versa
  955. TEST_F(AsyncReceiveCCSessionTest, noMatchResponseAgainstCommand) {
  956. // Send a command and check it is not matched as a response
  957. noMatchTest(-1, -1, true);
  958. }
  959. TEST_F(AsyncReceiveCCSessionTest, noMatchCommandAgainstResponse) {
  960. noMatchTest(2, -1, false);
  961. }
  962. // We check for command several times before the message actually arrives.
  963. TEST_F(AsyncReceiveCCSessionTest, delayedCallback) {
  964. // First, register the callback
  965. registerReply(1);
  966. // And see it is not called, because the message is not there yet
  967. EXPECT_FALSE(mccs_.hasQueuedMsgs());
  968. for (size_t i(0); i < 100; ++ i) {
  969. mccs_.checkCommand();
  970. EXPECT_FALSE(mccs_.hasQueuedMsgs());
  971. nothingCalled();
  972. }
  973. // Now the message finally arrives
  974. session.addMessage(msg_, "<ignored>", "<unused>", 1);
  975. EXPECT_TRUE(mccs_.hasQueuedMsgs());
  976. // And now, the callback is happily triggered.
  977. mccs_.checkCommand();
  978. called(0);
  979. EXPECT_EQ(msg_, last_msg_);
  980. // But only once
  981. nothingCalled();
  982. }
  983. // See that if we put multiple messages inside, and request some callbacks,
  984. // the callbacks are called in the order of messages, not in the order they
  985. // were registered.
  986. TEST_F(AsyncReceiveCCSessionTest, outOfOrder) {
  987. // First, put some messages there
  988. session.addMessage(msg_, "<ignored>", "<unused>", 1);
  989. session.addMessage(msg_, "test group", "<unused>");
  990. session.addMessage(msg_, "other group", "<unused>");
  991. session.addMessage(msg_, "<ignored>", "<unused>", 2);
  992. session.addMessage(msg_, "<ignored>", "<unused>", 3);
  993. session.addMessage(msg_, "<ignored>", "<unused>", 4);
  994. // Now register some callbacks
  995. registerReply(13); // Will not be called
  996. registerCommand("other group"); // Matches 3rd message
  997. registerReply(2); // Matches 4th message
  998. registerCommand(""); // Matches the 2nd message
  999. registerCommand("test group"); // Will not be called
  1000. registerReply(-1); // Matches the 1st message
  1001. registerReply(-1); // Matches the 5th message
  1002. // Process all messages there
  1003. while (mccs_.hasQueuedMsgs()) {
  1004. mccs_.checkCommand();
  1005. }
  1006. // These are the numbers of callbacks in the order of messages
  1007. called(5);
  1008. called(3);
  1009. called(1);
  1010. called(2);
  1011. called(6);
  1012. // The last message doesn't trigger anything, so nothing more is called
  1013. nothingCalled();
  1014. }
  1015. // We first add, then remove the callback again and check that nothing is
  1016. // matched.
  1017. TEST_F(AsyncReceiveCCSessionTest, cancel) {
  1018. // Add the callback
  1019. ModuleCCSession::AsyncRecvRequestID request(registerReply(1));
  1020. // Add corresponding message
  1021. session.addMessage(msg_, "<ignored>", "<unused>", 1);
  1022. EXPECT_TRUE(mccs_.hasQueuedMsgs());
  1023. // And now, remove the callback again
  1024. mccs_.cancelAsyncRecv(request);
  1025. // And see that Nothing Happens(TM)
  1026. mccs_.checkCommand();
  1027. EXPECT_FALSE(mccs_.hasQueuedMsgs());
  1028. nothingCalled();
  1029. }
  1030. // We add multiple requests and cancel only one of them to see the rest
  1031. // is unaffected.
  1032. TEST_F(AsyncReceiveCCSessionTest, cancelSome) {
  1033. // Register few callbacks
  1034. registerReply(1);
  1035. ModuleCCSession::AsyncRecvRequestID request(registerCommand(""));
  1036. registerCommand("test group");
  1037. // Put some messages there
  1038. session.addMessage(msg_, "test group", "<unused>");
  1039. session.addMessage(msg_, "<ignored>", "<unused>", 1);
  1040. // Cancel the second callback. Therefore the first message will be matched
  1041. // by the third callback, not by the second.
  1042. mccs_.cancelAsyncRecv(request);
  1043. // Now, process the messages
  1044. mccs_.checkCommand();
  1045. mccs_.checkCommand();
  1046. // And see how they matched
  1047. called(2);
  1048. called(0);
  1049. nothingCalled();
  1050. }
  1051. void doRelatedLoggersTest(const char* input, const char* expected) {
  1052. ConstElementPtr all_conf = isc::data::Element::fromJSON(input);
  1053. ConstElementPtr expected_conf = isc::data::Element::fromJSON(expected);
  1054. EXPECT_EQ(*expected_conf, *isc::config::getRelatedLoggers(all_conf));
  1055. }
  1056. TEST(LogConfigTest, relatedLoggersTest) {
  1057. // make sure logger configs for 'other' programs are ignored,
  1058. // and that * is substituted correctly
  1059. // We'll use a root logger name of "b10-test".
  1060. isc::log::setRootLoggerName("b10-test");
  1061. doRelatedLoggersTest("[{ \"name\": \"other_module\" }]",
  1062. "[]");
  1063. doRelatedLoggersTest("[{ \"name\": \"other_module.somelib\" }]",
  1064. "[]");
  1065. doRelatedLoggersTest("[{ \"name\": \"test_other\" }]",
  1066. "[]");
  1067. doRelatedLoggersTest("[{ \"name\": \"test_other.somelib\" }]",
  1068. "[]");
  1069. doRelatedLoggersTest("[ { \"name\": \"other_module\" },"
  1070. " { \"name\": \"test\" }]",
  1071. "[ { \"name\": \"b10-test\" } ]");
  1072. doRelatedLoggersTest("[ { \"name\": \"test\" }]",
  1073. "[ { \"name\": \"b10-test\" } ]");
  1074. doRelatedLoggersTest("[ { \"name\": \"test.somelib\" }]",
  1075. "[ { \"name\": \"b10-test.somelib\" } ]");
  1076. doRelatedLoggersTest("[ { \"name\": \"other_module.somelib\" },"
  1077. " { \"name\": \"test.somelib\" }]",
  1078. "[ { \"name\": \"b10-test.somelib\" } ]");
  1079. doRelatedLoggersTest("[ { \"name\": \"other_module.somelib\" },"
  1080. " { \"name\": \"test\" },"
  1081. " { \"name\": \"test.somelib\" }]",
  1082. "[ { \"name\": \"b10-test\" },"
  1083. " { \"name\": \"b10-test.somelib\" } ]");
  1084. doRelatedLoggersTest("[ { \"name\": \"*\" }]",
  1085. "[ { \"name\": \"b10-test\" } ]");
  1086. doRelatedLoggersTest("[ { \"name\": \"*.somelib\" }]",
  1087. "[ { \"name\": \"b10-test.somelib\" } ]");
  1088. doRelatedLoggersTest("[ { \"name\": \"*\", \"severity\": \"DEBUG\" },"
  1089. " { \"name\": \"test\", \"severity\": \"WARN\"}]",
  1090. "[ { \"name\": \"b10-test\", \"severity\": \"WARN\"} ]");
  1091. doRelatedLoggersTest("[ { \"name\": \"*\", \"severity\": \"DEBUG\" },"
  1092. " { \"name\": \"some_module\", \"severity\": \"WARN\"}]",
  1093. "[ { \"name\": \"b10-test\", \"severity\": \"DEBUG\"} ]");
  1094. doRelatedLoggersTest("[ { \"name\": \"b10-test\" }]",
  1095. "[]");
  1096. // make sure 'bad' things like '*foo.x' or '*lib' are ignored
  1097. // (cfgmgr should have already caught it in the logconfig plugin
  1098. // check, and is responsible for reporting the error)
  1099. doRelatedLoggersTest("[ { \"name\": \"*foo\" }]",
  1100. "[ ]");
  1101. doRelatedLoggersTest("[ { \"name\": \"*foo.bar\" }]",
  1102. "[ ]");
  1103. doRelatedLoggersTest("[ { \"name\": \"*foo\" },"
  1104. " { \"name\": \"*foo.lib\" },"
  1105. " { \"name\": \"test\" } ]",
  1106. "[ { \"name\": \"b10-test\" } ]");
  1107. }
  1108. }