Merge branch 'closure'
This commit is contained in:
commit
46e930f5c9
|
@ -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);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
|
@ -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_;
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue