summaryrefslogtreecommitdiffstats
path: root/Tests/CMakeLib
diff options
context:
space:
mode:
Diffstat (limited to 'Tests/CMakeLib')
-rw-r--r--Tests/CMakeLib/CMakeLists.txt7
-rw-r--r--Tests/CMakeLib/testUVProcessChain.cxx345
-rw-r--r--Tests/CMakeLib/testUVProcessChainHelper.cxx4
-rw-r--r--Tests/CMakeLib/testUVStreambuf.cxx97
4 files changed, 368 insertions, 85 deletions
diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt
index 5c14de2..8ce8f67 100644
--- a/Tests/CMakeLib/CMakeLists.txt
+++ b/Tests/CMakeLib/CMakeLists.txt
@@ -63,10 +63,15 @@ if(WIN32)
endif()
configure_file(testXMLParser.h.in testXMLParser.h @ONLY)
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/testUVProcessChainInput.txt" "HELLO WORLD!")
create_test_sourcelist(CMakeLib_TEST_SRCS CMakeLibTests.cxx ${CMakeLib_TESTS})
add_executable(CMakeLibTests ${CMakeLib_TEST_SRCS})
-target_link_libraries(CMakeLibTests CMakeLib CTestLib)
+target_link_libraries(CMakeLibTests PRIVATE CTestLib CMakeLib)
+if(CMake_BUILD_PCH)
+ target_precompile_headers(CMakeLibTests PRIVATE "<iostream>" "<cm3p/uv.h>")
+ target_compile_definitions(CMakeLibTests PRIVATE "NOMINMAX")
+endif()
set_property(TARGET CMakeLibTests PROPERTY C_CLANG_TIDY "")
set_property(TARGET CMakeLibTests PROPERTY CXX_CLANG_TIDY "")
diff --git a/Tests/CMakeLib/testUVProcessChain.cxx b/Tests/CMakeLib/testUVProcessChain.cxx
index 7027689..7630aa0 100644
--- a/Tests/CMakeLib/testUVProcessChain.cxx
+++ b/Tests/CMakeLib/testUVProcessChain.cxx
@@ -1,5 +1,6 @@
#include <algorithm>
#include <csignal>
+#include <cstdio>
#include <functional>
#include <iostream>
#include <sstream>
@@ -16,11 +17,11 @@
#include "cmStringAlgorithms.h"
#include "cmUVHandlePtr.h"
#include "cmUVProcessChain.h"
+#include "cmUVStream.h"
#include "cmUVStreambuf.h"
struct ExpectedStatus
{
- bool Finished;
bool MatchExitStatus;
bool MatchTermSignal;
cmUVProcessChain::Status Status;
@@ -28,38 +29,6 @@ struct ExpectedStatus
std::string ExceptionString;
};
-static const std::vector<ExpectedStatus> status1 = {
- { false, false, false, { 0, 0 }, cmUVProcessChain::ExceptionCode::None, "" },
- { false, false, false, { 0, 0 }, cmUVProcessChain::ExceptionCode::None, "" },
- { false, false, false, { 0, 0 }, cmUVProcessChain::ExceptionCode::None, "" },
-};
-
-static const std::vector<ExpectedStatus> status2 = {
- { true, true, true, { 0, 0 }, cmUVProcessChain::ExceptionCode::None, "" },
- { false, false, false, { 0, 0 }, cmUVProcessChain::ExceptionCode::None, "" },
- { false, false, false, { 0, 0 }, cmUVProcessChain::ExceptionCode::None, "" },
-};
-
-static const std::vector<ExpectedStatus> status3 = {
- { true, true, true, { 0, 0 }, cmUVProcessChain::ExceptionCode::None, "" },
- { true, true, true, { 1, 0 }, cmUVProcessChain::ExceptionCode::None, "" },
-#ifdef _WIN32
- { true,
- true,
- true,
- { STATUS_ACCESS_VIOLATION, 0 },
- cmUVProcessChain::ExceptionCode::Fault,
- "Access violation" },
-#else
- { true,
- false,
- true,
- { 0, SIGABRT },
- cmUVProcessChain::ExceptionCode::Other,
- "Subprocess aborted" },
-#endif
-};
-
static const char* ExceptionCodeToString(cmUVProcessChain::ExceptionCode code)
{
switch (code) {
@@ -73,6 +42,8 @@ static const char* ExceptionCodeToString(cmUVProcessChain::ExceptionCode code)
return "Interrupt";
case cmUVProcessChain::ExceptionCode::Numerical:
return "Numerical";
+ case cmUVProcessChain::ExceptionCode::Spawn:
+ return "Spawn";
case cmUVProcessChain::ExceptionCode::Other:
return "Other";
default:
@@ -83,9 +54,10 @@ static const char* ExceptionCodeToString(cmUVProcessChain::ExceptionCode code)
bool operator==(const cmUVProcessChain::Status* actual,
const ExpectedStatus& expected)
{
- if (!expected.Finished) {
- return !actual;
- } else if (!actual) {
+ if (expected.Status.SpawnResult != actual->SpawnResult) {
+ return false;
+ }
+ if (expected.Status.Finished != actual->Finished) {
return false;
}
if (expected.MatchExitStatus &&
@@ -96,7 +68,7 @@ bool operator==(const cmUVProcessChain::Status* actual,
expected.Status.TermSignal != actual->TermSignal) {
return false;
}
- if (expected.Finished &&
+ if (expected.Status.Finished &&
std::make_pair(expected.ExceptionCode, expected.ExceptionString) !=
actual->GetException()) {
return false;
@@ -150,39 +122,96 @@ static void printResults(
{
std::cout << "Expected: " << std::endl;
for (auto const& e : expected) {
- if (e.Finished) {
- std::cout << " ExitStatus: "
- << printExpected(e.MatchExitStatus, e.Status.ExitStatus)
- << ", TermSignal: "
- << printExpected(e.MatchTermSignal, e.Status.TermSignal)
- << ", ExceptionCode: "
- << printExpected(e.Finished,
- ExceptionCodeToString(e.ExceptionCode))
- << ", ExceptionString: \""
- << printExpected(e.Finished, e.ExceptionString) << '"'
- << std::endl;
- } else {
- std::cout << " null" << std::endl;
- }
+ std::cout << " SpawnResult: " << e.Status.SpawnResult
+ << ", Finished: " << e.Status.Finished << ", ExitStatus: "
+ << printExpected(e.MatchExitStatus, e.Status.ExitStatus)
+ << ", TermSignal: "
+ << printExpected(e.MatchTermSignal, e.Status.TermSignal)
+ << ", ExceptionCode: "
+ << printExpected(e.Status.Finished,
+ ExceptionCodeToString(e.ExceptionCode))
+ << ", ExceptionString: \""
+ << printExpected(e.Status.Finished, e.ExceptionString) << '"'
+ << std::endl;
}
std::cout << "Actual:" << std::endl;
for (auto const& a : actual) {
- if (a) {
- auto exception = a->GetException();
- std::cout << " ExitStatus: " << a->ExitStatus
- << ", TermSignal: " << a->TermSignal << ", ExceptionCode: "
- << ExceptionCodeToString(exception.first)
- << ", ExceptionString: \"" << exception.second << '"'
- << std::endl;
- } else {
- std::cout << " null" << std::endl;
- }
+ auto exception = a->GetException();
+ std::cout << " SpawnResult: " << a->SpawnResult
+ << ", Finished: " << a->Finished
+ << ", ExitStatus: " << a->ExitStatus
+ << ", TermSignal: " << a->TermSignal
+ << ", ExceptionCode: " << ExceptionCodeToString(exception.first)
+ << ", ExceptionString: \"" << exception.second << '"'
+ << std::endl;
}
}
static bool checkExecution(cmUVProcessChainBuilder& builder,
std::unique_ptr<cmUVProcessChain>& chain)
{
+ static const std::vector<ExpectedStatus> status1 = {
+ { false,
+ false,
+ { 0, false, 0, 0 },
+ cmUVProcessChain::ExceptionCode::None,
+ "" },
+ { false,
+ false,
+ { 0, false, 0, 0 },
+ cmUVProcessChain::ExceptionCode::None,
+ "" },
+ { false,
+ false,
+ { 0, false, 0, 0 },
+ cmUVProcessChain::ExceptionCode::None,
+ "" },
+ };
+
+ static const std::vector<ExpectedStatus> status2 = {
+ { true,
+ true,
+ { 0, true, 0, 0 },
+ cmUVProcessChain::ExceptionCode::None,
+ "" },
+ { false,
+ false,
+ { 0, false, 0, 0 },
+ cmUVProcessChain::ExceptionCode::None,
+ "" },
+ { false,
+ false,
+ { 0, false, 0, 0 },
+ cmUVProcessChain::ExceptionCode::None,
+ "" },
+ };
+
+ static const std::vector<ExpectedStatus> status3 = {
+ { true,
+ true,
+ { 0, true, 0, 0 },
+ cmUVProcessChain::ExceptionCode::None,
+ "" },
+ { true,
+ true,
+ { 0, true, 1, 0 },
+ cmUVProcessChain::ExceptionCode::None,
+ "" },
+#ifdef _WIN32
+ { true,
+ true,
+ { 0, true, STATUS_ACCESS_VIOLATION, 0 },
+ cmUVProcessChain::ExceptionCode::Fault,
+ "Access violation" },
+#else
+ { false,
+ true,
+ { 0, true, 0, SIGABRT },
+ cmUVProcessChain::ExceptionCode::Other,
+ "Subprocess aborted" },
+#endif
+ };
+
std::vector<const cmUVProcessChain::Status*> status;
chain = cm::make_unique<cmUVProcessChain>(builder.Start());
@@ -201,7 +230,7 @@ static bool checkExecution(cmUVProcessChainBuilder& builder,
return false;
}
- if (chain->Wait(6000)) {
+ if (chain->Wait(9000)) {
std::cout << "Wait() returned true, should be false" << std::endl;
return false;
}
@@ -273,16 +302,19 @@ bool testUVProcessChainBuiltin(const char* helperCommand)
return false;
}
- if (!chain->OutputStream()) {
- std::cout << "OutputStream() was null, expecting not null" << std::endl;
+ if (chain->OutputStream() < 0) {
+ std::cout << "OutputStream() was invalid, expecting valid" << std::endl;
return false;
}
- if (!chain->ErrorStream()) {
- std::cout << "ErrorStream() was null, expecting not null" << std::endl;
+ if (chain->ErrorStream() < 0) {
+ std::cout << "ErrorStream() was invalid, expecting valid" << std::endl;
return false;
}
- if (!checkOutput(*chain->OutputStream(), *chain->ErrorStream())) {
+ cmUVPipeIStream output(chain->GetLoop(), chain->OutputStream());
+ cmUVPipeIStream error(chain->GetLoop(), chain->ErrorStream());
+
+ if (!checkOutput(output, error)) {
return false;
}
@@ -302,12 +334,12 @@ bool testUVProcessChainBuiltinMerged(const char* helperCommand)
return false;
}
- if (!chain->OutputStream()) {
- std::cout << "OutputStream() was null, expecting not null" << std::endl;
+ if (chain->OutputStream() < 0) {
+ std::cout << "OutputStream() was invalid, expecting valid" << std::endl;
return false;
}
- if (!chain->ErrorStream()) {
- std::cout << "ErrorStream() was null, expecting not null" << std::endl;
+ if (chain->ErrorStream() < 0) {
+ std::cout << "ErrorStream() was invalid, expecting valid" << std::endl;
return false;
}
if (chain->OutputStream() != chain->ErrorStream()) {
@@ -316,7 +348,9 @@ bool testUVProcessChainBuiltinMerged(const char* helperCommand)
return false;
}
- std::string merged = getInput(*chain->OutputStream());
+ cmUVPipeIStream mergedStream(chain->GetLoop(), chain->OutputStream());
+
+ std::string merged = getInput(mergedStream);
auto qemuErrorPos = merged.find("qemu:");
if (qemuErrorPos != std::string::npos) {
merged.resize(qemuErrorPos);
@@ -370,12 +404,12 @@ bool testUVProcessChainExternal(const char* helperCommand)
return false;
}
- if (chain->OutputStream()) {
- std::cout << "OutputStream() was not null, expecting null" << std::endl;
+ if (chain->OutputStream() >= 0) {
+ std::cout << "OutputStream() was valid, expecting invalid" << std::endl;
return false;
}
- if (chain->ErrorStream()) {
- std::cout << "ErrorStream() was not null, expecting null" << std::endl;
+ if (chain->ErrorStream() >= 0) {
+ std::cout << "ErrorStream() was valid, expecting invalid" << std::endl;
return false;
}
@@ -418,12 +452,12 @@ bool testUVProcessChainNone(const char* helperCommand)
return false;
}
- if (chain->OutputStream()) {
- std::cout << "OutputStream() was not null, expecting null" << std::endl;
+ if (chain->OutputStream() >= 0) {
+ std::cout << "OutputStream() was valid, expecting invalid" << std::endl;
return false;
}
- if (chain->ErrorStream()) {
- std::cout << "ErrorStream() was not null, expecting null" << std::endl;
+ if (chain->ErrorStream() >= 0) {
+ std::cout << "ErrorStream() was valid, expecting invalid" << std::endl;
return false;
}
@@ -445,7 +479,8 @@ bool testUVProcessChainCwdUnchanged(const char* helperCommand)
return false;
}
- auto cwd = getInput(*chain.OutputStream());
+ cmUVPipeIStream output(chain.GetLoop(), chain.OutputStream());
+ auto cwd = getInput(output);
if (!cmHasLiteralSuffix(cwd, "/Tests/CMakeLib")) {
std::cout << "Working directory was \"" << cwd
<< "\", expected to end in \"/Tests/CMakeLib\"" << std::endl;
@@ -471,7 +506,8 @@ bool testUVProcessChainCwdChanged(const char* helperCommand)
return false;
}
- auto cwd = getInput(*chain.OutputStream());
+ cmUVPipeIStream output(chain.GetLoop(), chain.OutputStream());
+ auto cwd = getInput(output);
if (!cmHasLiteralSuffix(cwd, "/Tests")) {
std::cout << "Working directory was \"" << cwd
<< "\", expected to end in \"/Tests\"" << std::endl;
@@ -481,6 +517,141 @@ bool testUVProcessChainCwdChanged(const char* helperCommand)
return true;
}
+bool testUVProcessChainSpawnFail(const char* helperCommand)
+{
+ static const std::vector<ExpectedStatus> status1 = {
+ { false,
+ false,
+ { 0, false, 0, 0 },
+ cmUVProcessChain::ExceptionCode::None,
+ "" },
+ { false,
+ false,
+ { UV_ENOENT, true, 0, 0 },
+ cmUVProcessChain::ExceptionCode::Spawn,
+ uv_strerror(UV_ENOENT) },
+#ifdef _WIN32
+ { true,
+ true,
+ { 0, true, STATUS_ACCESS_VIOLATION, 0 },
+ cmUVProcessChain::ExceptionCode::Fault,
+ "Access violation" },
+#else
+ { false,
+ true,
+ { 0, true, 0, SIGABRT },
+ cmUVProcessChain::ExceptionCode::Other,
+ "Subprocess aborted" },
+#endif
+ };
+
+ static const std::vector<ExpectedStatus> status2 = {
+#ifdef _WIN32
+ { true,
+ true,
+ { 0, true, 0, 0 },
+ cmUVProcessChain::ExceptionCode::None,
+ "" },
+#else
+ { false,
+ true,
+ { 0, true, 0, SIGPIPE },
+ cmUVProcessChain::ExceptionCode::Other,
+ "SIGPIPE" },
+#endif
+ { false,
+ false,
+ { UV_ENOENT, true, 0, 0 },
+ cmUVProcessChain::ExceptionCode::Spawn,
+ uv_strerror(UV_ENOENT) },
+#ifdef _WIN32
+ { true,
+ true,
+ { 0, true, STATUS_ACCESS_VIOLATION, 0 },
+ cmUVProcessChain::ExceptionCode::Fault,
+ "Access violation" },
+#else
+ { false,
+ true,
+ { 0, true, 0, SIGABRT },
+ cmUVProcessChain::ExceptionCode::Other,
+ "Subprocess aborted" },
+#endif
+ };
+
+ std::vector<const cmUVProcessChain::Status*> status;
+
+ cmUVProcessChainBuilder builder;
+ builder.AddCommand({ helperCommand, "echo" })
+ .AddCommand({ "this_command_is_for_cmake_and_should_never_exist" })
+ .AddCommand({ helperCommand, "dedup" })
+ .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
+ .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
+
+ auto chain = builder.Start();
+ if (!chain.Valid()) {
+ std::cout << "Valid() returned false, should be true" << std::endl;
+ return false;
+ }
+
+ // Some platforms, like Solaris 10, take a long time to report a trapped
+ // subprocess to the parent process (about 1.7 seconds in the case of
+ // Solaris 10.) Wait 3 seconds to give it enough time.
+ if (chain.Wait(3000)) {
+ std::cout << "Wait() did not time out" << std::endl;
+ return false;
+ }
+
+ status = chain.GetStatus();
+ if (!resultsMatch(status, status1)) {
+ std::cout << "GetStatus() did not produce expected output" << std::endl;
+ printResults(status, status1);
+ return false;
+ }
+
+ if (!chain.Wait()) {
+ std::cout << "Wait() timed out" << std::endl;
+ return false;
+ }
+
+ status = chain.GetStatus();
+ if (!resultsMatch(status, status2)) {
+ std::cout << "GetStatus() did not produce expected output" << std::endl;
+ printResults(status, status2);
+ return false;
+ }
+
+ return true;
+}
+
+bool testUVProcessChainInputFile(const char* helperCommand)
+{
+ std::unique_ptr<FILE, int (*)(FILE*)> f(
+ fopen("testUVProcessChainInput.txt", "rb"), fclose);
+
+ cmUVProcessChainBuilder builder;
+ builder.AddCommand({ helperCommand, "dedup" })
+ .SetExternalStream(cmUVProcessChainBuilder::Stream_INPUT, fileno(f.get()))
+ .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT);
+
+ auto chain = builder.Start();
+
+ if (!chain.Wait()) {
+ std::cout << "Wait() timed out" << std::endl;
+ return false;
+ }
+
+ cmUVPipeIStream stream(chain.GetLoop(), chain.OutputStream());
+ std::string output = getInput(stream);
+ if (output != "HELO WRD!") {
+ std::cout << "Output was \"" << output << "\", expected \"HELO WRD!\""
+ << std::endl;
+ return false;
+ }
+
+ return true;
+}
+
int testUVProcessChain(int argc, char** const argv)
{
if (argc < 2) {
@@ -518,5 +689,15 @@ int testUVProcessChain(int argc, char** const argv)
return -1;
}
+ if (!testUVProcessChainSpawnFail(argv[1])) {
+ std::cout << "While executing testUVProcessChainSpawnFail().\n";
+ return -1;
+ }
+
+ if (!testUVProcessChainInputFile(argv[1])) {
+ std::cout << "While executing testUVProcessChainInputFile().\n";
+ return -1;
+ }
+
return 0;
}
diff --git a/Tests/CMakeLib/testUVProcessChainHelper.cxx b/Tests/CMakeLib/testUVProcessChainHelper.cxx
index 99743e7..fcc45b0 100644
--- a/Tests/CMakeLib/testUVProcessChainHelper.cxx
+++ b/Tests/CMakeLib/testUVProcessChainHelper.cxx
@@ -32,13 +32,13 @@ int main(int argc, char** argv)
std::string command = argv[1];
if (command == "echo") {
- std::this_thread::sleep_for(std::chrono::milliseconds(3000));
+ std::this_thread::sleep_for(std::chrono::milliseconds(6000));
std::cout << "HELLO world!" << std::flush;
std::cerr << "1" << std::flush;
return 0;
}
if (command == "capitalize") {
- std::this_thread::sleep_for(std::chrono::milliseconds(9000));
+ std::this_thread::sleep_for(std::chrono::milliseconds(12000));
std::string input = getStdin();
for (auto& c : input) {
c = static_cast<char>(std::toupper(c));
diff --git a/Tests/CMakeLib/testUVStreambuf.cxx b/Tests/CMakeLib/testUVStreambuf.cxx
index f9ed6af..f3977d4 100644
--- a/Tests/CMakeLib/testUVStreambuf.cxx
+++ b/Tests/CMakeLib/testUVStreambuf.cxx
@@ -3,11 +3,14 @@
#include <string>
#include <vector>
+#include <cmext/algorithm>
+
#include <cm3p/uv.h>
#include <stdint.h>
#include "cmGetPipes.h"
#include "cmUVHandlePtr.h"
+#include "cmUVStream.h"
#include "cmUVStreambuf.h"
#define TEST_STR_LINE_1 "This string must be exactly 128 characters long so"
@@ -437,6 +440,90 @@ end:
return success;
}
+bool testUVPipeIStream()
+{
+ int pipe[] = { -1, -1 };
+ if (cmGetPipes(pipe) < 0) {
+ std::cout << "cmGetPipes() returned an error" << std::endl;
+ return false;
+ }
+
+ cm::uv_loop_ptr loop;
+ loop.init();
+ cm::uv_pipe_ptr pipeSink;
+ pipeSink.init(*loop, 0);
+ uv_pipe_open(pipeSink, pipe[1]);
+
+ std::string str = "Hello world!\n";
+ uv_write_t writeReq;
+ uv_buf_t buf;
+ buf.base = &str.front();
+ buf.len = str.length();
+ uv_write(&writeReq, pipeSink, &buf, 1, nullptr);
+ uv_run(loop, UV_RUN_DEFAULT);
+
+ cmUVPipeIStream pin(*loop, pipe[0]);
+ std::string line;
+ std::getline(pin, line);
+ if (line != "Hello world!") {
+ std::cout << "Line was \"" << line << "\", should be \"Hello world!\""
+ << std::endl;
+ return false;
+ }
+
+ return true;
+}
+
+bool testUVStreamRead()
+{
+ int pipe[] = { -1, -1 };
+ if (cmGetPipes(pipe) < 0) {
+ std::cout << "cmGetPipes() returned an error" << std::endl;
+ return false;
+ }
+
+ cm::uv_loop_ptr loop;
+ loop.init();
+ cm::uv_pipe_ptr pipeSink;
+ pipeSink.init(*loop, 0);
+ uv_pipe_open(pipeSink, pipe[1]);
+
+ std::string str = "Hello world!";
+ uv_write_t writeReq;
+ uv_buf_t buf;
+ buf.base = &str.front();
+ buf.len = str.length();
+ uv_write(&writeReq, pipeSink, &buf, 1, nullptr);
+ uv_run(loop, UV_RUN_DEFAULT);
+ pipeSink.reset();
+
+ cm::uv_pipe_ptr pipeSource;
+ pipeSource.init(*loop, 0);
+ uv_pipe_open(pipeSource, pipe[0]);
+
+ std::string output;
+ bool finished = false;
+ cmUVStreamRead(
+ pipeSource,
+ [&output](std::vector<char> data) { cm::append(output, data); },
+ [&output, &finished]() {
+ if (output != "Hello world!") {
+ std::cout << "Output was \"" << output
+ << "\", should be \"Hello world!\"" << std::endl;
+ return;
+ }
+ finished = true;
+ });
+ uv_run(loop, UV_RUN_DEFAULT);
+
+ if (!finished) {
+ std::cout << "finished was not set" << std::endl;
+ return false;
+ }
+
+ return true;
+}
+
int testUVStreambuf(int argc, char** const argv)
{
if (argc < 2) {
@@ -454,5 +541,15 @@ int testUVStreambuf(int argc, char** const argv)
return -1;
}
+ if (!testUVPipeIStream()) {
+ std::cout << "While executing testUVPipeIStream().\n";
+ return -1;
+ }
+
+ if (!testUVStreamRead()) {
+ std::cout << "While executing testUVPipeIStream().\n";
+ return -1;
+ }
+
return 0;
}