initial commit

Signed-off-by: Peter Siegmund <mars3142@noreply.mars3142.dev>
This commit is contained in:
2025-10-31 23:37:30 +01:00
commit 7228269764
9653 changed files with 4034514 additions and 0 deletions

View File

@@ -0,0 +1,68 @@
///////////////////////////////////////////////////////////////////////////////
// Name: tests/events/clone.cpp
// Purpose: Test wxEvent::Clone() implementation by all event classes
// Author: Vadim Zeitlin, based on the code by Francesco Montorsi
// Created: 2009-03-22
// Copyright: (c) 2009 Vadim Zeitlin <vadim@wxwidgets.org>
///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#include "testprec.h"
#ifndef WX_PRECOMP
#include "wx/event.h"
#include "wx/timer.h"
#endif // WX_PRECOMP
TEST_CASE("EventClone", "[wxEvent][clone]")
{
// Dummy timer needed just to create a wxTimerEvent.
wxTimer dummyTimer;
// check if event classes implement Clone() correctly
// NOTE: the check is done against _all_ event classes which are linked to
// the executable currently running, which are not necessarily all
// wxWidgets event classes.
const wxClassInfo *ci = wxClassInfo::GetFirst();
for (; ci; ci = ci->GetNext())
{
wxString cn = wxString(ci->GetClassName());
// is this class derived from wxEvent?
if ( !ci->IsKindOf(CLASSINFO(wxEvent)) ||
cn == "wxEvent" )
continue;
INFO("Event class \"" << cn << "\"");
wxEvent* test;
if ( ci->IsDynamic() )
{
test = wxDynamicCast(ci->CreateObject(),wxEvent);
}
else if ( cn == "wxTimerEvent" )
{
test = new wxTimerEvent(dummyTimer);
}
else
{
FAIL("Can't create objects of type " + cn);
continue;
}
REQUIRE( test );
wxEvent * const cloned = test->Clone();
delete test;
REQUIRE( cloned );
CHECK( cloned->GetClassInfo() == ci );
delete cloned;
}
}

View File

@@ -0,0 +1,156 @@
///////////////////////////////////////////////////////////////////////////////
// Name: tests/events/enterleave.cpp
// Purpose: Test wxEVT_ENTER_WINDOW and wxEVT_LEAVE_WINDOW events
// Author: Ali Kettab
// Created: 2024-10-16
// Copyright: (c) 2024 wxWidgets team
///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#include "testprec.h"
#ifndef WX_PRECOMP
#include "wx/app.h"
#include "wx/button.h"
#include "wx/panel.h"
#include "wx/textctrl.h"
#include "wx/window.h"
#endif // WX_PRECOMP
#include "wx/uiaction.h"
#include "asserthelper.h"
#include "testableframe.h"
#include "waitfor.h"
// ----------------------------------------------------------------------------
// tests themselves
// ----------------------------------------------------------------------------
#if wxUSE_UIACTIONSIMULATOR
TEST_CASE("EnterLeaveEvents", "[wxEvent][enter-leave]")
{
if ( !EnableUITests() )
{
WARN("Skipping wxEVT_{ENTER,LEAVE}_WINDOW tests: wxUIActionSimulator not available");
return;
}
std::unique_ptr<wxPanel>
panel(new wxPanel(wxTheApp->GetTopWindow(), wxID_ANY));
auto button = new wxButton(panel.get(), wxID_ANY, "button", {50, 50});
auto textctrl = new wxTextCtrl(panel.get(), wxID_ANY, "", {160, 50});
EventCounter enter(panel.get(), wxEVT_ENTER_WINDOW);
EventCounter leave(panel.get(), wxEVT_LEAVE_WINDOW);
// Wait for the first paint event to be sure that panel really
// has its final size.
WaitForPaint waitForPaint(panel.get());
panel->SendSizeEventToParent();
waitForPaint.YieldUntilPainted();
wxUIActionSimulator sim;
SECTION("Without mouse capture")
{
sim.MouseMove(panel->GetScreenPosition() + wxPoint(5, 5));
wxYield();
CHECK( enter.GetCount() == 1 );
CHECK( leave.GetCount() == 0 );
enter.Clear();
sim.MouseMove(button->GetScreenPosition() + wxPoint(5, 5));
wxYield();
// The parent window (panel) should receive wxEVT_LEAVE_WINDOW event
// when mouse enters the child window (button)
CHECK( enter.GetCount() == 0 );
CHECK( leave.GetCount() == 1 );
leave.Clear();
sim.MouseMove(panel->GetScreenPosition() + wxPoint(5, 5));
wxYield();
// Now it (panel) should receive wxEVT_ENTER_WINDOW event when
// the mouse leaves the button and enters the panel again.
CHECK( enter.GetCount() == 1 );
CHECK( leave.GetCount() == 0 );
}
SECTION("With (implicit) mouse capture")
{
// Just to be sure that the button is really shown
EventCounter clicked(button, wxEVT_BUTTON);
sim.MouseMove(button->GetScreenPosition() + wxPoint(5, 5));
wxYield();
sim.MouseClick();
wxYield();
CHECK( clicked.GetCount() == 1 );
enter.Clear();
leave.Clear();
sim.MouseDown();
wxYield();
#if defined(__WXGTK__) && !defined(__WXGTK3__)
if ( IsAutomaticTest() )
{
WARN("Skipping tests known to fail under GitHub Actions");
return;
}
#endif
sim.MouseMove(button->GetScreenPosition() + wxPoint(10, 5));
wxYield();
// Holding the mouse button down (initiated on the button) and then
// hovering over the panel should not generate any events (enter/leave)
// Additionally, entering and leaving another child (textctrl) while the
// mouse is still held down should also not generate any events.
sim.MouseMove(panel->GetScreenPosition() + wxPoint(5, 5));
wxYield();
CHECK( enter.GetCount() == 0 );
CHECK( leave.GetCount() == 0 );
sim.MouseMove(textctrl->GetScreenPosition() + wxPoint(5, 5));
wxYield();
CHECK( enter.GetCount() == 0 );
CHECK( leave.GetCount() == 0 );
sim.MouseMove(panel->GetScreenPosition() + wxPoint(5, 5));
wxYield();
CHECK( enter.GetCount() == 0 );
CHECK( leave.GetCount() == 0 );
sim.MouseUp();
wxYield();
// wxGTK behaves differently here, as it does not generate a
// wxEVT_ENTER_WINDOW event when we release the mouse button.
#ifndef __WXGTK__
CHECK( enter.GetCount() == 1 );
#else
CHECK( enter.GetCount() == 0 );
#endif
CHECK( leave.GetCount() == 0 );
}
}
#endif // wxUSE_UIACTIONSIMULATOR

View File

@@ -0,0 +1,536 @@
///////////////////////////////////////////////////////////////////////////////
// Name: tests/events/evthandler.cpp
// Purpose: Test the new event types and wxEvtHandler-methods
// Author: Peter Most
// Created: 2009-01-24
// Copyright: (c) 2009 Peter Most
///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#include "testprec.h"
#include "wx/event.h"
// ----------------------------------------------------------------------------
// test events and their handlers
// ----------------------------------------------------------------------------
const wxEventType LegacyEventType = wxNewEventType();
class MyEvent;
wxDEFINE_EVENT(MyEventType, MyEvent);
class MyEvent : public wxEvent
{
public:
MyEvent() : wxEvent(0, MyEventType) { }
virtual wxEvent *Clone() const override { return new MyEvent; }
};
typedef void (wxEvtHandler::*MyEventFunction)(MyEvent&);
#define EVT_MYEVENT(func) \
wx__DECLARE_EVT0(MyEventType, &func)
class AnotherEvent : public wxEvent
{
};
namespace
{
struct Called
{
Called() { Reset(); }
void Reset()
{
function =
functor =
method =
smethod = false;
}
bool function,
functor,
method,
smethod;
} g_called;
void GlobalOnMyEvent(MyEvent&)
{
g_called.function = true;
}
void GlobalOnEvent(wxEvent&)
{
g_called.function = true;
}
#ifdef TEST_INVALID_BIND_GLOBAL
void GlobalOnAnotherEvent(AnotherEvent&);
#endif
void GlobalOnIdle(wxIdleEvent&)
{
g_called.function = true;
}
struct MyFunctor
{
void operator()(MyEvent &) { g_called.functor = true; }
};
struct IdleFunctor
{
void operator()(wxIdleEvent &) { g_called.functor = true; }
};
class MyHandler : public wxEvtHandler
{
public:
static void StaticOnMyEvent(MyEvent &) { g_called.smethod = true; }
static void StaticOnAnotherEvent(AnotherEvent &);
static void StaticOnIdle(wxIdleEvent&) { g_called.smethod = true; }
void OnMyEvent(MyEvent&) { g_called.method = true; }
void OnEvent(wxEvent&) { g_called.method = true; }
void OnAnotherEvent(AnotherEvent&);
void OnIdle(wxIdleEvent&) { g_called.method = true; }
void OnOverloadedHandler(wxIdleEvent&) { }
void OnOverloadedHandler(wxThreadEvent&) { }
};
// we can also handle events in classes not deriving from wxEvtHandler
struct MySink
{
void OnMyEvent(MyEvent&) { g_called.method = true; }
void OnEvent(wxEvent&) { g_called.method = true; }
void OnIdle(wxIdleEvent&) { g_called.method = true; }
};
// also test event table compilation
class MyClassWithEventTable : public wxEvtHandler
{
public:
void OnMyEvent(MyEvent&) { g_called.method = true; }
void OnEvent(wxEvent&) { g_called.method = true; }
void OnAnotherEvent(AnotherEvent&);
void OnIdle(wxIdleEvent&) { g_called.method = true; }
#ifdef wxHAS_NOEXCEPT
void OnIdleNoExcept(wxIdleEvent&) noexcept { }
#endif
private:
wxDECLARE_EVENT_TABLE();
};
// Avoid gcc warning about some of the functions defined by the expansion of
// the event table macros being unused: they are indeed unused, but we still
// want to have them to check that they compile.
wxGCC_WARNING_SUPPRESS(unused-function)
wxBEGIN_EVENT_TABLE(MyClassWithEventTable, wxEvtHandler)
EVT_IDLE(MyClassWithEventTable::OnIdle)
EVT_MYEVENT(MyClassWithEventTable::OnMyEvent)
EVT_MYEVENT(MyClassWithEventTable::OnEvent)
#ifdef wxHAS_NOEXCEPT
EVT_IDLE(MyClassWithEventTable::OnIdleNoExcept)
#endif
// this shouldn't compile:
//EVT_MYEVENT(MyClassWithEventTable::OnIdle)
//EVT_IDLE(MyClassWithEventTable::OnAnotherEvent)
wxEND_EVENT_TABLE()
wxGCC_WARNING_RESTORE(unused-function)
} // anonymous namespace
TEST_CASE("Event::BuiltinConnect", "[event][connect]")
{
MyHandler handler;
handler.Connect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle));
handler.Disconnect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle));
handler.Connect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle), nullptr, &handler);
handler.Disconnect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle), nullptr, &handler);
// using casts like this is even uglier than using wxIdleEventHandler and
// results in warnings with gcc, but it should still continue to work for
// compatibility
wxGCC_WARNING_SUPPRESS_CAST_FUNCTION_TYPE()
handler.Connect(wxEVT_IDLE, (wxObjectEventFunction)(wxEventFunction)&MyHandler::OnIdle);
handler.Disconnect(wxEVT_IDLE, (wxObjectEventFunction)(wxEventFunction)&MyHandler::OnIdle);
wxGCC_WARNING_RESTORE_CAST_FUNCTION_TYPE()
handler.Bind(wxEVT_IDLE, GlobalOnIdle);
handler.Unbind(wxEVT_IDLE, GlobalOnIdle);
IdleFunctor f;
handler.Bind(wxEVT_IDLE, f);
handler.Unbind(wxEVT_IDLE, f);
handler.Bind(wxEVT_IDLE, &MyHandler::OnIdle, &handler);
handler.Unbind(wxEVT_IDLE, &MyHandler::OnIdle, &handler);
handler.Bind(wxEVT_IDLE, &MyHandler::StaticOnIdle);
handler.Unbind(wxEVT_IDLE, &MyHandler::StaticOnIdle);
}
TEST_CASE("Event::LegacyConnect", "[event][connect]")
{
MyHandler handler;
handler.Connect( LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent );
handler.Connect( 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent );
handler.Connect( 0, 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent );
handler.Disconnect( LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent );
handler.Disconnect( 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent );
handler.Disconnect( 0, 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent );
handler.Connect( LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, nullptr, &handler );
handler.Connect( 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, nullptr, &handler );
handler.Connect( 0, 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, nullptr, &handler );
handler.Disconnect( LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, nullptr, &handler );
handler.Disconnect( 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, nullptr, &handler );
handler.Disconnect( 0, 0, LegacyEventType, (wxObjectEventFunction)&MyHandler::OnEvent, nullptr, &handler );
}
TEST_CASE("Event::ConnectOverloaded", "[event][connect]")
{
MyHandler handler;
handler.Connect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnOverloadedHandler));
handler.Connect(wxEVT_THREAD, wxThreadEventHandler(MyHandler::OnOverloadedHandler));
}
TEST_CASE("Event::DisconnectWildcard", "[event][connect][disconnect]")
{
MyHandler handler;
// should be able to disconnect a different handler using "wildcard search"
MyHandler sink;
wxEvtHandler source;
source.Connect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle), nullptr, &sink);
CHECK(source.Disconnect(wxID_ANY, wxEVT_IDLE));
// destruction of source and sink here should properly clean up the
// wxEventConnectionRef without crashing
}
TEST_CASE("Event::AutoDisconnect", "[event][connect][disconnect]")
{
wxEvtHandler source;
{
MyHandler sink;
source.Connect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle), nullptr, &sink);
// mismatched event type, so nothing should be disconnected
CHECK(!source.Disconnect(wxEVT_THREAD, wxIdleEventHandler(MyHandler::OnIdle), nullptr, &sink));
}
// destruction of sink should have automatically disconnected it, so
// there should be nothing to disconnect anymore
CHECK(!source.Disconnect(wxID_ANY, wxEVT_IDLE));
}
TEST_CASE("Event::BindFunction", "[event][bind]")
{
MyHandler handler;
MyEvent e;
// function tests
handler.Bind( MyEventType, GlobalOnMyEvent );
g_called.Reset();
handler.ProcessEvent(e);
CHECK( g_called.function );
handler.Unbind( MyEventType, GlobalOnMyEvent );
g_called.Reset();
handler.ProcessEvent(e);
CHECK( !g_called.function ); // check that it was disconnected
handler.Bind( MyEventType, GlobalOnMyEvent, 0 );
handler.Unbind( MyEventType, GlobalOnMyEvent, 0 );
handler.Bind( MyEventType, GlobalOnMyEvent, 0, 0 );
handler.Unbind( MyEventType, GlobalOnMyEvent, 0, 0 );
}
TEST_CASE("Event::BindStaticMethod", "[event][bind]")
{
MyHandler handler;
MyEvent e;
// static method tests (this is same as functions but still test it just in
// case we hit some strange compiler bugs)
handler.Bind( MyEventType, &MyHandler::StaticOnMyEvent );
g_called.Reset();
handler.ProcessEvent(e);
CHECK( g_called.smethod );
handler.Unbind( MyEventType, &MyHandler::StaticOnMyEvent );
g_called.Reset();
handler.ProcessEvent(e);
CHECK( !g_called.smethod );
handler.Bind( MyEventType, &MyHandler::StaticOnMyEvent, 0 );
handler.Unbind( MyEventType, &MyHandler::StaticOnMyEvent, 0 );
handler.Bind( MyEventType, &MyHandler::StaticOnMyEvent, 0, 0 );
handler.Unbind( MyEventType, &MyHandler::StaticOnMyEvent, 0, 0 );
}
TEST_CASE("Event::BindFunctor", "[event][bind]")
{
MyHandler handler;
MyEvent e;
// generalized functor tests
MyFunctor functor;
handler.Bind( MyEventType, functor );
g_called.Reset();
handler.ProcessEvent(e);
CHECK( g_called.functor );
handler.Unbind( MyEventType, functor );
g_called.Reset();
handler.ProcessEvent(e);
CHECK( !g_called.functor );
handler.Bind( MyEventType, functor, 0 );
handler.Unbind( MyEventType, functor, 0 );
handler.Bind( MyEventType, functor, 0, 0 );
handler.Unbind( MyEventType, functor, 0, 0 );
// test that a temporary functor is working as well and also test that
// unbinding a different (though equal) instance of the same functor does
// not work
MyFunctor func;
handler.Bind( MyEventType, MyFunctor() );
CHECK( !handler.Unbind( MyEventType, func ));
handler.Bind( MyEventType, MyFunctor(), 0 );
CHECK( !handler.Unbind( MyEventType, func, 0 ));
handler.Bind( MyEventType, MyFunctor(), 0, 0 );
CHECK( !handler.Unbind( MyEventType, func, 0, 0 ));
}
TEST_CASE("Event::BindMethod", "[event][bind]")
{
MyHandler handler;
MyEvent e;
// class method tests
handler.Bind( MyEventType, &MyHandler::OnMyEvent, &handler );
g_called.Reset();
handler.ProcessEvent(e);
CHECK( g_called.method );
handler.Unbind( MyEventType, &MyHandler::OnMyEvent, &handler );
g_called.Reset();
handler.ProcessEvent(e);
CHECK( !g_called.method );
handler.Bind( MyEventType, &MyHandler::OnMyEvent, &handler, 0 );
handler.Unbind( MyEventType, &MyHandler::OnMyEvent, &handler, 0 );
handler.Bind( MyEventType, &MyHandler::OnMyEvent, &handler, 0, 0 );
handler.Unbind( MyEventType, &MyHandler::OnMyEvent, &handler, 0, 0 );
}
TEST_CASE("Event::BindMethodUsingBaseEvent", "[event][bind]")
{
MyHandler handler;
MyEvent e;
// test connecting a method taking just wxEvent and not MyEvent: this
// should work too if we don't need any MyEvent-specific information in the
// handler
handler.Bind( MyEventType, &MyHandler::OnEvent, &handler );
g_called.Reset();
handler.ProcessEvent(e);
CHECK( g_called.method );
handler.Unbind( MyEventType, &MyHandler::OnEvent, &handler );
g_called.Reset();
handler.ProcessEvent(e);
CHECK( !g_called.method );
handler.Bind( MyEventType, &MyHandler::OnEvent, &handler, 0 );
handler.Unbind( MyEventType, &MyHandler::OnEvent, &handler, 0 );
handler.Bind( MyEventType, &MyHandler::OnEvent, &handler, 0, 0 );
handler.Unbind( MyEventType, &MyHandler::OnEvent, &handler, 0, 0 );
}
TEST_CASE("Event::BindFunctionUsingBaseEvent", "[event][bind]")
{
MyHandler handler;
MyEvent e;
// test connecting a function taking just wxEvent and not MyEvent: this
// should work too if we don't need any MyEvent-specific information in the
// handler
handler.Bind( MyEventType, GlobalOnEvent );
g_called.Reset();
handler.ProcessEvent(e);
CHECK( g_called.function );
handler.Unbind( MyEventType, GlobalOnEvent );
g_called.Reset();
handler.ProcessEvent(e);
CHECK( !g_called.function );
handler.Bind( MyEventType, GlobalOnEvent, 0 );
handler.Unbind( MyEventType, GlobalOnEvent, 0 );
handler.Bind( MyEventType, GlobalOnEvent, 0, 0 );
handler.Unbind( MyEventType, GlobalOnEvent, 0, 0 );
}
TEST_CASE("Event::BindNonHandler", "[event][bind]")
{
MyHandler handler;
MyEvent e;
// class method tests for class not derived from wxEvtHandler
MySink sink;
handler.Bind( MyEventType, &MySink::OnMyEvent, &sink );
g_called.Reset();
handler.ProcessEvent(e);
CHECK( g_called.method );
handler.Unbind( MyEventType, &MySink::OnMyEvent, &sink );
g_called.Reset();
handler.ProcessEvent(e);
CHECK( !g_called.method );
}
TEST_CASE("Event::InvalidBind", "[event][bind]")
{
// these calls shouldn't compile but we unfortunately can't check this
// automatically, you need to uncomment them manually and test that
// compilation does indeed fail
// connecting a handler with incompatible signature shouldn't work
#ifdef TEST_INVALID_BIND_GLOBAL
handler.Bind(MyEventType, GlobalOnAnotherEvent);
#endif
#ifdef TEST_INVALID_BIND_STATIC
handler.Bind(MyEventType, &MyHandler::StaticOnAnotherEvent);
#endif
#ifdef TEST_INVALID_BIND_METHOD
handler.Bind(MyEventType, &MyHandler::OnAnotherEvent, &handler);
#endif
#ifdef TEST_INVALID_BIND_FUNCTOR
IdleFunctor f;
handler.Bind(MyEventType, f);
#endif
// the handler can't be omitted when calling Bind()
#ifdef TEST_INVALID_BIND_NO_HANDLER
handler.Bind(MyEventType, &MyHandler::OnMyEvent);
#endif
// calling a derived class method with a base class pointer must not work
#ifdef TEST_INVALID_BIND_DERIVED
struct C1 : wxEvtHandler { };
struct C2 : wxEvtHandler { void OnWhatever(wxEvent&); };
C1 c1;
c1.Bind(&C2::OnWhatever);
#endif
// using object pointer incompatible with the method must not work
#ifdef TEST_INVALID_BIND_WRONG_CLASS
MySink mySink;
MyHandler myHandler;
myHandler.Bind(MyEventType, &MyHandler::OnMyEvent, &mySink);
#endif
}
// Helpers for UnbindFromHandler() test, have to be declared outside of the
// function in C++98.
struct Handler1
{
void OnDontCall(MyEvent&)
{
// Although this handler is bound, the second one below is bound
// later and so will be called first and will disconnect this one
// before it has a chance to be called.
FAIL("shouldn't be called");
}
};
class Handler2
{
public:
Handler2(MyHandler& handler, Handler1& h1)
: m_handler(handler),
m_h1(h1)
{
}
void OnUnbind(MyEvent& e)
{
m_handler.Unbind(MyEventType, &Handler1::OnDontCall, &m_h1);
// Check that the now disconnected first handler is not executed.
e.Skip();
}
private:
MyHandler& m_handler;
Handler1& m_h1;
wxDECLARE_NO_COPY_CLASS(Handler2);
};
TEST_CASE("Event::UnbindFromHandler", "[event][bind][unbind]")
{
MyHandler handler;
MyEvent e;
Handler1 h1;
handler.Bind(MyEventType, &Handler1::OnDontCall, &h1);
Handler2 h2(handler, h1);
handler.Bind(MyEventType, &Handler2::OnUnbind, &h2);
handler.ProcessEvent(e);
}
// This is a compilation-time-only test: just check that a class inheriting
// from wxEvtHandler non-publicly can use Bind() with its method, this used to
// result in compilation errors.
class HandlerNonPublic : protected wxEvtHandler
{
public:
HandlerNonPublic()
{
Bind(wxEVT_IDLE, &HandlerNonPublic::OnIdle, this);
}
void OnIdle(wxIdleEvent&) { }
};
// Another compilation-time-only test, but this one checking that these event
// objects can't be created from outside of the library.
#ifdef TEST_INVALID_EVENT_CREATION
void TestEventCreation()
{
wxPaintEvent eventPaint;
}
#endif // TEST_INVALID_EVENT_CREATION

View File

@@ -0,0 +1,124 @@
///////////////////////////////////////////////////////////////////////////////
// Name: tests/events/evtloop.cpp
// Purpose: Tests for the event loop classes
// Author: Rob Bresalier
// Created: 2013-05-02
// Copyright: (c) 2013 Rob Bresalier
///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#include "testprec.h"
#include "wx/timer.h"
// ----------------------------------------------------------------------------
// constants
// ----------------------------------------------------------------------------
// Use two arbitrary but different return codes for the two loops.
const int EXIT_CODE_OUTER_LOOP = 99;
const int EXIT_CODE_INNER_LOOP = 55;
// ----------------------------------------------------------------------------
// test class
// ----------------------------------------------------------------------------
class EvtloopTestCase : public CppUnit::TestCase
{
public:
EvtloopTestCase() { }
private:
CPPUNIT_TEST_SUITE( EvtloopTestCase );
CPPUNIT_TEST( TestExit );
CPPUNIT_TEST_SUITE_END();
void TestExit();
wxDECLARE_NO_COPY_CLASS(EvtloopTestCase);
};
// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION( EvtloopTestCase );
// also include in its own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( EvtloopTestCase, "EvtloopTestCase" );
// Helper class to schedule exit of the given event loop after the specified
// delay.
class ScheduleLoopExitTimer : public wxTimer
{
public:
ScheduleLoopExitTimer(wxEventLoop& loop, int rc)
: m_loop(loop),
m_rc(rc)
{
}
virtual void Notify() override
{
m_loop.ScheduleExit(m_rc);
}
private:
wxEventLoop& m_loop;
const int m_rc;
};
// Another helper which runs a nested loop and schedules exiting both the outer
// and the inner loop after the specified delays.
class RunNestedAndExitBothLoopsTimer : public wxTimer
{
public:
RunNestedAndExitBothLoopsTimer(wxTimer& timerOuter,
int loopOuterDuration,
int loopInnerDuration)
: m_timerOuter(timerOuter),
m_loopOuterDuration(loopOuterDuration),
m_loopInnerDuration(loopInnerDuration)
{
}
virtual void Notify() override
{
wxEventLoop loopInner;
ScheduleLoopExitTimer timerInner(loopInner, EXIT_CODE_INNER_LOOP);
m_timerOuter.StartOnce(m_loopOuterDuration);
timerInner.StartOnce(m_loopInnerDuration);
CPPUNIT_ASSERT_EQUAL( EXIT_CODE_INNER_LOOP, loopInner.Run() );
}
private:
wxTimer& m_timerOuter;
const int m_loopOuterDuration;
const int m_loopInnerDuration;
};
void EvtloopTestCase::TestExit()
{
// Test that simply exiting the loop works.
wxEventLoop loopOuter;
ScheduleLoopExitTimer timerExit(loopOuter, EXIT_CODE_OUTER_LOOP);
timerExit.StartOnce(1);
CPPUNIT_ASSERT_EQUAL( EXIT_CODE_OUTER_LOOP, loopOuter.Run() );
// Test that exiting the outer loop before the inner loop (outer duration
// parameter less than inner duration in the timer ctor below) works.
ScheduleLoopExitTimer timerExitOuter(loopOuter, EXIT_CODE_OUTER_LOOP);
RunNestedAndExitBothLoopsTimer timerRun(timerExitOuter, 5, 10);
timerRun.StartOnce(1);
CPPUNIT_ASSERT_EQUAL( EXIT_CODE_OUTER_LOOP, loopOuter.Run() );
// Test that exiting the inner loop before the outer one works too.
ScheduleLoopExitTimer timerExitOuter2(loopOuter, EXIT_CODE_OUTER_LOOP);
RunNestedAndExitBothLoopsTimer timerRun2(timerExitOuter, 10, 5);
timerRun2.StartOnce(1);
CPPUNIT_ASSERT_EQUAL( EXIT_CODE_OUTER_LOOP, loopOuter.Run() );
}

View File

@@ -0,0 +1,14 @@
///////////////////////////////////////////////////////////////////////////////
// Name: tests/events/evtsource.cpp
// Purpose: Test the event sources
// Author: Bartosz Bekier
// Created: 2009-01-24
// Copyright: (c) 2009 Bartosz Bekier <bartosz.bekier@gmail.com>
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#include "testprec.h"

View File

@@ -0,0 +1,376 @@
///////////////////////////////////////////////////////////////////////////////
// Name: tests/events/keyboard.cpp
// Purpose: Test keyboard events
// Author: Vadim Zeitlin
// Created: 2010-09-05
// Copyright: (c) 2010 Vadim Zeitlin <vadim@wxwidgets.org>
///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#include "testprec.h"
#if wxUSE_UIACTIONSIMULATOR
#ifndef WX_PRECOMP
#include "wx/app.h"
#include "wx/event.h"
#include "wx/window.h"
#endif // WX_PRECOMP
#include "wx/uiaction.h"
#include "wx/vector.h"
#include "waitfor.h"
namespace
{
// ----------------------------------------------------------------------------
// test window verifying the event generation
// ----------------------------------------------------------------------------
class KeyboardTestWindow : public wxWindow
{
public:
KeyboardTestWindow(wxWindow *parent)
: wxWindow(parent, wxID_ANY, wxPoint(0, 0), parent->GetClientSize())
{
Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(KeyboardTestWindow::OnKeyDown));
Connect(wxEVT_CHAR, wxKeyEventHandler(KeyboardTestWindow::OnChar));
Connect(wxEVT_KEY_UP, wxKeyEventHandler(KeyboardTestWindow::OnKeyUp));
}
unsigned GetKeyDownCount() const { return m_keyDownEvents.size(); }
unsigned GetCharCount() const { return m_charEvents.size(); }
unsigned GetKeyUpCount() const { return m_keyUpEvents.size(); }
const wxKeyEvent& GetKeyDownEvent(unsigned n = 0) const
{
return m_keyDownEvents[n];
}
const wxKeyEvent& GetCharEvent(unsigned n = 0) const
{
return m_charEvents[n];
}
const wxKeyEvent& GetKeyUpEvent(unsigned n = 0) const
{
return m_keyUpEvents[n];
}
void ClearEvents()
{
m_keyDownEvents =
m_charEvents =
m_keyUpEvents = wxVector<wxKeyEvent>();
}
private:
void OnKeyDown(wxKeyEvent& event)
{
m_keyDownEvents.push_back(event);
event.Skip();
}
void OnChar(wxKeyEvent& event)
{
m_charEvents.push_back(event);
event.Skip();
}
void OnKeyUp(wxKeyEvent& event)
{
m_keyUpEvents.push_back(event);
event.Skip();
}
wxVector<wxKeyEvent> m_keyDownEvents,
m_charEvents,
m_keyUpEvents;
wxDECLARE_NO_COPY_CLASS(KeyboardTestWindow);
};
// Object describing the (main fields of) keyboard event.
struct KeyDesc
{
KeyDesc(int keycode, int mods = 0)
: m_keycode(keycode),
m_mods(mods)
{
}
int m_keycode;
int m_mods;
};
// Helper for ModKeyDown().
int GetModForKey(int keycode)
{
switch ( keycode )
{
case WXK_CONTROL: return wxMOD_CONTROL;
case WXK_SHIFT: return wxMOD_SHIFT;
case WXK_ALT: return wxMOD_ALT;
default:
wxFAIL_MSG( "Unknown modifier key" );
}
return wxMOD_NONE;
}
// Helper function to allow writing just ModKeyDown(WXK_CONTROL) instead of
// more verbose KeyDesc(WXK_CONTROL, wxMOD_CONTROL).
KeyDesc ModKeyDown(int keycode)
{
return KeyDesc(keycode, GetModForKey(keycode));
}
// Another helper provided for symmetry with ModKeyDown() only.
KeyDesc ModKeyUp(int keycode)
{
return KeyDesc(keycode);
}
// Verify that the event object corresponds to our idea of what it should be.
void TestEvent(int line, const wxKeyEvent& ev, const KeyDesc& desc)
{
// Construct the message we'll display if an assert fails.
std::string msg;
const wxEventType t = ev.GetEventType();
if ( t == wxEVT_KEY_DOWN )
msg = "key down";
else if ( t == wxEVT_CHAR )
msg = "char";
else if ( t == wxEVT_KEY_UP )
msg = "key up";
else
CPPUNIT_FAIL( "unknown event type" );
msg += " event at line ";
msg += wxString::Format("%d", line).mb_str();
CPPUNIT_ASSERT_EQUAL_MESSAGE( "wrong key code in " + msg,
desc.m_keycode,
ev.GetKeyCode() );
if ( desc.m_keycode < WXK_START )
{
// For Latin-1 our key code is the same as Unicode character value.
CPPUNIT_ASSERT_EQUAL_MESSAGE( "wrong Unicode key in " + msg,
(char)desc.m_keycode,
(char)ev.GetUnicodeKey() );
}
else // Special key
{
// Key codes above WXK_START don't correspond to printable characters.
CPPUNIT_ASSERT_EQUAL_MESSAGE( "wrong non-zero Unicode key in " + msg,
0,
(int)ev.GetUnicodeKey() );
}
CPPUNIT_ASSERT_EQUAL_MESSAGE( "wrong modifiers in " + msg,
desc.m_mods,
ev.GetModifiers() );
}
// Call TestEvent() passing it the line number from where it was called: this
// is useful for interpreting the assert failure messages.
#define ASSERT_KEY_EVENT_IS( ev, desc ) TestEvent(__LINE__, ev, desc)
} // anonymous namespace
// --------------------------------------------------------------------------
// test class
// --------------------------------------------------------------------------
class KeyboardEventTestCase : public CppUnit::TestCase
{
public:
KeyboardEventTestCase() {}
virtual void setUp() override;
virtual void tearDown() override;
private:
CPPUNIT_TEST_SUITE( KeyboardEventTestCase );
WXUISIM_TEST( NormalLetter );
WXUISIM_TEST( NormalSpecial );
WXUISIM_TEST( CtrlLetter );
WXUISIM_TEST( CtrlSpecial );
WXUISIM_TEST( ShiftLetter );
WXUISIM_TEST( ShiftSpecial );
CPPUNIT_TEST_SUITE_END();
void NormalLetter();
void NormalSpecial();
void CtrlLetter();
void CtrlSpecial();
void ShiftLetter();
void ShiftSpecial();
KeyboardTestWindow *m_win;
wxDECLARE_NO_COPY_CLASS(KeyboardEventTestCase);
};
wxREGISTER_UNIT_TEST(KeyboardEvent);
void KeyboardEventTestCase::setUp()
{
m_win = new KeyboardTestWindow(wxTheApp->GetTopWindow());
wxYield();
m_win->SetFocus();
YieldForAWhile(10); // needed to show the new window
// The window might get some key up events when it's being shown if the key
// was pressed when the program was started and released after the window
// was shown, e.g. this does happen in practice when launching the test
// from command line. Simply discard all the spurious events so far.
m_win->ClearEvents();
}
void KeyboardEventTestCase::tearDown()
{
m_win->Destroy();
}
void KeyboardEventTestCase::NormalLetter()
{
#ifdef __WXQT__
WARN("FIXME! doesn't work like the other ports.");
#else
wxUIActionSimulator sim;
sim.Char('a');
wxYield();
CPPUNIT_ASSERT_EQUAL( 1, m_win->GetKeyDownCount() );
ASSERT_KEY_EVENT_IS( m_win->GetKeyDownEvent(), 'A' );
CPPUNIT_ASSERT_EQUAL( 1, m_win->GetCharCount() );
ASSERT_KEY_EVENT_IS( m_win->GetCharEvent(), 'a' );
CPPUNIT_ASSERT_EQUAL( 1, m_win->GetKeyUpCount() );
ASSERT_KEY_EVENT_IS( m_win->GetKeyUpEvent(), 'A' );
#endif
}
void KeyboardEventTestCase::NormalSpecial()
{
wxUIActionSimulator sim;
sim.Char(WXK_END);
wxYield();
CPPUNIT_ASSERT_EQUAL( 1, m_win->GetKeyDownCount() );
ASSERT_KEY_EVENT_IS( m_win->GetKeyDownEvent(), WXK_END );
CPPUNIT_ASSERT_EQUAL( 1, m_win->GetCharCount() );
ASSERT_KEY_EVENT_IS( m_win->GetCharEvent(), WXK_END );
CPPUNIT_ASSERT_EQUAL( 1, m_win->GetKeyUpCount() );
ASSERT_KEY_EVENT_IS( m_win->GetKeyUpEvent(), WXK_END );
}
void KeyboardEventTestCase::CtrlLetter()
{
#ifdef __WXQT__
WARN("FIXME! doesn't work like the other ports.");
#else
wxUIActionSimulator sim;
sim.Char('z', wxMOD_CONTROL);
wxYield();
CPPUNIT_ASSERT_EQUAL( 2, m_win->GetKeyDownCount() );
ASSERT_KEY_EVENT_IS( m_win->GetKeyDownEvent(0),
ModKeyDown(WXK_CONTROL) );
ASSERT_KEY_EVENT_IS( m_win->GetKeyDownEvent(1),
KeyDesc('Z', wxMOD_CONTROL) );
CPPUNIT_ASSERT_EQUAL( 1, m_win->GetCharCount() );
ASSERT_KEY_EVENT_IS( m_win->GetCharEvent(),
KeyDesc('\x1a', wxMOD_CONTROL) );
CPPUNIT_ASSERT_EQUAL( 2, m_win->GetKeyUpCount() );
ASSERT_KEY_EVENT_IS( m_win->GetKeyUpEvent(0),
KeyDesc('Z', wxMOD_CONTROL) );
ASSERT_KEY_EVENT_IS( m_win->GetKeyUpEvent(1),
ModKeyUp(WXK_CONTROL) );
#endif
}
void KeyboardEventTestCase::CtrlSpecial()
{
wxUIActionSimulator sim;
sim.Char(WXK_PAGEUP, wxMOD_CONTROL);
wxYield();
CPPUNIT_ASSERT_EQUAL( 2, m_win->GetKeyDownCount() );
ASSERT_KEY_EVENT_IS( m_win->GetKeyDownEvent(0),
ModKeyDown(WXK_CONTROL) );
ASSERT_KEY_EVENT_IS( m_win->GetKeyDownEvent(1),
KeyDesc(WXK_PAGEUP, wxMOD_CONTROL) );
CPPUNIT_ASSERT_EQUAL( 1, m_win->GetCharCount() );
ASSERT_KEY_EVENT_IS( m_win->GetCharEvent(),
KeyDesc(WXK_PAGEUP, wxMOD_CONTROL) );
CPPUNIT_ASSERT_EQUAL( 2, m_win->GetKeyUpCount() );
ASSERT_KEY_EVENT_IS( m_win->GetKeyUpEvent(0),
KeyDesc(WXK_PAGEUP, wxMOD_CONTROL) );
ASSERT_KEY_EVENT_IS( m_win->GetKeyUpEvent(1),
ModKeyUp(WXK_CONTROL) );
}
void KeyboardEventTestCase::ShiftLetter()
{
wxUIActionSimulator sim;
sim.Char('Q', wxMOD_SHIFT);
wxYield();
CPPUNIT_ASSERT_EQUAL( 2, m_win->GetKeyDownCount() );
ASSERT_KEY_EVENT_IS( m_win->GetKeyDownEvent(0),
ModKeyDown(WXK_SHIFT) );
ASSERT_KEY_EVENT_IS( m_win->GetKeyDownEvent(1),
KeyDesc('Q', wxMOD_SHIFT) );
CPPUNIT_ASSERT_EQUAL( 1, m_win->GetCharCount() );
ASSERT_KEY_EVENT_IS( m_win->GetCharEvent(),
KeyDesc('Q', wxMOD_SHIFT) );
CPPUNIT_ASSERT_EQUAL( 2, m_win->GetKeyUpCount() );
ASSERT_KEY_EVENT_IS( m_win->GetKeyUpEvent(0),
KeyDesc('Q', wxMOD_SHIFT) );
ASSERT_KEY_EVENT_IS( m_win->GetKeyUpEvent(1),
ModKeyUp(WXK_SHIFT) );
}
void KeyboardEventTestCase::ShiftSpecial()
{
wxUIActionSimulator sim;
sim.Char(WXK_F3, wxMOD_SHIFT);
wxYield();
CPPUNIT_ASSERT_EQUAL( 2, m_win->GetKeyDownCount() );
ASSERT_KEY_EVENT_IS( m_win->GetKeyDownEvent(0),
ModKeyDown(WXK_SHIFT) );
ASSERT_KEY_EVENT_IS( m_win->GetKeyDownEvent(1),
KeyDesc(WXK_F3, wxMOD_SHIFT) );
CPPUNIT_ASSERT_EQUAL( 1, m_win->GetCharCount() );
ASSERT_KEY_EVENT_IS( m_win->GetCharEvent(),
KeyDesc(WXK_F3, wxMOD_SHIFT) );
CPPUNIT_ASSERT_EQUAL( 2, m_win->GetKeyUpCount() );
ASSERT_KEY_EVENT_IS( m_win->GetKeyUpEvent(0),
KeyDesc(WXK_F3, wxMOD_SHIFT) );
ASSERT_KEY_EVENT_IS( m_win->GetKeyUpEvent(1),
ModKeyUp(WXK_SHIFT) );
}
#endif // wxUSE_UIACTIONSIMULATOR

View File

@@ -0,0 +1,832 @@
///////////////////////////////////////////////////////////////////////////////
// Name: tests/events/propagation.cpp
// Purpose: Test events propagation
// Author: Vadim Zeitlin
// Created: 2009-01-16
// Copyright: (c) 2009 Vadim Zeitlin <vadim@wxwidgets.org>
///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#include "testprec.h"
#ifndef WX_PRECOMP
#include "wx/app.h"
#include "wx/event.h"
#include "wx/scrolwin.h"
#include "wx/window.h"
#endif // WX_PRECOMP
#include "wx/docmdi.h"
#include "wx/frame.h"
#include "wx/menu.h"
#include "wx/scopeguard.h"
#include "wx/toolbar.h"
#include "wx/uiaction.h"
#if wxUSE_AUI
#include "wx/aui/framemanager.h"
#include "wx/aui/tabmdi.h"
#endif
#include <memory>
#include "waitfor.h"
// FIXME: Currently under OS X testing paint event doesn't work because neither
// calling Refresh()+Update() nor even sending wxPaintEvent directly to
// the window doesn't result in calls to its event handlers, so disable
// some tests there. But this should be fixed and the tests reenabled
// because wxPaintEvent propagation in wxScrolledWindow is a perfect
// example of fragile code that could be broken under OS X.
#if !defined(__WXOSX__)
#define CAN_TEST_PAINT_EVENTS
#endif
namespace
{
// this string will record the execution of all handlers
wxString g_str;
// a custom event
wxDEFINE_EVENT(TEST_EVT, wxCommandEvent);
// a custom event handler tracing the propagation of the events of the
// specified types
template <class Event>
class TestEvtHandlerBase : public wxEvtHandler
{
public:
TestEvtHandlerBase(wxEventType evtType, char tag)
: m_evtType(evtType),
m_tag(tag)
{
Connect(evtType,
static_cast<wxEventFunction>(&TestEvtHandlerBase::OnTest));
}
// override ProcessEvent() to confirm that it is called for all event
// handlers in the chain
virtual bool ProcessEvent(wxEvent& event) override
{
if ( event.GetEventType() == m_evtType )
g_str += 'o'; // "o" == "overridden"
return wxEvtHandler::ProcessEvent(event);
}
private:
void OnTest(wxEvent& event)
{
g_str += m_tag;
event.Skip();
}
const wxEventType m_evtType;
const char m_tag;
wxDECLARE_NO_COPY_TEMPLATE_CLASS(TestEvtHandlerBase, Event);
};
struct TestEvtHandler : TestEvtHandlerBase<wxCommandEvent>
{
TestEvtHandler(char tag)
: TestEvtHandlerBase<wxCommandEvent>(TEST_EVT, tag)
{
}
};
struct TestMenuEvtHandler : TestEvtHandlerBase<wxCommandEvent>
{
TestMenuEvtHandler(char tag)
: TestEvtHandlerBase<wxCommandEvent>(wxEVT_MENU, tag)
{
}
};
struct TestPaintEvtHandler : TestEvtHandlerBase<wxPaintEvent>
{
TestPaintEvtHandler(char tag)
: TestEvtHandlerBase<wxPaintEvent>(wxEVT_PAINT, tag)
{
}
};
// Another custom event handler, suitable for use with Connect().
struct TestEvtSink : wxEvtHandler
{
TestEvtSink(char tag)
: m_tag(tag)
{
}
void Handle(wxEvent& event)
{
g_str += m_tag;
event.Skip();
}
const char m_tag;
wxDECLARE_NO_COPY_CLASS(TestEvtSink);
};
// a window handling the test event
class TestWindow : public wxWindow
{
public:
TestWindow(wxWindow *parent, char tag)
: wxWindow(parent, wxID_ANY),
m_tag(tag)
{
Connect(TEST_EVT, wxCommandEventHandler(TestWindow::OnTest));
}
private:
void OnTest(wxCommandEvent& event)
{
g_str += m_tag;
event.Skip();
}
const char m_tag;
wxDECLARE_NO_COPY_CLASS(TestWindow);
};
// a scroll window handling paint event: we want to have a special test case
// for this because the event propagation is complicated even further than
// usual here by the presence of wxScrollHelperEvtHandler in the event handlers
// chain and the fact that OnDraw() virtual method must be called if EVT_PAINT
// is not handled
class TestScrollWindow : public wxScrolledWindow
{
public:
TestScrollWindow(wxWindow *parent)
: wxScrolledWindow(parent, wxID_ANY)
{
Connect(wxEVT_PAINT, wxPaintEventHandler(TestScrollWindow::OnPaint));
}
void GeneratePaintEvent()
{
#if defined(__WXGTK__) || defined(__WXQT__)
// We need to map the window, otherwise we're not going to get any
// paint events for it.
YieldForAWhile();
// Ignore events generated during the initial mapping.
g_str.clear();
#endif // __WXGTK__ || __WXQT__
Refresh();
Update();
}
virtual void OnDraw(wxDC& WXUNUSED(dc)) override
{
g_str += 'D'; // draw
}
private:
void OnPaint(wxPaintEvent& event)
{
g_str += 'P'; // paint
event.Skip();
}
wxDECLARE_NO_COPY_CLASS(TestScrollWindow);
};
int DoFilterEvent(wxEvent& event)
{
if ( event.GetEventType() == TEST_EVT ||
event.GetEventType() == wxEVT_MENU )
g_str += 'a';
return -1;
}
bool DoProcessEvent(wxEvent& event)
{
if ( event.GetEventType() == TEST_EVT ||
event.GetEventType() == wxEVT_MENU )
g_str += 'A';
return false;
}
} // anonymous namespace
// --------------------------------------------------------------------------
// test class
// --------------------------------------------------------------------------
class EventPropagationTestCase : public CppUnit::TestCase
{
public:
EventPropagationTestCase() {}
virtual void setUp() override;
virtual void tearDown() override;
private:
CPPUNIT_TEST_SUITE( EventPropagationTestCase );
CPPUNIT_TEST( OneHandler );
CPPUNIT_TEST( TwoHandlers );
CPPUNIT_TEST( WindowWithoutHandler );
CPPUNIT_TEST( WindowWithHandler );
CPPUNIT_TEST( ForwardEvent );
CPPUNIT_TEST( ScrollWindowWithoutHandler );
CPPUNIT_TEST( ScrollWindowWithHandler );
// for unknown reason, this test will cause the tests segmentation failed
// under x11, disable it for now.
#if !defined (__WXX11__) && wxUSE_MENUS
CPPUNIT_TEST( MenuEvent );
#endif
#if wxUSE_DOC_VIEW_ARCHITECTURE
CPPUNIT_TEST( DocView );
#if wxUSE_AUI
CPPUNIT_TEST( DocViewAui );
#endif
#endif // wxUSE_DOC_VIEW_ARCHITECTURE
WXUISIM_TEST( ContextMenuEvent );
WXUISIM_TEST( PropagationLevel );
CPPUNIT_TEST_SUITE_END();
void OneHandler();
void TwoHandlers();
void WindowWithoutHandler();
void WindowWithHandler();
void ForwardEvent();
void ScrollWindowWithoutHandler();
void ScrollWindowWithHandler();
#if wxUSE_MENUS
void MenuEvent();
#endif
#if wxUSE_DOC_VIEW_ARCHITECTURE
void DocView();
#if wxUSE_AUI
void DocViewAui();
#endif
void DocViewCommon(wxFrame* (*newParent)(wxDocManager *manager,
wxFrame *parent,
wxWindowID id,
const wxString& title),
wxFrame* (*newChild)(wxDocument *doc,
wxView *view,
wxFrame *parent,
wxWindowID id,
const wxString& title));
#endif // wxUSE_DOC_VIEW_ARCHITECTURE
#if wxUSE_UIACTIONSIMULATOR
void ContextMenuEvent();
void PropagationLevel();
#endif
wxDECLARE_NO_COPY_CLASS(EventPropagationTestCase);
};
// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION( EventPropagationTestCase );
// also include in its own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( EventPropagationTestCase, "EventPropagationTestCase" );
void EventPropagationTestCase::setUp()
{
SetFilterEventFunc(DoFilterEvent);
SetProcessEventFunc(DoProcessEvent);
g_str.clear();
}
void EventPropagationTestCase::tearDown()
{
SetFilterEventFunc(nullptr);
SetProcessEventFunc(nullptr);
}
void EventPropagationTestCase::OneHandler()
{
wxCommandEvent event(TEST_EVT);
TestEvtHandler h1('1');
h1.ProcessEvent(event);
CPPUNIT_ASSERT_EQUAL( "oa1A", g_str );
}
void EventPropagationTestCase::TwoHandlers()
{
wxCommandEvent event(TEST_EVT);
TestEvtHandler h1('1');
TestEvtHandler h2('2');
h1.SetNextHandler(&h2);
h2.SetPreviousHandler(&h1);
h1.ProcessEvent(event);
CPPUNIT_ASSERT_EQUAL( "oa1o2A", g_str );
}
void EventPropagationTestCase::WindowWithoutHandler()
{
wxCommandEvent event(TEST_EVT);
TestWindow * const parent = new TestWindow(wxTheApp->GetTopWindow(), 'p');
wxON_BLOCK_EXIT_OBJ0( *parent, wxWindow::Destroy );
TestWindow * const child = new TestWindow(parent, 'c');
child->GetEventHandler()->ProcessEvent(event);
CPPUNIT_ASSERT_EQUAL( "acpA", g_str );
}
void EventPropagationTestCase::WindowWithHandler()
{
wxCommandEvent event(TEST_EVT);
TestWindow * const parent = new TestWindow(wxTheApp->GetTopWindow(), 'p');
wxON_BLOCK_EXIT_OBJ0( *parent, wxWindow::Destroy );
TestWindow * const child = new TestWindow(parent, 'c');
TestEvtHandler h1('1');
child->PushEventHandler(&h1);
wxON_BLOCK_EXIT_OBJ1( *child, wxWindow::PopEventHandler, false );
TestEvtHandler h2('2');
child->PushEventHandler(&h2);
wxON_BLOCK_EXIT_OBJ1( *child, wxWindow::PopEventHandler, false );
child->HandleWindowEvent(event);
CPPUNIT_ASSERT_EQUAL( "oa2o1cpA", g_str );
}
void EventPropagationTestCase::ForwardEvent()
{
// The idea of this test is to check that the events explicitly forwarded
// to another event handler still get pre/post-processed as usual as this
// used to be broken by the fixes trying to avoid duplicate processing.
TestWindow * const win = new TestWindow(wxTheApp->GetTopWindow(), 'w');
wxON_BLOCK_EXIT_OBJ0( *win, wxWindow::Destroy );
TestEvtHandler h1('1');
win->PushEventHandler(&h1);
wxON_BLOCK_EXIT_OBJ1( *win, wxWindow::PopEventHandler, false );
class ForwardEvtHandler : public wxEvtHandler
{
public:
ForwardEvtHandler(wxEvtHandler& h) : m_h(&h) { }
virtual bool ProcessEvent(wxEvent& event) override
{
g_str += 'f';
return m_h->ProcessEvent(event);
}
private:
wxEvtHandler *m_h;
} f(h1);
// First send the event directly to f.
wxCommandEvent event1(TEST_EVT);
f.ProcessEvent(event1);
CPPUNIT_ASSERT_EQUAL( "foa1wA", g_str );
g_str.clear();
// And then also test sending it to f indirectly.
wxCommandEvent event2(TEST_EVT);
TestEvtHandler h2('2');
h2.SetNextHandler(&f);
h2.ProcessEvent(event2);
CPPUNIT_ASSERT_EQUAL( "oa2fo1wAA", g_str );
}
void EventPropagationTestCase::ScrollWindowWithoutHandler()
{
TestWindow * const parent = new TestWindow(wxTheApp->GetTopWindow(), 'p');
wxON_BLOCK_EXIT_OBJ0( *parent, wxWindow::Destroy );
TestScrollWindow * const win = new TestScrollWindow(parent);
#ifdef CAN_TEST_PAINT_EVENTS
win->GeneratePaintEvent();
CPPUNIT_ASSERT_EQUAL( "PD", g_str );
#endif
g_str.clear();
wxCommandEvent eventCmd(TEST_EVT);
win->HandleWindowEvent(eventCmd);
CPPUNIT_ASSERT_EQUAL( "apA", g_str );
}
void EventPropagationTestCase::ScrollWindowWithHandler()
{
TestWindow * const parent = new TestWindow(wxTheApp->GetTopWindow(), 'p');
wxON_BLOCK_EXIT_OBJ0( *parent, wxWindow::Destroy );
TestScrollWindow * const win = new TestScrollWindow(parent);
#ifdef CAN_TEST_PAINT_EVENTS
TestPaintEvtHandler h('h');
win->PushEventHandler(&h);
wxON_BLOCK_EXIT_OBJ1( *win, wxWindow::PopEventHandler, false );
win->GeneratePaintEvent();
CPPUNIT_ASSERT_EQUAL( "ohPD", g_str );
#endif
g_str.clear();
wxCommandEvent eventCmd(TEST_EVT);
win->HandleWindowEvent(eventCmd);
CPPUNIT_ASSERT_EQUAL( "apA", g_str );
}
#if wxUSE_MENUS
// Create a menu bar with a single menu containing wxID_APPLY menu item and
// attach it to the specified frame.
wxMenu* CreateTestMenu(wxFrame* frame)
{
wxMenu* const menu = new wxMenu;
menu->Append(wxID_APPLY);
#if wxUSE_MENUBAR
wxMenuBar* const mb = new wxMenuBar;
mb->Append(menu, "&Menu");
frame->SetMenuBar(mb);
#endif
return menu;
}
// Helper for checking that the menu event processing resulted in the expected
// output from the handlers.
//
// Note that we trigger the menu event by sending it directly as this is more
// reliable than using wxUIActionSimulator and currently works in all ports as
// they all call wxMenuBase::SendEvent() from their respective menu event
// handlers.
#define ASSERT_MENU_EVENT_RESULT_FOR(cmd, menu, result) \
g_str.clear(); \
menu->SendEvent(cmd); \
CHECK( g_str == result )
#define ASSERT_MENU_EVENT_RESULT(menu, result) \
ASSERT_MENU_EVENT_RESULT_FOR(wxID_APPLY, menu, result)
void EventPropagationTestCase::MenuEvent()
{
wxFrame* const frame = static_cast<wxFrame*>(wxTheApp->GetTopWindow());
// Create a minimal menu bar.
wxMenu* const menu = CreateTestMenu(frame);
#if wxUSE_MENUBAR
wxMenuBar* const mb = menu->GetMenuBar();
std::unique_ptr<wxMenuBar> ensureMenuBarDestruction(mb);
wxON_BLOCK_EXIT_OBJ1( *frame, wxFrame::SetMenuBar, (wxMenuBar*)nullptr );
#endif
// Check that wxApp gets the event exactly once.
ASSERT_MENU_EVENT_RESULT( menu, "aA" );
// Check that the menu event handler is called.
TestMenuEvtHandler hm('m'); // 'm' for "menu"
menu->SetNextHandler(&hm);
wxON_BLOCK_EXIT_OBJ1( *menu,
wxEvtHandler::SetNextHandler, (wxEvtHandler*)nullptr );
ASSERT_MENU_EVENT_RESULT( menu, "aomA" );
// Check that a handler can also be attached to a submenu.
wxMenu* const submenu = new wxMenu;
submenu->Append(wxID_ABOUT);
menu->Append(wxID_ANY, "Submenu", submenu);
TestMenuEvtHandler hs('s'); // 's' for "submenu"
submenu->SetNextHandler(&hs);
wxON_BLOCK_EXIT_OBJ1( *submenu,
wxEvtHandler::SetNextHandler, (wxEvtHandler*)nullptr );
ASSERT_MENU_EVENT_RESULT_FOR( wxID_ABOUT, submenu, "aosomA" );
#if wxUSE_MENUBAR
// Test that the event handler associated with the menu bar gets the event.
TestMenuEvtHandler hb('b'); // 'b' for "menu Bar"
mb->PushEventHandler(&hb);
wxON_BLOCK_EXIT_OBJ1( *mb, wxWindow::PopEventHandler, false );
ASSERT_MENU_EVENT_RESULT( menu, "aomobA" );
#endif
// Also test that the window to which the menu belongs gets the event.
TestMenuEvtHandler hw('w'); // 'w' for "Window"
frame->PushEventHandler(&hw);
wxON_BLOCK_EXIT_OBJ1( *frame, wxWindow::PopEventHandler, false );
ASSERT_MENU_EVENT_RESULT( menu, "aomobowA" );
}
#endif
#if wxUSE_DOC_VIEW_ARCHITECTURE
// Minimal viable implementations of wxDocument and wxView.
class EventTestDocument : public wxDocument
{
public:
EventTestDocument() { }
wxDECLARE_DYNAMIC_CLASS(EventTestDocument);
};
class EventTestView : public wxView
{
public:
EventTestView() { }
virtual void OnDraw(wxDC*) override { }
wxDECLARE_DYNAMIC_CLASS(EventTestView);
};
wxIMPLEMENT_DYNAMIC_CLASS(EventTestDocument, wxDocument);
wxIMPLEMENT_DYNAMIC_CLASS(EventTestView, wxView);
void EventPropagationTestCase::DocView()
{
DocViewCommon(
[](wxDocManager* manager,
wxFrame *parent,
wxWindowID id,
const wxString& title) -> wxFrame*
{
return new wxDocMDIParentFrame(manager, parent, id, title);
},
[](wxDocument *doc,
wxView *view,
wxFrame *parent,
wxWindowID id,
const wxString& title) -> wxFrame*
{
wxDocMDIChildFrame* child = new wxDocMDIChildFrame(doc, view, dynamic_cast<wxDocMDIParentFrame*>(parent), id, title);
// Ensure that the child that we've just created is the active one.
child->Activate();
return child;
}
);
}
#if wxUSE_AUI
void EventPropagationTestCase::DocViewAui()
{
DocViewCommon(
[](wxDocManager* manager,
wxFrame *parent,
wxWindowID id,
const wxString& title) -> wxFrame*
{
class AuiParentFrame : public wxDocParentFrameAny<wxAuiMDIParentFrame>
{
public:
AuiParentFrame(wxDocManager* manager,
wxFrame* parent,
wxWindowID id,
const wxString& title) :
wxDocParentFrameAny<wxAuiMDIParentFrame>(manager, parent, id, title),
auiMgr(this)
{
}
private:
wxAuiManager auiMgr;
};
return new AuiParentFrame(manager, parent, id, title);
},
[](wxDocument *doc,
wxView *view,
wxFrame *parentBase,
wxWindowID id,
const wxString& title) -> wxFrame*
{
typedef wxDocParentFrameAny<wxAuiMDIParentFrame> ParentType;
ParentType* parent = dynamic_cast<ParentType*>(parentBase);
wxFrame* child = new wxDocChildFrameAny<wxAuiMDIChildFrame, wxAuiMDIParentFrame>(doc, view, parent, id, title);
CHECK( parent->GetActiveChild() == child );
return child;
}
);
}
#endif // wxUSE_AUI
void EventPropagationTestCase::DocViewCommon(wxFrame* (*newParent)(wxDocManager *manager,
wxFrame *parent,
wxWindowID id,
const wxString& title),
wxFrame* (*newChild)(wxDocument *doc,
wxView *view,
wxFrame *parent,
wxWindowID id,
const wxString& title))
{
// Set up the parent frame and its menu bar.
wxDocManager docManager;
std::unique_ptr<wxFrame>
parent((*newParent)(&docManager, nullptr, wxID_ANY, "Parent"));
wxMenu* const menu = CreateTestMenu(parent.get());
// Set up the event handlers.
TestEvtSink sinkDM('m');
docManager.Connect(wxEVT_MENU,
wxEventHandler(TestEvtSink::Handle), nullptr, &sinkDM);
TestEvtSink sinkParent('p');
parent->Connect(wxEVT_MENU,
wxEventHandler(TestEvtSink::Handle), nullptr, &sinkParent);
// Check that wxDocManager and wxFrame get the event in order.
ASSERT_MENU_EVENT_RESULT( menu, "ampA" );
// The document template must be heap-allocated as wxDocManager owns it.
wxDocTemplate* const docTemplate = new wxDocTemplate
(
&docManager, "Test", "", "", "",
"Test Document", "Test View",
wxCLASSINFO(EventTestDocument),
wxCLASSINFO(EventTestView)
);
// Now check what happens if we have an active document.
wxDocument* const doc = docTemplate->CreateDocument("");
wxView* const view = doc->GetFirstView();
std::unique_ptr<wxFrame>
child((*newChild)(doc, view, parent.get(), wxID_ANY, "Child"));
wxMenu* const menuChild = CreateTestMenu(child.get());
// There are a lot of hacks related to child frame menu bar handling in
// wxGTK and, in particular, the code in src/gtk/mdi.cpp relies on getting
// idle events to really put everything in place. Moreover, as wxGTK uses
// GtkNotebook as its MDI pages container, the frame must be shown for all
// this to work as gtk_notebook_set_current_page() doesn't do anything if
// called for a hidden window (this incredible fact cost me quite some time
// to find empirically -- only to notice its confirmation in GTK+
// documentation immediately afterwards). So just do whatever it takes to
// make things work "as usual".
child->Show();
parent->Show();
child->SetFocus(); // Without this, the test would fail on wxGTK2
YieldForAWhile();
TestEvtSink sinkDoc('d');
doc->Connect(wxEVT_MENU,
wxEventHandler(TestEvtSink::Handle), nullptr, &sinkDoc);
TestEvtSink sinkView('v');
view->Connect(wxEVT_MENU,
wxEventHandler(TestEvtSink::Handle), nullptr, &sinkView);
TestEvtSink sinkChild('c');
child->Connect(wxEVT_MENU,
wxEventHandler(TestEvtSink::Handle), nullptr, &sinkChild);
// Check that wxDocument, wxView, wxDocManager, child frame and the parent
// get the event in order.
#if wxUSE_UIACTIONSIMULATOR
// We use wxUIActionSimulator instead of ASSERT_MENU_EVENT_RESULT because
// using the latter fails with wxQt on Linux.
wxUnusedVar(menuChild);
g_str.clear();
wxUIActionSimulator sim;
sim.Char('m', wxMOD_ALT);
// N.B.: Don't call wxYield() here, as this will cause the menu to appear
// immediately (and enter its internal message loop) and the next line will
// never be executed under wxMSW. In other words, the execution would block
// indefinitely.
sim.Char('a');
wxYield();
CHECK( g_str == "advmcpA" );
#else // !wxUSE_UIACTIONSIMULATOR
ASSERT_MENU_EVENT_RESULT( menuChild, "advmcpA" );
#endif // wxUSE_UIACTIONSIMULATOR
#if wxUSE_TOOLBAR
// Also check that toolbar events get forwarded to the active child.
wxToolBar* const tb = parent->CreateToolBar(wxTB_NOICONS);
tb->AddTool(wxID_APPLY, "Apply", wxNullBitmap);
tb->Realize();
// As in CheckMenuEvent(), use toolbar method actually sending the event
// instead of bothering with wxUIActionSimulator which would have been
// trickier.
g_str.clear();
tb->OnLeftClick(wxID_APPLY, true /* doesn't matter */);
CPPUNIT_ASSERT_EQUAL( "advmcpA", g_str );
#endif // wxUSE_TOOLBAR
}
#endif // wxUSE_DOC_VIEW_ARCHITECTURE
#if wxUSE_UIACTIONSIMULATOR
class ContextMenuTestWindow : public wxWindow
{
public:
ContextMenuTestWindow(wxWindow *parent, char tag)
: wxWindow(parent, wxID_ANY),
m_tag(tag)
{
Connect(wxEVT_CONTEXT_MENU,
wxContextMenuEventHandler(ContextMenuTestWindow::OnMenu));
}
private:
void OnMenu(wxContextMenuEvent& event)
{
g_str += m_tag;
event.Skip();
}
const char m_tag;
wxDECLARE_NO_COPY_CLASS(ContextMenuTestWindow);
};
void EventPropagationTestCase::ContextMenuEvent()
{
ContextMenuTestWindow * const
parent = new ContextMenuTestWindow(wxTheApp->GetTopWindow(), 'p');
wxON_BLOCK_EXIT_OBJ0( *parent, wxWindow::Destroy );
ContextMenuTestWindow * const
child = new ContextMenuTestWindow(parent, 'c');
parent->SetSize(100, 100);
child->SetSize(0, 0, 50, 50);
child->SetFocus();
wxUIActionSimulator sim;
const wxPoint origin = parent->ClientToScreen(wxPoint(0, 0));
// Right clicking in the child should generate an event for it and the
// parent.
g_str.clear();
sim.MouseMove(origin + wxPoint(10, 10));
sim.MouseClick(wxMOUSE_BTN_RIGHT);
// At least with MSW, for WM_CONTEXTMENU to be synthesized by the system
// from the right mouse click event, we must dispatch the mouse messages.
wxYield();
CPPUNIT_ASSERT_EQUAL( "cp", g_str );
// For some unfathomable reason the test below sporadically fails in wxGTK
// buildbot builds, so disable it there to avoid spurious failure reports.
#ifdef __WXGTK__
if ( IsAutomaticTest() )
return;
#endif // __WXGTK__
// Right clicking outside the child should generate the event just in the
// parent.
g_str.clear();
sim.MouseMove(origin + wxPoint(60, 60));
sim.MouseClick(wxMOUSE_BTN_RIGHT);
wxYield();
CPPUNIT_ASSERT_EQUAL( "p", g_str );
}
// Helper function: get the event propagation level.
int GetPropagationLevel(wxEvent& e)
{
const int level = e.StopPropagation();
e.ResumePropagation(level);
return level;
}
void EventPropagationTestCase::PropagationLevel()
{
wxSizeEvent se;
CPPUNIT_ASSERT_EQUAL( GetPropagationLevel(se), (int)wxEVENT_PROPAGATE_NONE );
wxCommandEvent ce;
CPPUNIT_ASSERT_EQUAL( GetPropagationLevel(ce), (int)wxEVENT_PROPAGATE_MAX );
wxCommandEvent ce2(ce);
CPPUNIT_ASSERT_EQUAL( GetPropagationLevel(ce2), (int)wxEVENT_PROPAGATE_MAX );
wxCommandEvent ce3;
ce3.ResumePropagation(17);
CPPUNIT_ASSERT_EQUAL( GetPropagationLevel(ce3), 17 );
wxCommandEvent ce4(ce3);
CPPUNIT_ASSERT_EQUAL( GetPropagationLevel(ce4), 17 );
}
#endif // wxUSE_UIACTIONSIMULATOR

View File

@@ -0,0 +1,169 @@
///////////////////////////////////////////////////////////////////////////////
// Name: tests/events/stopwatch.cpp
// Purpose: Test wxStopWatch class
// Author: Francesco Montorsi (extracted from console sample)
// Created: 2010-05-16
// Copyright: (c) 2010 wxWidgets team
///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#include "testprec.h"
#ifndef WX_PRECOMP
#endif // WX_PRECOMP
#include <time.h>
#include "wx/stopwatch.h"
#include "wx/utils.h"
namespace
{
const long tolerance = 50; // in ms
const int sleepTime = 500;
} // anonymous namespace
// --------------------------------------------------------------------------
// test class
// --------------------------------------------------------------------------
class StopWatchTestCase : public CppUnit::TestCase
{
public:
StopWatchTestCase() {}
private:
CPPUNIT_TEST_SUITE( StopWatchTestCase );
CPPUNIT_TEST( Misc );
CPPUNIT_TEST( BackwardsClockBug );
CPPUNIT_TEST( RestartBug );
CPPUNIT_TEST_SUITE_END();
void Misc();
void BackwardsClockBug();
void RestartBug();
wxDECLARE_NO_COPY_CLASS(StopWatchTestCase);
};
// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION( StopWatchTestCase );
// also include in its own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( StopWatchTestCase, "StopWatchTestCase" );
void StopWatchTestCase::Misc()
{
// Buildbot machines are quite slow and sleep doesn't work reliably there,
// i.e. it can sleep for much longer than requested. This is not really an
// error, so just don't run this test there -- and if you get failures in
// this test when running it interactively, this might also be normal if
// the machine is under heavy load.
if ( IsAutomaticTest() )
return;
wxStopWatch sw;
long t;
wxLongLong usec;
sw.Pause(); // pause it immediately
// verify that almost no time elapsed
usec = sw.TimeInMicro();
WX_ASSERT_MESSAGE
(
("Elapsed time was %" wxLongLongFmtSpec "dus", usec),
usec < tolerance*1000
);
wxSleep(1);
t = sw.Time();
// check that the stop watch doesn't advance while paused
WX_ASSERT_MESSAGE
(
("Actual time value is %ld", t),
t >= 0 && t < tolerance
);
sw.Resume();
wxMilliSleep(sleepTime);
t = sw.Time();
// check that it did advance now by ~1.5s
WX_ASSERT_MESSAGE
(
("Actual time value is %ld", t),
t > sleepTime - tolerance && t < 2*sleepTime
);
sw.Pause();
// check that this sleep won't be taken into account below
wxMilliSleep(sleepTime);
sw.Resume();
wxMilliSleep(sleepTime);
t = sw.Time();
// and it should advance again
WX_ASSERT_MESSAGE
(
("Actual time value is %ld", t),
t > 2*sleepTime - tolerance && t < 3*sleepTime
);
}
void StopWatchTestCase::BackwardsClockBug()
{
wxStopWatch sw;
wxStopWatch sw2;
for ( size_t n = 0; n < 10; n++ )
{
sw2.Start();
for ( size_t m = 0; m < 10000; m++ )
{
CPPUNIT_ASSERT( sw.Time() >= 0 );
CPPUNIT_ASSERT( sw2.Time() >= 0 );
}
}
}
void StopWatchTestCase::RestartBug()
{
wxStopWatch sw;
sw.Pause();
// Calling Start() should resume the stopwatch if it was paused.
static const int offset = 5000;
sw.Start(offset);
wxMilliSleep(sleepTime);
long t = sw.Time();
WX_ASSERT_MESSAGE
(
("Actual time value is %ld", t),
t >= offset + sleepTime - tolerance
);
// As above, this is not actually due to the fact of the test being
// automatic but just because buildbot machines are usually pretty slow, so
// this test often fails there simply because of the high load on them.
if ( !IsAutomaticTest() )
{
WX_ASSERT_MESSAGE
(
("Actual time value is %ld", t),
t < offset + sleepTime + tolerance
);
}
}

View File

@@ -0,0 +1,139 @@
///////////////////////////////////////////////////////////////////////////////
// Name: tests/events/timertest.cpp
// Purpose: Test wxTimer events
// Author: Vadim Zeitlin
// Created: 2008-10-22
// Copyright: (c) 2008 Vadim Zeitlin <vadim@wxwidgets.org>
///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#include "testprec.h"
#ifndef WX_PRECOMP
#endif // WX_PRECOMP
#include <time.h>
#include "wx/evtloop.h"
#include "wx/timer.h"
// --------------------------------------------------------------------------
// helper class counting the number of timer events
// --------------------------------------------------------------------------
class TimerCounterHandler : public wxEvtHandler
{
public:
TimerCounterHandler()
{
m_events = 0;
Connect(wxEVT_TIMER, wxTimerEventHandler(TimerCounterHandler::OnTimer));
}
int GetNumEvents() const { return m_events; }
private:
void OnTimer(wxTimerEvent& WXUNUSED(event))
{
m_events++;
Tick();
}
virtual void Tick() { /* nothing to do in the base class */ }
int m_events;
wxDECLARE_NO_COPY_CLASS(TimerCounterHandler);
};
// --------------------------------------------------------------------------
// test class
// --------------------------------------------------------------------------
class TimerEventTestCase : public CppUnit::TestCase
{
public:
TimerEventTestCase() {}
private:
CPPUNIT_TEST_SUITE( TimerEventTestCase );
CPPUNIT_TEST( OneShot );
CPPUNIT_TEST( Multiple );
CPPUNIT_TEST_SUITE_END();
void OneShot();
void Multiple();
wxDECLARE_NO_COPY_CLASS(TimerEventTestCase);
};
// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION( TimerEventTestCase );
// also include in its own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( TimerEventTestCase, "TimerEventTestCase" );
void TimerEventTestCase::OneShot()
{
class ExitOnTimerHandler : public TimerCounterHandler
{
public:
ExitOnTimerHandler(wxEventLoopBase& loop)
: TimerCounterHandler(),
m_loop(loop)
{
}
private:
virtual void Tick() override { m_loop.Exit(); }
wxEventLoopBase& m_loop;
// don't use wxDECLARE_NO_COPY_CLASS() to avoid upsetting MSVC
};
wxEventLoop loop;
ExitOnTimerHandler handler(loop);
wxTimer timer(&handler);
timer.Start(200, true);
loop.Run();
CPPUNIT_ASSERT_EQUAL( 1, handler.GetNumEvents() );
}
void TimerEventTestCase::Multiple()
{
wxEventLoop loop;
TimerCounterHandler handler;
wxTimer timer(&handler);
timer.Start(100);
// run the loop for 2 seconds
time_t t;
time(&t);
const time_t tEnd = t + 2;
while ( time(&t) < tEnd )
{
loop.Dispatch();
}
// we can't count on getting exactly 20 ticks but we shouldn't get more
// than this
const int numTicks = handler.GetNumEvents();
CPPUNIT_ASSERT( numTicks <= 20 );
// and we should get a decent number of them but if the system is very
// loaded (as happens with build bot slaves running a couple of builds in
// parallel actually) it may be much less than 20 so just check that we get
// more than one
CPPUNIT_ASSERT( numTicks > 1 );
}