aboutsummaryrefslogtreecommitdiff
path: root/quotest/quotest.cpp
diff options
context:
space:
mode:
authorarawaaa <77910862+arawaaa@users.noreply.github.com>2021-12-27 17:35:28 -0600
committerGitHub <noreply@github.com>2021-12-27 17:35:28 -0600
commit7ec3ba834dd313c4408622da30e04cdc6f4cf7c7 (patch)
tree679d7dc5939a229ad46676cd1d7aeaea7a25abce /quotest/quotest.cpp
parent17bf4d180297c7e87363e179b8afa79ddb15dca7 (diff)
parent674e984e459375974f619d0e778d43a2cc928dc3 (diff)
downloadlibquotient-7ec3ba834dd313c4408622da30e04cdc6f4cf7c7.tar.gz
libquotient-7ec3ba834dd313c4408622da30e04cdc6f4cf7c7.zip
Merge branch 'dev' into pinned
Diffstat (limited to 'quotest/quotest.cpp')
-rw-r--r--quotest/quotest.cpp168
1 files changed, 142 insertions, 26 deletions
diff --git a/quotest/quotest.cpp b/quotest/quotest.cpp
index a0914c72..7bd9c5c3 100644
--- a/quotest/quotest.cpp
+++ b/quotest/quotest.cpp
@@ -5,6 +5,7 @@
#include "room.h"
#include "user.h"
#include "uriresolver.h"
+#include "networkaccessmanager.h"
#include "csapi/joining.h"
#include "csapi/leaving.h"
@@ -20,6 +21,8 @@
#include <QtCore/QStringBuilder>
#include <QtCore/QTemporaryFile>
#include <QtCore/QTimer>
+#include <QtConcurrent/QtConcurrent>
+#include <QtNetwork/QNetworkReply>
#include <functional>
#include <iostream>
@@ -48,7 +51,7 @@ private:
QByteArrayList running {}, succeeded {}, failed {};
};
-using TestToken = QByteArray; // return value of QMetaMethod::name
+using TestToken = decltype(std::declval<QMetaMethod>().name());
Q_DECLARE_METATYPE(TestToken)
// For now, the token itself is the test name but that may change.
@@ -105,6 +108,7 @@ private slots:
TEST_DECL(addAndRemoveTag)
TEST_DECL(markDirectChat)
TEST_DECL(visitResources)
+ TEST_DECL(prettyPrintTests)
// Add more tests above here
public:
@@ -137,7 +141,7 @@ private:
// connectUntil() to break the QMetaObject::Connection upon finishing the test
// item.
#define FINISH_TEST(Condition) \
- return (finishTest(thisTest, Condition, __FILE__, __LINE__), true)
+ return (finishTest(thisTest, (Condition), __FILE__, __LINE__), true)
#define FAIL_TEST() FINISH_TEST(false)
@@ -210,6 +214,7 @@ TestManager::TestManager(int& argc, char** argv)
// Big countdown watchdog
QTimer::singleShot(180000, this, [this] {
+ clog << "Time is up, stopping the session\n";
if (testSuite)
conclude();
else
@@ -241,7 +246,7 @@ void TestManager::setupAndRun()
clog << "Sync " << ++i << " complete" << endl;
if (auto* r = testSuite->room()) {
clog << "Test room timeline size = " << r->timelineSize();
- if (r->pendingEvents().empty())
+ if (!r->pendingEvents().empty())
clog << ", pending size = " << r->pendingEvents().size();
clog << endl;
}
@@ -397,15 +402,16 @@ TEST_IMPL(sendFile)
}
tf->write("Test");
tf->close();
+ QFileInfo tfi { *tf };
// QFileInfo::fileName brings only the file name; QFile::fileName brings
// the full path
- const auto tfName = QFileInfo(*tf).fileName();
+ const auto tfName = tfi.fileName();
clog << "Sending file " << tfName.toStdString() << endl;
- const auto txnId =
- targetRoom->postFile("Test file", QUrl::fromLocalFile(tf->fileName()));
+ const auto txnId = targetRoom->postFile(
+ "Test file", new EventContent::FileContent(tfi));
if (!validatePendingEvent(txnId)) {
clog << "Invalid pending event right after submitting" << endl;
- delete tf;
+ tf->deleteLater();
FAIL_TEST();
}
@@ -434,6 +440,39 @@ TEST_IMPL(sendFile)
return false;
}
+// Can be replaced with a lambda once QtConcurrent is able to resolve return
+// types from lambda invocations (Qt 6 can, not sure about earlier)
+struct DownloadRunner {
+ QUrl url;
+
+ using result_type = QNetworkReply::NetworkError;
+
+ QNetworkReply::NetworkError operator()(int) const
+ {
+ QEventLoop el;
+ QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> reply {
+ NetworkAccessManager::instance()->get(QNetworkRequest(url))
+ };
+ QObject::connect(
+ reply.data(), &QNetworkReply::finished, &el, [&el] { el.exit(); },
+ Qt::QueuedConnection);
+ el.exec();
+ return reply->error();
+ }
+};
+
+bool testDownload(const QUrl& url)
+{
+ // Move out actual test from the multithreaded code
+ // to help debugging
+ auto results = QtConcurrent::blockingMapped(QVector<int> { 1, 2, 3 },
+ DownloadRunner { url });
+ return std::all_of(results.cbegin(), results.cend(),
+ [](QNetworkReply::NetworkError ne) {
+ return ne == QNetworkReply::NoError;
+ });
+}
+
bool TestSuite::checkFileSendingOutcome(const TestToken& thisTest,
const QString& txnId,
const QString& fileName)
@@ -460,18 +499,19 @@ bool TestSuite::checkFileSendingOutcome(const TestToken& thisTest,
clog << "File event " << txnId.toStdString()
<< " arrived in the timeline" << endl;
- // This part tests visit()
- return visit(
+ // This part tests switchOnType()
+ return switchOnType(
*evt,
[&](const RoomMessageEvent& e) {
- // TODO: actually try to download it to check, e.g., #366
- // (and #368 would help to test against bad file names).
+ // TODO: check #366 once #368 is implemented
FINISH_TEST(
!e.id().isEmpty()
- && pendingEvents[size_t(pendingIdx)]->transactionId()
- == txnId
- && e.hasFileContent()
- && e.content()->fileInfo()->originalName == fileName);
+ && pendingEvents[size_t(pendingIdx)]->transactionId()
+ == txnId
+ && e.hasFileContent()
+ && e.content()->fileInfo()->originalName == fileName
+ && testDownload(targetRoom->connection()->makeMediaUrl(
+ e.content()->fileInfo()->url)));
},
[this, thisTest](const RoomEvent&) { FAIL_TEST(); });
});
@@ -498,14 +538,32 @@ TEST_IMPL(setTopic)
TEST_IMPL(changeName)
{
- auto* const localUser = connection()->user();
- const auto& newName = connection()->generateTxnId(); // See setTopic()
- clog << "Renaming the user to " << newName.toStdString() << endl;
- localUser->rename(newName);
- connectUntil(localUser, &User::defaultNameChanged, this,
- [this, thisTest, localUser, newName] {
- FINISH_TEST(localUser->name() == newName);
- });
+ connectSingleShot(targetRoom, &Room::allMembersLoaded, this, [this, thisTest] {
+ auto* const localUser = connection()->user();
+ const auto& newName = connection()->generateTxnId(); // See setTopic()
+ clog << "Renaming the user to " << newName.toStdString()
+ << " in the target room" << endl;
+ localUser->rename(newName, targetRoom);
+ connectUntil(targetRoom, &Room::memberRenamed, this,
+ [this, thisTest, localUser, newName](const User* u) {
+ if (localUser != u)
+ return false;
+ if (localUser->name(targetRoom) != newName)
+ FAIL_TEST();
+
+ clog
+ << "Member rename successful, renaming the account"
+ << endl;
+ const auto newN = newName.mid(0, 5);
+ localUser->rename(newN);
+ connectUntil(localUser, &User::defaultNameChanged,
+ this, [this, thisTest, localUser, newN] {
+ targetRoom->localUser()->rename({});
+ FINISH_TEST(localUser->name() == newN);
+ });
+ return true;
+ });
+ });
return false;
}
@@ -556,7 +614,7 @@ bool TestSuite::checkRedactionOutcome(const QByteArray& thisTest,
// 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())
+ if (it == targetRoom->historyEdge())
return false; // Waiting for the next sync
if ((*it)->isRedacted()) {
@@ -786,6 +844,52 @@ TEST_IMPL(visitResources)
FINISH_TEST(true);
}
+bool checkPrettyPrint(
+ std::initializer_list<std::pair<const char*, const char*>> tests)
+{
+ bool result = true;
+ for (const auto& [test, etalon] : tests) {
+ const auto is = prettyPrint(test).toStdString();
+ const auto shouldBe = std::string("<span style='white-space:pre-wrap'>")
+ + etalon + "</span>";
+ if (is == shouldBe)
+ continue;
+ clog << is << " != " << shouldBe << endl;
+ result = false;
+ }
+ return result;
+}
+
+TEST_IMPL(prettyPrintTests)
+{
+ const bool prettyPrintTestResult = checkPrettyPrint(
+ { { "https://www.matrix.org",
+ R"(<a href="https://www.matrix.org">https://www.matrix.org</a>)" },
+// { "www.matrix.org", // Doesn't work yet
+// R"(<a href="https://www.matrix.org">www.matrix.org</a>)" },
+ { "smb://somewhere/file", "smb://somewhere/file" }, // Disallowed scheme
+ { "https:/something", "https:/something" }, // Malformed URL
+ { "https://matrix.to/#/!roomid:example.org",
+ R"(<a href="https://matrix.to/#/!roomid:example.org">https://matrix.to/#/!roomid:example.org</a>)" },
+ { "https://matrix.to/#/@user_id:example.org",
+ R"(<a href="https://matrix.to/#/@user_id:example.org">https://matrix.to/#/@user_id:example.org</a>)" },
+ { "https://matrix.to/#/#roomalias:example.org",
+ R"(<a href="https://matrix.to/#/#roomalias:example.org">https://matrix.to/#/#roomalias:example.org</a>)" },
+ { "https://matrix.to/#/##ircroomalias:example.org",
+ R"(<a href="https://matrix.to/#/##ircroomalias:example.org">https://matrix.to/#/##ircroomalias:example.org</a>)" },
+ { "me@example.org",
+ R"(<a href="mailto:me@example.org">me@example.org</a>)" },
+ { "mailto:me@example.org",
+ R"(<a href="mailto:me@example.org">mailto:me@example.org</a>)" },
+ { "!room_id:example.org",
+ R"(<a href="https://matrix.to/#/!room_id:example.org">!room_id:example.org</a>)" },
+ { "@user_id:example.org",
+ R"(<a href="https://matrix.to/#/@user_id:example.org">@user_id:example.org</a>)" },
+ { "#room_alias:example.org",
+ R"(<a href="https://matrix.to/#/#room_alias:example.org">#room_alias:example.org</a>)" } });
+ FINISH_TEST(prettyPrintTestResult);
+}
+
void TestManager::conclude()
{
// Clean up the room (best effort)
@@ -854,10 +958,22 @@ void TestManager::conclude()
void TestManager::finalize()
{
+ if (!c->isUsable() || !c->isLoggedIn()) {
+ clog << "No usable connection reached" << endl;
+ QCoreApplication::exit(-2);
+ return; // NB: QCoreApplication::exit() does return to the caller
+ }
clog << "Logging out" << endl;
c->logout();
- connect(c, &Connection::loggedOut, this,
- [this] { QCoreApplication::exit(failed.size() + running.size()); },
+ connect(
+ c, &Connection::loggedOut, this,
+ [this] {
+ QCoreApplication::exit(!testSuite ? -3
+ : succeeded.empty() && failed.empty()
+ && running.empty()
+ ? -4
+ : failed.size() + running.size());
+ },
Qt::QueuedConnection);
}