1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        tests/menu/menu.cpp
3 // Purpose:     wxMenu unit test
4 // Author:      wxWidgets team
5 // Created:     2010-11-10
6 // Copyright:   (c) 2010 wxWidgets team
7 ///////////////////////////////////////////////////////////////////////////////
8 
9 // ----------------------------------------------------------------------------
10 // headers
11 // ----------------------------------------------------------------------------
12 
13 #include "testprec.h"
14 
15 #ifdef __BORLANDC__
16     #pragma hdrstop
17 #endif
18 
19 #ifndef WX_PRECOMP
20     #include "wx/wx.h"
21 #endif // WX_PRECOMP
22 
23 #include "wx/menu.h"
24 #include "wx/uiaction.h"
25 
26 #include <stdarg.h>
27 
28 // ----------------------------------------------------------------------------
29 // helper
30 // ----------------------------------------------------------------------------
31 
32 namespace
33 {
34 
35 enum
36 {
37     MenuTestCase_Foo = 10000,
38     MenuTestCase_Bar,
39     MenuTestCase_First
40 };
41 
PopulateMenu(wxMenu * menu,const wxString & name,size_t & itemcount)42 void PopulateMenu(wxMenu* menu, const wxString& name,  size_t& itemcount)
43 {
44     // Start at item 1 to make it human-readable ;)
45     for (int n=1; n<6; ++n, ++itemcount)
46     {
47         wxString label = name; label << n;
48         menu->Append(MenuTestCase_First + itemcount, label, label + " help string");
49     }
50 }
51 
RecursivelyCountMenuItems(const wxMenu * menu,size_t & count)52 void RecursivelyCountMenuItems(const wxMenu* menu, size_t& count)
53 {
54     CPPUNIT_ASSERT( menu );
55 
56     count += menu->GetMenuItemCount();
57     for (size_t n=0; n < menu->GetMenuItemCount(); ++n)
58     {
59         wxMenuItem* item = menu->FindItemByPosition(n);
60         if (item->IsSubMenu())
61         {
62             RecursivelyCountMenuItems(item->GetSubMenu(), count);
63         }
64     }
65 }
66 
67 } // anon namespace
68 
69 
70 // ----------------------------------------------------------------------------
71 // test class
72 // ----------------------------------------------------------------------------
73 
74 class MenuTestCase : public CppUnit::TestCase
75 {
76 public:
MenuTestCase()77     MenuTestCase() {}
78 
setUp()79     virtual void setUp() { CreateFrame(); }
tearDown()80     virtual void tearDown() { m_frame->Destroy(); }
81 
82 private:
83     CPPUNIT_TEST_SUITE( MenuTestCase );
84         CPPUNIT_TEST( FindInMenubar );
85         CPPUNIT_TEST( FindInMenu );
86         CPPUNIT_TEST( EnableTop );
87         CPPUNIT_TEST( Count );
88         CPPUNIT_TEST( Labels );
89         CPPUNIT_TEST( RadioItems );
90         CPPUNIT_TEST( RemoveAdd );
91         WXUISIM_TEST( Events );
92     CPPUNIT_TEST_SUITE_END();
93 
94     void CreateFrame();
95 
96     void FindInMenubar();
97     void FindInMenu();
98     void EnableTop();
99     void Count();
100     void Labels();
101     void RadioItems();
102     void RemoveAdd();
103     void Events();
104 
105     wxFrame* m_frame;
106 
107     // Holds the number of menuitems contained in all the menus
108     size_t m_itemCount;
109 
110     // Store here the id of a known submenu item, to be searched for later
111     int m_submenuItemId;
112 
113     // and a sub-submenu item
114     int m_subsubmenuItemId;
115 
116     wxArrayString m_menuLabels;
117 
118     // The menu containing the item with MenuTestCase_Bar id.
119     wxMenu* m_menuWithBar;
120 
121     DECLARE_NO_COPY_CLASS(MenuTestCase)
122 };
123 
124 // register in the unnamed registry so that these tests are run by default
125 CPPUNIT_TEST_SUITE_REGISTRATION( MenuTestCase );
126 
127 // also include in its own registry so that these tests can be run alone
128 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( MenuTestCase, "MenuTestCase" );
129 
CreateFrame()130 void MenuTestCase::CreateFrame()
131 {
132     m_frame = new wxFrame(NULL, wxID_ANY, "test frame");
133 
134     wxMenu *fileMenu = new wxMenu;
135     wxMenu *helpMenu = new wxMenu;
136     wxMenu *subMenu = new wxMenu;
137     wxMenu *subsubMenu = new wxMenu;
138 
139     size_t itemcount = 0;
140 
141     PopulateMenu(subsubMenu, "Subsubmenu item ", itemcount);
142 
143     // Store one of its IDs for later
144     m_subsubmenuItemId = MenuTestCase_First + itemcount - 2;
145 
146     PopulateMenu(subMenu, "Submenu item ", itemcount);
147 
148     // Store one of its IDs for later
149     m_submenuItemId = MenuTestCase_First + itemcount - 2;
150 
151     subMenu->AppendSubMenu(subsubMenu, "Subsubmen&u", "Test a subsubmenu");
152 
153     // Check GetTitle() returns the correct string _before_ appending to the bar
154     fileMenu->SetTitle("&Foo\tCtrl-F");
155     CPPUNIT_ASSERT_EQUAL( "&Foo\tCtrl-F", fileMenu->GetTitle() );
156 
157     PopulateMenu(fileMenu, "Filemenu item ", itemcount);
158 
159     fileMenu->Append(MenuTestCase_Foo, "&Foo\tCtrl-F", "Test item to be found");
160 
161 
162     PopulateMenu(helpMenu, "Helpmenu item ", itemcount);
163     helpMenu->Append(MenuTestCase_Bar, "Bar\tF1");
164     m_menuWithBar = helpMenu;
165     helpMenu->AppendSubMenu(subMenu, "Sub&menu", "Test a submenu");
166 
167     // +2 for "Foo" and "Bar", +2 for the 2 submenus
168     m_itemCount = itemcount + 4;
169 
170     // Use an arraystring here, to help with future tests
171     m_menuLabels.Add("&File");
172     m_menuLabels.Add("&Help");
173 
174     wxMenuBar *menuBar = new wxMenuBar();
175     menuBar->Append(fileMenu, m_menuLabels[0]);
176     menuBar->Append(helpMenu, m_menuLabels[1]);
177     m_frame->SetMenuBar(menuBar);
178 }
179 
FindInMenubar()180 void MenuTestCase::FindInMenubar()
181 {
182     wxMenuBar* bar = m_frame->GetMenuBar();
183 
184     // Find by name:
185     CPPUNIT_ASSERT( bar->FindMenu("File") != wxNOT_FOUND );
186     CPPUNIT_ASSERT( bar->FindMenu("&File") != wxNOT_FOUND );
187     CPPUNIT_ASSERT( bar->FindMenu("&Fail") == wxNOT_FOUND );
188 
189     // Find by menu name plus item name:
190     CPPUNIT_ASSERT( bar->FindMenuItem("File", "Foo") != wxNOT_FOUND );
191     CPPUNIT_ASSERT( bar->FindMenuItem("&File", "&Foo") != wxNOT_FOUND );
192     // and using the menu label
193     int index = bar->FindMenu("&File");
194     CPPUNIT_ASSERT( index != wxNOT_FOUND );
195     wxString menulabel = bar->GetMenuLabel(index);
196     CPPUNIT_ASSERT( bar->FindMenuItem(menulabel, "&Foo") != wxNOT_FOUND );
197     // and title
198     wxString menutitle = bar->GetMenu(index)->GetTitle();
199     CPPUNIT_ASSERT( bar->FindMenuItem(menutitle, "&Foo") != wxNOT_FOUND );
200 
201     // Find by position:
202     for (size_t n=0; n < bar->GetMenuCount(); ++n)
203     {
204         CPPUNIT_ASSERT( bar->GetMenu(n) );
205     }
206 
207     // Find by id:
208     wxMenu* menu = NULL;
209     wxMenuItem* item = NULL;
210     item = bar->FindItem(MenuTestCase_Foo, &menu);
211     CPPUNIT_ASSERT( item );
212     CPPUNIT_ASSERT( menu );
213     // Check that the correct menu was found
214     CPPUNIT_ASSERT( menu->FindChildItem(MenuTestCase_Foo) );
215 
216     // Find submenu item:
217     item = bar->FindItem(m_submenuItemId, &menu);
218     CPPUNIT_ASSERT( item );
219     CPPUNIT_ASSERT( menu );
220     // and, for completeness, a subsubmenu one:
221     item = bar->FindItem(m_subsubmenuItemId, &menu);
222     CPPUNIT_ASSERT( item );
223     CPPUNIT_ASSERT( menu );
224 }
225 
FindInMenu()226 void MenuTestCase::FindInMenu()
227 {
228     wxMenuBar* bar = m_frame->GetMenuBar();
229 
230     // Find by name:
231     wxMenu* menuFind = bar->GetMenu(0);
232     CPPUNIT_ASSERT( menuFind->FindItem("Foo") != wxNOT_FOUND );
233     CPPUNIT_ASSERT( menuFind->FindItem("&Foo") != wxNOT_FOUND );
234     // and for submenus
235     wxMenu* menuHelp = bar->GetMenu(1);
236     CPPUNIT_ASSERT( menuHelp->FindItem("Submenu") != wxNOT_FOUND );
237     CPPUNIT_ASSERT( menuHelp->FindItem("Sub&menu") != wxNOT_FOUND );
238 
239     // Find by position:
240     size_t n;
241     for (n=0; n < menuHelp->GetMenuItemCount(); ++n)
242     {
243         CPPUNIT_ASSERT( menuHelp->FindItemByPosition(n) );
244     }
245 
246     // Find by id:
247     CPPUNIT_ASSERT( menuHelp->FindItem(MenuTestCase_Bar) );
248     CPPUNIT_ASSERT( menuHelp->FindItem(MenuTestCase_Foo) == NULL );
249 
250     for (n=0; n < menuHelp->GetMenuItemCount(); ++n)
251     {
252         size_t locatedAt;
253         wxMenuItem* itemByPos = menuHelp->FindItemByPosition(n);
254         CPPUNIT_ASSERT( itemByPos );
255         wxMenuItem* itemById = menuHelp->FindChildItem(itemByPos->GetId(), &locatedAt);
256         CPPUNIT_ASSERT_EQUAL( itemByPos, itemById );
257         CPPUNIT_ASSERT_EQUAL( locatedAt, n );
258     }
259 
260     // Find submenu item:
261     for (n=0; n < menuHelp->GetMenuItemCount(); ++n)
262     {
263         wxMenuItem* item = menuHelp->FindItemByPosition(n);
264         if (item->IsSubMenu())
265         {
266             wxMenu* submenu;
267             wxMenuItem* submenuItem = menuHelp->FindItem(m_submenuItemId, &submenu);
268             CPPUNIT_ASSERT( submenuItem );
269             CPPUNIT_ASSERT( item->GetSubMenu() == submenu );
270         }
271     }
272 }
273 
EnableTop()274 void MenuTestCase::EnableTop()
275 {
276     wxMenuBar* const bar = m_frame->GetMenuBar();
277     CPPUNIT_ASSERT( bar->IsEnabledTop(0) );
278     bar->EnableTop( 0, false );
279     CPPUNIT_ASSERT( !bar->IsEnabledTop(0) );
280     bar->EnableTop( 0, true );
281     CPPUNIT_ASSERT( bar->IsEnabledTop(0) );
282 }
283 
Count()284 void MenuTestCase::Count()
285 {
286     wxMenuBar* bar = m_frame->GetMenuBar();
287     // I suppose you could call this "counting menubars" :)
288     CPPUNIT_ASSERT( bar );
289 
290     CPPUNIT_ASSERT_EQUAL( bar->GetMenuCount(), 2 );
291 
292     size_t count = 0;
293     for (size_t n=0; n < bar->GetMenuCount(); ++n)
294     {
295         RecursivelyCountMenuItems(bar->GetMenu(n), count);
296     }
297     CPPUNIT_ASSERT_EQUAL( count, m_itemCount );
298 }
299 
Labels()300 void MenuTestCase::Labels()
301 {
302     wxMenuBar* bar = m_frame->GetMenuBar();
303     CPPUNIT_ASSERT( bar );
304     wxMenu* filemenu;
305     wxMenuItem* itemFoo = bar->FindItem(MenuTestCase_Foo, &filemenu);
306     CPPUNIT_ASSERT( itemFoo );
307     CPPUNIT_ASSERT( filemenu );
308 
309     // These return labels including mnemonics/accelerators:
310 
311     // wxMenuBar
312     CPPUNIT_ASSERT_EQUAL( "&File", bar->GetMenuLabel(0) );
313     CPPUNIT_ASSERT_EQUAL( "&Foo\tCtrl-F", bar->GetLabel(MenuTestCase_Foo) );
314 
315     // wxMenu
316     CPPUNIT_ASSERT_EQUAL( "&File", filemenu->GetTitle() );
317     CPPUNIT_ASSERT_EQUAL( "&Foo\tCtrl-F", filemenu->GetLabel(MenuTestCase_Foo) );
318 
319     // wxMenuItem
320     CPPUNIT_ASSERT_EQUAL( "&Foo\tCtrl-F", itemFoo->GetItemLabel() );
321 
322     // These return labels stripped of mnemonics/accelerators:
323 
324     // wxMenuBar
325     CPPUNIT_ASSERT_EQUAL( "File", bar->GetMenuLabelText(0) );
326 
327     // wxMenu
328     CPPUNIT_ASSERT_EQUAL( "Foo", filemenu->GetLabelText(MenuTestCase_Foo) );
329 
330     // wxMenuItem
331     CPPUNIT_ASSERT_EQUAL( "Foo", itemFoo->GetItemLabelText() );
332     CPPUNIT_ASSERT_EQUAL( "Foo", wxMenuItem::GetLabelText("&Foo\tCtrl-F") );
333 }
334 
RadioItems()335 void MenuTestCase::RadioItems()
336 {
337     wxMenuBar * const bar = m_frame->GetMenuBar();
338     wxMenu * const menu = new wxMenu;
339     bar->Append(menu, "&Radio");
340 
341     // Adding consecutive radio items creates a radio group.
342     menu->AppendRadioItem(MenuTestCase_First, "Radio 0");
343     menu->AppendRadioItem(MenuTestCase_First + 1, "Radio 1");
344 
345     // First item of a radio group is checked by default.
346     CPPUNIT_ASSERT( menu->IsChecked(MenuTestCase_First) );
347 
348     // Checking the second one make the first one unchecked however.
349     menu->Check(MenuTestCase_First + 1, true);
350     CPPUNIT_ASSERT( !menu->IsChecked(MenuTestCase_First) );
351     CPPUNIT_ASSERT( menu->IsChecked(MenuTestCase_First + 1) );
352 
353     // Adding more radio items after a separator creates another radio group...
354     menu->AppendSeparator();
355     menu->AppendRadioItem(MenuTestCase_First + 2, "Radio 2");
356     menu->AppendRadioItem(MenuTestCase_First + 3, "Radio 3");
357     menu->AppendRadioItem(MenuTestCase_First + 4, "Radio 4");
358 
359     // ... which is independent from the first one.
360     CPPUNIT_ASSERT( menu->IsChecked(MenuTestCase_First + 2) );
361 
362     menu->Check(MenuTestCase_First + 3, true);
363     CPPUNIT_ASSERT( menu->IsChecked(MenuTestCase_First + 3) );
364     CPPUNIT_ASSERT( !menu->IsChecked(MenuTestCase_First + 2) );
365     CPPUNIT_ASSERT( menu->IsChecked(MenuTestCase_First + 1) );
366 
367 
368     // Insert an item in the middle of an existing radio group.
369     menu->InsertRadioItem(4, MenuTestCase_First + 5, "Radio 5");
370     CPPUNIT_ASSERT( menu->IsChecked(MenuTestCase_First + 3) );
371 
372     menu->Check( MenuTestCase_First + 5, true );
373     CPPUNIT_ASSERT( !menu->IsChecked(MenuTestCase_First + 3) );
374 
375 
376     // Prepend a couple of items before the first group.
377     menu->PrependRadioItem(MenuTestCase_First + 6, "Radio 6");
378     menu->PrependRadioItem(MenuTestCase_First + 7, "Radio 7");
379     menu->Check(MenuTestCase_First + 7, true);
380     CPPUNIT_ASSERT( !menu->IsChecked(MenuTestCase_First + 1) );
381 
382 
383     // Check that the last radio group still works as expected.
384     menu->Check(MenuTestCase_First + 4, true);
385     CPPUNIT_ASSERT( !menu->IsChecked(MenuTestCase_First + 5) );
386 }
387 
RemoveAdd()388 void MenuTestCase::RemoveAdd()
389 {
390     wxMenuBar* bar = m_frame->GetMenuBar();
391 
392     wxMenu* menu0 = bar->GetMenu(0);
393     wxMenu* menu1 = bar->GetMenu(1);
394     wxMenuItem* item = new wxMenuItem(menu0, MenuTestCase_Foo + 100, "t&ext\tCtrl-E");
395     menu0->Insert(0, item);
396     CPPUNIT_ASSERT( menu0->FindItemByPosition(0) == item );
397     menu0->Remove(item);
398     CPPUNIT_ASSERT( menu0->FindItemByPosition(0) != item );
399     menu1->Insert(0, item);
400     CPPUNIT_ASSERT( menu1->FindItemByPosition(0) == item );
401     menu1->Remove(item);
402     CPPUNIT_ASSERT( menu1->FindItemByPosition(0) != item );
403     menu0->Insert(0, item);
404     CPPUNIT_ASSERT( menu0->FindItemByPosition(0) == item );
405     menu0->Delete(item);
406 }
407 
Events()408 void MenuTestCase::Events()
409 {
410 #ifdef __WXGTK__
411     // FIXME: For some reason, we sporadically fail to get the event in
412     //        buildbot slave builds even though the test always passes locally.
413     //        There is undoubtedly something wrong here but without being able
414     //        to debug it, I have no idea what is it, so let's just disable
415     //        this test when running under buildbot to let the entire test
416     //        suite pass.
417     if ( IsAutomaticTest() )
418         return;
419 #endif // __WXGTK__
420 
421 #if wxUSE_UIACTIONSIMULATOR
422     class MenuEventHandler : public wxEvtHandler
423     {
424     public:
425         MenuEventHandler(wxWindow* win)
426             : m_win(win)
427         {
428             m_win->Connect(wxEVT_MENU,
429                            wxCommandEventHandler(MenuEventHandler::OnMenu),
430                            NULL,
431                            this);
432 
433             m_gotEvent = false;
434             m_event = NULL;
435         }
436 
437         virtual ~MenuEventHandler()
438         {
439             m_win->Disconnect(wxEVT_MENU,
440                               wxCommandEventHandler(MenuEventHandler::OnMenu),
441                               NULL,
442                               this);
443 
444             delete m_event;
445         }
446 
447         const wxCommandEvent& GetEvent()
448         {
449             CPPUNIT_ASSERT( m_gotEvent );
450 
451             m_gotEvent = false;
452 
453             return *m_event;
454         }
455 
456     private:
457         void OnMenu(wxCommandEvent& event)
458         {
459             CPPUNIT_ASSERT( !m_gotEvent );
460 
461             delete m_event;
462             m_event = static_cast<wxCommandEvent*>(event.Clone());
463             m_gotEvent = true;
464         }
465 
466         wxWindow* const m_win;
467         wxCommandEvent* m_event;
468         bool m_gotEvent;
469     };
470 
471     MenuEventHandler handler(m_frame);
472 
473     // Invoke the accelerator.
474     m_frame->Show();
475     m_frame->SetFocus();
476     wxYield();
477 
478     wxUIActionSimulator sim;
479     sim.KeyDown(WXK_F1);
480     sim.KeyUp(WXK_F1);
481     wxYield();
482 
483     const wxCommandEvent& ev = handler.GetEvent();
484     CPPUNIT_ASSERT_EQUAL( static_cast<int>(MenuTestCase_Bar), ev.GetId() );
485 
486     wxObject* const src = ev.GetEventObject();
487     CPPUNIT_ASSERT( src );
488 
489     CPPUNIT_ASSERT_EQUAL( "wxMenu",
490                           wxString(src->GetClassInfo()->GetClassName()) );
491     CPPUNIT_ASSERT_EQUAL( static_cast<wxObject*>(m_menuWithBar),
492                           src );
493 #endif // wxUSE_UIACTIONSIMULATOR
494 }
495