1 ///////////////////////////////////////////////////////////////////////////////
3 // Purpose: Test program for wxWidgets
4 // Author: Mike Wetherell
6 // Copyright: (c) 2004 Mike Wetherell
7 // Licence: wxWidgets licence
8 ///////////////////////////////////////////////////////////////////////////////
10 // ----------------------------------------------------------------------------
12 // ----------------------------------------------------------------------------
14 // For compilers that support precompilation, includes "wx/wx.h"
22 // for all others, include the necessary headers
27 #include "wx/beforestd.h"
29 #pragma warning(disable:4100)
32 #include <cppunit/TestListener.h>
33 #include <cppunit/Protector.h>
34 #include <cppunit/Test.h>
35 #include <cppunit/TestResult.h>
36 #include <cppunit/TestFailure.h>
39 #pragma warning(default:4100)
41 #include "wx/afterstd.h"
43 #include "wx/cmdline.h"
47 #include "wx/msw/msvcrt.h"
51 #include "wx/osx/private.h"
54 #include "wx/socket.h"
59 using CppUnit::TestSuite
;
60 using CppUnit::TestFactoryRegistry
;
63 // ----------------------------------------------------------------------------
65 // ----------------------------------------------------------------------------
67 // exception class for MSVC debug CRT assertion failures
68 #ifdef wxUSE_VC_CRTDBG
70 struct CrtAssertFailure
72 CrtAssertFailure(const char *message
) : m_msg(message
) { }
76 wxDECLARE_NO_ASSIGN_CLASS(CrtAssertFailure
);
79 #endif // wxUSE_VC_CRTDBG
83 static wxString
FormatAssertMessage(const wxString
& file
,
90 str
<< "wxWidgets assert: " << cond
<< " failed "
91 "at " << file
<< ":" << line
<< " in " << func
92 << " with message '" << msg
<< "'";
96 static void TestAssertHandler(const wxString
& file
,
102 // can't throw from other threads, die immediately
103 if ( !wxIsMainThread() )
105 wxPrintf("%s in a worker thread -- aborting.",
106 FormatAssertMessage(file
, line
, func
, cond
, msg
));
111 throw TestAssertFailure(file
, line
, func
, cond
, msg
);
114 #endif // wxDEBUG_LEVEL
116 // this function should only be called from a catch clause
117 static string
GetExceptionMessage()
126 catch ( TestAssertFailure
& e
)
128 msg
<< FormatAssertMessage(e
.m_file
, e
.m_line
, e
.m_func
,
131 #endif // wxDEBUG_LEVEL
132 #ifdef wxUSE_VC_CRTDBG
133 catch ( CrtAssertFailure
& e
)
135 msg
<< "CRT assert failure: " << e
.m_msg
;
137 #endif // wxUSE_VC_CRTDBG
138 catch ( std::exception
& e
)
140 msg
<< "std::exception: " << e
.what();
144 msg
= "Unknown exception caught.";
147 return string(msg
.mb_str());
150 // Protector adding handling of wx-specific (this includes MSVC debug CRT in
151 // this context) exceptions
152 class wxUnitTestProtector
: public CppUnit::Protector
155 virtual bool protect(const CppUnit::Functor
&functor
,
156 const CppUnit::ProtectorContext
& context
)
162 catch ( std::exception
& )
164 // cppunit deals with the standard exceptions itself, let it do as
165 // it output more details (especially for std::exception-derived
166 // CppUnit::Exception) than we do
171 reportError(context
, CppUnit::Message("Uncaught exception",
172 GetExceptionMessage()));
179 // Displays the test name before starting to execute it: this helps with
180 // diagnosing where exactly does a test crash or hang when/if it does.
181 class DetailListener
: public CppUnit::TestListener
184 DetailListener(bool doTiming
= false):
185 CppUnit::TestListener(),
190 virtual void startTest(CppUnit::Test
*test
)
192 wxPrintf(" %-60s ", test
->getName());
193 m_result
= RESULT_OK
;
197 virtual void addFailure(const CppUnit::TestFailure
& failure
)
199 m_result
= failure
.isError() ? RESULT_ERROR
: RESULT_FAIL
;
202 virtual void endTest(CppUnit::Test
* WXUNUSED(test
))
205 wxPrintf(GetResultStr(m_result
));
207 wxPrintf(" %6d ms", m_watch
.Time());
220 wxString
GetResultStr(ResultType type
) const
222 static const char *resultTypeNames
[] =
229 wxCOMPILE_TIME_ASSERT( WXSIZEOF(resultTypeNames
) == RESULT_MAX
,
230 ResultTypeNamesMismatch
);
232 return resultTypeNames
[type
];
241 typedef wxApp TestAppBase
;
243 typedef wxAppConsole TestAppBase
;
246 // The application class
248 class TestApp
: public TestAppBase
253 // standard overrides
254 virtual void OnInitCmdLine(wxCmdLineParser
& parser
);
255 virtual bool OnCmdLineParsed(wxCmdLineParser
& parser
);
256 virtual bool OnInit();
258 virtual int OnExit();
260 // used by events propagation test
261 virtual int FilterEvent(wxEvent
& event
);
262 virtual bool ProcessEvent(wxEvent
& event
);
264 void SetFilterEventFunc(FilterEventFunc f
) { m_filterEventFunc
= f
; }
265 void SetProcessEventFunc(ProcessEventFunc f
) { m_processEventFunc
= f
; }
268 void List(Test
*test
, const string
& parent
= "") const;
270 // call List() if m_list or runner.addTest() otherwise
271 void AddTest(CppUnit::TestRunner
& runner
, Test
*test
)
276 runner
.addTest(test
);
279 // command lines options/parameters
284 wxArrayString m_registries
;
287 // event handling hooks
288 FilterEventFunc m_filterEventFunc
;
289 ProcessEventFunc m_processEventFunc
;
292 IMPLEMENT_APP_NO_MAIN(TestApp
)
295 // ----------------------------------------------------------------------------
297 // ----------------------------------------------------------------------------
299 #ifdef wxUSE_VC_CRTDBG
301 static int TestCrtReportHook(int reportType
, char *message
, int *)
303 if ( reportType
!= _CRT_ASSERT
)
306 throw CrtAssertFailure(message
);
309 #endif // wxUSE_VC_CRTDBG
311 int main(int argc
, char **argv
)
313 // tests can be ran non-interactively so make sure we don't show any assert
314 // dialog boxes -- neither our own nor from MSVC debug CRT -- which would
315 // prevent them from completing
318 wxSetAssertHandler(TestAssertHandler
);
319 #endif // wxDEBUG_LEVEL
321 #ifdef wxUSE_VC_CRTDBG
322 _CrtSetReportHook(TestCrtReportHook
);
323 #endif // wxUSE_VC_CRTDBG
327 return wxEntry(argc
, argv
);
331 cerr
<< "\n" << GetExceptionMessage() << endl
;
337 extern void SetFilterEventFunc(FilterEventFunc func
)
339 wxGetApp().SetFilterEventFunc(func
);
342 extern void SetProcessEventFunc(ProcessEventFunc func
)
344 wxGetApp().SetProcessEventFunc(func
);
347 extern bool IsNetworkAvailable()
349 // NOTE: we could use wxDialUpManager here if it was in wxNet; since it's in
350 // wxCore we use a simple rough test:
352 wxSocketBase::Initialize();
355 if (!addr
.Hostname("www.google.com") || !addr
.Service("www"))
357 wxSocketBase::Shutdown();
362 sock
.SetTimeout(10); // 10 secs
363 bool online
= sock
.Connect(addr
);
365 wxSocketBase::Shutdown();
370 // helper of OnRun(): gets the test with the given name, returning NULL (and
371 // not an empty test suite) if there is no such test
372 static Test
*GetTestByName(const wxString
& name
)
375 test
= TestFactoryRegistry::getRegistry(string(name
.mb_str())).makeTest();
378 TestSuite
* const suite
= dynamic_cast<TestSuite
*>(test
);
379 if ( !suite
|| !suite
->countTestCases() )
381 // it's a bogus test, don't use it
391 // ----------------------------------------------------------------------------
393 // ----------------------------------------------------------------------------
399 m_filterEventFunc
= NULL
;
400 m_processEventFunc
= NULL
;
407 bool TestApp::OnInit()
409 if ( !TestAppBase::OnInit() )
413 cout
<< "Test program for wxWidgets GUI features\n"
415 cout
<< "Test program for wxWidgets non-GUI features\n"
417 << "build: " << WX_BUILD_OPTIONS_SIGNATURE
<< std::endl
;
420 // create a hidden parent window to be used as parent for the GUI controls
421 new wxFrame(NULL
, wxID_ANY
, "Hidden wx test frame");
427 // The table of command line options
429 void TestApp::OnInitCmdLine(wxCmdLineParser
& parser
)
431 TestAppBase::OnInitCmdLine(parser
);
433 static const wxCmdLineEntryDesc cmdLineDesc
[] = {
434 { wxCMD_LINE_SWITCH
, "l", "list",
435 "list the test suites, do not run them",
436 wxCMD_LINE_VAL_NONE
, 0 },
437 { wxCMD_LINE_SWITCH
, "L", "longlist",
438 "list the test cases, do not run them",
439 wxCMD_LINE_VAL_NONE
, 0 },
440 { wxCMD_LINE_SWITCH
, "d", "detail",
441 "print the test case names, run them",
442 wxCMD_LINE_VAL_NONE
, 0 },
443 { wxCMD_LINE_SWITCH
, "t", "timing",
444 "print names and mesure running time of individual test, run them",
445 wxCMD_LINE_VAL_NONE
, 0 },
446 { wxCMD_LINE_OPTION
, "", "locale",
447 "locale to use when running the program",
448 wxCMD_LINE_VAL_STRING
, 0 },
449 { wxCMD_LINE_PARAM
, NULL
, NULL
, "REGISTRY", wxCMD_LINE_VAL_STRING
,
450 wxCMD_LINE_PARAM_OPTIONAL
| wxCMD_LINE_PARAM_MULTIPLE
},
454 parser
.SetDesc(cmdLineDesc
);
457 // Handle command line options
459 bool TestApp::OnCmdLineParsed(wxCmdLineParser
& parser
)
461 if (parser
.GetParamCount())
463 for (size_t i
= 0; i
< parser
.GetParamCount(); i
++)
464 m_registries
.push_back(parser
.GetParam(i
));
467 m_longlist
= parser
.Found("longlist");
468 m_list
= m_longlist
|| parser
.Found("list");
469 m_timing
= parser
.Found("timing");
470 m_detail
= !m_timing
&& parser
.Found("detail");
473 if ( parser
.Found("locale", &loc
) )
475 const wxLanguageInfo
* const info
= wxLocale::FindLanguageInfo(loc
);
478 cerr
<< "Locale \"" << string(loc
.mb_str()) << "\" is unknown.\n";
482 m_locale
= new wxLocale(info
->Language
);
483 if ( !m_locale
->IsOk() )
485 cerr
<< "Using locale \"" << string(loc
.mb_str()) << "\" failed.\n";
490 return TestAppBase::OnCmdLineParsed(parser
);
494 int TestApp::FilterEvent(wxEvent
& event
)
496 if ( m_filterEventFunc
)
497 return (*m_filterEventFunc
)(event
);
499 return TestAppBase::FilterEvent(event
);
502 bool TestApp::ProcessEvent(wxEvent
& event
)
504 if ( m_processEventFunc
)
505 return (*m_processEventFunc
)(event
);
507 return TestAppBase::ProcessEvent(event
);
516 // make sure there's always an autorelease pool ready
517 wxMacAutoreleasePool autoreleasepool
;
522 // Switch off logging unless --verbose
523 bool verbose
= wxLog::GetVerbose();
524 wxLog::EnableLogging(verbose
);
526 bool verbose
= false;
529 CppUnit::TextTestRunner runner
;
531 if ( m_registries
.empty() )
533 // run or list all tests
534 AddTest(runner
, TestFactoryRegistry::getRegistry().makeTest());
536 else // run only the selected tests
538 for (size_t i
= 0; i
< m_registries
.size(); i
++)
540 const wxString reg
= m_registries
[i
];
541 Test
*test
= GetTestByName(reg
);
543 if ( !test
&& !reg
.EndsWith("TestCase") )
545 test
= GetTestByName(reg
+ "TestCase");
550 cerr
<< "No such test suite: " << string(reg
.mb_str()) << endl
;
554 AddTest(runner
, test
);
561 runner
.setOutputter(new CppUnit::CompilerOutputter(&runner
.result(), cout
));
564 // (http://sf.net/tracker/index.php?func=detail&aid=1649369&group_id=11795&atid=111795)
565 // in some versions of cppunit: they write progress dots to cout (and not
566 // cerr) and don't flush it so all the dots appear at once at the end which
567 // is not very useful so unbuffer cout to work around this
568 cout
.setf(ios::unitbuf
);
570 // add detail listener if needed
571 DetailListener
detailListener(m_timing
);
572 if ( m_detail
|| m_timing
)
573 runner
.eventManager().addListener(&detailListener
);
575 // finally ensure that we report our own exceptions nicely instead of
576 // giving "uncaught exception of unknown type" messages
577 runner
.eventManager().pushProtector(new wxUnitTestProtector
);
579 bool printProgress
= !(verbose
|| m_detail
|| m_timing
);
580 return runner
.run("", false, true, printProgress
) ? EXIT_SUCCESS
: EXIT_FAILURE
;
583 int TestApp::OnExit()
588 delete GetTopWindow();
596 void TestApp::List(Test
*test
, const string
& parent
/*=""*/) const
598 TestSuite
*suite
= dynamic_cast<TestSuite
*>(test
);
602 // take the last component of the name and append to the parent
603 name
= test
->getName();
604 string::size_type i
= name
.find_last_of(".:");
605 if (i
!= string::npos
)
606 name
= name
.substr(i
+ 1);
607 name
= parent
+ "." + name
;
609 // drop the 1st component from the display and indent
611 string::size_type j
= i
= name
.find('.', 1);
612 while ((j
= name
.find('.', j
+ 1)) != string::npos
)
614 cout
<< " " << name
.substr(i
+ 1) << "\n";
617 typedef vector
<Test
*> Tests
;
618 typedef Tests::const_iterator Iter
;
620 const Tests
& tests
= suite
->getTests();
622 for (Iter it
= tests
.begin(); it
!= tests
.end(); ++it
)
625 else if (m_longlist
) {
626 string::size_type i
= 0;
627 while ((i
= parent
.find('.', i
+ 1)) != string::npos
)
629 cout
<< " " << test
->getName() << "\n";