#include "cmUVProcessChain.h" #include "cmAlgorithms.h" #include "cmGetPipes.h" #include "cmUVHandlePtr.h" #include "cmUVStreambuf.h" #include "cm_uv.h" #include #include #include #include #include #include #include struct ExpectedStatus { bool Finished; bool MatchExitStatus; bool MatchTermSignal; cmUVProcessChain::Status Status; }; static const std::vector status1 = { { false, false, false, { 0, 0 } }, { false, false, false, { 0, 0 } }, { false, false, false, { 0, 0 } }, }; static const std::vector status2 = { { true, true, true, { 0, 0 } }, { false, false, false, { 0, 0 } }, { false, false, false, { 0, 0 } }, }; static const std::vector status3 = { { true, true, true, { 0, 0 } }, { true, true, true, { 1, 0 } }, #ifdef _WIN32 { true, true, true, { 2, 0 } }, #else { true, false, true, { 0, SIGABRT } }, #endif }; bool operator==(const cmUVProcessChain::Status* actual, const ExpectedStatus& expected) { if (!expected.Finished) { return !actual; } else if (!actual) { return false; } if (expected.MatchExitStatus && expected.Status.ExitStatus != actual->ExitStatus) { return false; } if (expected.MatchTermSignal && expected.Status.TermSignal != actual->TermSignal) { return false; } return true; } bool resultsMatch(const std::vector& actual, const std::vector& expected) { return actual.size() == expected.size() && std::equal(actual.begin(), actual.end(), expected.begin()); } std::string getInput(std::istream& input) { char buffer[1024]; std::ostringstream str; do { input.read(buffer, 1024); str.write(buffer, input.gcount()); } while (input.gcount() > 0); return str.str(); } template std::function printExpected(bool match, const T& value) { return [match, value](std::ostream& stream) -> std::ostream& { if (match) { stream << value; } else { stream << "*"; } return stream; }; } std::ostream& operator<<( std::ostream& stream, const std::function& func) { return func(stream); } void printResults(const std::vector& actual, const std::vector& expected) { 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) << std::endl; } else { std::cout << " null" << std::endl; } } std::cout << "Actual:" << std::endl; for (auto const& a : actual) { if (a) { std::cout << " ExitStatus: " << a->ExitStatus << ", TermSignal: " << a->TermSignal << std::endl; } else { std::cout << " null" << std::endl; } } } bool checkExecution(cmUVProcessChainBuilder& builder, std::unique_ptr& chain) { std::vector status; chain = cm::make_unique(builder.Start()); if (!chain->Valid()) { std::cout << "Valid() returned false, should be true" << 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(6000)) { std::cout << "Wait() returned true, should be false" << 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; } if (!chain->Wait()) { std::cout << "Wait() returned false, should be true" << std::endl; return false; } status = chain->GetStatus(); if (!resultsMatch(status, status3)) { std::cout << "GetStatus() did not produce expected output" << std::endl; printResults(status, status3); return false; } return true; } bool checkOutput(std::istream& outputStream, std::istream& errorStream) { std::string output = getInput(outputStream); if (output != "HELO WRD!") { std::cout << "Output was \"" << output << "\", expected \"HELO WRD!\"" << std::endl; return false; } std::string error = getInput(errorStream); if (error.length() != 3 || error.find('1') == std::string::npos || error.find('2') == std::string::npos || error.find('3') == std::string::npos) { std::cout << "Error was \"" << error << "\", expected \"123\"" << std::endl; return false; } return true; } bool testUVProcessChainBuiltin(const char* helperCommand) { cmUVProcessChainBuilder builder; std::unique_ptr chain; builder.AddCommand({ helperCommand, "echo" }) .AddCommand({ helperCommand, "capitalize" }) .AddCommand({ helperCommand, "dedup" }) .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT) .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR); if (!checkExecution(builder, chain)) { return false; } if (!chain->OutputStream()) { std::cout << "OutputStream() was null, expecting not null" << std::endl; return false; } if (!chain->ErrorStream()) { std::cout << "ErrorStream() was null, expecting not null" << std::endl; return false; } if (!checkOutput(*chain->OutputStream(), *chain->ErrorStream())) { return false; } return true; } bool testUVProcessChainExternal(const char* helperCommand) { cmUVProcessChainBuilder builder; std::unique_ptr chain; int outputPipe[2], errorPipe[2]; cm::uv_pipe_ptr outputInPipe, outputOutPipe, errorInPipe, errorOutPipe; if (cmGetPipes(outputPipe) < 0) { std::cout << "Error creating pipes" << std::endl; return false; } if (cmGetPipes(errorPipe) < 0) { std::cout << "Error creating pipes" << std::endl; return false; } builder.AddCommand({ helperCommand, "echo" }) .AddCommand({ helperCommand, "capitalize" }) .AddCommand({ helperCommand, "dedup" }) .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, outputPipe[1]) .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, errorPipe[1]); if (!checkExecution(builder, chain)) { return false; } if (chain->OutputStream()) { std::cout << "OutputStream() was not null, expecting null" << std::endl; return false; } if (chain->ErrorStream()) { std::cout << "ErrorStream() was not null, expecting null" << std::endl; return false; } outputOutPipe.init(chain->GetLoop(), 0); uv_pipe_open(outputOutPipe, outputPipe[1]); outputOutPipe.reset(); errorOutPipe.init(chain->GetLoop(), 0); uv_pipe_open(errorOutPipe, errorPipe[1]); errorOutPipe.reset(); outputInPipe.init(chain->GetLoop(), 0); uv_pipe_open(outputInPipe, outputPipe[0]); cmUVStreambuf outputBuf; outputBuf.open(outputInPipe); std::istream outputStream(&outputBuf); errorInPipe.init(chain->GetLoop(), 0); uv_pipe_open(errorInPipe, errorPipe[0]); cmUVStreambuf errorBuf; errorBuf.open(errorInPipe); std::istream errorStream(&errorBuf); if (!checkOutput(outputStream, errorStream)) { return false; } return true; } bool testUVProcessChainNone(const char* helperCommand) { cmUVProcessChainBuilder builder; std::unique_ptr chain; builder.AddCommand({ helperCommand, "echo" }) .AddCommand({ helperCommand, "capitalize" }) .AddCommand({ helperCommand, "dedup" }); if (!checkExecution(builder, chain)) { return false; } if (chain->OutputStream()) { std::cout << "OutputStream() was not null, expecting null" << std::endl; return false; } if (chain->ErrorStream()) { std::cout << "ErrorStream() was not null, expecting null" << std::endl; return false; } return true; } int testUVProcessChain(int argc, char** const argv) { if (argc < 2) { std::cout << "Invalid arguments.\n"; return -1; } if (!testUVProcessChainBuiltin(argv[1])) { std::cout << "While executing testUVProcessChainBuiltin().\n"; return -1; } if (!testUVProcessChainExternal(argv[1])) { std::cout << "While executing testUVProcessChainExternal().\n"; return -1; } if (!testUVProcessChainNone(argv[1])) { std::cout << "While executing testUVProcessChainNone().\n"; return -1; } return 0; }