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 #define SINGLE_AND_MULTI_TESTS() \
53 WXUISIM_TEST( ReadOnly ); \
54 CPPUNIT_TEST( StreamInput ); \
55 CPPUNIT_TEST( Redirector )
57 CPPUNIT_TEST_SUITE( TextCtrlTestCase
);
58 // These tests run for single line text controls.
60 WXUISIM_TEST( MaxLength
);
61 SINGLE_AND_MULTI_TESTS();
63 // Now switch to the multi-line text controls.
64 CPPUNIT_TEST( PseudoTestSwitchToMultiLineStyle
);
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
);
78 SINGLE_AND_MULTI_TESTS();
81 // All tests from now on are for multi-line controls only.
82 CPPUNIT_TEST( MultiLineReplace
);
83 //WXUISIM_TEST( ProcessEnter );
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();
94 void PseudoTestSwitchToMultiLineStyle()
96 ms_style
= wxTE_MULTILINE
;
99 void MultiLineReplace();
104 //void ProcessEnter();
110 void PositionToCoords();
111 void PositionToCoordsRich();
112 void PositionToCoordsRich2();
114 void DoPositionToCoordsTestWithStyle(long style
);
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
);
122 static long ms_style
;
124 DECLARE_NO_COPY_CLASS(TextCtrlTestCase
)
127 // register in the unnamed registry so that these tests are run by default
128 CPPUNIT_TEST_SUITE_REGISTRATION( TextCtrlTestCase
);
130 // also include in its own registry so that these tests can be run alone
131 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( TextCtrlTestCase
, "TextCtrlTestCase" );
133 // ----------------------------------------------------------------------------
134 // test initialization
135 // ----------------------------------------------------------------------------
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;
141 void TextCtrlTestCase::CreateText(long extraStyles
)
144 if ( ms_style
== wxTE_MULTILINE
)
145 size
= wxSize(400, TEXT_HEIGHT
);
147 m_text
= new wxTextCtrl(wxTheApp
->GetTopWindow(), wxID_ANY
, "",
148 wxDefaultPosition
, size
,
149 ms_style
| extraStyles
);
152 void TextCtrlTestCase::setUp()
154 CreateText(ms_style
);
157 void TextCtrlTestCase::tearDown()
162 // ----------------------------------------------------------------------------
164 // ----------------------------------------------------------------------------
166 void TextCtrlTestCase::MultiLineReplace()
168 m_text
->SetValue("Hello replace\n"
170 m_text
->SetInsertionPoint(0);
172 m_text
->Replace(6, 13, "changed");
174 CPPUNIT_ASSERT_EQUAL("Hello changed\n"
177 CPPUNIT_ASSERT_EQUAL(13, m_text
->GetInsertionPoint());
179 m_text
->Replace(13, -1, "");
180 CPPUNIT_ASSERT_EQUAL("Hello changed", m_text
->GetValue());
181 CPPUNIT_ASSERT_EQUAL(13, m_text
->GetInsertionPoint());
184 void TextCtrlTestCase::ReadOnly()
186 #if wxUSE_UIACTIONSIMULATOR
187 // we need a read only control for this test so recreate it
189 CreateText(wxTE_READONLY
);
191 EventCounter
updated(m_text
, wxEVT_COMMAND_TEXT_UPDATED
);
195 wxUIActionSimulator sim
;
199 CPPUNIT_ASSERT_EQUAL("", m_text
->GetValue());
200 CPPUNIT_ASSERT_EQUAL(0, updated
.GetCount());
202 // SetEditable() is supposed to override wxTE_READONLY
203 m_text
->SetEditable(true);
208 CPPUNIT_ASSERT_EQUAL("abcdef", m_text
->GetValue());
209 CPPUNIT_ASSERT_EQUAL(6, updated
.GetCount());
213 void TextCtrlTestCase::MaxLength()
215 #if wxUSE_UIACTIONSIMULATOR
216 EventCounter
updated(m_text
, wxEVT_COMMAND_TEXT_UPDATED
);
217 EventCounter
maxlen(m_text
, wxEVT_COMMAND_TEXT_MAXLEN
);
220 m_text
->SetMaxLength(10);
222 wxUIActionSimulator sim
;
226 CPPUNIT_ASSERT_EQUAL(0, maxlen
.GetCount());
231 CPPUNIT_ASSERT_EQUAL(0, maxlen
.GetCount());
232 CPPUNIT_ASSERT_EQUAL(10, updated
.GetCount());
240 CPPUNIT_ASSERT_EQUAL(1, maxlen
.GetCount());
241 CPPUNIT_ASSERT_EQUAL(0, updated
.GetCount());
246 m_text
->SetMaxLength(0);
251 CPPUNIT_ASSERT_EQUAL(0, maxlen
.GetCount());
252 CPPUNIT_ASSERT_EQUAL(1, updated
.GetCount());
256 void TextCtrlTestCase::StreamInput()
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
);
264 *m_text
<< "stringinput"
273 CPPUNIT_ASSERT_EQUAL("stringinput1010003.142.71ab", m_text
->GetValue());
275 m_text
->SetValue("");
277 #if wxHAS_TEXT_WINDOW_STREAM
279 std::ostream
stream(m_text
);
281 // We don't test a wide character as this is not a wide stream
282 stream
<< "stringinput"
291 CPPUNIT_ASSERT_EQUAL("stringinput1010003.142.71a", m_text
->GetValue());
293 #endif // wxHAS_TEXT_WINDOW_STREAM
297 void TextCtrlTestCase::Redirector()
299 #if wxHAS_TEXT_WINDOW_STREAM && wxUSE_STD_IOSTREAM
301 wxStreamToTextRedirector
redirect(m_text
);
303 std::cout
<< "stringinput"
310 CPPUNIT_ASSERT_EQUAL("stringinput1010003.142.71a", m_text
->GetValue());
316 void TextCtrlTestCase::ProcessEnter()
318 #if wxUSE_UIACTIONSIMULATOR
319 wxTestableFrame
* frame
= wxStaticCast(wxTheApp
->GetTopWindow(),
322 EventCounter
count(m_text
, wxEVT_COMMAND_TEXT_ENTER
);
326 wxUIActionSimulator sim
;
327 sim
.Char(WXK_RETURN
);
330 CPPUNIT_ASSERT_EQUAL(0, frame
->GetEventCount(wxEVT_COMMAND_TEXT_ENTER
));
332 // we need a text control with wxTE_PROCESS_ENTER for this test
334 CreateText(wxTE_PROCESS_ENTER
);
338 sim
.Char(WXK_RETURN
);
341 CPPUNIT_ASSERT_EQUAL(1, frame
->GetEventCount(wxEVT_COMMAND_TEXT_ENTER
));
346 void TextCtrlTestCase::Url()
348 #if wxUSE_UIACTIONSIMULATOR && defined(__WXMSW__)
350 CreateText(wxTE_RICH
| wxTE_AUTO_URL
);
352 EventCounter
url(m_text
, wxEVT_COMMAND_TEXT_URL
);
354 m_text
->AppendText("http://www.wxwidgets.org");
356 wxUIActionSimulator sim
;
357 sim
.MouseMove(m_text
->ClientToScreen(wxPoint(5, 5)));
361 CPPUNIT_ASSERT_EQUAL(1, url
.GetCount());
365 void TextCtrlTestCase::Style()
369 // We need wxTE_RICH under windows for style support
370 CreateText(wxTE_RICH
);
372 // Red text on a white background
373 m_text
->SetDefaultStyle(wxTextAttr(*wxRED
, *wxWHITE
));
375 CPPUNIT_ASSERT_EQUAL(m_text
->GetDefaultStyle().GetTextColour(), *wxRED
);
376 CPPUNIT_ASSERT_EQUAL(m_text
->GetDefaultStyle().GetBackgroundColour(),
379 m_text
->AppendText("red on white ");
381 // Red text on a grey background
382 m_text
->SetDefaultStyle(wxTextAttr(wxNullColour
, *wxLIGHT_GREY
));
384 CPPUNIT_ASSERT_EQUAL(m_text
->GetDefaultStyle().GetTextColour(), *wxRED
);
385 CPPUNIT_ASSERT_EQUAL(m_text
->GetDefaultStyle().GetBackgroundColour(),
388 m_text
->AppendText("red on grey ");
390 // Blue text on a grey background
391 m_text
->SetDefaultStyle(wxTextAttr(*wxBLUE
));
394 CPPUNIT_ASSERT_EQUAL(m_text
->GetDefaultStyle().GetTextColour(), *wxBLUE
);
395 CPPUNIT_ASSERT_EQUAL(m_text
->GetDefaultStyle().GetBackgroundColour(),
398 m_text
->AppendText("blue on grey");
400 // Get getting the style at a specific location
403 // We have to check that styles are supported
404 if(m_text
->GetStyle(3, style
))
406 CPPUNIT_ASSERT_EQUAL(style
.GetTextColour(), *wxRED
);
407 CPPUNIT_ASSERT_EQUAL(style
.GetBackgroundColour(), *wxWHITE
);
410 // And then setting the style
411 if(m_text
->SetStyle(15, 18, style
))
413 m_text
->GetStyle(17, style
);
415 CPPUNIT_ASSERT_EQUAL(style
.GetTextColour(), *wxRED
);
416 CPPUNIT_ASSERT_EQUAL(style
.GetBackgroundColour(), *wxWHITE
);
421 void TextCtrlTestCase::FontStyle()
423 // We need wxTE_RICH under MSW and wxTE_MULTILINE under GTK for style
424 // support so recreate the control with these styles.
426 CreateText(wxTE_RICH
);
428 // Check that we get back the same font from GetStyle() after setting it
429 // with SetDefaultStyle().
431 wxFONTFAMILY_DEFAULT
,
433 wxFONTWEIGHT_NORMAL
);
435 attrIn
.SetFont(fontIn
);
436 if ( !m_text
->SetDefaultStyle(attrIn
) )
438 // Skip the test if the styles are not supported.
442 m_text
->AppendText("Default font size 14");
445 m_text
->GetStyle(5, attrOut
);
447 CPPUNIT_ASSERT( attrOut
.HasFont() );
449 wxFont fontOut
= attrOut
.GetFont();
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());
456 CPPUNIT_ASSERT_EQUAL( fontIn
, fontOut
);
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
);
465 m_text
->GetStyle(4, attrOut
);
466 CPPUNIT_ASSERT( attrOut
.HasFont() );
468 fontOut
= attrOut
.GetFont();
470 fontOut
.SetEncoding(fontIn
.GetEncoding());
472 CPPUNIT_ASSERT_EQUAL( fontIn
, fontOut
);
475 void TextCtrlTestCase::Lines()
477 m_text
->SetValue("line1\nline2\nlong long line 3");
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));
486 m_text
->AppendText("\n\nMore text on line 5");
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));
492 // Verify that wrapped lines count as 2 lines.
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());
501 CPPUNIT_ASSERT_EQUAL(7, m_text
->GetNumberOfLines());
505 void TextCtrlTestCase::LogTextCtrl()
507 CPPUNIT_ASSERT(m_text
->IsEmpty());
509 wxLogTextCtrl
* logtext
= new wxLogTextCtrl(m_text
);
511 wxLog
* old
= wxLog::SetActiveTarget(logtext
);
513 logtext
->LogText("text");
515 delete wxLog::SetActiveTarget(old
);
517 CPPUNIT_ASSERT(!m_text
->IsEmpty());
520 void TextCtrlTestCase::PositionToCoords()
522 DoPositionToCoordsTestWithStyle(0);
525 void TextCtrlTestCase::PositionToCoordsRich()
527 DoPositionToCoordsTestWithStyle(wxTE_RICH
);
530 void TextCtrlTestCase::PositionToCoordsRich2()
532 DoPositionToCoordsTestWithStyle(wxTE_RICH2
);
535 void TextCtrlTestCase::DoPositionToCoordsTestWithStyle(long style
)
540 // Asking for invalid index should fail.
541 WX_ASSERT_FAILS_WITH_ASSERT( m_text
->PositionToCoords(1) );
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
)
548 #if defined(__WXMSW__) || defined(__WXGTK20__)
549 CPPUNIT_FAIL( "PositionToCoords() unexpectedly failed." );
554 CPPUNIT_ASSERT(pos0
.x
>= 0);
555 CPPUNIT_ASSERT(pos0
.y
>= 0);
558 m_text
->SetValue("Hello");
559 wxYield(); // Let GTK layout the control correctly.
561 // Position of non-first character should be positive.
562 const long posHello4
= m_text
->PositionToCoords(4).x
;
563 CPPUNIT_ASSERT( posHello4
> 0 );
565 // Asking for position beyond the last character should succeed and return
566 // reasonable result.
567 CPPUNIT_ASSERT( m_text
->PositionToCoords(5).x
> posHello4
);
569 // But asking for the next position should fail.
570 WX_ASSERT_FAILS_WITH_ASSERT( m_text
->PositionToCoords(6) );
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
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 );
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.
586 // This shouldn't change anything for the first position coordinates.
587 CPPUNIT_ASSERT_EQUAL( pos0
, m_text
->PositionToCoords(0) );
589 // And the last one must be beyond the window boundary and so not be
590 // visible -- but getting its coordinate should still work.
593 m_text
->PositionToCoords(m_text
->GetLastPosition()).y
> TEXT_HEIGHT
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();
602 CPPUNIT_ASSERT( m_text
->PositionToCoords(0).y
< 0 );
605 m_text
->PositionToCoords(m_text
->GetInsertionPoint()).y
<= TEXT_HEIGHT
610 #endif //wxUSE_TEXTCTRL