testUVStreambuf.cxx 14 KB

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