ccsession_unittests.cc 47 KB

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