🚧 wip: che kku po in to

This commit is contained in:
2026-02-17 22:23:20 +01:00
parent e3b4d42762
commit 9622c5b1fe
21 changed files with 1662 additions and 219 deletions
+40
View File
@@ -0,0 +1,40 @@
project(Tests LANGUAGES CXX)
find_package(Qt6 REQUIRED COMPONENTS Core Test)
add_executable(tst_configmgr
tst_configmgr.cpp
)
add_executable(tst_imagemodel
tst_imagemodel.cpp
)
add_test(NAME tst_configmgr COMMAND tst_configmgr)
add_test(NAME tst_imagemodel COMMAND tst_imagemodel)
target_link_libraries(tst_configmgr PRIVATE
Qt6::Core
Qt6::Test
Qt6::Gui
wallreel-core
)
target_link_libraries(tst_imagemodel PRIVATE
Qt6::Core
Qt6::Test
Qt6::Gui
Qt6::Quick
wallreel-core
)
target_include_directories(tst_configmgr PRIVATE
${CMAKE_SOURCE_DIR}/WallReel/Core
${CMAKE_BINARY_DIR}/generated
)
target_include_directories(tst_imagemodel PRIVATE
${CMAKE_SOURCE_DIR}/WallReel/Core
${CMAKE_BINARY_DIR}/generated
)
+396
View File
@@ -0,0 +1,396 @@
#include <QtGui/qcolor.h>
#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QSignalSpy>
#include <QTemporaryDir>
#include <QTest>
#include "configmgr.hpp"
class TestConfigMgr : public QObject {
Q_OBJECT
private slots:
void initTestCase();
void cleanupTestCase();
void testDefaults();
void testFullConfigParsing();
void testInvalidConfigValues();
void testWallpaperScanRecursive();
void testWallpaperScanNonRecursive();
void testWallpaperExcludes();
void testExplicitPaths();
void testImageExtensions();
void testSortTypes();
private:
QTemporaryDir m_tempDir;
QString m_configPath;
QString m_wallpaperRoot;
void writeConfig(const QJsonObject& json);
void createDummyFile(const QString& relPath);
};
void TestConfigMgr::initTestCase() {
QVERIFY(m_tempDir.isValid());
m_configPath = m_tempDir.path() + "/config.json";
m_wallpaperRoot = m_tempDir.path() + "/wallpapers";
QDir().mkpath(m_wallpaperRoot);
}
void TestConfigMgr::cleanupTestCase() {
}
void TestConfigMgr::writeConfig(const QJsonObject& json) {
QFile configFile(m_configPath);
QVERIFY(configFile.open(QIODevice::WriteOnly));
configFile.write(QJsonDocument(json).toJson());
configFile.close();
}
void TestConfigMgr::createDummyFile(const QString& relPath) {
QString absPath = m_wallpaperRoot + "/" + relPath;
QFileInfo fi(absPath);
QDir().mkpath(fi.absolutePath());
QFile file(absPath);
QVERIFY(file.open(QIODevice::WriteOnly));
file.write("foobar");
file.close();
}
void TestConfigMgr::testDefaults() {
// Empty config file
writeConfig(QJsonObject());
Config config(m_tempDir.path(), {}, m_configPath);
// Check Style Defaults
QCOMPARE(config.getImageWidth(), 320);
QCOMPARE(config.getImageHeight(), 200);
QCOMPARE(config.getImageFocusScale(), 1.5);
QCOMPARE(config.getWindowWidth(), 750);
QCOMPARE(config.getWindowHeight(), 500);
// Check Sort Defaults
QCOMPARE(config.getSortConfig().type, Config::SortType::Name);
QCOMPARE(config.getSortConfig().reverse, false);
// Check Action Defaults
QCOMPARE(config.getActionConfig().previewDebounceTime, 300);
QCOMPARE(config.getActionConfig().printSelected, false);
QCOMPARE(config.getActionConfig().printPreview, false);
QVERIFY(config.getActionConfig().onSelected.isEmpty());
QVERIFY(config.getActionConfig().onPreview.isEmpty());
QVERIFY(config.getActionConfig().onRestore.isEmpty());
QVERIFY(config.getActionConfig().saveState.isEmpty());
}
void TestConfigMgr::testFullConfigParsing() {
QJsonObject root;
// Wallpaper settings
QJsonObject wallpaperObj;
QJsonArray dirsArray;
QJsonObject dir1;
dir1["path"] = "/tmp/w1";
dir1["recursive"] = true;
dirsArray.append(dir1);
wallpaperObj["dirs"] = dirsArray;
QJsonArray pathsArray;
pathsArray.append("/tmp/p1.jpg");
wallpaperObj["paths"] = pathsArray;
QJsonArray excludesArray;
excludesArray.append(".*bad.*");
wallpaperObj["excludes"] = excludesArray;
root["wallpaper"] = wallpaperObj;
// Palette
QJsonArray palettesArray;
QJsonObject palette1;
palette1["name"] = "Default";
QJsonArray colorsArray;
QJsonObject color1;
color1["name"] = "Red";
color1["value"] = "#FF0000";
colorsArray.append(color1);
palette1["colors"] = colorsArray;
palettesArray.append(palette1);
root["palettes"] = palettesArray;
// Action
QJsonObject actionObj;
actionObj["printSelected"] = true;
actionObj["onSelected"] = "echo {{ path }}";
root["action"] = actionObj;
// Style
QJsonObject styleObj;
styleObj["image_width"] = 100;
styleObj["image_height"] = 100;
root["style"] = styleObj;
// Sort
QJsonObject sortObj;
sortObj["type"] = "date";
sortObj["reverse"] = true;
root["sort"] = sortObj;
writeConfig(root);
Config config(m_tempDir.path(), {}, m_configPath);
// Assertions
QCOMPARE(config.getWallpaperConfig().dirs.size(), 1);
QCOMPARE(config.getWallpaperConfig().dirs[0].path, "/tmp/w1");
QCOMPARE(config.getWallpaperConfig().dirs[0].recursive, true);
QCOMPARE(config.getWallpaperConfig().paths.size(), 1);
QCOMPARE(config.getWallpaperConfig().paths[0], "/tmp/p1.jpg");
QCOMPARE(config.getWallpaperConfig().excludes.size(), 1);
QCOMPARE(config.getWallpaperConfig().excludes[0].pattern(), ".*bad.*");
QCOMPARE(config.getPaletteConfig().palettes.size(), 1);
QCOMPARE(config.getPaletteConfig().palettes[0].name, "Default");
QCOMPARE(config.getPaletteConfig().palettes[0].colors.size(), 1);
QCOMPARE(config.getPaletteConfig().palettes[0].colors[0].name, "Red");
QCOMPARE(config.getPaletteConfig().palettes[0].colors[0].value.name().toLower(), "#ff0000");
QCOMPARE(config.getActionConfig().printSelected, true);
QCOMPARE(config.getActionConfig().onSelected, "echo {{ path }}");
QCOMPARE(config.getImageWidth(), 100);
QCOMPARE(config.getImageHeight(), 100);
QCOMPARE(config.getSortConfig().type, Config::SortType::Date);
QCOMPARE(config.getSortConfig().reverse, true);
}
void TestConfigMgr::testInvalidConfigValues() {
QJsonObject root;
QJsonObject styleObj;
styleObj["image_width"] = "not a number"; // Should be ignored
root["style"] = styleObj;
QJsonObject sortObj;
sortObj["type"] = "invalid_type";
root["sort"] = sortObj;
writeConfig(root);
Config config(m_tempDir.path(), {}, m_configPath);
// Should retain defaults
QCOMPARE(config.getImageWidth(), 320);
QCOMPARE(config.getSortConfig().type, Config::SortType::Name);
}
void TestConfigMgr::testWallpaperScanRecursive() {
// Setup files
createDummyFile("rec/root.jpg");
createDummyFile("rec/sub/deep.png"); // should be found
createDummyFile("rec/ignore.txt"); // should be ignored
QJsonObject root;
QJsonObject wallpaperObj;
QJsonArray dirsArray;
QJsonObject dirConfig;
dirConfig["path"] = m_wallpaperRoot + "/rec";
dirConfig["recursive"] = true;
dirsArray.append(dirConfig);
wallpaperObj["dirs"] = dirsArray;
root["wallpaper"] = wallpaperObj;
writeConfig(root);
Config config(m_tempDir.path(), {}, m_configPath);
QStringList wallpapers = config.getWallpapers();
QCOMPARE(wallpapers.size(), 2);
// Sort to verify presence
wallpapers.sort();
// Paths are absolute
QVERIFY(wallpapers[0].endsWith("root.jpg"));
QVERIFY(wallpapers[1].endsWith("deep.png"));
}
void TestConfigMgr::testWallpaperScanNonRecursive() {
createDummyFile("nonrec/root.jpg");
createDummyFile("nonrec/sub/deep.png");
QJsonObject root;
QJsonObject wallpaperObj;
QJsonArray dirsArray;
QJsonObject dirConfig;
dirConfig["path"] = m_wallpaperRoot + "/nonrec";
dirConfig["recursive"] = false;
dirsArray.append(dirConfig);
wallpaperObj["dirs"] = dirsArray;
root["wallpaper"] = wallpaperObj;
writeConfig(root);
Config config(m_tempDir.path(), {}, m_configPath);
QStringList wallpapers = config.getWallpapers();
QCOMPARE(wallpapers.size(), 1);
QVERIFY(wallpapers[0].endsWith("root.jpg"));
}
void TestConfigMgr::testWallpaperExcludes() {
createDummyFile("excl/good.jpg");
createDummyFile("excl/bad.jpg");
QJsonObject root;
QJsonObject wallpaperObj;
QJsonArray dirsArray;
QJsonObject dirConfig;
dirConfig["path"] = m_wallpaperRoot + "/excl";
dirConfig["recursive"] = false;
dirsArray.append(dirConfig);
wallpaperObj["dirs"] = dirsArray;
QJsonArray excludes;
excludes.append(".*bad\\.jpg$");
wallpaperObj["excludes"] = excludes;
root["wallpaper"] = wallpaperObj;
writeConfig(root);
Config config(m_tempDir.path(), {}, m_configPath);
QStringList wallpapers = config.getWallpapers();
QCOMPARE(wallpapers.size(), 1);
QVERIFY(wallpapers[0].endsWith("good.jpg"));
}
void TestConfigMgr::testExplicitPaths() {
createDummyFile("explicit/a.jpg");
QString absPath = m_wallpaperRoot + "/explicit/a.jpg";
QJsonObject root;
QJsonObject wallpaperObj;
QJsonArray pathsArray;
pathsArray.append(absPath);
wallpaperObj["paths"] = pathsArray;
root["wallpaper"] = wallpaperObj;
writeConfig(root);
Config config(m_tempDir.path(), {}, m_configPath);
QStringList wallpapers = config.getWallpapers();
QCOMPARE(wallpapers.size(), 1);
QCOMPARE(wallpapers[0], absPath);
}
void TestConfigMgr::testImageExtensions() {
createDummyFile("ext/image1.jpg");
createDummyFile("ext/image2.jpeg");
createDummyFile("ext/image3.png");
createDummyFile("ext/image4.bmp");
createDummyFile("ext/text.txt");
createDummyFile("ext/script.sh");
createDummyFile("ext/noext");
QJsonObject root;
QJsonObject wallpaperObj;
QJsonArray dirsArray;
QJsonObject dirConfig;
dirConfig["path"] = m_wallpaperRoot + "/ext";
dirConfig["recursive"] = false;
dirsArray.append(dirConfig);
wallpaperObj["dirs"] = dirsArray;
root["wallpaper"] = wallpaperObj;
writeConfig(root);
Config config(m_tempDir.path(), {}, m_configPath);
QStringList wallpapers = config.getWallpapers();
int imageCount = 0;
for (const auto& w : wallpapers) {
if (w.endsWith(".txt") || w.endsWith(".sh") || w.endsWith("noext")) {
QFAIL(qPrintable("Found non-image file: " + w));
}
imageCount++;
}
QVERIFY(imageCount >= 3);
}
void TestConfigMgr::testSortTypes() {
// 1. None sort
{
QJsonObject root;
QJsonObject sortObj;
sortObj["type"] = "none";
sortObj["reverse"] = false;
root["sort"] = sortObj;
writeConfig(root);
Config config(m_tempDir.path(), {}, m_configPath);
QCOMPARE(config.getSortConfig().type, Config::SortType::None);
QCOMPARE(config.getSortConfig().reverse, false);
}
// 2. Name sort (default)
{
QJsonObject root;
QJsonObject sortObj;
sortObj["type"] = "name";
sortObj["reverse"] = true;
root["sort"] = sortObj;
writeConfig(root);
Config config(m_tempDir.path(), {}, m_configPath);
QCOMPARE(config.getSortConfig().type, Config::SortType::Name);
QCOMPARE(config.getSortConfig().reverse, true);
}
// 3. Size sort
{
QJsonObject root;
QJsonObject sortObj;
sortObj["type"] = "size";
root["sort"] = sortObj;
writeConfig(root);
Config config(m_tempDir.path(), {}, m_configPath);
QCOMPARE(config.getSortConfig().type, Config::SortType::Size);
}
// 4. Date sort
{
QJsonObject root;
QJsonObject sortObj;
sortObj["type"] = "date";
root["sort"] = sortObj;
writeConfig(root);
Config config(m_tempDir.path(), {}, m_configPath);
QCOMPARE(config.getSortConfig().type, Config::SortType::Date);
}
// 5. Invalid sort -> fallback to default (Name)
{
QJsonObject root;
QJsonObject sortObj;
sortObj["type"] = "invalid_blah";
root["sort"] = sortObj;
writeConfig(root);
Config config(m_tempDir.path(), {}, m_configPath);
// Default initialized in Config constructor is Name
// But warning is logged
QCOMPARE(config.getSortConfig().type, Config::SortType::Name);
}
// 6. Case insensitivity for type string
{
QJsonObject root;
QJsonObject sortObj;
sortObj["type"] = "DaTe";
root["sort"] = sortObj;
writeConfig(root);
Config config(m_tempDir.path(), {}, m_configPath);
QCOMPARE(config.getSortConfig().type, Config::SortType::Date);
}
}
QTEST_MAIN(TestConfigMgr)
#include "tst_configmgr.moc"
+218
View File
@@ -0,0 +1,218 @@
#include <QDate>
#include <QSignalSpy>
#include <QTemporaryDir>
#include <QtTest>
#include "configmgr.hpp"
#include "imagemodel.hpp"
#include "imageprovider.hpp"
class TestImageModel : public QObject {
Q_OBJECT
private slots:
void initTestCase();
void testSortName();
void testSortDate();
void testSortSize();
private:
QTemporaryDir m_tempDir;
QString m_pathA;
QString m_pathB;
QString m_pathC;
void createTestFiles();
void waitForModel(ImageModel* model);
};
// clang-format off
// xxd <file> -i
static const unsigned char smallGIF[] = {
0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 0xf0, 0x00,
0x00, 0xcd, 0xcf, 0xd2, 0x00, 0x00, 0x00, 0x21, 0xf9, 0x04, 0x00, 0x00,
0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
0x00, 0x02, 0x02, 0x44, 0x01, 0x00, 0x3b
};
static const unsigned char mediumGIF[] = {
0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x02, 0x00, 0x02, 0x00, 0xf1, 0x00,
0x00, 0xb0, 0xb8, 0xc0, 0xb7, 0xbc, 0xc2, 0xd8, 0xdb, 0xda, 0xe2, 0xdd,
0xdb, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x02, 0x03, 0xd4, 0x10, 0x05,
0x00, 0x3b
};
static const unsigned char largeGIF[] = {
0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x03, 0x00, 0x03, 0x00, 0xf3, 0x00,
0x00, 0x80, 0x8b, 0x9c, 0xa9, 0xad, 0xac, 0xcf, 0xd5, 0xd6, 0xc9, 0xd2,
0xdc, 0xde, 0xd7, 0xd8, 0xdf, 0xdf, 0xdf, 0xd3, 0xda, 0xe0, 0xe9, 0xea,
0xeb, 0xf8, 0xf0, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x04, 0x07, 0xf0, 0x14, 0x24,
0x02, 0x19, 0xc0, 0x44, 0x00, 0x3b
};
// clang-format on
void TestImageModel::initTestCase() {
createTestFiles();
}
void TestImageModel::createTestFiles() {
QVERIFY(m_tempDir.isValid());
// Create files with specific names, sizes, dates
// a.gif: medium size, medium date
// c.gif: small size, old date
// b.gif: big size, new date
// Note: Names are a.gif, b.gif, c.gif for name sort.
m_pathA = m_tempDir.path() + "/a.gif";
m_pathB = m_tempDir.path() + "/b.gif";
m_pathC = m_tempDir.path() + "/c.gif";
{
QFile f(m_pathA);
QVERIFY(f.open(QIODevice::WriteOnly));
f.write(reinterpret_cast<const char*>(mediumGIF), sizeof(mediumGIF));
f.close();
}
{
QFile f(m_pathB);
QVERIFY(f.open(QIODevice::WriteOnly));
f.write(reinterpret_cast<const char*>(largeGIF), sizeof(largeGIF));
f.close();
}
{
QFile f(m_pathC);
QVERIFY(f.open(QIODevice::WriteOnly));
f.write(reinterpret_cast<const char*>(smallGIF), sizeof(smallGIF));
f.close();
}
// Set times
QDateTime now = QDateTime::currentDateTime();
QDateTime timeOld = now.addDays(-10);
QDateTime timeMid = now.addDays(-5);
QDateTime timeNew = now;
{
QFile f(m_pathC);
QVERIFY(f.open(QIODevice::ReadWrite));
QVERIFY(f.setFileTime(timeOld, QFileDevice::FileModificationTime));
}
{
QFile f(m_pathA);
QVERIFY(f.open(QIODevice::ReadWrite));
QVERIFY(f.setFileTime(timeMid, QFileDevice::FileModificationTime));
}
{
QFile f(m_pathB);
QVERIFY(f.open(QIODevice::ReadWrite));
QVERIFY(f.setFileTime(timeNew, QFileDevice::FileModificationTime));
}
}
void TestImageModel::waitForModel(ImageModel* model) {
if (!model->isLoading()) {
return;
}
QSignalSpy spy(model, &ImageModel::isLoadingChanged);
while (model->isLoading()) {
if (!spy.wait(5000)) {
qWarning() << "Timeout waiting for model to load";
break;
}
}
}
void TestImageModel::testSortName() {
Config::SortConfigItems sortConfig;
sortConfig.type = Config::SortType::Name;
sortConfig.reverse = false;
ImageProvider provider;
ImageModel model(provider, sortConfig, QSize(100, 100));
QStringList paths = {m_pathB, m_pathA, m_pathC}; // Unordered input
model.loadAndProcess(paths);
waitForModel(&model);
QCOMPARE(model.rowCount(), 3);
// Expected: a.gif, b.gif, c.gif
QCOMPARE(model.data(model.index(0), ImageModel::NameRole).toString(), "a.gif");
QCOMPARE(model.data(model.index(1), ImageModel::NameRole).toString(), "b.gif");
QCOMPARE(model.data(model.index(2), ImageModel::NameRole).toString(), "c.gif");
// Reverse
sortConfig.reverse = true;
model.sortUpdate();
QCOMPARE(model.rowCount(), 3);
// Expected: c.gif, b.gif, a.gif
QCOMPARE(model.data(model.index(0), ImageModel::NameRole).toString(), "c.gif");
QCOMPARE(model.data(model.index(1), ImageModel::NameRole).toString(), "b.gif");
QCOMPARE(model.data(model.index(2), ImageModel::NameRole).toString(), "a.gif");
}
void TestImageModel::testSortDate() {
Config::SortConfigItems sortConfig;
sortConfig.type = Config::SortType::Date;
sortConfig.reverse = false;
ImageProvider provider;
ImageModel model(provider, sortConfig, QSize(100, 100));
QStringList paths = {m_pathA, m_pathC, m_pathB};
model.loadAndProcess(paths);
waitForModel(&model);
QCOMPARE(model.rowCount(), 3);
// Expected: c (old), a (mid), b (new)
QCOMPARE(model.data(model.index(0), ImageModel::NameRole).toString(), "c.gif");
QCOMPARE(model.data(model.index(1), ImageModel::NameRole).toString(), "a.gif");
QCOMPARE(model.data(model.index(2), ImageModel::NameRole).toString(), "b.gif");
// Reverse (Newest first)
sortConfig.reverse = true;
model.sortUpdate();
QCOMPARE(model.data(model.index(0), ImageModel::NameRole).toString(), "b.gif");
QCOMPARE(model.data(model.index(1), ImageModel::NameRole).toString(), "a.gif");
QCOMPARE(model.data(model.index(2), ImageModel::NameRole).toString(), "c.gif");
}
void TestImageModel::testSortSize() {
Config::SortConfigItems sortConfig;
sortConfig.type = Config::SortType::Size;
sortConfig.reverse = false;
ImageProvider provider;
ImageModel model(provider, sortConfig, QSize(100, 100));
QStringList paths = {m_pathB, m_pathC, m_pathA};
model.loadAndProcess(paths);
waitForModel(&model);
QCOMPARE(model.rowCount(), 3);
QCOMPARE(model.data(model.index(0), ImageModel::NameRole).toString(), "c.gif");
QCOMPARE(model.data(model.index(1), ImageModel::NameRole).toString(), "a.gif");
QCOMPARE(model.data(model.index(2), ImageModel::NameRole).toString(), "b.gif");
// Reverse
sortConfig.reverse = true;
model.sortUpdate();
QCOMPARE(model.data(model.index(0), ImageModel::NameRole).toString(), "b.gif");
QCOMPARE(model.data(model.index(1), ImageModel::NameRole).toString(), "a.gif");
QCOMPARE(model.data(model.index(2), ImageModel::NameRole).toString(), "c.gif");
}
QTEST_MAIN(TestImageModel)
#include "tst_imagemodel.moc"