loadtests.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. #include <string>
  2. #include <iostream>
  3. #include <sstream>
  4. #include <cassert>
  5. #include <cstdlib>
  6. #include <cstring>
  7. #include "service.h"
  8. #include "proc-service.h"
  9. //#include "load-service.h"
  10. std::string test_service_dir;
  11. environment tenv;
  12. environment::env_map tenvmap;
  13. void init_test_service_dir()
  14. {
  15. test_service_dir = "./test-services";
  16. tenvmap = tenv.build(main_env);
  17. }
  18. void test_basic()
  19. {
  20. dirload_service_set sset(test_service_dir.c_str());
  21. auto t1 = sset.load_service("t1");
  22. assert(t1->get_name() == "t1");
  23. }
  24. void test_env_subst()
  25. {
  26. dirload_service_set sset(test_service_dir.c_str());
  27. bp_sys::setenv("ONEVAR", "a", true);
  28. bp_sys::setenv("TWOVAR", "hellohello", true);
  29. bp_sys::setenv("THREEVAR", "", true);
  30. // leave FOURVAR undefined
  31. auto t2 = static_cast<base_process_service *>(sset.load_service("t2"));
  32. auto exec_parts = t2->get_exec_arg_parts();
  33. assert(strcmp("echo", exec_parts[0]) == 0);
  34. assert(strcmp("a", exec_parts[1]) == 0); // $ONEVAR
  35. assert(strcmp("a", exec_parts[2]) == 0); // ${ONEVAR}
  36. assert(strcmp("b", exec_parts[3]) == 0); // ${ONEVAR+b}
  37. assert(strcmp("b", exec_parts[4]) == 0); // ${ONEVAR:+b}
  38. assert(strcmp("hellohello", exec_parts[5]) == 0); // $TWOVAR
  39. assert(strcmp("hellohello", exec_parts[6]) == 0); // ${TWOVAR}
  40. assert(strcmp("hellohello", exec_parts[7]) == 0); // ${TWOVAR-world}
  41. assert(strcmp("hellohello", exec_parts[8]) == 0); // ${TWOVAR:-world}
  42. assert(strcmp("", exec_parts[9]) == 0); // $THREEVAR
  43. assert(strcmp("", exec_parts[10]) == 0); // ${THREEVAR}
  44. assert(strcmp("empty", exec_parts[11]) == 0); // ${THREEVAR+empty}
  45. assert(strcmp("", exec_parts[12]) == 0); // ${THREEVAR:+empty}
  46. assert(strcmp("", exec_parts[13]) == 0); // ${THREEVAR-empty}
  47. assert(strcmp("empty", exec_parts[14]) == 0); // ${THREEVAR:-empty}
  48. assert(strcmp("", exec_parts[15]) == 0); // $FOURVAR
  49. assert(strcmp("", exec_parts[16]) == 0); // ${FOURVAR}
  50. assert(strcmp("", exec_parts[17]) == 0); // ${FOURVAR+empty2}
  51. assert(strcmp("", exec_parts[18]) == 0); // ${FOURVAR:+empty2}
  52. assert(strcmp("empty2", exec_parts[19]) == 0); // ${FOURVAR-empty2}
  53. assert(strcmp("empty2", exec_parts[20]) == 0); // ${FOURVAR:-empty2}
  54. }
  55. void test_env_subst2()
  56. {
  57. auto resolve_env_var = [](const std::string &name, environment::env_map const &) {
  58. if (name == "ONE_VAR") return "a";
  59. if (name == "TWOVAR") return "hellohello";
  60. return "";
  61. };
  62. std::string line = "test x$ONE_VAR-${ONE_VAR}~ y$${TWOVAR}$TWOVAR$$ONE_VAR";
  63. std::list<std::pair<unsigned,unsigned>> offsets;
  64. std::string::iterator li = line.begin();
  65. std::string::iterator le = line.end();
  66. dinit_load::read_setting_value(1 /* line_num */, li, le, &offsets);
  67. dinit_load::value_var_subst("command", line, offsets, resolve_env_var, tenvmap);
  68. assert(line == "test xa-a~ y${TWOVAR}hellohello$ONE_VAR");
  69. assert(offsets.size() == 3);
  70. assert((*std::next(offsets.begin(), 0) == std::pair<unsigned,unsigned>{0, 4}));
  71. assert((*std::next(offsets.begin(), 1) == std::pair<unsigned,unsigned>{5, 10}));
  72. assert((*std::next(offsets.begin(), 2) == std::pair<unsigned,unsigned>{11, 39}));
  73. }
  74. void test_env_subst3()
  75. {
  76. auto resolve_env_var = [](const std::string &name, environment::env_map const &) {
  77. if (name == "EMPTY") return "";
  78. if (name == "WS") return " ";
  79. if (name == "PADDED") return " p ";
  80. return "";
  81. };
  82. std::string line = "test $/EMPTY foo";
  83. std::list<std::pair<unsigned,unsigned>> offsets;
  84. std::string::iterator li = line.begin();
  85. std::string::iterator le = line.end();
  86. dinit_load::read_setting_value(1 /* line_num */, li, le, &offsets);
  87. dinit_load::value_var_subst("command", line, offsets, resolve_env_var, tenvmap);
  88. auto check_arg = [&](unsigned idx, const char *val)
  89. {
  90. auto &offs = *std::next(offsets.begin(), idx);
  91. assert(line.substr(offs.first, offs.second - offs.first) == val);
  92. };
  93. assert(line == "test foo");
  94. check_arg(1, "foo");
  95. line = "test $EMPTY foo";
  96. li = line.begin(); le = line.end(); offsets.clear();
  97. dinit_load::read_setting_value(1 /* line_num */, li, le, &offsets);
  98. dinit_load::value_var_subst("command", line, offsets, resolve_env_var, tenvmap);
  99. assert(line == "test foo");
  100. check_arg(1, "");
  101. check_arg(2, "foo");
  102. // adjacent collapsing
  103. line = "test $/EMPTY$/EMPTY$/EMPTY foo";
  104. li = line.begin(); le = line.end(); offsets.clear();
  105. dinit_load::read_setting_value(1 /* line_num */, li, le, &offsets);
  106. dinit_load::value_var_subst("command", line, offsets, resolve_env_var, tenvmap);
  107. assert(line == "test foo");
  108. check_arg(1, "foo");
  109. // middle empty is non-collapsing:
  110. line = "test $/EMPTY$EMPTY$/EMPTY foo";
  111. li = line.begin(); le = line.end(); offsets.clear();
  112. dinit_load::read_setting_value(1 /* line_num */, li, le, &offsets);
  113. dinit_load::value_var_subst("command", line, offsets, resolve_env_var, tenvmap);
  114. assert(line == "test foo");
  115. check_arg(1, "");
  116. check_arg(2, "foo");
  117. // empty doesn't wordsplit:
  118. line = "test abc$/{EMPTY}def";
  119. li = line.begin(); le = line.end(); offsets.clear();
  120. dinit_load::read_setting_value(1 /* line_num */, li, le, &offsets);
  121. dinit_load::value_var_subst("command", line, offsets, resolve_env_var, tenvmap);
  122. assert(line == "test abcdef");
  123. check_arg(1, "abcdef");
  124. // whitespace does wordsplit:
  125. line = "test abc$/{WS}def";
  126. li = line.begin(); le = line.end(); offsets.clear();
  127. dinit_load::read_setting_value(1 /* line_num */, li, le, &offsets);
  128. dinit_load::value_var_subst("command", line, offsets, resolve_env_var, tenvmap);
  129. assert(line == "test abc def");
  130. check_arg(1, "abc");
  131. check_arg(2, "def");
  132. // internal words handled correctly:
  133. line = "test abc$/{PADDED}def";
  134. li = line.begin(); le = line.end(); offsets.clear();
  135. dinit_load::read_setting_value(1 /* line_num */, li, le, &offsets);
  136. dinit_load::value_var_subst("command", line, offsets, resolve_env_var, tenvmap);
  137. assert(line == "test abc p def");
  138. check_arg(1, "abc");
  139. check_arg(2, "p");
  140. check_arg(3, "def");
  141. }
  142. void test_nonexistent()
  143. {
  144. bool got_service_not_found = false;
  145. dirload_service_set sset(test_service_dir.c_str());
  146. try {
  147. sset.load_service("does-not-exist");
  148. }
  149. catch (service_not_found &) {
  150. got_service_not_found = true;
  151. }
  152. assert(got_service_not_found);
  153. }
  154. // test_prelim_dep: A preliminary (unresolved) service dependency
  155. class test_prelim_dep
  156. {
  157. public:
  158. std::string name;
  159. dependency_type dep_type;
  160. test_prelim_dep(const std::string &name_p, dependency_type dep_type_p)
  161. : name(name_p), dep_type(dep_type_p) { }
  162. test_prelim_dep(std::string &&name_p, dependency_type dep_type_p)
  163. : name(std::move(name_p)), dep_type(dep_type_p) { }
  164. };
  165. void test_settings()
  166. {
  167. using string = std::string;
  168. using string_iterator = std::string::iterator;
  169. using prelim_dep = test_prelim_dep;
  170. dinit_load::service_settings_wrapper<prelim_dep> settings;
  171. std::stringstream ss;
  172. ss << "type = process\n"
  173. "command = /something/test\n"
  174. "depends-on = abc\n"
  175. "rlimit-nofile = 50:100\n"
  176. "rlimit-core = 60:\n"
  177. "rlimit-data = -:-";
  178. try {
  179. process_service_file("test-service", ss,
  180. [&](string &line, unsigned line_num, string &setting, string_iterator &i, string_iterator &end) -> void {
  181. auto process_dep_dir_n = [&](std::list<prelim_dep> &deplist, const std::string &waitsford,
  182. dependency_type dep_type) -> void {
  183. //process_dep_dir(name.c_str(), service_filename, deplist, waitsford, dep_type);
  184. };
  185. auto load_service_n = [&](const string &dep_name) -> const string & {
  186. return dep_name;
  187. };
  188. try {
  189. process_service_line(settings, "test-service", line, line_num, setting, i, end, load_service_n, process_dep_dir_n);
  190. }
  191. catch (service_description_exc &exc) {
  192. //report_service_description_exc(exc);
  193. }
  194. });
  195. }
  196. catch (std::system_error &sys_err)
  197. {
  198. //report_error(sys_err, name);
  199. throw service_description_exc("", "error while reading service description.", "unknown");
  200. }
  201. assert(settings.service_type == service_type_t::PROCESS);
  202. assert(settings.command == "/something/test");
  203. assert(settings.rlimits.size() == 3);
  204. assert(settings.rlimits[0].resource_id == RLIMIT_NOFILE);
  205. assert(settings.rlimits[0].soft_set && settings.rlimits[0].hard_set);
  206. assert(settings.rlimits[0].limits.rlim_cur == 50);
  207. assert(settings.rlimits[0].limits.rlim_max == 100);
  208. assert(settings.rlimits[1].resource_id == RLIMIT_CORE);
  209. assert(settings.rlimits[1].soft_set && !settings.rlimits[1].hard_set);
  210. assert(settings.rlimits[1].limits.rlim_cur == 60);
  211. assert(settings.rlimits[2].resource_id == RLIMIT_DATA);
  212. assert(settings.rlimits[2].soft_set && settings.rlimits[2].hard_set);
  213. assert(settings.rlimits[2].limits.rlim_cur == RLIM_INFINITY);
  214. assert(settings.rlimits[2].limits.rlim_max == RLIM_INFINITY);
  215. assert(settings.depends.size() == 1);
  216. assert(settings.depends.front().dep_type == dependency_type::REGULAR);
  217. assert(settings.depends.front().name == "abc");
  218. }
  219. void test_path_env_subst()
  220. {
  221. using string = std::string;
  222. using string_iterator = std::string::iterator;
  223. using prelim_dep = test_prelim_dep;
  224. dinit_load::service_settings_wrapper<prelim_dep> settings;
  225. std::stringstream ss;
  226. ss << "type = process\n"
  227. "command = /something/test\n"
  228. "logfile = /some/$username/dir\n";
  229. try {
  230. process_service_file("test-service", ss,
  231. [&](string &line, unsigned line_num, string &setting, string_iterator &i, string_iterator &end) -> void {
  232. auto process_dep_dir_n = [&](std::list<prelim_dep> &deplist, const std::string &waitsford,
  233. dependency_type dep_type) -> void {
  234. //process_dep_dir(name.c_str(), service_filename, deplist, waitsford, dep_type);
  235. };
  236. auto load_service_n = [&](const string &dep_name) -> const string & {
  237. return dep_name;
  238. };
  239. try {
  240. process_service_line(settings, "test-service", line, line_num, setting, i, end, load_service_n, process_dep_dir_n);
  241. }
  242. catch (service_description_exc &exc) {
  243. //report_service_description_exc(exc);
  244. }
  245. });
  246. }
  247. catch (std::system_error &sys_err)
  248. {
  249. throw service_description_exc("", "error while reading service description.", "unknown");
  250. }
  251. auto report_error = [](const char *msg) {};
  252. auto resolve_var = [](const std::string &name, environment::env_map const &) -> const char * {
  253. if (name == "username") return "testsuccess";
  254. return nullptr;
  255. };
  256. settings.finalise(report_error, tenvmap, report_error /* lint */, resolve_var);
  257. assert(settings.service_type == service_type_t::PROCESS);
  258. assert(settings.command == "/something/test");
  259. assert(settings.logfile == "/some/testsuccess/dir");
  260. }
  261. void test_newline()
  262. {
  263. dirload_service_set sset(test_service_dir.c_str());
  264. bp_sys::setenv("arg", "t3", true);
  265. auto t3 = static_cast<base_process_service *>(sset.load_service("t3"));
  266. auto exec_parts = t3->get_exec_arg_parts();
  267. assert(t3->get_type() == service_type_t::PROCESS);
  268. assert(strcmp(exec_parts[0], "command1") == 0);
  269. assert(strcmp(exec_parts[1], "t3") == 0);
  270. assert(strcmp(exec_parts[2], "arg1") == 0);
  271. assert(strcmp(exec_parts[3], "command2") == 0);
  272. assert(strcmp(exec_parts[4], "t3") == 0);
  273. assert(strcmp(exec_parts[5], "arg2") == 0);
  274. assert(strcmp(exec_parts[6], "command3") == 0);
  275. assert(strcmp(exec_parts[7], "t3") == 0);
  276. assert(strcmp(exec_parts[8], "arg3") == 0);
  277. }
  278. void test_newline_err()
  279. {
  280. using string = std::string;
  281. using string_iterator = std::string::iterator;
  282. using sstream = std::stringstream;
  283. using prelim_dep = test_prelim_dep;
  284. unsigned errcount = 0;
  285. auto test_inner = [&](std::stringstream &ss)
  286. {
  287. dinit_load::service_settings_wrapper<prelim_dep> settings;
  288. process_service_file("test-service", ss,
  289. [&](string &line, unsigned line_num, string &setting, string_iterator &i, string_iterator &end) -> void {
  290. auto process_dep_dir_n = [&](std::list<prelim_dep> &deplist, const std::string &waitsford,
  291. dependency_type dep_type) -> void {
  292. //process_dep_dir(name.c_str(), service_filename, deplist, waitsford, dep_type);
  293. };
  294. auto load_service_n = [&](const string &dep_name) -> const string & {
  295. return dep_name;
  296. };
  297. process_service_line(settings, "test-service", line, line_num, setting, i, end, load_service_n, process_dep_dir_n);
  298. });
  299. };
  300. try {
  301. sstream ss;
  302. ss << "type = process\n"
  303. "command = /something/test\\\n"
  304. " # comment with leading space\\\n"
  305. "# comment without leading space";
  306. test_inner(ss);
  307. }
  308. catch (service_description_exc &exc)
  309. {
  310. if (exc.line_num == 4) ++errcount;
  311. }
  312. try {
  313. sstream ss;
  314. ss << "type = process\n"
  315. "command = EOF\\";
  316. test_inner(ss);
  317. }
  318. catch (service_description_exc &exc)
  319. {
  320. if (exc.line_num == 2) ++errcount;
  321. }
  322. assert(errcount == 2);
  323. }
  324. void test_comments()
  325. {
  326. std::string line = "one two three # comment";
  327. std::list<std::pair<unsigned,unsigned>> offsets;
  328. std::string::iterator li = line.begin();
  329. std::string::iterator le = line.end();
  330. std::string val = dinit_load::read_setting_value(1 /* line_num */, li, le, &offsets);
  331. assert(val == "one two three");
  332. assert(offsets.size() == 3);
  333. auto i = offsets.begin();
  334. assert(i->first == 0);
  335. assert(i->second == 3);
  336. ++i;
  337. assert(i->first == 4);
  338. assert(i->second == 7);
  339. ++i;
  340. assert(i->first == 8);
  341. assert(i->second == 13);
  342. }
  343. #define RUN_TEST(name, spacing) \
  344. std::cout << #name "..." spacing << std::flush; \
  345. name(); \
  346. std::cout << "PASSED" << std::endl;
  347. int main(int argc, char **argv)
  348. {
  349. init_test_service_dir();
  350. RUN_TEST(test_basic, " ");
  351. RUN_TEST(test_env_subst, " ");
  352. RUN_TEST(test_env_subst2, " ");
  353. RUN_TEST(test_env_subst3, " ");
  354. RUN_TEST(test_nonexistent, " ");
  355. RUN_TEST(test_settings, " ");
  356. RUN_TEST(test_path_env_subst, " ");
  357. RUN_TEST(test_newline, " ");
  358. RUN_TEST(test_newline_err, " ");
  359. RUN_TEST(test_comments, " ");
  360. bp_sys::clearenv();
  361. return 0;
  362. }