wxWidgets/tests/events/evthandler.cpp
Vadim Zeitlin f3ff831f3b Changes to template Connect() to make it compile with MSVC7 and possible other
not quite up-to-date compilers, to reduce repetitions and to allow using
methods of non-wxEvtHandler-derived classes as event callbacks:

1. Don't rely on compiler ability to deduce template parameter from the type
   of a parameter of a function used as another template parameter, at least
   MSVC7 can't do this and it's probably not the only one.
2. Do rely on compiler support for partial specialization to make
   wxEventFunctorMethod compile for non-wxEvtHandler-derived handlers while
   still keeping the old functionality for the wxEvtHandler-derived ones.
3. Don't make any difference between functions and functors, both are callable
   objects so use them as such, this allows to fold code for both cases.
4. Avoid the use of dynamic_cast<>.
5. Several naming changes:
 a) wxTypedEventType -> wxEventTypeTag (because this is what it is)
 b) Subscribe/Unsubscribe -> DoConnect/Disconnect (to follow the usual
    convention of public Foo calling private DoFoo and to avoid using up
    another name)
 c) Derived -> ObjClass (because it's not clear what does Derived mean)
6. Extend the unit test to cover more cases.


git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@58625 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2009-02-02 20:38:56 +00:00

356 lines
11 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Name: tests/events/evthandler.cpp
// Purpose: Test the new event types and wxEvtHandler-methods
// Author: Peter Most
// Created: 2009-01-24
// RCS-ID: $Id$
// Copyright: (c) 2009 Peter Most
///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#include "testprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#include "wx/event.h"
// ----------------------------------------------------------------------------
// test events and their handlers
// ----------------------------------------------------------------------------
const wxEventType EVT_LEGACY = wxNewEventType();
class MyEvent;
wxDEFINE_EVENT( EVT_MYEVENT, MyEvent )
class MyEvent : public wxEvent
{
public:
MyEvent() : wxEvent(0, EVT_MYEVENT) { }
virtual wxEvent *Clone() const { return new MyEvent; }
};
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 GlobalOnAnotherEvent(AnotherEvent&);
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; }
};
// we can also handle events in classes not deriving from wxEvtHandler
struct MySink
{
void OnMyEvent(MyEvent&) { 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; }
private:
DECLARE_EVENT_TABLE()
};
BEGIN_EVENT_TABLE(MyClassWithEventTable, wxEvtHandler)
EVT_IDLE(MyClassWithEventTable::OnIdle)
// this shouldn't compile:
//EVT_IDLE(MyClassWithEventTable::OnAnotherEvent)
END_EVENT_TABLE()
} // anonymous namespace
// --------------------------------------------------------------------------
// test class
// --------------------------------------------------------------------------
class EvtHandlerTestCase : public CppUnit::TestCase
{
public:
EvtHandlerTestCase() {}
private:
CPPUNIT_TEST_SUITE( EvtHandlerTestCase );
CPPUNIT_TEST( BuiltinConnect );
CPPUNIT_TEST( LegacyConnect );
#if !wxEVENTS_COMPATIBILITY_2_8
CPPUNIT_TEST( ConnectFunction );
CPPUNIT_TEST( ConnectStaticMethod );
CPPUNIT_TEST( ConnectFunctor );
CPPUNIT_TEST( ConnectMethod );
CPPUNIT_TEST( ConnectMethodWithSink );
CPPUNIT_TEST( ConnectNonHandler );
CPPUNIT_TEST( StaticConnect );
CPPUNIT_TEST( InvalidConnect );
#endif // !wxEVENTS_COMPATIBILITY_2_8
CPPUNIT_TEST_SUITE_END();
void BuiltinConnect();
void LegacyConnect();
#if !wxEVENTS_COMPATIBILITY_2_8
void ConnectFunction();
void ConnectStaticMethod();
void ConnectFunctor();
void ConnectMethod();
void ConnectMethodWithSink();
void ConnectNonHandler();
void StaticConnect();
void InvalidConnect();
#endif // !wxEVENTS_COMPATIBILITY_2_8
// these member variables exceptionally don't use "m_" prefix because
// they're used so many times
MyHandler handler;
MyEvent e;
DECLARE_NO_COPY_CLASS(EvtHandlerTestCase)
};
// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION( EvtHandlerTestCase );
// also include in it's own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( EvtHandlerTestCase, "EvtHandlerTestCase" );
void EvtHandlerTestCase::BuiltinConnect()
{
handler.Connect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle));
handler.Disconnect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle));
handler.Connect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle), NULL, &handler);
handler.Disconnect(wxEVT_IDLE, wxIdleEventHandler(MyHandler::OnIdle), NULL, &handler);
#if !wxEVENTS_COMPATIBILITY_2_8
handler.Connect(wxEVT_IDLE, GlobalOnIdle);
handler.Disconnect(wxEVT_IDLE, GlobalOnIdle);
IdleFunctor f;
handler.Connect(wxEVT_IDLE, f);
handler.Disconnect(wxEVT_IDLE, f);
handler.Connect(wxEVT_IDLE, &MyHandler::OnIdle);
handler.Disconnect(wxEVT_IDLE, &MyHandler::OnIdle);
handler.Connect(wxEVT_IDLE, &MyHandler::StaticOnIdle);
handler.Disconnect(wxEVT_IDLE, &MyHandler::StaticOnIdle);
#endif // !wxEVENTS_COMPATIBILITY_2_8
}
void EvtHandlerTestCase::LegacyConnect()
{
handler.Connect( EVT_LEGACY, (wxObjectEventFunction)&MyHandler::OnEvent );
handler.Connect( 0, EVT_LEGACY, (wxObjectEventFunction)&MyHandler::OnEvent );
handler.Connect( 0, 0, EVT_LEGACY, (wxObjectEventFunction)&MyHandler::OnEvent );
handler.Disconnect( EVT_LEGACY, (wxObjectEventFunction)&MyHandler::OnEvent );
handler.Disconnect( 0, EVT_LEGACY, (wxObjectEventFunction)&MyHandler::OnEvent );
handler.Disconnect( 0, 0, EVT_LEGACY, (wxObjectEventFunction)&MyHandler::OnEvent );
handler.Connect( EVT_LEGACY, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
handler.Connect( 0, EVT_LEGACY, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
handler.Connect( 0, 0, EVT_LEGACY, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
handler.Disconnect( EVT_LEGACY, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
handler.Disconnect( 0, EVT_LEGACY, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
handler.Disconnect( 0, 0, EVT_LEGACY, (wxObjectEventFunction)&MyHandler::OnEvent, NULL, &handler );
}
#if !wxEVENTS_COMPATIBILITY_2_8
void EvtHandlerTestCase::ConnectFunction()
{
// function tests
handler.Connect( EVT_MYEVENT, GlobalOnMyEvent );
g_called.Reset();
handler.ProcessEvent(e);
CPPUNIT_ASSERT( g_called.function );
handler.Disconnect( EVT_MYEVENT, GlobalOnMyEvent );
g_called.Reset();
handler.ProcessEvent(e);
CPPUNIT_ASSERT( !g_called.function ); // check that it was disconnected
handler.Connect( 0, EVT_MYEVENT, GlobalOnMyEvent );
handler.Disconnect( 0, EVT_MYEVENT, GlobalOnMyEvent );
handler.Connect( 0, 0, EVT_MYEVENT, GlobalOnMyEvent );
handler.Disconnect( 0, 0, EVT_MYEVENT, GlobalOnMyEvent );
}
void EvtHandlerTestCase::ConnectStaticMethod()
{
// static method tests (this is same as functions but still test it just in
// case we hit some strange compiler bugs)
handler.Connect( EVT_MYEVENT, &MyHandler::StaticOnMyEvent );
g_called.Reset();
handler.ProcessEvent(e);
CPPUNIT_ASSERT( g_called.smethod );
handler.Disconnect( EVT_MYEVENT, &MyHandler::StaticOnMyEvent );
g_called.Reset();
handler.ProcessEvent(e);
CPPUNIT_ASSERT( !g_called.smethod );
handler.Connect( 0, EVT_MYEVENT, &MyHandler::StaticOnMyEvent );
handler.Disconnect( 0, EVT_MYEVENT, &MyHandler::StaticOnMyEvent );
handler.Connect( 0, 0, EVT_MYEVENT, &MyHandler::StaticOnMyEvent );
handler.Disconnect( 0, 0, EVT_MYEVENT, &MyHandler::StaticOnMyEvent );
}
void EvtHandlerTestCase::ConnectFunctor()
{
// generalized functor tests
MyFunctor functor;
handler.Connect( EVT_MYEVENT, functor );
g_called.Reset();
handler.ProcessEvent(e);
CPPUNIT_ASSERT( g_called.functor );
handler.Disconnect( EVT_MYEVENT, functor );
g_called.Reset();
handler.ProcessEvent(e);
CPPUNIT_ASSERT( !g_called.functor );
handler.Connect( 0, EVT_MYEVENT, functor );
handler.Disconnect( 0, EVT_MYEVENT, functor );
handler.Connect( 0, 0, EVT_MYEVENT, functor );
handler.Disconnect( 0, 0, EVT_MYEVENT, functor );
}
void EvtHandlerTestCase::ConnectMethod()
{
// class method tests
handler.Connect( EVT_MYEVENT, &MyHandler::OnMyEvent );
g_called.Reset();
handler.ProcessEvent(e);
CPPUNIT_ASSERT( g_called.method );
handler.Disconnect( EVT_MYEVENT, &MyHandler::OnMyEvent );
g_called.Reset();
handler.ProcessEvent(e);
CPPUNIT_ASSERT( !g_called.method );
handler.Connect( 0, EVT_MYEVENT, &MyHandler::OnMyEvent );
handler.Disconnect( 0, EVT_MYEVENT, &MyHandler::OnMyEvent );
handler.Connect( 0, 0, EVT_MYEVENT, &MyHandler::OnMyEvent );
handler.Disconnect( 0, 0, EVT_MYEVENT, &MyHandler::OnMyEvent );
}
void EvtHandlerTestCase::ConnectMethodWithSink()
{
handler.Connect( EVT_MYEVENT, &MyHandler::OnMyEvent, NULL, &handler );
handler.Connect( 0, EVT_MYEVENT, &MyHandler::OnMyEvent, NULL, &handler );
handler.Connect( 0, 0, EVT_MYEVENT, &MyHandler::OnMyEvent, NULL, &handler );
handler.Disconnect( EVT_MYEVENT, &MyHandler::OnMyEvent, NULL, &handler );
handler.Disconnect( 0, EVT_MYEVENT, &MyHandler::OnMyEvent, NULL, &handler );
handler.Disconnect( 0, 0, EVT_MYEVENT, &MyHandler::OnMyEvent, NULL, &handler );
}
void EvtHandlerTestCase::ConnectNonHandler()
{
// class method tests for class not derived from wxEvtHandler
MySink sink;
handler.Connect( EVT_MYEVENT, &MySink::OnMyEvent, NULL, &sink );
g_called.Reset();
handler.ProcessEvent(e);
CPPUNIT_ASSERT( g_called.method );
handler.Disconnect( EVT_MYEVENT, &MySink::OnMyEvent, NULL, &sink );
g_called.Reset();
handler.ProcessEvent(e);
CPPUNIT_ASSERT( !g_called.method );
}
void EvtHandlerTestCase::StaticConnect()
{
wxEvtHandler::Connect( &handler, EVT_MYEVENT, &MyHandler::OnMyEvent, NULL, &handler );
wxEvtHandler::Connect( &handler, 0, EVT_MYEVENT, &MyHandler::OnMyEvent, NULL, &handler );
wxEvtHandler::Connect( &handler, 0, 0, EVT_MYEVENT, &MyHandler::OnMyEvent, NULL, &handler );
wxEvtHandler::Disconnect( &handler, EVT_MYEVENT, &MyHandler::OnMyEvent, NULL, &handler );
wxEvtHandler::Disconnect( &handler, 0, EVT_MYEVENT, &MyHandler::OnMyEvent, NULL, &handler );
wxEvtHandler::Disconnect( &handler, 0, 0, EVT_MYEVENT, &MyHandler::OnMyEvent, NULL, &handler );
}
void EvtHandlerTestCase::InvalidConnect()
{
// 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
//handler.Connect(EVT_MYEVENT, GlobalOnAnotherEvent);
//IdleFunctor f; handler.Connect(EVT_MYEVENT, f);
//handler.Connect(EVT_MYEVENT, &MyHandler::StaticOnAnotherEvent);
//handler.Connect(EVT_MYEVENT, &MyHandler::OnAnotherEvent);
}
#endif // !wxEVENTS_COMPATIBILITY_2_8