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