Skip to content
This repository was archived by the owner on Mar 28, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions src/settings/SettingsComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ void SettingsComponent::componentPostInitialize()
InputComponent::Get().registerHostCommand("set_setting", this, "setSettingCommand");
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void SettingsComponent::registerSection(SettingsSection* section)
{
m_sections.insert(section->sectionName(), section);
}

/////////////////////////////////////////////////////////////////////////////////////////
void SettingsComponent::cycleSettingCommand(const QString& args)
{
Expand Down Expand Up @@ -281,7 +287,7 @@ void SettingsComponent::loadConf(const QString& path, bool storage)
sec = new SettingsSection(section, PLATFORM_ANY, -1, this);
sec->setHidden(true);
sec->setStorage(true);
m_sections.insert(section, sec);
registerSection(sec);
}
else if (!sec)
{
Expand Down Expand Up @@ -398,7 +404,7 @@ void SettingsComponent::setValues(const QVariantMap& options)
section = new SettingsSection(key, PLATFORM_ANY, -1, this);
section->setHidden(true);
section->setStorage(true);
m_sections.insert(key, section);
registerSection(section);
}

if (values.isNull())
Expand Down Expand Up @@ -620,7 +626,7 @@ void SettingsComponent::parseSection(const QJsonObject& sectionObject)
section->registerSetting(setting);
}

m_sections.insert(sectionName, section);
registerSection(section);
}

/////////////////////////////////////////////////////////////////////////////////////////
Expand Down
2 changes: 2 additions & 0 deletions src/settings/SettingsComponent.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ class SettingsComponent : public ComponentBase
//
static bool resetAndSaveOldConfiguration();

void registerSection(SettingsSection* section);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally we wouldn't have to touch DisplayManager in order to test it, but I couldn't find a better way to access this internal data -- the other options felt sloppier IMO.


private:
explicit SettingsComponent(QObject *parent = nullptr);
bool loadDescription();
Expand Down
6 changes: 6 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,9 @@ target_link_libraries(test_settings jmp_core Qt6::Test)
target_include_directories(test_settings PRIVATE ${TEST_INCLUDE_DIRS})
target_compile_definitions(test_settings PRIVATE ${TEST_COMPILE_DEFS})
add_test(NAME test_settings COMMAND test_settings)

add_executable(test_displaymanager test_displaymanager.cpp)
target_link_libraries(test_displaymanager jmp_core Qt6::Test)
target_include_directories(test_displaymanager PRIVATE ${TEST_INCLUDE_DIRS})
target_compile_definitions(test_displaymanager PRIVATE ${TEST_COMPILE_DEFS})
add_test(NAME test_displaymanager COMMAND test_displaymanager)
229 changes: 229 additions & 0 deletions tests/test_displaymanager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
#include <QtTest/QtTest>
#include "../src/display/DisplayManager.h"
#include "../src/settings/SettingsComponent.h"
#include "../src/settings/SettingsSection.h"
#include "../src/settings/SettingsValue.h"

// Implements the pure virtuals (and some helpers) to test DisplayManager logic.
class TestableDisplayManager : public DisplayManager
{
public:
explicit TestableDisplayManager() : DisplayManager(nullptr), m_currentMode(0) {}

bool setDisplayMode(int, int) override { return true; }
int getCurrentDisplayMode(int) override { return m_currentMode; }
int getMainDisplay() override { return 0; }
int getDisplayFromPoint(int, int) override { return 0; }

void setCurrentMode(int mode) { m_currentMode = mode; }

void addMode(int id, int w, int h, int bpp, float hz, bool interlaced)
{
auto mode = DMVideoModePtr::create();
mode->m_id = id;
mode->m_width = w;
mode->m_height = h;
mode->m_bitsPerPixel = bpp;
mode->m_refreshRate = hz;
mode->m_interlaced = interlaced;
mode->m_privId = id;

if (!m_displays.contains(0))
{
auto disp = DMDisplayPtr::create();
disp->m_id = 0;
disp->m_name = "TestDisplay";
disp->m_privId = 0;
m_displays[0] = disp;
}
m_displays[0]->m_videoModes[id] = mode;
}

private:
int m_currentMode;
};


class TestDisplayManager : public QObject
{
Q_OBJECT

private:
void setAvoid25_30(bool on)
{
auto* section = SettingsComponent::Get().getSection(SETTINGS_SECTION_VIDEO);
section->setValue("refreshrate.avoid_25hz_30hz", on);
}

private slots:
void initTestCase();
void testExactRefreshRateMatch();
void testPrefers60HzOver30HzForFilm();
void testCloseRefreshRateMatch();
void testNoMatchWithDifferentResolution();
void testInterlacedPreference();
void testCurrentModeBonusBreaksTie();
void testAvoid25And30Hz();
void testNoSuitableModeReturnsNegative();
void testFindBestMode();
void testMultipleRefreshRate();
};

// Helper function: Populates a DisplayManager with some typical display modes.
static void setupStandardModes(TestableDisplayManager& mgr)
{
// id, w, h, bpp, hz, interlaced
mgr.addMode(0, 1920, 1080, 32, 24.0f, false);
mgr.addMode(1, 1920, 1080, 32, 30.0f, false);
mgr.addMode(2, 1920, 1080, 32, 50.0f, false);
mgr.addMode(3, 1920, 1080, 32, 60.0f, false); // This will be set as currentMode (see below)
mgr.addMode(4, 1920, 1080, 32, 23.976f, false);
mgr.addMode(5, 1920, 1080, 32, 59.94f, false);
mgr.setCurrentMode(3);
}

void TestDisplayManager::initTestCase()
{
auto* section = new SettingsSection(SETTINGS_SECTION_VIDEO, PLATFORM_ANY, -1,
&SettingsComponent::Get());
auto* avoidSetting = new SettingsValue("refreshrate.avoid_25hz_30hz",
false, PLATFORM_ANY, section);
section->registerSetting(avoidSetting);
SettingsComponent::Get().registerSection(section);
}

void TestDisplayManager::testExactRefreshRateMatch()
{
TestableDisplayManager mgr;
setupStandardModes(mgr);
DMMatchMediaInfo match(24.0f, false);

int best = mgr.findBestMatch(0, match);
QCOMPARE(best, 0); // 24 hz mode
}

void TestDisplayManager::testPrefers60HzOver30HzForFilm()
{
// Prefer 60 hz for 30 fps content.
TestableDisplayManager mgr;
setupStandardModes(mgr);
DMMatchMediaInfo match(30.0f, false);

setAvoid25_30(true);
int best = mgr.findBestMatch(0, match);
QCOMPARE(best, 3);
setAvoid25_30(false);
}

void TestDisplayManager::testCloseRefreshRateMatch()
{
// Prefer 24hz for 23.976 fps content.
TestableDisplayManager mgr;
setupStandardModes(mgr);
DMMatchMediaInfo match(23.976f, false);

int best = mgr.findBestMatch(0, match);
QCOMPARE(best, 4);
}

void TestDisplayManager::testNoMatchWithDifferentResolution()
{
// Prefer 1080p over 720p, regardless of framerate.
TestableDisplayManager mgr;
mgr.addMode(0, 1920, 1080, 32, 60.0f, false);
mgr.addMode(1, 1280, 720, 32, 24.0f, false);
mgr.setCurrentMode(0);

DMMatchMediaInfo match(24.0f, false);
int best = mgr.findBestMatch(0, match);
QCOMPARE(best, 0);
}

void TestDisplayManager::testInterlacedPreference()
{
// Prefer interlaced mode for interlaced content.
TestableDisplayManager mgr;
mgr.addMode(0, 1920, 1080, 32, 50.0f, false);
mgr.addMode(1, 1920, 1080, 32, 50.0f, true);
mgr.setCurrentMode(0); // non-interlaced

DMMatchMediaInfo match(50.0f, true);
int best = mgr.findBestMatch(0, match);
QCOMPARE(best, 1);
}

void TestDisplayManager::testCurrentModeBonusBreaksTie()
{
// Prefer current mode when multiple identical modes available.
TestableDisplayManager mgr;
mgr.addMode(0, 1920, 1080, 32, 60.0f, false);
mgr.addMode(1, 1920, 1080, 32, 60.0f, false);
mgr.setCurrentMode(1);

DMMatchMediaInfo match(60.0f, false);
int best = mgr.findBestMatch(0, match);
QCOMPARE(best, 1);
}

void TestDisplayManager::testAvoid25And30Hz()
{
// Prefer 50 hz for 25 fps content when avoid25_30 is enabled.
TestableDisplayManager mgr;
mgr.addMode(0, 1920, 1080, 32, 25.0f, false);
mgr.addMode(1, 1920, 1080, 32, 50.0f, false);
mgr.setCurrentMode(1);

DMMatchMediaInfo match(25.0f, false);
setAvoid25_30(true);
int best = mgr.findBestMatch(0, match);
QCOMPARE(best, 1);

// Disabling avoid25_30 should produce the opposite result.
setAvoid25_30(false);
best = mgr.findBestMatch(0, match);
QCOMPARE(best, 0);
}

void TestDisplayManager::testNoSuitableModeReturnsNegative()
{
// No preference when resolution doesn't match content.
TestableDisplayManager mgr;
mgr.addMode(0, 1920, 1080, 32, 60.0f, false);
mgr.addMode(1, 1280, 720, 32, 24.0f, false);
mgr.setCurrentMode(0);
mgr.m_displays[0]->m_videoModes.remove(0);

DMMatchMediaInfo match(24.0f, false);
int best = mgr.findBestMatch(0, match);

QCOMPARE(best, -1);
}

void TestDisplayManager::testFindBestMode()
{
// Prefer greatest refresh rate, greatest resolution, and non-interlaced.
TestableDisplayManager mgr;
mgr.addMode(0, 1280, 720, 32, 60.0f, false);
mgr.addMode(1, 1920, 1080, 32, 60.0f, false);
mgr.addMode(2, 1920, 1080, 32, 30.0f, true);
mgr.addMode(3, 1920, 1080, 32, 60.0f, true);

int best = mgr.findBestMode(0);
QCOMPARE(best, 1);
}

void TestDisplayManager::testMultipleRefreshRate()
{
// Prefer exact multiple of fps rate.
TestableDisplayManager mgr;
mgr.addMode(0, 1920, 1080, 32, 47.952f, false);
mgr.addMode(1, 1920, 1080, 32, 60.0f, false);
mgr.setCurrentMode(1);

DMMatchMediaInfo match(23.976f, false);
int best = mgr.findBestMatch(0, match);
QCOMPARE(best, 0);
}

QTEST_APPLESS_MAIN(TestDisplayManager)
#include "test_displaymanager.moc"