1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: helpers for GUI testing
4 // Author: Vaclav Slavik
7 // Copyright: (c) 2012 Vaclav Slavik
8 // Licence: wxWindows Licence
9 /////////////////////////////////////////////////////////////////////////////
11 #ifndef _WX_TESTING_H_
12 #define _WX_TESTING_H_
15 #include "wx/string.h"
17 class WXDLLIMPEXP_FWD_CORE wxDialog
;
18 class WXDLLIMPEXP_FWD_CORE wxMessageDialogBase
;
19 class WXDLLIMPEXP_FWD_CORE wxFileDialogBase
;
21 // ----------------------------------------------------------------------------
22 // implementation helpers
23 // ----------------------------------------------------------------------------
25 // Helper hook class used to redirect ShowModal() to testing code.
26 // Instead of showing a dialog modally, hook code is called to simulate what
27 // the user would do and return appropriate ID from ShowModal().
28 class WXDLLIMPEXP_CORE wxModalDialogHook
31 wxModalDialogHook() {}
32 virtual ~wxModalDialogHook() {}
34 /// Returns currently active hook object or NULL.
35 static wxModalDialogHook
*Get() { return ms_instance
; }
37 /// Set the hook and returns the previously set one.
38 static wxModalDialogHook
*Set(wxModalDialogHook
*hook
)
40 wxModalDialogHook
*old
= ms_instance
;
45 /// Entry point that is called from ShowModal().
46 virtual int Invoke(wxDialog
*dlg
) = 0;
49 static wxModalDialogHook
*ms_instance
;
51 wxDECLARE_NO_COPY_CLASS(wxModalDialogHook
);
54 // This macro needs to be used at the top of every implementation of
55 // ShowModal() in order for the above modal dialogs testing code to work.
56 #define WX_TESTING_SHOW_MODAL_HOOK() \
57 if ( wxModalDialogHook::Get() ) \
59 int rc = wxModalDialogHook::Get()->Invoke(this); \
60 if ( rc != wxID_NONE ) \
63 struct wxDummyTestingStruct /* just to force a semicolon */
66 // ----------------------------------------------------------------------------
68 // ----------------------------------------------------------------------------
70 // Don't include this code when building the library itself
73 #include "wx/beforestd.h"
77 #include "wx/afterstd.h"
79 #include "wx/dialog.h"
80 #include "wx/msgdlg.h"
81 #include "wx/filedlg.h"
83 class wxTestingModalHook
;
85 // Non-template base class for wxExpectModal<T> (via wxExpectModalBase).
86 // Only used internally.
87 class wxModalExpectation
90 wxModalExpectation() : m_isOptional(false) {}
91 virtual ~wxModalExpectation() {}
93 bool IsOptional() const { return m_isOptional
; }
95 virtual int Invoke(wxDialog
*dlg
) const = 0;
97 virtual wxString
GetDescription() const = 0;
100 // Is this dialog optional, i.e. not required to be shown?
105 // This must be specialized for each type. The specialization MUST be derived
106 // from wxExpectModalBase<T>.
107 template<class T
> class wxExpectModal
{};
111 Base class for wxExpectModal<T> specializations.
113 Every such specialization must be derived from wxExpectModalBase; there's
114 no other use for this class than to serve as wxExpectModal<T>'s base class.
116 T must be a class derived from wxDialog.
119 class wxExpectModalBase
: public wxModalExpectation
122 typedef T DialogType
;
123 typedef wxExpectModal
<DialogType
> ExpectationType
;
126 Returns a copy of the expectation where the expected dialog is marked
129 Optional dialogs aren't required to appear, it's not an error if they
132 ExpectationType
Optional() const
134 ExpectationType
e(*static_cast<const ExpectationType
*>(this));
135 e
.m_isOptional
= true;
140 virtual int Invoke(wxDialog
*dlg
) const
142 DialogType
*t
= dynamic_cast<DialogType
*>(dlg
);
146 return wxID_NONE
; // not handled
149 /// Returns description of the expected dialog (by default, its class).
150 virtual wxString
GetDescription() const
152 return wxCLASSINFO(T
)->GetClassName();
156 This method is called when ShowModal() was invoked on a dialog of type T.
158 @return Return value is used as ShowModal()'s return value.
160 virtual int OnInvoked(DialogType
*dlg
) const = 0;
164 // wxExpectModal<T> specializations for common dialogs:
167 class wxExpectModal
<wxMessageDialog
> : public wxExpectModalBase
<wxMessageDialog
>
170 wxExpectModal(int id
)
196 virtual int OnInvoked(wxMessageDialog
*WXUNUSED(dlg
)) const
206 class wxExpectModal
<wxFileDialog
> : public wxExpectModalBase
<wxFileDialog
>
209 wxExpectModal(const wxString
& path
, int id
= wxID_OK
)
210 : m_path(path
), m_id(id
)
215 virtual int OnInvoked(wxFileDialog
*dlg
) const
217 dlg
->SetPath(m_path
);
226 // Implementation of wxModalDialogHook for use in testing, with
227 // wxExpectModal<T> and the wxTEST_DIALOG() macro. It is not intended for
228 // direct use, use the macro instead.
229 class wxTestingModalHook
: public wxModalDialogHook
234 m_prevHook
= wxModalDialogHook::Set(this);
237 virtual ~wxTestingModalHook()
239 wxModalDialogHook::Set(m_prevHook
);
242 virtual int Invoke(wxDialog
*dlg
)
244 while ( !m_expectations
.empty() )
246 const wxModalExpectation
*expect
= m_expectations
.front();
247 m_expectations
.pop();
249 int ret
= expect
->Invoke(dlg
);
250 if ( ret
!= wxID_NONE
)
251 return ret
; // dialog shown as expected
253 // not showing an optional dialog is OK, but showing an unexpected
254 // one definitely isn't:
255 if ( !expect
->IsOptional() )
261 "A %s dialog was shown unexpectedly, expected %s.",
262 dlg
->GetClassInfo()->GetClassName(),
263 expect
->GetDescription()
268 // else: try the next expectation in the chain
275 "A dialog (%s) was shown unexpectedly.",
276 dlg
->GetClassInfo()->GetClassName()
282 // Called to verify that all expectations were met. This cannot be done in
283 // the destructor, because ReportFailure() may throw (either because it's
284 // overriden or because wx's assertions handling is, globally). And
285 // throwing from the destructor would introduce all sort of problems,
286 // including messing up the order of errors in some cases.
287 void CheckUnmetExpectations()
289 while ( !m_expectations
.empty() )
291 const wxModalExpectation
*expect
= m_expectations
.front();
292 m_expectations
.pop();
293 if ( expect
->IsOptional() )
300 "Expected %s dialog was not shown.",
301 expect
->GetDescription()
308 void AddExpectation(const wxModalExpectation
& e
)
310 m_expectations
.push(&e
);
314 virtual void ReportFailure(const wxString
& msg
)
320 wxModalDialogHook
*m_prevHook
;
321 std::queue
<const wxModalExpectation
*> m_expectations
;
323 wxDECLARE_NO_COPY_CLASS(wxTestingModalHook
);
327 // Redefining this value makes it possible to customize the hook class,
328 // including e.g. its error reporting.
329 #define wxTEST_DIALOG_HOOK_CLASS wxTestingModalHook
331 #define WX_TEST_IMPL_ADD_EXPECTATION(pos, expect) \
332 const wxModalExpectation& wx_exp##pos = expect; \
333 wx_hook.AddExpectation(wx_exp##pos);
336 Runs given code with all modal dialogs redirected to wxExpectModal<T>
337 hooks, instead of being shown to the user.
339 The first argument is any valid expression, typically a function call. The
340 remaining arguments are wxExpectModal<T> instances defining the dialogs
341 that are expected to be shown, in order of appearance.
343 Some typical examples:
348 rc = dlg.ShowModal(),
349 wxExpectModal<wxFileDialog>(wxGetCwd() + "/test.txt")
353 Sometimes, the code may show more than one dialog:
359 wxExpectModal<wxMessageDialog>(wxNO),
360 wxExpectModal<MyConfirmationDialog>(wxYES),
361 wxExpectModal<wxFileDialog>(wxGetCwd() + "/test.txt")
365 Notice that wxExpectModal<T> has some convenience methods for further
366 tweaking the expectations. For example, it's possible to mark an expected
367 dialog as @em optional for situations when a dialog may be shown, but isn't
368 required to, by calling the Optional() method:
374 wxExpectModal<wxMessageDialog>(wxNO),
375 wxExpectModal<wxFileDialog>(wxGetCwd() + "/test.txt").Optional()
379 @note By default, errors are reported with wxFAIL_MSG(). You may customize this by
380 implementing a class derived from wxTestingModalHook, overriding its
381 ReportFailure() method and redefining the wxTEST_DIALOG_HOOK_CLASS
382 macro to be the name of this class.
384 @note Custom dialogs are supported too. All you have to do is to specialize
385 wxExpectModal<> for your dialog type and implement its OnInvoked()
388 #define wxTEST_DIALOG(codeToRun, ...) \
390 wxTEST_DIALOG_HOOK_CLASS wx_hook; \
391 wxCALL_FOR_EACH(WX_TEST_IMPL_ADD_EXPECTATION, __VA_ARGS__) \
393 wx_hook.CheckUnmetExpectations(); \
397 #endif // !WXBUILDING
399 #endif // _WX_TESTING_H_