PageRenderTime 63ms CodeModel.GetById 24ms app.highlight 33ms RepoModel.GetById 2ms app.codeStats 0ms

/indra/test/test.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 486 lines | 374 code | 59 blank | 53 comment | 26 complexity | 13b1ecd3f0954571e84c64fff6dffda5 MD5 | raw file
  1/** 
  2 * @file test.cpp
  3 * @author Phoenix
  4 * @date 2005-09-26
  5 * @brief Entry point for the test app.
  6 *
  7 * $LicenseInfo:firstyear=2005&license=viewerlgpl$
  8 * Second Life Viewer Source Code
  9 * Copyright (C) 2010, Linden Research, Inc.
 10 * 
 11 * This library is free software; you can redistribute it and/or
 12 * modify it under the terms of the GNU Lesser General Public
 13 * License as published by the Free Software Foundation;
 14 * version 2.1 of the License only.
 15 * 
 16 * This library is distributed in the hope that it will be useful,
 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 19 * Lesser General Public License for more details.
 20 * 
 21 * You should have received a copy of the GNU Lesser General Public
 22 * License along with this library; if not, write to the Free Software
 23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 24 * 
 25 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 26 * $/LicenseInfo$
 27 */
 28
 29/** 
 30 *
 31 * You can add tests by creating a new cpp file in this directory, and
 32 * rebuilding. There are at most 50 tests per testgroup without a
 33 * little bit of template parameter and makefile tweaking.
 34 *
 35 */
 36
 37#include "linden_common.h"
 38#include "llerrorcontrol.h"
 39#include "lltut.h"
 40
 41#include "apr_pools.h"
 42#include "apr_getopt.h"
 43
 44// the CTYPE_WORKAROUND is needed for linux dev stations that don't
 45// have the broken libc6 packages needed by our out-of-date static 
 46// libs (such as libcrypto and libcurl). -- Leviathan 20060113
 47#ifdef CTYPE_WORKAROUND
 48#	include "ctype_workaround.h"
 49#endif
 50
 51#ifndef LL_WINDOWS
 52#include <gmock/gmock.h>
 53#include <gtest/gtest.h>
 54#endif
 55
 56namespace tut
 57{
 58	std::string sSourceDir;
 59	
 60    test_runner_singleton runner;
 61}
 62
 63class LLTestCallback : public tut::callback
 64{
 65public:
 66	LLTestCallback(bool verbose_mode, std::ostream *stream) :
 67	mVerboseMode(verbose_mode),
 68	mTotalTests(0),
 69	mPassedTests(0),
 70	mFailedTests(0),
 71	mSkippedTests(0),
 72	mStream(stream)
 73	{
 74	}
 75
 76	~LLTestCallback()
 77	{
 78	}	
 79
 80	virtual void run_started()
 81	{
 82		//std::cout << "run_started" << std::endl;
 83	}
 84
 85	virtual void group_started(const std::string& name) {
 86		std::cout << "Unit test group_started name=" << name << std::endl;
 87	}
 88
 89	virtual void group_completed(const std::string& name) {
 90		std::cout << "Unit test group_completed name=" << name << std::endl;
 91	}
 92
 93	virtual void test_completed(const tut::test_result& tr)
 94	{
 95		++mTotalTests;
 96		std::ostringstream out;
 97		out << "[" << tr.group << ", " << tr.test << "] ";
 98		switch(tr.result)
 99		{
100			case tut::test_result::ok:
101				++mPassedTests;
102				out << "ok";
103				break;
104			case tut::test_result::fail:
105				++mFailedTests;
106				out << "fail";
107				break;
108			case tut::test_result::ex:
109				++mFailedTests;
110				out << "exception";
111				break;
112			case tut::test_result::warn:
113				++mFailedTests;
114				out << "test destructor throw";
115				break;
116			case tut::test_result::term:
117				++mFailedTests;
118				out << "abnormal termination";
119				break;
120			case tut::test_result::skip:
121				++mSkippedTests;			
122				out << "skipped known failure";
123				break;
124			default:
125				++mFailedTests;
126				out << "unknown";
127		}
128		if(mVerboseMode || (tr.result != tut::test_result::ok))
129		{
130			if(!tr.message.empty())
131			{
132				out << ": '" << tr.message << "'";
133			}
134			if (mStream)
135			{
136				*mStream << out.str() << std::endl;
137			}
138			
139			std::cout << out.str() << std::endl;
140		}
141	}
142
143	virtual void run_completed()
144	{
145		if (mStream)
146		{
147			run_completed_(*mStream);
148		}
149		run_completed_(std::cout);
150	}
151
152	virtual int getFailedTests() const { return mFailedTests; }
153
154	virtual void run_completed_(std::ostream &stream)
155	{
156		stream << "\tTotal Tests:\t" << mTotalTests << std::endl;
157		stream << "\tPassed Tests:\t" << mPassedTests;
158		if (mPassedTests == mTotalTests)
159		{
160			stream << "\tYAY!! \\o/";
161		}
162		stream << std::endl;
163
164		if (mSkippedTests > 0)
165		{
166			stream << "\tSkipped known failures:\t" << mSkippedTests
167			<< std::endl;
168		}
169
170		if(mFailedTests > 0)
171		{
172			stream << "*********************************" << std::endl;
173			stream << "Failed Tests:\t" << mFailedTests << std::endl;
174			stream << "Please report or fix the problem." << std::endl;
175			stream << "*********************************" << std::endl;
176		}
177	}
178
179protected:
180	bool mVerboseMode;
181	int mTotalTests;
182	int mPassedTests;
183	int mFailedTests;
184	int mSkippedTests;
185	std::ostream *mStream;
186};
187
188// TeamCity specific class which emits service messages
189// http://confluence.jetbrains.net/display/TCD3/Build+Script+Interaction+with+TeamCity;#BuildScriptInteractionwithTeamCity-testReporting
190
191class LLTCTestCallback : public LLTestCallback
192{
193public:
194	LLTCTestCallback(bool verbose_mode, std::ostream *stream) :
195		LLTestCallback(verbose_mode, stream),
196		mTCStream()
197	{
198	}
199
200	~LLTCTestCallback()
201	{
202	}	
203
204	virtual void group_started(const std::string& name) {
205		LLTestCallback::group_started(name);
206		mTCStream << "\n##teamcity[testSuiteStarted name='" << name << "']" << std::endl;
207	}
208
209	virtual void group_completed(const std::string& name) {
210		LLTestCallback::group_completed(name);
211		mTCStream << "##teamcity[testSuiteFinished name='" << name << "']" << std::endl;
212	}
213
214	virtual void test_completed(const tut::test_result& tr)
215	{
216		LLTestCallback::test_completed(tr);
217
218		switch(tr.result)
219		{
220			case tut::test_result::ok:
221				mTCStream << "##teamcity[testStarted name='" << tr.group << "." << tr.test << "']" << std::endl;
222				mTCStream << "##teamcity[testFinished name='" << tr.group << "." << tr.test << "']" << std::endl;
223				break;
224			case tut::test_result::fail:
225				mTCStream << "##teamcity[testStarted name='" << tr.group << "." << tr.test << "']" << std::endl;
226				mTCStream << "##teamcity[testFailed name='" << tr.group << "." << tr.test << "' message='" << tr.message << "']" << std::endl;
227				mTCStream << "##teamcity[testFinished name='" << tr.group << "." << tr.test << "']" << std::endl;
228				break;
229			case tut::test_result::ex:
230				mTCStream << "##teamcity[testStarted name='" << tr.group << "." << tr.test << "']" << std::endl;
231				mTCStream << "##teamcity[testFailed name='" << tr.group << "." << tr.test << "' message='" << tr.message << "']" << std::endl;
232				mTCStream << "##teamcity[testFinished name='" << tr.group << "." << tr.test << "']" << std::endl;
233				break;
234			case tut::test_result::warn:
235				mTCStream << "##teamcity[testStarted name='" << tr.group << "." << tr.test << "']" << std::endl;
236				mTCStream << "##teamcity[testFailed name='" << tr.group << "." << tr.test << "' message='" << tr.message << "']" << std::endl;
237				mTCStream << "##teamcity[testFinished name='" << tr.group << "." << tr.test << "']" << std::endl;
238				break;
239			case tut::test_result::term:
240				mTCStream << "##teamcity[testStarted name='" << tr.group << "." << tr.test << "']" << std::endl;
241				mTCStream << "##teamcity[testFailed name='" << tr.group << "." << tr.test << "' message='" << tr.message << "']" << std::endl;
242				mTCStream << "##teamcity[testFinished name='" << tr.group << "." << tr.test << "']" << std::endl;
243				break;
244			case tut::test_result::skip:
245				mTCStream << "##teamcity[testStarted name='" << tr.group << "." << tr.test << "']" << std::endl;
246				mTCStream << "##teamcity[testIgnored name='" << tr.group << "." << tr.test << "']" << std::endl;
247				mTCStream << "##teamcity[testFinished name='" << tr.group << "." << tr.test << "']" << std::endl;
248				break;
249			default:
250				break;
251		}
252
253	}
254
255	virtual void run_completed()
256	{
257		LLTestCallback::run_completed();
258
259		// dump the TC reporting results to cout
260		tc_run_completed_(std::cout);
261	}
262
263	virtual void tc_run_completed_(std::ostream &stream)
264	{
265		
266		// dump the TC reporting results to cout
267		stream << mTCStream.str() << std::endl;
268	}
269	
270protected:
271	std::ostringstream mTCStream;
272
273};
274
275
276static const apr_getopt_option_t TEST_CL_OPTIONS[] =
277{
278	{"help", 'h', 0, "Print the help message."},
279	{"list", 'l', 0, "List available test groups."},
280	{"verbose", 'v', 0, "Verbose output."},
281	{"group", 'g', 1, "Run test group specified by option argument."},
282	{"output", 'o', 1, "Write output to the named file."},
283	{"sourcedir", 's', 1, "Project source file directory from CMake."},
284	{"touch", 't', 1, "Touch the given file if all tests succeed"},
285	{"wait", 'w', 0, "Wait for input before exit."},
286	{"debug", 'd', 0, "Emit full debug logs."},
287	{"suitename", 'x', 1, "Run tests using this suitename"},
288	{0, 0, 0, 0}
289};
290
291void stream_usage(std::ostream& s, const char* app)
292{
293	s << "Usage: " << app << " [OPTIONS]" << std::endl
294	<< std::endl;
295
296	s << "This application runs the unit tests." << std::endl << std::endl;
297
298	s << "Options: " << std::endl;
299	const apr_getopt_option_t* option = &TEST_CL_OPTIONS[0];
300	while(option->name)
301	{
302		s << "  ";
303		s << "  -" << (char)option->optch << ", --" << option->name
304		<< std::endl;
305		s << "\t" << option->description << std::endl << std::endl;
306		++option;
307	}
308
309	s << "Examples:" << std::endl;
310	s << "  " << app << " --verbose" << std::endl;
311	s << "\tRun all the tests and report all results." << std::endl;
312	s << "  " << app << " --list" << std::endl;
313	s << "\tList all available test groups." << std::endl;
314	s << "  " << app << " --group=uuid" << std::endl;
315	s << "\tRun the test group 'uuid'." << std::endl;
316}
317
318void stream_groups(std::ostream& s, const char* app)
319{
320	s << "Registered test groups:" << std::endl;
321	tut::groupnames gl = tut::runner.get().list_groups();
322	tut::groupnames::const_iterator it = gl.begin();
323	tut::groupnames::const_iterator end = gl.end();
324	for(; it != end; ++it)
325	{
326		s << "  " << *(it) << std::endl;
327	}
328}
329
330void wouldHaveCrashed(const std::string& message)
331{
332	tut::fail("llerrs message: " + message);
333}
334
335int main(int argc, char **argv)
336{
337	// The following line must be executed to initialize Google Mock
338	// (and Google Test) before running the tests.
339#ifndef LL_WINDOWS
340	::testing::InitGoogleMock(&argc, argv);
341#endif
342	LLError::initForApplication(".");
343	LLError::setFatalFunction(wouldHaveCrashed);
344	LLError::setDefaultLevel(LLError::LEVEL_ERROR);
345	//< *TODO: should come from error config file. Note that we
346	// have a command line option that sets this to debug.
347
348#ifdef CTYPE_WORKAROUND
349	ctype_workaround();
350#endif
351
352	apr_initialize();
353	apr_pool_t* pool = NULL;
354	if(APR_SUCCESS != apr_pool_create(&pool, NULL))
355	{
356		std::cerr << "Unable to initialize pool" << std::endl;
357		return 1;
358	}
359	apr_getopt_t* os = NULL;
360	if(APR_SUCCESS != apr_getopt_init(&os, pool, argc, argv))
361	{
362		std::cerr << "Unable to  pool" << std::endl;
363		return 1;
364	}
365
366	// values used for controlling application
367	bool verbose_mode = false;
368	bool wait_at_exit = false;
369	std::string test_group;
370	std::string suite_name;
371
372	// values use for options parsing
373	apr_status_t apr_err;
374	const char* opt_arg = NULL;
375	int opt_id = 0;
376	std::ofstream *output = NULL;
377	const char *touch = NULL;
378
379	while(true)
380	{
381		apr_err = apr_getopt_long(os, TEST_CL_OPTIONS, &opt_id, &opt_arg);
382		if(APR_STATUS_IS_EOF(apr_err)) break;
383		if(apr_err)
384		{
385			char buf[255];		/* Flawfinder: ignore */
386			std::cerr << "Error parsing options: "
387			<< apr_strerror(apr_err, buf, 255) << std::endl;
388			return 1;
389		}
390		switch (opt_id)
391		{
392			case 'g':
393				test_group.assign(opt_arg);
394				break;
395			case 'h':
396				stream_usage(std::cout, argv[0]);
397				return 0;
398				break;
399			case 'l':
400				stream_groups(std::cout, argv[0]);
401				return 0;
402			case 'v':
403				verbose_mode = true;
404				break;
405			case 'o':
406				output = new std::ofstream;
407				output->open(opt_arg);
408				break;
409			case 's':	// --sourcedir
410				tut::sSourceDir = opt_arg;
411				// For convenience, so you can use tut::sSourceDir + "myfile"
412				tut::sSourceDir += '/';
413				break;
414			case 't':
415				touch = opt_arg;
416				break;
417			case 'w':
418				wait_at_exit = true;
419				break;
420			case 'd':
421				// *TODO: should come from error config file. We set it to
422				// ERROR by default, so this allows full debug levels.
423				LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
424				break;
425			case 'x':
426				suite_name.assign(opt_arg);
427				break;
428			default:
429				stream_usage(std::cerr, argv[0]);
430				return 1;
431				break;
432		}
433	}
434
435	// run the tests
436
437	LLTestCallback* mycallback;
438	if (getenv("TEAMCITY_PROJECT_NAME"))
439	{
440		mycallback = new LLTCTestCallback(verbose_mode, output);		
441	}
442	else
443	{
444		mycallback = new LLTestCallback(verbose_mode, output);
445	}
446
447	tut::runner.get().set_callback(mycallback);
448
449	if(test_group.empty())
450	{
451		tut::runner.get().run_tests();
452	}
453	else
454	{
455		tut::runner.get().run_tests(test_group);
456	}
457
458	bool success = (mycallback->getFailedTests() == 0);
459
460	if (wait_at_exit)
461	{
462		std::cerr << "Press return to exit..." << std::endl;
463		std::cin.get();
464	}
465
466	if (output)
467	{
468		output->close();
469		delete output;
470	}
471
472	if (touch && success)
473	{
474		std::ofstream s;
475		s.open(touch);
476		s << "ok" << std::endl;
477		s.close();
478	}
479
480	apr_terminate();
481
482	int retval = (success ? 0 : 1);
483	return retval;
484
485	//delete mycallback;
486}