diff options
| -rw-r--r-- | indra/test/test.cpp | 191 | 
1 files changed, 120 insertions, 71 deletions
| diff --git a/indra/test/test.cpp b/indra/test/test.cpp index ffdb0cb976..1adcfb6f45 100644 --- a/indra/test/test.cpp +++ b/indra/test/test.cpp @@ -37,6 +37,7 @@  #include "linden_common.h"  #include "llerrorcontrol.h"  #include "lltut.h" +#include "stringize.h"  #include "apr_pools.h"  #include "apr_getopt.h" @@ -53,6 +54,21 @@  #include <gtest/gtest.h>  #endif +#if LL_MSVC +#pragma warning (push) +#pragma warning (disable : 4702) // warning C4702: unreachable code +#endif +#include <boost/iostreams/tee.hpp> +#include <boost/iostreams/stream.hpp> +#if LL_MSVC +#pragma warning (pop) +#endif + +#include <boost/shared_ptr.hpp> +#include <boost/make_shared.hpp> +#include <boost/foreach.hpp> +#include <boost/lambda/lambda.hpp> +  namespace tut  {  	std::string sSourceDir; @@ -69,8 +85,24 @@ public:  	mPassedTests(0),  	mFailedTests(0),  	mSkippedTests(0), -	mStream(stream) +	// By default, capture a shared_ptr to std::cout, with a no-op "deleter" +	// so that destroying the shared_ptr makes no attempt to delete std::cout. +	mStream(boost::shared_ptr<std::ostream>(&std::cout, boost::lambda::_1))  	{ +		if (stream) +		{ +			// We want a boost::iostreams::tee_device that will stream to two +			// std::ostreams. +			typedef boost::iostreams::tee_device<std::ostream, std::ostream> TeeDevice; +			// More than that, though, we want an actual stream using that +			// device. +			typedef boost::iostreams::stream<TeeDevice> TeeStream; +			// Allocate and assign in two separate steps, per Herb Sutter. +			// (Until we turn on C++11 support, have to wrap *stream with +			// boost::ref() due to lack of perfect forwarding.) +			boost::shared_ptr<std::ostream> pstream(new TeeStream(std::cout, boost::ref(*stream))); +			mStream = pstream; +		}  	}  	~LLTestCallback() @@ -94,7 +126,10 @@ public:  	{  		++mTotalTests;  		std::ostringstream out; -		out << "[" << tr.group << ", " << tr.test << "] "; +		out << "[" << tr.group << ", " << tr.test; +		if (! tr.name.empty()) +			out << ": " << tr.name; +		out << "] ";  		switch(tr.result)  		{  			case tut::test_result::ok: @@ -123,56 +158,43 @@ public:  				break;  			default:  				++mFailedTests; -				out << "unknown"; +				out << "unknown (tr.result == " << tr.result << ")";  		}  		if(mVerboseMode || (tr.result != tut::test_result::ok))  		{ +			*mStream << out.str();  			if(!tr.message.empty())  			{ -				out << ": '" << tr.message << "'"; +				*mStream << ": '" << tr.message << "'";  			} -			if (mStream) -			{ -				*mStream << out.str() << std::endl; -			} -			 -			std::cout << out.str() << std::endl; +			*mStream << std::endl;  		}  	} -	virtual void run_completed() -	{ -		if (mStream) -		{ -			run_completed_(*mStream); -		} -		run_completed_(std::cout); -	} -  	virtual int getFailedTests() const { return mFailedTests; } -	virtual void run_completed_(std::ostream &stream) +	virtual void run_completed()  	{ -		stream << "\tTotal Tests:\t" << mTotalTests << std::endl; -		stream << "\tPassed Tests:\t" << mPassedTests; +		*mStream << "\tTotal Tests:\t" << mTotalTests << std::endl; +		*mStream << "\tPassed Tests:\t" << mPassedTests;  		if (mPassedTests == mTotalTests)  		{ -			stream << "\tYAY!! \\o/"; +			*mStream << "\tYAY!! \\o/";  		} -		stream << std::endl; +		*mStream << std::endl;  		if (mSkippedTests > 0)  		{ -			stream << "\tSkipped known failures:\t" << mSkippedTests +			*mStream << "\tSkipped known failures:\t" << mSkippedTests  			<< std::endl;  		}  		if(mFailedTests > 0)  		{ -			stream << "*********************************" << std::endl; -			stream << "Failed Tests:\t" << mFailedTests << std::endl; -			stream << "Please report or fix the problem." << std::endl; -			stream << "*********************************" << std::endl; +			*mStream << "*********************************" << std::endl; +			*mStream << "Failed Tests:\t" << mFailedTests << std::endl; +			*mStream << "Please report or fix the problem." << std::endl; +			*mStream << "*********************************" << std::endl;  		}  	} @@ -182,7 +204,7 @@ protected:  	int mPassedTests;  	int mFailedTests;  	int mSkippedTests; -	std::ostream *mStream; +	boost::shared_ptr<std::ostream> mStream;  };  // TeamCity specific class which emits service messages @@ -192,84 +214,111 @@ class LLTCTestCallback : public LLTestCallback  {  public:  	LLTCTestCallback(bool verbose_mode, std::ostream *stream) : -		LLTestCallback(verbose_mode, stream), -		mTCStream() +		LLTestCallback(verbose_mode, stream)  	{  	}  	~LLTCTestCallback()  	{ -	}	 +	}  	virtual void group_started(const std::string& name) {  		LLTestCallback::group_started(name); -		mTCStream << "\n##teamcity[testSuiteStarted name='" << name << "']" << std::endl; +		std::cout << "\n##teamcity[testSuiteStarted name='" << escape(name) << "']" << std::endl;  	}  	virtual void group_completed(const std::string& name) {  		LLTestCallback::group_completed(name); -		mTCStream << "##teamcity[testSuiteFinished name='" << name << "']" << std::endl; +		std::cout << "##teamcity[testSuiteFinished name='" << escape(name) << "']" << std::endl;  	}  	virtual void test_completed(const tut::test_result& tr)  	{ +		std::string testname(STRINGIZE(tr.group << "." << tr.test)); +		if (! tr.name.empty()) +		{ +			testname.append(":"); +			testname.append(tr.name); +		} +		testname = escape(testname); + +		// Sadly, tut::callback doesn't give us control at test start; have to +		// backfill start message into TC output. +		std::cout << "##teamcity[testStarted name='" << testname << "']" << std::endl; + +		// now forward call to base class so any output produced there is in +		// the right TC context  		LLTestCallback::test_completed(tr);  		switch(tr.result)  		{  			case tut::test_result::ok: -				mTCStream << "##teamcity[testStarted name='" << tr.group << "." << tr.test << "']" << std::endl; -				mTCStream << "##teamcity[testFinished name='" << tr.group << "." << tr.test << "']" << std::endl;  				break; +  			case tut::test_result::fail: -				mTCStream << "##teamcity[testStarted name='" << tr.group << "." << tr.test << "']" << std::endl; -				mTCStream << "##teamcity[testFailed name='" << tr.group << "." << tr.test << "' message='" << tr.message << "']" << std::endl; -				mTCStream << "##teamcity[testFinished name='" << tr.group << "." << tr.test << "']" << std::endl; -				break;  			case tut::test_result::ex: -				mTCStream << "##teamcity[testStarted name='" << tr.group << "." << tr.test << "']" << std::endl; -				mTCStream << "##teamcity[testFailed name='" << tr.group << "." << tr.test << "' message='" << tr.message << "']" << std::endl; -				mTCStream << "##teamcity[testFinished name='" << tr.group << "." << tr.test << "']" << std::endl; -				break;  			case tut::test_result::warn: -				mTCStream << "##teamcity[testStarted name='" << tr.group << "." << tr.test << "']" << std::endl; -				mTCStream << "##teamcity[testFailed name='" << tr.group << "." << tr.test << "' message='" << tr.message << "']" << std::endl; -				mTCStream << "##teamcity[testFinished name='" << tr.group << "." << tr.test << "']" << std::endl; -				break;  			case tut::test_result::term: -				mTCStream << "##teamcity[testStarted name='" << tr.group << "." << tr.test << "']" << std::endl; -				mTCStream << "##teamcity[testFailed name='" << tr.group << "." << tr.test << "' message='" << tr.message << "']" << std::endl; -				mTCStream << "##teamcity[testFinished name='" << tr.group << "." << tr.test << "']" << std::endl; +				std::cout << "##teamcity[testFailed name='" << testname +						  << "' message='" << escape(tr.message) << "']" << std::endl;  				break; +  			case tut::test_result::skip: -				mTCStream << "##teamcity[testStarted name='" << tr.group << "." << tr.test << "']" << std::endl; -				mTCStream << "##teamcity[testIgnored name='" << tr.group << "." << tr.test << "']" << std::endl; -				mTCStream << "##teamcity[testFinished name='" << tr.group << "." << tr.test << "']" << std::endl; +				std::cout << "##teamcity[testIgnored name='" << testname << "']" << std::endl;  				break; +  			default:  				break;  		} +		std::cout << "##teamcity[testFinished name='" << testname << "']" << std::endl;  	} -	virtual void run_completed() -	{ -		LLTestCallback::run_completed(); - -		// dump the TC reporting results to cout -		tc_run_completed_(std::cout); -	} - -	virtual void tc_run_completed_(std::ostream &stream) +	static std::string escape(const std::string& str)  	{ -		 -		// dump the TC reporting results to cout -		stream << mTCStream.str() << std::endl; +		// Per http://confluence.jetbrains.net/display/TCD65/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-ServiceMessages +		std::string result; +		BOOST_FOREACH(char c, str) +		{ +			switch (c) +			{ +			case '\'': +				result.append("|'"); +				break; +			case '\n': +				result.append("|n"); +				break; +			case '\r': +				result.append("|r"); +				break; +/*==========================================================================*| +			// These are not possible 'char' values from a std::string. +			case '\u0085':			// next line +				result.append("|x"); +				break; +			case '\u2028':			// line separator +				result.append("|l"); +				break; +			case '\u2029':			// paragraph separator +				result.append("|p"); +				break; +|*==========================================================================*/ +			case '|': +				result.append("||"); +				break; +			case '[': +				result.append("|["); +				break; +			case ']': +				result.append("|]"); +				break; +			default: +				result.push_back(c); +				break; +			} +		} +		return result;  	} -	 -protected: -	std::ostringstream mTCStream; -  }; | 
