mirror of
https://github.com/simonbrunel/qtpromise.git
synced 2025-07-01 23:11:47 +08:00
Add support for creating promises from Qt signals (#25)
Introduce a new `QtPromise::connect()` helper that allows to create a promise resolved from a single signal and optionally, rejected by another one (from a different object or not). The promise type is determined by the type of the first signal argument (other arguments are currently ignored). A `QPromise<void>` is returned if the resolve signal doesn't provide any argument. If the rejection is emitted before the promise is resolved, the promise will be rejected with the value of the first argument (other arguments being ignored). If the rejection signal doesn't provide any argument, the promise will be rejected with `QPromiseUndefinedException` if the signal is emitted. Additionally, the promise will be automatically rejected with `QPromiseContextException` if the source object is destroyed before the promise is resolved.
This commit is contained in:
@ -15,6 +15,7 @@ class tst_exceptions : public QObject
|
||||
|
||||
private Q_SLOTS:
|
||||
void canceled();
|
||||
void context();
|
||||
void timeout();
|
||||
void undefined();
|
||||
|
||||
@ -41,6 +42,11 @@ void tst_exceptions::canceled()
|
||||
verify<QPromiseCanceledException>();
|
||||
}
|
||||
|
||||
void tst_exceptions::context()
|
||||
{
|
||||
verify<QPromiseContextException>();
|
||||
}
|
||||
|
||||
void tst_exceptions::timeout()
|
||||
{
|
||||
verify<QPromiseTimeoutException>();
|
||||
|
4
tests/auto/qtpromise/helpers/connect/connect.pro
Normal file
4
tests/auto/qtpromise/helpers/connect/connect.pro
Normal file
@ -0,0 +1,4 @@
|
||||
TARGET = tst_helpers_connect
|
||||
SOURCES += $$PWD/tst_connect.cpp
|
||||
|
||||
include(../../qtpromise.pri)
|
211
tests/auto/qtpromise/helpers/connect/tst_connect.cpp
Normal file
211
tests/auto/qtpromise/helpers/connect/tst_connect.cpp
Normal file
@ -0,0 +1,211 @@
|
||||
#include "../../shared/object.h"
|
||||
#include "../../shared/utils.h"
|
||||
|
||||
// QtPromise
|
||||
#include <QtPromise>
|
||||
|
||||
// Qt
|
||||
#include <QtTest>
|
||||
|
||||
using namespace QtPromise;
|
||||
|
||||
class tst_helpers_connect : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
// connect(QObject* sender, Signal resolver)
|
||||
void resolveOneSenderNoArg();
|
||||
void resolveOneSenderOneArg();
|
||||
void resolveOneSenderManyArgs();
|
||||
|
||||
// connect(QObject* sender, Signal resolver, Signal rejecter)
|
||||
void rejectOneSenderNoArg();
|
||||
void rejectOneSenderOneArg();
|
||||
void rejectOneSenderManyArgs();
|
||||
void rejectOneSenderDestroyed();
|
||||
|
||||
// connect(QObject* s0, Signal resolver, QObject* s1, Signal rejecter)
|
||||
void rejectTwoSendersNoArg();
|
||||
void rejectTwoSendersOneArg();
|
||||
void rejectTwoSendersManyArgs();
|
||||
void rejectTwoSendersDestroyed();
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_helpers_connect)
|
||||
#include "tst_connect.moc"
|
||||
|
||||
void tst_helpers_connect::resolveOneSenderNoArg()
|
||||
{
|
||||
Object sender;
|
||||
QtPromisePrivate::qtpromise_defer([&]() {
|
||||
Q_EMIT sender.noArgSignal();
|
||||
});
|
||||
|
||||
auto p = QtPromise::connect(&sender, &Object::noArgSignal);
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
|
||||
QCOMPARE(sender.hasConnections(), true);
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, -1, 42), 42);
|
||||
QCOMPARE(sender.hasConnections(), false);
|
||||
}
|
||||
|
||||
void tst_helpers_connect::resolveOneSenderOneArg()
|
||||
{
|
||||
Object sender;
|
||||
QtPromisePrivate::qtpromise_defer([&]() {
|
||||
Q_EMIT sender.oneArgSignal("foo");
|
||||
});
|
||||
|
||||
auto p = QtPromise::connect(&sender, &Object::oneArgSignal);
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QString>>::value));
|
||||
QCOMPARE(sender.hasConnections(), true);
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, QString()), QString("foo"));
|
||||
QCOMPARE(sender.hasConnections(), false);
|
||||
}
|
||||
|
||||
void tst_helpers_connect::resolveOneSenderManyArgs()
|
||||
{
|
||||
Object sender;
|
||||
QtPromisePrivate::qtpromise_defer([&]() {
|
||||
Q_EMIT sender.twoArgsSignal(42, "foo");
|
||||
});
|
||||
|
||||
auto p = QtPromise::connect(&sender, &Object::twoArgsSignal);
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int>>::value));
|
||||
QCOMPARE(sender.hasConnections(), true);
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, -1), 42);
|
||||
QCOMPARE(sender.hasConnections(), false);
|
||||
}
|
||||
|
||||
void tst_helpers_connect::rejectOneSenderNoArg()
|
||||
{
|
||||
Object sender;
|
||||
QtPromisePrivate::qtpromise_defer([&]() {
|
||||
Q_EMIT sender.noArgSignal();
|
||||
});
|
||||
|
||||
auto p = QtPromise::connect(&sender, &Object::oneArgSignal, &Object::noArgSignal);
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QString>>::value));
|
||||
QCOMPARE(sender.hasConnections(), true);
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForRejected<QPromiseUndefinedException>(p), true);
|
||||
QCOMPARE(sender.hasConnections(), false);
|
||||
}
|
||||
|
||||
void tst_helpers_connect::rejectOneSenderOneArg()
|
||||
{
|
||||
Object sender;
|
||||
QtPromisePrivate::qtpromise_defer([&]() {
|
||||
Q_EMIT sender.oneArgSignal("bar");
|
||||
});
|
||||
|
||||
auto p = QtPromise::connect(&sender, &Object::noArgSignal, &Object::oneArgSignal);
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
|
||||
QCOMPARE(sender.hasConnections(), true);
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForError(p, QString()), QString("bar"));
|
||||
QCOMPARE(sender.hasConnections(), false);
|
||||
}
|
||||
|
||||
void tst_helpers_connect::rejectOneSenderManyArgs()
|
||||
{
|
||||
Object sender;
|
||||
QtPromisePrivate::qtpromise_defer([&]() {
|
||||
Q_EMIT sender.twoArgsSignal(42, "bar");
|
||||
});
|
||||
|
||||
auto p = QtPromise::connect(&sender, &Object::noArgSignal, &Object::twoArgsSignal);
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
|
||||
QCOMPARE(sender.hasConnections(), true);
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForError(p, -1), 42);
|
||||
QCOMPARE(sender.hasConnections(), false);
|
||||
}
|
||||
|
||||
void tst_helpers_connect::rejectOneSenderDestroyed()
|
||||
{
|
||||
Object* sender = new Object();
|
||||
QtPromisePrivate::qtpromise_defer([&]() {
|
||||
sender->deleteLater();
|
||||
});
|
||||
|
||||
auto p = QtPromise::connect(sender, &Object::twoArgsSignal);
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int>>::value));
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForRejected<QPromiseContextException>(p), true);
|
||||
}
|
||||
|
||||
void tst_helpers_connect::rejectTwoSendersNoArg()
|
||||
{
|
||||
Object s0, s1;
|
||||
QtPromisePrivate::qtpromise_defer([&]() {
|
||||
Q_EMIT s1.noArgSignal();
|
||||
});
|
||||
|
||||
auto p = QtPromise::connect(&s0, &Object::noArgSignal, &s1, &Object::noArgSignal);
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
|
||||
QCOMPARE(s0.hasConnections(), true);
|
||||
QCOMPARE(s1.hasConnections(), true);
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForRejected<QPromiseUndefinedException>(p), true);
|
||||
QCOMPARE(s0.hasConnections(), false);
|
||||
QCOMPARE(s1.hasConnections(), false);
|
||||
}
|
||||
|
||||
void tst_helpers_connect::rejectTwoSendersOneArg()
|
||||
{
|
||||
Object s0, s1;
|
||||
QtPromisePrivate::qtpromise_defer([&]() {
|
||||
Q_EMIT s1.oneArgSignal("bar");
|
||||
});
|
||||
|
||||
auto p = QtPromise::connect(&s0, &Object::noArgSignal, &s1, &Object::oneArgSignal);
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
|
||||
QCOMPARE(s0.hasConnections(), true);
|
||||
QCOMPARE(s1.hasConnections(), true);
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForError(p, QString()), QString("bar"));
|
||||
QCOMPARE(s0.hasConnections(), false);
|
||||
QCOMPARE(s1.hasConnections(), false);
|
||||
}
|
||||
|
||||
void tst_helpers_connect::rejectTwoSendersManyArgs()
|
||||
{
|
||||
Object s0, s1;
|
||||
QtPromisePrivate::qtpromise_defer([&]() {
|
||||
Q_EMIT s1.twoArgsSignal(42, "bar");
|
||||
});
|
||||
|
||||
auto p = QtPromise::connect(&s0, &Object::noArgSignal, &s1, &Object::twoArgsSignal);
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
|
||||
QCOMPARE(s0.hasConnections(), true);
|
||||
QCOMPARE(s1.hasConnections(), true);
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForError(p, -1), 42);
|
||||
QCOMPARE(s0.hasConnections(), false);
|
||||
QCOMPARE(s1.hasConnections(), false);
|
||||
}
|
||||
|
||||
void tst_helpers_connect::rejectTwoSendersDestroyed()
|
||||
{
|
||||
Object* s0 = new Object();
|
||||
Object* s1 = new Object();
|
||||
|
||||
QtPromisePrivate::qtpromise_defer([&]() {
|
||||
QObject::connect(s1, &QObject::destroyed, [&]() {
|
||||
// Let's first delete s1, then resolve s0 and make sure
|
||||
// we don't reject when the rejecter object is destroyed.
|
||||
Q_EMIT s0->noArgSignal();
|
||||
});
|
||||
|
||||
s1->deleteLater();
|
||||
});
|
||||
|
||||
auto p = QtPromise::connect(s0, &Object::noArgSignal, s1, &Object::twoArgsSignal);
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, -1, 42), 42);
|
||||
}
|
@ -2,6 +2,7 @@ TEMPLATE = subdirs
|
||||
SUBDIRS += \
|
||||
all \
|
||||
attempt \
|
||||
connect \
|
||||
each \
|
||||
filter \
|
||||
map \
|
||||
|
@ -0,0 +1,4 @@
|
||||
TARGET = tst_qpromiseconnections
|
||||
SOURCES += $$PWD/tst_qpromiseconnections.cpp
|
||||
|
||||
include(../qtpromise.pri)
|
@ -0,0 +1,81 @@
|
||||
#include "../shared/object.h"
|
||||
#include "../shared/utils.h"
|
||||
|
||||
// QtPromise
|
||||
#include <QtPromise>
|
||||
|
||||
// Qt
|
||||
#include <QtTest>
|
||||
|
||||
using namespace QtPromise;
|
||||
|
||||
class tst_qpromiseconnections : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void connections();
|
||||
void destruction();
|
||||
void senderDestroyed();
|
||||
|
||||
}; // class tst_qpromiseconnections
|
||||
|
||||
QTEST_MAIN(tst_qpromiseconnections)
|
||||
#include "tst_qpromiseconnections.moc"
|
||||
|
||||
void tst_qpromiseconnections::connections()
|
||||
{
|
||||
Object sender;
|
||||
|
||||
QPromiseConnections connections;
|
||||
QCOMPARE(sender.hasConnections(), false);
|
||||
QCOMPARE(connections.count(), 0);
|
||||
|
||||
connections << connect(&sender, &Object::noArgSignal, [=]() {});
|
||||
QCOMPARE(sender.hasConnections(), true);
|
||||
QCOMPARE(connections.count(), 1);
|
||||
|
||||
connections << connect(&sender, &Object::twoArgsSignal, [=]() {});
|
||||
QCOMPARE(sender.hasConnections(), true);
|
||||
QCOMPARE(connections.count(), 2);
|
||||
|
||||
connections.disconnect();
|
||||
QCOMPARE(sender.hasConnections(), false);
|
||||
QCOMPARE(connections.count(), 0);
|
||||
}
|
||||
|
||||
void tst_qpromiseconnections::destruction()
|
||||
{
|
||||
Object sender;
|
||||
|
||||
{
|
||||
QPromiseConnections connections;
|
||||
QCOMPARE(sender.hasConnections(), false);
|
||||
QCOMPARE(connections.count(), 0);
|
||||
|
||||
connections << connect(&sender, &Object::noArgSignal, [=]() {});
|
||||
QCOMPARE(sender.hasConnections(), true);
|
||||
QCOMPARE(connections.count(), 1);
|
||||
}
|
||||
|
||||
QCOMPARE(sender.hasConnections(), false);
|
||||
}
|
||||
|
||||
void tst_qpromiseconnections::senderDestroyed()
|
||||
{
|
||||
QPromiseConnections connections;
|
||||
QCOMPARE(connections.count(), 0);
|
||||
|
||||
{
|
||||
Object sender;
|
||||
QCOMPARE(sender.hasConnections(), false);
|
||||
|
||||
connections << connect(&sender, &Object::noArgSignal, [=]() {});
|
||||
QCOMPARE(sender.hasConnections(), true);
|
||||
QCOMPARE(connections.count(), 1);
|
||||
}
|
||||
|
||||
// should not throw
|
||||
connections.disconnect();
|
||||
QCOMPARE(connections.count(), 0);
|
||||
}
|
@ -21,6 +21,7 @@ coverage {
|
||||
}
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/shared/object.h \
|
||||
$$PWD/shared/utils.h
|
||||
|
||||
include(../../../qtpromise.pri)
|
||||
|
@ -5,5 +5,6 @@ SUBDIRS += \
|
||||
future \
|
||||
helpers \
|
||||
qpromise \
|
||||
qpromiseconnections \
|
||||
requirements \
|
||||
thread
|
||||
|
25
tests/auto/qtpromise/shared/object.h
Normal file
25
tests/auto/qtpromise/shared/object.h
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef QTPROMISE_TESTS_AUTO_SHARED_SENDER_H
|
||||
#define QTPROMISE_TESTS_AUTO_SHARED_SENDER_H
|
||||
|
||||
// Qt
|
||||
#include <QObject>
|
||||
|
||||
class Object : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
bool hasConnections() const { return m_connections > 0; }
|
||||
|
||||
Q_SIGNALS:
|
||||
void noArgSignal();
|
||||
void oneArgSignal(const QString& v);
|
||||
void twoArgsSignal(int v1, const QString& v0);
|
||||
|
||||
protected:
|
||||
int m_connections = 0;
|
||||
void connectNotify(const QMetaMethod&) Q_DECL_OVERRIDE { ++m_connections; }
|
||||
void disconnectNotify(const QMetaMethod&) Q_DECL_OVERRIDE { --m_connections; }
|
||||
};
|
||||
|
||||
#endif // ifndef QTPROMISE_TESTS_AUTO_SHARED_SENDER_H
|
Reference in New Issue
Block a user