testUVProcessChain.cxx 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772
  1. #include <algorithm>
  2. #include <csignal>
  3. #include <cstdio>
  4. #include <functional>
  5. #include <iostream>
  6. #include <sstream>
  7. #include <string>
  8. #include <utility>
  9. #include <vector>
  10. #include <cm/memory>
  11. #include <cm3p/uv.h>
  12. #include "cmGetPipes.h"
  13. #include "cmStringAlgorithms.h"
  14. #include "cmUVHandlePtr.h"
  15. #include "cmUVProcessChain.h"
  16. #include "cmUVStream.h"
  17. #include "cmUVStreambuf.h"
  18. struct ExpectedStatus
  19. {
  20. bool MatchExitStatus;
  21. bool MatchTermSignal;
  22. cmUVProcessChain::Status Status;
  23. cmUVProcessChain::ExceptionCode ExceptionCode;
  24. std::string ExceptionString;
  25. };
  26. static const char* ExceptionCodeToString(cmUVProcessChain::ExceptionCode code)
  27. {
  28. switch (code) {
  29. case cmUVProcessChain::ExceptionCode::None:
  30. return "None";
  31. case cmUVProcessChain::ExceptionCode::Fault:
  32. return "Fault";
  33. case cmUVProcessChain::ExceptionCode::Illegal:
  34. return "Illegal";
  35. case cmUVProcessChain::ExceptionCode::Interrupt:
  36. return "Interrupt";
  37. case cmUVProcessChain::ExceptionCode::Numerical:
  38. return "Numerical";
  39. case cmUVProcessChain::ExceptionCode::Spawn:
  40. return "Spawn";
  41. case cmUVProcessChain::ExceptionCode::Other:
  42. return "Other";
  43. default:
  44. return "";
  45. }
  46. }
  47. bool operator==(const cmUVProcessChain::Status* actual,
  48. const ExpectedStatus& expected)
  49. {
  50. if (expected.Status.SpawnResult != actual->SpawnResult) {
  51. return false;
  52. }
  53. if (expected.Status.Finished != actual->Finished) {
  54. return false;
  55. }
  56. if (expected.MatchExitStatus &&
  57. expected.Status.ExitStatus != actual->ExitStatus) {
  58. return false;
  59. }
  60. if (expected.MatchTermSignal &&
  61. expected.Status.TermSignal != actual->TermSignal) {
  62. return false;
  63. }
  64. if (expected.Status.Finished &&
  65. std::make_pair(expected.ExceptionCode, expected.ExceptionString) !=
  66. actual->GetException()) {
  67. return false;
  68. }
  69. return true;
  70. }
  71. static bool resultsMatch(
  72. const std::vector<const cmUVProcessChain::Status*>& actual,
  73. const std::vector<ExpectedStatus>& expected)
  74. {
  75. return actual.size() == expected.size() &&
  76. std::equal(actual.begin(), actual.end(), expected.begin());
  77. }
  78. static std::string getInput(std::istream& input)
  79. {
  80. char buffer[1024];
  81. std::ostringstream str;
  82. do {
  83. input.read(buffer, 1024);
  84. str.write(buffer, input.gcount());
  85. } while (input.gcount() > 0);
  86. return str.str();
  87. }
  88. template <typename T>
  89. std::function<std::ostream&(std::ostream&)> printExpected(bool match,
  90. const T& value)
  91. {
  92. return [match, value](std::ostream& stream) -> std::ostream& {
  93. if (match) {
  94. stream << value;
  95. } else {
  96. stream << "*";
  97. }
  98. return stream;
  99. };
  100. }
  101. std::ostream& operator<<(
  102. std::ostream& stream,
  103. const std::function<std::ostream&(std::ostream&)>& func)
  104. {
  105. return func(stream);
  106. }
  107. static void printResults(
  108. const std::vector<const cmUVProcessChain::Status*>& actual,
  109. const std::vector<ExpectedStatus>& expected)
  110. {
  111. std::cout << "Expected: " << std::endl;
  112. for (auto const& e : expected) {
  113. std::cout << " SpawnResult: " << e.Status.SpawnResult
  114. << ", Finished: " << e.Status.Finished << ", ExitStatus: "
  115. << printExpected(e.MatchExitStatus, e.Status.ExitStatus)
  116. << ", TermSignal: "
  117. << printExpected(e.MatchTermSignal, e.Status.TermSignal)
  118. << ", ExceptionCode: "
  119. << printExpected(e.Status.Finished,
  120. ExceptionCodeToString(e.ExceptionCode))
  121. << ", ExceptionString: \""
  122. << printExpected(e.Status.Finished, e.ExceptionString) << '"'
  123. << std::endl;
  124. }
  125. std::cout << "Actual:" << std::endl;
  126. for (auto const& a : actual) {
  127. auto exception = a->GetException();
  128. std::cout << " SpawnResult: " << a->SpawnResult
  129. << ", Finished: " << a->Finished
  130. << ", ExitStatus: " << a->ExitStatus
  131. << ", TermSignal: " << a->TermSignal
  132. << ", ExceptionCode: " << ExceptionCodeToString(exception.first)
  133. << ", ExceptionString: \"" << exception.second << '"'
  134. << std::endl;
  135. }
  136. }
  137. static bool checkExecution(cmUVProcessChainBuilder& builder,
  138. std::unique_ptr<cmUVProcessChain>& chain)
  139. {
  140. static const std::vector<ExpectedStatus> status1 = {
  141. { false,
  142. false,
  143. { 0, false, 0, 0 },
  144. cmUVProcessChain::ExceptionCode::None,
  145. "" },
  146. { false,
  147. false,
  148. { 0, false, 0, 0 },
  149. cmUVProcessChain::ExceptionCode::None,
  150. "" },
  151. { false,
  152. false,
  153. { 0, false, 0, 0 },
  154. cmUVProcessChain::ExceptionCode::None,
  155. "" },
  156. };
  157. static const std::vector<ExpectedStatus> status2 = {
  158. { true,
  159. true,
  160. { 0, true, 0, 0 },
  161. cmUVProcessChain::ExceptionCode::None,
  162. "" },
  163. { false,
  164. false,
  165. { 0, false, 0, 0 },
  166. cmUVProcessChain::ExceptionCode::None,
  167. "" },
  168. { false,
  169. false,
  170. { 0, false, 0, 0 },
  171. cmUVProcessChain::ExceptionCode::None,
  172. "" },
  173. };
  174. static const std::vector<ExpectedStatus> status3 = {
  175. { true,
  176. true,
  177. { 0, true, 0, 0 },
  178. cmUVProcessChain::ExceptionCode::None,
  179. "" },
  180. { true,
  181. true,
  182. { 0, true, 1, 0 },
  183. cmUVProcessChain::ExceptionCode::None,
  184. "" },
  185. #ifdef _WIN32
  186. { true,
  187. true,
  188. { 0, true, STATUS_ACCESS_VIOLATION, 0 },
  189. cmUVProcessChain::ExceptionCode::Fault,
  190. "Access violation" },
  191. #else
  192. { false,
  193. true,
  194. { 0, true, 0, SIGABRT },
  195. cmUVProcessChain::ExceptionCode::Other,
  196. "Subprocess aborted" },
  197. #endif
  198. };
  199. std::vector<const cmUVProcessChain::Status*> status;
  200. chain = cm::make_unique<cmUVProcessChain>(builder.Start());
  201. if (!chain->Valid()) {
  202. std::cout << "Valid() returned false, should be true" << std::endl;
  203. return false;
  204. }
  205. status = chain->GetStatus();
  206. if (!resultsMatch(status, status1)) {
  207. std::cout << "GetStatus() did not produce expected output" << std::endl;
  208. printResults(status, status1);
  209. return false;
  210. }
  211. if (chain->Finished()) {
  212. std::cout << "Finished() returned true, should be false" << std::endl;
  213. return false;
  214. }
  215. if (chain->Wait(9000)) {
  216. std::cout << "Wait() returned true, should be false" << std::endl;
  217. return false;
  218. }
  219. status = chain->GetStatus();
  220. if (!resultsMatch(status, status2)) {
  221. std::cout << "GetStatus() did not produce expected output" << std::endl;
  222. printResults(status, status2);
  223. return false;
  224. }
  225. if (chain->Finished()) {
  226. std::cout << "Finished() returned true, should be false" << std::endl;
  227. return false;
  228. }
  229. if (!chain->Wait()) {
  230. std::cout << "Wait() returned false, should be true" << std::endl;
  231. return false;
  232. }
  233. status = chain->GetStatus();
  234. if (!resultsMatch(status, status3)) {
  235. std::cout << "GetStatus() did not produce expected output" << std::endl;
  236. printResults(status, status3);
  237. return false;
  238. }
  239. if (!chain->Finished()) {
  240. std::cout << "Finished() returned false, should be true" << std::endl;
  241. return false;
  242. }
  243. return true;
  244. }
  245. static bool checkOutput(std::istream& outputStream, std::istream& errorStream)
  246. {
  247. std::string output = getInput(outputStream);
  248. if (output != "HELO WRD!") {
  249. std::cout << "Output was \"" << output << "\", expected \"HELO WRD!\""
  250. << std::endl;
  251. return false;
  252. }
  253. std::string error = getInput(errorStream);
  254. auto qemu_error_pos = error.find("qemu:");
  255. if (qemu_error_pos != std::string::npos) {
  256. error.resize(qemu_error_pos);
  257. }
  258. if (error.length() != 3 || error.find('1') == std::string::npos ||
  259. error.find('2') == std::string::npos ||
  260. error.find('3') == std::string::npos) {
  261. std::cout << "Error was \"" << error << "\", expected \"123\""
  262. << std::endl;
  263. return false;
  264. }
  265. return true;
  266. }
  267. bool testUVProcessChainBuiltin(const char* helperCommand)
  268. {
  269. cmUVProcessChainBuilder builder;
  270. std::unique_ptr<cmUVProcessChain> chain;
  271. builder.AddCommand({ helperCommand, "echo" })
  272. .AddCommand({ helperCommand, "capitalize" })
  273. .AddCommand({ helperCommand, "dedup" })
  274. .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
  275. .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR)
  276. .SetBuiltinLoop();
  277. if (builder.GetLoop()) {
  278. std::cout << "GetLoop() should return null" << std::endl;
  279. return false;
  280. }
  281. if (!checkExecution(builder, chain)) {
  282. return false;
  283. }
  284. if (chain->OutputStream() < 0) {
  285. std::cout << "OutputStream() was invalid, expecting valid" << std::endl;
  286. return false;
  287. }
  288. if (chain->ErrorStream() < 0) {
  289. std::cout << "ErrorStream() was invalid, expecting valid" << std::endl;
  290. return false;
  291. }
  292. cmUVPipeIStream output(chain->GetLoop(), chain->OutputStream());
  293. cmUVPipeIStream error(chain->GetLoop(), chain->ErrorStream());
  294. if (!checkOutput(output, error)) {
  295. return false;
  296. }
  297. return true;
  298. }
  299. bool testUVProcessChainBuiltinMerged(const char* helperCommand)
  300. {
  301. cmUVProcessChainBuilder builder;
  302. std::unique_ptr<cmUVProcessChain> chain;
  303. builder.AddCommand({ helperCommand, "echo" })
  304. .AddCommand({ helperCommand, "capitalize" })
  305. .AddCommand({ helperCommand, "dedup" })
  306. .SetMergedBuiltinStreams();
  307. if (!checkExecution(builder, chain)) {
  308. return false;
  309. }
  310. if (chain->OutputStream() < 0) {
  311. std::cout << "OutputStream() was invalid, expecting valid" << std::endl;
  312. return false;
  313. }
  314. if (chain->ErrorStream() < 0) {
  315. std::cout << "ErrorStream() was invalid, expecting valid" << std::endl;
  316. return false;
  317. }
  318. if (chain->OutputStream() != chain->ErrorStream()) {
  319. std::cout << "OutputStream() and ErrorStream() expected to be the same"
  320. << std::endl;
  321. return false;
  322. }
  323. cmUVPipeIStream mergedStream(chain->GetLoop(), chain->OutputStream());
  324. std::string merged = getInput(mergedStream);
  325. auto qemuErrorPos = merged.find("qemu:");
  326. if (qemuErrorPos != std::string::npos) {
  327. merged.resize(qemuErrorPos);
  328. }
  329. if (merged.length() != cmStrLen("HELO WRD!123") ||
  330. merged.find('1') == std::string::npos ||
  331. merged.find('2') == std::string::npos ||
  332. merged.find('3') == std::string::npos) {
  333. std::cout << "Expected output to contain '1', '2', and '3', was \""
  334. << merged << "\"" << std::endl;
  335. return false;
  336. }
  337. std::string output;
  338. for (auto const& c : merged) {
  339. if (c != '1' && c != '2' && c != '3') {
  340. output += c;
  341. }
  342. }
  343. if (output != "HELO WRD!") {
  344. std::cout << "Output was \"" << output << "\", expected \"HELO WRD!\""
  345. << std::endl;
  346. return false;
  347. }
  348. return true;
  349. }
  350. bool testUVProcessChainExternal(const char* helperCommand)
  351. {
  352. cmUVProcessChainBuilder builder;
  353. std::unique_ptr<cmUVProcessChain> chain;
  354. int outputPipe[2], errorPipe[2];
  355. cm::uv_pipe_ptr outputInPipe, outputOutPipe, errorInPipe, errorOutPipe;
  356. if (cmGetPipes(outputPipe) < 0) {
  357. std::cout << "Error creating pipes" << std::endl;
  358. return false;
  359. }
  360. if (cmGetPipes(errorPipe) < 0) {
  361. std::cout << "Error creating pipes" << std::endl;
  362. return false;
  363. }
  364. builder.AddCommand({ helperCommand, "echo" })
  365. .AddCommand({ helperCommand, "capitalize" })
  366. .AddCommand({ helperCommand, "dedup" })
  367. .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, outputPipe[1])
  368. .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, errorPipe[1]);
  369. if (builder.GetLoop()) {
  370. std::cout << "GetLoop() should return null" << std::endl;
  371. return false;
  372. }
  373. if (!checkExecution(builder, chain)) {
  374. return false;
  375. }
  376. if (chain->OutputStream() >= 0) {
  377. std::cout << "OutputStream() was valid, expecting invalid" << std::endl;
  378. return false;
  379. }
  380. if (chain->ErrorStream() >= 0) {
  381. std::cout << "ErrorStream() was valid, expecting invalid" << std::endl;
  382. return false;
  383. }
  384. outputOutPipe.init(chain->GetLoop(), 0);
  385. uv_pipe_open(outputOutPipe, outputPipe[1]);
  386. outputOutPipe.reset();
  387. errorOutPipe.init(chain->GetLoop(), 0);
  388. uv_pipe_open(errorOutPipe, errorPipe[1]);
  389. errorOutPipe.reset();
  390. outputInPipe.init(chain->GetLoop(), 0);
  391. uv_pipe_open(outputInPipe, outputPipe[0]);
  392. cmUVStreambuf outputBuf;
  393. outputBuf.open(outputInPipe);
  394. std::istream outputStream(&outputBuf);
  395. errorInPipe.init(chain->GetLoop(), 0);
  396. uv_pipe_open(errorInPipe, errorPipe[0]);
  397. cmUVStreambuf errorBuf;
  398. errorBuf.open(errorInPipe);
  399. std::istream errorStream(&errorBuf);
  400. if (!checkOutput(outputStream, errorStream)) {
  401. return false;
  402. }
  403. return true;
  404. }
  405. bool testUVProcessChainNone(const char* helperCommand)
  406. {
  407. cmUVProcessChainBuilder builder;
  408. std::unique_ptr<cmUVProcessChain> chain;
  409. builder.AddCommand({ helperCommand, "echo" })
  410. .AddCommand({ helperCommand, "capitalize" })
  411. .AddCommand({ helperCommand, "dedup" });
  412. if (!checkExecution(builder, chain)) {
  413. return false;
  414. }
  415. if (chain->OutputStream() >= 0) {
  416. std::cout << "OutputStream() was valid, expecting invalid" << std::endl;
  417. return false;
  418. }
  419. if (chain->ErrorStream() >= 0) {
  420. std::cout << "ErrorStream() was valid, expecting invalid" << std::endl;
  421. return false;
  422. }
  423. return true;
  424. }
  425. bool testUVProcessChainCwdUnchanged(const char* helperCommand)
  426. {
  427. cmUVProcessChainBuilder builder;
  428. builder.AddCommand({ helperCommand, "pwd" })
  429. .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
  430. .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
  431. auto chain = builder.Start();
  432. chain.Wait();
  433. if (chain.GetStatus().front()->ExitStatus != 0) {
  434. std::cout << "Exit status was " << chain.GetStatus().front()->ExitStatus
  435. << ", expecting 0" << std::endl;
  436. return false;
  437. }
  438. cmUVPipeIStream output(chain.GetLoop(), chain.OutputStream());
  439. auto cwd = getInput(output);
  440. if (!cmHasLiteralSuffix(cwd, "/Tests/CMakeLib")) {
  441. std::cout << "Working directory was \"" << cwd
  442. << "\", expected to end in \"/Tests/CMakeLib\"" << std::endl;
  443. return false;
  444. }
  445. return true;
  446. }
  447. bool testUVProcessChainCwdChanged(const char* helperCommand)
  448. {
  449. cmUVProcessChainBuilder builder;
  450. builder.AddCommand({ helperCommand, "pwd" })
  451. .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
  452. .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR)
  453. .SetWorkingDirectory("..");
  454. auto chain = builder.Start();
  455. chain.Wait();
  456. if (chain.GetStatus().front()->ExitStatus != 0) {
  457. std::cout << "Exit status was " << chain.GetStatus().front()->ExitStatus
  458. << ", expecting 0" << std::endl;
  459. return false;
  460. }
  461. cmUVPipeIStream output(chain.GetLoop(), chain.OutputStream());
  462. auto cwd = getInput(output);
  463. if (!cmHasLiteralSuffix(cwd, "/Tests")) {
  464. std::cout << "Working directory was \"" << cwd
  465. << "\", expected to end in \"/Tests\"" << std::endl;
  466. return false;
  467. }
  468. return true;
  469. }
  470. bool testUVProcessChainSpawnFail(const char* helperCommand)
  471. {
  472. static const std::vector<ExpectedStatus> status1 = {
  473. { false,
  474. false,
  475. { 0, false, 0, 0 },
  476. cmUVProcessChain::ExceptionCode::None,
  477. "" },
  478. { false,
  479. false,
  480. { UV_ENOENT, true, 0, 0 },
  481. cmUVProcessChain::ExceptionCode::Spawn,
  482. uv_strerror(UV_ENOENT) },
  483. #ifdef _WIN32
  484. { true,
  485. true,
  486. { 0, true, STATUS_ACCESS_VIOLATION, 0 },
  487. cmUVProcessChain::ExceptionCode::Fault,
  488. "Access violation" },
  489. #else
  490. { false,
  491. true,
  492. { 0, true, 0, SIGABRT },
  493. cmUVProcessChain::ExceptionCode::Other,
  494. "Subprocess aborted" },
  495. #endif
  496. };
  497. static const std::vector<ExpectedStatus> status2 = {
  498. #ifdef _WIN32
  499. { true,
  500. true,
  501. { 0, true, 0, 0 },
  502. cmUVProcessChain::ExceptionCode::None,
  503. "" },
  504. #else
  505. { false,
  506. true,
  507. { 0, true, 0, SIGPIPE },
  508. cmUVProcessChain::ExceptionCode::Other,
  509. "SIGPIPE" },
  510. #endif
  511. { false,
  512. false,
  513. { UV_ENOENT, true, 0, 0 },
  514. cmUVProcessChain::ExceptionCode::Spawn,
  515. uv_strerror(UV_ENOENT) },
  516. #ifdef _WIN32
  517. { true,
  518. true,
  519. { 0, true, STATUS_ACCESS_VIOLATION, 0 },
  520. cmUVProcessChain::ExceptionCode::Fault,
  521. "Access violation" },
  522. #else
  523. { false,
  524. true,
  525. { 0, true, 0, SIGABRT },
  526. cmUVProcessChain::ExceptionCode::Other,
  527. "Subprocess aborted" },
  528. #endif
  529. };
  530. std::vector<const cmUVProcessChain::Status*> status;
  531. cmUVProcessChainBuilder builder;
  532. builder.AddCommand({ helperCommand, "echo" })
  533. .AddCommand({ "this_command_is_for_cmake_and_should_never_exist" })
  534. .AddCommand({ helperCommand, "dedup" })
  535. .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
  536. .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
  537. auto chain = builder.Start();
  538. if (!chain.Valid()) {
  539. std::cout << "Valid() returned false, should be true" << std::endl;
  540. return false;
  541. }
  542. // Some platforms, like Solaris 10, take a long time to report a trapped
  543. // subprocess to the parent process (about 1.7 seconds in the case of
  544. // Solaris 10.) Wait 3 seconds to give it enough time.
  545. if (chain.Wait(3000)) {
  546. std::cout << "Wait() did not time out" << std::endl;
  547. return false;
  548. }
  549. status = chain.GetStatus();
  550. if (!resultsMatch(status, status1)) {
  551. std::cout << "GetStatus() did not produce expected output" << std::endl;
  552. printResults(status, status1);
  553. return false;
  554. }
  555. if (!chain.Wait()) {
  556. std::cout << "Wait() timed out" << std::endl;
  557. return false;
  558. }
  559. status = chain.GetStatus();
  560. if (!resultsMatch(status, status2)) {
  561. std::cout << "GetStatus() did not produce expected output" << std::endl;
  562. printResults(status, status2);
  563. return false;
  564. }
  565. return true;
  566. }
  567. bool testUVProcessChainInputFile(const char* helperCommand)
  568. {
  569. std::unique_ptr<FILE, int (*)(FILE*)> f(
  570. fopen("testUVProcessChainInput.txt", "rb"), fclose);
  571. cmUVProcessChainBuilder builder;
  572. builder.AddCommand({ helperCommand, "dedup" })
  573. .SetExternalStream(cmUVProcessChainBuilder::Stream_INPUT, f.get())
  574. .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT);
  575. auto chain = builder.Start();
  576. if (!chain.Wait()) {
  577. std::cout << "Wait() timed out" << std::endl;
  578. return false;
  579. }
  580. cmUVPipeIStream stream(chain.GetLoop(), chain.OutputStream());
  581. std::string output = getInput(stream);
  582. if (output != "HELO WRD!") {
  583. std::cout << "Output was \"" << output << "\", expected \"HELO WRD!\""
  584. << std::endl;
  585. return false;
  586. }
  587. return true;
  588. }
  589. bool testUVProcessChainWait0(const char* helperCommand)
  590. {
  591. cmUVProcessChainBuilder builder;
  592. builder.AddCommand({ helperCommand, "echo" });
  593. auto chain = builder.Start();
  594. if (!chain.Wait(0)) {
  595. std::cout << "Wait(0) returned false, should be true" << std::endl;
  596. return false;
  597. }
  598. return true;
  599. }
  600. bool testUVProcessChainExternalLoop(const char* helperCommand)
  601. {
  602. cm::uv_loop_ptr loop;
  603. loop.init();
  604. cmUVProcessChainBuilder builder;
  605. builder.AddCommand({ helperCommand, "echo" })
  606. .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
  607. .SetExternalLoop(*loop);
  608. if (builder.GetLoop() != loop) {
  609. std::cout << "GetLoop() should return external loop" << std::endl;
  610. return false;
  611. }
  612. auto chain = builder.Start();
  613. if (&chain.GetLoop() != loop) {
  614. std::cout << "GetLoop() should return external loop" << std::endl;
  615. return false;
  616. }
  617. if (!chain.Wait()) {
  618. std::cout << "Wait() timed out" << std::endl;
  619. return false;
  620. }
  621. cmUVPipeIStream stream(chain.GetLoop(), chain.OutputStream());
  622. std::string output = getInput(stream);
  623. if (output != "HELLO world!") {
  624. std::cout << "Output was \"" << output << "\", expected \"HELLO world!\""
  625. << std::endl;
  626. return false;
  627. }
  628. return true;
  629. }
  630. int testUVProcessChain(int argc, char** const argv)
  631. {
  632. if (argc < 2) {
  633. std::cout << "Invalid arguments.\n";
  634. return -1;
  635. }
  636. if (!testUVProcessChainBuiltin(argv[1])) {
  637. std::cout << "While executing testUVProcessChainBuiltin().\n";
  638. return -1;
  639. }
  640. if (!testUVProcessChainBuiltinMerged(argv[1])) {
  641. std::cout << "While executing testUVProcessChainBuiltinMerged().\n";
  642. return -1;
  643. }
  644. if (!testUVProcessChainExternal(argv[1])) {
  645. std::cout << "While executing testUVProcessChainExternal().\n";
  646. return -1;
  647. }
  648. if (!testUVProcessChainNone(argv[1])) {
  649. std::cout << "While executing testUVProcessChainNone().\n";
  650. return -1;
  651. }
  652. if (!testUVProcessChainCwdUnchanged(argv[1])) {
  653. std::cout << "While executing testUVProcessChainCwdUnchanged().\n";
  654. return -1;
  655. }
  656. if (!testUVProcessChainCwdChanged(argv[1])) {
  657. std::cout << "While executing testUVProcessChainCwdChanged().\n";
  658. return -1;
  659. }
  660. if (!testUVProcessChainSpawnFail(argv[1])) {
  661. std::cout << "While executing testUVProcessChainSpawnFail().\n";
  662. return -1;
  663. }
  664. if (!testUVProcessChainInputFile(argv[1])) {
  665. std::cout << "While executing testUVProcessChainInputFile().\n";
  666. return -1;
  667. }
  668. if (!testUVProcessChainWait0(argv[1])) {
  669. std::cout << "While executing testUVProcessChainWait0().\n";
  670. return -1;
  671. }
  672. if (!testUVProcessChainExternalLoop(argv[1])) {
  673. std::cout << "While executing testUVProcessChainExternalLoop().\n";
  674. return -1;
  675. }
  676. return 0;
  677. }