diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/CMakeLists.txt | 3 | ||||
-rw-r--r-- | tests/DummyResourceAPI.h | 46 | ||||
-rw-r--r-- | tests/Packwiz_test.cpp | 4 | ||||
-rw-r--r-- | tests/ResourceModel_test.cpp | 88 | ||||
-rw-r--r-- | tests/Task_test.cpp | 174 |
5 files changed, 267 insertions, 48 deletions
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0f716a75..36a3b0f8 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -24,6 +24,9 @@ ecm_add_test(ResourceFolderModel_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_V ecm_add_test(ResourcePackParse_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test TEST_NAME ResourcePackParse) +ecm_add_test(ResourceModel_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test + TEST_NAME ResourceModel) + ecm_add_test(TexturePackParse_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test TEST_NAME TexturePackParse) diff --git a/tests/DummyResourceAPI.h b/tests/DummyResourceAPI.h new file mode 100644 index 00000000..0cc90958 --- /dev/null +++ b/tests/DummyResourceAPI.h @@ -0,0 +1,46 @@ +#pragma once + +#include <QJsonDocument> + +#include <modplatform/ResourceAPI.h> + +class SearchTask : public Task { + Q_OBJECT + + public: + void executeTask() override { emitSucceeded(); } +}; + +class DummyResourceAPI : public ResourceAPI { + public: + static auto searchRequestResult() + { + static QByteArray json_response = + "{\"hits\":[" + "{" + "\"author\":\"flowln\"," + "\"description\":\"the bestest mod\"," + "\"project_id\":\"something\"," + "\"project_type\":\"mod\"," + "\"slug\":\"bip_bop\"," + "\"title\":\"AAAAAAAA\"," + "\"versions\":[\"2.71\"]" + "}" + "]}"; + + return QJsonDocument::fromJson(json_response); + } + + DummyResourceAPI() : ResourceAPI() {} + [[nodiscard]] auto getSortingMethods() const -> QList<SortingMethod> override { return {}; }; + + [[nodiscard]] Task::Ptr searchProjects(SearchArgs&&, SearchCallbacks&& callbacks) const override + { + auto task = makeShared<SearchTask>(); + QObject::connect(task.get(), &Task::succeeded, [=] { + auto json = searchRequestResult(); + callbacks.on_succeed(json); + }); + return task; + } +}; diff --git a/tests/Packwiz_test.cpp b/tests/Packwiz_test.cpp index 098e8f89..29289469 100644 --- a/tests/Packwiz_test.cpp +++ b/tests/Packwiz_test.cpp @@ -48,7 +48,7 @@ class PackwizTest : public QObject { QCOMPARE(metadata.hash_format, "sha512"); QCOMPARE(metadata.hash, "c8fe6e15ddea32668822dddb26e1851e5f03834be4bcb2eff9c0da7fdc086a9b6cead78e31a44d3bc66335cba11144ee0337c6d5346f1ba63623064499b3188d"); - QCOMPARE(metadata.provider, ModPlatform::Provider::MODRINTH); + QCOMPARE(metadata.provider, ModPlatform::ResourceProvider::MODRINTH); QCOMPARE(metadata.version(), "ug2qKTPR"); QCOMPARE(metadata.mod_id(), "kYq5qkSL"); } @@ -76,7 +76,7 @@ class PackwizTest : public QObject { QCOMPARE(metadata.hash_format, "murmur2"); QCOMPARE(metadata.hash, "1781245820"); - QCOMPARE(metadata.provider, ModPlatform::Provider::FLAME); + QCOMPARE(metadata.provider, ModPlatform::ResourceProvider::FLAME); QCOMPARE(metadata.file_id, 3509043); QCOMPARE(metadata.project_id, 327154); } diff --git a/tests/ResourceModel_test.cpp b/tests/ResourceModel_test.cpp new file mode 100644 index 00000000..716bf853 --- /dev/null +++ b/tests/ResourceModel_test.cpp @@ -0,0 +1,88 @@ +#include <QAbstractItemModelTester> +#include <QTest> +#include <QTimer> + +#include <Json.h> + +#include <ui/pages/modplatform/ResourceModel.h> + +#include "DummyResourceAPI.h" + +using ResourceDownload::ResourceModel; + +#define EXEC_TASK(EXEC) \ + QEventLoop loop; \ + \ + connect(model, &ResourceModel::dataChanged, &loop, &QEventLoop::quit); \ + \ + QTimer expire_timer; \ + expire_timer.callOnTimeout(&loop, &QEventLoop::quit); \ + expire_timer.setSingleShot(true); \ + expire_timer.start(4000); \ + \ + EXEC; \ + if (model->hasActiveSearchJob()) \ + loop.exec(); \ + \ + QVERIFY2(expire_timer.isActive(), "Timer has expired. The search never finished."); \ + expire_timer.stop(); \ + \ + disconnect(model, nullptr, &loop, nullptr) + +class ResourceModelTest; + +class DummyResourceModel : public ResourceModel { + Q_OBJECT + + friend class ResourceModelTest; + + public: + DummyResourceModel() : ResourceModel(new DummyResourceAPI) {} + + [[nodiscard]] auto metaEntryBase() const -> QString override { return ""; }; + + ResourceAPI::SearchArgs createSearchArguments() override { return {}; }; + ResourceAPI::VersionSearchArgs createVersionsArguments(QModelIndex&) override { return {}; }; + ResourceAPI::ProjectInfoArgs createInfoArguments(QModelIndex&) override { return {}; }; + + QJsonArray documentToArray(QJsonDocument& doc) const override { return doc.object().value("hits").toArray(); } + + void loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj) override + { + pack.authors.append({ Json::requireString(obj, "author") }); + pack.description = Json::requireString(obj, "description"); + pack.addonId = Json::requireString(obj, "project_id"); + } +}; + +class ResourceModelTest : public QObject { + Q_OBJECT + private slots: + void test_abstract_item_model() { [[maybe_unused]] auto tester = new QAbstractItemModelTester(new DummyResourceModel); } + + void test_search() + { + auto model = new DummyResourceModel; + + QVERIFY(model->m_packs.isEmpty()); + + EXEC_TASK(model->search()); + + QVERIFY(model->m_packs.size() == 1); + QVERIFY(model->m_search_state == DummyResourceModel::SearchState::Finished); + + auto processed_pack = model->m_packs.at(0); + auto search_json = DummyResourceAPI::searchRequestResult(); + auto processed_response = model->documentToArray(search_json).first().toObject(); + + QVERIFY(processed_pack.addonId.toString() == Json::requireString(processed_response, "project_id")); + QVERIFY(processed_pack.description == Json::requireString(processed_response, "description")); + QVERIFY(processed_pack.authors.first().name == Json::requireString(processed_response, "author")); + } +}; + +QTEST_GUILESS_MAIN(ResourceModelTest) + +#include "ResourceModel_test.moc" + +#include "moc_DummyResourceAPI.cpp" diff --git a/tests/Task_test.cpp b/tests/Task_test.cpp index 80bba02f..95eb4a30 100644 --- a/tests/Task_test.cpp +++ b/tests/Task_test.cpp @@ -1,16 +1,23 @@ #include <QTest> +#include <QTimer> +#include <QThread> #include <tasks/ConcurrentTask.h> #include <tasks/MultipleOptionsTask.h> #include <tasks/SequentialTask.h> #include <tasks/Task.h> +#include <array> + /* Does nothing. Only used for testing. */ class BasicTask : public Task { Q_OBJECT friend class TaskTest; + public: + BasicTask(bool show_debug_log = true) : Task(nullptr, show_debug_log) {} + private: void executeTask() override { @@ -30,6 +37,57 @@ class BasicTask_MultiStep : public Task { void executeTask() override {}; }; +class BigConcurrentTask : public ConcurrentTask { + Q_OBJECT + + void startNext() override + { + // This is here only to help fill the stack a bit more quickly (if there's an issue, of course :^)) + // Each tasks thus adds 1024 * 4 bytes to the stack, at the very least. + [[maybe_unused]] volatile std::array<uint32_t, 1024> some_data_on_the_stack {}; + + ConcurrentTask::startNext(); + } +}; + +class BigConcurrentTaskThread : public QThread { + Q_OBJECT + + BigConcurrentTask big_task; + + void run() override + { + QTimer deadline; + deadline.setInterval(10000); + connect(&deadline, &QTimer::timeout, this, [this]{ passed_the_deadline = true; }); + deadline.start(); + + // NOTE: Arbitrary value that manages to trigger a problem when there is one. + // Considering each tasks, in a problematic state, adds 1024 * 4 bytes to the stack, + // this number is enough to fill up 16 MiB of stack, more than enough to cause a problem. + static const unsigned s_num_tasks = 1 << 12; + auto sub_tasks = new BasicTask::Ptr[s_num_tasks]; + + for (unsigned i = 0; i < s_num_tasks; i++) { + sub_tasks[i] = makeShared<BasicTask>(false); + big_task.addTask(sub_tasks[i]); + } + + big_task.run(); + + while (!big_task.isFinished() && !passed_the_deadline) + QCoreApplication::processEvents(); + + emit finished(); + } + + public: + bool passed_the_deadline = false; + + signals: + void finished(); +}; + class TaskTest : public QObject { Q_OBJECT @@ -78,21 +136,21 @@ class TaskTest : public QObject { } void test_basicConcurrentRun(){ - BasicTask t1; - BasicTask t2; - BasicTask t3; + auto t1 = makeShared<BasicTask>(); + auto t2 = makeShared<BasicTask>(); + auto t3 = makeShared<BasicTask>(); ConcurrentTask t; - t.addTask(&t1); - t.addTask(&t2); - t.addTask(&t3); + t.addTask(t1); + t.addTask(t2); + t.addTask(t3); QObject::connect(&t, &Task::finished, [&]{ QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been."); - QVERIFY(t1.wasSuccessful()); - QVERIFY(t2.wasSuccessful()); - QVERIFY(t3.wasSuccessful()); + QVERIFY(t1->wasSuccessful()); + QVERIFY(t2->wasSuccessful()); + QVERIFY(t3->wasSuccessful()); }); t.start(); @@ -103,31 +161,39 @@ class TaskTest : public QObject { // Tests if starting new tasks after the 6 initial ones is working void test_moreConcurrentRun(){ - BasicTask t1, t2, t3, t4, t5, t6, t7, t8, t9; + auto t1 = makeShared<BasicTask>(); + auto t2 = makeShared<BasicTask>(); + auto t3 = makeShared<BasicTask>(); + auto t4 = makeShared<BasicTask>(); + auto t5 = makeShared<BasicTask>(); + auto t6 = makeShared<BasicTask>(); + auto t7 = makeShared<BasicTask>(); + auto t8 = makeShared<BasicTask>(); + auto t9 = makeShared<BasicTask>(); ConcurrentTask t; - t.addTask(&t1); - t.addTask(&t2); - t.addTask(&t3); - t.addTask(&t4); - t.addTask(&t5); - t.addTask(&t6); - t.addTask(&t7); - t.addTask(&t8); - t.addTask(&t9); + t.addTask(t1); + t.addTask(t2); + t.addTask(t3); + t.addTask(t4); + t.addTask(t5); + t.addTask(t6); + t.addTask(t7); + t.addTask(t8); + t.addTask(t9); QObject::connect(&t, &Task::finished, [&]{ QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been."); - QVERIFY(t1.wasSuccessful()); - QVERIFY(t2.wasSuccessful()); - QVERIFY(t3.wasSuccessful()); - QVERIFY(t4.wasSuccessful()); - QVERIFY(t5.wasSuccessful()); - QVERIFY(t6.wasSuccessful()); - QVERIFY(t7.wasSuccessful()); - QVERIFY(t8.wasSuccessful()); - QVERIFY(t9.wasSuccessful()); + QVERIFY(t1->wasSuccessful()); + QVERIFY(t2->wasSuccessful()); + QVERIFY(t3->wasSuccessful()); + QVERIFY(t4->wasSuccessful()); + QVERIFY(t5->wasSuccessful()); + QVERIFY(t6->wasSuccessful()); + QVERIFY(t7->wasSuccessful()); + QVERIFY(t8->wasSuccessful()); + QVERIFY(t9->wasSuccessful()); }); t.start(); @@ -137,21 +203,21 @@ class TaskTest : public QObject { } void test_basicSequentialRun(){ - BasicTask t1; - BasicTask t2; - BasicTask t3; + auto t1 = makeShared<BasicTask>(); + auto t2 = makeShared<BasicTask>(); + auto t3 = makeShared<BasicTask>(); SequentialTask t; - t.addTask(&t1); - t.addTask(&t2); - t.addTask(&t3); + t.addTask(t1); + t.addTask(t2); + t.addTask(t3); QObject::connect(&t, &Task::finished, [&]{ QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been."); - QVERIFY(t1.wasSuccessful()); - QVERIFY(t2.wasSuccessful()); - QVERIFY(t3.wasSuccessful()); + QVERIFY(t1->wasSuccessful()); + QVERIFY(t2->wasSuccessful()); + QVERIFY(t3->wasSuccessful()); }); t.start(); @@ -161,21 +227,21 @@ class TaskTest : public QObject { } void test_basicMultipleOptionsRun(){ - BasicTask t1; - BasicTask t2; - BasicTask t3; + auto t1 = makeShared<BasicTask>(); + auto t2 = makeShared<BasicTask>(); + auto t3 = makeShared<BasicTask>(); MultipleOptionsTask t; - t.addTask(&t1); - t.addTask(&t2); - t.addTask(&t3); + t.addTask(t1); + t.addTask(t2); + t.addTask(t3); QObject::connect(&t, &Task::finished, [&]{ QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been."); - QVERIFY(t1.wasSuccessful()); - QVERIFY(!t2.wasSuccessful()); - QVERIFY(!t3.wasSuccessful()); + QVERIFY(t1->wasSuccessful()); + QVERIFY(!t2->wasSuccessful()); + QVERIFY(!t3->wasSuccessful()); }); t.start(); @@ -183,6 +249,22 @@ class TaskTest : public QObject { return t.isFinished(); }, 1000), "Task didn't finish as it should."); } + + void test_stackOverflowInConcurrentTask() + { + QEventLoop loop; + + auto thread = new BigConcurrentTaskThread; + + connect(thread, &BigConcurrentTaskThread::finished, &loop, &QEventLoop::quit); + + thread->start(); + + loop.exec(); + + QVERIFY(!thread->passed_the_deadline); + thread->deleteLater(); + } }; QTEST_GUILESS_MAIN(TaskTest) |