igr-runner.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. #include <string>
  2. #include <iostream>
  3. #include <cstring>
  4. #include <pwd.h>
  5. #include <spawn.h>
  6. #include <unistd.h>
  7. #include <sys/wait.h>
  8. #include <sys/stat.h>
  9. #include <sys/types.h>
  10. #include "igr.h"
  11. extern char **environ;
  12. // Integration test suite runner.
  13. std::string igr_dinit_socket_path;
  14. void basic_test();
  15. void environ_test();
  16. void environ2_test();
  17. void ps_environ_test();
  18. void chain_to_test();
  19. void force_stop_test();
  20. void restart_test();
  21. void check_basic_test();
  22. void check_cycle_test();
  23. void check_cycle2_test();
  24. void check_lint_test();
  25. int main(int argc, char **argv)
  26. {
  27. void (*test_funcs[])() = { basic_test, environ_test, environ2_test, ps_environ_test,
  28. chain_to_test, force_stop_test, restart_test, check_basic_test, check_cycle_test,
  29. check_cycle2_test, check_lint_test };
  30. const char * const test_dirs[] = { "basic", "environ", "environ2", "ps-environ", "chain-to", "force-stop",
  31. "restart", "check-basic", "check-cycle", "check-cycle2", "check-lint", "reload1", "reload2",
  32. "no-command-error", "add-rm-dep", "var-subst", "svc-start-fail", "dep-not-found", "pseudo-cycle",
  33. "before-after", "before-after2", "log-via-pipe", "catlog", "offline-enable", "xdg-config",
  34. "cycles" };
  35. constexpr int num_tests = sizeof(test_dirs) / sizeof(test_dirs[0]);
  36. dinit_bindir = "../.."; // XXX
  37. igr_output_basedir = "igr-output"; // XXX
  38. int passed = 0;
  39. int skipped = 0;
  40. int failed = 0;
  41. bool aborted_run = false;
  42. char *env_igr_output_base = getenv("IGR_OUTPUT_BASE");
  43. if (env_igr_output_base != nullptr) {
  44. igr_output_basedir = env_igr_output_base;
  45. }
  46. igr_dinit_socket_path = igr_output_basedir + "/dinit.socket";
  47. std::cout << "============== INTEGRATION TESTS =====================" << std::endl;
  48. if (mkdir(igr_output_basedir.c_str(), 0700) == -1 && errno != EEXIST) {
  49. std::system_error(errno, std::generic_category(), std::string("mkdir: ") + igr_output_basedir);
  50. }
  51. for (int i = 0; i < num_tests; i++) {
  52. const char * test_dir = test_dirs[i];
  53. std::cout << test_dir << "... " << std::flush;
  54. if ((unsigned)i < (sizeof(test_funcs) / sizeof(test_funcs[0]))) {
  55. // run function instead
  56. bool success;
  57. std::string failure_msg;
  58. try {
  59. test_funcs[i]();
  60. success = true;
  61. }
  62. catch (igr_failure_exc &exc) {
  63. success = false;
  64. failure_msg = exc.get_message();
  65. }
  66. if (success) {
  67. std::cout << "PASSED\n";
  68. passed++;
  69. }
  70. else {
  71. std::cout << "FAILED\n";
  72. failed++;
  73. std::cout << failure_msg << std::endl;
  74. }
  75. continue;
  76. }
  77. std::string prog_path = "./run-test.sh";
  78. char * const p_argv[2] = { const_cast<char *>(prog_path.c_str()), nullptr };
  79. // "Use posix_spawn", they said. "It will be easy", they said.
  80. if (chdir(test_dir) != 0) {
  81. std::cerr << "Couldn't chdir: " << test_dir << ": " << strerror(errno) << std::endl;
  82. continue;
  83. }
  84. posix_spawn_file_actions_t p_actions;
  85. posix_spawnattr_t p_attr;
  86. if (posix_spawn_file_actions_init(&p_actions) != 0) {
  87. // out of memory?
  88. std::cerr << "Error launching process: " << test_dir << "/run-test.sh: " << strerror(errno) << std::endl;
  89. aborted_run = true;
  90. break;
  91. }
  92. if (posix_spawnattr_init(&p_attr) != 0) {
  93. // out of memory?
  94. std::cerr << "Error launching process: " << test_dir << "/run-test.sh: " << strerror(errno) << std::endl;
  95. aborted_run = true;
  96. break;
  97. }
  98. pid_t subproc_pid;
  99. if (posix_spawn(&subproc_pid, prog_path.c_str(), &p_actions, &p_attr, p_argv, environ) != 0) {
  100. // fail out
  101. std::cerr << "Failed to run run-test.sh in " << test_dir << std::endl;
  102. continue;
  103. }
  104. int wstatus;
  105. if (waitpid(subproc_pid, &wstatus, 0) == -1) {
  106. std::cout << "(unknown)" << std::endl;
  107. std::cerr << "waitpid() failed" << std::endl;
  108. aborted_run = true;
  109. break;
  110. }
  111. if (WIFEXITED(wstatus)) {
  112. if (WEXITSTATUS(wstatus) == 0) {
  113. std::cout << "PASSED" << std::endl;
  114. passed++;
  115. }
  116. else if (WEXITSTATUS(wstatus) == 1) {
  117. std::cout << "FAILED" << std::endl;
  118. failed++;
  119. }
  120. else if (WEXITSTATUS(wstatus) == 77) {
  121. std::cout << "SKIPPED" << std::endl;
  122. skipped++;
  123. }
  124. else {
  125. std::cout << "???" << std::endl;
  126. }
  127. }
  128. else {
  129. std::cout << "*** terminated abnormally ***" << std::endl;
  130. aborted_run = true;
  131. break;
  132. }
  133. posix_spawnattr_destroy(&p_attr);
  134. posix_spawn_file_actions_destroy(&p_actions);
  135. chdir("..");
  136. }
  137. std::cout << "======================================================" << std::endl;
  138. if (! aborted_run) {
  139. std::cout << "Test run finished.\n"
  140. "Passed: " << passed << "\n"
  141. "Failed: " << failed;
  142. if (failed != 0) {
  143. std::cout << " XXX";
  144. }
  145. std::cout << "\n"
  146. "Skipped: " << skipped << std::endl;
  147. }
  148. else {
  149. std::cout << "Test run aborted." << std::endl;
  150. }
  151. return failed == 0 ? 0 : 1;
  152. }
  153. void basic_test()
  154. {
  155. igr_test_setup setup("basic");
  156. std::string ran_file = setup.prep_output_file("basic-ran");
  157. // Start the "basic" service. This creates an output file, "basic-ran", containing "ran\n".
  158. dinit_proc dinit_p;
  159. dinit_p.start("basic", {"-u", "-d", "sd", "-p", igr_dinit_socket_path, "-q", "basic"});
  160. dinit_p.wait_for_term({1,0} /* max 1 second */);
  161. igr_assert_eq("", dinit_p.get_stdout());
  162. igr_assert_eq("", dinit_p.get_stderr());
  163. check_file_contents(ran_file.c_str(), "ran\n");
  164. }
  165. void environ_test()
  166. {
  167. igr_test_setup setup("environ");
  168. std::string output_file = setup.prep_output_file("env-record");
  169. igr_env_var_setup env_output("OUTPUT", output_file.c_str());
  170. igr_env_var_setup env_socket("SOCKET", igr_dinit_socket_path.c_str());
  171. igr_env_var_setup env_dinitctl("DINITCTL", (dinit_bindir + "/dinitctl").c_str());
  172. dinit_proc dinit_p;
  173. dinit_p.start("environ", {"-u", "-d", "sd", "-p", igr_dinit_socket_path, "-q", "-e", "environment1", "checkenv"});
  174. dinit_p.wait_for_term({1,0} /* max 1 second */);
  175. dinit_p.start("environ", {"-u", "-d", "sd", "-p", igr_dinit_socket_path, "-q", "-e", "environment2", "checkenv"});
  176. dinit_p.wait_for_term({1,0} /* max 1 second */);
  177. dinit_p.start("environ", {"-u", "-d", "sd", "-p", igr_dinit_socket_path, /* "-q", */ "setenv1"});
  178. dinit_p.wait_for_term({1,0} /* max 1 second */);
  179. check_file_contents(output_file, igr_dinit_socket_path + "\n" +
  180. "checkenv\n" +
  181. "gotenv1\n" +
  182. "hello\n" +
  183. "gotenv2\n" +
  184. "goodbye\n" +
  185. "3\n2\n1\n");
  186. }
  187. void environ2_test()
  188. {
  189. igr_test_setup setup("environ2");
  190. std::string output_file = setup.prep_output_file("env-record");
  191. uid_t my_uid = getuid();
  192. gid_t my_gid = getgid();
  193. struct passwd *my_pwd_ent = getpwuid(my_uid);
  194. if (my_pwd_ent == nullptr) {
  195. throw std::system_error(errno, std::generic_category(), "getpwuid");
  196. }
  197. // unset variables to make sure the values seen in the test service were initialised by dinit:
  198. igr_env_var_setup env_user("USER", nullptr);
  199. igr_env_var_setup env_logname("LOGNAME", nullptr);
  200. igr_env_var_setup env_shell("SHELL", nullptr);
  201. igr_env_var_setup env_uid("UID", nullptr);
  202. igr_env_var_setup env_gid("GID", nullptr);
  203. // test whether vars from global environment propagate
  204. igr_env_var_setup env_test_var("TEST_VAR", "helloworld");
  205. dinit_proc dinit_p;
  206. dinit_p.start("environ2", {"-u", "-d", "sd", "-p", igr_dinit_socket_path, "-q", "-e", "env-dinit", "checkenv"});
  207. dinit_p.wait_for_term({1,0} /* max 1 second */);
  208. check_file_contents(output_file,
  209. std::string("helloworld\n") +
  210. "hello\n" +
  211. "override\n" +
  212. my_pwd_ent->pw_name + "\n" +
  213. my_pwd_ent->pw_name + "\n" +
  214. "/bogus/value\n" +
  215. std::to_string(my_uid) + "\n" +
  216. std::to_string(my_gid) + "\n");
  217. }
  218. void ps_environ_test()
  219. {
  220. igr_test_setup setup("ps-environ");
  221. std::string output_file = setup.prep_output_file("env-record");
  222. igr_env_var_setup env_output("OUTPUT", output_file.c_str());
  223. igr_env_var_setup env_test_var_two("TEST_VAR_TWO", "set-via-script");
  224. dinit_proc dinit_p;
  225. dinit_p.start("ps-environ", {"-u", "-d", "sd", "-p", igr_dinit_socket_path, "-q", "checkenv1"});
  226. dinit_p.wait_for_term({1,0} /* max 1 second */);
  227. dinit_p.start("ps-environ", {"-u", "-d", "sd", "-p", igr_dinit_socket_path, "-q", "checkenv2"});
  228. dinit_p.wait_for_term({1,0} /* max 1 second */);
  229. dinit_p.start("ps-environ", {"-u", "-d", "sd", "-p", igr_dinit_socket_path, "-q", "checkenv3"});
  230. dinit_p.wait_for_term({1,0} /* max 1 second */);
  231. // "set-in-dinit-env"
  232. dinit_p.start("ps-environ", {"-u", "-d", "sd", "-p", igr_dinit_socket_path, "-q", "-e", "dinit-environment", "checkenv4"});
  233. dinit_p.wait_for_term({1,0} /* max 1 second */);
  234. // "set-via-script" (as per above)
  235. dinit_p.start("ps-environ", {"-u", "-d", "sd", "-p", igr_dinit_socket_path, "-q", "checkenv4"});
  236. dinit_p.wait_for_term({1,0} /* max 1 second */);
  237. check_file_contents(output_file, read_file_contents("./ps-environ/env-expected"));
  238. }
  239. void chain_to_test()
  240. {
  241. igr_test_setup setup("chain-to");
  242. std::string output_file = setup.prep_output_file("recorded-output");
  243. dinit_proc dinit_p;
  244. dinit_p.start("chain-to", {"-u", "-d", "sd", "-p", igr_dinit_socket_path, "-q", "part1"});
  245. dinit_p.wait_for_term({1,0} /* max 1 second */);
  246. check_file_contents(output_file, read_file_contents("./chain-to/expected-output"));
  247. }
  248. void force_stop_test()
  249. {
  250. igr_test_setup setup("force-stop");
  251. dinit_proc dinit_p;
  252. dinit_p.start("force-stop", {"-u", "-d", "sd", "-p", igr_dinit_socket_path, "-q"}, true);
  253. // "dinitctl list"
  254. dinitctl_proc dinitctl_p;
  255. dinitctl_p.start("force-stop", {"-p", igr_dinit_socket_path, "list"});
  256. dinitctl_p.wait_for_term({1, 0} /* max 1 second */);
  257. igr_assert_eq(read_file_contents("./force-stop/expected-1"), dinitctl_p.get_stdout());
  258. igr_assert_eq("", dinitctl_p.get_stderr());
  259. // "dinitctl stop critical"
  260. dinitctl_p.start("force-stop", {"-p", igr_dinit_socket_path, "stop", "critical"});
  261. dinitctl_p.wait_for_term({1, 0} /* max 1 second */);
  262. igr_assert_eq("", dinitctl_p.get_stdout());
  263. igr_assert_eq(read_file_contents("./force-stop/expected-2.err"), dinitctl_p.get_stderr());
  264. // "dinitctl stop --force critical"
  265. dinitctl_p.start("force-stop", {"-p", igr_dinit_socket_path, "stop", "--force", "critical"});
  266. dinitctl_p.wait_for_term({1, 0} /* max 1 second */);
  267. igr_assert_eq(read_file_contents("./force-stop/expected-3"), dinitctl_p.get_stdout());
  268. igr_assert_eq("", dinitctl_p.get_stderr());
  269. // dinit should stop since all services are now stopped
  270. dinit_p.wait_for_term({1, 0});
  271. }
  272. void restart_test()
  273. {
  274. igr_test_setup setup("restart");
  275. std::string output_file = setup.prep_output_file("basic-ran");
  276. dinit_proc dinit_p;
  277. dinit_p.start("restart", {"-u", "-d", "sd", "-p", igr_dinit_socket_path, "-q"}, true);
  278. // "dinitctl start boot" - wait until "boot" has fully started:
  279. dinitctl_proc dinitctl_p;
  280. dinitctl_p.start("restart", {"-p", igr_dinit_socket_path, "start", "boot"});
  281. dinitctl_p.wait_for_term({1, 0} /* max 1 second */);
  282. // "basic" is a process service. It has started, but we need to give it a little time to
  283. // write its output:
  284. nanosleepx(0, 1000000000u / 10u);
  285. igr_assert_eq("ran\n", read_file_contents(output_file));
  286. if (unlink(output_file.c_str()) == -1) {
  287. throw std::system_error(errno, std::generic_category(), "unlink");
  288. }
  289. // "dinitctl restart basic"
  290. dinitctl_p.start("restart", {"-p", igr_dinit_socket_path, "restart", "basic"});
  291. dinitctl_p.wait_for_term({1, 0} /* max 1 second */);
  292. nanosleepx(0, 1000000000u / 10u);
  293. igr_assert_eq("ran\n", read_file_contents(output_file));
  294. }
  295. void check_basic_test()
  296. {
  297. igr_test_setup setup("check-basic");
  298. auto check_result = run_dinitcheck("check-basic", {"-d", "sd"});
  299. igr_assert_eq(read_file_contents("./check-basic/expected.txt"), check_result.first);
  300. igr_assert(WEXITSTATUS(check_result.second) == 1, "dinitcheck exit status == 1");
  301. }
  302. void check_cycle_test()
  303. {
  304. igr_test_setup setup("check-cycle");
  305. auto check_result = run_dinitcheck("check-cycle", {"-d", "sd"});
  306. igr_assert_eq(read_file_contents("./check-cycle/expected.txt"), check_result.first);
  307. igr_assert(WEXITSTATUS(check_result.second) == 1, "dinitcheck exit status == 1");
  308. }
  309. void check_cycle2_test()
  310. {
  311. igr_test_setup setup("check-cycle2");
  312. auto check_result = run_dinitcheck("check-cycle2", {"-d", "sd"});
  313. igr_assert_eq(read_file_contents("./check-cycle2/expected.txt"), check_result.first);
  314. igr_assert(WEXITSTATUS(check_result.second) == 1, "dinitcheck exit status == 1");
  315. }
  316. void check_lint_test()
  317. {
  318. igr_test_setup setup("check-lint");
  319. auto check_result = run_dinitcheck("check-lint", {"-d", "sd"});
  320. igr_assert_eq(read_file_contents("./check-lint/expected.txt"), check_result.first);
  321. igr_assert(WEXITSTATUS(check_result.second) == 1, "dinitcheck exit status == 1");
  322. }