added failing compilation test for the case of missing handler in Bind() call
[wxWidgets.git] / tests / events / evthandler.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: tests/events/evthandler.cpp
3 // Purpose: Test the new event types and wxEvtHandler-methods
4 // Author: Peter Most
5 // Created: 2009-01-24
6 // RCS-ID: $Id$
7 // Copyright: (c) 2009 Peter Most
8 ///////////////////////////////////////////////////////////////////////////////
9
10 // ----------------------------------------------------------------------------
11 // headers
12 // ----------------------------------------------------------------------------
13
14 #include "testprec.h"
15
16 #ifdef __BORLANDC__
17 #pragma hdrstop
18 #endif
19
20 #include "wx/event.h"
21
22 // ----------------------------------------------------------------------------
23 // test events and their handlers
24 // ----------------------------------------------------------------------------
25
26 const wxEventType LegacyEventType = wxNewEventType();
27
28 class MyEvent;
29 wxDEFINE_EVENT(MyEventType, MyEvent);
30
31 class MyEvent : public wxEvent
32 {
33 public:
34 MyEvent() : wxEvent(0, MyEventType) { }
35
36 virtual wxEvent *Clone() const { return new MyEvent; }
37 };
38
39 typedef void (wxEvtHandler::*MyEventFunction)(MyEvent&);
40 #if wxEVENTS_COMPATIBILITY_2_8
41 #define MyEventHandler(func) wxEVENT_HANDLER_CAST(MyEventFunction, func)
42 #else
43 #define MyEventHandler(func) &func
44 #endif
45 #define EVT_MYEVENT(func) \
46 wx__DECLARE_EVT0(MyEventType, MyEventHandler(func))
47
48 class AnotherEvent : public wxEvent
49 {
50 };
51
52 namespace
53 {
54
55 struct Called
56 {
57 Called() { Reset(); }
58
59 void Reset()
60 {
61 function =
62 functor =
63 method =
64 smethod = false;
65 }
66
67 bool function,
68 functor,
69 method,
70 smethod;
71 } g_called;
72
73 void GlobalOnMyEvent(MyEvent&)
74 {
75 g_called.function = true;
76 }
77
78 void GlobalOnAnotherEvent(AnotherEvent&);
79
80 void GlobalOnIdle(wxIdleEvent&)
81 {
82 g_called.function = true;
83 }
84
85 struct MyFunctor
86 {
87 void operator()(MyEvent &) { g_called.functor = true; }
88 };
89
90 struct IdleFunctor
91 {
92 void operator()(wxIdleEvent &) { g_called.functor = true; }
93 };
94
95 class MyHandler : public wxEvtHandler
96 {
97 public:
98 static void StaticOnMyEvent(MyEvent &) { g_called.smethod = true; }
99 static void StaticOnAnotherEvent(AnotherEvent &);
100 static void StaticOnIdle(wxIdleEvent&) { g_called.smethod = true; }
101
102 void OnMyEvent(MyEvent&) { g_called.method = true; }
103 void OnEvent(wxEvent&) { g_called.method = true; }
104 void OnAnotherEvent(AnotherEvent&);
105 void OnIdle(wxIdleEvent&) { g_called.method = true; }
106 };
107
108 // we can also handle events in classes not deriving from wxEvtHandler
109 struct MySink
110 {
111 void OnMyEvent(MyEvent&) { g_called.method = true; }
112 void OnEvent(wxEvent&) { g_called.method = true; }
113 void OnIdle(wxIdleEvent&) { g_called.method = true; }
114 };
115
116 // also test event table compilation
117 class MyClassWithEventTable : public wxEvtHandler
118 {
119 public:
120 void OnMyEvent(MyEvent&) { g_called.method = true; }
121 void OnEvent(wxEvent&) { g_called.method = true; }
122 void OnAnotherEvent(AnotherEvent&);
123 void OnIdle(wxIdleEvent&) { g_called.method = true; }
124
125 private:
126 DECLARE_EVENT_TABLE()
127 };
128
129 BEGIN_EVENT_TABLE(MyClassWithEventTable, wxEvtHandler)
130 EVT_IDLE(MyClassWithEventTable::OnIdle)
131
132 EVT_MYEVENT(MyClassWithEventTable::OnMyEvent)
133 #if !wxEVENTS_COMPATIBILITY_2_8
134 EVT_MYEVENT(MyClassWithEventTable::OnEvent)
135 #endif
136
137 // this shouldn't compile:
138 //EVT_MYEVENT(MyClassWithEventTable::OnIdle)
139 //EVT_IDLE(MyClassWithEventTable::OnAnotherEvent)
140 END_EVENT_TABLE()
141
142 } // anonymous namespace
143
144
145 // --------------------------------------------------------------------------
146 // test class
147 // --------------------------------------------------------------------------
148
149 class EvtHandlerTestCase : public CppUnit::TestCase
150 {
151 public:
152 EvtHandlerTestCase() {}
153
154 private:
155 CPPUNIT_TEST_SUITE( EvtHandlerTestCase );
156 CPPUNIT_TEST( BuiltinConnect );
157 CPPUNIT_TEST( LegacyConnect );
158 #if !wxEVENTS_COMPATIBILITY_2_8
159 CPPUNIT_TEST( BindFunction );
160 CPPUNIT_TEST( BindStaticMethod );
161 CPPUNIT_TEST( BindFunctor );
162 CPPUNIT_TEST( BindMethod );
163 CPPUNIT_TEST( BindMethodUsingBaseEvent );
164 CPPUNIT_TEST( BindNonHandler );
165 CPPUNIT_TEST( InvalidBind );
166 #endif // !wxEVENTS_COMPATIBILITY_2_8
167 CPPUNIT_TEST_SUITE_END();
168
169 void BuiltinConnect();
170 void LegacyConnect();
171 #if !wxEVENTS_COMPATIBILITY_2_8
172 void BindFunction();
173 void BindStaticMethod();
174 void BindFunctor();
175 void BindMethod();
176 void BindMethodUsingBaseEvent();
177 void BindNonHandler();
178 void InvalidBind();
179 #endif // !wxEVENTS_COMPATIBILITY_2_8
180
181
182 // these member variables exceptionally don't use "m_" prefix because
183 // they're used so many times
184 MyHandler handler;
185 MyEvent e;
186
187 DECLARE_NO_COPY_CLASS(EvtHandlerTestCase)
188 };
189
190 // register in the unnamed registry so that these tests are run by default
191 CPPUNIT_TEST_SUITE_REGISTRATION( EvtHandlerTestCase );
192
193 // also include in it's own registry so that these tests can be run alone
194 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( EvtHandlerTestCase, "EvtHandlerTestCase" );
195
196 void EvtHandlerTestCase::BuiltinConnect()
197 {
198 handler.Connect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle));
199 handler.Disconnect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle));
200
201 handler.Connect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle), NULL, &handler);
202 handler.Disconnect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle), NULL, &handler);
203
204 // using casts like this is even uglier than using wxIdleEventHandler but
205 // it should still continue to work for compatibility
206 handler.Connect(wxEVT_IDLE, (wxObjectEventFunction)(wxEventFunction)&MyHandler::OnIdle);
207 handler.Disconnect(wxEVT_IDLE, (wxObjectEventFunction)(wxEventFunction)&MyHandler::OnIdle);
208
209 #if !wxEVENTS_COMPATIBILITY_2_8
210 handler.Bind(wxEVT_IDLE, GlobalOnIdle);
211 handler.Unbind(wxEVT_IDLE, GlobalOnIdle);
212
213 IdleFunctor f;
214 handler.Bind(wxEVT_IDLE, f);
215 handler.Unbind(wxEVT_IDLE, f);
216
217 handler.Bind(wxEVT_IDLE, &MyHandler::OnIdle, &handler);
218 handler.Unbind(wxEVT_IDLE, &MyHandler::OnIdle, &handler);
219
220 handler.Bind(wxEVT_IDLE, &MyHandler::StaticOnIdle);
221 handler.Unbind(wxEVT_IDLE, &MyHandler::StaticOnIdle);
222 #endif // !wxEVENTS_COMPATIBILITY_2_8
223 }
224
225 void EvtHandlerTestCase::LegacyConnect()
226 {
227 handler.Connect( LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent );
228 handler.Connect( 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent );
229 handler.Connect( 0, 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent );
230
231 handler.Disconnect( LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent );
232 handler.Disconnect( 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent );
233 handler.Disconnect( 0, 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent );
234
235
236 handler.Connect( LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
237 handler.Connect( 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
238 handler.Connect( 0, 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
239
240 handler.Disconnect( LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
241 handler.Disconnect( 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
242 handler.Disconnect( 0, 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
243 }
244
245 #if !wxEVENTS_COMPATIBILITY_2_8
246
247 void EvtHandlerTestCase::BindFunction()
248 {
249 // function tests
250 handler.Bind( MyEventType, GlobalOnMyEvent );
251 g_called.Reset();
252 handler.ProcessEvent(e);
253 CPPUNIT_ASSERT( g_called.function );
254 handler.Unbind( MyEventType, GlobalOnMyEvent );
255 g_called.Reset();
256 handler.ProcessEvent(e);
257 CPPUNIT_ASSERT( !g_called.function ); // check that it was disconnected
258
259 handler.Bind( MyEventType, GlobalOnMyEvent, 0 );
260 handler.Unbind( MyEventType, GlobalOnMyEvent, 0 );
261
262 handler.Bind( MyEventType, GlobalOnMyEvent, 0, 0 );
263 handler.Unbind( MyEventType, GlobalOnMyEvent, 0, 0 );
264 }
265
266 void EvtHandlerTestCase::BindStaticMethod()
267 {
268 // static method tests (this is same as functions but still test it just in
269 // case we hit some strange compiler bugs)
270 handler.Bind( MyEventType, &MyHandler::StaticOnMyEvent );
271 g_called.Reset();
272 handler.ProcessEvent(e);
273 CPPUNIT_ASSERT( g_called.smethod );
274 handler.Unbind( MyEventType, &MyHandler::StaticOnMyEvent );
275 g_called.Reset();
276 handler.ProcessEvent(e);
277 CPPUNIT_ASSERT( !g_called.smethod );
278
279 handler.Bind( MyEventType, &MyHandler::StaticOnMyEvent, 0 );
280 handler.Unbind( MyEventType, &MyHandler::StaticOnMyEvent, 0 );
281
282 handler.Bind( MyEventType, &MyHandler::StaticOnMyEvent, 0, 0 );
283 handler.Unbind( MyEventType, &MyHandler::StaticOnMyEvent, 0, 0 );
284 }
285
286 void EvtHandlerTestCase::BindFunctor()
287 {
288 // generalized functor tests
289 MyFunctor functor;
290
291 handler.Bind( MyEventType, functor );
292 g_called.Reset();
293 handler.ProcessEvent(e);
294 CPPUNIT_ASSERT( g_called.functor );
295 handler.Unbind( MyEventType, functor );
296 g_called.Reset();
297 handler.ProcessEvent(e);
298 CPPUNIT_ASSERT( !g_called.functor );
299
300 handler.Bind( MyEventType, functor, 0 );
301 handler.Unbind( MyEventType, functor, 0 );
302
303 handler.Bind( MyEventType, functor, 0, 0 );
304 handler.Unbind( MyEventType, functor, 0, 0 );
305 }
306
307 void EvtHandlerTestCase::BindMethod()
308 {
309 // class method tests
310 handler.Bind( MyEventType, &MyHandler::OnMyEvent, &handler );
311 g_called.Reset();
312 handler.ProcessEvent(e);
313 CPPUNIT_ASSERT( g_called.method );
314 handler.Unbind( MyEventType, &MyHandler::OnMyEvent, &handler );
315 g_called.Reset();
316 handler.ProcessEvent(e);
317 CPPUNIT_ASSERT( !g_called.method );
318
319 handler.Bind( MyEventType, &MyHandler::OnMyEvent, &handler, 0 );
320 handler.Unbind( MyEventType, &MyHandler::OnMyEvent, &handler, 0 );
321
322 handler.Bind( MyEventType, &MyHandler::OnMyEvent, &handler, 0, 0 );
323 handler.Unbind( MyEventType, &MyHandler::OnMyEvent, &handler, 0, 0 );
324 }
325
326 void EvtHandlerTestCase::BindMethodUsingBaseEvent()
327 {
328 // test connecting a method taking just wxEvent and not MyEvent: this
329 // should work too if we don't need any MyEvent-specific information in the
330 // handler
331 handler.Bind( MyEventType, &MyHandler::OnEvent, &handler );
332 g_called.Reset();
333 handler.ProcessEvent(e);
334 CPPUNIT_ASSERT( g_called.method );
335 handler.Unbind( MyEventType, &MyHandler::OnEvent, &handler );
336 g_called.Reset();
337 handler.ProcessEvent(e);
338 CPPUNIT_ASSERT( !g_called.method );
339
340 handler.Bind( MyEventType, &MyHandler::OnEvent, &handler, 0 );
341 handler.Unbind( MyEventType, &MyHandler::OnEvent, &handler, 0 );
342
343 handler.Bind( MyEventType, &MyHandler::OnEvent, &handler, 0, 0 );
344 handler.Unbind( MyEventType, &MyHandler::OnEvent, &handler, 0, 0 );
345 }
346
347
348 void EvtHandlerTestCase::BindNonHandler()
349 {
350 // class method tests for class not derived from wxEvtHandler
351 MySink sink;
352
353 handler.Bind( MyEventType, &MySink::OnMyEvent, &sink );
354 g_called.Reset();
355 handler.ProcessEvent(e);
356 CPPUNIT_ASSERT( g_called.method );
357 handler.Unbind( MyEventType, &MySink::OnMyEvent, &sink );
358 g_called.Reset();
359 handler.ProcessEvent(e);
360 CPPUNIT_ASSERT( !g_called.method );
361 }
362
363 void EvtHandlerTestCase::InvalidBind()
364 {
365 // these calls shouldn't compile but we unfortunately can't check this
366 // automatically, you need to uncomment them manually and test that
367 // compilation does indeed fail
368
369 // connecting a handler with incompatible signature shouldn't work
370 #ifdef TEST_INVALID_BIND_GLOBAL
371 handler.Bind(MyEventType, GlobalOnAnotherEvent);
372 #endif
373 #ifdef TEST_INVALID_BIND_STATIC
374 handler.Bind(MyEventType, &MyHandler::StaticOnAnotherEvent);
375 #endif
376 #ifdef TEST_INVALID_BIND_METHOD
377 handler.Bind(MyEventType, &MyHandler::OnAnotherEvent, &handler);
378 #endif
379 #ifdef TEST_INVALID_BIND_FUNCTOR
380 IdleFunctor f;
381 handler.Bind(MyEventType, f);
382 #endif
383
384 // the handler can't be omitted when calling Bind()
385 #ifdef TEST_INVALID_BIND_NO_HANDLER
386 handler.Bind(MyEventType, &MyHandler::OnMyEvent);
387 #endif
388
389 // calling a derived class method with a base class pointer must not work
390 #ifdef TEST_INVALID_BIND_DERIVED
391 struct C1 : wxEvtHandler { };
392 struct C2 : wxEvtHandler { void OnWhatever(wxEvent&); };
393 C1 c1;
394 c1.Bind(&C2::OnWhatever);
395 #endif
396
397 // using object pointer incompatible with the method must not work
398 #ifdef TEST_INVALID_BIND_WRONG_CLASS
399 MySink mySink;
400 MyHandler myHandler;
401 myHandler.Bind(MyEventType, &MyHandler::OnMyEvent, &mySink);
402 #endif
403 }
404
405 #endif // !wxEVENTS_COMPATIBILITY_2_8