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"
16 #include "wx/modalhook.h"
18 class WXDLLIMPEXP_FWD_CORE wxMessageDialogBase
;
19 class WXDLLIMPEXP_FWD_CORE wxFileDialogBase
;
21 // ----------------------------------------------------------------------------
23 // ----------------------------------------------------------------------------
25 // Don't include this code when building the library itself
28 #include "wx/beforestd.h"
32 #include "wx/afterstd.h"
34 #include "wx/dialog.h"
35 #include "wx/msgdlg.h"
36 #include "wx/filedlg.h"
38 class wxTestingModalHook
;
40 // Non-template base class for wxExpectModal<T> (via wxExpectModalBase).
41 // Only used internally.
42 class wxModalExpectation
45 wxModalExpectation() : m_isOptional(false) {}
46 virtual ~wxModalExpectation() {}
48 bool IsOptional() const { return m_isOptional
; }
50 virtual int Invoke(wxDialog
*dlg
) const = 0;
52 virtual wxString
GetDescription() const = 0;
55 // Is this dialog optional, i.e. not required to be shown?
60 // This must be specialized for each type. The specialization MUST be derived
61 // from wxExpectModalBase<T>.
62 template<class T
> class wxExpectModal
{};
66 Base class for wxExpectModal<T> specializations.
68 Every such specialization must be derived from wxExpectModalBase; there's
69 no other use for this class than to serve as wxExpectModal<T>'s base class.
71 T must be a class derived from wxDialog.
74 class wxExpectModalBase
: public wxModalExpectation
78 typedef wxExpectModal
<DialogType
> ExpectationType
;
81 Returns a copy of the expectation where the expected dialog is marked
84 Optional dialogs aren't required to appear, it's not an error if they
87 ExpectationType
Optional() const
89 ExpectationType
e(*static_cast<const ExpectationType
*>(this));
90 e
.m_isOptional
= true;
95 virtual int Invoke(wxDialog
*dlg
) const
97 DialogType
*t
= dynamic_cast<DialogType
*>(dlg
);
101 return wxID_NONE
; // not handled
104 /// Returns description of the expected dialog (by default, its class).
105 virtual wxString
GetDescription() const
107 return wxCLASSINFO(T
)->GetClassName();
111 This method is called when ShowModal() was invoked on a dialog of type T.
113 @return Return value is used as ShowModal()'s return value.
115 virtual int OnInvoked(DialogType
*dlg
) const = 0;
119 // wxExpectModal<T> specializations for common dialogs:
122 class wxExpectModal
<wxMessageDialog
> : public wxExpectModalBase
<wxMessageDialog
>
125 wxExpectModal(int id
)
151 virtual int OnInvoked(wxMessageDialog
*WXUNUSED(dlg
)) const
162 class wxExpectModal
<wxFileDialog
> : public wxExpectModalBase
<wxFileDialog
>
165 wxExpectModal(const wxString
& path
, int id
= wxID_OK
)
166 : m_path(path
), m_id(id
)
171 virtual int OnInvoked(wxFileDialog
*dlg
) const
173 dlg
->SetPath(m_path
);
183 // Implementation of wxModalDialogHook for use in testing, with
184 // wxExpectModal<T> and the wxTEST_DIALOG() macro. It is not intended for
185 // direct use, use the macro instead.
186 class wxTestingModalHook
: public wxModalDialogHook
194 // Called to verify that all expectations were met. This cannot be done in
195 // the destructor, because ReportFailure() may throw (either because it's
196 // overriden or because wx's assertions handling is, globally). And
197 // throwing from the destructor would introduce all sort of problems,
198 // including messing up the order of errors in some cases.
199 void CheckUnmetExpectations()
201 while ( !m_expectations
.empty() )
203 const wxModalExpectation
*expect
= m_expectations
.front();
204 m_expectations
.pop();
205 if ( expect
->IsOptional() )
212 "Expected %s dialog was not shown.",
213 expect
->GetDescription()
220 void AddExpectation(const wxModalExpectation
& e
)
222 m_expectations
.push(&e
);
226 virtual int Enter(wxDialog
*dlg
)
228 while ( !m_expectations
.empty() )
230 const wxModalExpectation
*expect
= m_expectations
.front();
231 m_expectations
.pop();
233 int ret
= expect
->Invoke(dlg
);
234 if ( ret
!= wxID_NONE
)
235 return ret
; // dialog shown as expected
237 // not showing an optional dialog is OK, but showing an unexpected
238 // one definitely isn't:
239 if ( !expect
->IsOptional() )
245 "A %s dialog was shown unexpectedly, expected %s.",
246 dlg
->GetClassInfo()->GetClassName(),
247 expect
->GetDescription()
252 // else: try the next expectation in the chain
259 "A dialog (%s) was shown unexpectedly.",
260 dlg
->GetClassInfo()->GetClassName()
267 virtual void ReportFailure(const wxString
& msg
)
273 std::queue
<const wxModalExpectation
*> m_expectations
;
275 wxDECLARE_NO_COPY_CLASS(wxTestingModalHook
);
279 // Redefining this value makes it possible to customize the hook class,
280 // including e.g. its error reporting.
281 #define wxTEST_DIALOG_HOOK_CLASS wxTestingModalHook
283 #define WX_TEST_IMPL_ADD_EXPECTATION(pos, expect) \
284 const wxModalExpectation& wx_exp##pos = expect; \
285 wx_hook.AddExpectation(wx_exp##pos);
288 Runs given code with all modal dialogs redirected to wxExpectModal<T>
289 hooks, instead of being shown to the user.
291 The first argument is any valid expression, typically a function call. The
292 remaining arguments are wxExpectModal<T> instances defining the dialogs
293 that are expected to be shown, in order of appearance.
295 Some typical examples:
300 rc = dlg.ShowModal(),
301 wxExpectModal<wxFileDialog>(wxGetCwd() + "/test.txt")
305 Sometimes, the code may show more than one dialog:
311 wxExpectModal<wxMessageDialog>(wxNO),
312 wxExpectModal<MyConfirmationDialog>(wxYES),
313 wxExpectModal<wxFileDialog>(wxGetCwd() + "/test.txt")
317 Notice that wxExpectModal<T> has some convenience methods for further
318 tweaking the expectations. For example, it's possible to mark an expected
319 dialog as @em optional for situations when a dialog may be shown, but isn't
320 required to, by calling the Optional() method:
326 wxExpectModal<wxMessageDialog>(wxNO),
327 wxExpectModal<wxFileDialog>(wxGetCwd() + "/test.txt").Optional()
331 @note By default, errors are reported with wxFAIL_MSG(). You may customize this by
332 implementing a class derived from wxTestingModalHook, overriding its
333 ReportFailure() method and redefining the wxTEST_DIALOG_HOOK_CLASS
334 macro to be the name of this class.
336 @note Custom dialogs are supported too. All you have to do is to specialize
337 wxExpectModal<> for your dialog type and implement its OnInvoked()
340 #ifdef wxHAS_VARIADIC_MACROS
341 #define wxTEST_DIALOG(codeToRun, ...) \
343 wxTEST_DIALOG_HOOK_CLASS wx_hook; \
344 wxCALL_FOR_EACH(WX_TEST_IMPL_ADD_EXPECTATION, __VA_ARGS__) \
346 wx_hook.CheckUnmetExpectations(); \
348 #endif /* wxHAS_VARIADIC_MACROS */
350 #endif // !WXBUILDING
352 #endif // _WX_TESTING_H_