wxWidgets/tests/controls/dataviewctrltest.cpp

440 lines
12 KiB
C++
Raw Normal View History

///////////////////////////////////////////////////////////////////////////////
// Name: tests/controls/dataviewctrltest.cpp
// Purpose: wxDataViewCtrl unit test
// Author: Vaclav Slavik
// Created: 2011-08-08
// Copyright: (c) 2011 Vaclav Slavik <vslavik@gmail.com>
///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#include "testprec.h"
#if wxUSE_DATAVIEWCTRL
#include "wx/app.h"
#include "wx/dataview.h"
#include "wx/uiaction.h"
#ifdef __WXGTK__
#include "wx/stopwatch.h"
#endif // __WXGTK__
#include "testableframe.h"
#include "asserthelper.h"
// ----------------------------------------------------------------------------
// test class
// ----------------------------------------------------------------------------
class DataViewCtrlTestCase
{
public:
explicit DataViewCtrlTestCase(long style);
~DataViewCtrlTestCase();
protected:
void TestSelectionFor0and1();
// the dataview control itself
wxDataViewTreeCtrl *m_dvc;
// and some of its items
wxDataViewItem m_root,
m_child1,
m_child2,
m_grandchild;
wxDECLARE_NO_COPY_CLASS(DataViewCtrlTestCase);
};
class SingleSelectDataViewCtrlTestCase : public DataViewCtrlTestCase
{
public:
SingleSelectDataViewCtrlTestCase()
: DataViewCtrlTestCase(wxDV_SINGLE)
{
}
};
class MultiSelectDataViewCtrlTestCase : public DataViewCtrlTestCase
{
public:
MultiSelectDataViewCtrlTestCase()
: DataViewCtrlTestCase(wxDV_MULTIPLE)
{
}
};
class MultiColumnsDataViewCtrlTestCase
{
public:
MultiColumnsDataViewCtrlTestCase();
~MultiColumnsDataViewCtrlTestCase();
protected:
// the dataview control itself
wxDataViewListCtrl *m_dvc;
// constants
const wxSize m_size;
const int m_firstColumnWidth;
// and the columns
wxDataViewColumn* m_firstColumn;
wxDataViewColumn* m_lastColumn;
wxDECLARE_NO_COPY_CLASS(MultiColumnsDataViewCtrlTestCase);
};
// ----------------------------------------------------------------------------
// test initialization
// ----------------------------------------------------------------------------
DataViewCtrlTestCase::DataViewCtrlTestCase(long style)
{
m_dvc = new wxDataViewTreeCtrl(wxTheApp->GetTopWindow(),
wxID_ANY,
wxDefaultPosition,
wxSize(400, 200),
style);
m_root = m_dvc->AppendContainer(wxDataViewItem(), "The root");
m_child1 = m_dvc->AppendContainer(m_root, "child1");
m_grandchild = m_dvc->AppendItem(m_child1, "grandchild");
m_child2 = m_dvc->AppendItem(m_root, "child2");
m_dvc->Layout();
m_dvc->Expand(m_root);
m_dvc->Refresh();
m_dvc->Update();
}
DataViewCtrlTestCase::~DataViewCtrlTestCase()
{
delete m_dvc;
}
MultiColumnsDataViewCtrlTestCase::MultiColumnsDataViewCtrlTestCase()
: m_size(200, 100),
m_firstColumnWidth(50)
{
m_dvc = new wxDataViewListCtrl(wxTheApp->GetTopWindow(), wxID_ANY);
m_firstColumn =
m_dvc->AppendTextColumn(wxString(), wxDATAVIEW_CELL_INERT, m_firstColumnWidth);
m_lastColumn =
m_dvc->AppendTextColumn(wxString(), wxDATAVIEW_CELL_INERT);
// Set size after columns appending to extend size of the last column.
m_dvc->SetSize(m_size);
m_dvc->Layout();
m_dvc->Refresh();
m_dvc->Update();
}
MultiColumnsDataViewCtrlTestCase::~MultiColumnsDataViewCtrlTestCase()
{
delete m_dvc;
}
// ----------------------------------------------------------------------------
// the tests themselves
// ----------------------------------------------------------------------------
TEST_CASE_METHOD(MultiSelectDataViewCtrlTestCase,
"wxDVC::Selection",
"[wxDataViewCtrl][select]")
{
// Check selection round-trip.
wxDataViewItemArray sel;
sel.push_back(m_child1);
sel.push_back(m_grandchild);
REQUIRE_NOTHROW( m_dvc->SetSelections(sel) );
wxDataViewItemArray sel2;
CHECK( m_dvc->GetSelections(sel2) == static_cast<int>(sel.size()) );
CHECK( sel2 == sel );
// Invalid items in GetSelections() input are supposed to be just skipped.
sel.clear();
sel.push_back(wxDataViewItem());
REQUIRE_NOTHROW( m_dvc->SetSelections(sel) );
CHECK( m_dvc->GetSelections(sel2) == 0 );
CHECK( sel2.empty() );
}
TEST_CASE_METHOD(MultiSelectDataViewCtrlTestCase,
"wxDVC::DeleteSelected",
"[wxDataViewCtrl][delete]")
{
Skip wxDataViewCtrl unit test when using ASAN with wxGTK Somehow ASAN reports a memory leak in this test: Direct leak of 24 byte(s) in 1 object(s) allocated from: #0 0x7fc54732ebc8 in malloc (/lib/x86_64-linux-gnu/libasan.so.5+0x10dbc8) #1 0x7fc546685cb8 in g_malloc ../../../glib/gmem.c:102 #2 0x7fc543a49b41 in _gtk_rbtree_new ../../../../gtk/gtkrbtree.c:333 #3 0x7fc543b0831a in gtk_tree_view_real_expand_row ../../../../gtk/gtktreeview.c:12922 #4 0x7fc543b1087a in gtk_tree_view_expand_row ../../../../gtk/gtktreeview.c:12983 #5 0x7fc5453ba344 in wxDataViewCtrl::DoExpand(wxDataViewItem const&, bool) src/gtk/dataview.cpp:5013 #6 0x7fc5458e5a0e in wxDataViewCtrlBase::ExpandAncestors(wxDataViewItem const&) src/common/datavcmn.cpp:1297 #7 0x7fc5453bce08 in wxDataViewCtrl::SetSelections(wxDataViewItemArray const&) src/gtk/dataview.cpp:5228 #8 0x5557834c5745 in test controls/dataviewctrltest.cpp:178 #9 0x5557834d688c in invoke ../3rdparty/catch/include/internal/catch_test_registry.hpp:25 #10 0x5557831434ef in Catch::TestCase::invoke() const ../3rdparty/catch/include/internal/catch_test_case_info.hpp:176 #11 0x55578317f10f in Catch::RunContext::invokeActiveTestCase() (/home/runner/work/wxWidgets/wxWidgets/tests/test_gui+0x1bc10f) #12 0x55578317e9dd in Catch::RunContext::runCurrentTest(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) (/home/runner/work/wxWidgets/wxWidgets/tests/test_gui+0x1bb9dd) #13 0x55578317b5cc in Catch::RunContext::runTest(Catch::TestCase const&) (/home/runner/work/wxWidgets/wxWidgets/tests/test_gui+0x1b85cc) #14 0x55578313c64a in Catch::runTests(Catch::Ptr<Catch::Config> const&) ../3rdparty/catch/include/internal/../catch_session.hpp:82 #15 0x555783180e05 in Catch::Session::runInternal() (/home/runner/work/wxWidgets/wxWidgets/tests/test_gui+0x1bde05) #16 0x555783180931 in Catch::Session::run() (/home/runner/work/wxWidgets/wxWidgets/tests/test_gui+0x1bd931) #17 0x555783180899 in Catch::Session::run(int, char const* const*) (/home/runner/work/wxWidgets/wxWidgets/tests/test_gui+0x1bd899) #18 0x55578315719a in TestApp::RunTests() test.cpp:635 #19 0x5557831a63c7 in TestApp::OnIdle(wxIdleEvent&) (/home/runner/work/wxWidgets/wxWidgets/tests/test_gui+0x1e33c7) #20 0x7fc5446bbbfe in wxAppConsoleBase::HandleEvent(wxEvtHandler*, void (wxEvtHandler::*)(wxEvent&), wxEvent&) const src/common/appbase.cpp:654 #21 0x7fc5446bbdba in wxAppConsoleBase::CallEventHandler(wxEvtHandler*, wxEventFunctor&, wxEvent&) const src/common/appbase.cpp:666 #22 0x7fc5449b29dd in wxEvtHandler::ProcessEventIfMatchesId(wxEventTableEntryBase const&, wxEvtHandler*, wxEvent&) src/common/event.cpp:1416 #23 0x7fc5449b47f4 in wxEvtHandler::SearchDynamicEventTable(wxEvent&) src/common/event.cpp:1888 #24 0x7fc5449b32be in wxEvtHandler::TryHereOnly(wxEvent&) src/common/event.cpp:1609 #25 0x7fc5449b70a7 in wxEvtHandler::TryBeforeAndHere(wxEvent&) include/wx/event.h:3949 #26 0x7fc5449b2f72 in wxEvtHandler::ProcessEventLocally(wxEvent&) src/common/event.cpp:1546 #27 0x7fc5449b2ec5 in wxEvtHandler::ProcessEvent(wxEvent&) src/common/event.cpp:1519 #28 0x555783156ef7 in TestApp::ProcessEvent(wxEvent&) test.cpp:615 #29 0x7fc5446badef in wxAppConsoleBase::ProcessIdle() src/common/appbase.cpp:431 but only if another wxDataViewCtrl test runs after it, i.e. there is no leak when running just this test on its own. This doesn't seem to make sense, as wxDataViewCtrl, and hence GtkTreeView, is destroyed at the end of the test and it shouldn't be possible for a test running later to affect the presence or absence of a memory leak in it, so it might be a LeakSanitizer bug, but this doesn't help actually getting rid of it. The only other possibility would be to suppress this leak, but this doesn't seem ideal, as we could suppress some other real leak too, so for now just skip this test when using ASAN.
2021-01-31 10:30:05 -05:00
#ifdef __WXGTK__
wxString useASAN;
if ( wxGetEnv("wxUSE_ASAN", &useASAN) && useASAN == "1" )
{
WARN("Skipping test resulting in a memory leak report with wxGTK");
return;
}
#endif // __WXGTK__
wxDataViewItemArray sel;
sel.push_back(m_child1);
sel.push_back(m_grandchild);
sel.push_back(m_child2);
m_dvc->SetSelections(sel);
// delete a selected item
m_dvc->DeleteItem(m_child1);
m_dvc->GetSelections(sel);
// m_child1 and its children should be removed from the selection now
REQUIRE( sel.size() == 1 );
CHECK( sel[0] == m_child2 );
}
TEST_CASE_METHOD(MultiSelectDataViewCtrlTestCase,
"wxDVC::DeleteNotSelected",
"[wxDataViewCtrl][delete]")
{
// TODO not working on OS X as expected
#ifdef __WXOSX__
WARN("Disabled under MacOS because this test currently fails");
#else
wxDataViewItemArray sel;
sel.push_back(m_child1);
sel.push_back(m_grandchild);
m_dvc->SetSelections(sel);
// delete unselected item
m_dvc->DeleteItem(m_child2);
m_dvc->GetSelections(sel);
// m_child1 and its children should be unaffected
REQUIRE( sel.size() == 2 );
CHECK( sel[0] == m_child1 );
CHECK( sel[1] == m_grandchild );
#endif
}
void DataViewCtrlTestCase::TestSelectionFor0and1()
{
wxDataViewItemArray selections;
// Initially there is no selection.
CHECK( m_dvc->GetSelectedItemsCount() == 0 );
CHECK( !m_dvc->HasSelection() );
CHECK( !m_dvc->GetSelection().IsOk() );
CHECK( !m_dvc->GetSelections(selections) );
CHECK( selections.empty() );
// Select one item.
m_dvc->Select(m_child1);
CHECK( m_dvc->GetSelectedItemsCount() == 1 );
CHECK( m_dvc->HasSelection() );
CHECK( m_dvc->GetSelection().IsOk() );
REQUIRE( m_dvc->GetSelections(selections) == 1 );
CHECK( selections[0] == m_child1 );
}
TEST_CASE_METHOD(MultiSelectDataViewCtrlTestCase,
"wxDVC::GetSelectionForMulti",
"[wxDataViewCtrl][select]")
{
wxDataViewItemArray selections;
TestSelectionFor0and1();
m_dvc->Select(m_child2);
CHECK( m_dvc->GetSelectedItemsCount() == 2 );
CHECK( m_dvc->HasSelection() );
CHECK( !m_dvc->GetSelection().IsOk() );
REQUIRE( m_dvc->GetSelections(selections) == 2 );
CHECK( selections[1] == m_child2 );
}
TEST_CASE_METHOD(SingleSelectDataViewCtrlTestCase,
"wxDVC::SingleSelection",
"[wxDataViewCtrl][selection]")
{
TestSelectionFor0and1();
}
TEST_CASE_METHOD(SingleSelectDataViewCtrlTestCase,
"wxDVC::IsExpanded",
"[wxDataViewCtrl][expand]")
{
CHECK( m_dvc->IsExpanded(m_root) );
CHECK( !m_dvc->IsExpanded(m_child1) );
// No idea why, but the native NSOutlineView isItemExpanded: method returns
// true for this item for some reason.
#ifdef __WXOSX__
WARN("Disabled under MacOS: IsExpanded() returns true for grand child");
#else
CHECK( !m_dvc->IsExpanded(m_grandchild) );
#endif
CHECK( !m_dvc->IsExpanded(m_child2) );
m_dvc->Collapse(m_root);
CHECK( !m_dvc->IsExpanded(m_root) );
m_dvc->ExpandChildren(m_root);
CHECK( m_dvc->IsExpanded(m_root) );
CHECK( m_dvc->IsExpanded(m_child1) );
// Expanding an already expanded node must still expand all its children.
m_dvc->Collapse(m_child1);
CHECK( !m_dvc->IsExpanded(m_child1) );
m_dvc->ExpandChildren(m_root);
CHECK( m_dvc->IsExpanded(m_child1) );
}
TEST_CASE_METHOD(SingleSelectDataViewCtrlTestCase,
"wxDVC::GetItemRect",
"[wxDataViewCtrl][item]")
{
#ifdef __WXGTK__
// We need to let the native control have some events to lay itself out.
wxYield();
#endif // __WXGTK__
const wxRect rect1 = m_dvc->GetItemRect(m_child1);
const wxRect rect2 = m_dvc->GetItemRect(m_child2);
CHECK( rect1 != wxRect() );
CHECK( rect2 != wxRect() );
CHECK( rect1.x == rect2.x );
CHECK( rect1.width == rect2.width );
CHECK( rect1.height == rect2.height );
{
INFO("First child: " << rect1 << ", second one: " << rect2);
CHECK( rect1.y < rect2.y );
}
// This forces generic implementation to add m_grandchild to the tree, as
// it does it only on demand. We want the item to really be there to check
// that GetItemRect() returns an empty rectangle for collapsed items.
m_dvc->Expand(m_child1);
m_dvc->Collapse(m_child1);
const wxRect rectNotShown = m_dvc->GetItemRect(m_grandchild);
CHECK( rectNotShown == wxRect() );
// Append enough items to make the window scrollable.
for ( int i = 3; i < 100; ++i )
m_dvc->AppendItem(m_root, wxString::Format("child%d", i));
const wxDataViewItem last = m_dvc->AppendItem(m_root, "last");
// This should scroll the window to bring this item into view.
m_dvc->EnsureVisible(last);
#ifdef __WXGTK__
// Wait for the list control to be relaid out.
wxStopWatch sw;
while ( m_dvc->GetTopItem() == m_root )
{
if ( sw.Time() > 500 )
{
WARN("Timed out waiting for wxDataViewCtrl layout");
break;
}
wxYield();
}
#endif // __WXGTK__
// Check that this was indeed the case.
const wxDataViewItem top = m_dvc->GetTopItem();
CHECK( top != m_root );
// Verify that the coordinates are returned in physical coordinates of the
// window and not the logical coordinates affected by scrolling.
const wxRect rectScrolled = m_dvc->GetItemRect(top);
CHECK( rectScrolled.GetBottom() > 0 );
CHECK( rectScrolled.GetTop() <= m_dvc->GetClientSize().y );
// Also check that the root item is not currently visible (because it's
// scrolled off).
const wxRect rectRoot = m_dvc->GetItemRect(m_root);
CHECK( rectRoot == wxRect() );
}
TEST_CASE_METHOD(SingleSelectDataViewCtrlTestCase,
"wxDVC::DeleteAllItems",
"[wxDataViewCtrl][delete]")
{
// The invalid item corresponds to the root of tree store model, so it
// should have a single item (our m_root) initially.
CHECK( m_dvc->GetChildCount(wxDataViewItem()) == 1 );
m_dvc->DeleteAllItems();
// And none at all after deleting all the items.
CHECK( m_dvc->GetChildCount(wxDataViewItem()) == 0 );
}
TEST_CASE_METHOD(MultiColumnsDataViewCtrlTestCase,
"wxDVC::AppendTextColumn",
"[wxDataViewCtrl][column]")
{
#ifdef __WXGTK__
// Wait for the list control to be realized.
wxStopWatch sw;
while ( m_firstColumn->GetWidth() == 0 )
{
if ( sw.Time() > 500 )
{
WARN("Timed out waiting for wxDataViewListCtrl to be realized");
break;
}
wxYield();
}
#endif
// Check the width of the first column.
CHECK( m_firstColumn->GetWidth() == m_firstColumnWidth );
// Check that the last column was extended to fit client area.
const int lastColumnMaxWidth =
m_dvc->GetClientSize().GetWidth() - m_firstColumnWidth;
// In GTK and under Mac the width of the last column is less then
// a remaining client area.
const int lastColumnMinWidth = lastColumnMaxWidth - 10;
CHECK( m_lastColumn->GetWidth() <= lastColumnMaxWidth );
CHECK( m_lastColumn->GetWidth() >= lastColumnMinWidth );
}
#if wxUSE_UIACTIONSIMULATOR
TEST_CASE_METHOD(SingleSelectDataViewCtrlTestCase,
"wxDVC::KeyEvents",
"[wxDataViewCtrl][event]")
{
if ( !EnableUITests() )
return;
EventCounter keyEvents(m_dvc, wxEVT_KEY_DOWN);
m_dvc->SetFocus();
wxUIActionSimulator sim;
sim.Char(WXK_DOWN);
wxYield();
CHECK( keyEvents.GetCount() == 1 );
}
#endif // wxUSE_UIACTIONSIMULATOR
#endif //wxUSE_DATAVIEWCTRL