diff --git a/src/app/models/SettingsModel.h b/src/app/models/SettingsModel.h index 3118b0f2..5d67facb 100644 --- a/src/app/models/SettingsModel.h +++ b/src/app/models/SettingsModel.h @@ -12,6 +12,10 @@ class SettingsModel : public QObject { Q_PROPERTY(bool loggingEnabled READ loggingEnabled WRITE setLoggingEnabled NOTIFY loggingEnabledChanged) Q_PROPERTY(QString logFilePath READ logFilePath NOTIFY loggingEnabledChanged) + /// True only in debug builds. Gates developer-only affordances in QML — + /// notably the Test Exception button, which deliberately crashes the app + /// and must never be reachable in a shipped build. + Q_PROPERTY(bool debugBuild READ debugBuild CONSTANT) public: explicit SettingsModel(QObject *parent = nullptr); @@ -20,6 +24,16 @@ class SettingsModel : public QObject { void setLoggingEnabled(bool enabled); QString logFilePath() const; + /// Qt defines QT_NO_DEBUG for release configurations; packaging builds all + /// use CMAKE_BUILD_TYPE=Release, so this is false in every shipped package. + bool debugBuild() const { +#ifdef QT_NO_DEBUG + return false; +#else + return true; +#endif + } + Q_INVOKABLE void saveThemeDark(bool dark); Q_INVOKABLE void openBugReport(); Q_INVOKABLE void testCrash() { diff --git a/src/app/qml/AppSettingsView.qml b/src/app/qml/AppSettingsView.qml index f9464495..66b1806d 100644 --- a/src/app/qml/AppSettingsView.qml +++ b/src/app/qml/AppSettingsView.qml @@ -136,9 +136,10 @@ Item { } } - // Test crash button + // Test crash button — debug builds only. This deliberately throws, so + // it must never be reachable in a shipped package (see issue #146). Rectangle { - visible: SettingsModel.loggingEnabled + visible: SettingsModel.debugBuild && SettingsModel.loggingEnabled width: 180; height: 40 radius: 4; color: "transparent" border.color: "#ff4444"; border.width: 1 diff --git a/tests/test_settings_model.cpp b/tests/test_settings_model.cpp index da23d6db..c832c82c 100644 --- a/tests/test_settings_model.cpp +++ b/tests/test_settings_model.cpp @@ -90,4 +90,28 @@ TEST_F(SettingsModelTest, LogFilePathReturnsNonEmpty) { SUCCEED(); } + +// --- debug-build gating for the test-crash affordance ---------------------- +// +// The "Test Exception" button deliberately crashes the app. It was gated only +// on loggingEnabled, which is on by default, so it shipped to end users and +// produced real crash reports (issue #146). It must be a debug-build-only +// affordance. + +TEST_F(SettingsModelTest, DebugBuildPropertyIsExposedToQml) { + SettingsModel m; + const QVariant v = m.property("debugBuild"); + ASSERT_TRUE(v.isValid()) << "debugBuild must be a Q_PROPERTY so QML can gate on it"; + EXPECT_EQ(v.metaType().id(), QMetaType::Bool); +} + +TEST_F(SettingsModelTest, DebugBuildReflectsBuildConfiguration) { + SettingsModel m; +#ifdef QT_NO_DEBUG + EXPECT_FALSE(m.debugBuild()) << "release builds must not expose the crash affordance"; +#else + EXPECT_TRUE(m.debugBuild()) << "debug builds keep the crash affordance available"; +#endif +} + } // namespace logitune::test