Move getting the unit test event count from wxTestableFrame to the EventCounter class...
[wxWidgets.git] / tests / controls / textctrltest.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: tests/controls/textctrltest.cpp
3 // Purpose: wxTextCtrl unit test
4 // Author: Vadim Zeitlin
5 // Created: 2007-09-25
6 // RCS-ID: $Id$
7 // Copyright: (c) 2007 Vadim Zeitlin <vadim@wxwidgets.org>
8 ///////////////////////////////////////////////////////////////////////////////
9
10 // ----------------------------------------------------------------------------
11 // headers
12 // ----------------------------------------------------------------------------
13
14 #include "testprec.h"
15
16 #if wxUSE_TEXTCTRL
17
18 #ifdef __BORLANDC__
19 #pragma hdrstop
20 #endif
21
22 #ifndef WX_PRECOMP
23 #include "wx/app.h"
24 #include "wx/textctrl.h"
25 #endif // WX_PRECOMP
26
27 #include "wx/scopeguard.h"
28
29 #include "textentrytest.h"
30 #include "testableframe.h"
31 #include "asserthelper.h"
32 #include "wx/uiaction.h"
33
34 // ----------------------------------------------------------------------------
35 // test class
36 // ----------------------------------------------------------------------------
37
38 class TextCtrlTestCase : public TextEntryTestCase, public CppUnit::TestCase
39 {
40 public:
41 TextCtrlTestCase() { }
42
43 virtual void setUp();
44 virtual void tearDown();
45
46 private:
47 virtual wxTextEntry *GetTestEntry() const { return m_text; }
48 virtual wxWindow *GetTestWindow() const { return m_text; }
49
50 CPPUNIT_TEST_SUITE( TextCtrlTestCase );
51 wxTEXT_ENTRY_TESTS();
52 CPPUNIT_TEST( MultiLineReplace );
53 WXUISIM_TEST( ReadOnly );
54 WXUISIM_TEST( MaxLength );
55 CPPUNIT_TEST( StreamInput );
56 CPPUNIT_TEST( Redirector );
57 //WXUISIM_TEST( ProcessEnter );
58 WXUISIM_TEST( Url );
59 CPPUNIT_TEST( Style );
60 CPPUNIT_TEST( FontStyle );
61 CPPUNIT_TEST( Lines );
62 CPPUNIT_TEST( LogTextCtrl );
63 CPPUNIT_TEST( PositionToCoords );
64 CPPUNIT_TEST( PositionToCoordsRich );
65 CPPUNIT_TEST( PositionToCoordsRich2 );
66 CPPUNIT_TEST_SUITE_END();
67
68 void MultiLineReplace();
69 void ReadOnly();
70 void MaxLength();
71 void StreamInput();
72 void Redirector();
73 //void ProcessEnter();
74 void Url();
75 void Style();
76 void FontStyle();
77 void Lines();
78 void LogTextCtrl();
79 void PositionToCoords();
80 void PositionToCoordsRich();
81 void PositionToCoordsRich2();
82
83 void DoPositionToCoordsTestWithStyle(long style);
84
85 wxTextCtrl *m_text;
86
87 DECLARE_NO_COPY_CLASS(TextCtrlTestCase)
88 };
89
90 // register in the unnamed registry so that these tests are run by default
91 CPPUNIT_TEST_SUITE_REGISTRATION( TextCtrlTestCase );
92
93 // also include in its own registry so that these tests can be run alone
94 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( TextCtrlTestCase, "TextCtrlTestCase" );
95
96 // ----------------------------------------------------------------------------
97 // test initialization
98 // ----------------------------------------------------------------------------
99
100 void TextCtrlTestCase::setUp()
101 {
102 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY);
103 }
104
105 void TextCtrlTestCase::tearDown()
106 {
107 wxDELETE(m_text);
108 }
109
110 // ----------------------------------------------------------------------------
111 // tests themselves
112 // ----------------------------------------------------------------------------
113
114 void TextCtrlTestCase::MultiLineReplace()
115 {
116 // we need a multiline control for this test so recreate it
117 delete m_text;
118 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
119 wxDefaultPosition, wxDefaultSize,
120 wxTE_MULTILINE);
121
122 m_text->SetValue("Hello replace\n"
123 "0123456789012");
124 m_text->SetInsertionPoint(0);
125
126 m_text->Replace(6, 13, "changed");
127
128 CPPUNIT_ASSERT_EQUAL("Hello changed\n"
129 "0123456789012",
130 m_text->GetValue());
131 CPPUNIT_ASSERT_EQUAL(13, m_text->GetInsertionPoint());
132
133 m_text->Replace(13, -1, "");
134 CPPUNIT_ASSERT_EQUAL("Hello changed", m_text->GetValue());
135 CPPUNIT_ASSERT_EQUAL(13, m_text->GetInsertionPoint());
136
137 delete m_text;
138 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY);
139 }
140
141 void TextCtrlTestCase::ReadOnly()
142 {
143 #if wxUSE_UIACTIONSIMULATOR
144 // we need a read only control for this test so recreate it
145 delete m_text;
146 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
147 wxDefaultPosition, wxDefaultSize,
148 wxTE_READONLY);
149
150 EventCounter updated(m_text, wxEVT_COMMAND_TEXT_UPDATED);
151
152 m_text->SetFocus();
153
154 wxUIActionSimulator sim;
155 sim.Text("abcdef");
156 wxYield();
157
158 CPPUNIT_ASSERT_EQUAL("", m_text->GetValue());
159 CPPUNIT_ASSERT_EQUAL(0, updated.GetCount());
160
161 // SetEditable() is supposed to override wxTE_READONLY
162 m_text->SetEditable(true);
163
164 sim.Text("abcdef");
165 wxYield();
166
167 CPPUNIT_ASSERT_EQUAL("abcdef", m_text->GetValue());
168 CPPUNIT_ASSERT_EQUAL(6, updated.GetCount());
169
170 delete m_text;
171 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY);
172 #endif
173 }
174
175 void TextCtrlTestCase::MaxLength()
176 {
177 #if wxUSE_UIACTIONSIMULATOR
178 EventCounter updated(m_text, wxEVT_COMMAND_TEXT_UPDATED);
179 EventCounter maxlen(m_text, wxEVT_COMMAND_TEXT_MAXLEN);
180
181 m_text->SetFocus();
182 m_text->SetMaxLength(10);
183
184 wxUIActionSimulator sim;
185 sim.Text("abcdef");
186 wxYield();
187
188 CPPUNIT_ASSERT_EQUAL(0, maxlen.GetCount());
189
190 sim.Text("ghij");
191 wxYield();
192
193 CPPUNIT_ASSERT_EQUAL(0, maxlen.GetCount());
194 CPPUNIT_ASSERT_EQUAL(10, updated.GetCount());
195
196 maxlen.Clear();
197 updated.Clear();
198
199 sim.Text("k");
200 wxYield();
201
202 CPPUNIT_ASSERT_EQUAL(1, maxlen.GetCount());
203 CPPUNIT_ASSERT_EQUAL(0, updated.GetCount());
204
205 maxlen.Clear();
206 updated.Clear();
207
208 m_text->SetMaxLength(0);
209
210 sim.Text("k");
211 wxYield();
212
213 CPPUNIT_ASSERT_EQUAL(0, maxlen.GetCount());
214 CPPUNIT_ASSERT_EQUAL(1, updated.GetCount());
215 #endif
216 }
217
218 void TextCtrlTestCase::StreamInput()
219 {
220 #ifndef __WXOSX__
221 {
222 // Ensure we use decimal point and not a comma.
223 char * const locOld = setlocale(LC_NUMERIC, "C");
224 wxON_BLOCK_EXIT2( setlocale, (int)LC_NUMERIC, locOld );
225
226 *m_text << "stringinput"
227 << 10
228 << 1000L
229 << 3.14f
230 << 2.71
231 << 'a'
232 << L'b';
233 }
234
235 CPPUNIT_ASSERT_EQUAL("stringinput1010003.142.71ab", m_text->GetValue());
236
237 m_text->SetValue("");
238
239 #if wxHAS_TEXT_WINDOW_STREAM
240
241 std::ostream stream(m_text);
242
243 // We don't test a wide character as this is not a wide stream
244 stream << "stringinput"
245 << 10
246 << 1000L
247 << 3.14f
248 << 2.71
249 << 'a';
250
251 stream.flush();
252
253 CPPUNIT_ASSERT_EQUAL("stringinput1010003.142.71a", m_text->GetValue());
254
255 #endif // wxHAS_TEXT_WINDOW_STREAM
256 #endif // !__WXOSX__
257 }
258
259 void TextCtrlTestCase::Redirector()
260 {
261 #if wxHAS_TEXT_WINDOW_STREAM && wxUSE_STD_IOSTREAM
262
263 wxStreamToTextRedirector redirect(m_text);
264
265 std::cout << "stringinput"
266 << 10
267 << 1000L
268 << 3.14f
269 << 2.71
270 << 'a';
271
272 CPPUNIT_ASSERT_EQUAL("stringinput1010003.142.71a", m_text->GetValue());
273
274 #endif
275 }
276
277 #if 0
278 void TextCtrlTestCase::ProcessEnter()
279 {
280 #if wxUSE_UIACTIONSIMULATOR
281 wxTestableFrame* frame = wxStaticCast(wxTheApp->GetTopWindow(),
282 wxTestableFrame);
283
284 EventCounter count(m_text, wxEVT_COMMAND_TEXT_ENTER);
285
286 m_text->SetFocus();
287
288 wxUIActionSimulator sim;
289 sim.Char(WXK_RETURN);
290 wxYield();
291
292 CPPUNIT_ASSERT_EQUAL(0, frame->GetEventCount(wxEVT_COMMAND_TEXT_ENTER));
293
294 // we need a text control with wxTE_PROCESS_ENTER for this test
295 delete m_text;
296 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
297 wxDefaultPosition, wxDefaultSize,
298 wxTE_PROCESS_ENTER);
299
300 m_text->SetFocus();
301
302 sim.Char(WXK_RETURN);
303 wxYield();
304
305 CPPUNIT_ASSERT_EQUAL(1, frame->GetEventCount(wxEVT_COMMAND_TEXT_ENTER));
306 #endif
307 }
308 #endif
309
310 void TextCtrlTestCase::Url()
311 {
312 #if wxUSE_UIACTIONSIMULATOR && defined(__WXMSW__)
313 delete m_text;
314 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
315 wxDefaultPosition, wxDefaultSize,
316 wxTE_MULTILINE | wxTE_RICH | wxTE_AUTO_URL);
317
318 EventCounter url(m_text, wxEVT_COMMAND_TEXT_URL);
319
320 m_text->AppendText("http://www.wxwidgets.org");
321
322 wxUIActionSimulator sim;
323 sim.MouseMove(m_text->ClientToScreen(wxPoint(5, 5)));
324 sim.MouseClick();
325 wxYield();
326
327 CPPUNIT_ASSERT_EQUAL(1, url.GetCount());
328 #endif
329 }
330
331 void TextCtrlTestCase::Style()
332 {
333 #ifndef __WXOSX__
334 delete m_text;
335 // We need wxTE_RICH under windows for style support
336 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
337 wxDefaultPosition, wxDefaultSize, wxTE_RICH);
338
339 // Red text on a white background
340 m_text->SetDefaultStyle(wxTextAttr(*wxRED, *wxWHITE));
341
342 CPPUNIT_ASSERT_EQUAL(m_text->GetDefaultStyle().GetTextColour(), *wxRED);
343 CPPUNIT_ASSERT_EQUAL(m_text->GetDefaultStyle().GetBackgroundColour(),
344 *wxWHITE);
345
346 m_text->AppendText("red on white ");
347
348 // Red text on a grey background
349 m_text->SetDefaultStyle(wxTextAttr(wxNullColour, *wxLIGHT_GREY));
350
351 CPPUNIT_ASSERT_EQUAL(m_text->GetDefaultStyle().GetTextColour(), *wxRED);
352 CPPUNIT_ASSERT_EQUAL(m_text->GetDefaultStyle().GetBackgroundColour(),
353 *wxLIGHT_GREY);
354
355 m_text->AppendText("red on grey ");
356
357 // Blue text on a grey background
358 m_text->SetDefaultStyle(wxTextAttr(*wxBLUE));
359
360
361 CPPUNIT_ASSERT_EQUAL(m_text->GetDefaultStyle().GetTextColour(), *wxBLUE);
362 CPPUNIT_ASSERT_EQUAL(m_text->GetDefaultStyle().GetBackgroundColour(),
363 *wxLIGHT_GREY);
364
365 m_text->AppendText("blue on grey");
366
367 // Get getting the style at a specific location
368 wxTextAttr style;
369
370 // We have to check that styles are supported
371 if(m_text->GetStyle(3, style))
372 {
373 CPPUNIT_ASSERT_EQUAL(style.GetTextColour(), *wxRED);
374 CPPUNIT_ASSERT_EQUAL(style.GetBackgroundColour(), *wxWHITE);
375 }
376
377 // And then setting the style
378 if(m_text->SetStyle(15, 18, style))
379 {
380 m_text->GetStyle(17, style);
381
382 CPPUNIT_ASSERT_EQUAL(style.GetTextColour(), *wxRED);
383 CPPUNIT_ASSERT_EQUAL(style.GetBackgroundColour(), *wxWHITE);
384 }
385 #endif
386 }
387
388 void TextCtrlTestCase::FontStyle()
389 {
390 // We need wxTE_RICH under MSW and wxTE_MULTILINE under GTK for style
391 // support so recreate the control with these styles.
392 delete m_text;
393 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
394 wxDefaultPosition, wxDefaultSize,
395 wxTE_MULTILINE | wxTE_RICH);
396
397 // Check that we get back the same font from GetStyle() after setting it
398 // with SetDefaultStyle().
399 wxFont fontIn(14,
400 wxFONTFAMILY_DEFAULT,
401 wxFONTSTYLE_NORMAL,
402 wxFONTWEIGHT_NORMAL);
403 wxTextAttr attrIn;
404 attrIn.SetFont(fontIn);
405 if ( !m_text->SetDefaultStyle(attrIn) )
406 {
407 // Skip the test if the styles are not supported.
408 return;
409 }
410
411 m_text->AppendText("Default font size 14");
412
413 wxTextAttr attrOut;
414 m_text->GetStyle(5, attrOut);
415
416 CPPUNIT_ASSERT( attrOut.HasFont() );
417
418 wxFont fontOut = attrOut.GetFont();
419 #ifdef __WXMSW__
420 // Under MSW we get back an encoding in the font even though we hadn't
421 // specified it originally. It's not really a problem but we need this hack
422 // to prevent the assert below from failing because of it.
423 fontOut.SetEncoding(fontIn.GetEncoding());
424 #endif
425 CPPUNIT_ASSERT_EQUAL( fontIn, fontOut );
426
427
428 // Also check the same for SetStyle().
429 fontIn.SetPointSize(10);
430 fontIn.SetWeight(wxFONTWEIGHT_BOLD);
431 attrIn.SetFont(fontIn);
432 m_text->SetStyle(0, 6, attrIn);
433
434 m_text->GetStyle(4, attrOut);
435 CPPUNIT_ASSERT( attrOut.HasFont() );
436
437 fontOut = attrOut.GetFont();
438 #ifdef __WXMSW__
439 fontOut.SetEncoding(fontIn.GetEncoding());
440 #endif
441 CPPUNIT_ASSERT_EQUAL( fontIn, fontOut );
442 }
443
444 void TextCtrlTestCase::Lines()
445 {
446 delete m_text;
447 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
448 wxDefaultPosition, wxSize(400, 200), wxTE_MULTILINE);
449
450 m_text->SetValue("line1\nline2\nlong long line 3");
451 m_text->Refresh();
452 m_text->Update();
453
454 CPPUNIT_ASSERT_EQUAL(3, m_text->GetNumberOfLines());
455 CPPUNIT_ASSERT_EQUAL(5, m_text->GetLineLength(0));
456 CPPUNIT_ASSERT_EQUAL("line2", m_text->GetLineText(1));
457 CPPUNIT_ASSERT_EQUAL(16, m_text->GetLineLength(2));
458
459 m_text->AppendText("\n\nMore text on line 5");
460
461 CPPUNIT_ASSERT_EQUAL(5, m_text->GetNumberOfLines());
462 CPPUNIT_ASSERT_EQUAL(0, m_text->GetLineLength(3));
463 CPPUNIT_ASSERT_EQUAL("", m_text->GetLineText(3));
464
465 // Verify that wrapped lines count as 2 lines.
466 //
467 // This currently doesn't work neither in wxGTK nor wxOSX/Cocoa, see
468 // #12366, where GetNumberOfLines() always returns the number of logical,
469 // not physical, lines.
470 m_text->AppendText("\n" + wxString(50, '1') + ' ' + wxString(50, '2'));
471 #if defined(__WXGTK__) || defined(__WXOSX_COCOA__)
472 CPPUNIT_ASSERT_EQUAL(6, m_text->GetNumberOfLines());
473 #else
474 CPPUNIT_ASSERT_EQUAL(7, m_text->GetNumberOfLines());
475 #endif
476 }
477
478 void TextCtrlTestCase::LogTextCtrl()
479 {
480 delete m_text;
481 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
482 wxDefaultPosition, wxSize(400, 200),
483 wxTE_MULTILINE);
484
485 CPPUNIT_ASSERT(m_text->IsEmpty());
486
487 wxLogTextCtrl* logtext = new wxLogTextCtrl(m_text);
488
489 wxLog* old = wxLog::SetActiveTarget(logtext);
490
491 logtext->LogText("text");
492
493 delete wxLog::SetActiveTarget(old);
494
495 CPPUNIT_ASSERT(!m_text->IsEmpty());
496 }
497
498 void TextCtrlTestCase::PositionToCoords()
499 {
500 DoPositionToCoordsTestWithStyle(0);
501 }
502
503 void TextCtrlTestCase::PositionToCoordsRich()
504 {
505 DoPositionToCoordsTestWithStyle(wxTE_RICH);
506 }
507
508 void TextCtrlTestCase::PositionToCoordsRich2()
509 {
510 DoPositionToCoordsTestWithStyle(wxTE_RICH2);
511 }
512
513 void TextCtrlTestCase::DoPositionToCoordsTestWithStyle(long style)
514 {
515 static const int TEXT_HEIGHT = 200;
516
517 delete m_text;
518 m_text = new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, "",
519 wxDefaultPosition, wxSize(400, TEXT_HEIGHT),
520 wxTE_MULTILINE | style);
521
522 // Asking for invalid index should fail.
523 WX_ASSERT_FAILS_WITH_ASSERT( m_text->PositionToCoords(1) );
524
525 // Getting position shouldn't return wxDefaultPosition except if the method
526 // is not implemented at all in the current port.
527 const wxPoint pos0 = m_text->PositionToCoords(0);
528 if ( pos0 == wxDefaultPosition )
529 {
530 #if defined(__WXMSW__) || defined(__WXGTK20__)
531 CPPUNIT_FAIL( "PositionToCoords() unexpectedly failed." );
532 #endif
533 return;
534 }
535
536 CPPUNIT_ASSERT(pos0.x >= 0);
537 CPPUNIT_ASSERT(pos0.y >= 0);
538
539
540 m_text->SetValue("Hello");
541 wxYield(); // Let GTK layout the control correctly.
542
543 // Position of non-first character should be positive.
544 const long posHello4 = m_text->PositionToCoords(4).x;
545 CPPUNIT_ASSERT( posHello4 > 0 );
546
547 // Asking for position beyond the last character should succeed and return
548 // reasonable result.
549 CPPUNIT_ASSERT( m_text->PositionToCoords(5).x > posHello4 );
550
551 // But asking for the next position should fail.
552 WX_ASSERT_FAILS_WITH_ASSERT( m_text->PositionToCoords(6) );
553
554 // Test getting the coordinates of the last character when it is in the
555 // beginning of a new line to exercise MSW code which has specific logic
556 // for it.
557 m_text->AppendText("\n");
558 const wxPoint posLast = m_text->PositionToCoords(m_text->GetLastPosition());
559 CPPUNIT_ASSERT_EQUAL( pos0.x, posLast.x );
560 CPPUNIT_ASSERT( posLast.y > 0 );
561
562
563 // Add enough contents to the control to make sure it has a scrollbar.
564 m_text->SetValue("First line" + wxString(50, '\n') + "Last line");
565 m_text->SetInsertionPoint(0);
566 wxYield(); // Let GTK layout the control correctly.
567
568 // This shouldn't change anything for the first position coordinates.
569 CPPUNIT_ASSERT_EQUAL( pos0, m_text->PositionToCoords(0) );
570
571 // And the last one must be beyond the window boundary and so not be
572 // visible -- but getting its coordinate should still work.
573 CPPUNIT_ASSERT
574 (
575 m_text->PositionToCoords(m_text->GetLastPosition()).y > TEXT_HEIGHT
576 );
577
578
579 // Now make it scroll to the end and check that the first position now has
580 // negative offset as its above the visible part of the window while the
581 // last position is in its bounds.
582 m_text->SetInsertionPointEnd();
583
584 CPPUNIT_ASSERT( m_text->PositionToCoords(0).y < 0 );
585 CPPUNIT_ASSERT
586 (
587 m_text->PositionToCoords(m_text->GetInsertionPoint()).y <= TEXT_HEIGHT
588 );
589 }
590
591
592 #endif //wxUSE_TEXTCTRL