Merge branch 'closure'

This commit is contained in:
David Sansome 2012-03-19 19:38:30 +00:00
commit 46e930f5c9
6 changed files with 157 additions and 0 deletions

View File

@ -50,6 +50,9 @@ Closure::Closure(QObject* sender,
Connect(sender, signal); Connect(sender, signal);
} }
Closure::~Closure() {
}
void Closure::Connect(QObject* sender, const char* signal) { void Closure::Connect(QObject* sender, const char* signal) {
bool success = connect(sender, signal, SLOT(Invoked())); bool success = connect(sender, signal, SLOT(Invoked()));
Q_ASSERT(success); Q_ASSERT(success);

View File

@ -22,6 +22,7 @@
#include <QMetaMethod> #include <QMetaMethod>
#include <QObject> #include <QObject>
#include <QSharedPointer>
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
#include <boost/scoped_ptr.hpp> #include <boost/scoped_ptr.hpp>
@ -62,6 +63,8 @@ class Closure : public QObject, boost::noncopyable {
Closure(QObject* sender, const char* signal, Closure(QObject* sender, const char* signal,
std::tr1::function<void()> callback); std::tr1::function<void()> callback);
virtual ~Closure();
private slots: private slots:
void Invoked(); void Invoked();
void Cleanup(); void Cleanup();
@ -78,6 +81,46 @@ class Closure : public QObject, boost::noncopyable {
boost::scoped_ptr<const ClosureArgumentWrapper> val3_; boost::scoped_ptr<const ClosureArgumentWrapper> val3_;
}; };
class SharedPointerWrapper {
public:
virtual ~SharedPointerWrapper() {}
virtual void* data() const = 0;
};
template<typename T>
class SharedPointer : public SharedPointerWrapper {
public:
SharedPointer(QSharedPointer<T> ptr)
: ptr_(ptr) {
}
void* data() const {
return ptr_.data();
}
private:
QSharedPointer<T> ptr_;
};
// For use with a QSharedPointer as a sender.
class SharedClosure : public Closure {
Q_OBJECT
public:
SharedClosure(SharedPointerWrapper* sender, const char* signal,
QObject* receiver, const char* slot,
const ClosureArgumentWrapper* val0 = 0,
const ClosureArgumentWrapper* val1 = 0,
const ClosureArgumentWrapper* val2 = 0,
const ClosureArgumentWrapper* val3 = 0)
: Closure(reinterpret_cast<QObject*>(sender->data()), signal,
receiver, slot,
val0, val1, val2, val3),
shared_sender_(sender) {
}
private:
boost::scoped_ptr<SharedPointerWrapper> shared_sender_;
};
#define C_ARG(type, data) new ClosureArgument<type>(data) #define C_ARG(type, data) new ClosureArgument<type>(data)
Closure* NewClosure( Closure* NewClosure(
@ -86,6 +129,16 @@ Closure* NewClosure(
QObject* receiver, QObject* receiver,
const char* slot); const char* slot);
template <typename T>
Closure* NewClosure(
QSharedPointer<T> sender,
const char* signal,
QObject* receiver,
const char* slot) {
Q_ASSERT(sender.data()->metaObject()); // Static check for something QObject-like.
return new SharedClosure(new SharedPointer<T>(sender), signal, receiver, slot);
}
template <typename T> template <typename T>
Closure* NewClosure( Closure* NewClosure(
QObject* sender, QObject* sender,

View File

@ -63,6 +63,7 @@ set(TESTUTILS-SOURCES
set(TESTUTILS-MOC-HEADERS set(TESTUTILS-MOC-HEADERS
mock_networkaccessmanager.h mock_networkaccessmanager.h
test_utils.h
testobjectdecorators.h testobjectdecorators.h
) )
@ -138,6 +139,7 @@ add_test_file(song_test.cpp false)
add_test_file(translations_test.cpp false) add_test_file(translations_test.cpp false)
add_test_file(utilities_test.cpp false) add_test_file(utilities_test.cpp false)
#add_test_file(xspfparser_test.cpp false) #add_test_file(xspfparser_test.cpp false)
add_test_file(closure_test.cpp false)
#if(LINUX AND HAVE_DBUS) #if(LINUX AND HAVE_DBUS)
# add_test_file(mpris1_test.cpp true) # add_test_file(mpris1_test.cpp true)

67
tests/closure_test.cpp Normal file
View File

@ -0,0 +1,67 @@
#include "gtest/gtest.h"
#include <QCoreApplication>
#include <QPointer>
#include <QSharedPointer>
#include <QSignalSpy>
#include "core/closure.h"
#include "test_utils.h"
class ClosureTest : public ::testing::Test {
};
TEST_F(ClosureTest, ClosureInvokesReceiver) {
TestQObject sender;
TestQObject receiver;
Closure* closure = NewClosure(
&sender, SIGNAL(Emitted()),
&receiver, SLOT(Invoke()));
EXPECT_EQ(0, receiver.invoked());
sender.Emit();
EXPECT_EQ(1, receiver.invoked());
}
TEST_F(ClosureTest, ClosureDeletesSelf) {
TestQObject sender;
TestQObject receiver;
Closure* closure = NewClosure(
&sender, SIGNAL(Emitted()),
&receiver, SLOT(Invoke()));
QSignalSpy spy(closure, SIGNAL(destroyed()));
EXPECT_EQ(0, receiver.invoked());
sender.Emit();
EXPECT_EQ(1, receiver.invoked());
EXPECT_EQ(0, spy.count());
QEventLoop loop;
QObject::connect(closure, SIGNAL(destroyed()), &loop, SLOT(quit()));
loop.exec();
EXPECT_EQ(1, spy.count());
}
TEST_F(ClosureTest, ClosureDoesNotCrashWithSharedPointerSender) {
TestQObject receiver;
TestQObject* sender;
boost::scoped_ptr<QSignalSpy> spy;
QPointer<Closure> closure;
{
QSharedPointer<TestQObject> sender_shared(new TestQObject);
sender = sender_shared.data();
closure = QPointer<Closure>(NewClosure(
sender_shared, SIGNAL(Emitted()),
&receiver, SLOT(Invoke())));
spy.reset(new QSignalSpy(sender, SIGNAL(destroyed())));
}
EXPECT_EQ(0, receiver.invoked());
sender->Emit();
EXPECT_EQ(1, receiver.invoked());
EXPECT_EQ(0, spy->count());
QEventLoop loop;
QObject::connect(sender, SIGNAL(destroyed()), &loop, SLOT(quit()));
loop.exec();
EXPECT_EQ(1, spy->count());
EXPECT_TRUE(closure.isNull());
}

View File

@ -65,3 +65,16 @@ TemporaryResource::TemporaryResource(const QString& filename) {
reset(); reset();
} }
TestQObject::TestQObject(QObject* parent)
: QObject(parent),
invoked_(0) {
}
void TestQObject::Emit() {
emit Emitted();
}
void TestQObject::Invoke() {
++invoked_;
}

View File

@ -62,4 +62,23 @@ public:
TemporaryResource(const QString& filename); TemporaryResource(const QString& filename);
}; };
class TestQObject : public QObject {
Q_OBJECT
public:
TestQObject(QObject* parent = 0);
void Emit();
int invoked() const { return invoked_; }
signals:
void Emitted();
public slots:
void Invoke();
private:
int invoked_;
};
#endif // TEST_UTILS_H #endif // TEST_UTILS_H