From fcfa6d05fa4209d8297ccdbcf98f9a147110b405 Mon Sep 17 00:00:00 2001 From: Eric Cano <Eric.Cano@cern.ch> Date: Thu, 18 Feb 2016 17:48:47 +0100 Subject: [PATCH] Created a helper class to start sub processes for the system test. Created an embryo system test. --- tests/CMakeLists.txt | 22 ++++++ tests/Subprocess.cpp | 130 ++++++++++++++++++++++++++++++++ tests/Subprocess.hpp | 43 +++++++++++ tests/SubprocessSystemTests.cpp | 36 +++++++++ tests/system_tests.cpp | 28 +++++++ 5 files changed, 259 insertions(+) create mode 100644 tests/Subprocess.cpp create mode 100644 tests/Subprocess.hpp create mode 100644 tests/SubprocessSystemTests.cpp create mode 100644 tests/system_tests.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 69c0e4172e..523ff5d01e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -46,3 +46,25 @@ target_link_libraries(unitTests ${GMOCK_LIB} gtest pthread) + +add_library(systemTestHelper + Subprocess.cpp) + +add_library(systemTestHelperTests SHARED + SubprocessSystemTests.cpp) + +target_link_libraries(systemTestHelperTests + systemTestHelper + ctacommon) + +add_executable(systemTests + system_tests.cpp + ${GMOCK_SRC}) + +target_link_libraries(systemTests + systemTestHelper + systemTestHelperTests + gtest + pthread) + + diff --git a/tests/Subprocess.cpp b/tests/Subprocess.cpp new file mode 100644 index 0000000000..d1e24caf7b --- /dev/null +++ b/tests/Subprocess.cpp @@ -0,0 +1,130 @@ +/* + * The CERN Tape Archive (CTA) project + * Copyright (C) 2015 CERN + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "Subprocess.hpp" +#include "common/exception/Errnum.hpp" +#include <string.h> +#include <iostream> +#include <sys/signal.h> +#include <sys/wait.h> +#include <fcntl.h> + +namespace systemTests { +Subprocess::Subprocess(const std::string & executable, const std::list<std::string>& argv) { + // Sanity checks + if (argv.size() < 1) + throw cta::exception::Exception( + "In Subprocess::Subprocess: not enough elements in argv"); + // Prepare the pipes for the child's stdout and stderr (stdin will be closed) + const size_t readSide=0; + const size_t writeSide=1; + int stdoutPipe[2]; + int stderrPipe[2]; + cta::exception::Errnum::throwOnNonZero(::pipe2(stdoutPipe, O_NONBLOCK), + "In Subprocess::Subprocess failed to create the stdout pipe"); + cta::exception::Errnum::throwOnNonZero(::pipe2(stderrPipe, O_NONBLOCK), + "In Subprocess::Subprocess failed to create the stderr pipe"); + cta::exception::Errnum::throwOnMinusOne(m_child = fork(), + "In Subprocess::Subprocess failed to fork"); + if (m_child) { + // We are the parent process. Close the write sides of pipes. + ::close(stdoutPipe[writeSide]); + ::close(stderrPipe[writeSide]); + m_stdoutFd = stdoutPipe[readSide]; + m_stderrFd = stderrPipe[readSide]; + } else { + try { + // We are in the child process. Close the read sides of the pipes. + ::close(stdoutPipe[readSide]); + ::close(stderrPipe[readSide]); + // Close stdin and rewire the stdout and stderr to the pipes. + ::close(STDIN_FILENO); + cta::exception::Errnum::throwOnMinusOne( + ::dup2(stdoutPipe[writeSide], STDOUT_FILENO), + "In Subprocess::Subprocess failed to dup2 stdout pipe"); + cta::exception::Errnum::throwOnMinusOne( + ::dup2(stderrPipe[writeSide], STDERR_FILENO), + "In Subprocess::Subprocess failed to dup2 stderr pipe"); + // Close the now duplicated pipe file descriptors + ::close(stdoutPipe[writeSide]); + ::close(stderrPipe[writeSide]); + // Finally execv the command + char ** cargv = new char*[argv.size()+1]; + int index = 0; + for (auto a=argv.cbegin(); a!=argv.cend(); a++) { + cargv[index++] = ::strdup(a->c_str()); + } + cargv[argv.size()] = NULL; + cta::exception::Errnum::throwOnMinusOne( + execvp(executable.c_str(), cargv)); + // We should never get here. + throw cta::exception::Exception( + "In Subprocess::Subprocess execv failed without returning -1!"); + } catch (cta::exception::Exception & ex) { + std::cerr << ex.getMessageValue(); + exit(EXIT_FAILURE); + } + } + } + +void Subprocess::kill(int signal) { + ::kill(m_child, signal); +} + +int Subprocess::exitValue() { + if(!m_child) + throw cta::exception::Exception("In Subprocess::exitValue: child process not waited for"); + return WEXITSTATUS(m_childStatus); +} + +void Subprocess::wait() { + ::waitpid(m_child, &m_childStatus, 0); + char buff[1000]; + int rc; + while (0<(rc=::read(m_stdoutFd, buff, sizeof(buff)))) { + m_stdout.append(buff, rc); + } + ::close(m_stdoutFd); + while (0<(rc=::read(m_stderrFd, buff, sizeof(buff)))) { + m_stderr.append(buff, rc); + } + ::close(m_stderrFd); + m_childComplete = true; +} + +std::string Subprocess::stdout() { + if(!m_child) + throw cta::exception::Exception("In Subprocess::stdout: child process not waited for"); + return m_stdout; +} + +std::string Subprocess::stderr() { + if(!m_child) + throw cta::exception::Exception("In Subprocess::stderr: child process not waited for"); + return m_stderr; +} + +Subprocess::~Subprocess() { + if(!m_childComplete) { + this->kill(SIGKILL); + this->wait(); + } +} + + +} \ No newline at end of file diff --git a/tests/Subprocess.hpp b/tests/Subprocess.hpp new file mode 100644 index 0000000000..7e80051683 --- /dev/null +++ b/tests/Subprocess.hpp @@ -0,0 +1,43 @@ +/* + * The CERN Tape Archive (CTA) project + * Copyright (C) 2015 CERN + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <list> +#include <string> + +namespace systemTests { +class Subprocess { +public: + Subprocess(const std::string & program, const std::list<std::string> &argv); + ~Subprocess(); + void wait(void); + std::string stdout(); + std::string stderr(); + void kill(int signal); + int exitValue(); +private: + int m_stdoutFd; + int m_stderrFd; + pid_t m_child; + bool m_childComplete; + int m_childStatus; + std::string m_stdout; + std::string m_stderr; +}; +} \ No newline at end of file diff --git a/tests/SubprocessSystemTests.cpp b/tests/SubprocessSystemTests.cpp new file mode 100644 index 0000000000..6c1cb26f28 --- /dev/null +++ b/tests/SubprocessSystemTests.cpp @@ -0,0 +1,36 @@ +/* + * The CERN Tape Archive (CTA) project + * Copyright (C) 2015 CERN + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "Subprocess.hpp" + +#include <gtest/gtest.h> + +namespace systemTests { +TEST(SuprocessHelper, basicTests) { + Subprocess sp("echo", std::list<std::string>({"echo", "Hello,", "world."})); + sp.wait(); + ASSERT_EQ("Hello, world.\n", sp.stdout()); + ASSERT_EQ("", sp.stderr()); + ASSERT_EQ(0, sp.exitValue()); + Subprocess sp2("cat", std::list<std::string>({"cat", "/no/such/file"})); + sp2.wait(); + ASSERT_EQ("", sp2.stdout()); + ASSERT_NE(std::string::npos, sp2.stderr().find("/no/such/file")); + ASSERT_EQ(1, sp2.exitValue()); +} +} \ No newline at end of file diff --git a/tests/system_tests.cpp b/tests/system_tests.cpp new file mode 100644 index 0000000000..ad2030cda5 --- /dev/null +++ b/tests/system_tests.cpp @@ -0,0 +1,28 @@ +/* + * The CERN Tape Archive (CTA) project + * Copyright (C) 2015 CERN + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <gtest/gtest.h> +#include <gmock/gmock.h> + +// This is a simplified main for system tests. There is not point in using +// valgrind with it, and we will not use gmock with it either (pure gtest). +int main(int argc, char** argv) { + int newArgc = argc; + ::testing::InitGoogleTest(&newArgc, argv); + return RUN_ALL_TESTS(); +} -- GitLab