|
|
@@ -0,0 +1,335 @@
|
|
|
+#include "cmUVProcessChain.h"
|
|
|
+
|
|
|
+#include "cmAlgorithms.h"
|
|
|
+#include "cmGetPipes.h"
|
|
|
+#include "cmUVHandlePtr.h"
|
|
|
+#include "cmUVStreambuf.h"
|
|
|
+
|
|
|
+#include "cm_uv.h"
|
|
|
+
|
|
|
+#include <algorithm>
|
|
|
+#include <functional>
|
|
|
+#include <iostream>
|
|
|
+#include <sstream>
|
|
|
+#include <string>
|
|
|
+#include <vector>
|
|
|
+
|
|
|
+#include <csignal>
|
|
|
+
|
|
|
+struct ExpectedStatus
|
|
|
+{
|
|
|
+ bool Finished;
|
|
|
+ bool MatchExitStatus;
|
|
|
+ bool MatchTermSignal;
|
|
|
+ cmUVProcessChain::Status Status;
|
|
|
+};
|
|
|
+
|
|
|
+static const std::vector<ExpectedStatus> status1 = {
|
|
|
+ { false, false, false, { 0, 0 } },
|
|
|
+ { false, false, false, { 0, 0 } },
|
|
|
+ { false, false, false, { 0, 0 } },
|
|
|
+};
|
|
|
+
|
|
|
+static const std::vector<ExpectedStatus> status2 = {
|
|
|
+ { true, true, true, { 0, 0 } },
|
|
|
+ { false, false, false, { 0, 0 } },
|
|
|
+ { false, false, false, { 0, 0 } },
|
|
|
+};
|
|
|
+
|
|
|
+static const std::vector<ExpectedStatus> 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<const cmUVProcessChain::Status*>& actual,
|
|
|
+ const std::vector<ExpectedStatus>& 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 <typename T>
|
|
|
+std::function<std::ostream&(std::ostream&)> 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<std::ostream&(std::ostream&)>& func)
|
|
|
+{
|
|
|
+ return func(stream);
|
|
|
+}
|
|
|
+
|
|
|
+void printResults(const std::vector<const cmUVProcessChain::Status*>& actual,
|
|
|
+ const std::vector<ExpectedStatus>& 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<cmUVProcessChain>& chain)
|
|
|
+{
|
|
|
+ std::vector<const cmUVProcessChain::Status*> status;
|
|
|
+
|
|
|
+ chain = cm::make_unique<cmUVProcessChain>(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<cmUVProcessChain> 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<cmUVProcessChain> 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<cmUVProcessChain> 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;
|
|
|
+}
|