1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: test.cpp
3 // Purpose: Test program for wxWidgets
4 // Author: Mike Wetherell
5 // Copyright: (c) 2004 Mike Wetherell
6 // Licence: wxWindows licence
7 ///////////////////////////////////////////////////////////////////////////////
8
9 // ----------------------------------------------------------------------------
10 // headers
11 // ----------------------------------------------------------------------------
12
13 // For compilers that support precompilation, includes "wx/wx.h"
14 // and "catch.hpp"
15 #include "testprec.h"
16
17
18 // Suppress some warnings in catch_impl.hpp.
19 wxCLANG_WARNING_SUPPRESS(missing-braces)
20 wxCLANG_WARNING_SUPPRESS(logical-op-parentheses)
21 wxCLANG_WARNING_SUPPRESS(inconsistent-missing-override)
22
23 // This file needs to get the CATCH definitions in addition to the usual
24 // assertion macros declarations from catch.hpp included by testprec.h.
25 // Including an internal file like this is ugly, but there doesn't seem to be
26 // any better way, see https://github.com/philsquared/Catch/issues/1061
27 #include "internal/catch_impl.hpp"
28
29 wxCLANG_WARNING_RESTORE(missing-braces)
30 wxCLANG_WARNING_RESTORE(logical-op-parentheses)
31 wxCLANG_WARNING_RESTORE(inconsistent-missing-override)
32
33 // This probably could be done by predefining CLARA_CONFIG_MAIN, but at the
34 // point where we are, just define this global variable manually.
35 namespace Catch { namespace Clara { UnpositionalTag _; } }
36
37 // Also define our own global variables.
38 namespace wxPrivate
39 {
40 std::string wxTheCurrentTestClass, wxTheCurrentTestMethod;
41 }
42
43 // for all others, include the necessary headers
44 #ifndef WX_PRECOMP
45 #include "wx/wx.h"
46 #endif
47
48 #include "wx/apptrait.h"
49 #include "wx/cmdline.h"
50 #include <exception>
51 #include <iostream>
52
53 #ifdef __WINDOWS__
54 #include "wx/msw/msvcrt.h"
55 #endif
56
57 #ifdef __WXOSX__
58 #include "wx/osx/private.h"
59 #endif
60
61 #if wxUSE_GUI
62 #include "testableframe.h"
63
64 #ifdef __WXGTK__
65 #include <glib.h>
66 #endif // __WXGTK__
67 #endif // wxUSE_GUI
68
69 #include "wx/socket.h"
70 #include "wx/evtloop.h"
71
72 using namespace std;
73
74 // ----------------------------------------------------------------------------
75 // helper classes
76 // ----------------------------------------------------------------------------
77
78 // exception class for MSVC debug CRT assertion failures
79 #ifdef wxUSE_VC_CRTDBG
80
81 struct CrtAssertFailure
82 {
CrtAssertFailureCrtAssertFailure83 CrtAssertFailure(const char *message) : m_msg(message) { }
84
85 const wxString m_msg;
86
87 wxDECLARE_NO_ASSIGN_CLASS(CrtAssertFailure);
88 };
89
CATCH_TRANSLATE_EXCEPTION(CrtAssertFailure & e)90 CATCH_TRANSLATE_EXCEPTION(CrtAssertFailure& e)
91 {
92 return "CRT assert failure: " + e.m_msg.ToStdString(wxConvUTF8);
93 }
94
95 #endif // wxUSE_VC_CRTDBG
96
97 #if wxDEBUG_LEVEL
98
99 // Information about the last not yet handled assertion.
100 static wxString s_lastAssertMessage;
101
FormatAssertMessage(const wxString & file,int line,const wxString & func,const wxString & cond,const wxString & msg)102 static wxString FormatAssertMessage(const wxString& file,
103 int line,
104 const wxString& func,
105 const wxString& cond,
106 const wxString& msg)
107 {
108 wxString str;
109 str << wxASCII_STR("wxWidgets assert: ") << cond
110 << wxASCII_STR(" failed at ") << file << wxASCII_STR(":") << line
111 << wxASCII_STR(" in ") << func << wxASCII_STR(" with message '")
112 << msg << wxASCII_STR("'");
113 return str;
114 }
115
TestAssertHandler(const wxString & file,int line,const wxString & func,const wxString & cond,const wxString & msg)116 static void TestAssertHandler(const wxString& file,
117 int line,
118 const wxString& func,
119 const wxString& cond,
120 const wxString& msg)
121 {
122 // Determine whether we can safely throw an exception to just make the test
123 // fail or whether we need to abort (in this case "msg" will contain the
124 // explanation why did we decide to do it).
125 wxString abortReason;
126
127 const wxString
128 assertMessage = FormatAssertMessage(file, line, func, cond, msg);
129
130 if ( !wxIsMainThread() )
131 {
132 // Exceptions thrown from worker threads are not caught currently and
133 // so we'd just die without any useful information -- abort instead.
134 abortReason << assertMessage << wxASCII_STR(" in a worker thread.");
135 }
136 #if __cplusplus >= 201703L || wxCHECK_VISUALC_VERSION(14)
137 else if ( uncaught_exceptions() )
138 #else
139 else if ( uncaught_exception() )
140 #endif
141 {
142 // Throwing while already handling an exception would result in
143 // terminate() being called and we wouldn't get any useful information
144 // about why the test failed then.
145 if ( s_lastAssertMessage.empty() )
146 {
147 abortReason << assertMessage
148 << wxASCII_STR("while handling an exception");
149 }
150 else // In this case the exception is due to a previous assert.
151 {
152 abortReason << s_lastAssertMessage
153 << wxASCII_STR("\n and another ") << assertMessage
154 << wxASCII_STR(" while handling it.");
155 }
156 }
157 else // Can "safely" throw from here.
158 {
159 // Remember this in case another assert happens while handling this
160 // exception: we want to show the original assert as it's usually more
161 // useful to determine the real root of the problem.
162 s_lastAssertMessage = assertMessage;
163
164 throw TestAssertFailure(file, line, func, cond, msg);
165 }
166
167 #if wxUSE_STACKWALKER
168 const wxString& stackTrace = wxApp::GetValidTraits().GetAssertStackTrace();
169 if ( !stackTrace.empty() )
170 abortReason << wxASCII_STR("\n\nAssert call stack:\n") << stackTrace;
171 #endif // wxUSE_STACKWALKER
172
173 wxFputs(abortReason, stderr);
174 fflush(stderr);
175 _exit(-1);
176 }
177
CATCH_TRANSLATE_EXCEPTION(TestAssertFailure & e)178 CATCH_TRANSLATE_EXCEPTION(TestAssertFailure& e)
179 {
180 wxString desc = e.m_msg;
181 if ( desc.empty() )
182 desc.Printf(wxASCII_STR("Condition \"%s\" failed"), e.m_cond);
183
184 desc += wxString::Format(wxASCII_STR(" in %s() at %s:%d"), e.m_func, e.m_file, e.m_line);
185
186 return desc.ToStdString(wxConvUTF8);
187 }
188
189 #endif // wxDEBUG_LEVEL
190
191 #if wxUSE_GUI
192 typedef wxApp TestAppBase;
193 typedef wxGUIAppTraits TestAppTraitsBase;
194 #else
195 typedef wxAppConsole TestAppBase;
196 typedef wxConsoleAppTraits TestAppTraitsBase;
197 #endif
198
199 // The application class
200 //
201 class TestApp : public TestAppBase
202 {
203 public:
204 TestApp();
205
206 // standard overrides
207 virtual bool OnInit() wxOVERRIDE;
208 virtual int OnExit() wxOVERRIDE;
209
210 #ifdef __WIN32__
CreateTraits()211 virtual wxAppTraits *CreateTraits() wxOVERRIDE
212 {
213 // Define a new class just to customize CanUseStderr() behaviour.
214 class TestAppTraits : public TestAppTraitsBase
215 {
216 public:
217 // We want to always use stderr, tests are also run unattended and
218 // in this case we really don't want to show any message boxes, as
219 // wxMessageOutputBest, used e.g. from the default implementation
220 // of wxApp::OnUnhandledException(), would do by default.
221 virtual bool CanUseStderr() wxOVERRIDE { return true; }
222
223 // Overriding CanUseStderr() is not enough, we also need to
224 // override this one to avoid returning false from it.
225 virtual bool WriteToStderr(const wxString& text) wxOVERRIDE
226 {
227 wxFputs(text, stderr);
228 fflush(stderr);
229
230 // Intentionally ignore any errors, we really don't want to
231 // show any message boxes in any case.
232 return true;
233 }
234 };
235
236 return new TestAppTraits;
237 }
238 #endif // __WIN32__
239
240 // Also override this method to avoid showing any dialogs from here -- and
241 // show some details about the exception along the way.
OnExceptionInMainLoop()242 virtual bool OnExceptionInMainLoop() wxOVERRIDE
243 {
244 wxFprintf(stderr, wxASCII_STR("Unhandled exception in the main loop: %s\n"),
245 wxASCII_STR(Catch::translateActiveException().c_str()));
246
247 throw;
248 }
249
250 // used by events propagation test
251 virtual int FilterEvent(wxEvent& event) wxOVERRIDE;
252 virtual bool ProcessEvent(wxEvent& event) wxOVERRIDE;
253
SetFilterEventFunc(FilterEventFunc f)254 void SetFilterEventFunc(FilterEventFunc f) { m_filterEventFunc = f; }
SetProcessEventFunc(ProcessEventFunc f)255 void SetProcessEventFunc(ProcessEventFunc f) { m_processEventFunc = f; }
256
257 // In console applications we run the tests directly from the overridden
258 // OnRun(), but in the GUI ones we run them when we get the first call to
259 // our EVT_IDLE handler to ensure that we do everything from inside the
260 // main event loop. This is especially important under wxOSX/Cocoa where
261 // the main event loop is different from the others but it's also safer to
262 // do it like this in the other ports as we test the GUI code in the same
263 // context as it's used usually, in normal programs, and it might behave
264 // differently without the event loop.
265 #if wxUSE_GUI
OnIdle(wxIdleEvent & event)266 void OnIdle(wxIdleEvent& event)
267 {
268 if ( m_runTests )
269 {
270 m_runTests = false;
271
272 #ifdef __WXOSX__
273 // we need to wait until the window is activated and fully ready
274 // otherwise no events can be posted
275 wxEventLoopBase* const loop = wxEventLoop::GetActive();
276 if ( loop )
277 {
278 loop->DispatchTimeout(1000);
279 loop->Yield();
280 }
281 #endif // __WXOSX__
282
283 m_exitcode = RunTests();
284 ExitMainLoop();
285 }
286
287 event.Skip();
288 }
289
OnRun()290 virtual int OnRun() wxOVERRIDE
291 {
292 if ( TestAppBase::OnRun() != 0 )
293 m_exitcode = EXIT_FAILURE;
294
295 return m_exitcode;
296 }
297 #else // !wxUSE_GUI
OnRun()298 virtual int OnRun() wxOVERRIDE
299 {
300 return RunTests();
301 }
302 #endif // wxUSE_GUI/!wxUSE_GUI
303
304 private:
305 int RunTests();
306
307 // flag telling us whether we should run tests from our EVT_IDLE handler
308 bool m_runTests;
309
310 // event handling hooks
311 FilterEventFunc m_filterEventFunc;
312 ProcessEventFunc m_processEventFunc;
313
314 #if wxUSE_GUI
315 // the program exit code
316 int m_exitcode;
317 #endif // wxUSE_GUI
318 };
319
320 wxIMPLEMENT_APP_NO_MAIN(TestApp);
321
322
323 // ----------------------------------------------------------------------------
324 // global functions
325 // ----------------------------------------------------------------------------
326
327 #ifdef wxUSE_VC_CRTDBG
328
TestCrtReportHook(int reportType,char * message,int *)329 static int TestCrtReportHook(int reportType, char *message, int *)
330 {
331 if ( reportType != _CRT_ASSERT )
332 return FALSE;
333
334 throw CrtAssertFailure(message);
335 }
336
337 #endif // wxUSE_VC_CRTDBG
338
main(int argc,char ** argv)339 int main(int argc, char **argv)
340 {
341 // tests can be ran non-interactively so make sure we don't show any assert
342 // dialog boxes -- neither our own nor from MSVC debug CRT -- which would
343 // prevent them from completing
344
345 #if wxDEBUG_LEVEL
346 wxSetAssertHandler(TestAssertHandler);
347 #endif // wxDEBUG_LEVEL
348
349 #ifdef wxUSE_VC_CRTDBG
350 _CrtSetReportHook(TestCrtReportHook);
351 #endif // wxUSE_VC_CRTDBG
352
353 return wxEntry(argc, argv);
354 }
355
SetFilterEventFunc(FilterEventFunc func)356 extern void SetFilterEventFunc(FilterEventFunc func)
357 {
358 wxGetApp().SetFilterEventFunc(func);
359 }
360
SetProcessEventFunc(ProcessEventFunc func)361 extern void SetProcessEventFunc(ProcessEventFunc func)
362 {
363 wxGetApp().SetProcessEventFunc(func);
364 }
365
IsNetworkAvailable()366 extern bool IsNetworkAvailable()
367 {
368 // Somehow even though network is available on Travis CI build machines,
369 // attempts to open remote URIs sporadically fail, so don't run these tests
370 // under Travis to avoid false positives.
371 static int s_isTravis = -1;
372 if ( s_isTravis == -1 )
373 s_isTravis = wxGetEnv(wxASCII_STR("TRAVIS"), NULL);
374
375 if ( s_isTravis )
376 return false;
377
378 // NOTE: we could use wxDialUpManager here if it was in wxNet; since it's in
379 // wxCore we use a simple rough test:
380
381 wxSocketBase::Initialize();
382
383 wxIPV4address addr;
384 if (!addr.Hostname(wxASCII_STR("www.google.com")) || !addr.Service(wxASCII_STR("www")))
385 {
386 wxSocketBase::Shutdown();
387 return false;
388 }
389
390 wxSocketClient sock;
391 sock.SetTimeout(10); // 10 secs
392 bool online = sock.Connect(addr);
393
394 wxSocketBase::Shutdown();
395
396 return online;
397 }
398
IsAutomaticTest()399 extern bool IsAutomaticTest()
400 {
401 static int s_isAutomatic = -1;
402 if ( s_isAutomatic == -1 )
403 {
404 // Allow setting an environment variable to emulate buildslave user for
405 // testing.
406 wxString username;
407 if ( !wxGetEnv(wxASCII_STR("WX_TEST_USER"), &username) )
408 username = wxGetUserId();
409
410 username.MakeLower();
411 s_isAutomatic = username == wxASCII_STR("buildbot") ||
412 username.Matches(wxASCII_STR("sandbox*"));
413
414 // Also recognize various CI environments.
415 if ( !s_isAutomatic )
416 {
417 s_isAutomatic = wxGetEnv(wxASCII_STR("TRAVIS"), NULL) ||
418 wxGetEnv(wxASCII_STR("GITHUB_ACTIONS"), NULL) ||
419 wxGetEnv(wxASCII_STR("APPVEYOR"), NULL);
420 }
421 }
422
423 return s_isAutomatic == 1;
424 }
425
IsRunningUnderXVFB()426 extern bool IsRunningUnderXVFB()
427 {
428 static int s_isRunningUnderXVFB = -1;
429 if ( s_isRunningUnderXVFB == -1 )
430 {
431 wxString value;
432 s_isRunningUnderXVFB = wxGetEnv(wxASCII_STR("wxUSE_XVFB"), &value) && value == wxASCII_STR("1");
433 }
434
435 return s_isRunningUnderXVFB == 1;
436 }
437
438 #ifdef __LINUX__
439
IsRunningInLXC()440 extern bool IsRunningInLXC()
441 {
442 // We're supposed to be able to detect running in LXC by checking for
443 // /dev/lxd existency, but this doesn't work under Travis for some reason,
444 // so just rely on having the environment variable defined for the
445 // corresponding builds in our .travis.yml.
446 wxString value;
447 return wxGetEnv("wxLXC", &value) && value == "1";
448 }
449
450 #endif // __LINUX__
451
452 #if wxUSE_GUI
453
EnableUITests()454 bool EnableUITests()
455 {
456 static int s_enabled = -1;
457 if ( s_enabled == -1 )
458 {
459 // Allow explicitly configuring this via an environment variable under
460 // all platforms.
461 wxString enabled;
462 if ( wxGetEnv(wxASCII_STR("WX_UI_TESTS"), &enabled) )
463 {
464 if ( enabled == wxASCII_STR("1") )
465 s_enabled = 1;
466 else if ( enabled == wxASCII_STR("0") )
467 s_enabled = 0;
468 else
469 wxFprintf(stderr, wxASCII_STR("Unknown \"WX_UI_TESTS\" value \"%s\" ignored.\n"), enabled);
470 }
471
472 if ( s_enabled == -1 )
473 {
474 #if defined(__WXMSW__) || defined(__WXGTK__)
475 s_enabled = 1;
476 #else // !(__WXMSW__ || __WXGTK__)
477 s_enabled = 0;
478 #endif // (__WXMSW__ || __WXGTK__)
479 }
480 }
481
482 return s_enabled == 1;
483 }
484
DeleteTestWindow(wxWindow * win)485 void DeleteTestWindow(wxWindow* win)
486 {
487 if ( !win )
488 return;
489
490 wxWindow* const capture = wxWindow::GetCapture();
491 if ( capture )
492 {
493 if ( capture == win ||
494 capture->GetMainWindowOfCompositeControl() == win )
495 capture->ReleaseMouse();
496 }
497
498 delete win;
499 }
500
501 #ifdef __WXGTK__
502
503 #ifdef GDK_WINDOWING_X11
504
505 #include "X11/Xlib.h"
506
507 extern "C"
wxTestX11ErrorHandler(Display *,XErrorEvent *)508 int wxTestX11ErrorHandler(Display*, XErrorEvent*)
509 {
510 fprintf(stderr, "\n*** X11 error while running %s(): ",
511 wxGetCurrentTestName().c_str());
512 return 0;
513 }
514
515 #endif // GDK_WINDOWING_X11
516
517 extern "C"
518 void
wxTestGLogHandler(const gchar * domain,GLogLevelFlags level,const gchar * message,gpointer data)519 wxTestGLogHandler(const gchar* domain,
520 GLogLevelFlags level,
521 const gchar* message,
522 gpointer data)
523 {
524 // Check if debug messages in this domain will be logged.
525 if ( level == G_LOG_LEVEL_DEBUG )
526 {
527 static const char* const allowed = getenv("G_MESSAGES_DEBUG");
528
529 // By default debug messages are dropped, but if G_MESSAGES_DEBUG is
530 // defined, they're logged for the domains specified in it and if it
531 // has the special value "all", then all debug messages are shown.
532 //
533 // Note that the check here can result in false positives, e.g. domain
534 // "foo" would pass it even if G_MESSAGES_DEBUG only contains "foobar",
535 // but such cases don't seem to be important enough to bother
536 // accounting for them.
537 if ( !allowed ||
538 (strcmp(allowed, "all") != 0 && !strstr(allowed, domain)) )
539 {
540 return;
541 }
542 }
543
544 fprintf(stderr, "\n*** GTK log message while running %s(): ",
545 wxGetCurrentTestName().c_str());
546
547 g_log_default_handler(domain, level, message, data);
548 }
549
550 #endif // __WXGTK__
551
552 #endif // wxUSE_GUI
553
554 // ----------------------------------------------------------------------------
555 // TestApp
556 // ----------------------------------------------------------------------------
557
TestApp()558 TestApp::TestApp()
559 {
560 m_runTests = true;
561
562 m_filterEventFunc = NULL;
563 m_processEventFunc = NULL;
564
565 #if wxUSE_GUI
566 m_exitcode = EXIT_SUCCESS;
567 #endif // wxUSE_GUI
568 }
569
570 // Init
571 //
OnInit()572 bool TestApp::OnInit()
573 {
574 // Hack: don't call TestAppBase::OnInit() to let CATCH handle command line.
575
576 // Output some important information about the test environment.
577 #if wxUSE_GUI
578 cout << "Test program for wxWidgets GUI features\n"
579 #else
580 cout << "Test program for wxWidgets non-GUI features\n"
581 #endif
582 << "build: " << WX_BUILD_OPTIONS_SIGNATURE << "\n"
583 << "running under " << wxGetOsDescription()
584 << " as " << wxGetUserId()
585 << ", locale is " << setlocale(LC_ALL, NULL)
586 << std::endl;
587
588 #if wxUSE_GUI
589 // create a parent window to be used as parent for the GUI controls
590 new wxTestableFrame();
591
592 Connect(wxEVT_IDLE, wxIdleEventHandler(TestApp::OnIdle));
593
594 #ifdef __WXGTK20__
595 g_log_set_default_handler(wxTestGLogHandler, NULL);
596 #endif // __WXGTK__
597
598 #ifdef GDK_WINDOWING_X11
599 XSetErrorHandler(wxTestX11ErrorHandler);
600 #endif // GDK_WINDOWING_X11
601
602 #endif // wxUSE_GUI
603
604 return true;
605 }
606
607 // Event handling
FilterEvent(wxEvent & event)608 int TestApp::FilterEvent(wxEvent& event)
609 {
610 if ( m_filterEventFunc )
611 return (*m_filterEventFunc)(event);
612
613 return TestAppBase::FilterEvent(event);
614 }
615
ProcessEvent(wxEvent & event)616 bool TestApp::ProcessEvent(wxEvent& event)
617 {
618 if ( m_processEventFunc )
619 return (*m_processEventFunc)(event);
620
621 return TestAppBase::ProcessEvent(event);
622 }
623
624 // Run
625 //
RunTests()626 int TestApp::RunTests()
627 {
628 #if wxUSE_LOG
629 // Switch off logging to avoid interfering with the tests output unless
630 // WXTRACE is set, as otherwise setting it would have no effect while
631 // running the tests.
632 if ( !wxGetEnv(wxASCII_STR("WXTRACE"), NULL) )
633 wxLog::EnableLogging(false);
634 else
635 wxLog::SetTimestamp("%Y-%m-%d %H:%M:%S.%l");
636 #endif
637
638 // Cast is needed under MSW where Catch also provides an overload taking
639 // wchar_t, but as it simply converts arguments to char internally anyhow,
640 // we can just as well always use the char version.
641 return Catch::Session().run(argc, static_cast<char**>(argv));
642 }
643
OnExit()644 int TestApp::OnExit()
645 {
646 #if wxUSE_GUI
647 delete GetTopWindow();
648 #endif // wxUSE_GUI
649
650 return TestAppBase::OnExit();
651 }
652