aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-rw-r--r--examples/qmc-example.cpp193
1 files changed, 121 insertions, 72 deletions
diff --git a/examples/qmc-example.cpp b/examples/qmc-example.cpp
index 8fbf4824..421ead27 100644
--- a/examples/qmc-example.cpp
+++ b/examples/qmc-example.cpp
@@ -5,6 +5,7 @@
#include "csapi/room_send.h"
#include "csapi/joining.h"
#include "csapi/leaving.h"
+#include "events/simplestateevents.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QStringBuilder>
@@ -34,14 +35,14 @@ class QMCTest : public QObject
void sendFile();
void checkFileSendingOutcome(const QString& txnId,
const QString& fileName);
+ void setTopic();
void addAndRemoveTag();
void sendAndRedact();
- void checkRedactionOutcome(const QString& evtIdToRedact,
- const QMetaObject::Connection& sc);
+ bool checkRedactionOutcome(const QString& evtIdToRedact);
void markDirectChat();
void checkDirectChatOutcome(
const Connection::DirectChatsMap& added);
- void leave();
+ void conclude();
void finalize();
private:
@@ -95,7 +96,7 @@ QMCTest::QMCTest(Connection* conn, QString testRoomName, QString source)
connect(c.data(), &Connection::connected, this, &QMCTest::setupAndRun);
connect(c.data(), &Connection::loadedRoomState, this, &QMCTest::onNewRoom);
// Big countdown watchdog
- QTimer::singleShot(180000, this, &QMCTest::leave);
+ QTimer::singleShot(180000, this, &QMCTest::conclude);
}
void QMCTest::setupAndRun()
@@ -110,7 +111,7 @@ void QMCTest::setupAndRun()
running.push_back("Join room");
auto joinJob = c->joinRoom(targetRoomName);
connect(joinJob, &BaseJob::failure, this,
- [this] { QMC_CHECK("Join room", false); finalize(); });
+ [this] { QMC_CHECK("Join room", false); conclude(); });
// Connection::joinRoom() creates a Room object upon JoinRoomJob::success
// but this object is empty until the first sync is done.
connect(joinJob, &BaseJob::success, this, [this,joinJob] {
@@ -149,26 +150,12 @@ void QMCTest::run()
c->sync();
connectSingleShot(c.data(), &Connection::syncDone, this, &QMCTest::doTests);
connect(c.data(), &Connection::syncDone, c.data(), [this] {
- cout << "Sync complete, "
- << running.size() << " tests in the air" << endl;
+ cout << "Sync complete, " << running.size() << " test(s) in the air: "
+ << running.join(", ").toStdString() << endl;
if (!running.isEmpty())
c->sync(10000);
- else if (targetRoom)
- {
- // TODO: Waiting for proper futures to come so that it could be:
-// targetRoom->postPlainText(origin % ": All tests finished")
-// .then(this, &QMCTest::leave); // Qt-style
-// .then([this] { leave(); }); // STL-style
- auto txnId =
- targetRoom->postPlainText(origin % ": All tests finished");
- connect(targetRoom, &Room::messageSent, this,
- [this,txnId] (QString serverTxnId) {
- if (txnId == serverTxnId)
- leave();
- });
- }
else
- finalize();
+ conclude();
});
}
@@ -183,6 +170,7 @@ void QMCTest::doTests()
sendMessage();
sendFile();
+ setTopic();
addAndRemoveTag();
sendAndRedact();
markDirectChat();
@@ -230,21 +218,19 @@ void QMCTest::sendMessage()
return;
}
- QMetaObject::Connection sc;
- sc = connect(targetRoom, &Room::pendingEventAboutToMerge, this,
- [this,sc,txnId] (const RoomEvent* evt, int pendingIdx) {
+ connectUntil(targetRoom, &Room::pendingEventAboutToMerge, this,
+ [this,txnId] (const RoomEvent* evt, int pendingIdx) {
const auto& pendingEvents = targetRoom->pendingEvents();
Q_ASSERT(pendingIdx >= 0 && pendingIdx < int(pendingEvents.size()));
if (evt->transactionId() != txnId)
- return;
-
- disconnect(sc);
+ return false;
QMC_CHECK("Message sending",
is<RoomMessageEvent>(*evt) && !evt->id().isEmpty() &&
pendingEvents[size_t(pendingIdx)]->transactionId()
== evt->transactionId());
+ return true;
});
}
@@ -275,30 +261,26 @@ void QMCTest::sendFile()
return;
}
- QMetaObject::Connection scCompleted, scFailed;
- scCompleted = connect(targetRoom, &Room::fileTransferCompleted, this,
- [this,txnId,tf,tfName,scCompleted,scFailed] (const QString& id) {
+ // FIXME: Clean away connections (connectUntil doesn't help here).
+ connect(targetRoom, &Room::fileTransferCompleted, this,
+ [this,txnId,tf,tfName] (const QString& id) {
auto fti = targetRoom->fileTransferInfo(id);
Q_ASSERT(fti.status == FileTransferInfo::Completed);
if (id != txnId)
return;
- disconnect(scCompleted);
- disconnect(scFailed);
delete tf;
checkFileSendingOutcome(txnId, tfName);
});
- scFailed = connect(targetRoom, &Room::fileTransferFailed, this,
- [this,txnId,tf,scCompleted,scFailed]
+ connect(targetRoom, &Room::fileTransferFailed, this,
+ [this,txnId,tf]
(const QString& id, const QString& error) {
if (id != txnId)
return;
targetRoom->postPlainText(origin % ": File upload failed: " % error);
- disconnect(scCompleted);
- disconnect(scFailed);
delete tf;
QMC_CHECK("File sending", false);
@@ -325,18 +307,16 @@ void QMCTest::checkFileSendingOutcome(const QString& txnId,
return;
}
- QMetaObject::Connection sc;
- sc = connect(targetRoom, &Room::pendingEventAboutToMerge, this,
- [this,sc,txnId,fileName] (const RoomEvent* evt, int pendingIdx) {
+ connectUntil(targetRoom, &Room::pendingEventAboutToMerge, this,
+ [this,txnId,fileName] (const RoomEvent* evt, int pendingIdx) {
const auto& pendingEvents = targetRoom->pendingEvents();
Q_ASSERT(pendingIdx >= 0 && pendingIdx < int(pendingEvents.size()));
if (evt->transactionId() != txnId)
- return;
+ return false;
- cout << "Event " << txnId.toStdString()
+ cout << "File event " << txnId.toStdString()
<< " arrived in the timeline" << endl;
- disconnect(sc);
visit(*evt,
[&] (const RoomMessageEvent& e) {
QMC_CHECK("File sending",
@@ -349,6 +329,47 @@ void QMCTest::checkFileSendingOutcome(const QString& txnId,
[this] (const RoomEvent&) {
QMC_CHECK("File sending", false);
});
+ return true;
+ });
+}
+
+void QMCTest::setTopic()
+{
+ static const char* const stateTestName = "State setting test";
+ static const char* const fakeStateTestName = "Fake state event immunity test";
+ running.push_back(stateTestName);
+ running.push_back(fakeStateTestName);
+ auto initialTopic = targetRoom->topic();
+
+ const auto newTopic = c->generateTxnId();
+ targetRoom->setTopic(newTopic); // Sets the state by proper means
+ const auto fakeTopic = c->generateTxnId();
+ targetRoom->postJson(RoomTopicEvent::matrixTypeId(), // Fake state event
+ RoomTopicEvent(fakeTopic).contentJson());
+
+ connectUntil(targetRoom, &Room::topicChanged, this,
+ [this,newTopic,fakeTopic,initialTopic] {
+ if (targetRoom->topic() == newTopic)
+ {
+ QMC_CHECK(stateTestName, true);
+ // Don't reset the topic yet if the negative test still runs
+ if (!running.contains(fakeStateTestName))
+ targetRoom->setTopic(initialTopic);
+
+ return true;
+ }
+ return false;
+ });
+
+ connectUntil(targetRoom, &Room::pendingEventAboutToMerge, this,
+ [this,fakeTopic,initialTopic] (const RoomEvent* e, int) {
+ if (e->contentJson().value("topic").toString() != fakeTopic)
+ return false; // Wait on for the right event
+
+ QMC_CHECK(fakeStateTestName, !e->isStateEvent());
+ if (!running.contains(fakeStateTestName))
+ targetRoom->setTopic(initialTopic);
+ return true;
});
}
@@ -370,7 +391,7 @@ void QMCTest::addAndRemoveTag()
cout << "Test tag set, removing it now" << endl;
targetRoom->removeTag(TestTag);
QMC_CHECK("Tagging test", !targetRoom->tags().contains(TestTag));
- QObject::disconnect(targetRoom, &Room::tagsChanged, nullptr, nullptr);
+ disconnect(targetRoom, &Room::tagsChanged, nullptr, nullptr);
}
});
cout << "Adding a tag" << endl;
@@ -394,41 +415,40 @@ void QMCTest::sendAndRedact()
cout << "Redacting the message" << endl;
targetRoom->redactEvent(evtId, origin);
- QMetaObject::Connection sc;
- sc = connect(targetRoom, &Room::addedMessages, this,
- [this,sc,evtId] { checkRedactionOutcome(evtId, sc); });
+
+ connectUntil(targetRoom, &Room::addedMessages, this,
+ [this,evtId] { return checkRedactionOutcome(evtId); });
});
}
-void QMCTest::checkRedactionOutcome(const QString& evtIdToRedact,
- const QMetaObject::Connection& sc)
+bool QMCTest::checkRedactionOutcome(const QString& evtIdToRedact)
{
// There are two possible (correct) outcomes: either the event comes already
// redacted at the next sync, or the nearest sync completes with
// the unredacted event but the next one brings redaction.
auto it = targetRoom->findInTimeline(evtIdToRedact);
if (it == targetRoom->timelineEdge())
- return; // Waiting for the next sync
+ return false; // Waiting for the next sync
if ((*it)->isRedacted())
{
cout << "The sync brought already redacted message" << endl;
QMC_CHECK("Redaction", true);
- disconnect(sc);
- return;
- }
- cout << "Message came non-redacted with the sync, waiting for redaction"
- << endl;
- connect(targetRoom, &Room::replacedEvent, this,
- [this,evtIdToRedact]
- (const RoomEvent* newEvent, const RoomEvent* oldEvent) {
- if (oldEvent->id() == evtIdToRedact)
- {
+ } else {
+ cout << "Message came non-redacted with the sync, waiting for redaction"
+ << endl;
+ connectUntil(targetRoom, &Room::replacedEvent, this,
+ [this,evtIdToRedact]
+ (const RoomEvent* newEvent, const RoomEvent* oldEvent) {
+ if (oldEvent->id() != evtIdToRedact)
+ return false;
+
QMC_CHECK("Redaction", newEvent->isRedacted() &&
newEvent->redactionReason() == origin);
- disconnect(targetRoom, &Room::replacedEvent, nullptr, nullptr);
- }
- });
+ return true;
+ });
+ }
+ return true;
}
void QMCTest::markDirectChat()
@@ -469,13 +489,47 @@ void QMCTest::checkDirectChatOutcome(const Connection::DirectChatsMap& added)
QMC_CHECK("Direct chat test", !c->isDirectChat(targetRoom->id()));
}
-void QMCTest::leave()
+void QMCTest::conclude()
{
+ auto succeededRec = QString::number(succeeded.size()) + " tests succeeded";
+ if (!failed.isEmpty() || !running.isEmpty())
+ succeededRec += " of " %
+ QString::number(succeeded.size() + failed.size() + running.size()) %
+ " total";
+ QString plainReport = origin % ": Testing complete, " % succeededRec;
+ QString color = failed.isEmpty() && running.isEmpty() ? "00AA00" : "AA0000";
+ QString htmlReport = origin % ": <strong><font data-mx-color='#" % color %
+ "' color='#" % color % "'>Testing complete</font></strong>, " %
+ succeededRec;
+ if (!failed.isEmpty())
+ {
+ plainReport += "\nFAILED: " % failed.join(", ");
+ htmlReport += "<br><strong>Failed:</strong> " % failed.join(", ");
+ }
+ if (!running.isEmpty())
+ {
+ plainReport += "\nDID NOT FINISH: " % running.join(", ");
+ htmlReport +=
+ "<br><strong>Did not finish:</strong> " % running.join(", ");
+ }
+ cout << plainReport.toStdString() << endl;
+
if (targetRoom)
{
- cout << "Leaving the room" << endl;
- connect(targetRoom->leaveRoom(), &BaseJob::finished,
- this, &QMCTest::finalize);
+ // TODO: Waiting for proper futures to come so that it could be:
+// targetRoom->postHtmlText(...)
+// .then(this, &QMCTest::finalize); // Qt-style or
+// .then([this] { finalize(); }); // STL-style
+ auto txnId = targetRoom->postHtmlText(plainReport, htmlReport);
+ connect(targetRoom, &Room::messageSent, this,
+ [this,txnId] (QString serverTxnId) {
+ if (txnId != serverTxnId)
+ return;
+
+ cout << "Leaving the room" << endl;
+ connect(targetRoom->leaveRoom(), &BaseJob::finished,
+ this, &QMCTest::finalize);
+ });
}
else
finalize();
@@ -487,11 +541,6 @@ void QMCTest::finalize()
c->logout();
connect(c.data(), &Connection::loggedOut, qApp,
[this] {
- if (!failed.isEmpty())
- cout << "FAILED: " << failed.join(", ").toStdString() << endl;
- if (!running.isEmpty())
- cout << "DID NOT FINISH: "
- << running.join(", ").toStdString() << endl;
QCoreApplication::processEvents();
QCoreApplication::exit(failed.size() + running.size());
});