Merge branch 'grid-uitests-gtk'
Fixes to wxUIActionSimulator allowing the tests using it to work for wxGrid in wxGTK. And some improvements and bug fixes to wxGrid itself. Closes https://github.com/wxWidgets/wxWidgets/pull/1609
This commit is contained in:
@@ -16,26 +16,20 @@
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/app.h"
|
||||
#include "wx/dcclient.h"
|
||||
#endif // WX_PRECOMP
|
||||
|
||||
#include "wx/grid.h"
|
||||
#include "wx/headerctrl.h"
|
||||
#include "testableframe.h"
|
||||
#include "asserthelper.h"
|
||||
#include "wx/uiaction.h"
|
||||
|
||||
// FIXME: A lot of mouse-related tests sporadically fail in wxGTK. This happens
|
||||
// almost all the time but sometimes the tests do pass and the failure
|
||||
// doesn't happen when debugging so this looks like some kind of event
|
||||
// dispatching/simulating problem rather than a real problem in wxGrid.
|
||||
//
|
||||
// Just disable these tests for now but it would be really great to
|
||||
// really fix the problem.
|
||||
#ifdef __WXGTK__
|
||||
#define NONGTK_TEST(test)
|
||||
#else
|
||||
#define NONGTK_TEST(test) WXUISIM_TEST(test)
|
||||
#endif
|
||||
#include "wx/stopwatch.h"
|
||||
#endif // __WXGTK__
|
||||
|
||||
#include "waitforpaint.h"
|
||||
|
||||
class GridTestCase : public CppUnit::TestCase
|
||||
{
|
||||
@@ -48,13 +42,13 @@ public:
|
||||
private:
|
||||
CPPUNIT_TEST_SUITE( GridTestCase );
|
||||
WXUISIM_TEST( CellEdit );
|
||||
NONGTK_TEST( CellClick );
|
||||
NONGTK_TEST( ReorderedColumnsCellClick );
|
||||
NONGTK_TEST( CellSelect );
|
||||
NONGTK_TEST( LabelClick );
|
||||
NONGTK_TEST( SortClick );
|
||||
WXUISIM_TEST( CellClick );
|
||||
WXUISIM_TEST( ReorderedColumnsCellClick );
|
||||
WXUISIM_TEST( CellSelect );
|
||||
WXUISIM_TEST( LabelClick );
|
||||
WXUISIM_TEST( SortClick );
|
||||
WXUISIM_TEST( Size );
|
||||
NONGTK_TEST( RangeSelect );
|
||||
WXUISIM_TEST( RangeSelect );
|
||||
CPPUNIT_TEST( Cursor );
|
||||
CPPUNIT_TEST( Selection );
|
||||
CPPUNIT_TEST( AddRowCol );
|
||||
@@ -70,18 +64,21 @@ private:
|
||||
WXUISIM_TEST( ReadOnly );
|
||||
WXUISIM_TEST( ResizeScrolledHeader );
|
||||
WXUISIM_TEST( ColumnMinWidth );
|
||||
WXUISIM_TEST( AutoSizeColumn );
|
||||
CPPUNIT_TEST( PseudoTest_NativeHeader );
|
||||
NONGTK_TEST( LabelClick );
|
||||
NONGTK_TEST( SortClick );
|
||||
WXUISIM_TEST( LabelClick );
|
||||
WXUISIM_TEST( SortClick );
|
||||
CPPUNIT_TEST( ColumnOrder );
|
||||
WXUISIM_TEST( ResizeScrolledHeader );
|
||||
WXUISIM_TEST( ColumnMinWidth );
|
||||
WXUISIM_TEST( AutoSizeColumn );
|
||||
CPPUNIT_TEST( DeleteAndAddRowCol );
|
||||
CPPUNIT_TEST( PseudoTest_NativeLabels );
|
||||
NONGTK_TEST( LabelClick );
|
||||
NONGTK_TEST( SortClick );
|
||||
WXUISIM_TEST( LabelClick );
|
||||
WXUISIM_TEST( SortClick );
|
||||
CPPUNIT_TEST( ColumnOrder );
|
||||
WXUISIM_TEST( WindowAsEditorControl );
|
||||
WXUISIM_TEST( AutoSizeColumn );
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
void CellEdit();
|
||||
@@ -108,10 +105,25 @@ private:
|
||||
void WindowAsEditorControl();
|
||||
void ResizeScrolledHeader();
|
||||
void ColumnMinWidth();
|
||||
void AutoSizeColumn();
|
||||
void PseudoTest_NativeHeader() { ms_nativeheader = true; }
|
||||
void PseudoTest_NativeLabels() { ms_nativeheader = false;
|
||||
ms_nativelabels = true; }
|
||||
|
||||
// The helper function to determine the width of the column label depending
|
||||
// on whether the native column is used.
|
||||
int GetColumnLabelWidth(wxClientDC& dc, int col, int margin) const
|
||||
{
|
||||
if (ms_nativeheader)
|
||||
return m_grid->GetGridColHeader()->GetColumnTitleWidth(col);
|
||||
|
||||
int w, h;
|
||||
dc.GetMultiLineTextExtent(m_grid->GetColLabelValue(col), &w, &h);
|
||||
return w + margin;
|
||||
}
|
||||
|
||||
void CheckFirstColAutoSize(int expected);
|
||||
|
||||
static bool ms_nativeheader;
|
||||
static bool ms_nativelabels;
|
||||
|
||||
@@ -142,8 +154,15 @@ void GridTestCase::setUp()
|
||||
if( ms_nativelabels )
|
||||
m_grid->SetUseNativeColLabels();
|
||||
|
||||
WaitForPaint waitForPaint(m_grid->GetGridWindow());
|
||||
|
||||
m_grid->Refresh();
|
||||
m_grid->Update();
|
||||
|
||||
if ( !waitForPaint.YieldUntilPainted() )
|
||||
{
|
||||
WARN("Grid not repainted until timeout expiration");
|
||||
}
|
||||
}
|
||||
|
||||
void GridTestCase::tearDown()
|
||||
@@ -176,16 +195,48 @@ void GridTestCase::CellEdit()
|
||||
|
||||
m_grid->SetFocus();
|
||||
m_grid->SetGridCursor(1, 1);
|
||||
m_grid->ShowCellEditControl();
|
||||
|
||||
wxYield();
|
||||
|
||||
sim.Text("abab");
|
||||
|
||||
// We need to wait until the editor is really shown under GTK, consider
|
||||
// that it happens once it gets focus.
|
||||
#ifdef __WXGTK__
|
||||
for ( wxStopWatch sw; wxWindow::FindFocus() == m_grid; )
|
||||
{
|
||||
if ( sw.Time() > 250 )
|
||||
{
|
||||
WARN("Editor control not shown until timeout expiration");
|
||||
break;
|
||||
}
|
||||
|
||||
wxYield();
|
||||
}
|
||||
#endif // __WXGTK__
|
||||
|
||||
sim.Char(WXK_RETURN);
|
||||
|
||||
wxYield();
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(1, created.GetCount());
|
||||
CPPUNIT_ASSERT_EQUAL(1, changing.GetCount());
|
||||
CPPUNIT_ASSERT_EQUAL(1, changed.GetCount());
|
||||
#ifdef __WXGTK__
|
||||
for ( wxStopWatch sw; wxWindow::FindFocus() != m_grid; )
|
||||
{
|
||||
if ( sw.Time() > 250 )
|
||||
{
|
||||
WARN("Editor control not hidden until timeout expiration");
|
||||
break;
|
||||
}
|
||||
|
||||
wxYield();
|
||||
}
|
||||
#endif // __WXGTK__
|
||||
|
||||
CHECK(m_grid->GetCellValue(1, 1) == "abab");
|
||||
|
||||
CHECK(created.GetCount() == 1);
|
||||
CHECK(changing.GetCount() == 1);
|
||||
CHECK(changed.GetCount() == 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -770,7 +821,6 @@ void GridTestCase::Editable()
|
||||
|
||||
m_grid->SetFocus();
|
||||
m_grid->SetGridCursor(1, 1);
|
||||
m_grid->ShowCellEditControl();
|
||||
|
||||
sim.Text("abab");
|
||||
wxYield();
|
||||
@@ -797,12 +847,20 @@ void GridTestCase::ReadOnly()
|
||||
CPPUNIT_ASSERT(m_grid->IsReadOnly(1, 1));
|
||||
|
||||
m_grid->SetFocus();
|
||||
|
||||
#ifdef __WXGTK__
|
||||
// This is a mystery, but we somehow get WXK_RETURN generated by the
|
||||
// previous test (Editable) in this one. In spite of wxYield() in that
|
||||
// test, the key doesn't get dispatched there and we have to consume it
|
||||
// here before setting the current grid cell, as getting WXK_RETURN later
|
||||
// would move the selection down, to a non read-only cell.
|
||||
wxYield();
|
||||
#endif // __WXGTK__
|
||||
|
||||
m_grid->SetGridCursor(1, 1);
|
||||
|
||||
CPPUNIT_ASSERT(m_grid->IsCurrentCellReadOnly());
|
||||
|
||||
m_grid->ShowCellEditControl();
|
||||
|
||||
sim.Text("abab");
|
||||
wxYield();
|
||||
|
||||
@@ -950,4 +1008,94 @@ void GridTestCase::ColumnMinWidth()
|
||||
#endif
|
||||
}
|
||||
|
||||
void GridTestCase::CheckFirstColAutoSize(int expected)
|
||||
{
|
||||
m_grid->AutoSizeColumn(0);
|
||||
|
||||
wxYield();
|
||||
CHECK(m_grid->GetColSize(0) == expected);
|
||||
}
|
||||
|
||||
void GridTestCase::AutoSizeColumn()
|
||||
{
|
||||
// Hardcoded extra margin for the columns used in grid.cpp.
|
||||
const int margin = m_grid->FromDIP(10);
|
||||
|
||||
wxGridCellAttr *attr = m_grid->GetOrCreateCellAttr(0, 0);
|
||||
wxGridCellRenderer *renderer = attr->GetRenderer(m_grid, 0, 0);
|
||||
REQUIRE(renderer != NULL);
|
||||
|
||||
wxClientDC dcCell(m_grid->GetGridWindow());
|
||||
|
||||
wxClientDC dcLabel(m_grid->GetGridWindow());
|
||||
dcLabel.SetFont(m_grid->GetLabelFont());
|
||||
|
||||
const wxString shortStr = "W";
|
||||
const wxString mediumStr = "WWWW";
|
||||
const wxString longStr = "WWWWWWWW";
|
||||
const wxString multilineStr = mediumStr + "\n" + longStr;
|
||||
|
||||
SECTION("Empty column and label")
|
||||
{
|
||||
m_grid->SetColLabelValue(0, wxString());
|
||||
CheckFirstColAutoSize( m_grid->GetDefaultColSize() );
|
||||
}
|
||||
|
||||
SECTION("Empty column with label")
|
||||
{
|
||||
m_grid->SetColLabelValue(0, mediumStr);
|
||||
CheckFirstColAutoSize( GetColumnLabelWidth(dcLabel, 0, margin) );
|
||||
}
|
||||
|
||||
SECTION("Column with empty label")
|
||||
{
|
||||
m_grid->SetColLabelValue(0, wxString());
|
||||
m_grid->SetCellValue(0, 0, mediumStr);
|
||||
m_grid->SetCellValue(1, 0, shortStr);
|
||||
m_grid->SetCellValue(3, 0, longStr);
|
||||
|
||||
CheckFirstColAutoSize(
|
||||
renderer->GetBestWidth(*m_grid, *attr, dcCell, 3, 0,
|
||||
m_grid->GetRowHeight(3)) + margin );
|
||||
}
|
||||
|
||||
SECTION("Column with label longer than contents")
|
||||
{
|
||||
m_grid->SetColLabelValue(0, multilineStr);
|
||||
m_grid->SetCellValue(0, 0, mediumStr);
|
||||
m_grid->SetCellValue(1, 0, shortStr);
|
||||
CheckFirstColAutoSize( GetColumnLabelWidth(dcLabel, 0, margin) );
|
||||
}
|
||||
|
||||
SECTION("Column with contents longer than label")
|
||||
{
|
||||
m_grid->SetColLabelValue(0, mediumStr);
|
||||
m_grid->SetCellValue(0, 0, mediumStr);
|
||||
m_grid->SetCellValue(1, 0, shortStr);
|
||||
m_grid->SetCellValue(3, 0, multilineStr);
|
||||
CheckFirstColAutoSize(
|
||||
renderer->GetBestWidth(*m_grid, *attr, dcCell, 3, 0,
|
||||
m_grid->GetRowHeight(3)) + margin );
|
||||
}
|
||||
|
||||
SECTION("Column with equally sized contents and label")
|
||||
{
|
||||
m_grid->SetColLabelValue(0, mediumStr);
|
||||
m_grid->SetCellValue(0, 0, mediumStr);
|
||||
m_grid->SetCellValue(1, 0, mediumStr);
|
||||
m_grid->SetCellValue(3, 0, mediumStr);
|
||||
|
||||
const int labelWidth = GetColumnLabelWidth(dcLabel, 0, margin);
|
||||
|
||||
const int cellWidth =
|
||||
renderer->GetBestWidth(*m_grid, *attr, dcCell, 3, 0,
|
||||
m_grid->GetRowHeight(3))
|
||||
+ margin;
|
||||
|
||||
// We can't be sure which size will be greater because of different fonts
|
||||
// so just calculate the maximum width.
|
||||
CheckFirstColAutoSize( wxMax(labelWidth, cellWidth) );
|
||||
}
|
||||
}
|
||||
|
||||
#endif //wxUSE_GRID
|
||||
|
@@ -577,11 +577,11 @@ bool TestApp::ProcessEvent(wxEvent& event)
|
||||
int TestApp::RunTests()
|
||||
{
|
||||
#if wxUSE_LOG
|
||||
// Switch off logging unless --verbose
|
||||
bool verbose = wxLog::GetVerbose();
|
||||
wxLog::EnableLogging(verbose);
|
||||
#else
|
||||
bool verbose = false;
|
||||
// Switch off logging to avoid interfering with the tests output unless
|
||||
// WXTRACE is set, as otherwise setting it would have no effect while
|
||||
// running the tests.
|
||||
if ( !wxGetEnv("WXTRACE", NULL) )
|
||||
wxLog::EnableLogging(false);
|
||||
#endif
|
||||
|
||||
// Cast is needed under MSW where Catch also provides an overload taking
|
||||
|
96
tests/waitforpaint.h
Normal file
96
tests/waitforpaint.h
Normal file
@@ -0,0 +1,96 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Name: tests/waitforpaint.h
|
||||
// Purpose: Helper WaitForPaint class
|
||||
// Author: Vadim Zeitlin
|
||||
// Created: 2019-10-17 (extracted from tests/window/setsize.cpp)
|
||||
// Copyright: (c) 2019 Vadim Zeitlin <vadim@wxwidgets.org>
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _WX_TESTS_WAITFORPAINT_H_
|
||||
#define _WX_TESTS_WAITFORPAINT_H_
|
||||
|
||||
#include "wx/stopwatch.h"
|
||||
#include "wx/window.h"
|
||||
|
||||
// Class used to check if we received the (first) paint event: this is
|
||||
// currently used under GTK only, as MSW doesn't seem to need to wait for the
|
||||
// things to work, while under Mac nothing works anyhow.
|
||||
#ifdef __WXGTK__
|
||||
|
||||
class WaitForPaint
|
||||
{
|
||||
public:
|
||||
explicit WaitForPaint(wxWindow* win)
|
||||
: m_win(*win),
|
||||
m_painted(false),
|
||||
m_handler(&m_painted)
|
||||
{
|
||||
m_win.Bind(wxEVT_PAINT, m_handler);
|
||||
}
|
||||
|
||||
// This function waits up to the given number of milliseconds for the paint
|
||||
// event to come and returns true if we did get it or false otherwise.
|
||||
bool YieldUntilPainted(int timeoutInMS = 250) const
|
||||
{
|
||||
wxStopWatch sw;
|
||||
for ( ;; )
|
||||
{
|
||||
wxYield();
|
||||
|
||||
if ( m_painted )
|
||||
return true;
|
||||
|
||||
if ( sw.Time() > timeoutInMS )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
~WaitForPaint()
|
||||
{
|
||||
m_win.Unbind(wxEVT_PAINT, m_handler);
|
||||
}
|
||||
|
||||
private:
|
||||
wxWindow& m_win;
|
||||
bool m_painted;
|
||||
|
||||
class PaintHandler
|
||||
{
|
||||
public:
|
||||
// Note that we have to use a pointer here, i.e. we can't just store
|
||||
// the flag inside the class itself because it's going to be cloned
|
||||
// inside wx and querying the flag of the original copy wouldtn' work.
|
||||
explicit PaintHandler(bool* painted)
|
||||
: m_painted(*painted)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(wxPaintEvent& event)
|
||||
{
|
||||
event.Skip();
|
||||
m_painted = true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool& m_painted;
|
||||
} m_handler;
|
||||
};
|
||||
|
||||
#else // !__WXGTK__
|
||||
|
||||
class WaitForPaint
|
||||
{
|
||||
public:
|
||||
explicit WaitForPaint(wxWindow* WXUNUSED(win))
|
||||
{
|
||||
}
|
||||
|
||||
bool YieldUntilPainted(int WXUNUSED(timeoutInMS) = 250) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // __WXGTK__/!__WXGTK__
|
||||
|
||||
#endif // _WX_TESTS_WAITFORPAINT_H_
|
@@ -23,9 +23,9 @@
|
||||
#endif // WX_PRECOMP
|
||||
|
||||
#include "wx/scopedptr.h"
|
||||
#include "wx/stopwatch.h"
|
||||
|
||||
#include "asserthelper.h"
|
||||
#include "waitforpaint.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// tests helpers
|
||||
@@ -47,57 +47,6 @@ protected:
|
||||
virtual wxSize DoGetBestSize() const wxOVERRIDE { return wxSize(50, 250); }
|
||||
};
|
||||
|
||||
// Class used to check if we received the (first) paint event.
|
||||
class WaitForPaint
|
||||
{
|
||||
public:
|
||||
// Note that we have to use a pointer here, i.e. we can't just store the
|
||||
// flag inside the class itself because it's going to be cloned inside wx
|
||||
// and querying the flag of the original copy is not going to work.
|
||||
explicit WaitForPaint(bool* painted)
|
||||
: m_painted(*painted)
|
||||
{
|
||||
m_painted = false;
|
||||
}
|
||||
|
||||
void operator()(wxPaintEvent& event)
|
||||
{
|
||||
event.Skip();
|
||||
m_painted = true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool& m_painted;
|
||||
};
|
||||
|
||||
// This function should be used to show the window and wait until we can get
|
||||
// its real geometry.
|
||||
void ShowAndWaitForPaint(wxWindow* w)
|
||||
{
|
||||
// Unfortunately showing the window is asynchronous, at least when using
|
||||
// X11, so we have to wait for some time before retrieving its true
|
||||
// geometry. And it's not clear how long should we wait, so we do it until
|
||||
// we get the first paint event -- by then the window really should have
|
||||
// its final size.
|
||||
bool painted;
|
||||
WaitForPaint waitForPaint(&painted);
|
||||
w->Bind(wxEVT_PAINT, waitForPaint);
|
||||
|
||||
w->Show();
|
||||
|
||||
wxStopWatch sw;
|
||||
while ( !painted )
|
||||
{
|
||||
wxYield();
|
||||
|
||||
if ( sw.Time() > 250 )
|
||||
{
|
||||
WARN("Didn't get a paint event until timeout expiration");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -143,7 +92,19 @@ TEST_CASE("wxWindow::MovePreservesSize", "[window][size][move]")
|
||||
wxScopedPtr<wxWindow>
|
||||
w(new wxFrame(wxTheApp->GetTopWindow(), wxID_ANY, "Test child frame"));
|
||||
|
||||
ShowAndWaitForPaint(w.get());
|
||||
// Unfortunately showing the window is asynchronous, at least when using
|
||||
// X11, so we have to wait for some time before retrieving its true
|
||||
// geometry. And it's not clear how long should we wait, so we do it until
|
||||
// we get the first paint event -- by then the window really should have
|
||||
// its final size.
|
||||
WaitForPaint waitForPaint(w.get());
|
||||
|
||||
w->Show();
|
||||
|
||||
if ( !waitForPaint.YieldUntilPainted() )
|
||||
{
|
||||
WARN("Didn't get a paint event until timeout expiration");
|
||||
}
|
||||
|
||||
const wxRect rectOrig = w->GetRect();
|
||||
|
||||
|
Reference in New Issue
Block a user