testUVStreambuf.cxx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. #include <cstring>
  2. #include <iostream>
  3. #include <string>
  4. #include <vector>
  5. #include <cmext/algorithm>
  6. #include <cm3p/uv.h>
  7. #include <stdint.h>
  8. #include "cmGetPipes.h"
  9. #include "cmUVHandlePtr.h"
  10. #include "cmUVStream.h"
  11. #include "cmUVStreambuf.h"
  12. #define TEST_STR_LINE_1 "This string must be exactly 128 characters long so"
  13. #define TEST_STR_LINE_2 "that we can test CMake's std::streambuf integration"
  14. #define TEST_STR_LINE_3 "with libuv's uv_stream_t."
  15. #define TEST_STR TEST_STR_LINE_1 "\n" TEST_STR_LINE_2 "\n" TEST_STR_LINE_3
  16. static bool writeDataToStreamPipe(uv_loop_t& loop, cm::uv_pipe_ptr& inputPipe,
  17. char* outputData,
  18. unsigned int outputDataLength,
  19. const char* /* unused */)
  20. {
  21. int err;
  22. // Create the pipe
  23. int pipeHandles[2];
  24. if (cmGetPipes(pipeHandles) < 0) {
  25. std::cout << "Could not open pipe" << std::endl;
  26. return false;
  27. }
  28. cm::uv_pipe_ptr outputPipe;
  29. inputPipe.init(loop, 0);
  30. outputPipe.init(loop, 0);
  31. uv_pipe_open(inputPipe, pipeHandles[0]);
  32. uv_pipe_open(outputPipe, pipeHandles[1]);
  33. // Write data for reading
  34. uv_write_t writeReq;
  35. struct WriteCallbackData
  36. {
  37. bool Finished = false;
  38. int Status;
  39. } writeData;
  40. writeReq.data = &writeData;
  41. uv_buf_t outputBuf;
  42. outputBuf.base = outputData;
  43. outputBuf.len = outputDataLength;
  44. if ((err = uv_write(&writeReq, outputPipe, &outputBuf, 1,
  45. [](uv_write_t* req, int status) {
  46. auto data = static_cast<WriteCallbackData*>(req->data);
  47. data->Finished = true;
  48. data->Status = status;
  49. })) < 0) {
  50. std::cout << "Could not write to pipe: " << uv_strerror(err) << std::endl;
  51. return false;
  52. }
  53. while (!writeData.Finished) {
  54. uv_run(&loop, UV_RUN_ONCE);
  55. }
  56. if (writeData.Status < 0) {
  57. std::cout << "Status is " << uv_strerror(writeData.Status)
  58. << ", should be 0" << std::endl;
  59. return false;
  60. }
  61. return true;
  62. }
  63. static bool writeDataToStreamProcess(uv_loop_t& loop,
  64. cm::uv_pipe_ptr& inputPipe,
  65. char* outputData,
  66. unsigned int /* unused */,
  67. const char* cmakeCommand)
  68. {
  69. int err;
  70. inputPipe.init(loop, 0);
  71. std::vector<std::string> arguments = { cmakeCommand, "-E", "echo_append",
  72. outputData };
  73. std::vector<const char*> processArgs;
  74. for (auto const& arg : arguments) {
  75. processArgs.push_back(arg.c_str());
  76. }
  77. processArgs.push_back(nullptr);
  78. std::vector<uv_stdio_container_t> stdio(3);
  79. stdio[0].flags = UV_IGNORE;
  80. stdio[0].data.stream = nullptr;
  81. stdio[1].flags =
  82. static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE);
  83. stdio[1].data.stream = inputPipe;
  84. stdio[2].flags = UV_IGNORE;
  85. stdio[2].data.stream = nullptr;
  86. struct ProcessExitData
  87. {
  88. bool Finished = false;
  89. int64_t ExitStatus;
  90. int TermSignal;
  91. } exitData;
  92. cm::uv_process_ptr process;
  93. auto options = uv_process_options_t();
  94. options.file = cmakeCommand;
  95. options.args = const_cast<char**>(processArgs.data());
  96. options.flags = UV_PROCESS_WINDOWS_HIDE;
  97. options.stdio = stdio.data();
  98. options.stdio_count = static_cast<int>(stdio.size());
  99. options.exit_cb = [](uv_process_t* handle, int64_t exitStatus,
  100. int termSignal) {
  101. auto data = static_cast<ProcessExitData*>(handle->data);
  102. data->Finished = true;
  103. data->ExitStatus = exitStatus;
  104. data->TermSignal = termSignal;
  105. };
  106. if ((err = process.spawn(loop, options, &exitData)) < 0) {
  107. std::cout << "Could not spawn process: " << uv_strerror(err) << std::endl;
  108. return false;
  109. }
  110. while (!exitData.Finished) {
  111. uv_run(&loop, UV_RUN_ONCE);
  112. }
  113. if (exitData.ExitStatus != 0) {
  114. std::cout << "Process exit status is " << exitData.ExitStatus
  115. << ", should be 0" << std::endl;
  116. return false;
  117. }
  118. if (exitData.TermSignal != 0) {
  119. std::cout << "Process term signal is " << exitData.TermSignal
  120. << ", should be 0" << std::endl;
  121. return false;
  122. }
  123. return true;
  124. }
  125. static bool testUVStreambufRead(
  126. bool (*cb)(uv_loop_t& loop, cm::uv_pipe_ptr& inputPipe, char* outputData,
  127. unsigned int outputDataLength, const char* cmakeCommand),
  128. const char* cmakeCommand)
  129. {
  130. char outputData[] = TEST_STR;
  131. bool success = false;
  132. cm::uv_loop_ptr loop;
  133. loop.init();
  134. cm::uv_pipe_ptr inputPipe;
  135. std::vector<char> inputData(128);
  136. std::streamsize readLen;
  137. std::string line;
  138. cm::uv_timer_ptr timer;
  139. // Create the streambuf
  140. cmUVStreambuf inputBuf(64);
  141. std::istream inputStream(&inputBuf);
  142. if (inputBuf.is_open()) {
  143. std::cout << "is_open() is true, should be false" << std::endl;
  144. goto end;
  145. }
  146. if ((readLen = inputBuf.in_avail()) != -1) {
  147. std::cout << "in_avail() returned " << readLen << ", should be -1"
  148. << std::endl;
  149. goto end;
  150. }
  151. if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) {
  152. std::cout << "sgetn() returned " << readLen << ", should be 0"
  153. << std::endl;
  154. goto end;
  155. }
  156. // Perform first read test - read all the data
  157. if (!cb(*loop, inputPipe, outputData, 128, cmakeCommand)) {
  158. goto end;
  159. }
  160. inputBuf.open(inputPipe);
  161. if (!inputBuf.is_open()) {
  162. std::cout << "is_open() is false, should be true" << std::endl;
  163. goto end;
  164. }
  165. if ((readLen = inputBuf.in_avail()) != 0) {
  166. std::cout << "in_avail() returned " << readLen << ", should be 0"
  167. << std::endl;
  168. goto end;
  169. }
  170. if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 128) {
  171. std::cout << "sgetn() returned " << readLen << ", should be 128"
  172. << std::endl;
  173. goto end;
  174. }
  175. if ((readLen = inputBuf.in_avail()) != 0) {
  176. std::cout << "in_avail() returned " << readLen << ", should be 0"
  177. << std::endl;
  178. goto end;
  179. }
  180. if (std::memcmp(inputData.data(), outputData, 128)) {
  181. std::cout << "Read data does not match write data" << std::endl;
  182. goto end;
  183. }
  184. if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) {
  185. std::cout << "sgetn() returned " << readLen << ", should be 0"
  186. << std::endl;
  187. goto end;
  188. }
  189. if ((readLen = inputBuf.in_avail()) != -1) {
  190. std::cout << "in_avail() returned " << readLen << ", should be -1"
  191. << std::endl;
  192. goto end;
  193. }
  194. inputData.assign(128, char{});
  195. inputBuf.close();
  196. if (inputBuf.is_open()) {
  197. std::cout << "is_open() is true, should be false" << std::endl;
  198. goto end;
  199. }
  200. if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) {
  201. std::cout << "sgetn() returned " << readLen << ", should be 0"
  202. << std::endl;
  203. goto end;
  204. }
  205. if ((readLen = inputBuf.in_avail()) != -1) {
  206. std::cout << "in_avail() returned " << readLen << ", should be -1"
  207. << std::endl;
  208. goto end;
  209. }
  210. // Perform second read test - read some data and then close
  211. if (!cb(*loop, inputPipe, outputData, 128, cmakeCommand)) {
  212. goto end;
  213. }
  214. inputBuf.open(inputPipe);
  215. if (!inputBuf.is_open()) {
  216. std::cout << "is_open() is false, should be true" << std::endl;
  217. goto end;
  218. }
  219. if ((readLen = inputBuf.in_avail()) != 0) {
  220. std::cout << "in_avail() returned " << readLen << ", should be 0"
  221. << std::endl;
  222. goto end;
  223. }
  224. if ((readLen = inputBuf.sgetn(inputData.data(), 64)) != 64) {
  225. std::cout << "sgetn() returned " << readLen << ", should be 64"
  226. << std::endl;
  227. goto end;
  228. }
  229. if (std::memcmp(inputData.data(), outputData, 64)) {
  230. std::cout << "Read data does not match write data" << std::endl;
  231. goto end;
  232. }
  233. if ((readLen = inputBuf.in_avail()) != 8) {
  234. std::cout << "in_avail() returned " << readLen << ", should be 8"
  235. << std::endl;
  236. goto end;
  237. }
  238. inputData.assign(128, char{});
  239. inputBuf.close();
  240. if (inputBuf.is_open()) {
  241. std::cout << "is_open() is true, should be false" << std::endl;
  242. goto end;
  243. }
  244. if ((readLen = inputBuf.in_avail()) != -1) {
  245. std::cout << "in_avail() returned " << readLen << ", should be -1"
  246. << std::endl;
  247. goto end;
  248. }
  249. if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) {
  250. std::cout << "sgetn() returned " << readLen << ", should be 0"
  251. << std::endl;
  252. goto end;
  253. }
  254. // Perform third read test - read line by line
  255. if (!cb(*loop, inputPipe, outputData, 128, cmakeCommand)) {
  256. goto end;
  257. }
  258. inputBuf.open(inputPipe);
  259. if (!inputBuf.is_open()) {
  260. std::cout << "is_open() is false, should be true" << std::endl;
  261. goto end;
  262. }
  263. if ((readLen = inputBuf.in_avail()) != 0) {
  264. std::cout << "in_avail() returned " << readLen << ", should be 0"
  265. << std::endl;
  266. goto end;
  267. }
  268. if (!std::getline(inputStream, line)) {
  269. std::cout << "getline returned false, should be true" << std::endl;
  270. goto end;
  271. }
  272. if (line != TEST_STR_LINE_1) {
  273. std::cout << "Line 1 is \"" << line
  274. << "\", should be \"" TEST_STR_LINE_1 "\"" << std::endl;
  275. goto end;
  276. }
  277. if (!std::getline(inputStream, line)) {
  278. std::cout << "getline returned false, should be true" << std::endl;
  279. goto end;
  280. }
  281. if (line != TEST_STR_LINE_2) {
  282. std::cout << "Line 2 is \"" << line
  283. << "\", should be \"" TEST_STR_LINE_2 "\"" << std::endl;
  284. goto end;
  285. }
  286. if (!std::getline(inputStream, line)) {
  287. std::cout << "getline returned false, should be true" << std::endl;
  288. goto end;
  289. }
  290. if (line != TEST_STR_LINE_3) {
  291. std::cout << "Line 3 is \"" << line
  292. << "\", should be \"" TEST_STR_LINE_3 "\"" << std::endl;
  293. goto end;
  294. }
  295. if (std::getline(inputStream, line)) {
  296. std::cout << "getline returned true, should be false" << std::endl;
  297. goto end;
  298. }
  299. inputBuf.close();
  300. if (inputBuf.is_open()) {
  301. std::cout << "is_open() is true, should be false" << std::endl;
  302. goto end;
  303. }
  304. if ((readLen = inputBuf.in_avail()) != -1) {
  305. std::cout << "in_avail() returned " << readLen << ", should be -1"
  306. << std::endl;
  307. goto end;
  308. }
  309. if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) {
  310. std::cout << "sgetn() returned " << readLen << ", should be 0"
  311. << std::endl;
  312. goto end;
  313. }
  314. // Perform fourth read test - run the event loop outside of underflow()
  315. if (!cb(*loop, inputPipe, outputData, 128, cmakeCommand)) {
  316. goto end;
  317. }
  318. inputBuf.open(inputPipe);
  319. if (!inputBuf.is_open()) {
  320. std::cout << "is_open() is false, should be true" << std::endl;
  321. goto end;
  322. }
  323. if ((readLen = inputBuf.in_avail()) != 0) {
  324. std::cout << "in_avail() returned " << readLen << ", should be 0"
  325. << std::endl;
  326. goto end;
  327. }
  328. uv_run(loop, UV_RUN_DEFAULT);
  329. if ((readLen = inputBuf.in_avail()) != 72) {
  330. std::cout << "in_avail() returned " << readLen << ", should be 72"
  331. << std::endl;
  332. goto end;
  333. }
  334. if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 128) {
  335. std::cout << "sgetn() returned " << readLen << ", should be 128"
  336. << std::endl;
  337. goto end;
  338. }
  339. if ((readLen = inputBuf.in_avail()) != 0) {
  340. std::cout << "in_avail() returned " << readLen << ", should be 0"
  341. << std::endl;
  342. goto end;
  343. }
  344. if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) {
  345. std::cout << "sgetn() returned " << readLen << ", should be 128"
  346. << std::endl;
  347. goto end;
  348. }
  349. if ((readLen = inputBuf.in_avail()) != -1) {
  350. std::cout << "in_avail() returned " << readLen << ", should be -1"
  351. << std::endl;
  352. goto end;
  353. }
  354. inputBuf.close();
  355. if (inputBuf.is_open()) {
  356. std::cout << "is_open() is true, should be false" << std::endl;
  357. goto end;
  358. }
  359. if ((readLen = inputBuf.in_avail()) != -1) {
  360. std::cout << "in_avail() returned " << readLen << ", should be -1"
  361. << std::endl;
  362. goto end;
  363. }
  364. if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) {
  365. std::cout << "sgetn() returned " << readLen << ", should be 0"
  366. << std::endl;
  367. goto end;
  368. }
  369. // Perform fifth read test - close the streambuf in the middle of a read
  370. timer.init(*loop, &inputBuf);
  371. if (!cb(*loop, inputPipe, outputData, 128, cmakeCommand)) {
  372. goto end;
  373. }
  374. inputBuf.open(inputPipe);
  375. if (!inputBuf.is_open()) {
  376. std::cout << "is_open() is false, should be true" << std::endl;
  377. goto end;
  378. }
  379. if ((readLen = inputBuf.in_avail()) != 0) {
  380. std::cout << "in_avail() returned " << readLen << ", should be 0"
  381. << std::endl;
  382. goto end;
  383. }
  384. uv_timer_start(
  385. timer,
  386. [](uv_timer_t* handle) {
  387. auto buf = static_cast<cmUVStreambuf*>(handle->data);
  388. buf->close();
  389. },
  390. 0, 0);
  391. if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) {
  392. std::cout << "sgetn() returned " << readLen << ", should be 0"
  393. << std::endl;
  394. goto end;
  395. }
  396. if (inputBuf.is_open()) {
  397. std::cout << "is_open() is true, should be false" << std::endl;
  398. goto end;
  399. }
  400. if ((readLen = inputBuf.in_avail()) != -1) {
  401. std::cout << "in_avail() returned " << readLen << ", should be -1"
  402. << std::endl;
  403. goto end;
  404. }
  405. if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) {
  406. std::cout << "sgetn() returned " << readLen << ", should be 0"
  407. << std::endl;
  408. goto end;
  409. }
  410. success = true;
  411. end:
  412. return success;
  413. }
  414. bool testUVPipeIStream()
  415. {
  416. int pipe[] = { -1, -1 };
  417. if (cmGetPipes(pipe) < 0) {
  418. std::cout << "cmGetPipes() returned an error" << std::endl;
  419. return false;
  420. }
  421. cm::uv_loop_ptr loop;
  422. loop.init();
  423. cm::uv_pipe_ptr pipeSink;
  424. pipeSink.init(*loop, 0);
  425. uv_pipe_open(pipeSink, pipe[1]);
  426. std::string str = "Hello world!\n";
  427. uv_write_t writeReq;
  428. uv_buf_t buf;
  429. buf.base = &str.front();
  430. buf.len = str.length();
  431. uv_write(&writeReq, pipeSink, &buf, 1, nullptr);
  432. uv_run(loop, UV_RUN_DEFAULT);
  433. cmUVPipeIStream pin(*loop, pipe[0]);
  434. std::string line;
  435. std::getline(pin, line);
  436. if (line != "Hello world!") {
  437. std::cout << "Line was \"" << line << "\", should be \"Hello world!\""
  438. << std::endl;
  439. return false;
  440. }
  441. return true;
  442. }
  443. bool testUVStreamRead()
  444. {
  445. int pipe[] = { -1, -1 };
  446. if (cmGetPipes(pipe) < 0) {
  447. std::cout << "cmGetPipes() returned an error" << std::endl;
  448. return false;
  449. }
  450. cm::uv_loop_ptr loop;
  451. loop.init();
  452. cm::uv_pipe_ptr pipeSink;
  453. pipeSink.init(*loop, 0);
  454. uv_pipe_open(pipeSink, pipe[1]);
  455. std::string str = "Hello world!";
  456. uv_write_t writeReq;
  457. uv_buf_t buf;
  458. buf.base = &str.front();
  459. buf.len = str.length();
  460. uv_write(&writeReq, pipeSink, &buf, 1, nullptr);
  461. uv_run(loop, UV_RUN_DEFAULT);
  462. pipeSink.reset();
  463. cm::uv_pipe_ptr pipeSource;
  464. pipeSource.init(*loop, 0);
  465. uv_pipe_open(pipeSource, pipe[0]);
  466. std::string output;
  467. bool finished = false;
  468. cmUVStreamRead(
  469. pipeSource,
  470. [&output](std::vector<char> data) { cm::append(output, data); },
  471. [&output, &finished]() {
  472. if (output != "Hello world!") {
  473. std::cout << "Output was \"" << output
  474. << "\", should be \"Hello world!\"" << std::endl;
  475. return;
  476. }
  477. finished = true;
  478. });
  479. uv_run(loop, UV_RUN_DEFAULT);
  480. if (!finished) {
  481. std::cout << "finished was not set" << std::endl;
  482. return false;
  483. }
  484. return true;
  485. }
  486. int testUVStreambuf(int argc, char** const argv)
  487. {
  488. if (argc < 2) {
  489. std::cout << "Invalid arguments.\n";
  490. return -1;
  491. }
  492. if (!testUVStreambufRead(writeDataToStreamPipe, argv[1])) {
  493. std::cout << "While executing testUVStreambufRead() with pipe.\n";
  494. return -1;
  495. }
  496. if (!testUVStreambufRead(writeDataToStreamProcess, argv[1])) {
  497. std::cout << "While executing testUVStreambufRead() with process.\n";
  498. return -1;
  499. }
  500. if (!testUVPipeIStream()) {
  501. std::cout << "While executing testUVPipeIStream().\n";
  502. return -1;
  503. }
  504. if (!testUVStreamRead()) {
  505. std::cout << "While executing testUVPipeIStream().\n";
  506. return -1;
  507. }
  508. return 0;
  509. }