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"
80 class wxTestingModalHook
;
82 // Non-template base class for wxExpectModal<T> (via wxExpectModalBase).
83 // Only used internally.
84 class wxModalExpectation
87 wxModalExpectation() : m_isOptional(false) {}
88 virtual ~wxModalExpectation() {}
90 bool IsOptional() const { return m_isOptional
; }
92 virtual int Invoke(wxDialog
*dlg
) const = 0;
94 virtual wxString
GetDescription() const = 0;
97 // Is this dialog optional, i.e. not required to be shown?
102 // This must be specialized for each type. The specialization MUST be derived
103 // from wxExpectModalBase<T>.
104 template<class T
> class wxExpectModal
{};
108 Base class for wxExpectModal<T> specializations.
110 Every such specialization must be derived from wxExpectModalBase; there's
111 no other use for this class than to serve as wxExpectModal<T>'s base class.
113 T must be a class derived from wxDialog.
116 class wxExpectModalBase
: public wxModalExpectation
119 typedef T DialogType
;
120 typedef wxExpectModal
<DialogType
> ExpectationType
;
123 Returns a copy of the expectation where the expected dialog is marked
126 Optional dialogs aren't required to appear, it's not an error if they
129 ExpectationType
Optional() const
131 ExpectationType
e(*static_cast<const ExpectationType
*>(this));
132 e
.m_isOptional
= true;
137 virtual int Invoke(wxDialog
*dlg
) const
139 DialogType
*t
= dynamic_cast<DialogType
*>(dlg
);
143 return wxID_NONE
; // not handled
146 /// Returns description of the expected dialog (by default, its class).
147 virtual wxString
GetDescription() const
149 return wxCLASSINFO(T
)->GetClassName();
153 This method is called when ShowModal() was invoked on a dialog of type T.
155 @return Return value is used as ShowModal()'s return value.
157 virtual int OnInvoked(DialogType
*dlg
) const = 0;
161 // wxExpectModal<T> specializations for common dialogs:
164 class wxExpectModal
<wxMessageDialog
> : public wxExpectModalBase
<wxMessageDialog
>
167 wxExpectModal(int id
)
193 virtual int OnInvoked(wxMessageDialog
*WXUNUSED(dlg
)) const
203 class wxExpectModal
<wxFileDialog
> : public wxExpectModalBase
<wxFileDialog
>
206 wxExpectModal(const wxString
& path
, int id
= wxID_OK
)
207 : m_path(path
), m_id(id
)
212 virtual int OnInvoked(wxFileDialog
*dlg
) const
214 dlg
->SetPath(m_path
);
223 // Implementation of wxModalDialogHook for use in testing, with
224 // wxExpectModal<T> and the wxTEST_DIALOG() macro. It is not intended for
225 // direct use, use the macro instead.
226 class wxTestingModalHook
: public wxModalDialogHook
231 m_prevHook
= wxModalDialogHook::Set(this);
234 virtual ~wxTestingModalHook()
236 wxModalDialogHook::Set(m_prevHook
);
239 virtual int Invoke(wxDialog
*dlg
)
241 while ( !m_expectations
.empty() )
243 const wxModalExpectation
*expect
= m_expectations
.front();
244 m_expectations
.pop();
246 int ret
= expect
->Invoke(dlg
);
247 if ( ret
!= wxID_NONE
)
248 return ret
; // dialog shown as expected
250 // not showing an optional dialog is OK, but showing an unexpected
251 // one definitely isn't:
252 if ( !expect
->IsOptional() )
258 "A %s dialog was shown unexpectedly, expected %s.",
259 dlg
->GetClassInfo()->GetClassName(),
260 expect
->GetDescription()
265 // else: try the next expectation in the chain
272 "A dialog (%s) was shown unexpectedly.",
273 dlg
->GetClassInfo()->GetClassName()
279 // Called to verify that all expectations were met. This cannot be done in
280 // the destructor, because ReportFailure() may throw (either because it's
281 // overriden or because wx's assertions handling is, globally). And
282 // throwing from the destructor would introduce all sort of problems,
283 // including messing up the order of errors in some cases.
284 void CheckUnmetExpectations()
286 while ( !m_expectations
.empty() )
288 const wxModalExpectation
*expect
= m_expectations
.front();
289 m_expectations
.pop();
290 if ( expect
->IsOptional() )
297 "Expected %s dialog was not shown.",
298 expect
->GetDescription()
305 void AddExpectation(const wxModalExpectation
& e
)
307 m_expectations
.push(&e
);
311 virtual void ReportFailure(const wxString
& msg
)
317 wxModalDialogHook
*m_prevHook
;
318 std::queue
<const wxModalExpectation
*> m_expectations
;
320 wxDECLARE_NO_COPY_CLASS(wxTestingModalHook
);
324 // Redefining this value makes it possible to customize the hook class,
325 // including e.g. its error reporting.
326 #define wxTEST_DIALOG_HOOK_CLASS wxTestingModalHook
328 #define WX_TEST_IMPL_ADD_EXPECTATION(pos, expect) \
329 const wxModalExpectation& wx_exp##pos = expect; \
330 wx_hook.AddExpectation(wx_exp##pos);
333 Runs given code with all modal dialogs redirected to wxExpectModal<T>
334 hooks, instead of being shown to the user.
336 The first argument is any valid expression, typically a function call. The
337 remaining arguments are wxExpectModal<T> instances defining the dialogs
338 that are expected to be shown, in order of appearance.
340 Some typical examples:
345 rc = dlg.ShowModal(),
346 wxExpectModal<wxFileDialog>(wxGetCwd() + "/test.txt")
350 Sometimes, the code may show more than one dialog:
356 wxExpectModal<wxMessageDialog>(wxNO),
357 wxExpectModal<MyConfirmationDialog>(wxYES),
358 wxExpectModal<wxFileDialog>(wxGetCwd() + "/test.txt")
362 Notice that wxExpectModal<T> has some convenience methods for further
363 tweaking the expectations. For example, it's possible to mark an expected
364 dialog as @em optional for situations when a dialog may be shown, but isn't
365 required to, by calling the Optional() method:
371 wxExpectModal<wxMessageDialog>(wxNO),
372 wxExpectModal<wxFileDialog>(wxGetCwd() + "/test.txt").Optional()
376 @note By default, errors are reported with wxFAIL_MSG(). You may customize this by
377 implementing a class derived from wxTestingModalHook, overriding its
378 ReportFailure() method and redefining the wxTEST_DIALOG_HOOK_CLASS
379 macro to be the name of this class.
381 @note Custom dialogs are supported too. All you have to do is to specialize
382 wxExpectModal<> for your dialog type and implement its OnInvoked()
385 #define wxTEST_DIALOG(codeToRun, ...) \
387 wxTEST_DIALOG_HOOK_CLASS wx_hook; \
388 wxCALL_FOR_EACH(WX_TEST_IMPL_ADD_EXPECTATION, __VA_ARGS__) \
390 wx_hook.CheckUnmetExpectations(); \
394 #endif // !WXBUILDING
396 #endif // _WX_TESTING_H_