1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: helpers for GUI testing
4 // Author: Vaclav Slavik
6 // Copyright: (c) 2012 Vaclav Slavik
7 // Licence: wxWindows Licence
8 /////////////////////////////////////////////////////////////////////////////
10 #ifndef _WX_TESTING_H_
11 #define _WX_TESTING_H_
14 #include "wx/string.h"
15 #include "wx/modalhook.h"
17 class WXDLLIMPEXP_FWD_CORE wxMessageDialogBase
;
18 class WXDLLIMPEXP_FWD_CORE wxFileDialogBase
;
20 // ----------------------------------------------------------------------------
22 // ----------------------------------------------------------------------------
24 // Don't include this code when building the library itself
27 #include "wx/beforestd.h"
31 #include "wx/afterstd.h"
33 #include "wx/dialog.h"
34 #include "wx/msgdlg.h"
35 #include "wx/filedlg.h"
37 class wxTestingModalHook
;
39 // Non-template base class for wxExpectModal<T> (via wxExpectModalBase).
40 // Only used internally.
41 class wxModalExpectation
44 wxModalExpectation() : m_isOptional(false) {}
45 virtual ~wxModalExpectation() {}
47 bool IsOptional() const { return m_isOptional
; }
49 virtual int Invoke(wxDialog
*dlg
) const = 0;
51 virtual wxString
GetDescription() const = 0;
54 // Is this dialog optional, i.e. not required to be shown?
59 // This must be specialized for each type. The specialization MUST be derived
60 // from wxExpectModalBase<T>.
61 template<class T
> class wxExpectModal
{};
65 Base class for wxExpectModal<T> specializations.
67 Every such specialization must be derived from wxExpectModalBase; there's
68 no other use for this class than to serve as wxExpectModal<T>'s base class.
70 T must be a class derived from wxDialog.
73 class wxExpectModalBase
: public wxModalExpectation
77 typedef wxExpectModal
<DialogType
> ExpectationType
;
80 Returns a copy of the expectation where the expected dialog is marked
83 Optional dialogs aren't required to appear, it's not an error if they
86 ExpectationType
Optional() const
88 ExpectationType
e(*static_cast<const ExpectationType
*>(this));
89 e
.m_isOptional
= true;
94 virtual int Invoke(wxDialog
*dlg
) const
96 DialogType
*t
= dynamic_cast<DialogType
*>(dlg
);
100 return wxID_NONE
; // not handled
103 /// Returns description of the expected dialog (by default, its class).
104 virtual wxString
GetDescription() const
106 return wxCLASSINFO(T
)->GetClassName();
110 This method is called when ShowModal() was invoked on a dialog of type T.
112 @return Return value is used as ShowModal()'s return value.
114 virtual int OnInvoked(DialogType
*dlg
) const = 0;
118 // wxExpectModal<T> specializations for common dialogs:
121 class wxExpectModal
<wxMessageDialog
> : public wxExpectModalBase
<wxMessageDialog
>
124 wxExpectModal(int id
)
150 virtual int OnInvoked(wxMessageDialog
*WXUNUSED(dlg
)) const
161 class wxExpectModal
<wxFileDialog
> : public wxExpectModalBase
<wxFileDialog
>
164 wxExpectModal(const wxString
& path
, int id
= wxID_OK
)
165 : m_path(path
), m_id(id
)
170 virtual int OnInvoked(wxFileDialog
*dlg
) const
172 dlg
->SetPath(m_path
);
182 // Implementation of wxModalDialogHook for use in testing, with
183 // wxExpectModal<T> and the wxTEST_DIALOG() macro. It is not intended for
184 // direct use, use the macro instead.
185 class wxTestingModalHook
: public wxModalDialogHook
193 // Called to verify that all expectations were met. This cannot be done in
194 // the destructor, because ReportFailure() may throw (either because it's
195 // overriden or because wx's assertions handling is, globally). And
196 // throwing from the destructor would introduce all sort of problems,
197 // including messing up the order of errors in some cases.
198 void CheckUnmetExpectations()
200 while ( !m_expectations
.empty() )
202 const wxModalExpectation
*expect
= m_expectations
.front();
203 m_expectations
.pop();
204 if ( expect
->IsOptional() )
211 "Expected %s dialog was not shown.",
212 expect
->GetDescription()
219 void AddExpectation(const wxModalExpectation
& e
)
221 m_expectations
.push(&e
);
225 virtual int Enter(wxDialog
*dlg
)
227 while ( !m_expectations
.empty() )
229 const wxModalExpectation
*expect
= m_expectations
.front();
230 m_expectations
.pop();
232 int ret
= expect
->Invoke(dlg
);
233 if ( ret
!= wxID_NONE
)
234 return ret
; // dialog shown as expected
236 // not showing an optional dialog is OK, but showing an unexpected
237 // one definitely isn't:
238 if ( !expect
->IsOptional() )
244 "A %s dialog was shown unexpectedly, expected %s.",
245 dlg
->GetClassInfo()->GetClassName(),
246 expect
->GetDescription()
251 // else: try the next expectation in the chain
258 "A dialog (%s) was shown unexpectedly.",
259 dlg
->GetClassInfo()->GetClassName()
266 virtual void ReportFailure(const wxString
& msg
)
272 std::queue
<const wxModalExpectation
*> m_expectations
;
274 wxDECLARE_NO_COPY_CLASS(wxTestingModalHook
);
278 // Redefining this value makes it possible to customize the hook class,
279 // including e.g. its error reporting.
280 #define wxTEST_DIALOG_HOOK_CLASS wxTestingModalHook
282 #define WX_TEST_IMPL_ADD_EXPECTATION(pos, expect) \
283 const wxModalExpectation& wx_exp##pos = expect; \
284 wx_hook.AddExpectation(wx_exp##pos);
287 Runs given code with all modal dialogs redirected to wxExpectModal<T>
288 hooks, instead of being shown to the user.
290 The first argument is any valid expression, typically a function call. The
291 remaining arguments are wxExpectModal<T> instances defining the dialogs
292 that are expected to be shown, in order of appearance.
294 Some typical examples:
299 rc = dlg.ShowModal(),
300 wxExpectModal<wxFileDialog>(wxGetCwd() + "/test.txt")
304 Sometimes, the code may show more than one dialog:
310 wxExpectModal<wxMessageDialog>(wxNO),
311 wxExpectModal<MyConfirmationDialog>(wxYES),
312 wxExpectModal<wxFileDialog>(wxGetCwd() + "/test.txt")
316 Notice that wxExpectModal<T> has some convenience methods for further
317 tweaking the expectations. For example, it's possible to mark an expected
318 dialog as @em optional for situations when a dialog may be shown, but isn't
319 required to, by calling the Optional() method:
325 wxExpectModal<wxMessageDialog>(wxNO),
326 wxExpectModal<wxFileDialog>(wxGetCwd() + "/test.txt").Optional()
330 @note By default, errors are reported with wxFAIL_MSG(). You may customize this by
331 implementing a class derived from wxTestingModalHook, overriding its
332 ReportFailure() method and redefining the wxTEST_DIALOG_HOOK_CLASS
333 macro to be the name of this class.
335 @note Custom dialogs are supported too. All you have to do is to specialize
336 wxExpectModal<> for your dialog type and implement its OnInvoked()
339 #ifdef HAVE_VARIADIC_MACROS
340 #define wxTEST_DIALOG(codeToRun, ...) \
342 wxTEST_DIALOG_HOOK_CLASS wx_hook; \
343 wxCALL_FOR_EACH(WX_TEST_IMPL_ADD_EXPECTATION, __VA_ARGS__) \
345 wx_hook.CheckUnmetExpectations(); \
347 #endif /* HAVE_VARIADIC_MACROS */
349 #endif // !WXBUILDING
351 #endif // _WX_TESTING_H_