diff --git a/proxy.cpp b/proxy.cpp index 9a62bdb55b6875ae16581a37fb03d2752fa52a86..ab257771dca92a97e1e9894eaad369f0df831e4c 100755 --- a/proxy.cpp +++ b/proxy.cpp @@ -384,119 +384,123 @@ void RunTests::output_single_test_passfail(const unit_test & curr_test) std::cout << "FAILED: " << curr_test.errormsg << endl; } +test_execution::test_execution(unit_test & _test, RunTimeEnvironment & env, bool enable_valgrind_call) + : test(_test), environment(env) +{ + do_valgrind = enable_valgrind_call && test.is_valgrind; + if (!do_valgrind) + test.checkstream = new pipestream; +} - -// originally from stackoverflow.com user plinth -// http://stackoverflow.com/questions/2180079/how-can-i-copy-a-file-on-unix-using-c -// Modified by Jack Toole -bool UnitTestContainer::execute(bool enable_valgrind_call) +void test_execution::child() { - cout << std::flush; - bool do_valgrind = enable_valgrind_call && test.is_valgrind; + fmsg_pipe.close_read(); + cout_pipe.close_read(); + nums_pipe.close_read(); - // Make our pipes - pipestream fmsg_pipe; // For error messages - pipestream cout_pipe; // For stdout/stderr - pipestream nums_pipe; // for numbers: time, valgrind + // Redirect stdout/stderr to pipe + cout_pipe.steal_output(STDOUT_FILENO); + cout_pipe.steal_output(STDERR_FILENO); - if (!do_valgrind) - test.checkstream = new pipestream; + if (do_valgrind) + { + // We're giving up control to valgrind, so we can't + // Use anything but the cout pipe now + fmsg_pipe.close_write(); + nums_pipe.close_write(); + child_valgrind(); + } + else // if (!test.is_valgrind) + { + child_test(fmsg_pipe, nums_pipe); + } +} - // Fork - pid_t pid; - pid = fork(); +void test_execution::parent() +{ + fmsg_pipe.close_write(); + cout_pipe.close_write(); + nums_pipe.close_write(); + if (test.checkstream != NULL) + test.checkstream->close_write(); + + // Read stdout/stderr pipe while process is running + cout_pipe >> setmax(environment.max_output_length) >> test.output; + cout_pipe.close_read(); +} - if (pid == 0) /* child */ +void test_execution::clean_exit(int8_t return_code) +{ + if (do_valgrind) { fmsg_pipe.close_read(); - cout_pipe.close_read(); nums_pipe.close_read(); + valgrind_test_exit(return_code); + } + else // if (!test.is_valgrind) + { + test_exit(fmsg_pipe, nums_pipe); + } +} - // Redirect stdout/stderr to pipe - cout_pipe.steal_output(STDOUT_FILENO); - cout_pipe.steal_output(STDERR_FILENO); +void test_execution::interrupted_exit(int signal_number) +{ + fmsg_pipe.close_read(); + nums_pipe.close_read(); + test_signaled(signal_number); +} - if (do_valgrind) - { - // We're giving up control to valgrind, so we can't - // Use anything but the cout pipe now - fmsg_pipe.close_write(); - nums_pipe.close_write(); - child_valgrind(); - } - else // if (!test.is_valgrind) - { - child_test(fmsg_pipe, nums_pipe); - } +void test_execution::aborted_exit() +{ + fmsg_pipe.close_read(); + nums_pipe.close_read(); + test.errormsg = "Unexpectedly Aborted"; +} + +bool UnitTestContainer::execute(bool enable_valgrind_call) +{ + cout << std::flush; + test_execution executor(test, environment, enable_valgrind_call); + + // Fork + pid_t process_id; + process_id = fork(); + + if (process_id == 0) + { + executor.child(); // Unfortunately necessary to use a return stack instead of // exit() to get rid of valgrind errors // (which is important if we use valgrind ./proxy recursively) return false; // previously exit(0); } - else if (pid < 0) + else if (process_id > 0) { - perror("Abort: " __FILE__ ":" STR(__LINE__)); - exit(-1); - } - else - { - /* parent - wait for child - this has all error handling, you - * could just call wait() as long as you are only expecting to - * have one child process at a time. - */ - - fmsg_pipe.close_write(); - cout_pipe.close_write(); - nums_pipe.close_write(); - if (test.checkstream != NULL) - test.checkstream->close_write(); - - // Read stdout/stderr pipe while process is running - cout_pipe >> setmax(environment.max_output_length) >> test.output; - cout_pipe.close_read(); + executor.parent(); - // Eat child process - // Should be instant because of cout_pipe EOF int child_status; - pid_t ws = waitpid( pid, &child_status, 0); //should return immediately + pid_t ws = waitpid(process_id, &child_status, 0); //should return immediately EXIT_IF_ERROR(ws == -1); if (WIFEXITED(child_status)) /* exit code in child_status */ - { - int8_t return_code = WEXITSTATUS(child_status); - - if (do_valgrind) - { - fmsg_pipe.close_read(); - nums_pipe.close_read(); - valgrind_test_exit(return_code); - } - else // if (!test.is_valgrind) - { - test_exit(fmsg_pipe, nums_pipe); - } - return true; - } + executor.clean_exit(WEXITSTATUS(child_status)); else if (WIFSIGNALED(child_status)) /* killed */ - { - fmsg_pipe.close_read(); - nums_pipe.close_read(); - test_signaled(WTERMSIG(child_status)); - return true; - } + executor.interrupted_exit(WTERMSIG(child_status)); else - { - fmsg_pipe.close_read(); - nums_pipe.close_read(); - test.errormsg = "Unexpectedly Aborted"; - return true; - } + executor.aborted_exit(); + + return true; + } + else // if (process_id < 0) + { + perror("Abort: " __FILE__ ":" STR(__LINE__)); + exit(-1); } } -void UnitTestContainer::child_valgrind() +void test_execution::child_valgrind() { start_timeout(); execl("/usr/bin/valgrind", "/usr/bin/valgrind", "--trace-children=yes", /*"--log-fd=-1",*/ "-q", "./proxy", test.name, NULL); @@ -507,7 +511,7 @@ void UnitTestContainer::child_valgrind() } -void UnitTestContainer::child_test(pipestream & fmsg_pipe, pipestream & nums_pipe) +void test_execution::child_test(pipestream & fmsg_pipe, pipestream & nums_pipe) { test.checkstream->close_read(); // Execute test @@ -531,7 +535,7 @@ void UnitTestContainer::child_test(pipestream & fmsg_pipe, pipestream & nums_pip } -void UnitTestContainer::valgrind_test_exit(int8_t return_code) +void test_execution::valgrind_test_exit(int8_t return_code) { size_t last_endl = findNthLast(test.output, '\n', 2); if (last_endl == string::npos) @@ -551,7 +555,7 @@ void UnitTestContainer::valgrind_test_exit(int8_t return_code) } -void UnitTestContainer::test_exit(pipestream & fmsg_pipe, pipestream & nums_pipe) +void test_execution::test_exit(pipestream & fmsg_pipe, pipestream & nums_pipe) { fmsg_pipe >> test.errormsg; fmsg_pipe.close(); @@ -587,7 +591,7 @@ void UnitTestContainer::test_exit(pipestream & fmsg_pipe, pipestream & nums_pipe } -void UnitTestContainer::test_signaled(int signum) +void test_execution::test_signaled(int signum) { if (signum == environment.timeout_signum0 || signum == environment.timeout_signum1) { @@ -635,7 +639,7 @@ const char * get_valgrind_string(int32_t flags) } -void UnitTestContainer::start_timeout() +void test_execution::start_timeout() { struct itimerval timeout; timeout.it_interval.tv_sec = 0; @@ -651,7 +655,7 @@ void UnitTestContainer::start_timeout() } -long UnitTestContainer::end_timeout() +long test_execution::end_timeout() { struct itimerval timeout; timeout.it_interval.tv_sec = 0; diff --git a/proxy.h b/proxy.h index 31e535287e0ef99f6d0982f0f14171351d277c46..2ae72238ece7e42870dd1357e79a388c9154d5ef 100755 --- a/proxy.h +++ b/proxy.h @@ -114,7 +114,6 @@ namespace proxy class UnitTestContainer { private: - RunTimeEnvironment & environment; unit_test & test; @@ -122,6 +121,29 @@ namespace proxy UnitTestContainer(unit_test & _test, RunTimeEnvironment & env); bool execute(bool enable_valgrind_call); + private: + UnitTestContainer(const UnitTestContainer & other); + UnitTestContainer & operator=(const UnitTestContainer & other); + }; + + class test_execution + { + private: + util::pipestream fmsg_pipe; // For error messages + util::pipestream cout_pipe; // For stdout/stderr + util::pipestream nums_pipe; // for numbers: time, valgrind + unit_test & test; + RunTimeEnvironment & environment; + bool do_valgrind; + + public: + test_execution(unit_test & _test, RunTimeEnvironment & env, bool enable_valgrind_call); + void parent(); + void child(); + void clean_exit(int8_t return_code); + void interrupted_exit(int signal_number); + void aborted_exit(); + private: void child_valgrind(); void child_test(util::pipestream & fmsg_pipe, util::pipestream & nums_pipe); @@ -132,10 +154,11 @@ namespace proxy long end_timeout(); private: - UnitTestContainer(const UnitTestContainer & other); - UnitTestContainer & operator=(const UnitTestContainer & other); + test_execution(const test_execution & other); + test_execution & operator=(const test_execution & other); }; + const char * get_valgrind_string(int32_t flags); int32_t get_valgrind_flags(bool test_failed); int32_t bitflags(unsigned long a, unsigned long b = 0, unsigned long c = 0,