1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        tests/controls/gridtest.cpp
3 // Purpose:     wxGrid unit test
4 // Author:      Steven Lamerton
5 // Created:     2010-06-25
6 // Copyright:   (c) 2010 Steven Lamerton
7 ///////////////////////////////////////////////////////////////////////////////
8 
9 #include "testprec.h"
10 
11 #if wxUSE_GRID
12 
13 
14 #ifndef WX_PRECOMP
15     #include "wx/app.h"
16     #include "wx/dcclient.h"
17 #endif // WX_PRECOMP
18 
19 #include "wx/grid.h"
20 #include "wx/headerctrl.h"
21 #include "testableframe.h"
22 #include "asserthelper.h"
23 #include "wx/uiaction.h"
24 
25 #ifdef __WXGTK__
26     #include "wx/stopwatch.h"
27 #endif // __WXGTK__
28 
29 #include "waitforpaint.h"
30 
31 namespace
32 {
33 
CellCoordsToString(int row,int col)34 wxString CellCoordsToString(int row, int col)
35 {
36     return wxString::Format("R%dC%d", col + 1, row + 1);
37 }
38 
CellSizeToString(int rows,int cols)39 wxString CellSizeToString(int rows, int cols)
40 {
41     return wxString::Format("%dx%d", rows, cols);
42 }
43 
44 struct Multicell
45 {
46     int row, col, rows, cols;
47 
Multicell__anonf7e7c6b40111::Multicell48     Multicell(int row, int col, int rows, int cols)
49         : row(row), col(col), rows(rows), cols(cols) { }
50 
Coords__anonf7e7c6b40111::Multicell51     wxString Coords() const { return CellCoordsToString(row, col); }
52 
Size__anonf7e7c6b40111::Multicell53     wxString Size() const { return CellSizeToString(rows, cols); }
54 
ToString__anonf7e7c6b40111::Multicell55     wxString ToString() const
56     {
57         wxString s;
58 
59         if ( rows == 1 && cols == 1 )
60         {
61             s = "cell";
62         }
63         else
64         {
65             s = Size() + " ";
66             if ( rows > 1 || cols > 1 )
67                 s += "multicell";
68             else
69                 s += "inside cell";
70         }
71 
72         return wxString::Format("%s at %s", s, Coords());
73     }
74 };
75 
76 // Stores insertion/deletion info of rows or columns.
77 struct EditInfo
78 {
79     int pos, count;
80     wxGridDirection direction;
81 
EditInfo__anonf7e7c6b40111::EditInfo82     EditInfo(int pos = 0,
83              int count = 0,
84              wxGridDirection direction = wxGRID_COLUMN)
85         : pos(pos), count(count), direction(direction) { }
86 };
87 
88 // Derive a new class inheriting from wxGrid, also to get access to its
89 // protected GetCellAttr(). This is not pretty, but we don't have any other way
90 // of testing this function.
91 class TestableGrid : public wxGrid
92 {
93 public:
TestableGrid(wxWindow * parent)94     explicit TestableGrid(wxWindow* parent)
95         : wxGrid(parent, wxID_ANY)
96     {
97     }
98 
CallGetCellAttr(int row,int col) const99     wxGridCellAttr* CallGetCellAttr(int row, int col) const
100     {
101         return GetCellAttr(row, col);
102     }
103 
HasAttr(int row,int col,wxGridCellAttr::wxAttrKind kind=wxGridCellAttr::Cell) const104     bool HasAttr(int row, int col,
105                  wxGridCellAttr::wxAttrKind kind = wxGridCellAttr::Cell) const
106     {
107         // Can't use GetCellAttr() here as it always returns an attr.
108         wxGridCellAttr* attr = GetTable()->GetAttr(row, col, kind);
109         if ( attr )
110             attr->DecRef();
111 
112         return attr != NULL;
113     }
114 
GetCellAttrCount() const115     size_t GetCellAttrCount() const
116     {
117         // Note that only attributes in grid range can easily be checked
118         // and this function only counts those, not any outside of
119         // the grid (e.g. with invalid negative coords).
120         size_t count = 0;
121         for ( int row = 0; row < GetNumberRows(); ++row )
122         {
123             for ( int col = 0; col < GetNumberCols(); ++col )
124                 count += HasAttr(row, col);
125         }
126 
127         return count;
128     }
129 
SetMulticell(const Multicell & multi)130     void SetMulticell(const Multicell& multi)
131     {
132         SetCellSize(multi.row, multi.col, multi.rows, multi.cols);
133     }
134 
135     // Performs given insertions/deletions on either rows or columns.
136     void DoEdit(const EditInfo& edit);
137 
138     // Returns annotated grid represented as a string.
139     wxString ToString() const;
140 
141     // Used when drawing annotated grid to know what happens to it.
142     EditInfo m_edit;
143 
144     // Grid as string before editing, with edit info annotated.
145     wxString m_beforeGridAnnotated;
146 };
147 
148 // Compares two grids, checking for differences with attribute presence and
149 // cell sizes.
150 class GridAttrMatcher : public Catch::MatcherBase<TestableGrid>
151 {
152 public:
153     GridAttrMatcher(const TestableGrid& grid);
154 
155     bool match(const TestableGrid& other) const wxOVERRIDE;
156 
157     std::string describe() const wxOVERRIDE;
158 
159 private:
160     const TestableGrid* m_grid;
161 
162     mutable wxString m_diffDesc;
163     mutable wxString m_expectedGridDesc;
164 };
165 
166 // Helper function for recreating a grid to fit (only) a multicell.
FitGridToMulticell(TestableGrid * grid,const Multicell & multi)167 void FitGridToMulticell(TestableGrid* grid, const Multicell& multi)
168 {
169     const int oldRowCount = grid->GetNumberRows();
170     const int oldColCount = grid->GetNumberCols();
171 
172     const int margin = 1;
173     const int newRowCount = multi.row + multi.rows + margin;
174     const int newColCount = multi.col + multi.cols + margin;
175 
176     if ( !oldRowCount && !oldColCount )
177     {
178         grid->CreateGrid(newRowCount, newColCount);
179     }
180     else
181     {
182         grid->DeleteRows(0, oldRowCount);
183         grid->DeleteCols(0, oldColCount);
184         grid->AppendRows(newRowCount);
185         grid->AppendCols(newColCount);
186     }
187 }
188 
189 } // anonymous namespace
190 
191 namespace Catch
192 {
193 
194 template <> struct StringMaker<TestableGrid>
195 {
convertCatch::StringMaker196     static std::string convert(const TestableGrid& grid)
197     {
198         return ("Content before edit:\n" + grid.m_beforeGridAnnotated
199                 + "\nContent after edit:\n" + grid.ToString()).ToStdString();
200     }
201 };
202 
203 template <> struct StringMaker<Multicell>
204 {
convertCatch::StringMaker205     static std::string convert(const Multicell& multi)
206     {
207         return multi.ToString().ToStdString();
208     }
209 };
210 
211 } // namespace Catch
212 
213 class GridTestCase
214 {
215 public:
216     GridTestCase();
217     ~GridTestCase();
218 
219 protected:
InsertRows(int pos=0,int count=1)220     void InsertRows(int pos = 0, int count = 1)
221     {
222         m_grid->DoEdit(EditInfo(pos, count, wxGRID_ROW));
223     }
224 
InsertCols(int pos=0,int count=1)225     void InsertCols(int pos = 0, int count = 1)
226     {
227         m_grid->DoEdit(EditInfo(pos, count, wxGRID_COLUMN));
228     }
229 
DeleteRows(int pos=0,int count=1)230     void DeleteRows(int pos = 0, int count = 1)
231     {
232         m_grid->DoEdit(EditInfo(pos, -count, wxGRID_ROW));
233     }
234 
DeleteCols(int pos=0,int count=1)235     void DeleteCols(int pos = 0, int count = 1)
236     {
237         m_grid->DoEdit(EditInfo(pos, -count, wxGRID_COLUMN));
238     }
239 
240     // The helper function to determine the width of the column label depending
241     // on whether the native column header is used.
GetColumnLabelWidth(wxClientDC & dc,int col,int margin) const242     int GetColumnLabelWidth(wxClientDC& dc, int col, int margin) const
243     {
244         if ( m_grid->IsUsingNativeHeader() )
245             return m_grid->GetGridColHeader()->GetColumnTitleWidth(col);
246 
247         int w, h;
248         dc.GetMultiLineTextExtent(m_grid->GetColLabelValue(col), &w, &h);
249         return w + margin;
250     }
251 
252     void CheckFirstColAutoSize(int expected);
253 
254     // Helper to check that the selection is equal to the specified block.
CheckSelection(const wxGridBlockCoords & block)255     void CheckSelection(const wxGridBlockCoords& block)
256     {
257         const wxGridBlocks selected = m_grid->GetSelectedBlocks();
258         wxGridBlocks::iterator it = selected.begin();
259 
260         REQUIRE( it != selected.end() );
261         CHECK( *it == block );
262         CHECK( ++it == selected.end() );
263     }
264 
265     // Or specified ranges.
266     struct RowRange
267     {
RowRangeGridTestCase::RowRange268         RowRange(int top, int bottom) : top(top), bottom(bottom) { }
269 
270         int top, bottom;
271     };
272 
273     typedef wxVector<RowRange> RowRanges;
274 
CheckRowSelection(const RowRanges & ranges)275     void CheckRowSelection(const RowRanges& ranges)
276     {
277         const wxGridBlockCoordsVector sel = m_grid->GetSelectedRowBlocks();
278         REQUIRE( sel.size() == ranges.size() );
279 
280         for ( size_t n = 0; n < sel.size(); ++n )
281         {
282             INFO("n = " << n);
283 
284             const RowRange& r = ranges[n];
285             CHECK( sel[n] == wxGridBlockCoords(r.top, 0, r.bottom, 1) );
286         }
287     }
288 
HasCellAttr(int row,int col) const289     bool HasCellAttr(int row, int col) const
290     {
291         return m_grid->HasAttr(row, col, wxGridCellAttr::Cell);
292     }
293 
SetCellAttr(int row,int col)294     void SetCellAttr(int row, int col)
295     {
296         m_grid->SetAttr(row, col, new wxGridCellAttr);
297     }
298 
299     // Fills temp. grid with a multicell and returns a matcher with it.
HasMulticellOnly(const Multicell & multi)300     GridAttrMatcher HasMulticellOnly(const Multicell& multi)
301     {
302         return CheckMulticell(multi);
303     }
304 
305     // Returns a matcher with empty (temp.) grid.
HasEmptyGrid()306     GridAttrMatcher HasEmptyGrid()
307     {
308         return CheckMulticell(Multicell(0, 0, 1, 1));
309     }
310 
311     // Helper function used by the previous two functions.
CheckMulticell(const Multicell & multi)312     GridAttrMatcher CheckMulticell(const Multicell& multi)
313     {
314         TestableGrid* grid = GetTempGrid();
315 
316         FitGridToMulticell(grid, multi);
317 
318         if ( multi.rows > 1 || multi.cols > 1 )
319             grid->SetMulticell(multi);
320 
321         return GridAttrMatcher(*grid);
322     }
323 
GetTempGrid()324     TestableGrid* GetTempGrid()
325     {
326         if ( !m_tempGrid )
327         {
328             m_tempGrid = new TestableGrid(wxTheApp->GetTopWindow());
329             m_tempGrid->Hide();
330         }
331 
332         return m_tempGrid;
333     }
334 
335     TestableGrid *m_grid;
336 
337     // Temporary/scratch grid filled with expected content, used when
338     // comparing against m_grid.
339     TestableGrid *m_tempGrid;
340 
341     wxDECLARE_NO_COPY_CLASS(GridTestCase);
342 };
343 
GridTestCase()344 GridTestCase::GridTestCase() : m_tempGrid(NULL)
345 {
346     m_grid = new TestableGrid(wxTheApp->GetTopWindow());
347     m_grid->CreateGrid(10, 2);
348     m_grid->SetSize(400, 200);
349 
350     WaitForPaint waitForPaint(m_grid->GetGridWindow());
351 
352     m_grid->Refresh();
353     m_grid->Update();
354 
355     waitForPaint.YieldUntilPainted();
356 }
357 
~GridTestCase()358 GridTestCase::~GridTestCase()
359 {
360     // This is just a hack to continue the rest of the tests to run: if we
361     // destroy the header control while it has capture, this results in an
362     // assert failure and while handling an exception from it more bad things
363     // happen (as it's thrown from a dtor), resulting in simply aborting
364     // everything. So ensure that it doesn't have capture in any case.
365     //
366     // Of course, the right thing to do would be to understand why does it
367     // still have capture when the grid is destroyed sometimes.
368     wxWindow* const win = wxWindow::GetCapture();
369     if ( win )
370         win->ReleaseMouse();
371 
372     wxDELETE(m_grid);
373     delete m_tempGrid;
374 }
375 
376 TEST_CASE_METHOD(GridTestCase, "Grid::CellEdit", "[grid]")
377 {
378     // TODO on OSX when running the grid test suite solo this works
379     // but not when running it together with other tests
380 #if wxUSE_UIACTIONSIMULATOR && !defined(__WXOSX__)
381     if ( !EnableUITests() )
382         return;
383 
384     EventCounter changing(m_grid, wxEVT_GRID_CELL_CHANGING);
385     EventCounter changed(m_grid, wxEVT_GRID_CELL_CHANGED);
386     EventCounter created(m_grid, wxEVT_GRID_EDITOR_CREATED);
387 
388     wxUIActionSimulator sim;
389 
390     m_grid->SetFocus();
391     m_grid->SetGridCursor(1, 1);
392 
393     wxYield();
394 
395     sim.Text("abab");
396 
397     wxYield();
398 
399     sim.Char(WXK_RETURN);
400 
401     wxYield();
402 
403     CHECK(m_grid->GetCellValue(1, 1) == "abab");
404 
405     CHECK(created.GetCount() == 1);
406     CHECK(changing.GetCount() == 1);
407     CHECK(changed.GetCount() == 1);
408 #endif
409 }
410 
411 TEST_CASE_METHOD(GridTestCase, "Grid::CellClick", "[grid]")
412 {
413 #if wxUSE_UIACTIONSIMULATOR
414     EventCounter lclick(m_grid, wxEVT_GRID_CELL_LEFT_CLICK);
415     EventCounter ldclick(m_grid, wxEVT_GRID_CELL_LEFT_DCLICK);
416     EventCounter rclick(m_grid, wxEVT_GRID_CELL_RIGHT_CLICK);
417     EventCounter rdclick(m_grid, wxEVT_GRID_CELL_RIGHT_DCLICK);
418 
419 
420     wxUIActionSimulator sim;
421 
422     wxRect rect = m_grid->CellToRect(0, 0);
423     wxPoint point = m_grid->CalcScrolledPosition(rect.GetPosition());
424     point = m_grid->ClientToScreen(point + wxPoint(m_grid->GetRowLabelSize(),
425                                                    m_grid->GetColLabelSize())
426                                          + wxPoint(2, 2));
427 
428     sim.MouseMove(point);
429     wxYield();
430 
431     sim.MouseClick();
432     wxYield();
433 
434     CHECK(lclick.GetCount() == 1);
435     lclick.Clear();
436 
437     sim.MouseDblClick();
438     wxYield();
439 
440     //A double click event sends a single click event first
441     //test to ensure this still happens in the future
442     CHECK(lclick.GetCount() == 1);
443     CHECK(ldclick.GetCount() == 1);
444 
445     sim.MouseClick(wxMOUSE_BTN_RIGHT);
446     wxYield();
447 
448     CHECK(rclick.GetCount() == 1);
449     rclick.Clear();
450 
451     sim.MouseDblClick(wxMOUSE_BTN_RIGHT);
452     wxYield();
453 
454     CHECK(rclick.GetCount() == 1);
455     CHECK(rdclick.GetCount() == 1);
456 #endif
457 }
458 
459 TEST_CASE_METHOD(GridTestCase, "Grid::ReorderedColumnsCellClick", "[grid]")
460 {
461 #if wxUSE_UIACTIONSIMULATOR
462     EventCounter click(m_grid, wxEVT_GRID_CELL_LEFT_CLICK);
463 
464     wxUIActionSimulator sim;
465 
466     wxArrayInt neworder;
467     neworder.push_back(1);
468     neworder.push_back(0);
469 
470     m_grid->SetColumnsOrder(neworder);
471 
472     wxRect rect = m_grid->CellToRect(0, 1);
473     wxPoint point = m_grid->CalcScrolledPosition(rect.GetPosition());
474     point = m_grid->ClientToScreen(point + wxPoint(m_grid->GetRowLabelSize(),
475         m_grid->GetColLabelSize())
476         + wxPoint(2, 2));
477 
478     sim.MouseMove(point);
479     wxYield();
480 
481     sim.MouseClick();
482     wxYield();
483 
484     CHECK(click.GetCount() == 1);
485 #endif
486 }
487 
488 TEST_CASE_METHOD(GridTestCase, "Grid::CellSelect", "[grid]")
489 {
490 #if wxUSE_UIACTIONSIMULATOR
491     EventCounter cell(m_grid, wxEVT_GRID_SELECT_CELL);
492 
493     wxUIActionSimulator sim;
494 
495     wxRect rect = m_grid->CellToRect(0, 0);
496     wxPoint point = m_grid->CalcScrolledPosition(rect.GetPosition());
497     point = m_grid->ClientToScreen(point + wxPoint(m_grid->GetRowLabelSize(),
498                                                    m_grid->GetColLabelSize())
499                                          + wxPoint(4, 4));
500 
501     sim.MouseMove(point);
502     wxYield();
503 
504     sim.MouseClick();
505     wxYield();
506 
507     CHECK(cell.GetCount() == 1);
508 
509     cell.Clear();
510 
511     m_grid->SetGridCursor(1, 1);
512     m_grid->GoToCell(1, 0);
513 
514     sim.MouseMove(point);
515     wxYield();
516 
517     sim.MouseDblClick();
518     wxYield();
519 
520     CHECK(cell.GetCount() == 3);
521 #endif
522 }
523 
524 TEST_CASE_METHOD(GridTestCase, "Grid::LabelClick", "[grid]")
525 {
526 #if wxUSE_UIACTIONSIMULATOR
527     if ( !EnableUITests() )
528         return;
529 
530     SECTION("Default") {}
531     SECTION("Native header") { m_grid->UseNativeColHeader(); }
532     SECTION("Native labels") { m_grid->SetUseNativeColLabels(); }
533 
534     EventCounter lclick(m_grid, wxEVT_GRID_LABEL_LEFT_CLICK);
535     EventCounter ldclick(m_grid, wxEVT_GRID_LABEL_LEFT_DCLICK);
536     EventCounter rclick(m_grid, wxEVT_GRID_LABEL_RIGHT_CLICK);
537     EventCounter rdclick(m_grid, wxEVT_GRID_LABEL_RIGHT_DCLICK);
538 
539     wxUIActionSimulator sim;
540 
541     wxPoint pos(m_grid->GetRowLabelSize() + 2, 2);
542     pos = m_grid->ClientToScreen(pos);
543 
544     sim.MouseMove(pos);
545     wxYield();
546 
547     sim.MouseClick();
548     wxYield();
549 
550     CHECK(lclick.GetCount() == 1);
551 
552     sim.MouseDblClick();
553     wxYield();
554 
555     CHECK(ldclick.GetCount() == 1);
556 
557     sim.MouseClick(wxMOUSE_BTN_RIGHT);
558     wxYield();
559 
560     CHECK(rclick.GetCount() == 1);
561     rclick.Clear();
562 
563     sim.MouseDblClick(wxMOUSE_BTN_RIGHT);
564     wxYield();
565 
566     if ( m_grid->IsUsingNativeHeader() )
567     {
568         //Right double click not supported with native headers so we get two
569         //right click events
570         CHECK(rclick.GetCount() == 2);
571     }
572     else
573     {
574         CHECK(rclick.GetCount() == 1);
575         CHECK(rdclick.GetCount() == 1);
576     }
577 #endif
578 }
579 
580 TEST_CASE_METHOD(GridTestCase, "Grid::SortClick", "[grid]")
581 {
582 #if wxUSE_UIACTIONSIMULATOR
583     if ( !EnableUITests() )
584         return;
585 
586     SECTION("Default") {}
587     SECTION("Native header") { m_grid->UseNativeColHeader(); }
588     SECTION("Native labels") { m_grid->SetUseNativeColLabels(); }
589 
590     m_grid->SetSortingColumn(0);
591 
592     EventCounter sort(m_grid, wxEVT_GRID_COL_SORT);
593 
594     wxUIActionSimulator sim;
595 
596     wxPoint pos(m_grid->GetRowLabelSize() + 4, 4);
597     pos = m_grid->ClientToScreen(pos);
598 
599     sim.MouseMove(pos);
600     wxYield();
601 
602     sim.MouseClick();
603     wxYield();
604 
605     CHECK(sort.GetCount() == 1);
606 #endif
607 }
608 
609 TEST_CASE_METHOD(GridTestCase, "Grid::Size", "[grid]")
610 {
611     // TODO on OSX resizing interactively works, but not automated
612     // Grid could not pass the test under GTK, OSX, and Universal.
613     // So there may has bug in Grid implementation
614 #if wxUSE_UIACTIONSIMULATOR && !defined(__WXOSX__) && !defined(__WXUNIVERSAL__)
615     if ( !EnableUITests() )
616         return;
617 
618 #ifdef __WXGTK20__
619     // Works locally, but not when run on Travis CI.
620     if ( IsAutomaticTest() )
621         return;
622 #endif
623 
624     EventCounter colsize(m_grid, wxEVT_GRID_COL_SIZE);
625     EventCounter rowsize(m_grid, wxEVT_GRID_ROW_SIZE);
626 
627     wxUIActionSimulator sim;
628 
629     wxPoint pt = m_grid->ClientToScreen(wxPoint(m_grid->GetRowLabelSize() +
630                                         m_grid->GetColSize(0), 5));
631     sim.MouseMove(pt);
632     wxYield();
633 
634     sim.MouseDown();
635     wxYield();
636 
637     sim.MouseMove(pt.x + 50, pt.y);
638     wxYield();
639 
640     sim.MouseUp();
641     wxYield();
642 
643     CHECK(colsize.GetCount() == 1);
644 
645     pt = m_grid->ClientToScreen(wxPoint(5, m_grid->GetColLabelSize() +
646                                         m_grid->GetRowSize(0)));
647 
648     sim.MouseDragDrop(pt.x, pt.y, pt.x, pt.y + 50);
649 
650     wxYield();
651 
652     CHECK(rowsize.GetCount() == 1);
653 #endif
654 }
655 
656 TEST_CASE_METHOD(GridTestCase, "Grid::RangeSelect", "[grid]")
657 {
658 #if wxUSE_UIACTIONSIMULATOR
659     if ( !EnableUITests() )
660         return;
661 
662     EventCounter select(m_grid, wxEVT_GRID_RANGE_SELECTED);
663 
664     wxUIActionSimulator sim;
665 
666     //We add the extra 10 to ensure that we are inside the cell
667     wxPoint pt = m_grid->ClientToScreen(wxPoint(m_grid->GetRowLabelSize() + 10,
668                                                 m_grid->GetColLabelSize() + 10)
669                                                 );
670 
671     sim.MouseMove(pt);
672     wxYield();
673 
674     sim.MouseDown();
675     wxYield();
676 
677     sim.MouseMove(pt.x + 50, pt.y + 50);
678     wxYield();
679 
680     sim.MouseUp();
681     wxYield();
682 
683     CHECK(select.GetCount() == 1);
684 #endif
685 }
686 
687 TEST_CASE_METHOD(GridTestCase, "Grid::Cursor", "[grid]")
688 {
689     m_grid->SetGridCursor(1, 1);
690 
691     CHECK(m_grid->GetGridCursorCol() == 1);
692     CHECK(m_grid->GetGridCursorRow() == 1);
693 
694     m_grid->MoveCursorDown(false);
695     m_grid->MoveCursorLeft(false);
696     m_grid->MoveCursorUp(false);
697     m_grid->MoveCursorUp(false);
698     m_grid->MoveCursorRight(false);
699 
700     CHECK(m_grid->GetGridCursorCol() == 1);
701     CHECK(m_grid->GetGridCursorRow() == 0);
702 
703     m_grid->SetCellValue(0, 0, "some text");
704     m_grid->SetCellValue(3, 0, "other text");
705     m_grid->SetCellValue(0, 1, "more text");
706     m_grid->SetCellValue(3, 1, "extra text");
707 
708     m_grid->Update();
709     m_grid->Refresh();
710 
711     m_grid->MoveCursorLeftBlock(false);
712 
713     CHECK(m_grid->GetGridCursorCol() == 0);
714     CHECK(m_grid->GetGridCursorRow() == 0);
715 
716     m_grid->MoveCursorDownBlock(false);
717 
718     CHECK(m_grid->GetGridCursorCol() == 0);
719     CHECK(m_grid->GetGridCursorRow() == 3);
720 
721     m_grid->MoveCursorRightBlock(false);
722 
723     CHECK(m_grid->GetGridCursorCol() == 1);
724     CHECK(m_grid->GetGridCursorRow() == 3);
725 
726     m_grid->MoveCursorUpBlock(false);
727 
728     CHECK(m_grid->GetGridCursorCol() == 1);
729     CHECK(m_grid->GetGridCursorRow() == 0);
730 }
731 
732 TEST_CASE_METHOD(GridTestCase, "Grid::KeyboardSelection", "[grid][selection]")
733 {
734     m_grid->SetCellValue(1, 1, "R2C2");
735     m_grid->SetCellValue(2, 0, "R3C1");
736     m_grid->SetCellValue(3, 0, "R4C1");
737     m_grid->SetCellValue(4, 0, "R5C1");
738     m_grid->SetCellValue(7, 0, "R8C1");
739 
740     CHECK(m_grid->GetGridCursorCoords() == wxGridCellCoords(0, 0));
741 
742     m_grid->MoveCursorRight(true);
743     CheckSelection(wxGridBlockCoords(0, 0, 0, 1));
744 
745     m_grid->MoveCursorDownBlock(true);
746     CheckSelection(wxGridBlockCoords(0, 0, 2, 1));
747 
748     m_grid->MoveCursorDownBlock(true);
749     CheckSelection(wxGridBlockCoords(0, 0, 4, 1));
750 
751     m_grid->MoveCursorDownBlock(true);
752     CheckSelection(wxGridBlockCoords(0, 0, 7, 1));
753 
754     m_grid->MoveCursorUpBlock(true);
755     CheckSelection(wxGridBlockCoords(0, 0, 4, 1));
756 
757     m_grid->MoveCursorLeft(true);
758     CheckSelection(wxGridBlockCoords(0, 0, 4, 0));
759 }
760 
761 TEST_CASE_METHOD(GridTestCase, "Grid::Selection", "[grid]")
762 {
763     m_grid->SelectAll();
764 
765     CHECK(m_grid->IsSelection());
766     CHECK(m_grid->IsInSelection(0, 0));
767     CHECK(m_grid->IsInSelection(9, 1));
768 
769     m_grid->SelectBlock(1, 0, 3, 1);
770 
771     wxGridCellCoordsArray topleft = m_grid->GetSelectionBlockTopLeft();
772     wxGridCellCoordsArray bottomright = m_grid->GetSelectionBlockBottomRight();
773 
774     CHECK(topleft.Count() == 1);
775     CHECK(bottomright.Count() == 1);
776 
777     CHECK(topleft.Item(0).GetCol() == 0);
778     CHECK(topleft.Item(0).GetRow() == 1);
779     CHECK(bottomright.Item(0).GetCol() == 1);
780     CHECK(bottomright.Item(0).GetRow() == 3);
781 
782     m_grid->SelectCol(1);
783 
784     CHECK(m_grid->IsInSelection(0, 1));
785     CHECK(m_grid->IsInSelection(9, 1));
786     CHECK(!m_grid->IsInSelection(3, 0));
787 
788     m_grid->SelectRow(4, true /* add to selection */);
789 
790     CHECK(m_grid->IsInSelection(4, 0));
791     CHECK(m_grid->IsInSelection(4, 1));
792     CHECK(!m_grid->IsInSelection(3, 0));
793 
794     // Check that deselecting a row does deselect the cells in it, but leaves
795     // the other ones selected.
796     m_grid->DeselectRow(4);
797     CHECK(!m_grid->IsInSelection(4, 0));
798     CHECK(!m_grid->IsInSelection(4, 1));
799     CHECK(m_grid->IsInSelection(0, 1));
800 
801     m_grid->DeselectCol(1);
802     CHECK(!m_grid->IsInSelection(0, 1));
803 }
804 
805 TEST_CASE_METHOD(GridTestCase, "Grid::SelectionRange", "[grid]")
806 {
807     const wxGridBlocks empty = m_grid->GetSelectedBlocks();
808     CHECK( empty.begin() == empty.end() );
809 
810     m_grid->SelectBlock(1, 0, 3, 1);
811 
812     wxGridBlocks sel = m_grid->GetSelectedBlocks();
813     REQUIRE( sel.begin() != sel.end() );
814     CHECK( *sel.begin() == wxGridBlockCoords(1, 0, 3, 1) );
815 
816 #if __cplusplus >= 201103L || wxCHECK_VISUALC_VERSION(11)
817     m_grid->SelectBlock(4, 0, 7, 1, true);
818     int index = 0;
819     for ( const wxGridBlockCoords& block : m_grid->GetSelectedBlocks() )
820     {
821         switch ( index )
822         {
823         case 0:
824             CHECK(block == wxGridBlockCoords(1, 0, 3, 1));
825             break;
826         case 1:
827             CHECK(block == wxGridBlockCoords(4, 0, 7, 1));
828             break;
829         default:
830             FAIL("Unexpected iterations count");
831             break;
832         }
833         ++index;
834     }
835 #endif
836 }
837 
838 TEST_CASE_METHOD(GridTestCase, "Grid::SelectEmptyGrid", "[grid]")
839 {
840     for ( int i = 0; i < 2; ++i )
841     {
842         SECTION(i == 0 ? "No rows" : "No columns")
843         {
844 
845             if ( i == 0 )
846             {
847                 m_grid->DeleteRows(0, 10);
848                 REQUIRE( m_grid->GetNumberRows() == 0 );
849             }
850             else
851             {
852                 m_grid->DeleteCols(0, 2);
853                 REQUIRE( m_grid->GetNumberCols() == 0 );
854             }
855 
856             SECTION("Move right")
857             {
858                 m_grid->MoveCursorRight(true);
859             }
860             SECTION("Move down")
861             {
862                 m_grid->MoveCursorDown(true);
863             }
864             SECTION("Select row")
865             {
866                 m_grid->SelectRow(1);
867             }
868             SECTION("Select column")
869             {
870                 m_grid->SelectCol(1);
871             }
872         }
873     }
874 
875     CHECK( m_grid->GetSelectedCells().Count() == 0 );
876     CHECK( m_grid->GetSelectionBlockTopLeft().Count() == 0 );
877     CHECK( m_grid->GetSelectionBlockBottomRight().Count() == 0 );
878     CHECK( m_grid->GetSelectedRows().Count() == 0 );
879     CHECK( m_grid->GetSelectedCols().Count() == 0 );
880 }
881 
882 TEST_CASE_METHOD(GridTestCase, "Grid::ScrollWhenSelect", "[grid]")
883 {
884     m_grid->AppendCols(10);
885 
886     REQUIRE( m_grid->GetGridCursorCol() == 0 );
887     REQUIRE( m_grid->GetGridCursorRow() == 0 );
888     REQUIRE( m_grid->IsVisible(0, 0) );
889     REQUIRE( !m_grid->IsVisible(0, 4) );
890 
891     for ( int i = 0; i < 4; ++i )
892     {
893         m_grid->MoveCursorRight(true);
894     }
895     CHECK( m_grid->IsVisible(0, 4) );
896 
897     m_grid->ClearSelection();
898     m_grid->GoToCell(1, 1);
899     for ( int i = 0; i < 8; ++i )
900     {
901         m_grid->MoveCursorDown(true);
902     }
903     CHECK( m_grid->IsVisible(9, 1) );
904 }
905 
906 TEST_CASE_METHOD(GridTestCase, "Grid::MoveGridCursorUsingEndKey", "[grid]")
907 {
908 #if wxUSE_UIACTIONSIMULATOR
909     if ( !EnableUITests() )
910         return;
911 
912     wxUIActionSimulator sim;
913 
914     m_grid->AppendCols(10);
915 
916     REQUIRE( m_grid->GetGridCursorCol() == 0 );
917     REQUIRE( m_grid->GetGridCursorRow() == 0 );
918     REQUIRE( m_grid->IsVisible(0, 0) );
919 
920     // Hide the last row.
921     m_grid->HideRow(9);
922     // Hide the last column.
923     m_grid->HideCol(11);
924     // Move the penult column.
925     m_grid->SetColPos(10, 5);
926 
927     m_grid->SetFocus();
928 
929     sim.KeyDown(WXK_END, wxMOD_CONTROL);
930     sim.KeyUp(WXK_END, wxMOD_CONTROL);
931     wxYield();
932 
933     CHECK( m_grid->GetGridCursorRow() == 8 );
934     CHECK( m_grid->GetGridCursorCol() == 9 );
935     CHECK( m_grid->IsVisible(8, 9) );
936 #endif
937 }
938 
939 TEST_CASE_METHOD(GridTestCase, "Grid::SelectUsingEndKey", "[grid]")
940 {
941 #if wxUSE_UIACTIONSIMULATOR
942     if ( !EnableUITests() )
943         return;
944 
945     wxUIActionSimulator sim;
946 
947     m_grid->AppendCols(10);
948 
949     REQUIRE( m_grid->GetGridCursorCol() == 0 );
950     REQUIRE( m_grid->GetGridCursorRow() == 0 );
951     REQUIRE( m_grid->IsVisible(0, 0) );
952 
953     m_grid->SetFocus();
954 
955     sim.KeyDown(WXK_END, wxMOD_CONTROL | wxMOD_SHIFT);
956     sim.KeyUp(WXK_END, wxMOD_CONTROL | wxMOD_SHIFT);
957     wxYield();
958 
959     wxGridCellCoordsArray topleft = m_grid->GetSelectionBlockTopLeft();
960     wxGridCellCoordsArray bottomright = m_grid->GetSelectionBlockBottomRight();
961 
962     CHECK( topleft.Count() == 1 );
963     CHECK( bottomright.Count() == 1 );
964 
965     CHECK( topleft.Item(0).GetCol() == 0 );
966     CHECK( topleft.Item(0).GetRow() == 0 );
967     CHECK( bottomright.Item(0).GetCol() == 11 );
968     CHECK( bottomright.Item(0).GetRow() == 9 );
969 
970     CHECK( m_grid->IsVisible(8, 9) );
971 #endif
972 }
973 
974 TEST_CASE_METHOD(GridTestCase, "Grid::AddRowCol", "[grid]")
975 {
976     CHECK(m_grid->GetNumberRows() == 10);
977     CHECK(m_grid->GetNumberCols() == 2);
978 
979     m_grid->AppendCols();
980     m_grid->AppendRows();
981 
982     CHECK(m_grid->GetNumberRows() == 11);
983     CHECK(m_grid->GetNumberCols() == 3);
984 
985     m_grid->AppendCols(2);
986     m_grid->AppendRows(2);
987 
988     CHECK(m_grid->GetNumberRows() == 13);
989     CHECK(m_grid->GetNumberCols() == 5);
990 
991     m_grid->InsertCols(1, 2);
992     m_grid->InsertRows(2, 3);
993 
994     CHECK(m_grid->GetNumberRows() == 16);
995     CHECK(m_grid->GetNumberCols() == 7);
996 }
997 
998 TEST_CASE_METHOD(GridTestCase, "Grid::DeleteAndAddRowCol", "[grid]")
999 {
1000     SECTION("Default") {}
1001     SECTION("Native header") { m_grid->UseNativeColHeader(); }
1002 
1003     CHECK(m_grid->GetNumberRows() == 10);
1004     CHECK(m_grid->GetNumberCols() == 2);
1005 
1006     m_grid->DeleteRows(0, 10);
1007     m_grid->DeleteCols(0, 2);
1008 
1009     CHECK(m_grid->GetNumberRows() == 0);
1010     CHECK(m_grid->GetNumberCols() == 0);
1011 
1012     m_grid->AppendRows(5);
1013     m_grid->AppendCols(3);
1014 
1015     CHECK(m_grid->GetNumberRows() == 5);
1016     CHECK(m_grid->GetNumberCols() == 3);
1017 
1018     // The order of functions calls can be important
1019     m_grid->DeleteCols(0, 3);
1020     m_grid->DeleteRows(0, 5);
1021 
1022     CHECK(m_grid->GetNumberRows() == 0);
1023     CHECK(m_grid->GetNumberCols() == 0);
1024 
1025     // Different functions calls order
1026     m_grid->AppendCols(3);
1027     m_grid->AppendRows(5);
1028 }
1029 
1030 TEST_CASE_METHOD(GridTestCase, "Grid::ColumnOrder", "[grid]")
1031 {
1032     SECTION("Default") {}
1033     SECTION("Native header") { m_grid->UseNativeColHeader(); }
1034     SECTION("Native labels") { m_grid->SetUseNativeColLabels(); }
1035 
1036     m_grid->AppendCols(2);
1037 
1038     CHECK(m_grid->GetNumberCols() == 4);
1039 
1040     wxArrayInt neworder;
1041     neworder.push_back(1);
1042     neworder.push_back(3);
1043     neworder.push_back(2);
1044     neworder.push_back(0);
1045 
1046     m_grid->SetColumnsOrder(neworder);
1047 
1048     CHECK(m_grid->GetColPos(1) == 0);
1049     CHECK(m_grid->GetColPos(3) == 1);
1050     CHECK(m_grid->GetColPos(2) == 2);
1051     CHECK(m_grid->GetColPos(0) == 3);
1052 
1053     CHECK(m_grid->GetColAt(0) == 1);
1054     CHECK(m_grid->GetColAt(1) == 3);
1055     CHECK(m_grid->GetColAt(2) == 2);
1056     CHECK(m_grid->GetColAt(3) == 0);
1057 
1058     m_grid->ResetColPos();
1059 
1060     CHECK(m_grid->GetColPos(0) == 0);
1061     CHECK(m_grid->GetColPos(1) == 1);
1062     CHECK(m_grid->GetColPos(2) == 2);
1063     CHECK(m_grid->GetColPos(3) == 3);
1064 }
1065 
1066 TEST_CASE_METHOD(GridTestCase, "Grid::ColumnVisibility", "[grid]")
1067 {
1068     m_grid->AppendCols(3);
1069     CHECK( m_grid->IsColShown(1) );
1070 
1071     m_grid->HideCol(1);
1072     CHECK( !m_grid->IsColShown(1) );
1073     CHECK( m_grid->IsColShown(2) );
1074 
1075     m_grid->ShowCol(1);
1076     CHECK( m_grid->IsColShown(1) );
1077 }
1078 
1079 TEST_CASE_METHOD(GridTestCase, "Grid::LineFormatting", "[grid]")
1080 {
1081     CHECK(m_grid->GridLinesEnabled());
1082 
1083     m_grid->EnableGridLines(false);
1084 
1085     CHECK(!m_grid->GridLinesEnabled());
1086 
1087     m_grid->EnableGridLines();
1088 
1089     m_grid->SetGridLineColour(*wxRED);
1090 
1091     CHECK(*wxRED == m_grid->GetGridLineColour());
1092 }
1093 
1094 TEST_CASE_METHOD(GridTestCase, "Grid::SortSupport", "[grid]")
1095 {
1096     CHECK(m_grid->GetSortingColumn() == wxNOT_FOUND);
1097 
1098     m_grid->SetSortingColumn(1);
1099 
1100     CHECK(!m_grid->IsSortingBy(0));
1101     CHECK(m_grid->IsSortingBy(1));
1102     CHECK(m_grid->IsSortOrderAscending());
1103 
1104     m_grid->SetSortingColumn(0, false);
1105 
1106     CHECK(m_grid->IsSortingBy(0));
1107     CHECK(!m_grid->IsSortingBy(1));
1108     CHECK(!m_grid->IsSortOrderAscending());
1109 
1110     m_grid->UnsetSortingColumn();
1111 
1112     CHECK(!m_grid->IsSortingBy(0));
1113     CHECK(!m_grid->IsSortingBy(1));
1114 }
1115 
1116 TEST_CASE_METHOD(GridTestCase, "Grid::Labels", "[grid]")
1117 {
1118     CHECK(m_grid->GetColLabelValue(0) == "A");
1119     CHECK(m_grid->GetRowLabelValue(0) == "1");
1120 
1121     m_grid->SetColLabelValue(0, "Column 1");
1122     m_grid->SetRowLabelValue(0, "Row 1");
1123 
1124     CHECK(m_grid->GetColLabelValue(0) == "Column 1");
1125     CHECK(m_grid->GetRowLabelValue(0) == "Row 1");
1126 
1127     m_grid->SetLabelTextColour(*wxGREEN);
1128     m_grid->SetLabelBackgroundColour(*wxRED);
1129 
1130     CHECK(m_grid->GetLabelTextColour() == *wxGREEN);
1131     CHECK(m_grid->GetLabelBackgroundColour() == *wxRED);
1132 
1133     m_grid->SetColLabelTextOrientation(wxVERTICAL);
1134 
1135     CHECK(m_grid->GetColLabelTextOrientation() == wxVERTICAL);
1136 }
1137 
1138 TEST_CASE_METHOD(GridTestCase, "Grid::SelectionMode", "[grid]")
1139 {
1140     //We already test this mode in Select
1141     CHECK(m_grid->GetSelectionMode() == wxGrid::wxGridSelectCells);
1142 
1143     // Select an individual cell and an entire row.
1144     m_grid->SelectBlock(3, 1, 3, 1);
1145     m_grid->SelectRow(5, true /* add to selection */);
1146 
1147     // Test that after switching to row selection mode only the row remains
1148     // selected.
1149     m_grid->SetSelectionMode(wxGrid::wxGridSelectRows);
1150     CHECK( m_grid->IsInSelection(5, 0) );
1151     CHECK( m_grid->IsInSelection(5, 1) );
1152     CHECK( !m_grid->IsInSelection(3, 1) );
1153 
1154     //Test row selection be selecting a single cell and checking the whole
1155     //row is selected
1156     m_grid->ClearSelection();
1157     m_grid->SelectBlock(3, 1, 3, 1);
1158 
1159     wxArrayInt selectedRows = m_grid->GetSelectedRows();
1160     CHECK(selectedRows.Count() == 1);
1161     CHECK(selectedRows[0] == 3);
1162 
1163     // Check that overlapping selection blocks are handled correctly.
1164     m_grid->ClearSelection();
1165     m_grid->SelectBlock(0, 0, 4, 1);
1166     m_grid->SelectBlock(2, 0, 6, 1, true /* add to selection */);
1167     CHECK( m_grid->GetSelectedRows().size() == 7 );
1168 
1169     CHECK( m_grid->GetSelectedColBlocks().empty() );
1170 
1171     RowRanges rowRanges;
1172     rowRanges.push_back(RowRange(0, 6));
1173     CheckRowSelection(rowRanges);
1174 
1175     m_grid->SelectBlock(6, 0, 8, 1);
1176     m_grid->SelectBlock(1, 0, 4, 1, true /* add to selection */);
1177     m_grid->SelectBlock(0, 0, 2, 1, true /* add to selection */);
1178     CHECK( m_grid->GetSelectedRows().size() == 8 );
1179 
1180     rowRanges.clear();
1181     rowRanges.push_back(RowRange(0, 4));
1182     rowRanges.push_back(RowRange(6, 8));
1183     CheckRowSelection(rowRanges);
1184 
1185     // Select all odd rows.
1186     m_grid->ClearSelection();
1187     rowRanges.clear();
1188     for ( int i = 1; i < m_grid->GetNumberRows(); i += 2 )
1189     {
1190         m_grid->SelectBlock(i, 0, i, 1, true);
1191         rowRanges.push_back(RowRange(i, i));
1192     }
1193 
1194     CheckRowSelection(rowRanges);
1195 
1196     // Now select another block overlapping 2 of them and bordering 2 others.
1197     m_grid->SelectBlock(2, 0, 6, 1, true);
1198 
1199     rowRanges.clear();
1200     rowRanges.push_back(RowRange(1, 7));
1201     rowRanges.push_back(RowRange(9, 9));
1202     CheckRowSelection(rowRanges);
1203 
1204     CHECK(m_grid->GetSelectionMode() == wxGrid::wxGridSelectRows);
1205 
1206 
1207     //Test column selection be selecting a single cell and checking the whole
1208     //column is selected
1209     m_grid->SetSelectionMode(wxGrid::wxGridSelectColumns);
1210     m_grid->SelectBlock(3, 1, 3, 1);
1211 
1212     CHECK( m_grid->GetSelectedRowBlocks().empty() );
1213 
1214     wxArrayInt selectedCols = m_grid->GetSelectedCols();
1215     CHECK(selectedCols.Count() == 1);
1216     CHECK(selectedCols[0] == 1);
1217 
1218     wxGridBlockCoordsVector colBlocks = m_grid->GetSelectedColBlocks();
1219     CHECK( colBlocks.size() == 1 );
1220     CHECK( colBlocks.at(0) == wxGridBlockCoords(0, 1, 9, 1) );
1221 
1222     CHECK(m_grid->GetSelectionMode() == wxGrid::wxGridSelectColumns);
1223 }
1224 
1225 TEST_CASE_METHOD(GridTestCase, "Grid::CellFormatting", "[grid]")
1226 {
1227     //Check that initial alignment is default
1228     int horiz, cellhoriz, vert, cellvert;
1229 
1230     m_grid->GetDefaultCellAlignment(&horiz, &vert);
1231     m_grid->GetCellAlignment(0, 0, &cellhoriz, &cellvert);
1232 
1233     CHECK(horiz == cellhoriz);
1234     CHECK(vert == cellvert);
1235 
1236     //Check initial text colour and background colour are default
1237     wxColour text, back;
1238 
1239     back = m_grid->GetDefaultCellBackgroundColour();
1240 
1241     CHECK(m_grid->GetCellBackgroundColour(0, 0) == back);
1242 
1243     back = m_grid->GetDefaultCellTextColour();
1244 
1245     CHECK(m_grid->GetCellTextColour(0, 0) == back);
1246 
1247 #if WXWIN_COMPATIBILITY_2_8
1248     m_grid->SetCellAlignment(wxALIGN_CENTRE, 0, 0);
1249     m_grid->GetCellAlignment(0, 0, &cellhoriz, &cellvert);
1250 
1251     CHECK(cellhoriz == wxALIGN_CENTRE);
1252     CHECK(cellvert == wxALIGN_CENTRE);
1253 #endif // WXWIN_COMPATIBILITY_2_8
1254 
1255     m_grid->SetCellAlignment(0, 0, wxALIGN_LEFT, wxALIGN_BOTTOM);
1256     m_grid->GetCellAlignment(0, 0, &cellhoriz, &cellvert);
1257 
1258     CHECK(cellhoriz == wxALIGN_LEFT);
1259     CHECK(cellvert == wxALIGN_BOTTOM);
1260 
1261 #if WXWIN_COMPATIBILITY_2_8
1262     m_grid->SetCellTextColour(*wxRED, 0, 0);
1263     CHECK(m_grid->GetCellTextColour(0, 0) == *wxRED);
1264 #endif // WXWIN_COMPATIBILITY_2_8
1265 
1266     m_grid->SetCellTextColour(0, 0, *wxGREEN);
1267     CHECK(m_grid->GetCellTextColour(0,0) == *wxGREEN);
1268 }
1269 
1270 TEST_CASE_METHOD(GridTestCase, "Grid::GetNonDefaultAlignment", "[grid]")
1271 {
1272     // GetNonDefaultAlignment() is used by several renderers having their own
1273     // preferred alignment, so check that if we don't reset the alignment
1274     // explicitly, it doesn't override the alignment used by default.
1275     wxGridCellAttrPtr attr;
1276     int hAlign = wxALIGN_RIGHT,
1277         vAlign = wxALIGN_INVALID;
1278 
1279     attr = m_grid->CallGetCellAttr(0, 0);
1280     REQUIRE( attr );
1281 
1282     // Check that the specified alignment is preserved, while the unspecified
1283     // component is filled with the default value (which is "top" by default).
1284     attr->GetNonDefaultAlignment(&hAlign, &vAlign);
1285     CHECK( hAlign == wxALIGN_RIGHT );
1286     CHECK( vAlign == wxALIGN_TOP );
1287 
1288     // Now change the defaults and check that the unspecified alignment
1289     // component is filled with the new default.
1290     m_grid->SetDefaultCellAlignment(wxALIGN_CENTRE_HORIZONTAL,
1291                                     wxALIGN_CENTRE_VERTICAL);
1292 
1293     vAlign = wxALIGN_INVALID;
1294 
1295     attr = m_grid->CallGetCellAttr(0, 0);
1296     REQUIRE( attr );
1297 
1298     attr->GetNonDefaultAlignment(&hAlign, &vAlign);
1299     CHECK( hAlign == wxALIGN_RIGHT );
1300     CHECK( vAlign == wxALIGN_CENTRE_VERTICAL );
1301 
1302     // This is only indirectly related, but test here for CanOverflow() working
1303     // correctly for the cells with non-default alignment, as this used to be
1304     // broken.
1305     m_grid->SetCellAlignment(0, 0, wxALIGN_INVALID, wxALIGN_CENTRE);
1306     attr = m_grid->CallGetCellAttr(0, 0);
1307     REQUIRE( attr );
1308     CHECK( attr->CanOverflow() );
1309 }
1310 
1311 TEST_CASE_METHOD(GridTestCase, "Grid::Editable", "[grid]")
1312 {
1313 #if wxUSE_UIACTIONSIMULATOR
1314     if ( !EnableUITests() )
1315         return;
1316 
1317     //As the grid is not editable we shouldn't create an editor
1318     EventCounter created(m_grid, wxEVT_GRID_EDITOR_CREATED);
1319 
1320     wxUIActionSimulator sim;
1321 
1322     CHECK(m_grid->IsEditable());
1323 
1324     m_grid->EnableEditing(false);
1325 
1326     CHECK(!m_grid->IsEditable());
1327 
1328     m_grid->SetFocus();
1329     m_grid->SetGridCursor(1, 1);
1330 
1331     sim.Text("abab");
1332     wxYield();
1333 
1334     sim.Char(WXK_RETURN);
1335     wxYield();
1336 
1337     CHECK(created.GetCount() == 0);
1338 #endif
1339 }
1340 
1341 TEST_CASE_METHOD(GridTestCase, "Grid::ReadOnly", "[grid]")
1342 {
1343 #if wxUSE_UIACTIONSIMULATOR
1344     if ( !EnableUITests() )
1345         return;
1346 
1347     //As the cell is readonly we shouldn't create an editor
1348     EventCounter created(m_grid, wxEVT_GRID_EDITOR_CREATED);
1349 
1350     wxUIActionSimulator sim;
1351 
1352     CHECK(!m_grid->IsReadOnly(1, 1));
1353 
1354     m_grid->SetReadOnly(1, 1);
1355 
1356     CHECK(m_grid->IsReadOnly(1, 1));
1357 
1358     m_grid->SetFocus();
1359 
1360     m_grid->SetGridCursor(1, 1);
1361 
1362     CHECK(m_grid->IsCurrentCellReadOnly());
1363 
1364     sim.Text("abab");
1365     wxYield();
1366 
1367     sim.Char(WXK_RETURN);
1368     wxYield();
1369 
1370     CHECK(created.GetCount() == 0);
1371 #endif
1372 }
1373 
1374 TEST_CASE_METHOD(GridTestCase, "Grid::WindowAsEditorControl", "[grid]")
1375 {
1376 #if wxUSE_UIACTIONSIMULATOR
1377     if ( !EnableUITests() )
1378         return;
1379 
1380     // A very simple editor using a window not derived from wxControl as the
1381     // editor.
1382     class TestEditor : public wxGridCellEditor
1383     {
1384     public:
TestEditor()1385         TestEditor() {}
1386 
Create(wxWindow * parent,wxWindowID id,wxEvtHandler * evtHandler)1387         void Create(wxWindow* parent,
1388                     wxWindowID id,
1389                     wxEvtHandler* evtHandler) wxOVERRIDE
1390         {
1391             SetWindow(new wxWindow(parent, id));
1392             wxGridCellEditor::Create(parent, id, evtHandler);
1393         }
1394 
BeginEdit(int,int,wxGrid *)1395         void BeginEdit(int, int, wxGrid*) wxOVERRIDE {}
1396 
EndEdit(int,int,wxGrid const *,wxString const &,wxString * newval)1397         bool EndEdit(int, int, wxGrid const*, wxString const&,
1398                      wxString* newval) wxOVERRIDE
1399         {
1400             *newval = GetValue();
1401             return true;
1402         }
1403 
ApplyEdit(int row,int col,wxGrid * grid)1404         void ApplyEdit(int row, int col, wxGrid* grid) wxOVERRIDE
1405         {
1406             grid->GetTable()->SetValue(row, col, GetValue());
1407         }
1408 
Reset()1409         void Reset() wxOVERRIDE {}
1410 
Clone() const1411         wxGridCellEditor* Clone() const wxOVERRIDE { return new TestEditor(); }
1412 
GetValue() const1413         wxString GetValue() const wxOVERRIDE { return "value"; }
1414     };
1415 
1416     wxGridCellAttr* attr = new wxGridCellAttr();
1417     attr->SetRenderer(new wxGridCellStringRenderer());
1418     attr->SetEditor(new TestEditor());
1419     m_grid->SetAttr(1, 1, attr);
1420 
1421     EventCounter created(m_grid, wxEVT_GRID_EDITOR_CREATED);
1422 
1423     wxUIActionSimulator sim;
1424 
1425     m_grid->SetFocus();
1426     m_grid->SetGridCursor(1, 1);
1427     m_grid->EnableCellEditControl();
1428 
1429     sim.Char(WXK_RETURN);
1430 
1431     wxYield();
1432 
1433     CHECK(created.GetCount() == 1);
1434 #endif
1435 }
1436 
1437 TEST_CASE_METHOD(GridTestCase, "Grid::ResizeScrolledHeader", "[grid]")
1438 {
1439     // TODO this test currently works only under Windows unfortunately
1440 #if wxUSE_UIACTIONSIMULATOR && (defined(__WXMSW__) || defined(__WXGTK__))
1441     if ( !EnableUITests() )
1442         return;
1443 
1444 #ifdef __WXGTK20__
1445     // Works locally, but not when run on Travis CI.
1446     if ( IsAutomaticTest() )
1447         return;
1448 #endif
1449 
1450     SECTION("Default") {}
1451     SECTION("Native header") { m_grid->UseNativeColHeader(); }
1452 
1453     int const startwidth = m_grid->GetColSize(0);
1454     int const draglength = 100;
1455 
1456     m_grid->AppendCols(8);
1457     m_grid->Scroll(5, 0);
1458     m_grid->Refresh();
1459     m_grid->Update();
1460 
1461     wxRect rect = m_grid->CellToRect(0, 1);
1462     wxPoint point = m_grid->CalcScrolledPosition(rect.GetPosition());
1463     point = m_grid->ClientToScreen(point
1464                                    + wxPoint(m_grid->GetRowLabelSize(),
1465                                              m_grid->GetColLabelSize())
1466                                    - wxPoint(0, 5));
1467 
1468     wxUIActionSimulator sim;
1469 
1470     wxYield();
1471     sim.MouseMove(point);
1472 
1473     wxYield();
1474     sim.MouseDown();
1475 
1476     wxYield();
1477     sim.MouseMove(point + wxPoint(draglength, 0));
1478 
1479     wxYield();
1480     sim.MouseUp();
1481 
1482     wxYield();
1483 
1484     CHECK(m_grid->GetColSize(0) == startwidth + draglength);
1485 #endif
1486 }
1487 
1488 TEST_CASE_METHOD(GridTestCase, "Grid::ColumnMinWidth", "[grid]")
1489 {
1490     // TODO this test currently works only under Windows unfortunately
1491 #if wxUSE_UIACTIONSIMULATOR && (defined(__WXMSW__) || defined(__WXGTK__))
1492     if ( !EnableUITests() )
1493         return;
1494 
1495 #ifdef __WXGTK20__
1496     // Works locally, but not when run on Travis CI.
1497     if ( IsAutomaticTest() )
1498         return;
1499 #endif
1500 
1501     SECTION("Default") {}
1502     SECTION("Native header")
1503     {
1504         // For some unknown reason, this test fails under AppVeyor even though
1505         // it passes locally, so disable it there. If anybody can reproduce the
1506         // problem locally, where it can be debugged, please let us know.
1507         if ( IsAutomaticTest() )
1508             return;
1509 
1510         m_grid->UseNativeColHeader();
1511     }
1512 
1513     int const startminwidth = m_grid->GetColMinimalAcceptableWidth();
1514     m_grid->SetColMinimalAcceptableWidth(startminwidth*2);
1515     int const newminwidth = m_grid->GetColMinimalAcceptableWidth();
1516     int const startwidth = m_grid->GetColSize(0);
1517 
1518     CHECK(m_grid->GetColMinimalAcceptableWidth() < startwidth);
1519 
1520     wxRect rect = m_grid->CellToRect(0, 1);
1521     wxPoint point = m_grid->CalcScrolledPosition(rect.GetPosition());
1522     point = m_grid->ClientToScreen(point
1523                                    + wxPoint(m_grid->GetRowLabelSize(),
1524                                              m_grid->GetColLabelSize())
1525                                    - wxPoint(0, 5));
1526 
1527     wxUIActionSimulator sim;
1528 
1529     // Drag to reach the minimal width.
1530     wxYield();
1531     sim.MouseMove(point);
1532     wxYield();
1533     sim.MouseDown();
1534     wxYield();
1535     sim.MouseMove(point - wxPoint(startwidth - startminwidth, 0));
1536     wxYield();
1537     sim.MouseUp();
1538     wxYield();
1539 
1540     CHECK(m_grid->GetColSize(0) == newminwidth);
1541 #endif
1542 }
1543 
CheckFirstColAutoSize(int expected)1544 void GridTestCase::CheckFirstColAutoSize(int expected)
1545 {
1546     m_grid->AutoSizeColumn(0);
1547 
1548     wxYield();
1549     CHECK(m_grid->GetColSize(0) == expected);
1550 }
1551 
1552 TEST_CASE_METHOD(GridTestCase, "Grid::AutoSizeColumn", "[grid]")
1553 {
1554     SECTION("Default") {}
1555     SECTION("Native header") { m_grid->UseNativeColHeader(); }
1556     SECTION("Native labels") { m_grid->SetUseNativeColLabels(); }
1557 
1558     // Hardcoded extra margin for the columns used in grid.cpp.
1559     const int margin = m_grid->FromDIP(10);
1560 
1561     wxGridCellAttrPtr attr(m_grid->GetOrCreateCellAttr(0, 0));
1562     wxGridCellRendererPtr renderer(attr->GetRenderer(m_grid, 0, 0));
1563     REQUIRE(renderer);
1564 
1565     wxClientDC dcCell(m_grid->GetGridWindow());
1566 
1567     wxClientDC dcLabel(m_grid->GetGridWindow());
1568     dcLabel.SetFont(m_grid->GetLabelFont());
1569 
1570     const wxString shortStr     = "W";
1571     const wxString mediumStr    = "WWWW";
1572     const wxString longStr      = "WWWWWWWW";
1573     const wxString multilineStr = mediumStr + "\n" + longStr;
1574 
1575     SECTION("Empty column and label")
1576     {
1577         m_grid->SetColLabelValue(0, wxString());
1578         CheckFirstColAutoSize( m_grid->GetDefaultColSize() );
1579     }
1580 
1581     SECTION("Empty column with label")
1582     {
1583         m_grid->SetColLabelValue(0, mediumStr);
1584         CheckFirstColAutoSize( GetColumnLabelWidth(dcLabel, 0, margin) );
1585     }
1586 
1587     SECTION("Column with empty label")
1588     {
1589         m_grid->SetColLabelValue(0, wxString());
1590         m_grid->SetCellValue(0, 0, mediumStr);
1591         m_grid->SetCellValue(1, 0, shortStr);
1592         m_grid->SetCellValue(3, 0, longStr);
1593 
1594         CheckFirstColAutoSize(
1595             renderer->GetBestWidth(*m_grid, *attr, dcCell, 3, 0,
1596                                    m_grid->GetRowHeight(3)) + margin );
1597     }
1598 
1599     SECTION("Column with label longer than contents")
1600     {
1601         m_grid->SetColLabelValue(0, multilineStr);
1602         m_grid->SetCellValue(0, 0, mediumStr);
1603         m_grid->SetCellValue(1, 0, shortStr);
1604         CheckFirstColAutoSize( GetColumnLabelWidth(dcLabel, 0, margin) );
1605     }
1606 
1607     SECTION("Column with contents longer than label")
1608     {
1609         m_grid->SetColLabelValue(0, mediumStr);
1610         m_grid->SetCellValue(0, 0, mediumStr);
1611         m_grid->SetCellValue(1, 0, shortStr);
1612         m_grid->SetCellValue(3, 0, multilineStr);
1613         CheckFirstColAutoSize(
1614             renderer->GetBestWidth(*m_grid, *attr, dcCell, 3, 0,
1615                                    m_grid->GetRowHeight(3)) + margin );
1616     }
1617 
1618     SECTION("Column with equally sized contents and label")
1619     {
1620         m_grid->SetColLabelValue(0, mediumStr);
1621         m_grid->SetCellValue(0, 0, mediumStr);
1622         m_grid->SetCellValue(1, 0, mediumStr);
1623         m_grid->SetCellValue(3, 0, mediumStr);
1624 
1625         const int labelWidth = GetColumnLabelWidth(dcLabel, 0, margin);
1626 
1627         const int cellWidth =
1628             renderer->GetBestWidth(*m_grid, *attr, dcCell, 3, 0,
1629                                    m_grid->GetRowHeight(3))
1630             + margin;
1631 
1632         // We can't be sure which size will be greater because of different fonts
1633         // so just calculate the maximum width.
1634         CheckFirstColAutoSize( wxMax(labelWidth, cellWidth) );
1635     }
1636 
1637     SECTION("Column with auto wrapping contents taller than row")
1638     {
1639         // Verify row height remains unchanged with an auto-wrapping multi-line
1640         // cell.
1641         // Also shouldn't continuously try to fit the multi-line content into
1642         // a single line, which is not possible. See
1643         // https://trac.wxwidgets.org/ticket/15943 .
1644 
1645         m_grid->SetCellValue(0, 0, multilineStr);
1646         m_grid->SetCellRenderer(0 , 0, new wxGridCellAutoWrapStringRenderer);
1647         m_grid->AutoSizeColumn(0);
1648 
1649         wxYield();
1650         CHECK( m_grid->GetRowSize(0) == m_grid->GetDefaultRowSize() );
1651     }
1652 }
1653 
1654 TEST_CASE_METHOD(GridTestCase, "Grid::DrawInvalidCell", "[grid][multicell]")
1655 {
1656     // Set up a multicell with inside an overflowing cell.
1657     // This is artificial and normally inside cells are probably not expected
1658     // to have a value but this is merely done to check if inside cells are
1659     // drawn, which they shouldn't be.
1660     m_grid->SetCellSize(0, 0, 2, 1);
1661     m_grid->SetCellValue( 1, 0, wxString('W', 42) );
1662 
1663     // Update()s, yields and sleep are needed to try to make the test fail with
1664     // macOS, GTK and MSW.
1665     // MSW needs just the yields (or updates), macOS in addition needs to sleep
1666     // (doesn't work with updates) and for GTK it's usually enough to just do
1667     // two updates (not yields). This test does all unconditionally.
1668     m_grid->Update();
1669     wxYield();
1670     wxMilliSleep(20);
1671 
1672     // Try to force redrawing of the inside cell: if it still draws there will
1673     // be an infinite recursion.
1674     m_grid->SetColSize(1, m_grid->GetColSize(1) + 1);
1675 
1676     m_grid->Update();
1677     wxYield();
1678 }
1679 
1680 #define CHECK_ATTR_COUNT(n) CHECK( m_grid->GetCellAttrCount() == n )
1681 
1682 TEST_CASE_METHOD(GridTestCase, "Grid::CellAttribute", "[attr][cell][grid]")
1683 {
1684     SECTION("Overwrite")
1685     {
1686         CHECK_ATTR_COUNT( 0 );
1687 
1688         m_grid->SetAttr(0, 0, NULL);
1689         CHECK_ATTR_COUNT( 0 );
1690 
1691         SetCellAttr(0, 0);
1692         CHECK_ATTR_COUNT( 1 );
1693 
1694         // Overwrite existing attribute with another.
1695         SetCellAttr(0, 0);
1696         CHECK_ATTR_COUNT( 1 );
1697 
1698         m_grid->SetAttr(0, 0, NULL);
1699         CHECK_ATTR_COUNT( 0 );
1700 
1701         SetCellAttr(0, 0);
1702         m_grid->SetCellBackgroundColour(0, 1, *wxGREEN);
1703         CHECK_ATTR_COUNT( 2 );
1704 
1705         m_grid->SetAttr(0, 1, NULL);
1706         m_grid->SetAttr(0, 0, NULL);
1707         CHECK_ATTR_COUNT( 0 );
1708     }
1709 
1710 
1711     // Fill the grid with attributes for next sections.
1712 
1713     const int numRows = m_grid->GetNumberRows();
1714     const int numCols = m_grid->GetNumberCols();
1715 
1716     for ( int row = 0; row < numRows; ++row )
1717     {
1718         for ( int col = 0; col < numCols; ++col )
1719             SetCellAttr(row, col);
1720     }
1721 
1722     size_t numAttrs = static_cast<size_t>(numRows * numCols);
1723 
1724     CHECK_ATTR_COUNT( numAttrs );
1725 
1726     SECTION("Expanding")
1727     {
1728         CHECK( !HasCellAttr(numRows, numCols) );
1729 
1730         m_grid->InsertCols();
1731         CHECK_ATTR_COUNT( numAttrs );
1732         CHECK( !HasCellAttr(0, 0) );
1733         CHECK( HasCellAttr(0, numCols) );
1734 
1735         m_grid->InsertRows();
1736         CHECK_ATTR_COUNT( numAttrs );
1737         CHECK( HasCellAttr(numRows, numCols) );
1738     }
1739 
1740     SECTION("Shrinking")
1741     {
1742         CHECK( HasCellAttr(numRows - 1 , numCols - 1) );
1743         CHECK( HasCellAttr(0, numCols - 1) );
1744 
1745         m_grid->DeleteCols();
1746         numAttrs -= m_grid->GetNumberRows();
1747         CHECK_ATTR_COUNT( numAttrs );
1748         CHECK( HasCellAttr(0, 0) );
1749         CHECK( !HasCellAttr(0, numCols - 1) );
1750 
1751         m_grid->DeleteRows();
1752         numAttrs -= m_grid->GetNumberCols();
1753         CHECK_ATTR_COUNT( numAttrs );
1754         CHECK( !HasCellAttr(numRows - 1 , numCols - 1) );
1755     }
1756 }
1757 
1758 #define CHECK_MULTICELL() CHECK_THAT( *m_grid, HasMulticellOnly(multi) )
1759 
1760 #define CHECK_NO_MULTICELL() CHECK_THAT( *m_grid, HasEmptyGrid() )
1761 
1762 #define WHEN_N(s, n) WHEN(wxString::Format(s, n).ToStdString())
1763 
1764 TEST_CASE_METHOD(GridTestCase,
1765                  "Grid::InsertionsWithMulticell",
1766                  "[attr][cell][grid][insert][multicell]")
1767 {
1768     int insertions = 0, offset = 0;
1769 
1770     Multicell multi(1, 1, 3, 5);
1771 
1772     SECTION("Sanity checks")
1773     {
1774         FitGridToMulticell(m_grid, multi);
1775         m_grid->SetMulticell(multi);
1776 
1777         REQUIRE( static_cast<int>(m_grid->GetCellAttrCount())
1778                     == multi.rows * multi.cols );
1779 
1780         int row, col, rows, cols;
1781 
1782         // Check main cell.
1783         row = multi.row,
1784         col = multi.col;
1785         wxGrid::CellSpan span = m_grid->GetCellSize(row, col, &rows, &cols);
1786 
1787         REQUIRE( span == wxGrid::CellSpan_Main );
1788         REQUIRE( rows == multi.rows );
1789         REQUIRE( cols == multi.cols );
1790 
1791         // Check inside cell at opposite of main.
1792         row = multi.row + multi.rows - 1;
1793         col = multi.col + multi.cols - 1;
1794         span = m_grid->GetCellSize(row, col, &rows, &cols);
1795 
1796         REQUIRE( span == wxGrid::CellSpan_Inside );
1797         REQUIRE( rows == multi.row - row );
1798         REQUIRE( cols == multi.col - col );
1799     }
1800 
1801     // Do some basic testing with column insertions first and do more tests
1802     // with edge cases later on just with rows. It's not really needed to
1803     // repeat the same tests for both rows and columns as the code for
1804     // updating them works symmetrically.
1805 
GIVEN(Catch::toString (multi))1806     GIVEN(Catch::toString(multi))
1807     {
1808         FitGridToMulticell(m_grid, multi);
1809         m_grid->SetMulticell(multi);
1810 
1811         insertions = 2;
1812 
1813         WHEN("inserting any columns in multicell, at main")
1814         {
1815             InsertCols(multi.col + 0, insertions);
1816 
1817             THEN("the position changes but not the size")
1818             {
1819                 multi.col += insertions;
1820                 CHECK_MULTICELL();
1821             }
1822         }
1823 
1824         WHEN("inserting any columns in multicell, just after main")
1825         {
1826             InsertCols(multi.col + 1, insertions);
1827 
1828             THEN("the size changes but not the position")
1829             {
1830                 multi.cols += insertions;
1831                 CHECK_MULTICELL();
1832             }
1833         }
1834 
1835     }
1836 
1837     // Do more extensive testing with rows.
1838 
1839     wxSwap(multi.rows, multi.cols);
1840 
GIVEN(Catch::toString (multi))1841     GIVEN(Catch::toString(multi))
1842     {
1843         FitGridToMulticell(m_grid, multi);
1844         m_grid->SetMulticell(multi);
1845 
1846         const int insertionCounts[] = {1, 2, multi.rows};
1847 
1848         for ( size_t i = 0; i < WXSIZEOF(insertionCounts); ++i )
1849         {
1850             insertions = insertionCounts[i];
1851 
1852             WHEN_N("inserting %d row(s), just before main", insertions)
1853             {
1854                 InsertRows(multi.row - 1, insertions);
1855 
1856                 THEN("the position changes but not the size")
1857                 {
1858                     multi.row += insertions;
1859                     CHECK_MULTICELL();
1860                 }
1861             }
1862 
1863             WHEN_N("inserting %d row(s) in multicell, at main", insertions)
1864             {
1865                 InsertRows(multi.row + 0, insertions);
1866 
1867                 THEN("the position changes but not the size")
1868                 {
1869                     multi.row += insertions;
1870                     CHECK_MULTICELL();
1871                 }
1872             }
1873         }
1874 
1875         insertions = multi.rows / 2;
1876 
1877         // Check insertions within multicell, at and near edges.
1878         const int insertionOffsets[] = {1, 2, multi.rows - 2, multi.rows - 1};
1879 
1880         for ( size_t i = 0; i < WXSIZEOF(insertionOffsets); ++i )
1881         {
1882             offset = insertionOffsets[i];
1883 
1884             WHEN_N("inserting rows in multicell, %d row(s) after main", offset)
1885             {
1886                 InsertRows(multi.row + offset, insertions);
1887 
1888                 THEN("the size changes but not the position")
1889                 {
1890                     multi.rows += insertions;
1891                     CHECK_MULTICELL();
1892                 }
1893             }
1894         }
1895 
1896         // Check at least one case of inserting after multicell.
1897         WHEN("inserting rows, just after multicell")
1898         {
1899             insertions = 2;
1900             InsertRows(multi.row + multi.rows, insertions);
1901 
1902             THEN("neither size nor position change")
1903             {
1904                 CHECK_MULTICELL();
1905             }
1906         }
1907     }
1908 
1909 }
1910 
1911 TEST_CASE_METHOD(GridTestCase,
1912                  "GridMulticell::DeletionsWithMulticell",
1913                  "[cellattr][delete][grid][multicell]")
1914 {
1915     int deletions = 0, offset = 0;
1916 
1917     // Same as with the previous (insertions) test case but instead of some
1918     // basic testing with columns first, this time use rows for that and do more
1919     // extensive testing with columns.
1920 
1921     Multicell multi(1, 1, 5, 3);
1922 
GIVEN(Catch::toString (multi))1923     GIVEN(Catch::toString(multi))
1924     {
1925         FitGridToMulticell(m_grid, multi);
1926         m_grid->SetMulticell(multi);
1927 
1928         WHEN("deleting any rows, at main")
1929         {
1930             deletions = 1;
1931             DeleteRows(multi.row + 0, deletions);
1932 
1933             THEN("the multicell is deleted")
1934             {
1935                 CHECK_NO_MULTICELL();
1936             }
1937         }
1938 
1939         WHEN("deleting more rows than length of multicell,"
1940              " at end of multicell")
1941         {
1942             deletions = multi.rows + 2;
1943             offset = multi.rows - 1;
1944             DeleteRows(multi.row + offset, deletions);
1945 
1946             THEN("the size changes but not the position")
1947             {
1948                 multi.rows = offset;
1949                 CHECK_MULTICELL();
1950             }
1951         }
1952     }
1953 
1954     // Do more extensive testing with columns.
1955 
1956     wxSwap(multi.rows, multi.cols);
1957 
GIVEN(Catch::toString (multi))1958     GIVEN(Catch::toString(multi))
1959     {
1960         FitGridToMulticell(m_grid, multi);
1961         m_grid->SetMulticell(multi);
1962 
1963         WHEN("deleting one column, just before main")
1964         {
1965             DeleteCols(multi.col - 1);
1966 
1967             THEN("the position changes but not the size")
1968             {
1969                 multi.col--;
1970                 CHECK_MULTICELL();
1971             }
1972         }
1973 
1974         WHEN("deleting multiple columns, just before main")
1975         {
1976             deletions = 2; // Must be at least 2 to affect main.
1977             DeleteCols(multi.col - 1, wxMax(2, deletions));
1978 
1979             THEN("the multicell is deleted")
1980             {
1981                 CHECK_NO_MULTICELL();
1982             }
1983         }
1984 
1985         WHEN("deleting any columns, at main")
1986         {
1987             deletions = 1;
1988             DeleteCols(multi.col + 0, deletions);
1989 
1990             THEN("the multicell is deleted")
1991             {
1992                 CHECK_NO_MULTICELL();
1993             }
1994         }
1995 
1996         WHEN("deleting one column within multicell, after main")
1997         {
1998             offset = 1;
1999             offset = wxClip(offset, 1, multi.cols - 1);
2000             DeleteCols(multi.col + offset, 1);
2001 
2002             THEN("the size changes but not the position")
2003             {
2004                 multi.cols--;
2005                 CHECK_MULTICELL();
2006             }
2007         }
2008 
2009         deletions = 2;
2010 
2011         // Check deletions within multicell, at and near edges.
2012         const int offsets[] = {1, 2, multi.cols - deletions};
2013 
2014         for ( size_t i = 0; i < WXSIZEOF(offsets); ++i )
2015         {
2016             offset = offsets[i];
2017 
2018             WHEN_N("deleting columns only within multicell,"
2019                    " %d column(s) after main", offset)
2020             {
2021                 DeleteCols(multi.col + offset, deletions);
2022 
2023                 THEN("the size changes but not the position")
2024                 {
2025                     multi.cols -= deletions;
2026                     CHECK_MULTICELL();
2027                 }
2028             }
2029         }
2030 
2031         // Instead of stuffing the multicell's length logic in the above test,
2032         // separately check at least two cases of starting many deletions
2033         // within multicell.
2034 
2035         WHEN("deleting more columns than length of multicell, just after main")
2036         {
2037             deletions = multi.cols + 2;
2038             offset = 1;
2039             DeleteCols(multi.col + offset, deletions);
2040 
2041             THEN("the size changes but not the position")
2042             {
2043                 multi.cols = offset;
2044                 CHECK_MULTICELL();
2045             }
2046         }
2047 
2048         WHEN("deleting more columns than length of multicell,"
2049              " at end of multicell")
2050         {
2051             deletions = multi.cols + 2;
2052             offset = multi.cols - 1;
2053             DeleteCols(multi.col + offset, deletions);
2054 
2055             THEN("the size changes but not the position")
2056             {
2057                 multi.cols = offset;
2058                 CHECK_MULTICELL();
2059             }
2060         }
2061     }
2062 
2063 }
2064 
2065 // Test wxGridBlockCoords here because it'a a part of grid sources.
2066 
operator <<(std::ostream & os,const wxGridBlockCoords & block)2067 std::ostream& operator<<(std::ostream& os, const wxGridBlockCoords& block) {
2068     os << "wxGridBlockCoords(" <<
2069         block.GetTopRow() << ", " << block.GetLeftCol() << ", " <<
2070         block.GetBottomRow() << ", " << block.GetRightCol() << ")";
2071     return os;
2072 }
2073 
2074 TEST_CASE("GridBlockCoords::Canonicalize", "[grid]")
2075 {
2076     const wxGridBlockCoords block =
2077         wxGridBlockCoords(4, 3, 2, 1).Canonicalize();
2078 
2079     CHECK(block.GetTopRow() == 2);
2080     CHECK(block.GetLeftCol() == 1);
2081     CHECK(block.GetBottomRow() == 4);
2082     CHECK(block.GetRightCol() == 3);
2083 }
2084 
2085 TEST_CASE("GridBlockCoords::Intersects", "[grid]")
2086 {
2087     // Inside.
2088     CHECK(wxGridBlockCoords(1, 1, 3, 3).Intersects(wxGridBlockCoords(1, 2, 2, 3)));
2089 
2090     // Intersects.
2091     CHECK(wxGridBlockCoords(1, 1, 3, 3).Intersects(wxGridBlockCoords(2, 2, 4, 4)));
2092 
2093     // Doesn't intersects.
2094     CHECK(!wxGridBlockCoords(1, 1, 3, 3).Intersects(wxGridBlockCoords(4, 4, 6, 6)));
2095 }
2096 
2097 TEST_CASE("GridBlockCoords::Contains", "[grid]")
2098 {
2099     // Inside.
2100     CHECK(wxGridBlockCoords(1, 1, 3, 3).Contains(wxGridCellCoords(2, 2)));
2101 
2102     // Outside.
2103     CHECK(!wxGridBlockCoords(1, 1, 3, 3).Contains(wxGridCellCoords(5, 5)));
2104 
2105     wxGridBlockCoords block1(1, 1, 5, 5);
2106     wxGridBlockCoords block2(1, 1, 3, 3);
2107     wxGridBlockCoords block3(2, 2, 7, 7);
2108     wxGridBlockCoords block4(10, 10, 12, 12);
2109 
2110     CHECK( block1.Contains(block2));
2111     CHECK(!block2.Contains(block1));
2112     CHECK(!block1.Contains(block3));
2113     CHECK(!block1.Contains(block4));
2114     CHECK(!block3.Contains(block1));
2115     CHECK(!block4.Contains(block1));
2116 }
2117 
2118 TEST_CASE("GridBlockCoords::Difference", "[grid]")
2119 {
2120     SECTION("Subtract contained block (splitted horizontally)")
2121     {
2122         const wxGridBlockCoords block1(1, 1, 7, 7);
2123         const wxGridBlockCoords block2(3, 3, 5, 5);
2124         const wxGridBlockDiffResult result =
2125             block1.Difference(block2, wxHORIZONTAL);
2126 
2127         CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 2, 7));
2128         CHECK(result.m_parts[1] == wxGridBlockCoords(6, 1, 7, 7));
2129         CHECK(result.m_parts[2] == wxGridBlockCoords(3, 1, 5, 2));
2130         CHECK(result.m_parts[3] == wxGridBlockCoords(3, 6, 5, 7));
2131     }
2132 
2133     SECTION("Subtract contained block (splitted vertically)")
2134     {
2135         const wxGridBlockCoords block1(1, 1, 7, 7);
2136         const wxGridBlockCoords block2(3, 3, 5, 5);
2137         const wxGridBlockDiffResult result =
2138             block1.Difference(block2, wxVERTICAL);
2139 
2140         CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 7, 2));
2141         CHECK(result.m_parts[1] == wxGridBlockCoords(1, 6, 7, 7));
2142         CHECK(result.m_parts[2] == wxGridBlockCoords(1, 3, 2, 5));
2143         CHECK(result.m_parts[3] == wxGridBlockCoords(6, 3, 7, 5));
2144     }
2145 
2146     SECTION("Blocks intersect by the corner (splitted horizontally)")
2147     {
2148         const wxGridBlockCoords block1(1, 1, 5, 5);
2149         const wxGridBlockCoords block2(3, 3, 7, 7);
2150         const wxGridBlockDiffResult result =
2151             block1.Difference(block2, wxHORIZONTAL);
2152 
2153         CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 2, 5));
2154         CHECK(result.m_parts[1] == wxGridNoBlockCoords);
2155         CHECK(result.m_parts[2] == wxGridBlockCoords(3, 1, 5, 2));
2156         CHECK(result.m_parts[3] == wxGridNoBlockCoords);
2157     }
2158 
2159     SECTION("Blocks intersect by the corner (splitted vertically)")
2160     {
2161         const wxGridBlockCoords block1(1, 1, 5, 5);
2162         const wxGridBlockCoords block2(3, 3, 7, 7);
2163         const wxGridBlockDiffResult result =
2164             block1.Difference(block2, wxVERTICAL);
2165 
2166         CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 5, 2));
2167         CHECK(result.m_parts[1] == wxGridNoBlockCoords);
2168         CHECK(result.m_parts[2] == wxGridBlockCoords(1, 3, 2, 5));
2169         CHECK(result.m_parts[3] == wxGridNoBlockCoords);
2170     }
2171 
2172     SECTION("Blocks are the same")
2173     {
2174         const wxGridBlockCoords block1(1, 1, 3, 3);
2175         const wxGridBlockCoords block2(1, 1, 3, 3);
2176         const wxGridBlockDiffResult result =
2177             block1.Difference(block2, wxHORIZONTAL);
2178 
2179         CHECK(result.m_parts[0] == wxGridNoBlockCoords);
2180         CHECK(result.m_parts[1] == wxGridNoBlockCoords);
2181         CHECK(result.m_parts[2] == wxGridNoBlockCoords);
2182         CHECK(result.m_parts[3] == wxGridNoBlockCoords);
2183     }
2184 
2185     SECTION("Blocks doesn't intersects")
2186     {
2187         const wxGridBlockCoords block1(1, 1, 3, 3);
2188         const wxGridBlockCoords block2(5, 5, 7, 7);
2189         const wxGridBlockDiffResult result =
2190             block1.Difference(block2, wxHORIZONTAL);
2191 
2192         CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 3, 3));
2193         CHECK(result.m_parts[1] == wxGridNoBlockCoords);
2194         CHECK(result.m_parts[2] == wxGridNoBlockCoords);
2195         CHECK(result.m_parts[3] == wxGridNoBlockCoords);
2196     }
2197 }
2198 
2199 
2200 TEST_CASE("GridBlockCoords::SymDifference", "[grid]")
2201 {
2202     SECTION("With contained block")
2203     {
2204         const wxGridBlockCoords block1(1, 1, 7, 7);
2205         const wxGridBlockCoords block2(3, 3, 5, 5);
2206         const wxGridBlockDiffResult result = block1.SymDifference(block2);
2207 
2208         CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 2, 7));
2209         CHECK(result.m_parts[1] == wxGridBlockCoords(6, 1, 7, 7));
2210         CHECK(result.m_parts[2] == wxGridBlockCoords(3, 1, 5, 2));
2211         CHECK(result.m_parts[3] == wxGridBlockCoords(3, 6, 5, 7));
2212     }
2213 
2214     SECTION("Blocks intersect by the corner")
2215     {
2216         const wxGridBlockCoords block1(1, 1, 5, 5);
2217         const wxGridBlockCoords block2(3, 3, 7, 7);
2218         const wxGridBlockDiffResult result = block1.SymDifference(block2);
2219 
2220         CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 2, 5));
2221         CHECK(result.m_parts[1] == wxGridBlockCoords(6, 3, 7, 7));
2222         CHECK(result.m_parts[2] == wxGridBlockCoords(3, 1, 5, 2));
2223         CHECK(result.m_parts[3] == wxGridBlockCoords(3, 6, 5, 7));
2224     }
2225 
2226     SECTION("Blocks are the same")
2227     {
2228         const wxGridBlockCoords block1(1, 1, 3, 3);
2229         const wxGridBlockCoords block2(1, 1, 3, 3);
2230         const wxGridBlockDiffResult result = block1.SymDifference(block2);
2231 
2232         CHECK(result.m_parts[0] == wxGridNoBlockCoords);
2233         CHECK(result.m_parts[1] == wxGridNoBlockCoords);
2234         CHECK(result.m_parts[2] == wxGridNoBlockCoords);
2235         CHECK(result.m_parts[3] == wxGridNoBlockCoords);
2236     }
2237 
2238     SECTION("Blocks doesn't intersects")
2239     {
2240         const wxGridBlockCoords block1(1, 1, 3, 3);
2241         const wxGridBlockCoords block2(5, 5, 7, 7);
2242         const wxGridBlockDiffResult result = block1.SymDifference(block2);
2243 
2244         CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 3, 3));
2245         CHECK(result.m_parts[1] == wxGridBlockCoords(5, 5, 7, 7));
2246         CHECK(result.m_parts[2] == wxGridNoBlockCoords);
2247         CHECK(result.m_parts[3] == wxGridNoBlockCoords);
2248     }
2249 }
2250 
2251 //
2252 // TestableGrid
2253 //
2254 
DoEdit(const EditInfo & edit)2255 void TestableGrid::DoEdit(const EditInfo& edit)
2256 {
2257     m_edit = edit;
2258     m_beforeGridAnnotated = ToString();
2259 
2260     switch ( edit.direction )
2261     {
2262     case wxGRID_COLUMN:
2263         if ( edit.count < 0 )
2264             DeleteCols(edit.pos, -edit.count);
2265         else
2266             InsertCols(edit.pos, edit.count);
2267         break;
2268 
2269     case wxGRID_ROW:
2270         if ( edit.count < 0 )
2271             DeleteRows(edit.pos, -edit.count);
2272         else
2273             InsertRows(edit.pos, edit.count);
2274         break;
2275     }
2276 }
2277 
ToString() const2278 wxString TestableGrid::ToString() const
2279 {
2280     const int numRows = GetNumberRows();
2281     const int numCols = GetNumberCols();
2282 
2283     const int colMargin = GetRowLabelValue(numRows - 1).length();
2284     const wxString leftIndent = wxString(' ', colMargin + 1);
2285 
2286     // String s contains the rendering of the grid, start with drawing
2287     // the header columns.
2288     wxString s = leftIndent;
2289 
2290     const int base = 10;
2291     // Draw the multiples of 10.
2292     for ( int col = base; col <= numCols; col += base)
2293     {
2294         s += wxString(' ', base - 1);
2295         s += ('0' + (col / base));
2296     }
2297 
2298     if ( numCols >= base )
2299         s += "\n" + leftIndent;
2300 
2301     // Draw the single digits.
2302     for ( int col = 1; col <= numCols; ++col )
2303         s += '0' + (col % base);
2304     s += "\n";
2305 
2306     // Draw horizontal divider.
2307     s += wxString(' ', colMargin) + '+' + wxString('-', numCols) + "\n";
2308 
2309     const int absEditCount = abs(m_edit.count);
2310     wxString action;
2311     action.Printf(" %s: %d",
2312                   m_edit.count < 0 ? "deletions" : "insertions",
2313                   absEditCount);
2314 
2315     // Will contain summary of grid (only multicells mentioned).
2316     wxString content;
2317 
2318     // Draw grid content.
2319     for ( int row = 0; row < numRows; ++row )
2320     {
2321         const wxString label = GetRowLabelValue(row);
2322         s += wxString(' ', colMargin - label.length());
2323         s += label + '|';
2324 
2325         for ( int col = 0; col < numCols; ++col )
2326         {
2327             char c = 'x';
2328             int rows, cols;
2329             switch ( GetCellSize(row, col, &rows, &cols) )
2330             {
2331             case wxGrid::CellSpan_None:
2332                 c = HasAttr(row, col) ? '*' : '.';
2333                 break;
2334 
2335             case wxGrid::CellSpan_Main:
2336                 c = 'M';
2337                 content += Multicell(row, col, rows, cols).ToString() + "\n";
2338                 break;
2339 
2340             case wxGrid::CellSpan_Inside:
2341                 // Check if the offset to main cell is correct.
2342                 c = (GetCellSize(row + rows, col + cols, &rows, &cols)
2343                         == wxGrid::CellSpan_Main) ? 'm' : '?';
2344                 break;
2345             }
2346 
2347             s += c;
2348         }
2349 
2350         // If applicable draw annotated row edits.
2351         if ( m_edit.count && m_edit.direction == wxGRID_ROW
2352              && row >= m_edit.pos && row < m_edit.pos + absEditCount)
2353         {
2354             s += (m_edit.count < 0 ? " ^" : " v");
2355 
2356             if ( row == m_edit.pos )
2357                 s += action;
2358         }
2359 
2360         s += "\n";
2361     }
2362 
2363     // Draw annotated footer if columns edited.
2364     if ( m_edit.count && m_edit.direction == wxGRID_COLUMN )
2365     {
2366         s += leftIndent;
2367 
2368         for ( int col = 0; col < m_edit.pos + absEditCount; ++col )
2369         {
2370             if ( col < m_edit.pos )
2371                 s += " ";
2372             else
2373                 s += (m_edit.count < 0 ? "<" : ">");
2374         }
2375 
2376         s += action + "\n";
2377     }
2378 
2379     s += "\n";
2380 
2381     if ( content.empty() )
2382         content = "Empty grid\n";
2383 
2384     return content + s;
2385 }
2386 
2387 //
2388 // GridAttrMatcher
2389 //
2390 
GridAttrMatcher(const TestableGrid & grid)2391 GridAttrMatcher::GridAttrMatcher(const TestableGrid& grid) : m_grid(&grid)
2392 {
2393    m_expectedGridDesc = m_grid->ToString();
2394 }
2395 
match(const TestableGrid & other) const2396 bool GridAttrMatcher::match(const TestableGrid& other) const
2397 {
2398     const int rows = wxMax(m_grid->GetNumberRows(), other.GetNumberRows());
2399     const int cols = wxMax(m_grid->GetNumberCols(), other.GetNumberCols());
2400 
2401     for ( int row = 0; row < rows; ++row )
2402     {
2403         for ( int col = 0; col < cols; ++col )
2404         {
2405             const bool hasAttr = m_grid->HasAttr(row, col);
2406             if ( hasAttr != other.HasAttr(row, col) )
2407             {
2408                 m_diffDesc.Printf("%s: attribute presence (%d, expected %d)",
2409                                   CellCoordsToString(row, col),
2410                                   !hasAttr, hasAttr);
2411 
2412                 return false;
2413             }
2414 
2415             int thisRows, thisCols;
2416             const wxGrid::CellSpan thisSpan
2417                 = m_grid->GetCellSize(row, col, &thisRows, &thisCols);
2418 
2419             int otherRows, otherCols;
2420             (void) other.GetCellSize(row, col, &otherRows, &otherCols);
2421 
2422             if ( thisRows != otherRows || thisCols != otherCols )
2423             {
2424                 wxString mismatchKind;
2425                 switch ( thisSpan )
2426                 {
2427                 case wxGrid::CellSpan_None:
2428                     mismatchKind = "different cell size";
2429                     break;
2430 
2431                 case wxGrid::CellSpan_Main:
2432                     mismatchKind = "different multicell size";
2433                     break;
2434 
2435                 case wxGrid::CellSpan_Inside:
2436                     mismatchKind = "main offset mismatch";
2437                     break;
2438                 }
2439 
2440                 m_diffDesc.Printf( "%s: %s (%s, expected %s)",
2441                                    CellCoordsToString(row, col),
2442                                    mismatchKind,
2443                                    CellSizeToString(otherRows, otherCols),
2444                                    CellSizeToString(thisRows, thisCols)
2445                                    );
2446 
2447                 return false;
2448             }
2449         }
2450     }
2451 
2452     return true;
2453 }
2454 
describe() const2455 std::string GridAttrMatcher::describe() const
2456 {
2457     std::string desc = (m_diffDesc.empty() ? "Matches" : "Doesn't match");
2458     desc += " expected content:\n" + m_expectedGridDesc.ToStdString();
2459 
2460     if ( !m_diffDesc.empty() )
2461         desc += + "first difference at " + m_diffDesc.ToStdString();
2462 
2463     return desc;
2464 }
2465 
2466 #endif //wxUSE_GRID
2467