/*
Copyright 2006-2026 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see .
*/
#include "logging/crashhandler.h"
#include
#include
#include
#include
/**
@brief The tst_CrashHandler class
Covers CrashHandler::formatInt(), the decimal formatter the signal
handler uses to write "Signal: 11" into a crash dump.
It is worth a test out of proportion to its size. It cannot use
snprintf(), which is not on the POSIX async-signal-safe list, so it is
hand-rolled; it runs only inside a signal handler, where nothing can
assert and a fault produces no diagnostic; and it is exercised only
when the application is already crashing, so a defect here would
corrupt or truncate exactly the dumps that matter and would never be
noticed in ordinary use.
The negation is the interesting part: -value on INT_MIN is undefined
behaviour, so the implementation goes through unsigned.
*/
class tst_CrashHandler : public QObject
{
Q_OBJECT
private slots:
void formatsZero();
void formatsPositive();
void formatsTheHandledSignals();
void formatsNegative();
void formatsIntMinWithoutOverflow();
void formatsIntMax();
void truncatesRatherThanOverflowing();
void writesNothingWhenThereIsNoRoom();
private:
/// Formats into a buffer poisoned with a sentinel, and fails if
/// anything past the returned length was touched.
static QByteArray format(int value, int size = 32);
};
QByteArray tst_CrashHandler::format(int value, int size)
{
char buffer[64];
memset(buffer, '\xAB', sizeof(buffer));
const int len = CrashHandler::formatInt(buffer, size, value);
// Nothing may be written past what was reported, nor past `size`.
for (int i = qMax(len, 0) ; i < static_cast(sizeof(buffer)) ; ++i) {
if (buffer[i] != '\xAB') {
return QByteArray("WROTE PAST END at ") + QByteArray::number(i);
}
}
if (len < 0 || len > size) {
return QByteArray("BAD LENGTH ") + QByteArray::number(len);
}
return QByteArray(buffer, len);
}
void tst_CrashHandler::formatsZero()
{
QCOMPARE(format(0), QByteArray("0"));
}
void tst_CrashHandler::formatsPositive()
{
QCOMPARE(format(1), QByteArray("1"));
QCOMPARE(format(9), QByteArray("9"));
QCOMPARE(format(10), QByteArray("10"));
QCOMPARE(format(1234567), QByteArray("1234567"));
}
/**
The values this actually sees in the field: kHandledSignals, as
written into the "Signal: N" line of every dump.
*/
void tst_CrashHandler::formatsTheHandledSignals()
{
QCOMPARE(format(SIGSEGV), QByteArray::number(SIGSEGV));
QCOMPARE(format(SIGABRT), QByteArray::number(SIGABRT));
QCOMPARE(format(SIGBUS), QByteArray::number(SIGBUS));
QCOMPARE(format(SIGFPE), QByteArray::number(SIGFPE));
QCOMPARE(format(SIGILL), QByteArray::number(SIGILL));
}
void tst_CrashHandler::formatsNegative()
{
QCOMPARE(format(-1), QByteArray("-1"));
QCOMPARE(format(-42), QByteArray("-42"));
}
/**
-INT_MIN is undefined behaviour; the implementation negates through
unsigned instead. A build that got this wrong would either trap under
-ftrapv/UBSan or silently print the wrong number.
*/
void tst_CrashHandler::formatsIntMinWithoutOverflow()
{
QCOMPARE(format(INT_MIN), QByteArray::number(INT_MIN));
}
void tst_CrashHandler::formatsIntMax()
{
QCOMPARE(format(INT_MAX), QByteArray::number(INT_MAX));
}
/**
A buffer too small must be filled and stopped at, never run past --
the handler passes a fixed 64-byte stack buffer and subtracts what it
has already used.
*/
void tst_CrashHandler::truncatesRatherThanOverflowing()
{
QCOMPARE(format(12345, 3), QByteArray("123"));
QCOMPARE(format(-12345, 3), QByteArray("-12"));
QCOMPARE(format(7, 1), QByteArray("7"));
}
void tst_CrashHandler::writesNothingWhenThereIsNoRoom()
{
QCOMPARE(format(123, 0), QByteArray());
QCOMPARE(format(0, 0), QByteArray());
QCOMPARE(format(-5, 0), QByteArray());
}
QTEST_APPLESS_MAIN(tst_CrashHandler)
#include "tst_crashhandler.moc"