From c862ecea1f18d22e7f5f179b323c8cacc413ec40 Mon Sep 17 00:00:00 2001
From: toole1 <toole1@6fbd10e7-183d-0410-a318-cb416676e4f2>
Date: Sat, 23 Jul 2011 05:55:19 +0000
Subject: [PATCH] i can't believe this worked

git-svn-id: https://subversion.cs.illinois.edu/svn/cs225@3377 6fbd10e7-183d-0410-a318-cb416676e4f2
---
 proxy.cpp | 184 ++++++++++++++++++++++++++++--------------------------
 proxy.h   |  29 ++++++++-
 2 files changed, 120 insertions(+), 93 deletions(-)

diff --git a/proxy.cpp b/proxy.cpp
index 9a62bdb..ab25777 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 31e5352..2ae7223 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,
-- 
GitLab