]> git.saurik.com Git - wxWidgets.git/blob - include/wx/testing.h
wxMessageBox off the main thread lost result code.
[wxWidgets.git] / include / wx / testing.h
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: wx/testing.h
3 // Purpose: helpers for GUI testing
4 // Author: Vaclav Slavik
5 // Created: 2012-08-28
6 // Copyright: (c) 2012 Vaclav Slavik
7 // Licence: wxWindows Licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 #ifndef _WX_TESTING_H_
11 #define _WX_TESTING_H_
12
13 #include "wx/debug.h"
14 #include "wx/string.h"
15 #include "wx/modalhook.h"
16
17 class WXDLLIMPEXP_FWD_CORE wxMessageDialogBase;
18 class WXDLLIMPEXP_FWD_CORE wxFileDialogBase;
19
20 // ----------------------------------------------------------------------------
21 // testing API
22 // ----------------------------------------------------------------------------
23
24 // Don't include this code when building the library itself
25 #ifndef WXBUILDING
26
27 #include "wx/beforestd.h"
28 #include <algorithm>
29 #include <iterator>
30 #include <queue>
31 #include "wx/afterstd.h"
32 #include "wx/cpp.h"
33 #include "wx/dialog.h"
34 #include "wx/msgdlg.h"
35 #include "wx/filedlg.h"
36
37 class wxTestingModalHook;
38
39 // Non-template base class for wxExpectModal<T> (via wxExpectModalBase).
40 // Only used internally.
41 class wxModalExpectation
42 {
43 public:
44 wxModalExpectation() : m_isOptional(false) {}
45 virtual ~wxModalExpectation() {}
46
47 bool IsOptional() const { return m_isOptional; }
48
49 virtual int Invoke(wxDialog *dlg) const = 0;
50
51 virtual wxString GetDescription() const = 0;
52
53 protected:
54 // Is this dialog optional, i.e. not required to be shown?
55 bool m_isOptional;
56 };
57
58
59 // This must be specialized for each type. The specialization MUST be derived
60 // from wxExpectModalBase<T>.
61 template<class T> class wxExpectModal {};
62
63
64 /**
65 Base class for wxExpectModal<T> specializations.
66
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.
69
70 T must be a class derived from wxDialog.
71 */
72 template<class T>
73 class wxExpectModalBase : public wxModalExpectation
74 {
75 public:
76 typedef T DialogType;
77 typedef wxExpectModal<DialogType> ExpectationType;
78
79 /**
80 Returns a copy of the expectation where the expected dialog is marked
81 as optional.
82
83 Optional dialogs aren't required to appear, it's not an error if they
84 don't.
85 */
86 ExpectationType Optional() const
87 {
88 ExpectationType e(*static_cast<const ExpectationType*>(this));
89 e.m_isOptional = true;
90 return e;
91 }
92
93 protected:
94 virtual int Invoke(wxDialog *dlg) const
95 {
96 DialogType *t = dynamic_cast<DialogType*>(dlg);
97 if ( t )
98 return OnInvoked(t);
99 else
100 return wxID_NONE; // not handled
101 }
102
103 /// Returns description of the expected dialog (by default, its class).
104 virtual wxString GetDescription() const
105 {
106 return wxCLASSINFO(T)->GetClassName();
107 }
108
109 /**
110 This method is called when ShowModal() was invoked on a dialog of type T.
111
112 @return Return value is used as ShowModal()'s return value.
113 */
114 virtual int OnInvoked(DialogType *dlg) const = 0;
115 };
116
117
118 // wxExpectModal<T> specializations for common dialogs:
119
120 template<>
121 class wxExpectModal<wxMessageDialog> : public wxExpectModalBase<wxMessageDialog>
122 {
123 public:
124 wxExpectModal(int id)
125 {
126 switch ( id )
127 {
128 case wxYES:
129 m_id = wxID_YES;
130 break;
131 case wxNO:
132 m_id = wxID_NO;
133 break;
134 case wxCANCEL:
135 m_id = wxID_CANCEL;
136 break;
137 case wxOK:
138 m_id = wxID_OK;
139 break;
140 case wxHELP:
141 m_id = wxID_HELP;
142 break;
143 default:
144 m_id = id;
145 break;
146 }
147 }
148
149 protected:
150 virtual int OnInvoked(wxMessageDialog *WXUNUSED(dlg)) const
151 {
152 return m_id;
153 }
154
155 int m_id;
156 };
157
158 #if wxUSE_FILEDLG
159
160 template<>
161 class wxExpectModal<wxFileDialog> : public wxExpectModalBase<wxFileDialog>
162 {
163 public:
164 wxExpectModal(const wxString& path, int id = wxID_OK)
165 : m_path(path), m_id(id)
166 {
167 }
168
169 protected:
170 virtual int OnInvoked(wxFileDialog *dlg) const
171 {
172 dlg->SetPath(m_path);
173 return m_id;
174 }
175
176 wxString m_path;
177 int m_id;
178 };
179
180 #endif
181
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
186 {
187 public:
188 wxTestingModalHook()
189 {
190 Register();
191 }
192
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()
199 {
200 while ( !m_expectations.empty() )
201 {
202 const wxModalExpectation *expect = m_expectations.front();
203 m_expectations.pop();
204 if ( expect->IsOptional() )
205 continue;
206
207 ReportFailure
208 (
209 wxString::Format
210 (
211 "Expected %s dialog was not shown.",
212 expect->GetDescription()
213 )
214 );
215 break;
216 }
217 }
218
219 void AddExpectation(const wxModalExpectation& e)
220 {
221 m_expectations.push(&e);
222 }
223
224 protected:
225 virtual int Enter(wxDialog *dlg)
226 {
227 while ( !m_expectations.empty() )
228 {
229 const wxModalExpectation *expect = m_expectations.front();
230 m_expectations.pop();
231
232 int ret = expect->Invoke(dlg);
233 if ( ret != wxID_NONE )
234 return ret; // dialog shown as expected
235
236 // not showing an optional dialog is OK, but showing an unexpected
237 // one definitely isn't:
238 if ( !expect->IsOptional() )
239 {
240 ReportFailure
241 (
242 wxString::Format
243 (
244 "A %s dialog was shown unexpectedly, expected %s.",
245 dlg->GetClassInfo()->GetClassName(),
246 expect->GetDescription()
247 )
248 );
249 return wxID_NONE;
250 }
251 // else: try the next expectation in the chain
252 }
253
254 ReportFailure
255 (
256 wxString::Format
257 (
258 "A dialog (%s) was shown unexpectedly.",
259 dlg->GetClassInfo()->GetClassName()
260 )
261 );
262 return wxID_NONE;
263 }
264
265 protected:
266 virtual void ReportFailure(const wxString& msg)
267 {
268 wxFAIL_MSG( msg );
269 }
270
271 private:
272 std::queue<const wxModalExpectation*> m_expectations;
273
274 wxDECLARE_NO_COPY_CLASS(wxTestingModalHook);
275 };
276
277
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
281
282 #define WX_TEST_IMPL_ADD_EXPECTATION(pos, expect) \
283 const wxModalExpectation& wx_exp##pos = expect; \
284 wx_hook.AddExpectation(wx_exp##pos);
285
286 /**
287 Runs given code with all modal dialogs redirected to wxExpectModal<T>
288 hooks, instead of being shown to the user.
289
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.
293
294 Some typical examples:
295
296 @code
297 wxTEST_DIALOG
298 (
299 rc = dlg.ShowModal(),
300 wxExpectModal<wxFileDialog>(wxGetCwd() + "/test.txt")
301 );
302 @endcode
303
304 Sometimes, the code may show more than one dialog:
305
306 @code
307 wxTEST_DIALOG
308 (
309 RunSomeFunction(),
310 wxExpectModal<wxMessageDialog>(wxNO),
311 wxExpectModal<MyConfirmationDialog>(wxYES),
312 wxExpectModal<wxFileDialog>(wxGetCwd() + "/test.txt")
313 );
314 @endcode
315
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:
320
321 @code
322 wxTEST_DIALOG
323 (
324 RunSomeFunction(),
325 wxExpectModal<wxMessageDialog>(wxNO),
326 wxExpectModal<wxFileDialog>(wxGetCwd() + "/test.txt").Optional()
327 );
328 @endcode
329
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.
334
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()
337 method.
338 */
339 #ifdef HAVE_VARIADIC_MACROS
340 #define wxTEST_DIALOG(codeToRun, ...) \
341 { \
342 wxTEST_DIALOG_HOOK_CLASS wx_hook; \
343 wxCALL_FOR_EACH(WX_TEST_IMPL_ADD_EXPECTATION, __VA_ARGS__) \
344 codeToRun; \
345 wx_hook.CheckUnmetExpectations(); \
346 }
347 #endif /* HAVE_VARIADIC_MACROS */
348
349 #endif // !WXBUILDING
350
351 #endif // _WX_TESTING_H_