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