PityTest11 is a very flexible C++11 peer-to-peer test framework supporting hierarchically structured test suites, multi-processing, logging, IPC, synchronization and more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

458 lines
15 KiB

  1. // This file is under GNU General Public License 3.0
  2. // see LICENSE.txt
  3. #include "AbstractPityUnit.hh"
  4. #include <pEp/std_utils.hh>
  5. #include <iostream>
  6. #include <unistd.h>
  7. #include <cstdlib>
  8. #include <sys/stat.h>
  9. #include <exception>
  10. #include <memory>
  11. #include <sys/wait.h>
  12. namespace pEp {
  13. namespace PityTest11 {
  14. // static
  15. std::string AbstractPityUnit::_global_root_dir = Utils::path_get_abs("pitytest_data/");
  16. // static
  17. bool AbstractPityUnit::debug_log_enabled = false;
  18. // static
  19. int AbstractPityUnit::_procUnitsCount = 0;
  20. AbstractPityUnit::AbstractPityUnit(const std::string &name, ExecutionMode exec_mode) :
  21. PityTree<AbstractPityUnit>(*this, name), _exec_mode{ exec_mode }, _procUnitNr{ 0 }
  22. {
  23. _init();
  24. }
  25. AbstractPityUnit::AbstractPityUnit(
  26. AbstractPityUnit &parent,
  27. const std::string &name,
  28. ExecutionMode exec_mode) :
  29. PityTree<AbstractPityUnit>(*this, name, parent),
  30. _exec_mode{ exec_mode }, _procUnitNr{ 0 }
  31. {
  32. _init();
  33. }
  34. AbstractPityUnit::AbstractPityUnit(const AbstractPityUnit &rhs, AbstractPityUnit &self) :
  35. PityTree<AbstractPityUnit>(rhs, self)
  36. {
  37. _procUnitNr = rhs._procUnitNr;
  38. _exec_mode = rhs._exec_mode;
  39. _transport = rhs._transport; // Will re-initialized in run()
  40. _transport_endpoints = rhs._transport_endpoints; // Will re-initialized in run()
  41. _init();
  42. }
  43. // AbstractPityUnit &AbstractPityUnit::operator=(const AbstractPityUnit &rhs)
  44. // {
  45. // _procUnitNr = rhs._procUnitNr;
  46. // _exec_mode = rhs._exec_mode;
  47. // _transport = rhs._transport;
  48. // _transport_endpoints = rhs._transport_endpoints;
  49. // return *this;
  50. // }
  51. void AbstractPityUnit::_init()
  52. {
  53. _log_mutex = std::make_shared<fs_mutex>("log.mutex");
  54. _log_mutex->release();
  55. }
  56. // static
  57. // void AbstractPityUnit::setGlobalRootDir(const std::string &dir)
  58. // {
  59. // AbstractPityUnit::_global_root_dir = dir;
  60. // }
  61. // static
  62. std::string AbstractPityUnit::getGlobalRootDir()
  63. {
  64. return AbstractPityUnit::_global_root_dir;
  65. }
  66. void AbstractPityUnit::setExecMode(AbstractPityUnit::ExecutionMode execMode)
  67. {
  68. _exec_mode = execMode;
  69. }
  70. // For:
  71. // RootUnit - "<name>"
  72. // ProcessUnit - ".../<proc>"
  73. // When Process as dir. parent - ".../<proc>/name"
  74. // When no process as dir. parent - ".../<proc>/.../name"
  75. std::string AbstractPityUnit::getPathShort() const
  76. {
  77. std::string ret;
  78. if (isRoot()) {
  79. ret = getName();
  80. } else {
  81. if (isProcessUnit()) {
  82. ret += ".../" + getName();
  83. } else {
  84. if (&(getParentProcessUnit()) == (getParent())) {
  85. ret = getParentProcessUnit().getPathShort() + "/" + getName();
  86. } else {
  87. ret = getParentProcessUnit().getPathShort() + "/.../" + getName();
  88. }
  89. }
  90. }
  91. return ret;
  92. }
  93. // Every process has its own dir inside its rootUnitDir
  94. // All other units inherit processDir from their Root/ProcessUnit
  95. std::string AbstractPityUnit::getProcessDir()
  96. {
  97. if (isRoot()) {
  98. return getRootUnitDir();
  99. } else {
  100. if (isProcessUnit()) {
  101. return getGlobalRootDir() + _normalizeName(getPath()) + "/";
  102. } else {
  103. return getParent()->getProcessDir();
  104. }
  105. }
  106. }
  107. // Every RootUnit has its own dir
  108. std::string AbstractPityUnit::getRootUnitDir()
  109. {
  110. return getGlobalRootDir() + getRoot()->getName() + "/";
  111. }
  112. // Every process has its own dir inside its rootUnitDir
  113. // All other units inherit transportDir from their Root/ProcessUnit
  114. std::string AbstractPityUnit::getTransportDir()
  115. {
  116. return getProcessDir() + "inbox/";
  117. }
  118. void AbstractPityUnit::_initProcUnitNrRecurse()
  119. {
  120. if (!isRoot()) {
  121. // Inherit
  122. _procUnitNr = getParent()->_procUnitNr;
  123. //Or update if procUnit
  124. if (isProcessUnit()) {
  125. _procUnitsCount++;
  126. _procUnitNr = _procUnitsCount;
  127. }
  128. } else {
  129. _procUnitNr = _procUnitsCount;
  130. }
  131. // Recurse
  132. for (const auto &chld : getChildRefs()) {
  133. chld.second._initProcUnitNrRecurse();
  134. }
  135. }
  136. void AbstractPityUnit::_initTransportRecurse()
  137. {
  138. if (!isRoot()) {
  139. if (isProcessUnit()) {
  140. _createTransport();
  141. }
  142. }
  143. // Recurse
  144. for (const auto &chld : getChildRefs()) {
  145. chld.second._initTransportRecurse();
  146. }
  147. }
  148. void AbstractPityUnit::_initDirsRecursive()
  149. {
  150. Utils::dir_recreate(getProcessDir());
  151. // Recurse
  152. for (const auto &child : getChildRefs()) {
  153. child.second._initDirsRecursive();
  154. }
  155. }
  156. void AbstractPityUnit::run(bool init_tree)
  157. {
  158. pEpLogClass("called");
  159. if (init_tree) {
  160. logH1("PityTest Starting...");
  161. _logRaw("RootUnit: " + getPath());
  162. _logRaw("GlobalRootDir: " + getGlobalRootDir());
  163. _logRaw("Ensuring GlobalRootDir...");
  164. Utils::dir_ensure(getGlobalRootDir());
  165. _logRaw("Recreating process dirs recursively...");
  166. _initDirsRecursive();
  167. //TODO:HACK wait for dir
  168. Utils::sleep_millis(500);
  169. _logRaw("Initializing Transport recursively...");
  170. _initTransportRecurse();
  171. _logRaw("\n\nTestTree");
  172. _logRaw("--------");
  173. _logRaw(to_string() + "\n");
  174. _procUnitsCount = 0;
  175. _initProcUnitNrRecurse();
  176. }
  177. // TODO: hack move to _executeOnFork()
  178. setenv("HOME", getProcessDir().c_str(), true);
  179. // Execute in fork and wait here until process ends
  180. if (_exec_mode == ExecutionMode::PROCESS_SEQUENTIAL) { // fork
  181. _executeInFork(std::bind(&AbstractPityUnit::_runRecurse, this), true);
  182. // Execute in fork and go on, wait for process execution in the end
  183. } else if (_exec_mode == ExecutionMode::PROCESS_PARALLEL) {
  184. _executeInFork(std::bind(&AbstractPityUnit::_runRecurse, this), false);
  185. // Execute as normal function
  186. } else if (_exec_mode == ExecutionMode::FUNCTION) {
  187. _runRecurse();
  188. } else if (_exec_mode == ExecutionMode::THREAD_PARALLEL) {
  189. throw std::invalid_argument(to_string(_exec_mode) + " - not implemented");
  190. } else if (_exec_mode == ExecutionMode::THREAD_SEQUENTIAL) {
  191. throw std::invalid_argument(to_string(_exec_mode) + " - not implemented");
  192. }
  193. if (init_tree) {
  194. _waitChildProcesses();
  195. }
  196. }
  197. std::string AbstractPityUnit::to_string(bool recursive, int indent)
  198. {
  199. std::string ret;
  200. std::stringstream builder;
  201. builder << std::string(indent * 4, ' ');
  202. builder << getName();
  203. builder << " [ ";
  204. builder << to_string(_exec_mode) << " - ";
  205. builder << "\"" << getProcessDir() << "\"";
  206. builder << " ]";
  207. builder << std::endl;
  208. ret = builder.str();
  209. if (recursive) {
  210. if (!getChildRefs().empty()) {
  211. indent++;
  212. for (const auto child : getChildRefs()) {
  213. ret += child.second.to_string(true, indent);
  214. }
  215. indent--;
  216. }
  217. }
  218. return ret;
  219. }
  220. std::string AbstractPityUnit::to_string(const ExecutionMode &emode)
  221. {
  222. switch (emode) {
  223. case ExecutionMode::FUNCTION:
  224. return "FUNCTION";
  225. case ExecutionMode::PROCESS_SEQUENTIAL:
  226. return "PROC_SEQ";
  227. case ExecutionMode::PROCESS_PARALLEL:
  228. return "PROC_PAR";
  229. case ExecutionMode::THREAD_SEQUENTIAL:
  230. return "THREAD_S";
  231. case ExecutionMode::THREAD_PARALLEL:
  232. return "THREAD_P";
  233. case ExecutionMode::INHERIT:
  234. return "INHERIT";
  235. default:
  236. return "UNDEFINED EXECUTION MODE";
  237. }
  238. }
  239. void AbstractPityUnit::registerAsTransportEndpoint()
  240. {
  241. transportEndpoints().insert({ getName(), getTransportDir() });
  242. }
  243. Endpoints &AbstractPityUnit::transportEndpoints()
  244. {
  245. if (isRoot()) {
  246. return _transport_endpoints;
  247. } else {
  248. return getRoot()->transportEndpoints();
  249. }
  250. }
  251. void AbstractPityUnit::log(const std::string &msg) const
  252. {
  253. std::stringstream builder;
  254. builder << "[ ";
  255. builder << std::to_string(getpid());
  256. builder << " - ";
  257. builder << getPathShort();
  258. builder << " ] - ";
  259. builder << msg;
  260. _logRaw(builder.str());
  261. }
  262. void AbstractPityUnit::logH1(const std::string &msg) const
  263. {
  264. Adapter::pEpLog::logH1(msg, _color());
  265. }
  266. void AbstractPityUnit::logH2(const std::string &msg) const
  267. {
  268. Adapter::pEpLog::logH2(msg, _color());
  269. }
  270. void AbstractPityUnit::logH3(const std::string &msg) const
  271. {
  272. Adapter::pEpLog::logH3(msg, _color());
  273. }
  274. // PRIVATE ---------------------------------------------------------------------------------
  275. void AbstractPityUnit::_runRecurse()
  276. {
  277. logH2(_status_string("STARTING"));
  278. _runSelf();
  279. if (!getChildRefs().empty()) {
  280. for (const auto child : getChildRefs()) {
  281. child.second.run(false);
  282. }
  283. }
  284. // This should be fine
  285. _waitChildProcesses();
  286. }
  287. void AbstractPityUnit::_executeInFork(std::function<void(void)> func, bool wait_child)
  288. {
  289. pid_t pid;
  290. pid = fork();
  291. if (pid == pid_t(0)) {
  292. // Setup process env.
  293. Utils::dir_set_cwd(getProcessDir());
  294. func();
  295. exit(0);
  296. } else if (pid < pid_t(0)) {
  297. throw std::runtime_error("Error forking");
  298. }
  299. if (wait_child) {
  300. _waitChildProcesses();
  301. }
  302. }
  303. void AbstractPityUnit::_waitChildProcesses() const
  304. {
  305. int status;
  306. pid_t pid;
  307. while ((pid = wait(&status)) > 0) {
  308. std::string color;
  309. if (status == 0) {
  310. color = "\033[1m\033[32m"; // Green
  311. } else {
  312. color = "\033[1m\033[31m"; // Red
  313. }
  314. logH3(
  315. color + "PROCESS [ " + std::to_string((int)pid) +
  316. " ] EXITED with status code: " + std::to_string(status) +
  317. Utils::to_termcol(_color()));
  318. }
  319. }
  320. bool AbstractPityUnit::isProcessUnit() const
  321. {
  322. bool ret = false;
  323. if (_exec_mode == ExecutionMode::PROCESS_SEQUENTIAL ||
  324. _exec_mode == ExecutionMode::PROCESS_PARALLEL) {
  325. ret = true;
  326. }
  327. return ret;
  328. }
  329. const AbstractPityUnit &AbstractPityUnit::getParentProcessUnit() const
  330. {
  331. if (isRoot() || isProcessUnit()) {
  332. return *this;
  333. } else {
  334. return getParent()->getParentProcessUnit();
  335. }
  336. }
  337. // Inherited (if null see parent recursively)
  338. void AbstractPityUnit::_createTransport()
  339. {
  340. registerAsTransportEndpoint();
  341. _transport = std::make_shared<PityTransport>(getTransportDir(), transportEndpoints());
  342. }
  343. // Inherited (if null see parent recursively)
  344. PityTransport *AbstractPityUnit::transport() const
  345. {
  346. // pEpLogClass("called");
  347. PityTransport *ret = nullptr;
  348. if (_transport != nullptr) {
  349. ret = _transport.get();
  350. } else {
  351. if (!isRoot()) {
  352. ret = getParent()->transport();
  353. }
  354. }
  355. return ret;
  356. }
  357. std::string AbstractPityUnit::_status_string(const std::string &msg) const
  358. {
  359. std::string ret;
  360. ret = "[ " + to_string(_exec_mode) + ": " + std::to_string(getpid()) + " ] [ " +
  361. getPathShort() + " ] [ " + msg + " ]";
  362. return ret;
  363. }
  364. //static
  365. Utils::Color AbstractPityUnit::_colForProcUnitNr(int procUnitNr)
  366. {
  367. int nrColors = 6;
  368. switch (procUnitNr % nrColors) {
  369. case 0:
  370. return Utils::Color::WHITE;
  371. case 1:
  372. return Utils::Color::GREEN;
  373. case 2:
  374. return Utils::Color::YELLOW;
  375. case 3:
  376. return Utils::Color::CYAN;
  377. case 4:
  378. return Utils::Color::BLUE;
  379. case 5:
  380. return Utils::Color::MAGENTA;
  381. default:
  382. return Utils::Color::WHITE;
  383. }
  384. }
  385. Utils::Color AbstractPityUnit::_color() const
  386. {
  387. return _colForProcUnitNr(_procUnitNr);
  388. }
  389. void AbstractPityUnit::_logRaw(const std::string &msg) const
  390. {
  391. // fs-mutex to sync across processes
  392. _log_mutex->aquire();
  393. Adapter::pEpLog::log(msg, _color());
  394. _log_mutex->release();
  395. }
  396. } // namespace PityTest11
  397. } // namespace pEp