1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: tests/controls/textctrltest.cpp
3 // Purpose: wxTextCtrl unit test
4 // Author: Vadim Zeitlin
7 // Copyright: (c) 2007 Vadim Zeitlin <vadim@wxwidgets.org>
8 ///////////////////////////////////////////////////////////////////////////////
10 // ----------------------------------------------------------------------------
12 // ----------------------------------------------------------------------------
24 #include "wx/textctrl.h"
27 #include "wx/scopeguard.h"
29 #include "textentrytest.h"
30 #include "testableframe.h"
31 #include "asserthelper.h"
32 #include "wx/uiaction.h"
34 static const int TEXT_HEIGHT
= 200;
36 // ----------------------------------------------------------------------------
38 // ----------------------------------------------------------------------------
40 class TextCtrlTestCase
: public TextEntryTestCase
, public CppUnit
::TestCase
43 TextCtrlTestCase() { }
46 virtual void tearDown();
49 virtual wxTextEntry
*GetTestEntry() const { return m_text
; }
50 virtual wxWindow
*GetTestWindow() const { return m_text
; }
52 CPPUNIT_TEST_SUITE( TextCtrlTestCase
);
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 );
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();
71 void PseudoTestSwitchToMultiLineStyle()
73 ms_style
= wxTE_MULTILINE
;
76 void MultiLineReplace();
81 //void ProcessEnter();
87 void PositionToCoords();
88 void PositionToCoordsRich();
89 void PositionToCoordsRich2();
91 void DoPositionToCoordsTestWithStyle(long style
);
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
);
101 DECLARE_NO_COPY_CLASS(TextCtrlTestCase
)
104 // register in the unnamed registry so that these tests are run by default
105 CPPUNIT_TEST_SUITE_REGISTRATION( TextCtrlTestCase
);
107 // also include in its own registry so that these tests can be run alone
108 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( TextCtrlTestCase
, "TextCtrlTestCase" );
110 // ----------------------------------------------------------------------------
111 // test initialization
112 // ----------------------------------------------------------------------------
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;
118 void TextCtrlTestCase
::CreateText(long extraStyles
)
121 if ( ms_style
== wxTE_MULTILINE
)
122 size
= wxSize(400, TEXT_HEIGHT
);
124 m_text
= new wxTextCtrl(wxTheApp
->GetTopWindow(), wxID_ANY
, "",
125 wxDefaultPosition
, size
,
126 ms_style
| extraStyles
);
129 void TextCtrlTestCase
::setUp()
131 CreateText(ms_style
);
134 void TextCtrlTestCase
::tearDown()
139 // ----------------------------------------------------------------------------
141 // ----------------------------------------------------------------------------
143 void TextCtrlTestCase
::MultiLineReplace()
145 m_text
->SetValue("Hello replace\n"
147 m_text
->SetInsertionPoint(0);
149 m_text
->Replace(6, 13, "changed");
151 CPPUNIT_ASSERT_EQUAL("Hello changed\n"
154 CPPUNIT_ASSERT_EQUAL(13, m_text
->GetInsertionPoint());
156 m_text
->Replace(13, -1, "");
157 CPPUNIT_ASSERT_EQUAL("Hello changed", m_text
->GetValue());
158 CPPUNIT_ASSERT_EQUAL(13, m_text
->GetInsertionPoint());
161 void TextCtrlTestCase
::ReadOnly()
163 #if wxUSE_UIACTIONSIMULATOR
164 // we need a read only control for this test so recreate it
166 CreateText(wxTE_READONLY
);
168 EventCounter
updated(m_text
, wxEVT_COMMAND_TEXT_UPDATED
);
172 wxUIActionSimulator sim
;
176 CPPUNIT_ASSERT_EQUAL("", m_text
->GetValue());
177 CPPUNIT_ASSERT_EQUAL(0, updated
.GetCount());
179 // SetEditable() is supposed to override wxTE_READONLY
180 m_text
->SetEditable(true);
185 CPPUNIT_ASSERT_EQUAL("abcdef", m_text
->GetValue());
186 CPPUNIT_ASSERT_EQUAL(6, updated
.GetCount());
190 void TextCtrlTestCase
::MaxLength()
192 #if wxUSE_UIACTIONSIMULATOR
193 EventCounter
updated(m_text
, wxEVT_COMMAND_TEXT_UPDATED
);
194 EventCounter
maxlen(m_text
, wxEVT_COMMAND_TEXT_MAXLEN
);
197 m_text
->SetMaxLength(10);
199 wxUIActionSimulator sim
;
203 CPPUNIT_ASSERT_EQUAL(0, maxlen
.GetCount());
208 CPPUNIT_ASSERT_EQUAL(0, maxlen
.GetCount());
209 CPPUNIT_ASSERT_EQUAL(10, updated
.GetCount());
217 CPPUNIT_ASSERT_EQUAL(1, maxlen
.GetCount());
218 CPPUNIT_ASSERT_EQUAL(0, updated
.GetCount());
223 m_text
->SetMaxLength(0);
228 CPPUNIT_ASSERT_EQUAL(0, maxlen
.GetCount());
229 CPPUNIT_ASSERT_EQUAL(1, updated
.GetCount());
233 void TextCtrlTestCase
::StreamInput()
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
);
241 *m_text
<< "stringinput"
250 CPPUNIT_ASSERT_EQUAL("stringinput1010003.142.71ab", m_text
->GetValue());
252 m_text
->SetValue("");
254 #if wxHAS_TEXT_WINDOW_STREAM
256 std
::ostream
stream(m_text
);
258 // We don't test a wide character as this is not a wide stream
259 stream
<< "stringinput"
268 CPPUNIT_ASSERT_EQUAL("stringinput1010003.142.71a", m_text
->GetValue());
270 #endif // wxHAS_TEXT_WINDOW_STREAM
274 void TextCtrlTestCase
::Redirector()
276 #if wxHAS_TEXT_WINDOW_STREAM && wxUSE_STD_IOSTREAM
278 wxStreamToTextRedirector
redirect(m_text
);
280 std
::cout
<< "stringinput"
287 CPPUNIT_ASSERT_EQUAL("stringinput1010003.142.71a", m_text
->GetValue());
293 void TextCtrlTestCase
::ProcessEnter()
295 #if wxUSE_UIACTIONSIMULATOR
296 wxTestableFrame
* frame
= wxStaticCast(wxTheApp
->GetTopWindow(),
299 EventCounter
count(m_text
, wxEVT_COMMAND_TEXT_ENTER
);
303 wxUIActionSimulator sim
;
304 sim
.Char(WXK_RETURN
);
307 CPPUNIT_ASSERT_EQUAL(0, frame
->GetEventCount(wxEVT_COMMAND_TEXT_ENTER
));
309 // we need a text control with wxTE_PROCESS_ENTER for this test
311 CreateText(wxTE_PROCESS_ENTER
);
315 sim
.Char(WXK_RETURN
);
318 CPPUNIT_ASSERT_EQUAL(1, frame
->GetEventCount(wxEVT_COMMAND_TEXT_ENTER
));
323 void TextCtrlTestCase
::Url()
325 #if wxUSE_UIACTIONSIMULATOR && defined(__WXMSW__)
327 CreateText(wxTE_RICH
| wxTE_AUTO_URL
);
329 EventCounter
url(m_text
, wxEVT_COMMAND_TEXT_URL
);
331 m_text
->AppendText("http://www.wxwidgets.org");
333 wxUIActionSimulator sim
;
334 sim
.MouseMove(m_text
->ClientToScreen(wxPoint(5, 5)));
338 CPPUNIT_ASSERT_EQUAL(1, url
.GetCount());
342 void TextCtrlTestCase
::Style()
346 // We need wxTE_RICH under windows for style support
347 CreateText(wxTE_RICH
);
349 // Red text on a white background
350 m_text
->SetDefaultStyle(wxTextAttr(*wxRED
, *wxWHITE
));
352 CPPUNIT_ASSERT_EQUAL(m_text
->GetDefaultStyle().GetTextColour(), *wxRED
);
353 CPPUNIT_ASSERT_EQUAL(m_text
->GetDefaultStyle().GetBackgroundColour(),
356 m_text
->AppendText("red on white ");
358 // Red text on a grey background
359 m_text
->SetDefaultStyle(wxTextAttr(wxNullColour
, *wxLIGHT_GREY
));
361 CPPUNIT_ASSERT_EQUAL(m_text
->GetDefaultStyle().GetTextColour(), *wxRED
);
362 CPPUNIT_ASSERT_EQUAL(m_text
->GetDefaultStyle().GetBackgroundColour(),
365 m_text
->AppendText("red on grey ");
367 // Blue text on a grey background
368 m_text
->SetDefaultStyle(wxTextAttr(*wxBLUE
));
371 CPPUNIT_ASSERT_EQUAL(m_text
->GetDefaultStyle().GetTextColour(), *wxBLUE
);
372 CPPUNIT_ASSERT_EQUAL(m_text
->GetDefaultStyle().GetBackgroundColour(),
375 m_text
->AppendText("blue on grey");
377 // Get getting the style at a specific location
380 // We have to check that styles are supported
381 if(m_text
->GetStyle(3, style
))
383 CPPUNIT_ASSERT_EQUAL(style
.GetTextColour(), *wxRED
);
384 CPPUNIT_ASSERT_EQUAL(style
.GetBackgroundColour(), *wxWHITE
);
387 // And then setting the style
388 if(m_text
->SetStyle(15, 18, style
))
390 m_text
->GetStyle(17, style
);
392 CPPUNIT_ASSERT_EQUAL(style
.GetTextColour(), *wxRED
);
393 CPPUNIT_ASSERT_EQUAL(style
.GetBackgroundColour(), *wxWHITE
);
398 void TextCtrlTestCase
::FontStyle()
400 // We need wxTE_RICH under MSW and wxTE_MULTILINE under GTK for style
401 // support so recreate the control with these styles.
403 CreateText(wxTE_RICH
);
405 // Check that we get back the same font from GetStyle() after setting it
406 // with SetDefaultStyle().
408 wxFONTFAMILY_DEFAULT
,
410 wxFONTWEIGHT_NORMAL
);
412 attrIn
.SetFont(fontIn
);
413 if ( !m_text
->SetDefaultStyle(attrIn
) )
415 // Skip the test if the styles are not supported.
419 m_text
->AppendText("Default font size 14");
422 m_text
->GetStyle(5, attrOut
);
424 CPPUNIT_ASSERT( attrOut
.HasFont() );
426 wxFont fontOut
= attrOut
.GetFont();
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());
433 CPPUNIT_ASSERT_EQUAL( fontIn
, fontOut
);
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
);
442 m_text
->GetStyle(4, attrOut
);
443 CPPUNIT_ASSERT( attrOut
.HasFont() );
445 fontOut
= attrOut
.GetFont();
447 fontOut
.SetEncoding(fontIn
.GetEncoding());
449 CPPUNIT_ASSERT_EQUAL( fontIn
, fontOut
);
452 void TextCtrlTestCase
::Lines()
454 m_text
->SetValue("line1\nline2\nlong long line 3");
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));
463 m_text
->AppendText("\n\nMore text on line 5");
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));
469 // Verify that wrapped lines count as 2 lines.
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());
478 CPPUNIT_ASSERT_EQUAL(7, m_text
->GetNumberOfLines());
482 void TextCtrlTestCase
::LogTextCtrl()
484 CPPUNIT_ASSERT(m_text
->IsEmpty());
486 wxLogTextCtrl
* logtext
= new wxLogTextCtrl(m_text
);
488 wxLog
* old
= wxLog
::SetActiveTarget(logtext
);
490 logtext
->LogText("text");
492 delete wxLog
::SetActiveTarget(old
);
494 CPPUNIT_ASSERT(!m_text
->IsEmpty());
497 void TextCtrlTestCase
::PositionToCoords()
499 DoPositionToCoordsTestWithStyle(0);
502 void TextCtrlTestCase
::PositionToCoordsRich()
504 DoPositionToCoordsTestWithStyle(wxTE_RICH
);
507 void TextCtrlTestCase
::PositionToCoordsRich2()
509 DoPositionToCoordsTestWithStyle(wxTE_RICH2
);
512 void TextCtrlTestCase
::DoPositionToCoordsTestWithStyle(long style
)
517 // Asking for invalid index should fail.
518 WX_ASSERT_FAILS_WITH_ASSERT( m_text
->PositionToCoords(1) );
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
)
525 #if defined(__WXMSW__) || defined(__WXGTK20__)
526 CPPUNIT_FAIL( "PositionToCoords() unexpectedly failed." );
531 CPPUNIT_ASSERT(pos0
.x
>= 0);
532 CPPUNIT_ASSERT(pos0
.y
>= 0);
535 m_text
->SetValue("Hello");
536 wxYield(); // Let GTK layout the control correctly.
538 // Position of non-first character should be positive.
539 const long posHello4
= m_text
->PositionToCoords(4).x
;
540 CPPUNIT_ASSERT( posHello4
> 0 );
542 // Asking for position beyond the last character should succeed and return
543 // reasonable result.
544 CPPUNIT_ASSERT( m_text
->PositionToCoords(5).x
> posHello4
);
546 // But asking for the next position should fail.
547 WX_ASSERT_FAILS_WITH_ASSERT( m_text
->PositionToCoords(6) );
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
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 );
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.
563 // This shouldn't change anything for the first position coordinates.
564 CPPUNIT_ASSERT_EQUAL( pos0
, m_text
->PositionToCoords(0) );
566 // And the last one must be beyond the window boundary and so not be
567 // visible -- but getting its coordinate should still work.
570 m_text
->PositionToCoords(m_text
->GetLastPosition()).y
> TEXT_HEIGHT
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();
579 CPPUNIT_ASSERT( m_text
->PositionToCoords(0).y
< 0 );
582 m_text
->PositionToCoords(m_text
->GetInsertionPoint()).y
<= TEXT_HEIGHT
587 #endif //wxUSE_TEXTCTRL