]> git.saurik.com Git - wxWidgets.git/blame - include/wx/testing.h
support retina display
[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
6// RCS-ID: $Id$
7// Copyright: (c) 2012 Vaclav Slavik
8// Licence: wxWindows Licence
9/////////////////////////////////////////////////////////////////////////////
10
11#ifndef _WX_TESTING_H_
12#define _WX_TESTING_H_
13
14#include "wx/debug.h"
15#include "wx/string.h"
691745ab 16#include "wx/modalhook.h"
643e9cf9 17
643e9cf9
VS
18class WXDLLIMPEXP_FWD_CORE wxMessageDialogBase;
19class WXDLLIMPEXP_FWD_CORE wxFileDialogBase;
20
643e9cf9
VS
21// ----------------------------------------------------------------------------
22// testing API
23// ----------------------------------------------------------------------------
24
25// Don't include this code when building the library itself
26#ifndef WXBUILDING
27
28#include "wx/beforestd.h"
29#include <algorithm>
30#include <iterator>
31#include <queue>
32#include "wx/afterstd.h"
33#include "wx/cpp.h"
9e0959cd
VS
34#include "wx/dialog.h"
35#include "wx/msgdlg.h"
36#include "wx/filedlg.h"
643e9cf9
VS
37
38class wxTestingModalHook;
39
40// Non-template base class for wxExpectModal<T> (via wxExpectModalBase).
41// Only used internally.
42class wxModalExpectation
43{
44public:
45 wxModalExpectation() : m_isOptional(false) {}
46 virtual ~wxModalExpectation() {}
47
48 bool IsOptional() const { return m_isOptional; }
49
50 virtual int Invoke(wxDialog *dlg) const = 0;
51
52 virtual wxString GetDescription() const = 0;
53
54protected:
55 // Is this dialog optional, i.e. not required to be shown?
56 bool m_isOptional;
57};
58
59
60// This must be specialized for each type. The specialization MUST be derived
61// from wxExpectModalBase<T>.
62template<class T> class wxExpectModal {};
63
64
65/**
66 Base class for wxExpectModal<T> specializations.
67
68 Every such specialization must be derived from wxExpectModalBase; there's
69 no other use for this class than to serve as wxExpectModal<T>'s base class.
70
71 T must be a class derived from wxDialog.
72 */
73template<class T>
74class wxExpectModalBase : public wxModalExpectation
75{
76public:
77 typedef T DialogType;
78 typedef wxExpectModal<DialogType> ExpectationType;
79
80 /**
81 Returns a copy of the expectation where the expected dialog is marked
82 as optional.
83
84 Optional dialogs aren't required to appear, it's not an error if they
85 don't.
86 */
87 ExpectationType Optional() const
88 {
89 ExpectationType e(*static_cast<const ExpectationType*>(this));
90 e.m_isOptional = true;
91 return e;
92 }
93
94protected:
95 virtual int Invoke(wxDialog *dlg) const
96 {
97 DialogType *t = dynamic_cast<DialogType*>(dlg);
98 if ( t )
99 return OnInvoked(t);
100 else
101 return wxID_NONE; // not handled
102 }
103
104 /// Returns description of the expected dialog (by default, its class).
105 virtual wxString GetDescription() const
106 {
107 return wxCLASSINFO(T)->GetClassName();
108 }
109
110 /**
111 This method is called when ShowModal() was invoked on a dialog of type T.
112
113 @return Return value is used as ShowModal()'s return value.
114 */
115 virtual int OnInvoked(DialogType *dlg) const = 0;
116};
117
118
119// wxExpectModal<T> specializations for common dialogs:
120
121template<>
122class wxExpectModal<wxMessageDialog> : public wxExpectModalBase<wxMessageDialog>
123{
124public:
125 wxExpectModal(int id)
126 {
127 switch ( id )
128 {
129 case wxYES:
130 m_id = wxID_YES;
131 break;
132 case wxNO:
133 m_id = wxID_NO;
134 break;
135 case wxCANCEL:
136 m_id = wxID_CANCEL;
137 break;
138 case wxOK:
139 m_id = wxID_OK;
140 break;
141 case wxHELP:
142 m_id = wxID_HELP;
143 break;
144 default:
145 m_id = id;
146 break;
147 }
148 }
149
150protected:
151 virtual int OnInvoked(wxMessageDialog *WXUNUSED(dlg)) const
152 {
153 return m_id;
154 }
155
156 int m_id;
157};
158
0b7a7b8c 159#if wxUSE_FILEDLG
643e9cf9
VS
160
161template<>
162class wxExpectModal<wxFileDialog> : public wxExpectModalBase<wxFileDialog>
163{
164public:
165 wxExpectModal(const wxString& path, int id = wxID_OK)
166 : m_path(path), m_id(id)
167 {
168 }
169
170protected:
171 virtual int OnInvoked(wxFileDialog *dlg) const
172 {
173 dlg->SetPath(m_path);
174 return m_id;
175 }
176
177 wxString m_path;
178 int m_id;
179};
180
0b7a7b8c 181#endif
643e9cf9
VS
182
183// Implementation of wxModalDialogHook for use in testing, with
184// wxExpectModal<T> and the wxTEST_DIALOG() macro. It is not intended for
185// direct use, use the macro instead.
186class wxTestingModalHook : public wxModalDialogHook
187{
188public:
189 wxTestingModalHook()
190 {
691745ab 191 Register();
643e9cf9
VS
192 }
193
691745ab
VZ
194 // Called to verify that all expectations were met. This cannot be done in
195 // the destructor, because ReportFailure() may throw (either because it's
196 // overriden or because wx's assertions handling is, globally). And
197 // throwing from the destructor would introduce all sort of problems,
198 // including messing up the order of errors in some cases.
199 void CheckUnmetExpectations()
643e9cf9 200 {
691745ab
VZ
201 while ( !m_expectations.empty() )
202 {
203 const wxModalExpectation *expect = m_expectations.front();
204 m_expectations.pop();
205 if ( expect->IsOptional() )
206 continue;
207
208 ReportFailure
209 (
210 wxString::Format
211 (
212 "Expected %s dialog was not shown.",
213 expect->GetDescription()
214 )
215 );
216 break;
217 }
218 }
219
220 void AddExpectation(const wxModalExpectation& e)
221 {
222 m_expectations.push(&e);
643e9cf9
VS
223 }
224
691745ab
VZ
225protected:
226 virtual int Enter(wxDialog *dlg)
643e9cf9
VS
227 {
228 while ( !m_expectations.empty() )
229 {
230 const wxModalExpectation *expect = m_expectations.front();
231 m_expectations.pop();
232
233 int ret = expect->Invoke(dlg);
234 if ( ret != wxID_NONE )
235 return ret; // dialog shown as expected
236
237 // not showing an optional dialog is OK, but showing an unexpected
238 // one definitely isn't:
239 if ( !expect->IsOptional() )
240 {
241 ReportFailure
242 (
243 wxString::Format
244 (
245 "A %s dialog was shown unexpectedly, expected %s.",
246 dlg->GetClassInfo()->GetClassName(),
247 expect->GetDescription()
248 )
249 );
250 return wxID_NONE;
251 }
252 // else: try the next expectation in the chain
253 }
254
255 ReportFailure
256 (
257 wxString::Format
258 (
259 "A dialog (%s) was shown unexpectedly.",
260 dlg->GetClassInfo()->GetClassName()
261 )
262 );
263 return wxID_NONE;
264 }
265
643e9cf9
VS
266protected:
267 virtual void ReportFailure(const wxString& msg)
268 {
269 wxFAIL_MSG( msg );
270 }
271
272private:
643e9cf9
VS
273 std::queue<const wxModalExpectation*> m_expectations;
274
275 wxDECLARE_NO_COPY_CLASS(wxTestingModalHook);
276};
277
278
279// Redefining this value makes it possible to customize the hook class,
280// including e.g. its error reporting.
281#define wxTEST_DIALOG_HOOK_CLASS wxTestingModalHook
282
283#define WX_TEST_IMPL_ADD_EXPECTATION(pos, expect) \
284 const wxModalExpectation& wx_exp##pos = expect; \
285 wx_hook.AddExpectation(wx_exp##pos);
286
287/**
288 Runs given code with all modal dialogs redirected to wxExpectModal<T>
289 hooks, instead of being shown to the user.
290
291 The first argument is any valid expression, typically a function call. The
292 remaining arguments are wxExpectModal<T> instances defining the dialogs
293 that are expected to be shown, in order of appearance.
294
295 Some typical examples:
296
297 @code
298 wxTEST_DIALOG
299 (
300 rc = dlg.ShowModal(),
301 wxExpectModal<wxFileDialog>(wxGetCwd() + "/test.txt")
302 );
303 @endcode
304
305 Sometimes, the code may show more than one dialog:
306
307 @code
308 wxTEST_DIALOG
309 (
310 RunSomeFunction(),
311 wxExpectModal<wxMessageDialog>(wxNO),
312 wxExpectModal<MyConfirmationDialog>(wxYES),
313 wxExpectModal<wxFileDialog>(wxGetCwd() + "/test.txt")
314 );
315 @endcode
316
317 Notice that wxExpectModal<T> has some convenience methods for further
318 tweaking the expectations. For example, it's possible to mark an expected
319 dialog as @em optional for situations when a dialog may be shown, but isn't
320 required to, by calling the Optional() method:
321
322 @code
323 wxTEST_DIALOG
324 (
325 RunSomeFunction(),
326 wxExpectModal<wxMessageDialog>(wxNO),
327 wxExpectModal<wxFileDialog>(wxGetCwd() + "/test.txt").Optional()
328 );
329 @endcode
330
331 @note By default, errors are reported with wxFAIL_MSG(). You may customize this by
332 implementing a class derived from wxTestingModalHook, overriding its
333 ReportFailure() method and redefining the wxTEST_DIALOG_HOOK_CLASS
334 macro to be the name of this class.
335
336 @note Custom dialogs are supported too. All you have to do is to specialize
337 wxExpectModal<> for your dialog type and implement its OnInvoked()
338 method.
339 */
e4f3b467 340#ifdef wxHAS_VARIADIC_MACROS
643e9cf9
VS
341#define wxTEST_DIALOG(codeToRun, ...) \
342 { \
343 wxTEST_DIALOG_HOOK_CLASS wx_hook; \
344 wxCALL_FOR_EACH(WX_TEST_IMPL_ADD_EXPECTATION, __VA_ARGS__) \
345 codeToRun; \
346 wx_hook.CheckUnmetExpectations(); \
347 }
e4f3b467 348#endif /* wxHAS_VARIADIC_MACROS */
643e9cf9
VS
349
350#endif // !WXBUILDING
351
352#endif // _WX_TESTING_H_