Merge pull request #1306 from Ryex/ci/address-sanitiser_on_debug_builds

This commit is contained in:
Sefa Eyeoglu 2023-07-14 10:11:34 +02:00 committed by GitHub
commit 3becd4386b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 129 additions and 91 deletions

View File

@ -85,6 +85,38 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTOML_ENABLE_FLOAT16=0")
# set CXXFLAGS for build targets # set CXXFLAGS for build targets
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}") 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) option(ENABLE_LTO "Enable Link Time Optimization" off)
if(ENABLE_LTO) if(ENABLE_LTO)

View File

@ -42,6 +42,10 @@ class LinkTask : public Task {
m_lnk->debug(true); m_lnk->debug(true);
} }
~LinkTask() {
delete m_lnk;
}
void matcher(const IPathMatcher *filter) void matcher(const IPathMatcher *filter)
{ {
m_lnk->matcher(filter); m_lnk->matcher(filter);
@ -219,7 +223,8 @@ slots:
qDebug() << tempDir.path(); qDebug() << tempDir.path();
qDebug() << target_dir.path(); qDebug() << target_dir.path();
FS::copy c(folder, target_dir.path()); FS::copy c(folder, target_dir.path());
c.matcher(new RegexpMatcher("[.]?mcmeta")); RegexpMatcher re("[.]?mcmeta");
c.matcher(&re);
c(); c();
for(auto entry: target_dir.entryList()) for(auto entry: target_dir.entryList())
@ -253,7 +258,8 @@ slots:
qDebug() << tempDir.path(); qDebug() << tempDir.path();
qDebug() << target_dir.path(); qDebug() << target_dir.path();
FS::copy c(folder, 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.whitelist(true);
c(); c();
@ -460,7 +466,8 @@ slots:
qDebug() << target_dir.path(); qDebug() << target_dir.path();
LinkTask lnk_tsk(folder, 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.linkRecursively(true);
QObject::connect(&lnk_tsk, &Task::finished, [&]{ QObject::connect(&lnk_tsk, &Task::finished, [&]{
QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been."); QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been.");
@ -511,7 +518,8 @@ slots:
qDebug() << target_dir.path(); qDebug() << target_dir.path();
LinkTask lnk_tsk(folder, 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.linkRecursively(true);
lnk_tsk.whitelist(true); lnk_tsk.whitelist(true);
QObject::connect(&lnk_tsk, &Task::finished, [&]{ QObject::connect(&lnk_tsk, &Task::finished, [&]{

View File

@ -38,6 +38,7 @@ class DummyResourceModel : public ResourceModel {
public: public:
DummyResourceModel() : ResourceModel(new DummyResourceAPI) {} DummyResourceModel() : ResourceModel(new DummyResourceAPI) {}
~DummyResourceModel() {}
[[nodiscard]] auto metaEntryBase() const -> QString override { return ""; }; [[nodiscard]] auto metaEntryBase() const -> QString override { return ""; };
@ -58,7 +59,10 @@ class DummyResourceModel : public ResourceModel {
class ResourceModelTest : public QObject { class ResourceModelTest : public QObject {
Q_OBJECT Q_OBJECT
private slots: 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() 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->addonId.toString() == Json::requireString(processed_response, "project_id"));
QVERIFY(processed_pack->description == Json::requireString(processed_response, "description")); QVERIFY(processed_pack->description == Json::requireString(processed_response, "description"));
QVERIFY(processed_pack->authors.first().name == Json::requireString(processed_response, "author")); QVERIFY(processed_pack->authors.first().name == Json::requireString(processed_response, "author"));
delete model;
} }
}; };

View File

@ -1,6 +1,6 @@
#include <QTest> #include <QTest>
#include <QTimer>
#include <QThread> #include <QThread>
#include <QTimer>
#include <tasks/ConcurrentTask.h> #include <tasks/ConcurrentTask.h>
#include <tasks/MultipleOptionsTask.h> #include <tasks/MultipleOptionsTask.h>
@ -19,10 +19,7 @@ class BasicTask : public Task {
BasicTask(bool show_debug_log = true) : Task(nullptr, show_debug_log) {} BasicTask(bool show_debug_log = true) : Task(nullptr, show_debug_log) {}
private: private:
void executeTask() override void executeTask() override { emitSucceeded(); };
{
emitSucceeded();
};
}; };
/* Does nothing. Only used for testing. */ /* Does nothing. Only used for testing. */
@ -34,7 +31,7 @@ class BasicTask_MultiStep : public Task {
private: private:
auto isMultiStep() const -> bool override { return true; } auto isMultiStep() const -> bool override { return true; }
void executeTask() override {}; void executeTask() override{};
}; };
class BigConcurrentTask : public ConcurrentTask { 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 :^)) // 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. // 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 {}; [[maybe_unused]] volatile std::array<uint32_t, 1024> some_data_on_the_stack{};
ConcurrentTask::startNext(); ConcurrentTask::startNext();
} }
@ -53,49 +50,42 @@ class BigConcurrentTask : public ConcurrentTask {
class BigConcurrentTaskThread : public QThread { class BigConcurrentTaskThread : public QThread {
Q_OBJECT Q_OBJECT
BigConcurrentTask big_task; QTimer m_deadline;
void run() override void run() override
{ {
QTimer deadline; BigConcurrentTask big_task;
deadline.setInterval(10000); m_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. // 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, // 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. // 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; 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++) { for (unsigned i = 0; i < s_num_tasks; i++) {
auto sub_task = makeShared<BasicTask>(false); auto sub_task = makeShared<BasicTask>(false);
sub_tasks[i] = sub_task;
big_task.addTask(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(); big_task.run();
while (!big_task.isFinished() && !passed_the_deadline) exec();
QCoreApplication::processEvents();
emit finished();
} }
public: public:
bool passed_the_deadline = false; bool passed_the_deadline = false;
signals:
void finished();
}; };
class TaskTest : public QObject { class TaskTest : public QObject {
Q_OBJECT Q_OBJECT
private slots: private slots:
void test_SetStatus_NoMultiStep(){ void test_SetStatus_NoMultiStep()
{
BasicTask t; BasicTask t;
QString status {"test status"}; QString status{ "test status" };
t.setStatus(status); t.setStatus(status);
@ -103,9 +93,10 @@ class TaskTest : public QObject {
QCOMPARE(t.getStepProgress().isEmpty(), TaskStepProgressList{}.isEmpty()); QCOMPARE(t.getStepProgress().isEmpty(), TaskStepProgressList{}.isEmpty());
} }
void test_SetStatus_MultiStep(){ void test_SetStatus_MultiStep()
{
BasicTask_MultiStep t; BasicTask_MultiStep t;
QString status {"test status"}; QString status{ "test status" };
t.setStatus(status); t.setStatus(status);
@ -115,7 +106,8 @@ class TaskTest : public QObject {
QCOMPARE(t.getStepProgress().isEmpty(), TaskStepProgressList{}.isEmpty()); QCOMPARE(t.getStepProgress().isEmpty(), TaskStepProgressList{}.isEmpty());
} }
void test_SetProgress(){ void test_SetProgress()
{
BasicTask t; BasicTask t;
int current = 42; int current = 42;
int total = 207; int total = 207;
@ -126,17 +118,18 @@ class TaskTest : public QObject {
QCOMPARE(t.getTotalProgress(), total); QCOMPARE(t.getTotalProgress(), total);
} }
void test_basicRun(){ void test_basicRun()
{
BasicTask t; 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(); t.start();
QVERIFY2(QTest::qWaitFor([&]() { QVERIFY2(QTest::qWaitFor([&]() { return t.isFinished(); }, 1000), "Task didn't finish as it should.");
return t.isFinished();
}, 1000), "Task didn't finish as it should.");
} }
void test_basicConcurrentRun(){ void test_basicConcurrentRun()
{
auto t1 = makeShared<BasicTask>(); auto t1 = makeShared<BasicTask>();
auto t2 = makeShared<BasicTask>(); auto t2 = makeShared<BasicTask>();
auto t3 = makeShared<BasicTask>(); auto t3 = makeShared<BasicTask>();
@ -147,21 +140,20 @@ class TaskTest : public QObject {
t.addTask(t2); t.addTask(t2);
t.addTask(t3); t.addTask(t3);
QObject::connect(&t, &Task::finished, [&]{ QObject::connect(&t, &Task::finished, [&t, &t1, &t2, &t3] {
QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been."); QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been.");
QVERIFY(t1->wasSuccessful()); QVERIFY(t1->wasSuccessful());
QVERIFY(t2->wasSuccessful()); QVERIFY(t2->wasSuccessful());
QVERIFY(t3->wasSuccessful()); QVERIFY(t3->wasSuccessful());
}); });
t.start(); t.start();
QVERIFY2(QTest::qWaitFor([&]() { QVERIFY2(QTest::qWaitFor([&]() { return t.isFinished(); }, 1000), "Task didn't finish as it should.");
return t.isFinished();
}, 1000), "Task didn't finish as it should.");
} }
// Tests if starting new tasks after the 6 initial ones is working // Tests if starting new tasks after the 6 initial ones is working
void test_moreConcurrentRun(){ void test_moreConcurrentRun()
{
auto t1 = makeShared<BasicTask>(); auto t1 = makeShared<BasicTask>();
auto t2 = makeShared<BasicTask>(); auto t2 = makeShared<BasicTask>();
auto t3 = makeShared<BasicTask>(); auto t3 = makeShared<BasicTask>();
@ -184,26 +176,25 @@ class TaskTest : public QObject {
t.addTask(t8); t.addTask(t8);
t.addTask(t9); t.addTask(t9);
QObject::connect(&t, &Task::finished, [&]{ 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."); QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been.");
QVERIFY(t1->wasSuccessful()); QVERIFY(t1->wasSuccessful());
QVERIFY(t2->wasSuccessful()); QVERIFY(t2->wasSuccessful());
QVERIFY(t3->wasSuccessful()); QVERIFY(t3->wasSuccessful());
QVERIFY(t4->wasSuccessful()); QVERIFY(t4->wasSuccessful());
QVERIFY(t5->wasSuccessful()); QVERIFY(t5->wasSuccessful());
QVERIFY(t6->wasSuccessful()); QVERIFY(t6->wasSuccessful());
QVERIFY(t7->wasSuccessful()); QVERIFY(t7->wasSuccessful());
QVERIFY(t8->wasSuccessful()); QVERIFY(t8->wasSuccessful());
QVERIFY(t9->wasSuccessful()); QVERIFY(t9->wasSuccessful());
}); });
t.start(); t.start();
QVERIFY2(QTest::qWaitFor([&]() { QVERIFY2(QTest::qWaitFor([&]() { return t.isFinished(); }, 1000), "Task didn't finish as it should.");
return t.isFinished();
}, 1000), "Task didn't finish as it should.");
} }
void test_basicSequentialRun(){ void test_basicSequentialRun()
{
auto t1 = makeShared<BasicTask>(); auto t1 = makeShared<BasicTask>();
auto t2 = makeShared<BasicTask>(); auto t2 = makeShared<BasicTask>();
auto t3 = makeShared<BasicTask>(); auto t3 = makeShared<BasicTask>();
@ -214,20 +205,19 @@ class TaskTest : public QObject {
t.addTask(t2); t.addTask(t2);
t.addTask(t3); t.addTask(t3);
QObject::connect(&t, &Task::finished, [&]{ QObject::connect(&t, &Task::finished, [&t, &t1, &t2, &t3] {
QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been."); QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been.");
QVERIFY(t1->wasSuccessful()); QVERIFY(t1->wasSuccessful());
QVERIFY(t2->wasSuccessful()); QVERIFY(t2->wasSuccessful());
QVERIFY(t3->wasSuccessful()); QVERIFY(t3->wasSuccessful());
}); });
t.start(); t.start();
QVERIFY2(QTest::qWaitFor([&]() { QVERIFY2(QTest::qWaitFor([&]() { return t.isFinished(); }, 1000), "Task didn't finish as it should.");
return t.isFinished();
}, 1000), "Task didn't finish as it should.");
} }
void test_basicMultipleOptionsRun(){ void test_basicMultipleOptionsRun()
{
auto t1 = makeShared<BasicTask>(); auto t1 = makeShared<BasicTask>();
auto t2 = makeShared<BasicTask>(); auto t2 = makeShared<BasicTask>();
auto t3 = makeShared<BasicTask>(); auto t3 = makeShared<BasicTask>();
@ -238,33 +228,30 @@ class TaskTest : public QObject {
t.addTask(t2); t.addTask(t2);
t.addTask(t3); t.addTask(t3);
QObject::connect(&t, &Task::finished, [&]{ QObject::connect(&t, &Task::finished, [&t, &t1, &t2, &t3] {
QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been."); QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been.");
QVERIFY(t1->wasSuccessful()); QVERIFY(t1->wasSuccessful());
QVERIFY(!t2->wasSuccessful()); QVERIFY(!t2->wasSuccessful());
QVERIFY(!t3->wasSuccessful()); QVERIFY(!t3->wasSuccessful());
}); });
t.start(); t.start();
QVERIFY2(QTest::qWaitFor([&]() { QVERIFY2(QTest::qWaitFor([&]() { return t.isFinished(); }, 1000), "Task didn't finish as it should.");
return t.isFinished();
}, 1000), "Task didn't finish as it should.");
} }
void test_stackOverflowInConcurrentTask() void test_stackOverflowInConcurrentTask()
{ {
QEventLoop loop; 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(); loop.exec();
QVERIFY(!thread->passed_the_deadline); QVERIFY(!thread.passed_the_deadline);
thread->deleteLater();
} }
}; };

View File

@ -20,6 +20,8 @@
class VersionTest : public QObject { class VersionTest : public QObject {
Q_OBJECT Q_OBJECT
QStringList m_flex_test_names = {};
void addDataColumns() void addDataColumns()
{ {
QTest::addColumn<QString>("first"); QTest::addColumn<QString>("first");
@ -101,8 +103,9 @@ class VersionTest : public QObject {
QString first{split_line.first().simplified()}; QString first{split_line.first().simplified()};
QString second{split_line.last().simplified()}; QString second{split_line.last().simplified()};
auto new_test_name = test_name_template.arg(QString::number(test_number), "lessThan").toLatin1().data(); auto new_test_name = test_name_template.arg(QString::number(test_number), "lessThan");
QTest::newRow(new_test_name) << first << second << true << false; m_flex_test_names.append(new_test_name);
QTest::newRow(m_flex_test_names.last().toLatin1().data()) << first << second << true << false;
continue; continue;
} }
@ -112,8 +115,9 @@ class VersionTest : public QObject {
QString first{split_line.first().simplified()}; QString first{split_line.first().simplified()};
QString second{split_line.last().simplified()}; QString second{split_line.last().simplified()};
auto new_test_name = test_name_template.arg(QString::number(test_number), "equals").toLatin1().data(); auto new_test_name = test_name_template.arg(QString::number(test_number), "equals");
QTest::newRow(new_test_name) << first << second << false << true; m_flex_test_names.append(new_test_name);
QTest::newRow(m_flex_test_names.last().toLatin1().data()) << first << second << false << true;
continue; continue;
} }
@ -123,8 +127,9 @@ class VersionTest : public QObject {
QString first{split_line.first().simplified()}; QString first{split_line.first().simplified()};
QString second{split_line.last().simplified()}; QString second{split_line.last().simplified()};
auto new_test_name = test_name_template.arg(QString::number(test_number), "greaterThan").toLatin1().data(); auto new_test_name = test_name_template.arg(QString::number(test_number), "greaterThan");
QTest::newRow(new_test_name) << first << second << false << false; m_flex_test_names.append(new_test_name);
QTest::newRow(m_flex_test_names.last().toLatin1().data()) << first << second << false << false;
continue; continue;
} }