diff options
author | Kitsune Ral <Kitsune-Ral@users.sf.net> | 2019-10-23 11:46:56 +0900 |
---|---|---|
committer | Kitsune Ral <Kitsune-Ral@users.sf.net> | 2019-10-23 11:46:56 +0900 |
commit | 8a574f8727cc8b1c91acf0840e99c9382c289098 (patch) | |
tree | 8d6602f9ae0c8deaaa405cac03f27fc00f7f695e /examples/qmc-example.cpp | |
parent | 15bd23eb3ac1d1d043b7492a60748981dcef4627 (diff) | |
download | libquotient-8a574f8727cc8b1c91acf0840e99c9382c289098.tar.gz libquotient-8a574f8727cc8b1c91acf0840e99c9382c289098.zip |
Remove the old source file
Diffstat (limited to 'examples/qmc-example.cpp')
-rw-r--r-- | examples/qmc-example.cpp | 619 |
1 files changed, 0 insertions, 619 deletions
diff --git a/examples/qmc-example.cpp b/examples/qmc-example.cpp deleted file mode 100644 index bf4d04c7..00000000 --- a/examples/qmc-example.cpp +++ /dev/null @@ -1,619 +0,0 @@ - -#include "connection.h" -#include "room.h" -#include "user.h" - -#include "csapi/joining.h" -#include "csapi/leaving.h" -#include "csapi/room_send.h" - -#include "events/reactionevent.h" -#include "events/simplestateevents.h" - -#include <QtCore/QCoreApplication> -#include <QtCore/QFileInfo> -#include <QtCore/QStringBuilder> -#include <QtCore/QTemporaryFile> -#include <QtCore/QTimer> - -#include <functional> -#include <iostream> - -using namespace Quotient; -using std::cout; -using std::endl; -using namespace std::placeholders; - -class QMCTest : public QObject { -public: - QMCTest(Connection* conn, QString testRoomName, QString source); - -private slots: - // clang-format off - void setupAndRun(); - void onNewRoom(Room* r); - void run(); - void doTests(); - void loadMembers(); - void sendMessage(); - void sendReaction(const QString& targetEvtId); - void sendFile(); - void checkFileSendingOutcome(const QString& txnId, - const QString& fileName); - void setTopic(); - void addAndRemoveTag(); - void sendAndRedact(); - bool checkRedactionOutcome(const QString& evtIdToRedact); - void markDirectChat(); - void checkDirectChatOutcome( - const Connection::DirectChatsMap& added); - void conclude(); - void finalize(); - // clang-format on - -private: - QScopedPointer<Connection, QScopedPointerDeleteLater> c; - QStringList running; - QStringList succeeded; - QStringList failed; - QString origin; - QString targetRoomName; - Room* targetRoom = nullptr; - - bool validatePendingEvent(const QString& txnId); -}; - -#define QMC_CHECK(description, condition) \ - { \ - Q_ASSERT(running.removeOne(description)); \ - if (!!(condition)) { \ - succeeded.push_back(description); \ - cout << (description) << " successful" << endl; \ - if (targetRoom) \ - targetRoom->postMessage(origin % ": " % (description) \ - % " successful", \ - MessageEventType::Notice); \ - } else { \ - failed.push_back(description); \ - cout << (description) << " FAILED" << endl; \ - if (targetRoom) \ - targetRoom->postPlainText(origin % ": " % (description) \ - % " FAILED"); \ - } \ - } - -bool QMCTest::validatePendingEvent(const QString& txnId) -{ - auto it = targetRoom->findPendingEvent(txnId); - return it != targetRoom->pendingEvents().end() - && it->deliveryStatus() == EventStatus::Submitted - && (*it)->transactionId() == txnId; -} - -QMCTest::QMCTest(Connection* conn, QString testRoomName, QString source) - : c(conn), origin(std::move(source)), targetRoomName(std::move(testRoomName)) -{ - if (!origin.isEmpty()) - cout << "Origin for the test message: " << origin.toStdString() << endl; - if (!targetRoomName.isEmpty()) - cout << "Test room name: " << targetRoomName.toStdString() << endl; - - connect(c.data(), &Connection::connected, this, &QMCTest::setupAndRun); - connect(c.data(), &Connection::loadedRoomState, this, &QMCTest::onNewRoom); - // Big countdown watchdog - QTimer::singleShot(180000, this, &QMCTest::conclude); -} - -void QMCTest::setupAndRun() -{ - Q_ASSERT(!c->homeserver().isEmpty() && c->homeserver().isValid()); - Q_ASSERT(c->domain() == c->userId().section(':', 1)); - cout << "Connected, server: " - << c->homeserver().toDisplayString().toStdString() << endl; - cout << "Access token: " << c->accessToken().toStdString() << endl; - - if (!targetRoomName.isEmpty()) { - cout << "Joining " << targetRoomName.toStdString() << endl; - running.push_back("Join room"); - auto joinJob = c->joinRoom(targetRoomName); - connect(joinJob, &BaseJob::failure, this, [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] { - targetRoom = c->room(joinJob->roomId(), JoinState::Join); - QMC_CHECK("Join room", targetRoom != nullptr); - - run(); - }); - } else - run(); -} - -void QMCTest::onNewRoom(Room* r) -{ - cout << "New room: " << r->id().toStdString() << endl - << " Name: " << r->name().toStdString() << endl - << " Canonical alias: " << r->canonicalAlias().toStdString() << endl - << endl; - connect(r, &Room::aboutToAddNewMessages, r, [r](RoomEventsRange timeline) { - cout << timeline.size() << " new event(s) in room " - << r->canonicalAlias().toStdString() << endl; - // for (const auto& item: timeline) - // { - // cout << "From: " - // << r->roomMembername(item->senderId()).toStdString() - // << endl << "Timestamp:" - // << item->timestamp().toString().toStdString() << endl - // << "JSON:" << endl << - // item->originalJson().toStdString() << endl; - // } - }); -} - -void QMCTest::run() -{ - c->setLazyLoading(true); - c->syncLoop(); - connectSingleShot(c.data(), &Connection::syncDone, this, &QMCTest::doTests); - connect(c.data(), &Connection::syncDone, c.data(), [this] { - cout << "Sync complete, " << running.size() - << " test(s) in the air: " << running.join(", ").toStdString() - << endl; - if (running.isEmpty()) - conclude(); - }); -} - -void QMCTest::doTests() -{ - cout << "Starting tests" << endl; - - loadMembers(); - // Add here tests not requiring the test room - if (targetRoomName.isEmpty()) - return; - - sendMessage(); - sendFile(); - setTopic(); - addAndRemoveTag(); - sendAndRedact(); - markDirectChat(); - // Add here tests with the test room -} - -void QMCTest::loadMembers() -{ - running.push_back("Loading members"); - auto* r = c->roomByAlias(QStringLiteral("#quotient:matrix.org"), - JoinState::Join); - if (!r) { - cout << "#test:matrix.org is not found in the test user's rooms" << endl; - QMC_CHECK("Loading members", false); - return; - } - // It's not exactly correct because an arbitrary server might not support - // lazy loading; but in the absence of capabilities framework we assume - // it does. - if (r->memberNames().size() >= r->joinedCount()) { - cout << "Lazy loading doesn't seem to be enabled" << endl; - QMC_CHECK("Loading members", false); - return; - } - r->setDisplayed(); - connect(r, &Room::allMembersLoaded, [this, r] { - QMC_CHECK("Loading members", - r->memberNames().size() >= r->joinedCount()); - }); -} - -void QMCTest::sendMessage() -{ - running.push_back("Message sending"); - cout << "Sending a message" << endl; - auto txnId = targetRoom->postPlainText("Hello, " % origin % " is here"); - if (!validatePendingEvent(txnId)) { - cout << "Invalid pending event right after submitting" << endl; - QMC_CHECK("Message sending", false); - return; - } - - 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 false; - - QMC_CHECK("Message sending", - is<RoomMessageEvent>(*evt) && !evt->id().isEmpty() - && pendingEvents[size_t(pendingIdx)]->transactionId() - == evt->transactionId()); - sendReaction(evt->id()); - return true; - }); -} - -void QMCTest::sendReaction(const QString& targetEvtId) -{ - running.push_back("Reaction sending"); - cout << "Reacting to the newest message in the room" << endl; - Q_ASSERT(targetRoom->timelineSize() > 0); - const auto key = QStringLiteral("+1"); - auto txnId = targetRoom->postReaction(targetEvtId, key); - if (!validatePendingEvent(txnId)) { - cout << "Invalid pending event right after submitting" << endl; - QMC_CHECK("Reaction sending", false); - return; - } - - // TODO: Check that it came back as a reaction event and that it attached to - // the right event - connectUntil( - targetRoom, &Room::updatedEvent, this, - [this, txnId, key, targetEvtId](const QString& actualTargetEvtId) { - if (actualTargetEvtId != targetEvtId) - return false; - const auto reactions = targetRoom->relatedEvents( - targetEvtId, EventRelation::Annotation()); - // It's a test room, assuming no interference there should - // be exactly one reaction - if (reactions.size() != 1) { - QMC_CHECK("Reaction sending", false); - } else { - const auto* evt = - eventCast<const ReactionEvent>(reactions.back()); - QMC_CHECK("Reaction sending", - is<ReactionEvent>(*evt) && !evt->id().isEmpty() - && evt->relation().key == key - && evt->transactionId() == txnId); - } - return true; - }); -} - -void QMCTest::sendFile() -{ - running.push_back("File sending"); - cout << "Sending a file" << endl; - auto* tf = new QTemporaryFile; - if (!tf->open()) { - cout << "Failed to create a temporary file" << endl; - QMC_CHECK("File sending", false); - return; - } - tf->write("Test"); - tf->close(); - // QFileInfo::fileName brings only the file name; QFile::fileName brings - // the full path - const auto tfName = QFileInfo(*tf).fileName(); - cout << "Sending file" << tfName.toStdString() << endl; - const auto txnId = - targetRoom->postFile("Test file", QUrl::fromLocalFile(tf->fileName())); - if (!validatePendingEvent(txnId)) { - cout << "Invalid pending event right after submitting" << endl; - QMC_CHECK("File sending", false); - delete tf; - return; - } - - // 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; - - delete tf; - - checkFileSendingOutcome(txnId, tfName); - }); - 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); - delete tf; - - QMC_CHECK("File sending", false); - }); -} - -void QMCTest::checkFileSendingOutcome(const QString& txnId, - const QString& fileName) -{ - auto it = targetRoom->findPendingEvent(txnId); - if (it == targetRoom->pendingEvents().end()) { - cout << "Pending file event dropped before upload completion" << endl; - QMC_CHECK("File sending", false); - return; - } - if (it->deliveryStatus() != EventStatus::FileUploaded) { - cout << "Pending file event status upon upload completion is " - << it->deliveryStatus() << " != FileUploaded(" - << EventStatus::FileUploaded << ')' << endl; - QMC_CHECK("File sending", false); - return; - } - - 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 false; - - cout << "File event " << txnId.toStdString() - << " arrived in the timeline" << endl; - visit( - *evt, - [&](const RoomMessageEvent& e) { - QMC_CHECK( - "File sending", - !e.id().isEmpty() - && pendingEvents[size_t(pendingIdx)]->transactionId() - == txnId - && e.hasFileContent() - && e.content()->fileInfo()->originalName == fileName); - }, - [this](const RoomEvent&) { QMC_CHECK("File sending", false); }); - return true; - }); -} - -void QMCTest::setTopic() -{ - static const char* const stateTestName = "State setting test"; - running.push_back(stateTestName); - - const auto newTopic = c->generateTxnId(); // Just a way to get a unique id - targetRoom->setTopic(newTopic); // Sets the state by proper means - const auto fakeTopic = c->generateTxnId(); - const auto fakeTxnId = - targetRoom->postJson(RoomTopicEvent::matrixTypeId(), // Fake state event - RoomTopicEvent(fakeTopic).contentJson()); - - connectUntil(targetRoom, &Room::topicChanged, this, - [this, newTopic] { - if (targetRoom->topic() == newTopic) { - QMC_CHECK(stateTestName, true); - return true; - } - return false; - }); - - // Older Synapses allowed sending fake state events through, although - // did not process them; // https://github.com/matrix-org/synapse/pull/5805 - // changed that and now Synapse 400's in response to fake state events. - // The following two-step approach handles both cases, assuming that - // Room::pendingEventChanged() with EventStatus::ReachedServer is guaranteed - // to be emitted before Room::pendingEventAboutToMerge. - connectUntil( - targetRoom, &Room::pendingEventChanged, this, - [this, fakeTopic, fakeTxnId](int pendingIdx) { - const auto& pendingEvents = targetRoom->pendingEvents(); - Q_ASSERT(pendingIdx >= 0 && pendingIdx < int(pendingEvents.size())); - const auto& evt = pendingEvents[pendingIdx]; - if (evt->transactionId() != fakeTxnId) - return false; - - // If Synapse rejected the event, skip the immunity test. - if (evt.deliveryStatus() == EventStatus::SendingFailed) - return true; - - if (evt.deliveryStatus() != EventStatus::ReachedServer) - return false; - - // All before was just a preparation, this is where the test starts. - static const char* const fakeStateTestName = - "Fake state event immunity test"; - running.push_back(fakeStateTestName); - connectUntil( - targetRoom, &Room::pendingEventAboutToMerge, this, - [this, fakeTopic](const RoomEvent* e, int) { - if (e->contentJson().value("topic").toString() != fakeTopic) - return false; // Wait on for the right event - - QMC_CHECK(fakeStateTestName, !e->isStateEvent()); - return true; - }); - return true; - }); -} - -void QMCTest::addAndRemoveTag() -{ - running.push_back("Tagging test"); - static const auto TestTag = QStringLiteral("org.quotient.test"); - // Pre-requisite - if (targetRoom->tags().contains(TestTag)) - targetRoom->removeTag(TestTag); - - // Connect first because the signal is emitted synchronously. - connect(targetRoom, &Room::tagsChanged, targetRoom, [=] { - cout << "Room " << targetRoom->id().toStdString() - << ", tag(s) changed:" << endl - << " " << targetRoom->tagNames().join(", ").toStdString() << endl; - if (targetRoom->tags().contains(TestTag)) { - cout << "Test tag set, removing it now" << endl; - targetRoom->removeTag(TestTag); - QMC_CHECK("Tagging test", !targetRoom->tags().contains(TestTag)); - disconnect(targetRoom, &Room::tagsChanged, nullptr, nullptr); - } - }); - cout << "Adding a tag" << endl; - targetRoom->addTag(TestTag); -} - -void QMCTest::sendAndRedact() -{ - running.push_back("Redaction"); - cout << "Sending a message to redact" << endl; - auto txnId = targetRoom->postPlainText(origin % ": message to redact"); - if (txnId.isEmpty()) { - QMC_CHECK("Redaction", false); - return; - } - connect(targetRoom, &Room::messageSent, this, - [this, txnId](const QString& tId, const QString& evtId) { - if (tId != txnId) - return; - - cout << "Redacting the message" << endl; - targetRoom->redactEvent(evtId, origin); - - connectUntil(targetRoom, &Room::addedMessages, this, - [this, evtId] { - return checkRedactionOutcome(evtId); - }); - }); -} - -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 false; // Waiting for the next sync - - if ((*it)->isRedacted()) { - cout << "The sync brought already redacted message" << endl; - QMC_CHECK("Redaction", true); - } 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); - return true; - }); - } - return true; -} - -void QMCTest::markDirectChat() -{ - if (targetRoom->directChatUsers().contains(c->user())) { - cout << "Warning: the room is already a direct chat," - " only unmarking will be tested" - << endl; - checkDirectChatOutcome({ { c->user(), targetRoom->id() } }); - return; - } - // Connect first because the signal is emitted synchronously. - connect(c.data(), &Connection::directChatsListChanged, this, - &QMCTest::checkDirectChatOutcome); - cout << "Marking the room as a direct chat" << endl; - c->addToDirectChats(targetRoom, c->user()); -} - -void QMCTest::checkDirectChatOutcome(const Connection::DirectChatsMap& added) -{ - running.push_back("Direct chat test"); - disconnect(c.data(), &Connection::directChatsListChanged, nullptr, nullptr); - if (!targetRoom->isDirectChat()) { - cout << "The room has not been marked as a direct chat" << endl; - QMC_CHECK("Direct chat test", false); - return; - } - if (!added.contains(c->user(), targetRoom->id())) { - cout << "The room has not been listed in new direct chats" << endl; - QMC_CHECK("Direct chat test", false); - return; - } - - cout << "Unmarking the direct chat" << endl; - c->removeFromDirectChats(targetRoom->id(), c->user()); - QMC_CHECK("Direct chat test", !c->isDirectChat(targetRoom->id())); -} - -void QMCTest::conclude() -{ - c->stopSync(); - 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) { - // 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(); -} - -void QMCTest::finalize() -{ - cout << "Logging out" << endl; - c->logout(); - connect(c.data(), &Connection::loggedOut, qApp, [this] { - QCoreApplication::processEvents(); - QCoreApplication::exit(failed.size() + running.size()); - }); -} - -int main(int argc, char* argv[]) -{ - QCoreApplication app(argc, argv); - if (argc < 4) { - cout << "Usage: qmc-example <user> <passwd> <device_name> " - "[<room_alias> [origin]]" - << endl; - return -1; - } - - cout << "Connecting to the server as " << argv[1] << endl; - auto conn = new Connection; - conn->connectToServer(argv[1], argv[2], argv[3]); - QMCTest test { conn, argc >= 5 ? argv[4] : nullptr, - argc >= 6 ? argv[5] : nullptr }; - return app.exec(); -} |