testUVStreambuf.cxx 14 KB

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