| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457 |
- #include <cstring>
- #include <iostream>
- #include <string>
- #include <vector>
- #include <cm3p/uv.h>
- #include <stdint.h>
- #include "cmGetPipes.h"
- #include "cmUVHandlePtr.h"
- #include "cmUVStreambuf.h"
- #define TEST_STR_LINE_1 "This string must be exactly 128 characters long so"
- #define TEST_STR_LINE_2 "that we can test CMake's std::streambuf integration"
- #define TEST_STR_LINE_3 "with libuv's uv_stream_t."
- #define TEST_STR TEST_STR_LINE_1 "\n" TEST_STR_LINE_2 "\n" TEST_STR_LINE_3
- static bool writeDataToStreamPipe(uv_loop_t& loop, cm::uv_pipe_ptr& inputPipe,
- char* outputData,
- unsigned int outputDataLength,
- const char* /* unused */)
- {
- int err;
- // Create the pipe
- int pipeHandles[2];
- if (cmGetPipes(pipeHandles) < 0) {
- std::cout << "Could not open pipe" << std::endl;
- return false;
- }
- cm::uv_pipe_ptr outputPipe;
- inputPipe.init(loop, 0);
- outputPipe.init(loop, 0);
- uv_pipe_open(inputPipe, pipeHandles[0]);
- uv_pipe_open(outputPipe, pipeHandles[1]);
- // Write data for reading
- uv_write_t writeReq;
- struct WriteCallbackData
- {
- bool Finished = false;
- int Status;
- } writeData;
- writeReq.data = &writeData;
- uv_buf_t outputBuf;
- outputBuf.base = outputData;
- outputBuf.len = outputDataLength;
- if ((err = uv_write(&writeReq, outputPipe, &outputBuf, 1,
- [](uv_write_t* req, int status) {
- auto data = static_cast<WriteCallbackData*>(req->data);
- data->Finished = true;
- data->Status = status;
- })) < 0) {
- std::cout << "Could not write to pipe: " << uv_strerror(err) << std::endl;
- return false;
- }
- while (!writeData.Finished) {
- uv_run(&loop, UV_RUN_ONCE);
- }
- if (writeData.Status < 0) {
- std::cout << "Status is " << uv_strerror(writeData.Status)
- << ", should be 0" << std::endl;
- return false;
- }
- return true;
- }
- static bool writeDataToStreamProcess(uv_loop_t& loop,
- cm::uv_pipe_ptr& inputPipe,
- char* outputData,
- unsigned int /* unused */,
- const char* cmakeCommand)
- {
- int err;
- inputPipe.init(loop, 0);
- std::vector<std::string> arguments = { cmakeCommand, "-E", "echo_append",
- outputData };
- std::vector<const char*> processArgs;
- for (auto const& arg : arguments) {
- processArgs.push_back(arg.c_str());
- }
- processArgs.push_back(nullptr);
- std::vector<uv_stdio_container_t> stdio(3);
- stdio[0].flags = UV_IGNORE;
- stdio[0].data.stream = nullptr;
- stdio[1].flags =
- static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE);
- stdio[1].data.stream = inputPipe;
- stdio[2].flags = UV_IGNORE;
- stdio[2].data.stream = nullptr;
- struct ProcessExitData
- {
- bool Finished = false;
- int64_t ExitStatus;
- int TermSignal;
- } exitData;
- cm::uv_process_ptr process;
- auto options = uv_process_options_t();
- options.file = cmakeCommand;
- options.args = const_cast<char**>(processArgs.data());
- options.flags = UV_PROCESS_WINDOWS_HIDE;
- options.stdio = stdio.data();
- options.stdio_count = static_cast<int>(stdio.size());
- options.exit_cb = [](uv_process_t* handle, int64_t exitStatus,
- int termSignal) {
- auto data = static_cast<ProcessExitData*>(handle->data);
- data->Finished = true;
- data->ExitStatus = exitStatus;
- data->TermSignal = termSignal;
- };
- if ((err = process.spawn(loop, options, &exitData)) < 0) {
- std::cout << "Could not spawn process: " << uv_strerror(err) << std::endl;
- return false;
- }
- while (!exitData.Finished) {
- uv_run(&loop, UV_RUN_ONCE);
- }
- if (exitData.ExitStatus != 0) {
- std::cout << "Process exit status is " << exitData.ExitStatus
- << ", should be 0" << std::endl;
- return false;
- }
- if (exitData.TermSignal != 0) {
- std::cout << "Process term signal is " << exitData.TermSignal
- << ", should be 0" << std::endl;
- return false;
- }
- return true;
- }
- static bool testUVStreambufRead(
- bool (*cb)(uv_loop_t& loop, cm::uv_pipe_ptr& inputPipe, char* outputData,
- unsigned int outputDataLength, const char* cmakeCommand),
- const char* cmakeCommand)
- {
- char outputData[] = TEST_STR;
- bool success = false;
- cm::uv_loop_ptr loop;
- loop.init();
- cm::uv_pipe_ptr inputPipe;
- std::vector<char> inputData(128);
- std::streamsize readLen;
- std::string line;
- cm::uv_timer_ptr timer;
- // Create the streambuf
- cmUVStreambuf inputBuf(64);
- std::istream inputStream(&inputBuf);
- if (inputBuf.is_open()) {
- std::cout << "is_open() is true, should be false" << std::endl;
- goto end;
- }
- if ((readLen = inputBuf.in_avail()) != -1) {
- std::cout << "in_avail() returned " << readLen << ", should be -1"
- << std::endl;
- goto end;
- }
- if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) {
- std::cout << "sgetn() returned " << readLen << ", should be 0"
- << std::endl;
- goto end;
- }
- // Perform first read test - read all the data
- if (!cb(*loop, inputPipe, outputData, 128, cmakeCommand)) {
- goto end;
- }
- inputBuf.open(inputPipe);
- if (!inputBuf.is_open()) {
- std::cout << "is_open() is false, should be true" << std::endl;
- goto end;
- }
- if ((readLen = inputBuf.in_avail()) != 0) {
- std::cout << "in_avail() returned " << readLen << ", should be 0"
- << std::endl;
- goto end;
- }
- if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 128) {
- std::cout << "sgetn() returned " << readLen << ", should be 128"
- << std::endl;
- goto end;
- }
- if ((readLen = inputBuf.in_avail()) != 0) {
- std::cout << "in_avail() returned " << readLen << ", should be 0"
- << std::endl;
- goto end;
- }
- if (std::memcmp(inputData.data(), outputData, 128)) {
- std::cout << "Read data does not match write data" << std::endl;
- goto end;
- }
- if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) {
- std::cout << "sgetn() returned " << readLen << ", should be 0"
- << std::endl;
- goto end;
- }
- if ((readLen = inputBuf.in_avail()) != -1) {
- std::cout << "in_avail() returned " << readLen << ", should be -1"
- << std::endl;
- goto end;
- }
- inputData.assign(128, char{});
- inputBuf.close();
- if (inputBuf.is_open()) {
- std::cout << "is_open() is true, should be false" << std::endl;
- goto end;
- }
- if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) {
- std::cout << "sgetn() returned " << readLen << ", should be 0"
- << std::endl;
- goto end;
- }
- if ((readLen = inputBuf.in_avail()) != -1) {
- std::cout << "in_avail() returned " << readLen << ", should be -1"
- << std::endl;
- goto end;
- }
- // Perform second read test - read some data and then close
- if (!cb(*loop, inputPipe, outputData, 128, cmakeCommand)) {
- goto end;
- }
- inputBuf.open(inputPipe);
- if (!inputBuf.is_open()) {
- std::cout << "is_open() is false, should be true" << std::endl;
- goto end;
- }
- if ((readLen = inputBuf.in_avail()) != 0) {
- std::cout << "in_avail() returned " << readLen << ", should be 0"
- << std::endl;
- goto end;
- }
- if ((readLen = inputBuf.sgetn(inputData.data(), 64)) != 64) {
- std::cout << "sgetn() returned " << readLen << ", should be 64"
- << std::endl;
- goto end;
- }
- if (std::memcmp(inputData.data(), outputData, 64)) {
- std::cout << "Read data does not match write data" << std::endl;
- goto end;
- }
- if ((readLen = inputBuf.in_avail()) != 8) {
- std::cout << "in_avail() returned " << readLen << ", should be 8"
- << std::endl;
- goto end;
- }
- inputData.assign(128, char{});
- inputBuf.close();
- if (inputBuf.is_open()) {
- std::cout << "is_open() is true, should be false" << std::endl;
- goto end;
- }
- if ((readLen = inputBuf.in_avail()) != -1) {
- std::cout << "in_avail() returned " << readLen << ", should be -1"
- << std::endl;
- goto end;
- }
- if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) {
- std::cout << "sgetn() returned " << readLen << ", should be 0"
- << std::endl;
- goto end;
- }
- // Perform third read test - read line by line
- if (!cb(*loop, inputPipe, outputData, 128, cmakeCommand)) {
- goto end;
- }
- inputBuf.open(inputPipe);
- if (!inputBuf.is_open()) {
- std::cout << "is_open() is false, should be true" << std::endl;
- goto end;
- }
- if ((readLen = inputBuf.in_avail()) != 0) {
- std::cout << "in_avail() returned " << readLen << ", should be 0"
- << std::endl;
- goto end;
- }
- if (!std::getline(inputStream, line)) {
- std::cout << "getline returned false, should be true" << std::endl;
- goto end;
- }
- if (line != TEST_STR_LINE_1) {
- std::cout << "Line 1 is \"" << line
- << "\", should be \"" TEST_STR_LINE_1 "\"" << std::endl;
- goto end;
- }
- if (!std::getline(inputStream, line)) {
- std::cout << "getline returned false, should be true" << std::endl;
- goto end;
- }
- if (line != TEST_STR_LINE_2) {
- std::cout << "Line 2 is \"" << line
- << "\", should be \"" TEST_STR_LINE_2 "\"" << std::endl;
- goto end;
- }
- if (!std::getline(inputStream, line)) {
- std::cout << "getline returned false, should be true" << std::endl;
- goto end;
- }
- if (line != TEST_STR_LINE_3) {
- std::cout << "Line 3 is \"" << line
- << "\", should be \"" TEST_STR_LINE_3 "\"" << std::endl;
- goto end;
- }
- if (std::getline(inputStream, line)) {
- std::cout << "getline returned true, should be false" << std::endl;
- goto end;
- }
- inputBuf.close();
- if (inputBuf.is_open()) {
- std::cout << "is_open() is true, should be false" << std::endl;
- goto end;
- }
- if ((readLen = inputBuf.in_avail()) != -1) {
- std::cout << "in_avail() returned " << readLen << ", should be -1"
- << std::endl;
- goto end;
- }
- if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) {
- std::cout << "sgetn() returned " << readLen << ", should be 0"
- << std::endl;
- goto end;
- }
- // Perform fourth read test - run the event loop outside of underflow()
- if (!cb(*loop, inputPipe, outputData, 128, cmakeCommand)) {
- goto end;
- }
- inputBuf.open(inputPipe);
- if (!inputBuf.is_open()) {
- std::cout << "is_open() is false, should be true" << std::endl;
- goto end;
- }
- if ((readLen = inputBuf.in_avail()) != 0) {
- std::cout << "in_avail() returned " << readLen << ", should be 0"
- << std::endl;
- goto end;
- }
- uv_run(loop, UV_RUN_DEFAULT);
- if ((readLen = inputBuf.in_avail()) != 72) {
- std::cout << "in_avail() returned " << readLen << ", should be 72"
- << std::endl;
- goto end;
- }
- if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 128) {
- std::cout << "sgetn() returned " << readLen << ", should be 128"
- << std::endl;
- goto end;
- }
- if ((readLen = inputBuf.in_avail()) != 0) {
- std::cout << "in_avail() returned " << readLen << ", should be 0"
- << std::endl;
- goto end;
- }
- if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) {
- std::cout << "sgetn() returned " << readLen << ", should be 128"
- << std::endl;
- goto end;
- }
- if ((readLen = inputBuf.in_avail()) != -1) {
- std::cout << "in_avail() returned " << readLen << ", should be -1"
- << std::endl;
- goto end;
- }
- inputBuf.close();
- if (inputBuf.is_open()) {
- std::cout << "is_open() is true, should be false" << std::endl;
- goto end;
- }
- if ((readLen = inputBuf.in_avail()) != -1) {
- std::cout << "in_avail() returned " << readLen << ", should be -1"
- << std::endl;
- goto end;
- }
- if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) {
- std::cout << "sgetn() returned " << readLen << ", should be 0"
- << std::endl;
- goto end;
- }
- // Perform fifth read test - close the streambuf in the middle of a read
- timer.init(*loop, &inputBuf);
- if (!cb(*loop, inputPipe, outputData, 128, cmakeCommand)) {
- goto end;
- }
- inputBuf.open(inputPipe);
- if (!inputBuf.is_open()) {
- std::cout << "is_open() is false, should be true" << std::endl;
- goto end;
- }
- if ((readLen = inputBuf.in_avail()) != 0) {
- std::cout << "in_avail() returned " << readLen << ", should be 0"
- << std::endl;
- goto end;
- }
- uv_timer_start(timer,
- [](uv_timer_t* handle) {
- auto buf = static_cast<cmUVStreambuf*>(handle->data);
- buf->close();
- },
- 0, 0);
- if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) {
- std::cout << "sgetn() returned " << readLen << ", should be 0"
- << std::endl;
- goto end;
- }
- if (inputBuf.is_open()) {
- std::cout << "is_open() is true, should be false" << std::endl;
- goto end;
- }
- if ((readLen = inputBuf.in_avail()) != -1) {
- std::cout << "in_avail() returned " << readLen << ", should be -1"
- << std::endl;
- goto end;
- }
- if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) {
- std::cout << "sgetn() returned " << readLen << ", should be 0"
- << std::endl;
- goto end;
- }
- success = true;
- end:
- return success;
- }
- int testUVStreambuf(int argc, char** const argv)
- {
- if (argc < 2) {
- std::cout << "Invalid arguments.\n";
- return -1;
- }
- if (!testUVStreambufRead(writeDataToStreamPipe, argv[1])) {
- std::cout << "While executing testUVStreambufRead() with pipe.\n";
- return -1;
- }
- if (!testUVStreambufRead(writeDataToStreamProcess, argv[1])) {
- std::cout << "While executing testUVStreambufRead() with process.\n";
- return -1;
- }
- return 0;
- }
|