]> git.saurik.com Git - wxWidgets.git/blame - include/wx/testing.h
Comment-only update
[wxWidgets.git] / include / wx / testing.h
CommitLineData
643e9cf9
VS
1/////////////////////////////////////////////////////////////////////////////
2// Name: wx/testing.h
3// Purpose: helpers for GUI testing
4// Author: Vaclav Slavik
5// Created: 2012-08-28
643e9cf9
VS
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"
691745ab 15#include "wx/modalhook.h"
643e9cf9 16
643e9cf9
VS
17class WXDLLIMPEXP_FWD_CORE wxMessageDialogBase;
18class WXDLLIMPEXP_FWD_CORE wxFileDialogBase;
19
643e9cf9
VS
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"
9e0959cd
VS
33#include "wx/dialog.h"
34#include "wx/msgdlg.h"
35#include "wx/filedlg.h"
643e9cf9
VS
36
37class wxTestingModalHook;
38
39// Non-template base class for wxExpectModal<T> (via wxExpectModalBase).
40// Only used internally.
41class wxModalExpectation
42{
43public:
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
53protected:
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>.
61template<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 */
72template<class T>
73class wxExpectModalBase : public wxModalExpectation
74{
75public:
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
93protected:
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
120template<>
121class wxExpectModal<wxMessageDialog> : public wxExpectModalBase<wxMessageDialog>
122{
123public:
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
149protected:
150 virtual int OnInvoked(wxMessageDialog *WXUNUSED(dlg)) const
151 {
152 return m_id;
153 }
154
155 int m_id;
156};
157
0b7a7b8c 158#if wxUSE_FILEDLG
643e9cf9
VS
159
160template<>
161class wxExpectModal<wxFileDialog> : public wxExpectModalBase<wxFileDialog>
162{
163public:
164 wxExpectModal(const wxString& path, int id = wxID_OK)
165 : m_path(path), m_id(id)
166 {
167 }
168
169protected:
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
0b7a7b8c 180#endif
643e9cf9
VS
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.
185class wxTestingModalHook : public wxModalDialogHook
186{
187public:
188 wxTestingModalHook()
189 {
691745ab 190 Register();
643e9cf9
VS
191 }
192
691745ab
VZ
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()
643e9cf9 199 {
691745ab
VZ
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);
643e9cf9
VS
222 }
223
691745ab
VZ
224protected:
225 virtual int Enter(wxDialog *dlg)
643e9cf9
VS
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
643e9cf9
VS
265protected:
266 virtual void ReportFailure(const wxString& msg)
267 {
268 wxFAIL_MSG( msg );
269 }
270
271private:
643e9cf9
VS
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 */
168d1f65 339#ifdef HAVE_VARIADIC_MACROS
643e9cf9
VS
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 }
168d1f65 347#endif /* HAVE_VARIADIC_MACROS */
643e9cf9
VS
348
349#endif // !WXBUILDING
350
351#endif // _WX_TESTING_H_