junit.h 13.8 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
/***************************************************************************
 *   Copyright (C) 2001 Claus Dreby                                        *
 *   root@sat                                                              *
 *                                                                         *
 *   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 2 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, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#ifndef J_UNIT_H
#define J_UNIT_H

#include "jobject.h"
#include "jruntimeexception.h"

#include <vector>
#include <string>
#include <stack>
#include <map>
#include <iostream>
#include <algorithm>
#include <typeinfo>

namespace jcommon {

extern bool verbose;
extern bool line_fmt;

class Visitor;

/**
 * \brief The heart of a test system: A test. The test is meant as a base class 
 * for the tests that a client want performed. This means that all tests are to
 * be pointers dynamically allocated. However, the test system takes
 * responsibilities for freeing them again.
 *
 * The function call overload mechanism is used for the executable part of the test
 */
class Test {
	std::string nam;
public:
	/// A test just needs a name
	Test(const std::string& name): 
		nam(name) 
	{
	}
	
	virtual ~Test() 
	{
	}
	
	// The execution of the test
	virtual void operator()() 
	{
	}

	virtual void Visit(Visitor*);
	
	virtual Test* GetChild(const std::string&) 
	{
	   	return 0; 
	}
	
	std::string GetName() const 
	{
	   	return nam; 
	}

};

/** 
 * A test that is implemented by a member function.
 *
 */
template<typename C>class TestFunction : public Test {
	public:
		typedef void (C::*mfp)();

		// An object, a name, and a pointer to a member function.
		TestFunction(C* par, const std::string& name, mfp fp):
	   		Test(name), par(par), fp(fp)
		{
		}
	
		// Executed by invoking the function in the object.
		virtual void operator()()
		{
			(par->*fp)();
		}

	private:
		C* par;
		mfp fp;

};

/**
 * A ref counted reference to a test. This is what test suites are composed
 * of, and what ensures destruction.
 *
 */
class TestCase {
	size_t* cnt;
	Test* tst;
	void dec_cnt();

	public:
		// Simply wrap -- and own -- a test.
		TestCase(Test* t);
	
		// Keep the ref count
		TestCase(const TestCase& tr);
	
		/**
		 * Make a TestCase from a class and a member function.
		 *
		 * The normal usage is inside some test suite class Test:
		 *
		 * #add("id", TestCase(this, "Testing this and that", &Test::test))#
		 *
		 * to make a test that invokes the test method on the instance of the
		 * suite class.
		 * \Ref{TestFunction}
		 */
		template<typename C>TestCase(C* par, const std::string& name, typename TestFunction<C>::mfp fp): 
			cnt(new size_t(1)), tst(new TestFunction<C>(par, name, fp))
		{ 
		}

		virtual ~TestCase();

		// Assignment that maintains reference count.
		TestCase& operator=(const TestCase&);
	
		void Visit(Visitor* vp) const 
		{
		   	tst->Visit(vp); 
		}

		operator Test& () 
		{
		   	return *tst; 
		}

		operator const Test& () const 
		{
		   	return *tst; 
		}

};

/**
 * A wrapper class for the TestCase class that succedes if the correct
 * exception is generated.
 *
 */
template<typename E>class ExceptionTest : public Test {
	private:
		TestCase tc;
		char const* file;
		unsigned int line;

	public:
		/**
		 * The constructor needs a TestCase to wrap. This exception_test will
		 * fail unless the wrapped TestCase generates the exception.
		 *
		 * The name of the exception_test is copied from the wrapped test.
		 */
		ExceptionTest(char const* f, unsigned int l, const TestCase& tc):
		   	Test(static_cast<const Test&>(tc).GetName()), tc(tc), file(f), line(l) 
		{
		}

		virtual ~ExceptionTest() 
		{
		}

		// Runs the wrapped test, and fails unless the correct exception is thrown.
		virtual void operator()();

};

/**
 * Generate a TestCase that expects a specific exception from the TestCase it
 * wraps. It can be used something like
 *
 * #TestCase tc(this, "Test name", &Test::test);#
 *
 * #add("ex", exception_case<out_of_range>(tc));#
 *
 * The name of the exception_case is copied from the wrapped TestCase, and
 * the exception_case will execute the tc test case and report a failure
 * unless the #out_of_range# exception is generated.
 */
template<typename E>TestCase exception_case_f(const char* f, unsigned int l, const TestCase& tc)
{
	return TestCase(new ExceptionTest<E>(tc));
}

/**
 * Splits the string by char c. Each c will generate a new element in the
 * vector, including leading and trailing c.
 *
 */
extern std::vector<std::string> vectorize(const std::string& str, char c);

/**
 * A suite is a test that happens to be a collection of tests. This is an
 * implementation of the Composite pattern.
 *
 */
class Suite : public Test {
	std::vector<std::string> ids;
	std::vector<TestCase> tests;

	public:
		// Make an empty test suite.
		Suite(const std::string& name):
		   	Test(name) 
		{
		}
	
		virtual ~Suite() 
		{
		}

		/// Add a TestCase to the suite.
		void Add(const std::string& id, const TestCase& t);
		
		/**
		 * Get a child with the specified id.
		 * @return 0 if not found.
		 */
		virtual Test* GetChild(const std::string& id);
		
		/// An empty implementation.
		virtual void operator()() 
		{
		}
		
		/// Allow a Visitor to Visit a suite node of the test tree.
		void Visit(Visitor*);
		
		/// Get a reference to the main test suite that the main program will run.
		static Suite& Main();
		
		// Splits the string by dots, and use each id to find a suite or test.
		Test* Find(const std::string& id);
};

/**
 * The Visitor class is a base class for classes that wants to participate in
 * the Visitor pattern with the test hierarchi.
 *
 * This is a slightly extended Visitor pattern implementation, intended for
 * collaboration with the Composite pattern. The aggregate node (here the
 * suite node) is Visited twice, before and after the children are Visited.
 * This allows different algorithms to be implemented.
 */
class Visitor {
	public:
		virtual ~Visitor() 
		{
		}
		
		// Visit a test case, that is not a suite.
		virtual void Visit(Test&) = 0;
		
		// Visit a suite node before the children are Visited.
		virtual void Visit(Suite&) 
		{
		}

		/**
		 * Visit a suite after the children are Visited
		 *
		 */
		virtual void Visit(Suite&, int dummy) // post childs
		{
		}
};

// The basic for all failed assert statements.
class AssertionError : public RuntimeException {
	char const* file_;
	unsigned int line_;
	std::string msg;

	public:
		/// An assertion error with the given message.
		AssertionError(char const* file, unsigned int line, const std::string& msg): 
			RuntimeException("assertion error"), file_(file), line_(line), msg(msg) 
		{
		}
	
		std::string GetMessage() const 
		{
		   	return msg; 
		}
		
		virtual ~AssertionError() throw () 
		{
		}

		/**
		 * The virtual method used for operator<<.
		 *
		 */
		virtual void Out(std::ostream& os) const;

		char const* GetFile() 
		{
		   	return file_; 
		}
		
		unsigned int GetLine() 
		{
		   	return line_; 
		}

};

/**
 * This exception represents a failed comparison between two values of types
 * T1 and T2. Both the expected and the actually value are kept.
 *
 */
template<class T1, class T2>class AssertValueError : public AssertionError {
		T1 exp;
		T2 got;

	public:
		/// Construct by message, expected and gotten.
		AssertValueError(const char* f, unsigned int l, const std::string& msg, T1& exp, T2& got):
		   	AssertionError(f, l, msg), exp(exp), got(got)
		{
		}

		virtual ~AssertValueError() throw () 
		{
		}

		/**
		 * Specialized version that requires both T1 and T2 to support
		 * operator<<(ostream&, Tn).
		 *
		 */
		virtual void Out(std::ostream& os) const
		{
			os << GetMessage() << " [expected: `" << exp << "' got: `" << got << "']";
		}

};

// The test was not succesful.
inline void assert_fail_f(const char* f, unsigned int l, const std::string& msg)
{
	throw AssertionError(f, l, msg);
}

template<typename E>void ExceptionTest<E>::operator()()
{
	try {
		(static_cast<Test&>(tc))();
		assert_fail_f(file, line, "unexpected lack of exception");
	} catch (E& ) {
		// fine!
	}
}

// Assert that the assertion is true, that is fail #if (!assertion) ...#
template<class A> inline void assert_true_f(char const* f, unsigned int l, const std::string& msg, A assertion)
{
	if (!assertion)
		throw AssertionError(f, l, msg);
}

#define assert_true(m, a) assert_true_f(__FILE__, __LINE__, m, a)
#define assert_fail(m) assert_fail_f(__FILE__, __LINE__, m)
#define assert_eq(m, e, g) assert_eq_f(__FILE__, __LINE__, m, e, g)

// Assert that the two arguments are equal in the #==# sense.
template<class T1, class T2>inline void assert_eq_f(char const* f, unsigned int l, const std::string& msg, T1 exp, T2 got)
{
	if (!(exp == got))
		throw AssertValueError<T1,T2>(f, l, msg, exp, got);
}

/*
 * Put an assertion error to a stream, using the out method. The out method
 * is virtual.
 */
inline std::ostream& operator<<(std::ostream& os, const AssertionError& a)
{
	a.Out(os);
	return os;
}

// A mostly internal class for keeping score.
class res_cnt {
	int ok, fail, err;

	public:
		// Create a 0 count.
		res_cnt(): 
			ok(0), fail(0), err(0) 
		{
		}

		// Count one ok.
		void add_ok() 
		{
		   	++ok; 
		}
		
		// Count one fail.
		void add_fail() 
		{
		   	++fail; 
		}
		
		// Count one error.
		void add_err() 
		{
		   	++err; 
		}
		
		// get ok count.
		int n_ok() 
		{
		   	return ok; 
		}
		
		// get fail count.
		int n_fail() 
		{
		   	return fail; 
		}
		
		// get error count.
		int n_err() 
		{
		   	return err; 
		}
		
		// get total count.
		int n() 
		{
		   	return ok+fail+err; 
		}
};

/**
 * The standard text based tester. It implements the Visitor pattern for the
 * test and suite classes, and executes each test case in a depth first
 * traversal, toting the score of test cases.
 *
 * The class might be used for test executers aimed at other environments,
 * e.g. a GUI based version.
 *
 * Please note that this class is automagically instantiated by the main
 * method supplied in the library. This means that you might very well do all
 * your testing without directly laying hans on this fellow.
 */
class Tester : public Visitor {
	std::ostream& os;
	bool verbose; // list succeded tests
	bool line_fmt; // format output with file and line
	std::stack<res_cnt> accu;
	res_cnt n_suite, n_test;

	void Disp(Test& t, const std::string&);
	void Write(Test& t);
	void Write(Test& t, AssertionError& e);
	void Write(Test& t, RuntimeException& e);
	void Write(Test& t, int dummy);

	public:
		/**
		 * Create a text tester.
		 * \param os the stream to write results to.
		 * \param verbose whether to report on succesful tests
		 */
		Tester(std::ostream& os, bool verbose = false, bool line = false): 
			os(os), verbose(verbose), line_fmt(line) 
		{
		}
	
		// Get the score for tests
		res_cnt res_tests() 
		{
		   	return n_test; 
		}
		
		// Get the score for suites
		res_cnt res_suites() 
		{
		   	return n_suite; 
		}
		
		// Write the summary
		virtual void Summary();
		
		// Part of the Visitor pattern.
		virtual void Visit(Test&);
		
		// Part of the Visitor pattern.
		virtual void Visit(Suite&);
		
		// Part of the Visitor pattern; Visit to suite after children.
		virtual void Visit(Suite& t, int);
};

// A runner is the base class for the objects that actually processes the tests 
// from main. Main simply invokes the run_tests method of the current test runner
class test_runner {

	public:
		virtual ~test_runner()
		{
		}

		// run all the tests with arguments in the argc, argv set
		virtual bool run_tests(int argc, const char** argv) = 0;
};

// A plain test runner for the ordinary text version.
class plain_runner : public test_runner {
	public:
		// Run the tests specified in argv, starting at i
		bool run_tests(int argc, const char** argv)
		{
			bool res = true;

			res = run_test();
			return res;
		}

	private:
		// Run a test found in the suite::main() test by id. If id is empty run the main test
		bool run_test(const std::string& id = "")
		{
			Test* tp = Suite::Main().Find(id);
			if (!tp) {
				return false;
			}
			return run_test(tp);
		}

		// Run the test and return true if succesful. @see{run_test-id}
		bool run_test(Test* tp)
		{
			Tester tst(std::cout, verbose, line_fmt);
			tp->Visit(&tst);
			tst.Summary();
			res_cnt res(tst.res_tests());
			return res.n_err() == 0 && res.n_fail() == 0;
		}

};

}

#endif