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