summaryrefslogtreecommitdiffstats
path: root/Tests/CMakeLib
diff options
context:
space:
mode:
authorKyle Edwards <kyle.edwards@kitware.com>2019-04-16 21:58:02 (GMT)
committerKyle Edwards <kyle.edwards@kitware.com>2019-04-25 16:14:00 (GMT)
commitc74698cb75b9923517f7f87eacf04ca0eefc8e72 (patch)
tree890d752d404fe359c616ccf8bfa33e426fe46327 /Tests/CMakeLib
parent8cfd25db711c22f9478e565a496145577df24d77 (diff)
downloadCMake-c74698cb75b9923517f7f87eacf04ca0eefc8e72.zip
CMake-c74698cb75b9923517f7f87eacf04ca0eefc8e72.tar.gz
CMake-c74698cb75b9923517f7f87eacf04ca0eefc8e72.tar.bz2
cmUVStreambuf: Add std::streambuf implementation for uv_stream_t
This will allow std::istream/std::ostream-based interaction with processes spawned by libuv.
Diffstat (limited to 'Tests/CMakeLib')
-rw-r--r--Tests/CMakeLib/CMakeLists.txt4
-rw-r--r--Tests/CMakeLib/testUVStreambuf.cxx457
2 files changed, 460 insertions, 1 deletions
diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt
index 031ab01..e04bba2 100644
--- a/Tests/CMakeLib/CMakeLists.txt
+++ b/Tests/CMakeLib/CMakeLists.txt
@@ -16,9 +16,11 @@ set(CMakeLib_TESTS
testXMLSafe.cxx
testFindPackageCommand.cxx
testUVRAII.cxx
+ testUVStreambuf.cxx
)
set(testRST_ARGS ${CMAKE_CURRENT_SOURCE_DIR})
+set(testUVStreambuf_ARGS $<TARGET_FILE:cmake>)
if(WIN32)
list(APPEND CMakeLib_TESTS
@@ -43,7 +45,7 @@ target_link_libraries(testEncoding cmsys)
foreach(testfile ${CMakeLib_TESTS})
get_filename_component(test "${testfile}" NAME_WE)
- add_test(CMakeLib.${test} CMakeLibTests ${test} ${${test}_ARGS})
+ add_test(NAME CMakeLib.${test} COMMAND CMakeLibTests ${test} ${${test}_ARGS})
endforeach()
if(TEST_CompileCommandOutput)
diff --git a/Tests/CMakeLib/testUVStreambuf.cxx b/Tests/CMakeLib/testUVStreambuf.cxx
new file mode 100644
index 0000000..39655f3
--- /dev/null
+++ b/Tests/CMakeLib/testUVStreambuf.cxx
@@ -0,0 +1,457 @@
+#include "cmUVStreambuf.h"
+
+#include "cmGetPipes.h"
+#include "cmUVHandlePtr.h"
+
+#include "cm_uv.h"
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <cstring>
+
+#include <stdint.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
+
+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;
+}
+
+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;
+}
+
+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;
+}