diff --git a/src/lib/base/LogOutputters.cpp b/src/lib/base/LogOutputters.cpp index a21e14f14..cde07eed9 100644 --- a/src/lib/base/LogOutputters.cpp +++ b/src/lib/base/LogOutputters.cpp @@ -138,7 +138,7 @@ bool FileLogOutputter::write(LogLevel::Level, const QString &message) if (file.size() > s_logFileSizeLimit) { const auto oldFile = QStringLiteral("%1.1").arg(m_fileName); - QFile::remove(m_fileName); + QFile::remove(oldFile); QFile::rename(m_fileName, oldFile); } diff --git a/src/unittests/base/CMakeLists.txt b/src/unittests/base/CMakeLists.txt index a6d955725..5cf0a543c 100644 --- a/src/unittests/base/CMakeLists.txt +++ b/src/unittests/base/CMakeLists.txt @@ -29,6 +29,14 @@ create_test( WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/src/lib/base" ) +create_test( + NAME FileLogOutputterTests + DEPENDS base + LIBS arch ${extra_libs} + SOURCE FileLogOutputterTests.cpp + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/src/lib/base" +) + create_test( NAME BaseExceptionTests DEPENDS base diff --git a/src/unittests/base/FileLogOutputterTests.cpp b/src/unittests/base/FileLogOutputterTests.cpp new file mode 100644 index 000000000..587a41145 --- /dev/null +++ b/src/unittests/base/FileLogOutputterTests.cpp @@ -0,0 +1,79 @@ +/* + * Deskflow -- mouse and keyboard sharing utility + * SPDX-FileCopyrightText: (C) 2026 Synergy App Ltd + * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception + */ + +#include "FileLogOutputterTests.h" + +#include +#include +#include + +void FileLogOutputterTests::init() +{ + QVERIFY(m_dir.isValid()); + m_fileName = m_dir.filePath(QStringLiteral("deskflow.log")); + QFile::remove(m_fileName); + QFile::remove(rotatedFileName()); +} + +QString FileLogOutputterTests::rotatedFileName() const +{ + return QStringLiteral("%1.1").arg(m_fileName); +} + +QString FileLogOutputterTests::readAll(const QString &fileName) +{ + QFile file(fileName); + if (!file.open(QFile::ReadOnly)) + return {}; + return QString::fromUtf8(file.readAll()); +} + +void FileLogOutputterTests::growLogPastSizeLimit(FileLogOutputter &outputter, const QString &marker) +{ + const QString padding(s_paddingSize, QLatin1Char('x')); + + QVERIFY(outputter.write(LogLevel::Level::Info, marker)); + + // rotating renames the live log away, so its absence is what ends the loop + for (int i = 0; i < s_maxPaddingWrites && QFileInfo::exists(m_fileName); ++i) + QVERIFY(outputter.write(LogLevel::Level::Info, padding)); +} + +void FileLogOutputterTests::write_multipleMessages_appendsEachLine() +{ + FileLogOutputter outputter(m_fileName); + + QVERIFY(outputter.write(LogLevel::Level::Info, QStringLiteral("first"))); + QVERIFY(outputter.write(LogLevel::Level::Info, QStringLiteral("second"))); + + QCOMPARE(readAll(m_fileName), QStringLiteral("first\nsecond\n")); +} + +void FileLogOutputterTests::write_sizeLimitExceeded_preservesPreviousLog() +{ + FileLogOutputter outputter(m_fileName); + + growLogPastSizeLimit(outputter, QStringLiteral("oldest entry")); + + QVERIFY2(QFileInfo::exists(rotatedFileName()), "log was discarded instead of rotated"); + QVERIFY(readAll(rotatedFileName()).startsWith(QStringLiteral("oldest entry\n"))); + + QVERIFY(outputter.write(LogLevel::Level::Info, QStringLiteral("after rotating"))); + QCOMPARE(readAll(m_fileName), QStringLiteral("after rotating\n")); +} + +void FileLogOutputterTests::write_rotatedLogExists_replacesIt() +{ + FileLogOutputter outputter(m_fileName); + + growLogPastSizeLimit(outputter, QStringLiteral("first batch")); + QVERIFY(readAll(rotatedFileName()).startsWith(QStringLiteral("first batch\n"))); + + growLogPastSizeLimit(outputter, QStringLiteral("second batch")); + QVERIFY(readAll(rotatedFileName()).startsWith(QStringLiteral("second batch\n"))); +} + +QTEST_MAIN(FileLogOutputterTests) diff --git a/src/unittests/base/FileLogOutputterTests.h b/src/unittests/base/FileLogOutputterTests.h new file mode 100644 index 000000000..1f15ec333 --- /dev/null +++ b/src/unittests/base/FileLogOutputterTests.h @@ -0,0 +1,35 @@ +/* + * Deskflow -- mouse and keyboard sharing utility + * SPDX-FileCopyrightText: (C) 2026 Synergy App Ltd + * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception + */ + +#pragma once + +#include "base/LogOutputters.h" + +#include +#include +#include + +class FileLogOutputterTests : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void init(); + void write_multipleMessages_appendsEachLine(); + void write_sizeLimitExceeded_preservesPreviousLog(); + void write_rotatedLogExists_replacesIt(); + +private: + QString rotatedFileName() const; + void growLogPastSizeLimit(FileLogOutputter &outputter, const QString &marker); + static QString readAll(const QString &fileName); + + static constexpr int s_paddingSize = 64 * 1024; + static constexpr int s_maxPaddingWrites = 64; + + QTemporaryDir m_dir; + QString m_fileName; +};