Skip to content
Snippets Groups Projects
proxy.h 6.57 KiB
// proxy.h
// NOTE: This is a generic file. Actual unit tests are located in
//       unit_tests.cpp.
// By Jack Toole for CS 225 spring 2011

#ifndef MONAD_PROXY_H
#define MONAD_PROXY_H

#include <map>
#include <string>
#include <vector>
#include <iostream>
#include <utility>
#include "pipestream.h"
#include "monad_shared.h"

#define NO_MP_PART -1
#include "_mp_part_number.h"
#define MP_PART(x) (MP_PART_NUMBER == (x) || MP_PART_NUMBER == NO_MP_PART)

namespace proxy
{
	using namespace monad_shared;

	class RunTests;
	typedef bool (*output_check)(const std::string &, const std::string &);

	extern std::vector<unit_test> * global_tests;
	typedef std::map<std::string, output_check, util::ci_less> output_check_map;
	extern output_check_map * global_output_checks;

	class add_unit_test
	{
		public:
		add_unit_test(int8_t MPpart, const char * name, unit_test::function func,
		              int32_t points_in_part, int32_t points_in_total, long timeout,
		              bool is_valgrind);

		private:
		void lazy_init_global_tests();
		void assertMPpart(int8_t MPpart, const char * name);
		int32_t get_points(int32_t points_in_total, int32_t points_in_part);
	};

	class add_output_check
	{
		public:
		add_output_check(const char * name, output_check func);
	};

	enum mode_t
	{
		SINGLE_TEST,
		MP_PART_TESTS,
		ALL_TESTS
	};

	struct RunTimeEnvironment
	{
		public:
		const int itimer_number0;
		const int itimer_number1;
		const int timeout_signum0;
		const int timeout_signum1;
		const size_t max_output_length;
		const char * single_test_passed_string;
		std::vector<unit_test> * heap_tests;
		output_check_map * output_checks;
		int32_t cleanup_globals();
		RunTimeEnvironment(std::vector<unit_test> *& init_tests,
		                   output_check_map *& init_output_checks);
		
		private:
		RunTimeEnvironment(const RunTimeEnvironment & other);
		RunTimeEnvironment & operator=(RunTimeEnvironment & other);
		static int8_t singleton;
	};

	class RunTests
	{
		private:
		RunTimeEnvironment & environment;
		mode_t mode;
		const char * test_arg;
		int8_t mp_part;

		public:
		RunTests(int argc, char ** argv, RunTimeEnvironment & env);
		int execute();
		private:
		void redirect_glibc_to_stderr();
		void process_args(int argc, char ** argv);

		protected:
		int32_t execute_by_mode();
		int32_t run_single_test(const char * testname);
		int32_t run_single_test(unit_test & curr_test);
		void    handle_single_test_output(const std::string & output);
		void    output_single_test_passfail(const unit_test & curr_test);
		
		int32_t run_all_tests();
		int32_t get_sum_points();
		int32_t get_max_testname_length();
		int32_t get_max_points_length();
		void output_detailed_info_if_any_failed(int32_t score);
		void output_detailed_tests_info(int32_t score);

		private:
		RunTests(const RunTests & other);
		RunTests & operator=(const RunTests & other);
	};

	class UnitTestContainer
	{
		private:
		RunTimeEnvironment & environment;
		unit_test & test;
		
		public:
		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);
		void valgrind_test_exit(int8_t return_code);
		void test_exit(util::pipestream & fmsg_pipe, util::pipestream & nums_pipe);
		void test_signaled(int signum);
		void start_timeout();
		long end_timeout();
		
		private:
		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,
					 unsigned long d = 0, unsigned long e = 0);
	bool bitflag(int32_t flags, int32_t num);

} // namespace proxy

using std::cout;
using std::cerr;
using std::endl;

#define UNIT_TEST(MPpart,func,pointsInPart,pointsInTotal,timeout)      \
	proxy::unit_test::return_type                                   \
	func(proxy::unit_test & this_test); \
	proxy::add_unit_test                                            \
		func##_adder(MPpart, #func, func, pointsInPart,                \
		             pointsInTotal, timeout, false);                   \
	proxy::unit_test::return_type                                   \
	func(proxy::unit_test & this_test)

#define VALGRIND_TEST(MPpart,func,pointsInPart,pointsInTotal,timeout)  \
	proxy::unit_test::return_type                                   \
	func(proxy::unit_test & this_test); \
	proxy::add_unit_test                                            \
		func##_adder(MPpart, #func, func, pointsInPart,                \
		             pointsInTotal, timeout, true);                    \
	proxy::unit_test::return_type                                   \
	func(proxy::unit_test & this_test)

#define OUTPUT_CHECK(func)                                              \
	bool output_check_##func(const std::string & output, const std::string & expected); \
	proxy::add_output_check                                          \
		output_check_##func##_adder(#func, output_check_##func);        \
	bool output_check_##func(const std::string & output, const std::string & expected)

#define STRINGIFY1(p)   #p
#define STR(p)          STRINGIFY1(p)

#define FAIL(error)     return std::string(__FILE__ ":" STR(__LINE__) ": ") + (error)

#define PASS            return monad_shared::unit_test::pass_string;

#define ASSERT(expr)    if (!(expr))       \
                            FAIL("Assertion (" #expr ") failed")

#define ASSERT_OUTPUT(checkFunc, str) \
	*this_test.checkstream << #checkFunc << str;
namespace proxy {

inline int32_t bitflags(unsigned long a, unsigned long b, unsigned long c,
                        unsigned long d, unsigned long e)
{
	return ((int)(a != 0))        | (((int)(b != 0)) << 1) |
           (((int)(c != 0)) << 2) | (((int)(d != 0)) << 3) |
           (((int)(e != 0)) << 4) ;
}

inline bool bitflag(int32_t flags, int32_t num)
{
	return (flags & (1 << num)) != 0;
}

}
#endif