ccsession_unittests.cc 44 KB

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