Files
wxWidgets/tests/controls/auitest.cpp
Vadim Zeitlin e69ac228ea Add unit test for wxAuiNotebook (de)serialization
Verify that the serialized data has the expected form and that
deserializing it results in the expected results.

Also check that invalid data is handled gracefully.
2025-03-16 23:08:59 +01:00

361 lines
10 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Name: tests/controls/auitest.cpp
// Purpose: wxAui control tests
// Author: Sebastian Walderich
// Created: 2018-12-19
// Copyright: (c) 2018 Sebastian Walderich
///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#include "testprec.h"
#if wxUSE_AUI
#ifndef WX_PRECOMP
#include "wx/app.h"
#endif // WX_PRECOMP
#include "wx/panel.h"
#include "wx/aui/auibar.h"
#include "wx/aui/auibook.h"
#include "wx/aui/serializer.h"
#include "asserthelper.h"
#include <memory>
// ----------------------------------------------------------------------------
// test fixtures
// ----------------------------------------------------------------------------
class AuiNotebookTestCase
{
public:
AuiNotebookTestCase()
: nb(new wxAuiNotebook(wxTheApp->GetTopWindow()))
{
}
~AuiNotebookTestCase()
{
delete nb;
}
protected:
wxAuiNotebook* const nb;
};
// ----------------------------------------------------------------------------
// the tests themselves
// ----------------------------------------------------------------------------
TEST_CASE_METHOD(AuiNotebookTestCase, "wxAuiNotebook::DoGetBestSize", "[aui]")
{
wxPanel *p = new wxPanel(nb);
p->SetMinSize(wxSize(100, 100));
REQUIRE( nb->AddPage(p, "Center Pane") );
const int tabHeight = nb->GetTabCtrlHeight();
SECTION( "Single pane with multiple tabs" )
{
p = new wxPanel(nb);
p->SetMinSize(wxSize(300, 100));
nb->AddPage(p, "Center Tab 2");
p = new wxPanel(nb);
p->SetMinSize(wxSize(100, 200));
nb->AddPage(p, "Center Tab 3");
CHECK( nb->GetBestSize() == wxSize(300, 200 + tabHeight) );
}
SECTION( "Horizontal split" )
{
p = new wxPanel(nb);
p->SetMinSize(wxSize(25, 0));
nb->AddPage(p, "Left Pane");
nb->Split(nb->GetPageCount()-1, wxLEFT);
CHECK( nb->GetBestSize() == wxSize(125, 100 + tabHeight) );
p = new wxPanel(nb);
p->SetMinSize(wxSize(50, 0));
nb->AddPage(p, "Right Pane 1");
nb->Split(nb->GetPageCount()-1, wxRIGHT);
CHECK( nb->GetBestSize() == wxSize(175, 100 + tabHeight) );
p = new wxPanel(nb);
p->SetMinSize(wxSize(100, 0));
nb->AddPage(p, "Right Pane 2");
nb->Split(nb->GetPageCount()-1, wxRIGHT);
CHECK( nb->GetBestSize() == wxSize(275, 100 + tabHeight) );
}
SECTION( "Vertical split" )
{
p = new wxPanel(nb);
p->SetMinSize(wxSize(0, 100));
nb->AddPage(p, "Top Pane 1");
nb->Split(nb->GetPageCount()-1, wxTOP);
p = new wxPanel(nb);
p->SetMinSize(wxSize(0, 50));
nb->AddPage(p, "Top Pane 2");
nb->Split(nb->GetPageCount()-1, wxTOP);
CHECK( nb->GetBestSize() == wxSize(100, 250 + 3*tabHeight) );
p = new wxPanel(nb);
p->SetMinSize(wxSize(0, 25));
nb->AddPage(p, "Bottom Pane");
nb->Split(nb->GetPageCount()-1, wxBOTTOM);
CHECK( nb->GetBestSize() == wxSize(100, 275 + 4*tabHeight) );
}
SECTION( "Surrounding panes" )
{
p = new wxPanel(nb);
p->SetMinSize(wxSize(50, 25));
nb->AddPage(p, "Bottom Pane");
nb->Split(nb->GetPageCount()-1, wxBOTTOM);
p = new wxPanel(nb);
p->SetMinSize(wxSize(50, 120));
nb->AddPage(p, "Right Pane");
nb->Split(nb->GetPageCount()-1, wxRIGHT);
p = new wxPanel(nb);
p->SetMinSize(wxSize(225, 50));
nb->AddPage(p, "Top Pane");
nb->Split(nb->GetPageCount()-1, wxTOP);
p = new wxPanel(nb);
p->SetMinSize(wxSize(25, 105));
nb->AddPage(p, "Left Pane");
nb->Split(nb->GetPageCount()-1, wxLEFT);
CHECK( nb->GetBestSize() == wxSize(250, 175 + 3*tabHeight) );
}
}
TEST_CASE_METHOD(AuiNotebookTestCase, "wxAuiNotebook::RTTI", "[aui][rtti]")
{
wxBookCtrlBase* const book = nb;
CHECK( wxDynamicCast(book, wxAuiNotebook) == nb );
CHECK( wxDynamicCast(nb, wxBookCtrlBase) == book );
}
TEST_CASE_METHOD(AuiNotebookTestCase, "wxAuiNotebook::FindPage", "[aui]")
{
wxPanel *p1 = new wxPanel(nb);
wxPanel *p2 = new wxPanel(nb);
wxPanel *p3 = new wxPanel(nb);
REQUIRE( nb->AddPage(p1, "Page 1") );
REQUIRE( nb->AddPage(p2, "Page 2") );
CHECK( nb->FindPage(nullptr) == wxNOT_FOUND );
CHECK( nb->FindPage(p1) == 0 );
CHECK( nb->FindPage(p2) == 1 );
CHECK( nb->FindPage(p3) == wxNOT_FOUND );
}
TEST_CASE_METHOD(AuiNotebookTestCase, "wxAuiNotebook::Layout", "[aui]")
{
const auto addPage = [this](int n)
{
return nb->AddPage(new wxPanel(nb), wxString::Format("Page %d", n + 1));
};
for ( int n = 0; n < 5; n++ )
{
REQUIRE( addPage(n) );
}
using Ints = std::vector<int>;
using Indices = std::vector<size_t>;
// This serializer allows the code below to tweak its data before using it
// as deserializer.
class TestSerializer : public wxAuiBookSerializer,
public wxAuiBookDeserializer
{
public:
virtual void BeforeSaveNotebook(const wxString& name) override
{
m_name = name;
m_afterSaveCalled = false;
m_tabsLayoutInfo.clear();
}
virtual void
SaveNotebookTabControl(const wxAuiTabLayoutInfo& tab) override
{
m_tabsLayoutInfo.push_back(tab);
}
virtual void AfterSaveNotebook() override
{
m_afterSaveCalled = true;
}
virtual std::vector<wxAuiTabLayoutInfo>
LoadNotebookTabs(const wxString& name) override
{
CHECK( name == m_name );
m_orphanedPages.clear();
return m_tabsLayoutInfo;
}
virtual bool
HandleOrphanedPage(wxAuiNotebook& WXUNUSED(book),
int page,
wxAuiTabCtrl** WXUNUSED(tabCtrl),
int* tabIndex) override
{
m_orphanedPages.push_back(page);
*tabIndex = m_orphanedPageReturnIndex;
return m_orphanedPageReturnValue;
}
wxString m_name;
std::vector<wxAuiTabLayoutInfo> m_tabsLayoutInfo;
bool m_afterSaveCalled = false;
Ints m_orphanedPages;
bool m_orphanedPageReturnValue = true;
int m_orphanedPageReturnIndex = wxNOT_FOUND;
} ser;
// Just for convenience.
auto& info = ser.m_tabsLayoutInfo;
// Check the default layout has expected representation.
nb->SaveLayout("layout", ser);
CHECK( ser.m_name == "layout" );
CHECK( ser.m_afterSaveCalled );
REQUIRE( info.size() == 1 );
CHECK( info[0].pages == Ints{} );
CHECK( info[0].pinned == Ints{} );
CHECK( info[0].active == 0 );
// Check that the active page is restored correctly.
info[0].active = 1;
nb->LoadLayout("layout", ser);
CHECK( nb->GetSelection() == 1 );
// Check that page order is serialized as expected.
auto* mainTabCtrl = nb->GetMainTabCtrl();
REQUIRE( mainTabCtrl );
CHECK( mainTabCtrl->MovePage(1, 4) );
nb->SaveLayout("layout", ser);
REQUIRE( info.size() == 1 );
CHECK( info[0].pages == Ints{0, 2, 3, 4, 1} );
// Check that pinned pages are serialized as expected.
REQUIRE( nb->SetPageKind(2, wxAuiTabKind::Pinned) );
nb->SaveLayout("layout", ser);
REQUIRE( info.size() == 1 );
// Note that pinning a page moves it in front of all other pages.
CHECK( info[0].pages == Ints{2, 0, 3, 4, 1} );
CHECK( info[0].pinned == Ints{2} );
// Check a more complicated case with both locked and pinned pages.
REQUIRE( nb->SetPageKind(4, wxAuiTabKind::Locked) );
REQUIRE( nb->SetPageKind(3, wxAuiTabKind::Pinned) );
nb->SaveLayout("layout", ser);
REQUIRE( info.size() == 1 );
// Note that pinning a page moves it in front of all other pages.
CHECK( info[0].pages == Ints{4, 2, 3, 0, 1} );
CHECK( info[0].pinned == Ints{2, 3} );
// Check that restoring existing layout after adding some pages works.
addPage(5);
addPage(6);
nb->LoadLayout("layout", ser);
CHECK( ser.m_orphanedPages == Ints{5, 6} );
// By default, orphaned pages should have been appended.
CHECK( nb->GetPagesInDisplayOrder(mainTabCtrl) ==
Indices{4, 2, 3, 0, 1, 5, 6} );
// But we can change this by telling deserializer to insert them in front.
ser.m_orphanedPageReturnIndex = 0;
nb->LoadLayout("layout", ser);
CHECK( ser.m_orphanedPages == Ints{5, 6} );
CHECK( nb->GetPagesInDisplayOrder(mainTabCtrl) ==
Indices{6, 5, 4, 2, 3, 0, 1} );
// Or drop them entirely.
ser.m_orphanedPageReturnValue = false;
nb->LoadLayout("layout", ser);
CHECK( ser.m_orphanedPages == Ints{5, 6} );
CHECK( nb->GetPagesInDisplayOrder(mainTabCtrl) ==
Indices{4, 2, 3, 0, 1} );
// Finally, check that invalid data is handled gracefully.
info[0].active = 100;
info[0].pages = Ints{10, 0, 1, 2, 3, 4};
info[0].pinned = Ints{2, 99, 0};
nb->LoadLayout("layout", ser);
CHECK( ser.m_orphanedPages == Ints{} );
// Locked tab should have remained first.
CHECK( nb->GetPagesInDisplayOrder(mainTabCtrl) == Indices{4, 0, 1, 2, 3} );
// And selection should have been set to it because the specified value was
// invalid.
CHECK( nb->GetSelection() == 4 );
// And only tabs appearing before the normal ones can be pinned.
CHECK( nb->GetPageKind(0) == wxAuiTabKind::Pinned );
CHECK( nb->GetPageKind(1) == wxAuiTabKind::Normal );
CHECK( nb->GetPageKind(2) == wxAuiTabKind::Normal );
CHECK( nb->GetPageKind(3) == wxAuiTabKind::Normal );
CHECK( nb->GetPageKind(4) == wxAuiTabKind::Locked );
}
TEST_CASE("wxAuiToolBar::Items", "[aui][toolbar]")
{
std::unique_ptr<wxAuiToolBar> tbar{new wxAuiToolBar(wxTheApp->GetTopWindow())};
// Check that adding more toolbar elements doesn't invalidate the existing
// pointers.
auto first = tbar->AddLabel(wxID_ANY, "first");
tbar->AddLabel(wxID_ANY, "second");
CHECK( first->GetLabel() == "first" );
}
#endif