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
207 class wxExpectModal
<wxFileDialog
> : public wxExpectModalBase
<wxFileDialog
>
210 wxExpectModal(const wxString
& path
, int id
= wxID_OK
)
211 : m_path(path
), m_id(id
)
216 virtual int OnInvoked(wxFileDialog
*dlg
) const
218 dlg
->SetPath(m_path
);
228 // Implementation of wxModalDialogHook for use in testing, with
229 // wxExpectModal<T> and the wxTEST_DIALOG() macro. It is not intended for
230 // direct use, use the macro instead.
231 class wxTestingModalHook
: public wxModalDialogHook
236 m_prevHook
= wxModalDialogHook::Set(this);
239 virtual ~wxTestingModalHook()
241 wxModalDialogHook::Set(m_prevHook
);
244 virtual int Invoke(wxDialog
*dlg
)
246 while ( !m_expectations
.empty() )
248 const wxModalExpectation
*expect
= m_expectations
.front();
249 m_expectations
.pop();
251 int ret
= expect
->Invoke(dlg
);
252 if ( ret
!= wxID_NONE
)
253 return ret
; // dialog shown as expected
255 // not showing an optional dialog is OK, but showing an unexpected
256 // one definitely isn't:
257 if ( !expect
->IsOptional() )
263 "A %s dialog was shown unexpectedly, expected %s.",
264 dlg
->GetClassInfo()->GetClassName(),
265 expect
->GetDescription()
270 // else: try the next expectation in the chain
277 "A dialog (%s) was shown unexpectedly.",
278 dlg
->GetClassInfo()->GetClassName()
284 // Called to verify that all expectations were met. This cannot be done in
285 // the destructor, because ReportFailure() may throw (either because it's
286 // overriden or because wx's assertions handling is, globally). And
287 // throwing from the destructor would introduce all sort of problems,
288 // including messing up the order of errors in some cases.
289 void CheckUnmetExpectations()
291 while ( !m_expectations
.empty() )
293 const wxModalExpectation
*expect
= m_expectations
.front();
294 m_expectations
.pop();
295 if ( expect
->IsOptional() )
302 "Expected %s dialog was not shown.",
303 expect
->GetDescription()
310 void AddExpectation(const wxModalExpectation
& e
)
312 m_expectations
.push(&e
);
316 virtual void ReportFailure(const wxString
& msg
)
322 wxModalDialogHook
*m_prevHook
;
323 std::queue
<const wxModalExpectation
*> m_expectations
;
325 wxDECLARE_NO_COPY_CLASS(wxTestingModalHook
);
329 // Redefining this value makes it possible to customize the hook class,
330 // including e.g. its error reporting.
331 #define wxTEST_DIALOG_HOOK_CLASS wxTestingModalHook
333 #define WX_TEST_IMPL_ADD_EXPECTATION(pos, expect) \
334 const wxModalExpectation& wx_exp##pos = expect; \
335 wx_hook.AddExpectation(wx_exp##pos);
338 Runs given code with all modal dialogs redirected to wxExpectModal<T>
339 hooks, instead of being shown to the user.
341 The first argument is any valid expression, typically a function call. The
342 remaining arguments are wxExpectModal<T> instances defining the dialogs
343 that are expected to be shown, in order of appearance.
345 Some typical examples:
350 rc = dlg.ShowModal(),
351 wxExpectModal<wxFileDialog>(wxGetCwd() + "/test.txt")
355 Sometimes, the code may show more than one dialog:
361 wxExpectModal<wxMessageDialog>(wxNO),
362 wxExpectModal<MyConfirmationDialog>(wxYES),
363 wxExpectModal<wxFileDialog>(wxGetCwd() + "/test.txt")
367 Notice that wxExpectModal<T> has some convenience methods for further
368 tweaking the expectations. For example, it's possible to mark an expected
369 dialog as @em optional for situations when a dialog may be shown, but isn't
370 required to, by calling the Optional() method:
376 wxExpectModal<wxMessageDialog>(wxNO),
377 wxExpectModal<wxFileDialog>(wxGetCwd() + "/test.txt").Optional()
381 @note By default, errors are reported with wxFAIL_MSG(). You may customize this by
382 implementing a class derived from wxTestingModalHook, overriding its
383 ReportFailure() method and redefining the wxTEST_DIALOG_HOOK_CLASS
384 macro to be the name of this class.
386 @note Custom dialogs are supported too. All you have to do is to specialize
387 wxExpectModal<> for your dialog type and implement its OnInvoked()
390 #define wxTEST_DIALOG(codeToRun, ...) \
392 wxTEST_DIALOG_HOOK_CLASS wx_hook; \
393 wxCALL_FOR_EACH(WX_TEST_IMPL_ADD_EXPECTATION, __VA_ARGS__) \
395 wx_hook.CheckUnmetExpectations(); \
399 #endif // !WXBUILDING
401 #endif // _WX_TESTING_H_