wxWidgets/tests/controls/gridtest.cpp
Vadim Zeitlin 9324c7bfdd Fix sending wxEVT_GRID_RANGE_SELECTED from Select{All,Col,Row}()
This was broken in to 682cb8355c (Replace "sendEvent" parameter in
wxGridSelection with "eventType", 2020-08-21) which changed Select() to
take wxEventType argument but still passed it "true" in a few places.

Fix this and add a unit test verifying that this works as expected.

Ideal would, of course, be to avoid implicit conversions from bool to
wxEventType but making wxEventType anything other than int, for which
such conversions are unavoidable, is too backwards-incompatible to
seriously consider.
2021-06-09 16:09:55 +02:00

2479 lines
68 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Name: tests/controls/gridtest.cpp
// Purpose: wxGrid unit test
// Author: Steven Lamerton
// Created: 2010-06-25
// Copyright: (c) 2010 Steven Lamerton
///////////////////////////////////////////////////////////////////////////////
#include "testprec.h"
#if wxUSE_GRID
#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"
#ifdef __WXGTK__
#include "wx/stopwatch.h"
#endif // __WXGTK__
#include "waitforpaint.h"
namespace
{
wxString CellCoordsToString(int row, int col)
{
return wxString::Format("R%dC%d", col + 1, row + 1);
}
wxString CellSizeToString(int rows, int cols)
{
return wxString::Format("%dx%d", rows, cols);
}
struct Multicell
{
int row, col, rows, cols;
Multicell(int row, int col, int rows, int cols)
: row(row), col(col), rows(rows), cols(cols) { }
wxString Coords() const { return CellCoordsToString(row, col); }
wxString Size() const { return CellSizeToString(rows, cols); }
wxString ToString() const
{
wxString s;
if ( rows == 1 && cols == 1 )
{
s = "cell";
}
else
{
s = Size() + " ";
if ( rows > 1 || cols > 1 )
s += "multicell";
else
s += "inside cell";
}
return wxString::Format("%s at %s", s, Coords());
}
};
// Stores insertion/deletion info of rows or columns.
struct EditInfo
{
int pos, count;
wxGridDirection direction;
EditInfo(int pos = 0,
int count = 0,
wxGridDirection direction = wxGRID_COLUMN)
: pos(pos), count(count), direction(direction) { }
};
// Derive a new class inheriting from wxGrid, also to get access to its
// protected GetCellAttr(). This is not pretty, but we don't have any other way
// of testing this function.
class TestableGrid : public wxGrid
{
public:
explicit TestableGrid(wxWindow* parent)
: wxGrid(parent, wxID_ANY)
{
}
wxGridCellAttr* CallGetCellAttr(int row, int col) const
{
return GetCellAttr(row, col);
}
bool HasAttr(int row, int col,
wxGridCellAttr::wxAttrKind kind = wxGridCellAttr::Cell) const
{
// Can't use GetCellAttr() here as it always returns an attr.
wxGridCellAttr* attr = GetTable()->GetAttr(row, col, kind);
if ( attr )
attr->DecRef();
return attr != NULL;
}
size_t GetCellAttrCount() const
{
// Note that only attributes in grid range can easily be checked
// and this function only counts those, not any outside of
// the grid (e.g. with invalid negative coords).
size_t count = 0;
for ( int row = 0; row < GetNumberRows(); ++row )
{
for ( int col = 0; col < GetNumberCols(); ++col )
count += HasAttr(row, col);
}
return count;
}
void SetMulticell(const Multicell& multi)
{
SetCellSize(multi.row, multi.col, multi.rows, multi.cols);
}
// Performs given insertions/deletions on either rows or columns.
void DoEdit(const EditInfo& edit);
// Returns annotated grid represented as a string.
wxString ToString() const;
// Used when drawing annotated grid to know what happens to it.
EditInfo m_edit;
// Grid as string before editing, with edit info annotated.
wxString m_beforeGridAnnotated;
};
// Compares two grids, checking for differences with attribute presence and
// cell sizes.
class GridAttrMatcher : public Catch::MatcherBase<TestableGrid>
{
public:
GridAttrMatcher(const TestableGrid& grid);
bool match(const TestableGrid& other) const wxOVERRIDE;
std::string describe() const wxOVERRIDE;
private:
const TestableGrid* m_grid;
mutable wxString m_diffDesc;
mutable wxString m_expectedGridDesc;
};
// Helper function for recreating a grid to fit (only) a multicell.
void FitGridToMulticell(TestableGrid* grid, const Multicell& multi)
{
const int oldRowCount = grid->GetNumberRows();
const int oldColCount = grid->GetNumberCols();
const int margin = 1;
const int newRowCount = multi.row + multi.rows + margin;
const int newColCount = multi.col + multi.cols + margin;
if ( !oldRowCount && !oldColCount )
{
grid->CreateGrid(newRowCount, newColCount);
}
else
{
grid->DeleteRows(0, oldRowCount);
grid->DeleteCols(0, oldColCount);
grid->AppendRows(newRowCount);
grid->AppendCols(newColCount);
}
}
} // anonymous namespace
namespace Catch
{
template <> struct StringMaker<TestableGrid>
{
static std::string convert(const TestableGrid& grid)
{
return ("Content before edit:\n" + grid.m_beforeGridAnnotated
+ "\nContent after edit:\n" + grid.ToString()).ToStdString();
}
};
template <> struct StringMaker<Multicell>
{
static std::string convert(const Multicell& multi)
{
return multi.ToString().ToStdString();
}
};
} // namespace Catch
class GridTestCase
{
public:
GridTestCase();
~GridTestCase();
protected:
void InsertRows(int pos = 0, int count = 1)
{
m_grid->DoEdit(EditInfo(pos, count, wxGRID_ROW));
}
void InsertCols(int pos = 0, int count = 1)
{
m_grid->DoEdit(EditInfo(pos, count, wxGRID_COLUMN));
}
void DeleteRows(int pos = 0, int count = 1)
{
m_grid->DoEdit(EditInfo(pos, -count, wxGRID_ROW));
}
void DeleteCols(int pos = 0, int count = 1)
{
m_grid->DoEdit(EditInfo(pos, -count, wxGRID_COLUMN));
}
// The helper function to determine the width of the column label depending
// on whether the native column header is used.
int GetColumnLabelWidth(wxClientDC& dc, int col, int margin) const
{
if ( m_grid->IsUsingNativeHeader() )
return m_grid->GetGridColHeader()->GetColumnTitleWidth(col);
int w, h;
dc.GetMultiLineTextExtent(m_grid->GetColLabelValue(col), &w, &h);
return w + margin;
}
void CheckFirstColAutoSize(int expected);
// Helper to check that the selection is equal to the specified block.
void CheckSelection(const wxGridBlockCoords& block)
{
const wxGridBlocks selected = m_grid->GetSelectedBlocks();
wxGridBlocks::iterator it = selected.begin();
REQUIRE( it != selected.end() );
CHECK( *it == block );
CHECK( ++it == selected.end() );
}
// Or specified ranges.
struct RowRange
{
RowRange(int top, int bottom) : top(top), bottom(bottom) { }
int top, bottom;
};
typedef wxVector<RowRange> RowRanges;
void CheckRowSelection(const RowRanges& ranges)
{
const wxGridBlockCoordsVector sel = m_grid->GetSelectedRowBlocks();
REQUIRE( sel.size() == ranges.size() );
for ( size_t n = 0; n < sel.size(); ++n )
{
INFO("n = " << n);
const RowRange& r = ranges[n];
CHECK( sel[n] == wxGridBlockCoords(r.top, 0, r.bottom, 1) );
}
}
bool HasCellAttr(int row, int col) const
{
return m_grid->HasAttr(row, col, wxGridCellAttr::Cell);
}
void SetCellAttr(int row, int col)
{
m_grid->SetAttr(row, col, new wxGridCellAttr);
}
// Fills temp. grid with a multicell and returns a matcher with it.
GridAttrMatcher HasMulticellOnly(const Multicell& multi)
{
return CheckMulticell(multi);
}
// Returns a matcher with empty (temp.) grid.
GridAttrMatcher HasEmptyGrid()
{
return CheckMulticell(Multicell(0, 0, 1, 1));
}
// Helper function used by the previous two functions.
GridAttrMatcher CheckMulticell(const Multicell& multi)
{
TestableGrid* grid = GetTempGrid();
FitGridToMulticell(grid, multi);
if ( multi.rows > 1 || multi.cols > 1 )
grid->SetMulticell(multi);
return GridAttrMatcher(*grid);
}
TestableGrid* GetTempGrid()
{
if ( !m_tempGrid )
{
m_tempGrid = new TestableGrid(wxTheApp->GetTopWindow());
m_tempGrid->Hide();
}
return m_tempGrid;
}
TestableGrid *m_grid;
// Temporary/scratch grid filled with expected content, used when
// comparing against m_grid.
TestableGrid *m_tempGrid;
wxDECLARE_NO_COPY_CLASS(GridTestCase);
};
GridTestCase::GridTestCase() : m_tempGrid(NULL)
{
m_grid = new TestableGrid(wxTheApp->GetTopWindow());
m_grid->CreateGrid(10, 2);
m_grid->SetSize(400, 200);
WaitForPaint waitForPaint(m_grid->GetGridWindow());
m_grid->Refresh();
m_grid->Update();
waitForPaint.YieldUntilPainted();
}
GridTestCase::~GridTestCase()
{
// This is just a hack to continue the rest of the tests to run: if we
// destroy the header control while it has capture, this results in an
// assert failure and while handling an exception from it more bad things
// happen (as it's thrown from a dtor), resulting in simply aborting
// everything. So ensure that it doesn't have capture in any case.
//
// Of course, the right thing to do would be to understand why does it
// still have capture when the grid is destroyed sometimes.
wxWindow* const win = wxWindow::GetCapture();
if ( win )
win->ReleaseMouse();
wxDELETE(m_grid);
delete m_tempGrid;
}
TEST_CASE_METHOD(GridTestCase, "Grid::CellEdit", "[grid]")
{
// TODO on OSX when running the grid test suite solo this works
// but not when running it together with other tests
#if wxUSE_UIACTIONSIMULATOR && !defined(__WXOSX__)
if ( !EnableUITests() )
return;
EventCounter changing(m_grid, wxEVT_GRID_CELL_CHANGING);
EventCounter changed(m_grid, wxEVT_GRID_CELL_CHANGED);
EventCounter created(m_grid, wxEVT_GRID_EDITOR_CREATED);
wxUIActionSimulator sim;
m_grid->SetFocus();
m_grid->SetGridCursor(1, 1);
wxYield();
sim.Text("abab");
wxYield();
sim.Char(WXK_RETURN);
wxYield();
CHECK(m_grid->GetCellValue(1, 1) == "abab");
CHECK(created.GetCount() == 1);
CHECK(changing.GetCount() == 1);
CHECK(changed.GetCount() == 1);
#endif
}
TEST_CASE_METHOD(GridTestCase, "Grid::CellClick", "[grid]")
{
#if wxUSE_UIACTIONSIMULATOR
EventCounter lclick(m_grid, wxEVT_GRID_CELL_LEFT_CLICK);
EventCounter ldclick(m_grid, wxEVT_GRID_CELL_LEFT_DCLICK);
EventCounter rclick(m_grid, wxEVT_GRID_CELL_RIGHT_CLICK);
EventCounter rdclick(m_grid, wxEVT_GRID_CELL_RIGHT_DCLICK);
wxUIActionSimulator sim;
wxRect rect = m_grid->CellToRect(0, 0);
wxPoint point = m_grid->CalcScrolledPosition(rect.GetPosition());
point = m_grid->ClientToScreen(point + wxPoint(m_grid->GetRowLabelSize(),
m_grid->GetColLabelSize())
+ wxPoint(2, 2));
sim.MouseMove(point);
wxYield();
sim.MouseClick();
wxYield();
CHECK(lclick.GetCount() == 1);
lclick.Clear();
sim.MouseDblClick();
wxYield();
//A double click event sends a single click event first
//test to ensure this still happens in the future
CHECK(lclick.GetCount() == 1);
CHECK(ldclick.GetCount() == 1);
sim.MouseClick(wxMOUSE_BTN_RIGHT);
wxYield();
CHECK(rclick.GetCount() == 1);
rclick.Clear();
sim.MouseDblClick(wxMOUSE_BTN_RIGHT);
wxYield();
CHECK(rclick.GetCount() == 1);
CHECK(rdclick.GetCount() == 1);
#endif
}
TEST_CASE_METHOD(GridTestCase, "Grid::ReorderedColumnsCellClick", "[grid]")
{
#if wxUSE_UIACTIONSIMULATOR
EventCounter click(m_grid, wxEVT_GRID_CELL_LEFT_CLICK);
wxUIActionSimulator sim;
wxArrayInt neworder;
neworder.push_back(1);
neworder.push_back(0);
m_grid->SetColumnsOrder(neworder);
wxRect rect = m_grid->CellToRect(0, 1);
wxPoint point = m_grid->CalcScrolledPosition(rect.GetPosition());
point = m_grid->ClientToScreen(point + wxPoint(m_grid->GetRowLabelSize(),
m_grid->GetColLabelSize())
+ wxPoint(2, 2));
sim.MouseMove(point);
wxYield();
sim.MouseClick();
wxYield();
CHECK(click.GetCount() == 1);
#endif
}
TEST_CASE_METHOD(GridTestCase, "Grid::CellSelect", "[grid]")
{
#if wxUSE_UIACTIONSIMULATOR
EventCounter cell(m_grid, wxEVT_GRID_SELECT_CELL);
wxUIActionSimulator sim;
wxRect rect = m_grid->CellToRect(0, 0);
wxPoint point = m_grid->CalcScrolledPosition(rect.GetPosition());
point = m_grid->ClientToScreen(point + wxPoint(m_grid->GetRowLabelSize(),
m_grid->GetColLabelSize())
+ wxPoint(4, 4));
sim.MouseMove(point);
wxYield();
sim.MouseClick();
wxYield();
CHECK(cell.GetCount() == 1);
cell.Clear();
m_grid->SetGridCursor(1, 1);
m_grid->GoToCell(1, 0);
sim.MouseMove(point);
wxYield();
sim.MouseDblClick();
wxYield();
CHECK(cell.GetCount() == 3);
#endif
}
TEST_CASE_METHOD(GridTestCase, "Grid::LabelClick", "[grid]")
{
#if wxUSE_UIACTIONSIMULATOR
if ( !EnableUITests() )
return;
SECTION("Default") {}
SECTION("Native header") { m_grid->UseNativeColHeader(); }
SECTION("Native labels") { m_grid->SetUseNativeColLabels(); }
EventCounter lclick(m_grid, wxEVT_GRID_LABEL_LEFT_CLICK);
EventCounter ldclick(m_grid, wxEVT_GRID_LABEL_LEFT_DCLICK);
EventCounter rclick(m_grid, wxEVT_GRID_LABEL_RIGHT_CLICK);
EventCounter rdclick(m_grid, wxEVT_GRID_LABEL_RIGHT_DCLICK);
wxUIActionSimulator sim;
wxPoint pos(m_grid->GetRowLabelSize() + 2, 2);
pos = m_grid->ClientToScreen(pos);
sim.MouseMove(pos);
wxYield();
sim.MouseClick();
wxYield();
CHECK(lclick.GetCount() == 1);
sim.MouseDblClick();
wxYield();
CHECK(ldclick.GetCount() == 1);
sim.MouseClick(wxMOUSE_BTN_RIGHT);
wxYield();
CHECK(rclick.GetCount() == 1);
rclick.Clear();
sim.MouseDblClick(wxMOUSE_BTN_RIGHT);
wxYield();
if ( m_grid->IsUsingNativeHeader() )
{
//Right double click not supported with native headers so we get two
//right click events
CHECK(rclick.GetCount() == 2);
}
else
{
CHECK(rclick.GetCount() == 1);
CHECK(rdclick.GetCount() == 1);
}
#endif
}
TEST_CASE_METHOD(GridTestCase, "Grid::SortClick", "[grid]")
{
#if wxUSE_UIACTIONSIMULATOR
if ( !EnableUITests() )
return;
SECTION("Default") {}
SECTION("Native header") { m_grid->UseNativeColHeader(); }
SECTION("Native labels") { m_grid->SetUseNativeColLabels(); }
m_grid->SetSortingColumn(0);
EventCounter sort(m_grid, wxEVT_GRID_COL_SORT);
wxUIActionSimulator sim;
wxPoint pos(m_grid->GetRowLabelSize() + 4, 4);
pos = m_grid->ClientToScreen(pos);
sim.MouseMove(pos);
wxYield();
sim.MouseClick();
wxYield();
CHECK(sort.GetCount() == 1);
#endif
}
TEST_CASE_METHOD(GridTestCase, "Grid::Size", "[grid]")
{
// TODO on OSX resizing interactively works, but not automated
// Grid could not pass the test under GTK, OSX, and Universal.
// So there may has bug in Grid implementation
#if wxUSE_UIACTIONSIMULATOR && !defined(__WXOSX__) && !defined(__WXUNIVERSAL__)
if ( !EnableUITests() )
return;
#ifdef __WXGTK20__
// Works locally, but not when run on Travis CI.
if ( IsAutomaticTest() )
return;
#endif
EventCounter colsize(m_grid, wxEVT_GRID_COL_SIZE);
EventCounter rowsize(m_grid, wxEVT_GRID_ROW_SIZE);
wxUIActionSimulator sim;
wxPoint pt = m_grid->ClientToScreen(wxPoint(m_grid->GetRowLabelSize() +
m_grid->GetColSize(0), 5));
sim.MouseMove(pt);
wxYield();
sim.MouseDown();
wxYield();
sim.MouseMove(pt.x + 50, pt.y);
wxYield();
sim.MouseUp();
wxYield();
CHECK(colsize.GetCount() == 1);
pt = m_grid->ClientToScreen(wxPoint(5, m_grid->GetColLabelSize() +
m_grid->GetRowSize(0)));
sim.MouseDragDrop(pt.x, pt.y, pt.x, pt.y + 50);
wxYield();
CHECK(rowsize.GetCount() == 1);
#endif
}
TEST_CASE_METHOD(GridTestCase, "Grid::RangeSelect", "[grid]")
{
#if wxUSE_UIACTIONSIMULATOR
if ( !EnableUITests() )
return;
EventCounter select(m_grid, wxEVT_GRID_RANGE_SELECTED);
wxUIActionSimulator sim;
//We add the extra 10 to ensure that we are inside the cell
wxPoint pt = m_grid->ClientToScreen(wxPoint(m_grid->GetRowLabelSize() + 10,
m_grid->GetColLabelSize() + 10)
);
sim.MouseMove(pt);
wxYield();
sim.MouseDown();
wxYield();
sim.MouseMove(pt.x + 50, pt.y + 50);
wxYield();
sim.MouseUp();
wxYield();
CHECK(select.GetCount() == 1);
#endif
}
TEST_CASE_METHOD(GridTestCase, "Grid::Cursor", "[grid]")
{
m_grid->SetGridCursor(1, 1);
CHECK(m_grid->GetGridCursorCol() == 1);
CHECK(m_grid->GetGridCursorRow() == 1);
m_grid->MoveCursorDown(false);
m_grid->MoveCursorLeft(false);
m_grid->MoveCursorUp(false);
m_grid->MoveCursorUp(false);
m_grid->MoveCursorRight(false);
CHECK(m_grid->GetGridCursorCol() == 1);
CHECK(m_grid->GetGridCursorRow() == 0);
m_grid->SetCellValue(0, 0, "some text");
m_grid->SetCellValue(3, 0, "other text");
m_grid->SetCellValue(0, 1, "more text");
m_grid->SetCellValue(3, 1, "extra text");
m_grid->Update();
m_grid->Refresh();
m_grid->MoveCursorLeftBlock(false);
CHECK(m_grid->GetGridCursorCol() == 0);
CHECK(m_grid->GetGridCursorRow() == 0);
m_grid->MoveCursorDownBlock(false);
CHECK(m_grid->GetGridCursorCol() == 0);
CHECK(m_grid->GetGridCursorRow() == 3);
m_grid->MoveCursorRightBlock(false);
CHECK(m_grid->GetGridCursorCol() == 1);
CHECK(m_grid->GetGridCursorRow() == 3);
m_grid->MoveCursorUpBlock(false);
CHECK(m_grid->GetGridCursorCol() == 1);
CHECK(m_grid->GetGridCursorRow() == 0);
}
TEST_CASE_METHOD(GridTestCase, "Grid::KeyboardSelection", "[grid][selection]")
{
m_grid->SetCellValue(1, 1, "R2C2");
m_grid->SetCellValue(2, 0, "R3C1");
m_grid->SetCellValue(3, 0, "R4C1");
m_grid->SetCellValue(4, 0, "R5C1");
m_grid->SetCellValue(7, 0, "R8C1");
CHECK(m_grid->GetGridCursorCoords() == wxGridCellCoords(0, 0));
m_grid->MoveCursorRight(true);
CheckSelection(wxGridBlockCoords(0, 0, 0, 1));
m_grid->MoveCursorDownBlock(true);
CheckSelection(wxGridBlockCoords(0, 0, 2, 1));
m_grid->MoveCursorDownBlock(true);
CheckSelection(wxGridBlockCoords(0, 0, 4, 1));
m_grid->MoveCursorDownBlock(true);
CheckSelection(wxGridBlockCoords(0, 0, 7, 1));
m_grid->MoveCursorUpBlock(true);
CheckSelection(wxGridBlockCoords(0, 0, 4, 1));
m_grid->MoveCursorLeft(true);
CheckSelection(wxGridBlockCoords(0, 0, 4, 0));
}
TEST_CASE_METHOD(GridTestCase, "Grid::Selection", "[grid]")
{
EventCounter select(m_grid, wxEVT_GRID_RANGE_SELECTED);
m_grid->SelectAll();
CHECK(select.GetCount() == 1);
CHECK(m_grid->IsSelection());
CHECK(m_grid->IsInSelection(0, 0));
CHECK(m_grid->IsInSelection(9, 1));
m_grid->SelectBlock(1, 0, 3, 1);
wxGridCellCoordsArray topleft = m_grid->GetSelectionBlockTopLeft();
wxGridCellCoordsArray bottomright = m_grid->GetSelectionBlockBottomRight();
CHECK(topleft.Count() == 1);
CHECK(bottomright.Count() == 1);
CHECK(topleft.Item(0).GetCol() == 0);
CHECK(topleft.Item(0).GetRow() == 1);
CHECK(bottomright.Item(0).GetCol() == 1);
CHECK(bottomright.Item(0).GetRow() == 3);
// Note that calling SelectCol() results in 2 events because there is a
// deselection event first.
select.Clear();
m_grid->SelectCol(1);
CHECK(select.GetCount() == 2);
CHECK(m_grid->IsInSelection(0, 1));
CHECK(m_grid->IsInSelection(9, 1));
CHECK(!m_grid->IsInSelection(3, 0));
// But if we explicitly avoid deselecting the existing selection, we should
// get exactly one event.
select.Clear();
m_grid->SelectRow(4, true /* add to selection */);
CHECK(select.GetCount() == 1);
CHECK(m_grid->IsInSelection(4, 0));
CHECK(m_grid->IsInSelection(4, 1));
CHECK(!m_grid->IsInSelection(3, 0));
// Check that deselecting a row does deselect the cells in it, but leaves
// the other ones selected.
m_grid->DeselectRow(4);
CHECK(!m_grid->IsInSelection(4, 0));
CHECK(!m_grid->IsInSelection(4, 1));
CHECK(m_grid->IsInSelection(0, 1));
m_grid->DeselectCol(1);
CHECK(!m_grid->IsInSelection(0, 1));
}
TEST_CASE_METHOD(GridTestCase, "Grid::SelectionRange", "[grid]")
{
const wxGridBlocks empty = m_grid->GetSelectedBlocks();
CHECK( empty.begin() == empty.end() );
m_grid->SelectBlock(1, 0, 3, 1);
wxGridBlocks sel = m_grid->GetSelectedBlocks();
REQUIRE( sel.begin() != sel.end() );
CHECK( *sel.begin() == wxGridBlockCoords(1, 0, 3, 1) );
#if __cplusplus >= 201103L || wxCHECK_VISUALC_VERSION(11)
m_grid->SelectBlock(4, 0, 7, 1, true);
int index = 0;
for ( const wxGridBlockCoords& block : m_grid->GetSelectedBlocks() )
{
switch ( index )
{
case 0:
CHECK(block == wxGridBlockCoords(1, 0, 3, 1));
break;
case 1:
CHECK(block == wxGridBlockCoords(4, 0, 7, 1));
break;
default:
FAIL("Unexpected iterations count");
break;
}
++index;
}
#endif
}
TEST_CASE_METHOD(GridTestCase, "Grid::SelectEmptyGrid", "[grid]")
{
for ( int i = 0; i < 2; ++i )
{
SECTION(i == 0 ? "No rows" : "No columns")
{
if ( i == 0 )
{
m_grid->DeleteRows(0, 10);
REQUIRE( m_grid->GetNumberRows() == 0 );
}
else
{
m_grid->DeleteCols(0, 2);
REQUIRE( m_grid->GetNumberCols() == 0 );
}
SECTION("Move right")
{
m_grid->MoveCursorRight(true);
}
SECTION("Move down")
{
m_grid->MoveCursorDown(true);
}
SECTION("Select row")
{
m_grid->SelectRow(1);
}
SECTION("Select column")
{
m_grid->SelectCol(1);
}
}
}
CHECK( m_grid->GetSelectedCells().Count() == 0 );
CHECK( m_grid->GetSelectionBlockTopLeft().Count() == 0 );
CHECK( m_grid->GetSelectionBlockBottomRight().Count() == 0 );
CHECK( m_grid->GetSelectedRows().Count() == 0 );
CHECK( m_grid->GetSelectedCols().Count() == 0 );
}
TEST_CASE_METHOD(GridTestCase, "Grid::ScrollWhenSelect", "[grid]")
{
m_grid->AppendCols(10);
REQUIRE( m_grid->GetGridCursorCol() == 0 );
REQUIRE( m_grid->GetGridCursorRow() == 0 );
REQUIRE( m_grid->IsVisible(0, 0) );
REQUIRE( !m_grid->IsVisible(0, 4) );
for ( int i = 0; i < 4; ++i )
{
m_grid->MoveCursorRight(true);
}
CHECK( m_grid->IsVisible(0, 4) );
m_grid->ClearSelection();
m_grid->GoToCell(1, 1);
for ( int i = 0; i < 8; ++i )
{
m_grid->MoveCursorDown(true);
}
CHECK( m_grid->IsVisible(9, 1) );
}
TEST_CASE_METHOD(GridTestCase, "Grid::MoveGridCursorUsingEndKey", "[grid]")
{
#if wxUSE_UIACTIONSIMULATOR
if ( !EnableUITests() )
return;
wxUIActionSimulator sim;
m_grid->AppendCols(10);
REQUIRE( m_grid->GetGridCursorCol() == 0 );
REQUIRE( m_grid->GetGridCursorRow() == 0 );
REQUIRE( m_grid->IsVisible(0, 0) );
// Hide the last row.
m_grid->HideRow(9);
// Hide the last column.
m_grid->HideCol(11);
// Move the penult column.
m_grid->SetColPos(10, 5);
m_grid->SetFocus();
sim.KeyDown(WXK_END, wxMOD_CONTROL);
sim.KeyUp(WXK_END, wxMOD_CONTROL);
wxYield();
CHECK( m_grid->GetGridCursorRow() == 8 );
CHECK( m_grid->GetGridCursorCol() == 9 );
CHECK( m_grid->IsVisible(8, 9) );
#endif
}
TEST_CASE_METHOD(GridTestCase, "Grid::SelectUsingEndKey", "[grid]")
{
#if wxUSE_UIACTIONSIMULATOR
if ( !EnableUITests() )
return;
wxUIActionSimulator sim;
m_grid->AppendCols(10);
REQUIRE( m_grid->GetGridCursorCol() == 0 );
REQUIRE( m_grid->GetGridCursorRow() == 0 );
REQUIRE( m_grid->IsVisible(0, 0) );
m_grid->SetFocus();
sim.KeyDown(WXK_END, wxMOD_CONTROL | wxMOD_SHIFT);
sim.KeyUp(WXK_END, wxMOD_CONTROL | wxMOD_SHIFT);
wxYield();
wxGridCellCoordsArray topleft = m_grid->GetSelectionBlockTopLeft();
wxGridCellCoordsArray bottomright = m_grid->GetSelectionBlockBottomRight();
CHECK( topleft.Count() == 1 );
CHECK( bottomright.Count() == 1 );
CHECK( topleft.Item(0).GetCol() == 0 );
CHECK( topleft.Item(0).GetRow() == 0 );
CHECK( bottomright.Item(0).GetCol() == 11 );
CHECK( bottomright.Item(0).GetRow() == 9 );
CHECK( m_grid->IsVisible(8, 9) );
#endif
}
TEST_CASE_METHOD(GridTestCase, "Grid::AddRowCol", "[grid]")
{
CHECK(m_grid->GetNumberRows() == 10);
CHECK(m_grid->GetNumberCols() == 2);
m_grid->AppendCols();
m_grid->AppendRows();
CHECK(m_grid->GetNumberRows() == 11);
CHECK(m_grid->GetNumberCols() == 3);
m_grid->AppendCols(2);
m_grid->AppendRows(2);
CHECK(m_grid->GetNumberRows() == 13);
CHECK(m_grid->GetNumberCols() == 5);
m_grid->InsertCols(1, 2);
m_grid->InsertRows(2, 3);
CHECK(m_grid->GetNumberRows() == 16);
CHECK(m_grid->GetNumberCols() == 7);
}
TEST_CASE_METHOD(GridTestCase, "Grid::DeleteAndAddRowCol", "[grid]")
{
SECTION("Default") {}
SECTION("Native header") { m_grid->UseNativeColHeader(); }
CHECK(m_grid->GetNumberRows() == 10);
CHECK(m_grid->GetNumberCols() == 2);
m_grid->DeleteRows(0, 10);
m_grid->DeleteCols(0, 2);
CHECK(m_grid->GetNumberRows() == 0);
CHECK(m_grid->GetNumberCols() == 0);
m_grid->AppendRows(5);
m_grid->AppendCols(3);
CHECK(m_grid->GetNumberRows() == 5);
CHECK(m_grid->GetNumberCols() == 3);
// The order of functions calls can be important
m_grid->DeleteCols(0, 3);
m_grid->DeleteRows(0, 5);
CHECK(m_grid->GetNumberRows() == 0);
CHECK(m_grid->GetNumberCols() == 0);
// Different functions calls order
m_grid->AppendCols(3);
m_grid->AppendRows(5);
}
TEST_CASE_METHOD(GridTestCase, "Grid::ColumnOrder", "[grid]")
{
SECTION("Default") {}
SECTION("Native header") { m_grid->UseNativeColHeader(); }
SECTION("Native labels") { m_grid->SetUseNativeColLabels(); }
m_grid->AppendCols(2);
CHECK(m_grid->GetNumberCols() == 4);
wxArrayInt neworder;
neworder.push_back(1);
neworder.push_back(3);
neworder.push_back(2);
neworder.push_back(0);
m_grid->SetColumnsOrder(neworder);
CHECK(m_grid->GetColPos(1) == 0);
CHECK(m_grid->GetColPos(3) == 1);
CHECK(m_grid->GetColPos(2) == 2);
CHECK(m_grid->GetColPos(0) == 3);
CHECK(m_grid->GetColAt(0) == 1);
CHECK(m_grid->GetColAt(1) == 3);
CHECK(m_grid->GetColAt(2) == 2);
CHECK(m_grid->GetColAt(3) == 0);
m_grid->ResetColPos();
CHECK(m_grid->GetColPos(0) == 0);
CHECK(m_grid->GetColPos(1) == 1);
CHECK(m_grid->GetColPos(2) == 2);
CHECK(m_grid->GetColPos(3) == 3);
}
TEST_CASE_METHOD(GridTestCase, "Grid::ColumnVisibility", "[grid]")
{
m_grid->AppendCols(3);
CHECK( m_grid->IsColShown(1) );
m_grid->HideCol(1);
CHECK( !m_grid->IsColShown(1) );
CHECK( m_grid->IsColShown(2) );
m_grid->ShowCol(1);
CHECK( m_grid->IsColShown(1) );
}
TEST_CASE_METHOD(GridTestCase, "Grid::LineFormatting", "[grid]")
{
CHECK(m_grid->GridLinesEnabled());
m_grid->EnableGridLines(false);
CHECK(!m_grid->GridLinesEnabled());
m_grid->EnableGridLines();
m_grid->SetGridLineColour(*wxRED);
CHECK(*wxRED == m_grid->GetGridLineColour());
}
TEST_CASE_METHOD(GridTestCase, "Grid::SortSupport", "[grid]")
{
CHECK(m_grid->GetSortingColumn() == wxNOT_FOUND);
m_grid->SetSortingColumn(1);
CHECK(!m_grid->IsSortingBy(0));
CHECK(m_grid->IsSortingBy(1));
CHECK(m_grid->IsSortOrderAscending());
m_grid->SetSortingColumn(0, false);
CHECK(m_grid->IsSortingBy(0));
CHECK(!m_grid->IsSortingBy(1));
CHECK(!m_grid->IsSortOrderAscending());
m_grid->UnsetSortingColumn();
CHECK(!m_grid->IsSortingBy(0));
CHECK(!m_grid->IsSortingBy(1));
}
TEST_CASE_METHOD(GridTestCase, "Grid::Labels", "[grid]")
{
CHECK(m_grid->GetColLabelValue(0) == "A");
CHECK(m_grid->GetRowLabelValue(0) == "1");
m_grid->SetColLabelValue(0, "Column 1");
m_grid->SetRowLabelValue(0, "Row 1");
CHECK(m_grid->GetColLabelValue(0) == "Column 1");
CHECK(m_grid->GetRowLabelValue(0) == "Row 1");
m_grid->SetLabelTextColour(*wxGREEN);
m_grid->SetLabelBackgroundColour(*wxRED);
CHECK(m_grid->GetLabelTextColour() == *wxGREEN);
CHECK(m_grid->GetLabelBackgroundColour() == *wxRED);
m_grid->SetColLabelTextOrientation(wxVERTICAL);
CHECK(m_grid->GetColLabelTextOrientation() == wxVERTICAL);
}
TEST_CASE_METHOD(GridTestCase, "Grid::SelectionMode", "[grid]")
{
//We already test this mode in Select
CHECK(m_grid->GetSelectionMode() == wxGrid::wxGridSelectCells);
// Select an individual cell and an entire row.
m_grid->SelectBlock(3, 1, 3, 1);
m_grid->SelectRow(5, true /* add to selection */);
// Test that after switching to row selection mode only the row remains
// selected.
m_grid->SetSelectionMode(wxGrid::wxGridSelectRows);
CHECK( m_grid->IsInSelection(5, 0) );
CHECK( m_grid->IsInSelection(5, 1) );
CHECK( !m_grid->IsInSelection(3, 1) );
//Test row selection be selecting a single cell and checking the whole
//row is selected
m_grid->ClearSelection();
m_grid->SelectBlock(3, 1, 3, 1);
wxArrayInt selectedRows = m_grid->GetSelectedRows();
CHECK(selectedRows.Count() == 1);
CHECK(selectedRows[0] == 3);
// Check that overlapping selection blocks are handled correctly.
m_grid->ClearSelection();
m_grid->SelectBlock(0, 0, 4, 1);
m_grid->SelectBlock(2, 0, 6, 1, true /* add to selection */);
CHECK( m_grid->GetSelectedRows().size() == 7 );
CHECK( m_grid->GetSelectedColBlocks().empty() );
RowRanges rowRanges;
rowRanges.push_back(RowRange(0, 6));
CheckRowSelection(rowRanges);
m_grid->SelectBlock(6, 0, 8, 1);
m_grid->SelectBlock(1, 0, 4, 1, true /* add to selection */);
m_grid->SelectBlock(0, 0, 2, 1, true /* add to selection */);
CHECK( m_grid->GetSelectedRows().size() == 8 );
rowRanges.clear();
rowRanges.push_back(RowRange(0, 4));
rowRanges.push_back(RowRange(6, 8));
CheckRowSelection(rowRanges);
// Select all odd rows.
m_grid->ClearSelection();
rowRanges.clear();
for ( int i = 1; i < m_grid->GetNumberRows(); i += 2 )
{
m_grid->SelectBlock(i, 0, i, 1, true);
rowRanges.push_back(RowRange(i, i));
}
CheckRowSelection(rowRanges);
// Now select another block overlapping 2 of them and bordering 2 others.
m_grid->SelectBlock(2, 0, 6, 1, true);
rowRanges.clear();
rowRanges.push_back(RowRange(1, 7));
rowRanges.push_back(RowRange(9, 9));
CheckRowSelection(rowRanges);
CHECK(m_grid->GetSelectionMode() == wxGrid::wxGridSelectRows);
//Test column selection be selecting a single cell and checking the whole
//column is selected
m_grid->SetSelectionMode(wxGrid::wxGridSelectColumns);
m_grid->SelectBlock(3, 1, 3, 1);
CHECK( m_grid->GetSelectedRowBlocks().empty() );
wxArrayInt selectedCols = m_grid->GetSelectedCols();
CHECK(selectedCols.Count() == 1);
CHECK(selectedCols[0] == 1);
wxGridBlockCoordsVector colBlocks = m_grid->GetSelectedColBlocks();
CHECK( colBlocks.size() == 1 );
CHECK( colBlocks.at(0) == wxGridBlockCoords(0, 1, 9, 1) );
CHECK(m_grid->GetSelectionMode() == wxGrid::wxGridSelectColumns);
}
TEST_CASE_METHOD(GridTestCase, "Grid::CellFormatting", "[grid]")
{
//Check that initial alignment is default
int horiz, cellhoriz, vert, cellvert;
m_grid->GetDefaultCellAlignment(&horiz, &vert);
m_grid->GetCellAlignment(0, 0, &cellhoriz, &cellvert);
CHECK(horiz == cellhoriz);
CHECK(vert == cellvert);
//Check initial text colour and background colour are default
wxColour text, back;
back = m_grid->GetDefaultCellBackgroundColour();
CHECK(m_grid->GetCellBackgroundColour(0, 0) == back);
back = m_grid->GetDefaultCellTextColour();
CHECK(m_grid->GetCellTextColour(0, 0) == back);
#if WXWIN_COMPATIBILITY_2_8
m_grid->SetCellAlignment(wxALIGN_CENTRE, 0, 0);
m_grid->GetCellAlignment(0, 0, &cellhoriz, &cellvert);
CHECK(cellhoriz == wxALIGN_CENTRE);
CHECK(cellvert == wxALIGN_CENTRE);
#endif // WXWIN_COMPATIBILITY_2_8
m_grid->SetCellAlignment(0, 0, wxALIGN_LEFT, wxALIGN_BOTTOM);
m_grid->GetCellAlignment(0, 0, &cellhoriz, &cellvert);
CHECK(cellhoriz == wxALIGN_LEFT);
CHECK(cellvert == wxALIGN_BOTTOM);
#if WXWIN_COMPATIBILITY_2_8
m_grid->SetCellTextColour(*wxRED, 0, 0);
CHECK(m_grid->GetCellTextColour(0, 0) == *wxRED);
#endif // WXWIN_COMPATIBILITY_2_8
m_grid->SetCellTextColour(0, 0, *wxGREEN);
CHECK(m_grid->GetCellTextColour(0,0) == *wxGREEN);
}
TEST_CASE_METHOD(GridTestCase, "Grid::GetNonDefaultAlignment", "[grid]")
{
// GetNonDefaultAlignment() is used by several renderers having their own
// preferred alignment, so check that if we don't reset the alignment
// explicitly, it doesn't override the alignment used by default.
wxGridCellAttrPtr attr;
int hAlign = wxALIGN_RIGHT,
vAlign = wxALIGN_INVALID;
attr = m_grid->CallGetCellAttr(0, 0);
REQUIRE( attr );
// Check that the specified alignment is preserved, while the unspecified
// component is filled with the default value (which is "top" by default).
attr->GetNonDefaultAlignment(&hAlign, &vAlign);
CHECK( hAlign == wxALIGN_RIGHT );
CHECK( vAlign == wxALIGN_TOP );
// Now change the defaults and check that the unspecified alignment
// component is filled with the new default.
m_grid->SetDefaultCellAlignment(wxALIGN_CENTRE_HORIZONTAL,
wxALIGN_CENTRE_VERTICAL);
vAlign = wxALIGN_INVALID;
attr = m_grid->CallGetCellAttr(0, 0);
REQUIRE( attr );
attr->GetNonDefaultAlignment(&hAlign, &vAlign);
CHECK( hAlign == wxALIGN_RIGHT );
CHECK( vAlign == wxALIGN_CENTRE_VERTICAL );
// This is only indirectly related, but test here for CanOverflow() working
// correctly for the cells with non-default alignment, as this used to be
// broken.
m_grid->SetCellAlignment(0, 0, wxALIGN_INVALID, wxALIGN_CENTRE);
attr = m_grid->CallGetCellAttr(0, 0);
REQUIRE( attr );
CHECK( attr->CanOverflow() );
}
TEST_CASE_METHOD(GridTestCase, "Grid::Editable", "[grid]")
{
#if wxUSE_UIACTIONSIMULATOR
if ( !EnableUITests() )
return;
//As the grid is not editable we shouldn't create an editor
EventCounter created(m_grid, wxEVT_GRID_EDITOR_CREATED);
wxUIActionSimulator sim;
CHECK(m_grid->IsEditable());
m_grid->EnableEditing(false);
CHECK(!m_grid->IsEditable());
m_grid->SetFocus();
m_grid->SetGridCursor(1, 1);
sim.Text("abab");
wxYield();
sim.Char(WXK_RETURN);
wxYield();
CHECK(created.GetCount() == 0);
#endif
}
TEST_CASE_METHOD(GridTestCase, "Grid::ReadOnly", "[grid]")
{
#if wxUSE_UIACTIONSIMULATOR
if ( !EnableUITests() )
return;
//As the cell is readonly we shouldn't create an editor
EventCounter created(m_grid, wxEVT_GRID_EDITOR_CREATED);
wxUIActionSimulator sim;
CHECK(!m_grid->IsReadOnly(1, 1));
m_grid->SetReadOnly(1, 1);
CHECK(m_grid->IsReadOnly(1, 1));
m_grid->SetFocus();
m_grid->SetGridCursor(1, 1);
CHECK(m_grid->IsCurrentCellReadOnly());
sim.Text("abab");
wxYield();
sim.Char(WXK_RETURN);
wxYield();
CHECK(created.GetCount() == 0);
#endif
}
TEST_CASE_METHOD(GridTestCase, "Grid::WindowAsEditorControl", "[grid]")
{
#if wxUSE_UIACTIONSIMULATOR
if ( !EnableUITests() )
return;
// A very simple editor using a window not derived from wxControl as the
// editor.
class TestEditor : public wxGridCellEditor
{
public:
TestEditor() {}
void Create(wxWindow* parent,
wxWindowID id,
wxEvtHandler* evtHandler) wxOVERRIDE
{
SetWindow(new wxWindow(parent, id));
wxGridCellEditor::Create(parent, id, evtHandler);
}
void BeginEdit(int, int, wxGrid*) wxOVERRIDE {}
bool EndEdit(int, int, wxGrid const*, wxString const&,
wxString* newval) wxOVERRIDE
{
*newval = GetValue();
return true;
}
void ApplyEdit(int row, int col, wxGrid* grid) wxOVERRIDE
{
grid->GetTable()->SetValue(row, col, GetValue());
}
void Reset() wxOVERRIDE {}
wxGridCellEditor* Clone() const wxOVERRIDE { return new TestEditor(); }
wxString GetValue() const wxOVERRIDE { return "value"; }
};
wxGridCellAttr* attr = new wxGridCellAttr();
attr->SetRenderer(new wxGridCellStringRenderer());
attr->SetEditor(new TestEditor());
m_grid->SetAttr(1, 1, attr);
EventCounter created(m_grid, wxEVT_GRID_EDITOR_CREATED);
wxUIActionSimulator sim;
m_grid->SetFocus();
m_grid->SetGridCursor(1, 1);
m_grid->EnableCellEditControl();
sim.Char(WXK_RETURN);
wxYield();
CHECK(created.GetCount() == 1);
#endif
}
TEST_CASE_METHOD(GridTestCase, "Grid::ResizeScrolledHeader", "[grid]")
{
// TODO this test currently works only under Windows unfortunately
#if wxUSE_UIACTIONSIMULATOR && (defined(__WXMSW__) || defined(__WXGTK__))
if ( !EnableUITests() )
return;
#ifdef __WXGTK20__
// Works locally, but not when run on Travis CI.
if ( IsAutomaticTest() )
return;
#endif
SECTION("Default") {}
SECTION("Native header") { m_grid->UseNativeColHeader(); }
int const startwidth = m_grid->GetColSize(0);
int const draglength = 100;
m_grid->AppendCols(8);
m_grid->Scroll(5, 0);
m_grid->Refresh();
m_grid->Update();
wxRect rect = m_grid->CellToRect(0, 1);
wxPoint point = m_grid->CalcScrolledPosition(rect.GetPosition());
point = m_grid->ClientToScreen(point
+ wxPoint(m_grid->GetRowLabelSize(),
m_grid->GetColLabelSize())
- wxPoint(0, 5));
wxUIActionSimulator sim;
wxYield();
sim.MouseMove(point);
wxYield();
sim.MouseDown();
wxYield();
sim.MouseMove(point + wxPoint(draglength, 0));
wxYield();
sim.MouseUp();
wxYield();
CHECK(m_grid->GetColSize(0) == startwidth + draglength);
#endif
}
TEST_CASE_METHOD(GridTestCase, "Grid::ColumnMinWidth", "[grid]")
{
// TODO this test currently works only under Windows unfortunately
#if wxUSE_UIACTIONSIMULATOR && (defined(__WXMSW__) || defined(__WXGTK__))
if ( !EnableUITests() )
return;
#ifdef __WXGTK20__
// Works locally, but not when run on Travis CI.
if ( IsAutomaticTest() )
return;
#endif
SECTION("Default") {}
SECTION("Native header")
{
// For some unknown reason, this test fails under AppVeyor even though
// it passes locally, so disable it there. If anybody can reproduce the
// problem locally, where it can be debugged, please let us know.
if ( IsAutomaticTest() )
return;
m_grid->UseNativeColHeader();
}
int const startminwidth = m_grid->GetColMinimalAcceptableWidth();
m_grid->SetColMinimalAcceptableWidth(startminwidth*2);
int const newminwidth = m_grid->GetColMinimalAcceptableWidth();
int const startwidth = m_grid->GetColSize(0);
CHECK(m_grid->GetColMinimalAcceptableWidth() < startwidth);
wxRect rect = m_grid->CellToRect(0, 1);
wxPoint point = m_grid->CalcScrolledPosition(rect.GetPosition());
point = m_grid->ClientToScreen(point
+ wxPoint(m_grid->GetRowLabelSize(),
m_grid->GetColLabelSize())
- wxPoint(0, 5));
wxUIActionSimulator sim;
// Drag to reach the minimal width.
wxYield();
sim.MouseMove(point);
wxYield();
sim.MouseDown();
wxYield();
sim.MouseMove(point - wxPoint(startwidth - startminwidth, 0));
wxYield();
sim.MouseUp();
wxYield();
CHECK(m_grid->GetColSize(0) == newminwidth);
#endif
}
void GridTestCase::CheckFirstColAutoSize(int expected)
{
m_grid->AutoSizeColumn(0);
wxYield();
CHECK(m_grid->GetColSize(0) == expected);
}
TEST_CASE_METHOD(GridTestCase, "Grid::AutoSizeColumn", "[grid]")
{
SECTION("Default") {}
SECTION("Native header") { m_grid->UseNativeColHeader(); }
SECTION("Native labels") { m_grid->SetUseNativeColLabels(); }
// Hardcoded extra margin for the columns used in grid.cpp.
const int margin = m_grid->FromDIP(10);
wxGridCellAttrPtr attr(m_grid->GetOrCreateCellAttr(0, 0));
wxGridCellRendererPtr renderer(attr->GetRenderer(m_grid, 0, 0));
REQUIRE(renderer);
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) );
}
SECTION("Column with auto wrapping contents taller than row")
{
// Verify row height remains unchanged with an auto-wrapping multi-line
// cell.
// Also shouldn't continuously try to fit the multi-line content into
// a single line, which is not possible. See
// https://trac.wxwidgets.org/ticket/15943 .
m_grid->SetCellValue(0, 0, multilineStr);
m_grid->SetCellRenderer(0 , 0, new wxGridCellAutoWrapStringRenderer);
m_grid->AutoSizeColumn(0);
wxYield();
CHECK( m_grid->GetRowSize(0) == m_grid->GetDefaultRowSize() );
}
}
TEST_CASE_METHOD(GridTestCase, "Grid::DrawInvalidCell", "[grid][multicell]")
{
// Set up a multicell with inside an overflowing cell.
// This is artificial and normally inside cells are probably not expected
// to have a value but this is merely done to check if inside cells are
// drawn, which they shouldn't be.
m_grid->SetCellSize(0, 0, 2, 1);
m_grid->SetCellValue( 1, 0, wxString('W', 42) );
// Update()s, yields and sleep are needed to try to make the test fail with
// macOS, GTK and MSW.
// MSW needs just the yields (or updates), macOS in addition needs to sleep
// (doesn't work with updates) and for GTK it's usually enough to just do
// two updates (not yields). This test does all unconditionally.
m_grid->Update();
wxYield();
wxMilliSleep(20);
// Try to force redrawing of the inside cell: if it still draws there will
// be an infinite recursion.
m_grid->SetColSize(1, m_grid->GetColSize(1) + 1);
m_grid->Update();
wxYield();
}
#define CHECK_ATTR_COUNT(n) CHECK( m_grid->GetCellAttrCount() == n )
TEST_CASE_METHOD(GridTestCase, "Grid::CellAttribute", "[attr][cell][grid]")
{
SECTION("Overwrite")
{
CHECK_ATTR_COUNT( 0 );
m_grid->SetAttr(0, 0, NULL);
CHECK_ATTR_COUNT( 0 );
SetCellAttr(0, 0);
CHECK_ATTR_COUNT( 1 );
// Overwrite existing attribute with another.
SetCellAttr(0, 0);
CHECK_ATTR_COUNT( 1 );
m_grid->SetAttr(0, 0, NULL);
CHECK_ATTR_COUNT( 0 );
SetCellAttr(0, 0);
m_grid->SetCellBackgroundColour(0, 1, *wxGREEN);
CHECK_ATTR_COUNT( 2 );
m_grid->SetAttr(0, 1, NULL);
m_grid->SetAttr(0, 0, NULL);
CHECK_ATTR_COUNT( 0 );
}
// Fill the grid with attributes for next sections.
const int numRows = m_grid->GetNumberRows();
const int numCols = m_grid->GetNumberCols();
for ( int row = 0; row < numRows; ++row )
{
for ( int col = 0; col < numCols; ++col )
SetCellAttr(row, col);
}
size_t numAttrs = static_cast<size_t>(numRows * numCols);
CHECK_ATTR_COUNT( numAttrs );
SECTION("Expanding")
{
CHECK( !HasCellAttr(numRows, numCols) );
m_grid->InsertCols();
CHECK_ATTR_COUNT( numAttrs );
CHECK( !HasCellAttr(0, 0) );
CHECK( HasCellAttr(0, numCols) );
m_grid->InsertRows();
CHECK_ATTR_COUNT( numAttrs );
CHECK( HasCellAttr(numRows, numCols) );
}
SECTION("Shrinking")
{
CHECK( HasCellAttr(numRows - 1 , numCols - 1) );
CHECK( HasCellAttr(0, numCols - 1) );
m_grid->DeleteCols();
numAttrs -= m_grid->GetNumberRows();
CHECK_ATTR_COUNT( numAttrs );
CHECK( HasCellAttr(0, 0) );
CHECK( !HasCellAttr(0, numCols - 1) );
m_grid->DeleteRows();
numAttrs -= m_grid->GetNumberCols();
CHECK_ATTR_COUNT( numAttrs );
CHECK( !HasCellAttr(numRows - 1 , numCols - 1) );
}
}
#define CHECK_MULTICELL() CHECK_THAT( *m_grid, HasMulticellOnly(multi) )
#define CHECK_NO_MULTICELL() CHECK_THAT( *m_grid, HasEmptyGrid() )
#define WHEN_N(s, n) WHEN(wxString::Format(s, n).ToStdString())
TEST_CASE_METHOD(GridTestCase,
"Grid::InsertionsWithMulticell",
"[attr][cell][grid][insert][multicell]")
{
int insertions = 0, offset = 0;
Multicell multi(1, 1, 3, 5);
SECTION("Sanity checks")
{
FitGridToMulticell(m_grid, multi);
m_grid->SetMulticell(multi);
REQUIRE( static_cast<int>(m_grid->GetCellAttrCount())
== multi.rows * multi.cols );
int row, col, rows, cols;
// Check main cell.
row = multi.row,
col = multi.col;
wxGrid::CellSpan span = m_grid->GetCellSize(row, col, &rows, &cols);
REQUIRE( span == wxGrid::CellSpan_Main );
REQUIRE( rows == multi.rows );
REQUIRE( cols == multi.cols );
// Check inside cell at opposite of main.
row = multi.row + multi.rows - 1;
col = multi.col + multi.cols - 1;
span = m_grid->GetCellSize(row, col, &rows, &cols);
REQUIRE( span == wxGrid::CellSpan_Inside );
REQUIRE( rows == multi.row - row );
REQUIRE( cols == multi.col - col );
}
// Do some basic testing with column insertions first and do more tests
// with edge cases later on just with rows. It's not really needed to
// repeat the same tests for both rows and columns as the code for
// updating them works symmetrically.
GIVEN(Catch::toString(multi))
{
FitGridToMulticell(m_grid, multi);
m_grid->SetMulticell(multi);
insertions = 2;
WHEN("inserting any columns in multicell, at main")
{
InsertCols(multi.col + 0, insertions);
THEN("the position changes but not the size")
{
multi.col += insertions;
CHECK_MULTICELL();
}
}
WHEN("inserting any columns in multicell, just after main")
{
InsertCols(multi.col + 1, insertions);
THEN("the size changes but not the position")
{
multi.cols += insertions;
CHECK_MULTICELL();
}
}
}
// Do more extensive testing with rows.
wxSwap(multi.rows, multi.cols);
GIVEN(Catch::toString(multi))
{
FitGridToMulticell(m_grid, multi);
m_grid->SetMulticell(multi);
const int insertionCounts[] = {1, 2, multi.rows};
for ( size_t i = 0; i < WXSIZEOF(insertionCounts); ++i )
{
insertions = insertionCounts[i];
WHEN_N("inserting %d row(s), just before main", insertions)
{
InsertRows(multi.row - 1, insertions);
THEN("the position changes but not the size")
{
multi.row += insertions;
CHECK_MULTICELL();
}
}
WHEN_N("inserting %d row(s) in multicell, at main", insertions)
{
InsertRows(multi.row + 0, insertions);
THEN("the position changes but not the size")
{
multi.row += insertions;
CHECK_MULTICELL();
}
}
}
insertions = multi.rows / 2;
// Check insertions within multicell, at and near edges.
const int insertionOffsets[] = {1, 2, multi.rows - 2, multi.rows - 1};
for ( size_t i = 0; i < WXSIZEOF(insertionOffsets); ++i )
{
offset = insertionOffsets[i];
WHEN_N("inserting rows in multicell, %d row(s) after main", offset)
{
InsertRows(multi.row + offset, insertions);
THEN("the size changes but not the position")
{
multi.rows += insertions;
CHECK_MULTICELL();
}
}
}
// Check at least one case of inserting after multicell.
WHEN("inserting rows, just after multicell")
{
insertions = 2;
InsertRows(multi.row + multi.rows, insertions);
THEN("neither size nor position change")
{
CHECK_MULTICELL();
}
}
}
}
TEST_CASE_METHOD(GridTestCase,
"GridMulticell::DeletionsWithMulticell",
"[cellattr][delete][grid][multicell]")
{
int deletions = 0, offset = 0;
// Same as with the previous (insertions) test case but instead of some
// basic testing with columns first, this time use rows for that and do more
// extensive testing with columns.
Multicell multi(1, 1, 5, 3);
GIVEN(Catch::toString(multi))
{
FitGridToMulticell(m_grid, multi);
m_grid->SetMulticell(multi);
WHEN("deleting any rows, at main")
{
deletions = 1;
DeleteRows(multi.row + 0, deletions);
THEN("the multicell is deleted")
{
CHECK_NO_MULTICELL();
}
}
WHEN("deleting more rows than length of multicell,"
" at end of multicell")
{
deletions = multi.rows + 2;
offset = multi.rows - 1;
DeleteRows(multi.row + offset, deletions);
THEN("the size changes but not the position")
{
multi.rows = offset;
CHECK_MULTICELL();
}
}
}
// Do more extensive testing with columns.
wxSwap(multi.rows, multi.cols);
GIVEN(Catch::toString(multi))
{
FitGridToMulticell(m_grid, multi);
m_grid->SetMulticell(multi);
WHEN("deleting one column, just before main")
{
DeleteCols(multi.col - 1);
THEN("the position changes but not the size")
{
multi.col--;
CHECK_MULTICELL();
}
}
WHEN("deleting multiple columns, just before main")
{
deletions = 2; // Must be at least 2 to affect main.
DeleteCols(multi.col - 1, wxMax(2, deletions));
THEN("the multicell is deleted")
{
CHECK_NO_MULTICELL();
}
}
WHEN("deleting any columns, at main")
{
deletions = 1;
DeleteCols(multi.col + 0, deletions);
THEN("the multicell is deleted")
{
CHECK_NO_MULTICELL();
}
}
WHEN("deleting one column within multicell, after main")
{
offset = 1;
offset = wxClip(offset, 1, multi.cols - 1);
DeleteCols(multi.col + offset, 1);
THEN("the size changes but not the position")
{
multi.cols--;
CHECK_MULTICELL();
}
}
deletions = 2;
// Check deletions within multicell, at and near edges.
const int offsets[] = {1, 2, multi.cols - deletions};
for ( size_t i = 0; i < WXSIZEOF(offsets); ++i )
{
offset = offsets[i];
WHEN_N("deleting columns only within multicell,"
" %d column(s) after main", offset)
{
DeleteCols(multi.col + offset, deletions);
THEN("the size changes but not the position")
{
multi.cols -= deletions;
CHECK_MULTICELL();
}
}
}
// Instead of stuffing the multicell's length logic in the above test,
// separately check at least two cases of starting many deletions
// within multicell.
WHEN("deleting more columns than length of multicell, just after main")
{
deletions = multi.cols + 2;
offset = 1;
DeleteCols(multi.col + offset, deletions);
THEN("the size changes but not the position")
{
multi.cols = offset;
CHECK_MULTICELL();
}
}
WHEN("deleting more columns than length of multicell,"
" at end of multicell")
{
deletions = multi.cols + 2;
offset = multi.cols - 1;
DeleteCols(multi.col + offset, deletions);
THEN("the size changes but not the position")
{
multi.cols = offset;
CHECK_MULTICELL();
}
}
}
}
// Test wxGridBlockCoords here because it'a a part of grid sources.
std::ostream& operator<<(std::ostream& os, const wxGridBlockCoords& block) {
os << "wxGridBlockCoords(" <<
block.GetTopRow() << ", " << block.GetLeftCol() << ", " <<
block.GetBottomRow() << ", " << block.GetRightCol() << ")";
return os;
}
TEST_CASE("GridBlockCoords::Canonicalize", "[grid]")
{
const wxGridBlockCoords block =
wxGridBlockCoords(4, 3, 2, 1).Canonicalize();
CHECK(block.GetTopRow() == 2);
CHECK(block.GetLeftCol() == 1);
CHECK(block.GetBottomRow() == 4);
CHECK(block.GetRightCol() == 3);
}
TEST_CASE("GridBlockCoords::Intersects", "[grid]")
{
// Inside.
CHECK(wxGridBlockCoords(1, 1, 3, 3).Intersects(wxGridBlockCoords(1, 2, 2, 3)));
// Intersects.
CHECK(wxGridBlockCoords(1, 1, 3, 3).Intersects(wxGridBlockCoords(2, 2, 4, 4)));
// Doesn't intersects.
CHECK(!wxGridBlockCoords(1, 1, 3, 3).Intersects(wxGridBlockCoords(4, 4, 6, 6)));
}
TEST_CASE("GridBlockCoords::Contains", "[grid]")
{
// Inside.
CHECK(wxGridBlockCoords(1, 1, 3, 3).Contains(wxGridCellCoords(2, 2)));
// Outside.
CHECK(!wxGridBlockCoords(1, 1, 3, 3).Contains(wxGridCellCoords(5, 5)));
wxGridBlockCoords block1(1, 1, 5, 5);
wxGridBlockCoords block2(1, 1, 3, 3);
wxGridBlockCoords block3(2, 2, 7, 7);
wxGridBlockCoords block4(10, 10, 12, 12);
CHECK( block1.Contains(block2));
CHECK(!block2.Contains(block1));
CHECK(!block1.Contains(block3));
CHECK(!block1.Contains(block4));
CHECK(!block3.Contains(block1));
CHECK(!block4.Contains(block1));
}
TEST_CASE("GridBlockCoords::Difference", "[grid]")
{
SECTION("Subtract contained block (splitted horizontally)")
{
const wxGridBlockCoords block1(1, 1, 7, 7);
const wxGridBlockCoords block2(3, 3, 5, 5);
const wxGridBlockDiffResult result =
block1.Difference(block2, wxHORIZONTAL);
CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 2, 7));
CHECK(result.m_parts[1] == wxGridBlockCoords(6, 1, 7, 7));
CHECK(result.m_parts[2] == wxGridBlockCoords(3, 1, 5, 2));
CHECK(result.m_parts[3] == wxGridBlockCoords(3, 6, 5, 7));
}
SECTION("Subtract contained block (splitted vertically)")
{
const wxGridBlockCoords block1(1, 1, 7, 7);
const wxGridBlockCoords block2(3, 3, 5, 5);
const wxGridBlockDiffResult result =
block1.Difference(block2, wxVERTICAL);
CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 7, 2));
CHECK(result.m_parts[1] == wxGridBlockCoords(1, 6, 7, 7));
CHECK(result.m_parts[2] == wxGridBlockCoords(1, 3, 2, 5));
CHECK(result.m_parts[3] == wxGridBlockCoords(6, 3, 7, 5));
}
SECTION("Blocks intersect by the corner (splitted horizontally)")
{
const wxGridBlockCoords block1(1, 1, 5, 5);
const wxGridBlockCoords block2(3, 3, 7, 7);
const wxGridBlockDiffResult result =
block1.Difference(block2, wxHORIZONTAL);
CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 2, 5));
CHECK(result.m_parts[1] == wxGridNoBlockCoords);
CHECK(result.m_parts[2] == wxGridBlockCoords(3, 1, 5, 2));
CHECK(result.m_parts[3] == wxGridNoBlockCoords);
}
SECTION("Blocks intersect by the corner (splitted vertically)")
{
const wxGridBlockCoords block1(1, 1, 5, 5);
const wxGridBlockCoords block2(3, 3, 7, 7);
const wxGridBlockDiffResult result =
block1.Difference(block2, wxVERTICAL);
CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 5, 2));
CHECK(result.m_parts[1] == wxGridNoBlockCoords);
CHECK(result.m_parts[2] == wxGridBlockCoords(1, 3, 2, 5));
CHECK(result.m_parts[3] == wxGridNoBlockCoords);
}
SECTION("Blocks are the same")
{
const wxGridBlockCoords block1(1, 1, 3, 3);
const wxGridBlockCoords block2(1, 1, 3, 3);
const wxGridBlockDiffResult result =
block1.Difference(block2, wxHORIZONTAL);
CHECK(result.m_parts[0] == wxGridNoBlockCoords);
CHECK(result.m_parts[1] == wxGridNoBlockCoords);
CHECK(result.m_parts[2] == wxGridNoBlockCoords);
CHECK(result.m_parts[3] == wxGridNoBlockCoords);
}
SECTION("Blocks doesn't intersects")
{
const wxGridBlockCoords block1(1, 1, 3, 3);
const wxGridBlockCoords block2(5, 5, 7, 7);
const wxGridBlockDiffResult result =
block1.Difference(block2, wxHORIZONTAL);
CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 3, 3));
CHECK(result.m_parts[1] == wxGridNoBlockCoords);
CHECK(result.m_parts[2] == wxGridNoBlockCoords);
CHECK(result.m_parts[3] == wxGridNoBlockCoords);
}
}
TEST_CASE("GridBlockCoords::SymDifference", "[grid]")
{
SECTION("With contained block")
{
const wxGridBlockCoords block1(1, 1, 7, 7);
const wxGridBlockCoords block2(3, 3, 5, 5);
const wxGridBlockDiffResult result = block1.SymDifference(block2);
CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 2, 7));
CHECK(result.m_parts[1] == wxGridBlockCoords(6, 1, 7, 7));
CHECK(result.m_parts[2] == wxGridBlockCoords(3, 1, 5, 2));
CHECK(result.m_parts[3] == wxGridBlockCoords(3, 6, 5, 7));
}
SECTION("Blocks intersect by the corner")
{
const wxGridBlockCoords block1(1, 1, 5, 5);
const wxGridBlockCoords block2(3, 3, 7, 7);
const wxGridBlockDiffResult result = block1.SymDifference(block2);
CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 2, 5));
CHECK(result.m_parts[1] == wxGridBlockCoords(6, 3, 7, 7));
CHECK(result.m_parts[2] == wxGridBlockCoords(3, 1, 5, 2));
CHECK(result.m_parts[3] == wxGridBlockCoords(3, 6, 5, 7));
}
SECTION("Blocks are the same")
{
const wxGridBlockCoords block1(1, 1, 3, 3);
const wxGridBlockCoords block2(1, 1, 3, 3);
const wxGridBlockDiffResult result = block1.SymDifference(block2);
CHECK(result.m_parts[0] == wxGridNoBlockCoords);
CHECK(result.m_parts[1] == wxGridNoBlockCoords);
CHECK(result.m_parts[2] == wxGridNoBlockCoords);
CHECK(result.m_parts[3] == wxGridNoBlockCoords);
}
SECTION("Blocks doesn't intersects")
{
const wxGridBlockCoords block1(1, 1, 3, 3);
const wxGridBlockCoords block2(5, 5, 7, 7);
const wxGridBlockDiffResult result = block1.SymDifference(block2);
CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 3, 3));
CHECK(result.m_parts[1] == wxGridBlockCoords(5, 5, 7, 7));
CHECK(result.m_parts[2] == wxGridNoBlockCoords);
CHECK(result.m_parts[3] == wxGridNoBlockCoords);
}
}
//
// TestableGrid
//
void TestableGrid::DoEdit(const EditInfo& edit)
{
m_edit = edit;
m_beforeGridAnnotated = ToString();
switch ( edit.direction )
{
case wxGRID_COLUMN:
if ( edit.count < 0 )
DeleteCols(edit.pos, -edit.count);
else
InsertCols(edit.pos, edit.count);
break;
case wxGRID_ROW:
if ( edit.count < 0 )
DeleteRows(edit.pos, -edit.count);
else
InsertRows(edit.pos, edit.count);
break;
}
}
wxString TestableGrid::ToString() const
{
const int numRows = GetNumberRows();
const int numCols = GetNumberCols();
const int colMargin = GetRowLabelValue(numRows - 1).length();
const wxString leftIndent = wxString(' ', colMargin + 1);
// String s contains the rendering of the grid, start with drawing
// the header columns.
wxString s = leftIndent;
const int base = 10;
// Draw the multiples of 10.
for ( int col = base; col <= numCols; col += base)
{
s += wxString(' ', base - 1);
s += ('0' + (col / base));
}
if ( numCols >= base )
s += "\n" + leftIndent;
// Draw the single digits.
for ( int col = 1; col <= numCols; ++col )
s += '0' + (col % base);
s += "\n";
// Draw horizontal divider.
s += wxString(' ', colMargin) + '+' + wxString('-', numCols) + "\n";
const int absEditCount = abs(m_edit.count);
wxString action;
action.Printf(" %s: %d",
m_edit.count < 0 ? "deletions" : "insertions",
absEditCount);
// Will contain summary of grid (only multicells mentioned).
wxString content;
// Draw grid content.
for ( int row = 0; row < numRows; ++row )
{
const wxString label = GetRowLabelValue(row);
s += wxString(' ', colMargin - label.length());
s += label + '|';
for ( int col = 0; col < numCols; ++col )
{
char c = 'x';
int rows, cols;
switch ( GetCellSize(row, col, &rows, &cols) )
{
case wxGrid::CellSpan_None:
c = HasAttr(row, col) ? '*' : '.';
break;
case wxGrid::CellSpan_Main:
c = 'M';
content += Multicell(row, col, rows, cols).ToString() + "\n";
break;
case wxGrid::CellSpan_Inside:
// Check if the offset to main cell is correct.
c = (GetCellSize(row + rows, col + cols, &rows, &cols)
== wxGrid::CellSpan_Main) ? 'm' : '?';
break;
}
s += c;
}
// If applicable draw annotated row edits.
if ( m_edit.count && m_edit.direction == wxGRID_ROW
&& row >= m_edit.pos && row < m_edit.pos + absEditCount)
{
s += (m_edit.count < 0 ? " ^" : " v");
if ( row == m_edit.pos )
s += action;
}
s += "\n";
}
// Draw annotated footer if columns edited.
if ( m_edit.count && m_edit.direction == wxGRID_COLUMN )
{
s += leftIndent;
for ( int col = 0; col < m_edit.pos + absEditCount; ++col )
{
if ( col < m_edit.pos )
s += " ";
else
s += (m_edit.count < 0 ? "<" : ">");
}
s += action + "\n";
}
s += "\n";
if ( content.empty() )
content = "Empty grid\n";
return content + s;
}
//
// GridAttrMatcher
//
GridAttrMatcher::GridAttrMatcher(const TestableGrid& grid) : m_grid(&grid)
{
m_expectedGridDesc = m_grid->ToString();
}
bool GridAttrMatcher::match(const TestableGrid& other) const
{
const int rows = wxMax(m_grid->GetNumberRows(), other.GetNumberRows());
const int cols = wxMax(m_grid->GetNumberCols(), other.GetNumberCols());
for ( int row = 0; row < rows; ++row )
{
for ( int col = 0; col < cols; ++col )
{
const bool hasAttr = m_grid->HasAttr(row, col);
if ( hasAttr != other.HasAttr(row, col) )
{
m_diffDesc.Printf("%s: attribute presence (%d, expected %d)",
CellCoordsToString(row, col),
!hasAttr, hasAttr);
return false;
}
int thisRows, thisCols;
const wxGrid::CellSpan thisSpan
= m_grid->GetCellSize(row, col, &thisRows, &thisCols);
int otherRows, otherCols;
(void) other.GetCellSize(row, col, &otherRows, &otherCols);
if ( thisRows != otherRows || thisCols != otherCols )
{
wxString mismatchKind;
switch ( thisSpan )
{
case wxGrid::CellSpan_None:
mismatchKind = "different cell size";
break;
case wxGrid::CellSpan_Main:
mismatchKind = "different multicell size";
break;
case wxGrid::CellSpan_Inside:
mismatchKind = "main offset mismatch";
break;
}
m_diffDesc.Printf( "%s: %s (%s, expected %s)",
CellCoordsToString(row, col),
mismatchKind,
CellSizeToString(otherRows, otherCols),
CellSizeToString(thisRows, thisCols)
);
return false;
}
}
}
return true;
}
std::string GridAttrMatcher::describe() const
{
std::string desc = (m_diffDesc.empty() ? "Matches" : "Doesn't match");
desc += " expected content:\n" + m_expectedGridDesc.ToStdString();
if ( !m_diffDesc.empty() )
desc += + "first difference at " + m_diffDesc.ToStdString();
return desc;
}
#endif //wxUSE_GRID