module_spec_unittests.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. // Copyright (C) 2009, 2011, 2015 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 <gtest/gtest.h>
  15. #include <config/module_spec.h>
  16. #include <fstream>
  17. #include <boost/foreach.hpp>
  18. #include <config/tests/data_def_unittests_config.h>
  19. using namespace isc::data;
  20. using namespace isc::config;
  21. std::string specfile(const std::string& name) {
  22. return (std::string(TEST_DATA_PATH) + "/" + name);
  23. }
  24. void
  25. moduleSpecError(const std::string& file,
  26. const std::string& error1,
  27. const std::string& error2 = "",
  28. const std::string& error3 = "")
  29. {
  30. EXPECT_THROW(moduleSpecFromFile(specfile(file)), ModuleSpecError);
  31. try {
  32. ModuleSpec dd = moduleSpecFromFile(specfile(file));
  33. } catch (const ModuleSpecError& dde) {
  34. std::string ddew = dde.what();
  35. EXPECT_EQ(error1 + error2 + error3, ddew);
  36. }
  37. }
  38. TEST(ModuleSpec, ReadingSpecfiles) {
  39. // Tests whether we can open specfiles and if we get the
  40. // right parse errors
  41. ModuleSpec dd = moduleSpecFromFile(specfile("spec1.spec"));
  42. EXPECT_EQ(dd.getFullSpec()->get("module_name")
  43. ->stringValue(), "Spec1");
  44. dd = moduleSpecFromFile(specfile("spec2.spec"));
  45. EXPECT_EQ(dd.getFullSpec()->get("config_data")->size(), 6);
  46. moduleSpecError("doesnotexist",
  47. "Error opening ",
  48. specfile("doesnotexist"),
  49. ": No such file or directory");
  50. dd = moduleSpecFromFile(specfile("spec2.spec"));
  51. EXPECT_EQ("[ { \"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 Kea\", \"command_name\": \"shutdown\" } ]", dd.getCommandsSpec()->str());
  52. EXPECT_EQ("[ { \"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\" } ]", dd.getStatisticsSpec()->str());
  53. EXPECT_EQ("Spec2", dd.getModuleName());
  54. EXPECT_EQ("", dd.getModuleDescription());
  55. dd = moduleSpecFromFile(specfile("spec25.spec"));
  56. EXPECT_EQ("Spec25", dd.getModuleName());
  57. EXPECT_EQ("Just an empty module", dd.getModuleDescription());
  58. EXPECT_THROW(moduleSpecFromFile(specfile("spec26.spec")), ModuleSpecError);
  59. EXPECT_THROW(moduleSpecFromFile(specfile("spec34.spec")), ModuleSpecError);
  60. EXPECT_THROW(moduleSpecFromFile(specfile("spec35.spec")), ModuleSpecError);
  61. EXPECT_THROW(moduleSpecFromFile(specfile("spec36.spec")), ModuleSpecError);
  62. EXPECT_THROW(moduleSpecFromFile(specfile("spec37.spec")), ModuleSpecError);
  63. EXPECT_THROW(moduleSpecFromFile(specfile("spec38.spec")), ModuleSpecError);
  64. std::ifstream file;
  65. file.open(specfile("spec1.spec").c_str());
  66. dd = moduleSpecFromFile(file);
  67. EXPECT_EQ(dd.getFullSpec()->get("module_name")
  68. ->stringValue(), "Spec1");
  69. EXPECT_TRUE(isNull(dd.getCommandsSpec()));
  70. EXPECT_TRUE(isNull(dd.getStatisticsSpec()));
  71. std::ifstream file2;
  72. file2.open(specfile("spec8.spec").c_str());
  73. EXPECT_THROW(moduleSpecFromFile(file2), ModuleSpecError);
  74. }
  75. TEST(ModuleSpec, SpecfileItems) {
  76. moduleSpecError("spec3.spec",
  77. "item_name missing in { \"item_default\": 1, \"item_optional\": false, \"item_type\": \"integer\" }");
  78. moduleSpecError("spec4.spec",
  79. "item_type missing in { \"item_default\": 1, \"item_name\": \"item1\", \"item_optional\": false }");
  80. moduleSpecError("spec5.spec",
  81. "item_optional missing in { \"item_default\": 1, \"item_name\": \"item1\", \"item_type\": \"integer\" }");
  82. moduleSpecError("spec6.spec",
  83. "item_default missing in { \"item_name\": \"item1\", \"item_optional\": false, \"item_type\": \"integer\" }");
  84. moduleSpecError("spec9.spec",
  85. "item_default not of type integer");
  86. moduleSpecError("spec10.spec",
  87. "item_default not of type real");
  88. moduleSpecError("spec11.spec",
  89. "item_default not of type boolean");
  90. moduleSpecError("spec12.spec",
  91. "item_default not of type string");
  92. moduleSpecError("spec13.spec",
  93. "item_default not of type list");
  94. moduleSpecError("spec14.spec",
  95. "item_default not of type map");
  96. moduleSpecError("spec15.spec",
  97. "badname is not a valid type name");
  98. EXPECT_NO_THROW(moduleSpecFromFile(specfile("spec40.spec")));
  99. }
  100. TEST(ModuleSpec, SpecfileConfigData) {
  101. moduleSpecError("spec7.spec",
  102. "module_name missing in { }");
  103. moduleSpecError("spec8.spec",
  104. "No module_spec in specification");
  105. moduleSpecError("spec16.spec",
  106. "config_data is not a list of elements");
  107. moduleSpecError("spec21.spec",
  108. "commands is not a list of elements");
  109. }
  110. TEST(ModuleSpec, SpecfileStatistics) {
  111. moduleSpecError("spec36.spec", "item_default not valid type of item_format");
  112. moduleSpecError("spec37.spec", "statistics is not a list of elements");
  113. moduleSpecError("spec38.spec", "item_default not valid type of item_format");
  114. }
  115. TEST(ModuleSpec, SpecfileCommands) {
  116. moduleSpecError("spec17.spec",
  117. "command_name missing in { \"command_args\": [ { \"item_default\": \"\", \"item_name\": \"message\", \"item_optional\": false, \"item_type\": \"string\" } ], \"command_description\": \"Print the given message to stdout\" }");
  118. moduleSpecError("spec18.spec",
  119. "command_args missing in { \"command_description\": \"Print the given message to stdout\", \"command_name\": \"print_message\" }");
  120. moduleSpecError("spec19.spec",
  121. "command_args not of type list");
  122. moduleSpecError("spec20.spec",
  123. "somethingbad is not a valid type name");
  124. }
  125. bool
  126. dataTest(const ModuleSpec& dd, const std::string& data_file_name) {
  127. std::ifstream data_file;
  128. data_file.open(specfile(data_file_name).c_str());
  129. ConstElementPtr data = Element::fromJSON(data_file, data_file_name);
  130. data_file.close();
  131. return (dd.validateConfig(data));
  132. }
  133. bool
  134. statisticsTest(const ModuleSpec& dd, const std::string& data_file_name) {
  135. std::ifstream data_file;
  136. data_file.open(specfile(data_file_name).c_str());
  137. ConstElementPtr data = Element::fromJSON(data_file, data_file_name);
  138. data_file.close();
  139. return (dd.validateStatistics(data));
  140. }
  141. bool
  142. dataTestWithErrors(const ModuleSpec& dd, const std::string& data_file_name,
  143. ElementPtr errors)
  144. {
  145. std::ifstream data_file;
  146. data_file.open(specfile(data_file_name).c_str());
  147. ConstElementPtr data = Element::fromJSON(data_file, data_file_name);
  148. data_file.close();
  149. return (dd.validateConfig(data, true, errors));
  150. }
  151. bool
  152. statisticsTestWithErrors(const ModuleSpec& dd, const std::string& data_file_name,
  153. ElementPtr errors)
  154. {
  155. std::ifstream data_file;
  156. data_file.open(specfile(data_file_name).c_str());
  157. ConstElementPtr data = Element::fromJSON(data_file, data_file_name);
  158. data_file.close();
  159. return (dd.validateStatistics(data, true, errors));
  160. }
  161. TEST(ModuleSpec, DataValidation) {
  162. ModuleSpec dd = moduleSpecFromFile(specfile("spec22.spec"));
  163. EXPECT_TRUE(dataTest(dd, "data22_1.data"));
  164. EXPECT_FALSE(dataTest(dd, "data22_2.data"));
  165. EXPECT_FALSE(dataTest(dd, "data22_3.data"));
  166. EXPECT_FALSE(dataTest(dd, "data22_4.data"));
  167. EXPECT_FALSE(dataTest(dd, "data22_5.data"));
  168. EXPECT_TRUE(dataTest(dd, "data22_6.data"));
  169. EXPECT_TRUE(dataTest(dd, "data22_7.data"));
  170. EXPECT_FALSE(dataTest(dd, "data22_8.data"));
  171. EXPECT_FALSE(dataTest(dd, "data22_9.data"));
  172. // Test if "version" is allowed in config data
  173. // (same data as 22_7, but added "version")
  174. EXPECT_TRUE(dataTest(dd, "data22_10.data"));
  175. ElementPtr errors = Element::createList();
  176. EXPECT_FALSE(dataTestWithErrors(dd, "data22_8.data", errors));
  177. EXPECT_EQ("[ \"Type mismatch\" ]", errors->str());
  178. errors = Element::createList();
  179. EXPECT_FALSE(dataTestWithErrors(dd, "data22_9.data", errors));
  180. EXPECT_EQ("[ \"Unknown item value_does_not_exist\" ]", errors->str());
  181. }
  182. TEST(ModuleSpec, StatisticsValidation) {
  183. ModuleSpec dd = moduleSpecFromFile(specfile("spec33.spec"));
  184. EXPECT_TRUE(statisticsTest(dd, "data33_1.data"));
  185. EXPECT_FALSE(statisticsTest(dd, "data33_2.data"));
  186. ElementPtr errors = Element::createList();
  187. EXPECT_FALSE(statisticsTestWithErrors(dd, "data33_2.data", errors));
  188. EXPECT_EQ("[ \"Format mismatch\", \"Format mismatch\", \"Format mismatch\" ]", errors->str());
  189. dd = moduleSpecFromFile(specfile("spec41.spec"));
  190. EXPECT_TRUE(statisticsTest(dd, "data41_1.data"));
  191. EXPECT_FALSE(statisticsTest(dd, "data41_2.data"));
  192. errors = Element::createList();
  193. EXPECT_FALSE(statisticsTestWithErrors(dd, "data41_2.data", errors));
  194. EXPECT_EQ("[ \"Type mismatch\" ]", errors->str());
  195. }
  196. TEST(ModuleSpec, CommandValidation) {
  197. ModuleSpec dd = moduleSpecFromFile(specfile("spec2.spec"));
  198. ConstElementPtr arg = Element::fromJSON("{}");
  199. ElementPtr errors = Element::createList();
  200. EXPECT_TRUE(dd.validateCommand("shutdown", arg, errors));
  201. EXPECT_EQ(errors->size(), 0);
  202. errors = Element::createList();
  203. EXPECT_FALSE(dd.validateCommand("unknowncommand", arg, errors));
  204. EXPECT_EQ(errors->size(), 1);
  205. EXPECT_EQ(errors->get(0)->stringValue(), "Unknown command unknowncommand");
  206. errors = Element::createList();
  207. EXPECT_FALSE(dd.validateCommand("print_message", arg, errors));
  208. EXPECT_EQ(errors->size(), 1);
  209. EXPECT_EQ(errors->get(0)->stringValue(), "Non-optional value missing");
  210. errors = Element::createList();
  211. arg = Element::fromJSON("{ \"message\": \"Hello\" }");
  212. EXPECT_TRUE(dd.validateCommand("print_message", arg, errors));
  213. EXPECT_EQ(errors->size(), 0);
  214. errors = Element::createList();
  215. arg = Element::fromJSON("{ \"message\": \"Hello\", \"unknown_second_arg\": 1 }");
  216. EXPECT_FALSE(dd.validateCommand("print_message", arg, errors));
  217. EXPECT_EQ(errors->size(), 1);
  218. EXPECT_EQ(errors->get(0)->stringValue(), "Unknown item unknown_second_arg");
  219. errors = Element::createList();
  220. arg = Element::fromJSON("{ \"message\": 1 }");
  221. EXPECT_FALSE(dd.validateCommand("print_message", arg, errors));
  222. EXPECT_EQ(errors->size(), 1);
  223. EXPECT_EQ(errors->get(0)->stringValue(), "Type mismatch");
  224. }
  225. TEST(ModuleSpec, NamedSetValidation) {
  226. ModuleSpec dd = moduleSpecFromFile(specfile("spec32.spec"));
  227. ElementPtr errors = Element::createList();
  228. EXPECT_TRUE(dataTestWithErrors(dd, "data32_1.data", errors));
  229. EXPECT_FALSE(dataTest(dd, "data32_2.data"));
  230. EXPECT_FALSE(dataTest(dd, "data32_3.data"));
  231. }
  232. TEST(ModuleSpec, CheckFormat) {
  233. const std::string json_begin = "{ \"module_spec\": { \"module_name\": \"Foo\", \"statistics\": [ { \"item_name\": \"dummy_time\", \"item_type\": \"string\", \"item_optional\": true, \"item_title\": \"Dummy Time\", \"item_description\": \"A dummy date time\"";
  234. const std::string json_end = " } ] } }";
  235. std::string item_default;
  236. std::string item_format;
  237. std::vector<std::string> specs;
  238. ConstElementPtr el;
  239. specs.clear();
  240. item_default = "\"item_default\": \"2011-05-27T19:42:57Z\",";
  241. item_format = "\"item_format\": \"date-time\"";
  242. specs.push_back("," + item_default + item_format);
  243. item_default = "\"item_default\": \"2011-05-27\",";
  244. item_format = "\"item_format\": \"date\"";
  245. specs.push_back("," + item_default + item_format);
  246. item_default = "\"item_default\": \"19:42:57\",";
  247. item_format = "\"item_format\": \"time\"";
  248. specs.push_back("," + item_default + item_format);
  249. item_format = "\"item_format\": \"date-time\"";
  250. specs.push_back("," + item_format);
  251. item_default = "";
  252. item_format = "\"item_format\": \"date\"";
  253. specs.push_back("," + item_format);
  254. // cppcheck-suppress redundantAssignment
  255. item_default = "";
  256. item_format = "\"item_format\": \"time\"";
  257. specs.push_back("," + item_format);
  258. // cppcheck-suppress redundantAssignment
  259. item_default = "\"item_default\": \"a\"";
  260. specs.push_back("," + item_default);
  261. item_default = "\"item_default\": \"b\"";
  262. specs.push_back("," + item_default);
  263. item_default = "\"item_default\": \"c\"";
  264. specs.push_back("," + item_default);
  265. item_format = "\"item_format\": \"dummy\"";
  266. specs.push_back("," + item_format);
  267. specs.push_back("");
  268. BOOST_FOREACH(std::string s, specs) {
  269. el = Element::fromJSON(json_begin + s + json_end)->get("module_spec");
  270. EXPECT_NO_THROW(ModuleSpec(el, true));
  271. }
  272. specs.clear();
  273. item_default = "\"item_default\": \"2011-05-27T19:42:57Z\",";
  274. item_format = "\"item_format\": \"dummy\"";
  275. specs.push_back("," + item_default + item_format);
  276. item_default = "\"item_default\": \"2011-05-27\",";
  277. item_format = "\"item_format\": \"dummy\"";
  278. specs.push_back("," + item_default + item_format);
  279. item_default = "\"item_default\": \"19:42:57Z\",";
  280. item_format = "\"item_format\": \"dummy\"";
  281. specs.push_back("," + item_default + item_format);
  282. item_default = "\"item_default\": \"2011-13-99T99:99:99Z\",";
  283. item_format = "\"item_format\": \"date-time\"";
  284. specs.push_back("," + item_default + item_format);
  285. item_default = "\"item_default\": \"2011-13-99\",";
  286. item_format = "\"item_format\": \"date\"";
  287. specs.push_back("," + item_default + item_format);
  288. item_default = "\"item_default\": \"99:99:99Z\",";
  289. item_format = "\"item_format\": \"time\"";
  290. specs.push_back("," + item_default + item_format);
  291. item_default = "\"item_default\": \"1\",";
  292. item_format = "\"item_format\": \"date-time\"";
  293. specs.push_back("," + item_default + item_format);
  294. item_default = "\"item_default\": \"1\",";
  295. item_format = "\"item_format\": \"date\"";
  296. specs.push_back("," + item_default + item_format);
  297. item_default = "\"item_default\": \"1\",";
  298. item_format = "\"item_format\": \"time\"";
  299. specs.push_back("," + item_default + item_format);
  300. item_default = "\"item_default\": \"\",";
  301. item_format = "\"item_format\": \"date-time\"";
  302. specs.push_back("," + item_default + item_format);
  303. item_default = "\"item_default\": \"\",";
  304. item_format = "\"item_format\": \"date\"";
  305. specs.push_back("," + item_default + item_format);
  306. item_default = "\"item_default\": \"\",";
  307. item_format = "\"item_format\": \"time\"";
  308. specs.push_back("," + item_default + item_format);
  309. // wrong date-time-type format not ending with "Z"
  310. item_default = "\"item_default\": \"2011-05-27T19:42:57\",";
  311. item_format = "\"item_format\": \"date-time\"";
  312. specs.push_back("," + item_default + item_format);
  313. // wrong date-type format ending with "T"
  314. item_default = "\"item_default\": \"2011-05-27T\",";
  315. item_format = "\"item_format\": \"date\"";
  316. specs.push_back("," + item_default + item_format);
  317. // wrong time-type format ending with "Z"
  318. item_default = "\"item_default\": \"19:42:57Z\",";
  319. item_format = "\"item_format\": \"time\"";
  320. specs.push_back("," + item_default + item_format);
  321. BOOST_FOREACH(std::string s, specs) {
  322. el = Element::fromJSON(json_begin + s + json_end)->get("module_spec");
  323. EXPECT_THROW(ModuleSpec(el, true), ModuleSpecError);
  324. }
  325. }