Add a simple test for keyboard events generation.
[wxWidgets.git] / tests / events / keyboard.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: tests/events/keyboard.cpp
3 // Purpose: Test keyboard events
4 // Author: Vadim Zeitlin
5 // Created: 2010-09-05
6 // RCS-ID: $Id$
7 // Copyright: (c) 2010 Vadim Zeitlin <vadim@wxwidgets.org>
8 ///////////////////////////////////////////////////////////////////////////////
9
10 // ----------------------------------------------------------------------------
11 // headers
12 // ----------------------------------------------------------------------------
13
14 #include "testprec.h"
15
16 #ifdef __BORLANDC__
17 #pragma hdrstop
18 #endif
19
20 #if wxUSE_UIACTIONSIMULATOR
21
22 #ifndef WX_PRECOMP
23 #include "wx/app.h"
24 #include "wx/event.h"
25 #include "wx/window.h"
26 #endif // WX_PRECOMP
27
28 #include "wx/uiaction.h"
29 #include "wx/vector.h"
30
31 namespace
32 {
33
34 // ----------------------------------------------------------------------------
35 // test window verifying the event generation
36 // ----------------------------------------------------------------------------
37
38 class KeyboardTestWindow : public wxWindow
39 {
40 public:
41 KeyboardTestWindow(wxWindow *parent)
42 : wxWindow(parent, wxID_ANY)
43 {
44 Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(KeyboardTestWindow::OnKeyDown));
45 Connect(wxEVT_CHAR, wxKeyEventHandler(KeyboardTestWindow::OnChar));
46 Connect(wxEVT_KEY_UP, wxKeyEventHandler(KeyboardTestWindow::OnKeyUp));
47 }
48
49 unsigned GetKeyDownCount() const { return m_keyDownEvents.size(); }
50 unsigned GetCharCount() const { return m_charEvents.size(); }
51 unsigned GetKeyUpCount() const { return m_keyUpEvents.size(); }
52
53 const wxKeyEvent& GetKeyDownEvent(unsigned n = 0) const
54 {
55 return m_keyDownEvents[n];
56 }
57 const wxKeyEvent& GetCharEvent(unsigned n = 0) const
58 {
59 return m_charEvents[n];
60 }
61 const wxKeyEvent& GetKeyUpEvent(unsigned n = 0) const
62 {
63 return m_keyUpEvents[n];
64 }
65
66 void ClearEvents()
67 {
68 m_keyDownEvents =
69 m_charEvents =
70 m_keyUpEvents = wxVector<wxKeyEvent>();
71 }
72
73 private:
74 void OnKeyDown(wxKeyEvent& event)
75 {
76 m_keyDownEvents.push_back(event);
77 event.Skip();
78 }
79
80 void OnChar(wxKeyEvent& event)
81 {
82 m_charEvents.push_back(event);
83 event.Skip();
84 }
85
86 void OnKeyUp(wxKeyEvent& event)
87 {
88 m_keyUpEvents.push_back(event);
89 event.Skip();
90 }
91
92 wxVector<wxKeyEvent> m_keyDownEvents,
93 m_charEvents,
94 m_keyUpEvents;
95
96
97 wxDECLARE_NO_COPY_CLASS(KeyboardTestWindow);
98 };
99
100 // Object describing the (main fields of) keyboard event.
101 struct KeyDesc
102 {
103 KeyDesc(int keycode, int mods = 0)
104 : m_keycode(keycode),
105 m_mods(mods)
106 {
107 }
108
109 int m_keycode;
110 int m_mods;
111 };
112
113 // These functions are only needed because of wx bug: currently, modifiers key
114 // events are inconsistent between platforms and wxMSW generates key down event
115 // for e.g. WXK_CONTROL with wxMOD_CONTROL set and key up event with it unset
116 // while wxGTK does exactly vice versa. So we provide these helpers to make it
117 // possible to make the tests pass under all platforms for now but ideally they
118 // should all be made to behave the same and this should become unnecessary.
119
120 int GetModForKey(int keycode)
121 {
122 switch ( keycode )
123 {
124 case WXK_CONTROL: return wxMOD_CONTROL;
125 case WXK_SHIFT: return wxMOD_SHIFT;
126 case WXK_ALT: return wxMOD_ALT;
127 default:
128 wxFAIL_MSG( "Unknown modifier key" );
129 }
130
131 return wxMOD_NONE;
132 }
133
134 #ifdef __WXGTK__
135
136 KeyDesc ModKeyDown(int keycode)
137 {
138 // Second level bug: currently wxUIActionSimulator produces different
139 // modifiers than actually pressing the key. So while the above comment is
140 // true for keys pressed by user, when simulating them we do get the
141 // corresponding bit set for the modifier press events.
142 //
143 // Again, this is a bug and wxUIActionSimulator should be fixed to behave
144 // as the real events do but until this happens just work around this here.
145 return KeyDesc(keycode, GetModForKey(keycode));
146 }
147
148 KeyDesc ModKeyUp(int keycode)
149 {
150 return KeyDesc(keycode, GetModForKey(keycode));
151 }
152
153 #else // Assume MSW-like behaviour for all the other platforms.
154
155 KeyDesc ModKeyDown(int keycode)
156 {
157 return KeyDesc(keycode, GetModForKey(keycode));
158 }
159
160 KeyDesc ModKeyUp(int keycode)
161 {
162 return KeyDesc(keycode);
163 }
164
165 #endif // Platforms.
166
167 // Verify that the event object corresponds to our idea of what it should be.
168 void TestEvent(int line, const wxKeyEvent& ev, const KeyDesc& desc)
169 {
170 // Construct the message we'll display if an assert fails.
171 std::string msg;
172 const wxEventType t = ev.GetEventType();
173 if ( t == wxEVT_KEY_DOWN )
174 msg = "key down";
175 else if ( t == wxEVT_CHAR )
176 msg = "char";
177 else if ( t == wxEVT_KEY_UP )
178 msg = "key up";
179 else
180 CPPUNIT_FAIL( "unknown event type" );
181
182 msg += " event at line ";
183 msg += wxString::Format("%d", line).mb_str();
184
185
186 CPPUNIT_ASSERT_EQUAL_MESSAGE( "wrong key code in " + msg,
187 desc.m_keycode,
188 ev.GetKeyCode() );
189
190 #if wxUSE_UNICODE
191 if ( desc.m_keycode < 0x80 )
192 {
193 // FIXME: Currently wxMSW generates 'A' key code for key down/up events
194 // for the 'a' physical key while wxGTK and wxOSX/Cocoa generate them
195 // with 'a' and it's not clear which behaviour is more correct so don't
196 // test this for those events, only test it for EVT_CHAR where the
197 // correct behaviour is clear.
198
199 if ( t == wxEVT_CHAR )
200 {
201 // For 7-bit ASCII Unicode keys are the same as normal key codes.
202 CPPUNIT_ASSERT_EQUAL_MESSAGE( "wrong Unicode key in " + msg,
203 (char)desc.m_keycode,
204 (char)ev.GetUnicodeKey() );
205 }
206 }
207 else
208 {
209 // In this test we don't use any really Unicode characters so far so
210 // anything above 0x80 must be special keys (e.g. WXK_CONTROL &c) which
211 // don't have any Unicode equivalent.
212 CPPUNIT_ASSERT_EQUAL_MESSAGE( "wrong non-zero Unicode key in " + msg,
213 0,
214 (int)ev.GetUnicodeKey() );
215 }
216 #endif // wxUSE_UNICODE
217
218 CPPUNIT_ASSERT_EQUAL_MESSAGE( "wrong modifiers in " + msg,
219 desc.m_mods,
220 ev.GetModifiers() );
221 }
222
223 // Call TestEvent() passing it the line number from where it was called: this
224 // is useful for interpreting the assert failure messages.
225 #define ASSERT_KEY_EVENT_IS( ev, desc ) TestEvent(__LINE__, ev, desc)
226
227 } // anonymous namespace
228
229 // --------------------------------------------------------------------------
230 // test class
231 // --------------------------------------------------------------------------
232
233 class KeyboardEventTestCase : public CppUnit::TestCase
234 {
235 public:
236 KeyboardEventTestCase() {}
237
238 virtual void setUp();
239 virtual void tearDown();
240
241 private:
242 CPPUNIT_TEST_SUITE( KeyboardEventTestCase );
243 CPPUNIT_TEST( NormalLetter );
244 CPPUNIT_TEST( NormalSpecial );
245 CPPUNIT_TEST( CtrlLetter );
246 CPPUNIT_TEST( CtrlSpecial );
247 CPPUNIT_TEST( ShiftLetter );
248 CPPUNIT_TEST( ShiftSpecial );
249 CPPUNIT_TEST_SUITE_END();
250
251 void NormalLetter();
252 void NormalSpecial();
253 void CtrlLetter();
254 void CtrlSpecial();
255 void ShiftLetter();
256 void ShiftSpecial();
257
258 KeyboardTestWindow *m_win;
259
260 wxDECLARE_NO_COPY_CLASS(KeyboardEventTestCase);
261 };
262
263 wxREGISTER_UNIT_TEST(KeyboardEvent);
264
265 void KeyboardEventTestCase::setUp()
266 {
267 m_win = new KeyboardTestWindow(wxTheApp->GetTopWindow());
268 m_win->SetFocus();
269 wxYield(); // needed to show the new window
270
271 // The window might get some key up events when it's being shown if the key
272 // was pressed when the program was started and released after the window
273 // was shown, e.g. this does happen in practice when launching the test
274 // from command line. Simply discard all the spurious events so far.
275 m_win->ClearEvents();
276 }
277
278 void KeyboardEventTestCase::tearDown()
279 {
280 m_win->Destroy();
281 }
282
283 void KeyboardEventTestCase::NormalLetter()
284 {
285 wxUIActionSimulator sim;
286 sim.Char('a');
287 wxYield();
288
289 CPPUNIT_ASSERT_EQUAL( 1, m_win->GetKeyDownCount() );
290 ASSERT_KEY_EVENT_IS( m_win->GetKeyDownEvent(), 'A' );
291
292 CPPUNIT_ASSERT_EQUAL( 1, m_win->GetCharCount() );
293 ASSERT_KEY_EVENT_IS( m_win->GetCharEvent(), 'a' );
294
295 CPPUNIT_ASSERT_EQUAL( 1, m_win->GetKeyUpCount() );
296 ASSERT_KEY_EVENT_IS( m_win->GetKeyUpEvent(), 'A' );
297 }
298
299 void KeyboardEventTestCase::NormalSpecial()
300 {
301 wxUIActionSimulator sim;
302 sim.Char(WXK_END);
303 wxYield();
304
305 CPPUNIT_ASSERT_EQUAL( 1, m_win->GetKeyDownCount() );
306 ASSERT_KEY_EVENT_IS( m_win->GetKeyDownEvent(), WXK_END );
307
308 CPPUNIT_ASSERT_EQUAL( 1, m_win->GetCharCount() );
309 ASSERT_KEY_EVENT_IS( m_win->GetCharEvent(), WXK_END );
310
311 CPPUNIT_ASSERT_EQUAL( 1, m_win->GetKeyUpCount() );
312 ASSERT_KEY_EVENT_IS( m_win->GetKeyUpEvent(), WXK_END );
313 }
314
315 void KeyboardEventTestCase::CtrlLetter()
316 {
317 wxUIActionSimulator sim;
318 sim.Char('z', wxMOD_CONTROL);
319 wxYield();
320
321 CPPUNIT_ASSERT_EQUAL( 2, m_win->GetKeyDownCount() );
322 ASSERT_KEY_EVENT_IS( m_win->GetKeyDownEvent(0),
323 ModKeyDown(WXK_CONTROL) );
324 ASSERT_KEY_EVENT_IS( m_win->GetKeyDownEvent(1),
325 KeyDesc('Z', wxMOD_CONTROL) );
326
327 CPPUNIT_ASSERT_EQUAL( 1, m_win->GetCharCount() );
328 ASSERT_KEY_EVENT_IS( m_win->GetCharEvent(),
329 KeyDesc('\x1a', wxMOD_CONTROL) );
330
331 CPPUNIT_ASSERT_EQUAL( 2, m_win->GetKeyUpCount() );
332 ASSERT_KEY_EVENT_IS( m_win->GetKeyUpEvent(0),
333 KeyDesc('Z', wxMOD_CONTROL) );
334 ASSERT_KEY_EVENT_IS( m_win->GetKeyUpEvent(1),
335 ModKeyUp(WXK_CONTROL) );
336 }
337
338 void KeyboardEventTestCase::CtrlSpecial()
339 {
340 wxUIActionSimulator sim;
341 sim.Char(WXK_PAGEUP, wxMOD_CONTROL);
342 wxYield();
343
344 CPPUNIT_ASSERT_EQUAL( 2, m_win->GetKeyDownCount() );
345 ASSERT_KEY_EVENT_IS( m_win->GetKeyDownEvent(0),
346 ModKeyDown(WXK_CONTROL) );
347 ASSERT_KEY_EVENT_IS( m_win->GetKeyDownEvent(1),
348 KeyDesc(WXK_PAGEUP, wxMOD_CONTROL) );
349
350 CPPUNIT_ASSERT_EQUAL( 1, m_win->GetCharCount() );
351 ASSERT_KEY_EVENT_IS( m_win->GetCharEvent(),
352 KeyDesc(WXK_PAGEUP, wxMOD_CONTROL) );
353
354 CPPUNIT_ASSERT_EQUAL( 2, m_win->GetKeyUpCount() );
355 ASSERT_KEY_EVENT_IS( m_win->GetKeyUpEvent(0),
356 KeyDesc(WXK_PAGEUP, wxMOD_CONTROL) );
357 ASSERT_KEY_EVENT_IS( m_win->GetKeyUpEvent(1),
358 ModKeyUp(WXK_CONTROL) );
359 }
360
361 void KeyboardEventTestCase::ShiftLetter()
362 {
363 wxUIActionSimulator sim;
364 sim.Char('Q', wxMOD_SHIFT);
365 wxYield();
366
367 CPPUNIT_ASSERT_EQUAL( 2, m_win->GetKeyDownCount() );
368 ASSERT_KEY_EVENT_IS( m_win->GetKeyDownEvent(0),
369 ModKeyDown(WXK_SHIFT) );
370 ASSERT_KEY_EVENT_IS( m_win->GetKeyDownEvent(1),
371 KeyDesc('Q', wxMOD_SHIFT) );
372
373 CPPUNIT_ASSERT_EQUAL( 1, m_win->GetCharCount() );
374 ASSERT_KEY_EVENT_IS( m_win->GetCharEvent(),
375 KeyDesc('Q', wxMOD_SHIFT) );
376
377 CPPUNIT_ASSERT_EQUAL( 2, m_win->GetKeyUpCount() );
378 ASSERT_KEY_EVENT_IS( m_win->GetKeyUpEvent(0),
379 KeyDesc('Q', wxMOD_SHIFT) );
380 ASSERT_KEY_EVENT_IS( m_win->GetKeyUpEvent(1),
381 ModKeyUp(WXK_SHIFT) );
382 }
383
384 void KeyboardEventTestCase::ShiftSpecial()
385 {
386 wxUIActionSimulator sim;
387 sim.Char(WXK_TAB, wxMOD_SHIFT);
388 wxYield();
389
390 CPPUNIT_ASSERT_EQUAL( 2, m_win->GetKeyDownCount() );
391 ASSERT_KEY_EVENT_IS( m_win->GetKeyDownEvent(0),
392 ModKeyDown(WXK_SHIFT) );
393 ASSERT_KEY_EVENT_IS( m_win->GetKeyDownEvent(1),
394 KeyDesc(WXK_TAB, wxMOD_SHIFT) );
395
396 CPPUNIT_ASSERT_EQUAL( 1, m_win->GetCharCount() );
397 ASSERT_KEY_EVENT_IS( m_win->GetCharEvent(),
398 KeyDesc(WXK_TAB, wxMOD_SHIFT) );
399
400 CPPUNIT_ASSERT_EQUAL( 2, m_win->GetKeyUpCount() );
401 ASSERT_KEY_EVENT_IS( m_win->GetKeyUpEvent(0),
402 KeyDesc(WXK_TAB, wxMOD_SHIFT) );
403 ASSERT_KEY_EVENT_IS( m_win->GetKeyUpEvent(1),
404 ModKeyUp(WXK_SHIFT) );
405 }
406
407 #endif // wxUSE_UIACTIONSIMULATOR