mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
Systematically resolved 30 identified potential crash vectors and established automated regression testing to prevent strict reoccurrence. Key Changes: - Fixed 11 instances of unsafe `QObject::connect` calls (missing context object) in srd/Charts/AgendaWindow.cpp, FixSpikes.cpp, src/FileIO/FixSpikes.cpp, src/Gui/Agenda.cpp, src/Gui/BatchProcessingDialog.cpp, and src/Gui/IconManager.cpp. This prevents crashes caused by signals firing after the receiver has been destroyed. - Fixed 19 instances of unsafe `QTreeWidgetItem` child access in src/Charts/LTMChartParser.cpp, src/Gui/ColorButton.cpp, src/Gui/AthletePages.cpp, and src/Gui/Pages.cpp by adding defensive `nullptr` checks before dereferencing. - Added Python detection scripts util/check_unsafe_connects.py and util/check_unsafe_tree_child.py to statically analyze the codebase for these specific unsafe patterns. - Integrated detection scripts into the regression test suite under `unittests/Core/signalSafety`, verifying the fixes and enforcing a strict zero-tolerance policy for future regressions. - Added `testSplineCrash` to cover edge cases with empty spline lookups.
79 lines
2.2 KiB
C++
79 lines
2.2 KiB
C++
#include <QTest>
|
|
#include <QObject>
|
|
#include <QSignalSpy>
|
|
|
|
class Sender : public QObject
|
|
{
|
|
Q_OBJECT
|
|
signals:
|
|
void theSignal();
|
|
};
|
|
|
|
class Receiver : public QObject
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
int callCount = 0;
|
|
};
|
|
|
|
class TestSignalSafety : public QObject
|
|
{
|
|
Q_OBJECT
|
|
|
|
private slots:
|
|
void testMissingReceiver() {
|
|
// This test mimics the "missing receiver" bug.
|
|
// We want to show that if we DON'T provide a context object, the lambda is connected to the connection handle,
|
|
// and if the "conceptual" receiver dies, the lambda still runs.
|
|
// NOTE: In a real app this leads to a crash if the lambda captures 'this'.
|
|
|
|
Sender sender;
|
|
int callCount = 0;
|
|
|
|
{
|
|
Receiver receiver;
|
|
// UNSAFE PATTERN: No context object.
|
|
// The connection is tied to 'sender' life, not 'receiver' life.
|
|
connect(&sender, &Sender::theSignal, [&]() {
|
|
// accessing 'receiver' here after it dies would use dangling reference/pointer
|
|
// For this test we just increment a counter to show it runs.
|
|
callCount++;
|
|
});
|
|
|
|
// Emit while receiver is alive -> should run
|
|
emit sender.theSignal();
|
|
QCOMPARE(callCount, 1);
|
|
} // receiver dies here
|
|
|
|
// Emit after receiver died -> should STILL run (unsafe!)
|
|
emit sender.theSignal();
|
|
QCOMPARE(callCount, 2);
|
|
}
|
|
|
|
void testFixedPattern() {
|
|
// This test mimics the fix.
|
|
// We provide the context object (receiver). When it dies, the connection is auto-disconnected.
|
|
|
|
Sender sender;
|
|
int callCount = 0;
|
|
|
|
{
|
|
Receiver receiver;
|
|
// SAFE PATTERN: Context object provided as 3rd arg.
|
|
connect(&sender, &Sender::theSignal, &receiver, [&]() {
|
|
callCount++;
|
|
});
|
|
|
|
// Emit while receiver is alive -> should run
|
|
emit sender.theSignal();
|
|
QCOMPARE(callCount, 1);
|
|
} // receiver dies here -> connection should be removed
|
|
|
|
// Emit after receiver died -> should NOT run
|
|
emit sender.theSignal();
|
|
QCOMPARE(callCount, 1);
|
|
}
|
|
};
|
|
|
|
#include "testSignalSafety.moc"
|