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