From 27f6debdafe043c977f27ce074079db578edca0e Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Fri, 14 Jul 2023 10:11:34 +0200 Subject: [PATCH] Merge pull request #1306 from Ryex/ci/address-sanitiser_on_debug_builds --- CMakeLists.txt | 32 ++++++++ tests/FileSystem_test.cpp | 16 +++- tests/ResourceModel_test.cpp | 8 +- tests/Task_test.cpp | 147 ++++++++++++++++------------------- tests/Version_test.cpp | 17 ++-- 5 files changed, 129 insertions(+), 91 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 31f4629e5..ef0d01e1e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,6 +85,38 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTOML_ENABLE_FLOAT16=0") # set CXXFLAGS for build targets set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}") +option(DEBUG_ADDRESS_SANITIZER "Enable Address Sanitizer in Debug builds" on) + +# If this is a Debug build turn on address sanitiser +if (CMAKE_BUILD_TYPE STREQUAL "Debug" AND DEBUG_ADDRESS_SANITIZER) + message(STATUS "Address Sanitizer enabled for Debug builds, Turn it off with -DDEBUG_ADDRESS_SANITIZER=off") + if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") + # using clang with clang-cl front end + message(STATUS "Address Sanitizer available on Clang MSVC frontend") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /O1 /Oy-") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /O1 /Oy-") + else() + # AppleClang and Clang + message(STATUS "Address Sanitizer available on Clang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer") + endif() + elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + # GCC + message(STATUS "Address Sanitizer available on GCC") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer") + link_libraries("asan") + elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + message(STATUS "Address Sanitizer available on MSVC") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /O1 /Oy-") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /O1 /Oy-") + else() + message(STATUS "Address Sanitizer not available on compiler ${CMAKE_CXX_COMPILER_ID}") + endif() +endif() + option(ENABLE_LTO "Enable Link Time Optimization" off) if(ENABLE_LTO) diff --git a/tests/FileSystem_test.cpp b/tests/FileSystem_test.cpp index ec1f0bcff..a41345c2e 100644 --- a/tests/FileSystem_test.cpp +++ b/tests/FileSystem_test.cpp @@ -42,6 +42,10 @@ class LinkTask : public Task { m_lnk->debug(true); } + ~LinkTask() { + delete m_lnk; + } + void matcher(const IPathMatcher *filter) { m_lnk->matcher(filter); @@ -219,7 +223,8 @@ slots: qDebug() << tempDir.path(); qDebug() << target_dir.path(); FS::copy c(folder, target_dir.path()); - c.matcher(new RegexpMatcher("[.]?mcmeta")); + RegexpMatcher re("[.]?mcmeta"); + c.matcher(&re); c(); for(auto entry: target_dir.entryList()) @@ -253,7 +258,8 @@ slots: qDebug() << tempDir.path(); qDebug() << target_dir.path(); FS::copy c(folder, target_dir.path()); - c.matcher(new RegexpMatcher("[.]?mcmeta")); + RegexpMatcher re("[.]?mcmeta"); + c.matcher(&re); c.whitelist(true); c(); @@ -460,7 +466,8 @@ slots: qDebug() << target_dir.path(); LinkTask lnk_tsk(folder, target_dir.path()); - lnk_tsk.matcher(new RegexpMatcher("[.]?mcmeta")); + RegexpMatcher re("[.]?mcmeta"); + lnk_tsk.matcher(&re); lnk_tsk.linkRecursively(true); QObject::connect(&lnk_tsk, &Task::finished, [&]{ QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been."); @@ -511,7 +518,8 @@ slots: qDebug() << target_dir.path(); LinkTask lnk_tsk(folder, target_dir.path()); - lnk_tsk.matcher(new RegexpMatcher("[.]?mcmeta")); + RegexpMatcher re("[.]?mcmeta"); + lnk_tsk.matcher(&re); lnk_tsk.linkRecursively(true); lnk_tsk.whitelist(true); QObject::connect(&lnk_tsk, &Task::finished, [&]{ diff --git a/tests/ResourceModel_test.cpp b/tests/ResourceModel_test.cpp index c0d9cd95d..30353d3f9 100644 --- a/tests/ResourceModel_test.cpp +++ b/tests/ResourceModel_test.cpp @@ -38,6 +38,7 @@ class DummyResourceModel : public ResourceModel { public: DummyResourceModel() : ResourceModel(new DummyResourceAPI) {} + ~DummyResourceModel() {} [[nodiscard]] auto metaEntryBase() const -> QString override { return ""; }; @@ -58,7 +59,10 @@ class DummyResourceModel : public ResourceModel { class ResourceModelTest : public QObject { Q_OBJECT private slots: - void test_abstract_item_model() { [[maybe_unused]] auto tester = new QAbstractItemModelTester(new DummyResourceModel); } + void test_abstract_item_model() { + auto dummy = DummyResourceModel(); + auto tester = QAbstractItemModelTester(&dummy); + } void test_search() { @@ -78,6 +82,8 @@ class ResourceModelTest : public QObject { 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")); + + delete model; } }; diff --git a/tests/Task_test.cpp b/tests/Task_test.cpp index dabe5da26..c59d4bb73 100644 --- a/tests/Task_test.cpp +++ b/tests/Task_test.cpp @@ -1,6 +1,6 @@ #include -#include #include +#include #include #include @@ -19,10 +19,7 @@ class BasicTask : public Task { BasicTask(bool show_debug_log = true) : Task(nullptr, show_debug_log) {} private: - void executeTask() override - { - emitSucceeded(); - }; + void executeTask() override { emitSucceeded(); }; }; /* Does nothing. Only used for testing. */ @@ -34,7 +31,7 @@ class BasicTask_MultiStep : public Task { private: auto isMultiStep() const -> bool override { return true; } - void executeTask() override {}; + void executeTask() override{}; }; class BigConcurrentTask : public ConcurrentTask { @@ -44,7 +41,7 @@ class BigConcurrentTask : public ConcurrentTask { { // 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 some_data_on_the_stack {}; + [[maybe_unused]] volatile std::array some_data_on_the_stack{}; ConcurrentTask::startNext(); } @@ -53,49 +50,42 @@ class BigConcurrentTask : public ConcurrentTask { class BigConcurrentTaskThread : public QThread { Q_OBJECT - BigConcurrentTask big_task; - + QTimer m_deadline; void run() override { - QTimer deadline; - deadline.setInterval(10000); - connect(&deadline, &QTimer::timeout, this, [this]{ passed_the_deadline = true; }); - deadline.start(); + BigConcurrentTask big_task; + m_deadline.setInterval(10000); // 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++) { auto sub_task = makeShared(false); - sub_tasks[i] = sub_task; big_task.addTask(sub_task); } + connect(&big_task, &Task::finished, this, &QThread::quit); + connect(&m_deadline, &QTimer::timeout, this, [&] { passed_the_deadline = true; quit(); }); + + m_deadline.start(); big_task.run(); - while (!big_task.isFinished() && !passed_the_deadline) - QCoreApplication::processEvents(); - - emit finished(); + exec(); } public: bool passed_the_deadline = false; - - signals: - void finished(); }; class TaskTest : public QObject { Q_OBJECT private slots: - void test_SetStatus_NoMultiStep(){ + void test_SetStatus_NoMultiStep() + { BasicTask t; - QString status {"test status"}; + QString status{ "test status" }; t.setStatus(status); @@ -103,9 +93,10 @@ class TaskTest : public QObject { QCOMPARE(t.getStepProgress().isEmpty(), TaskStepProgressList{}.isEmpty()); } - void test_SetStatus_MultiStep(){ + void test_SetStatus_MultiStep() + { BasicTask_MultiStep t; - QString status {"test status"}; + QString status{ "test status" }; t.setStatus(status); @@ -115,7 +106,8 @@ class TaskTest : public QObject { QCOMPARE(t.getStepProgress().isEmpty(), TaskStepProgressList{}.isEmpty()); } - void test_SetProgress(){ + void test_SetProgress() + { BasicTask t; int current = 42; int total = 207; @@ -126,17 +118,18 @@ class TaskTest : public QObject { QCOMPARE(t.getTotalProgress(), total); } - void test_basicRun(){ + void test_basicRun() + { BasicTask t; - QObject::connect(&t, &Task::finished, [&]{ QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been."); }); + QObject::connect(&t, &Task::finished, + [&] { QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been."); }); t.start(); - QVERIFY2(QTest::qWaitFor([&]() { - return t.isFinished(); - }, 1000), "Task didn't finish as it should."); + QVERIFY2(QTest::qWaitFor([&]() { return t.isFinished(); }, 1000), "Task didn't finish as it should."); } - void test_basicConcurrentRun(){ + void test_basicConcurrentRun() + { auto t1 = makeShared(); auto t2 = makeShared(); auto t3 = makeShared(); @@ -147,21 +140,20 @@ class TaskTest : public QObject { 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()); + QObject::connect(&t, &Task::finished, [&t, &t1, &t2, &t3] { + QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been."); + QVERIFY(t1->wasSuccessful()); + QVERIFY(t2->wasSuccessful()); + QVERIFY(t3->wasSuccessful()); }); t.start(); - QVERIFY2(QTest::qWaitFor([&]() { - return t.isFinished(); - }, 1000), "Task didn't finish as it should."); + QVERIFY2(QTest::qWaitFor([&]() { return t.isFinished(); }, 1000), "Task didn't finish as it should."); } // Tests if starting new tasks after the 6 initial ones is working - void test_moreConcurrentRun(){ + void test_moreConcurrentRun() + { auto t1 = makeShared(); auto t2 = makeShared(); auto t3 = makeShared(); @@ -184,26 +176,25 @@ class TaskTest : public QObject { 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()); + QObject::connect(&t, &Task::finished, [&t, &t1, &t2, &t3, &t4, &t5, &t6, &t7, &t8, &t9] { + 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()); }); t.start(); - QVERIFY2(QTest::qWaitFor([&]() { - return t.isFinished(); - }, 1000), "Task didn't finish as it should."); + QVERIFY2(QTest::qWaitFor([&]() { return t.isFinished(); }, 1000), "Task didn't finish as it should."); } - void test_basicSequentialRun(){ + void test_basicSequentialRun() + { auto t1 = makeShared(); auto t2 = makeShared(); auto t3 = makeShared(); @@ -214,20 +205,19 @@ class TaskTest : public QObject { 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()); + QObject::connect(&t, &Task::finished, [&t, &t1, &t2, &t3] { + QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been."); + QVERIFY(t1->wasSuccessful()); + QVERIFY(t2->wasSuccessful()); + QVERIFY(t3->wasSuccessful()); }); t.start(); - QVERIFY2(QTest::qWaitFor([&]() { - return t.isFinished(); - }, 1000), "Task didn't finish as it should."); + QVERIFY2(QTest::qWaitFor([&]() { return t.isFinished(); }, 1000), "Task didn't finish as it should."); } - void test_basicMultipleOptionsRun(){ + void test_basicMultipleOptionsRun() + { auto t1 = makeShared(); auto t2 = makeShared(); auto t3 = makeShared(); @@ -238,33 +228,30 @@ class TaskTest : public QObject { 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()); + QObject::connect(&t, &Task::finished, [&t, &t1, &t2, &t3] { + QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been."); + QVERIFY(t1->wasSuccessful()); + QVERIFY(!t2->wasSuccessful()); + QVERIFY(!t3->wasSuccessful()); }); t.start(); - QVERIFY2(QTest::qWaitFor([&]() { - return t.isFinished(); - }, 1000), "Task didn't finish as it should."); + QVERIFY2(QTest::qWaitFor([&]() { return t.isFinished(); }, 1000), "Task didn't finish as it should."); } void test_stackOverflowInConcurrentTask() { QEventLoop loop; - auto thread = new BigConcurrentTaskThread; + BigConcurrentTaskThread thread; - connect(thread, &BigConcurrentTaskThread::finished, &loop, &QEventLoop::quit); + connect(&thread, &BigConcurrentTaskThread::finished, &loop, &QEventLoop::quit); - thread->start(); + thread.start(); loop.exec(); - QVERIFY(!thread->passed_the_deadline); - thread->deleteLater(); + QVERIFY(!thread.passed_the_deadline); } }; diff --git a/tests/Version_test.cpp b/tests/Version_test.cpp index afb4c6102..f5488cbce 100644 --- a/tests/Version_test.cpp +++ b/tests/Version_test.cpp @@ -20,6 +20,8 @@ class VersionTest : public QObject { Q_OBJECT + QStringList m_flex_test_names = {}; + void addDataColumns() { QTest::addColumn("first"); @@ -101,8 +103,9 @@ class VersionTest : public QObject { QString first{split_line.first().simplified()}; QString second{split_line.last().simplified()}; - auto new_test_name = test_name_template.arg(QString::number(test_number), "lessThan").toLatin1().data(); - QTest::newRow(new_test_name) << first << second << true << false; + auto new_test_name = test_name_template.arg(QString::number(test_number), "lessThan"); + m_flex_test_names.append(new_test_name); + QTest::newRow(m_flex_test_names.last().toLatin1().data()) << first << second << true << false; continue; } @@ -112,8 +115,9 @@ class VersionTest : public QObject { QString first{split_line.first().simplified()}; QString second{split_line.last().simplified()}; - auto new_test_name = test_name_template.arg(QString::number(test_number), "equals").toLatin1().data(); - QTest::newRow(new_test_name) << first << second << false << true; + auto new_test_name = test_name_template.arg(QString::number(test_number), "equals"); + m_flex_test_names.append(new_test_name); + QTest::newRow(m_flex_test_names.last().toLatin1().data()) << first << second << false << true; continue; } @@ -123,8 +127,9 @@ class VersionTest : public QObject { QString first{split_line.first().simplified()}; QString second{split_line.last().simplified()}; - auto new_test_name = test_name_template.arg(QString::number(test_number), "greaterThan").toLatin1().data(); - QTest::newRow(new_test_name) << first << second << false << false; + auto new_test_name = test_name_template.arg(QString::number(test_number), "greaterThan"); + m_flex_test_names.append(new_test_name); + QTest::newRow(m_flex_test_names.last().toLatin1().data()) << first << second << false << false; continue; }