From b62e604b2e5aad8ee387cd9b26150c738eb800cd Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 13 Nov 2023 08:52:51 +0100 Subject: [PATCH] Adds a persistent file used to store user settings (#2931) * Enable the loading of settings * Add a user interface for changing the settings in the launcher Co-authored-by: Emma Broman --- .gitignore | 1 + apps/OpenSpace/ext/launcher/CMakeLists.txt | 2 + .../ext/launcher/include/launcherwindow.h | 6 +- .../ext/launcher/include/settingsdialog.h | 74 ++ .../resources/images/cogwheel-highlight.png | Bin 0 -> 8227 bytes .../launcher/resources/images/cogwheel.png | Bin 0 -> 9197 bytes .../ext/launcher/resources/qss/launcher.qss | 19 + .../ext/launcher/resources/resources.qrc | 4 +- .../ext/launcher/src/launcherwindow.cpp | 36 +- .../ext/launcher/src/settingsdialog.cpp | 410 +++++++++ apps/OpenSpace/main.cpp | 39 +- include/openspace/engine/configuration.h | 9 +- include/openspace/engine/globals.h | 4 +- include/openspace/engine/settings.h | 62 ++ openspace.cfg | 8 - src/CMakeLists.txt | 2 + src/engine/configuration.cpp | 51 +- src/engine/globals.cpp | 8 +- src/engine/openspaceengine.cpp | 4 +- src/engine/settings.cpp | 184 ++++ src/rendering/renderengine.cpp | 2 +- tests/CMakeLists.txt | 1 + tests/main.cpp | 5 +- tests/test_settings.cpp | 863 ++++++++++++++++++ tests/test_sgctedit.cpp | 2 +- 25 files changed, 1754 insertions(+), 42 deletions(-) create mode 100644 apps/OpenSpace/ext/launcher/include/settingsdialog.h create mode 100644 apps/OpenSpace/ext/launcher/resources/images/cogwheel-highlight.png create mode 100644 apps/OpenSpace/ext/launcher/resources/images/cogwheel.png create mode 100644 apps/OpenSpace/ext/launcher/src/settingsdialog.cpp create mode 100644 include/openspace/engine/settings.h create mode 100644 src/engine/settings.cpp create mode 100644 tests/test_settings.cpp diff --git a/.gitignore b/.gitignore index 78bfbc5a7d..bb70a3b337 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,4 @@ COMMIT.md /modules/skybrowser/wwtimagedata doc config/schema/sgct.schema.json +settings.json diff --git a/apps/OpenSpace/ext/launcher/CMakeLists.txt b/apps/OpenSpace/ext/launcher/CMakeLists.txt index f7a465bd3c..42b1c0f042 100644 --- a/apps/OpenSpace/ext/launcher/CMakeLists.txt +++ b/apps/OpenSpace/ext/launcher/CMakeLists.txt @@ -27,6 +27,7 @@ include(${PROJECT_SOURCE_DIR}/support/cmake/set_openspace_compile_settings.cmake set(HEADER_FILES include/filesystemaccess.h include/launcherwindow.h + include/settingsdialog.h include/profile/actiondialog.h include/profile/additionalscriptsdialog.h include/profile/assetsdialog.h @@ -55,6 +56,7 @@ set(HEADER_FILES set(SOURCE_FILES src/launcherwindow.cpp src/filesystemaccess.cpp + src/settingsdialog.cpp src/profile/actiondialog.cpp src/profile/additionalscriptsdialog.cpp src/profile/assetsdialog.cpp diff --git a/apps/OpenSpace/ext/launcher/include/launcherwindow.h b/apps/OpenSpace/ext/launcher/include/launcherwindow.h index 840aabb6e4..f5efc4c1b6 100644 --- a/apps/OpenSpace/ext/launcher/include/launcherwindow.h +++ b/apps/OpenSpace/ext/launcher/include/launcherwindow.h @@ -34,7 +34,7 @@ #include #include -namespace openspace::configuration { struct Configuration; } +namespace openspace { struct Configuration; } class QComboBox; class QLabel; @@ -54,8 +54,8 @@ public: * in the tree structure. */ LauncherWindow(bool profileEnabled, - const openspace::configuration::Configuration& globalConfig, - bool sgctConfigEnabled, std::string sgctConfigName, QWidget* parent); + const openspace::Configuration& globalConfig, bool sgctConfigEnabled, + std::string sgctConfigName, QWidget* parent); /** * Returns bool for whether "start OpenSpace" was chosen when this window closed. diff --git a/apps/OpenSpace/ext/launcher/include/settingsdialog.h b/apps/OpenSpace/ext/launcher/include/settingsdialog.h new file mode 100644 index 0000000000..eb06e92b0d --- /dev/null +++ b/apps/OpenSpace/ext/launcher/include/settingsdialog.h @@ -0,0 +1,74 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2023 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_UI_LAUNCHER___SETTINGSDIALOG___H__ +#define __OPENSPACE_UI_LAUNCHER___SETTINGSDIALOG___H__ + +#include + +#include + +class QCheckBox; +class QComboBox; +class QDialogButtonBox; +class QLabel; +class QLineEdit; + +class SettingsDialog : public QDialog { +Q_OBJECT +public: + SettingsDialog(openspace::Settings settings, + QWidget* parent = nullptr); + +signals: + void saveSettings(openspace::Settings settings); + +private: + void createWidgets(); + void loadFromSettings(const openspace::Settings& settings); + void updateSaveButton(); + + void save(); + void reject(); + + QLineEdit* _configuration = nullptr; + QCheckBox* _rememberLastConfiguration = nullptr; + QLineEdit* _profile = nullptr; + QCheckBox* _rememberLastProfile = nullptr; + QComboBox* _propertyVisibility = nullptr; + QCheckBox* _bypassLauncher = nullptr; + QLabel* _bypassInformation = nullptr; + + struct { + QCheckBox* isEnabled = nullptr; + QLineEdit* location = nullptr; + } _mrf; + + QDialogButtonBox* _dialogButtons = nullptr; + + // The set of settings that we have while editing + openspace::Settings _currentEdit; +}; + +#endif // __OPENSPACE_UI_LAUNCHER___SETTINGSDIALOG___H__ diff --git a/apps/OpenSpace/ext/launcher/resources/images/cogwheel-highlight.png b/apps/OpenSpace/ext/launcher/resources/images/cogwheel-highlight.png new file mode 100644 index 0000000000000000000000000000000000000000..c4b46943ced9e205ad874c36f3b918e43ba7411d GIT binary patch literal 8227 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 z zW~T%O*Y<0@Z%Nrt6-O(?AWc;=%rYgtgwn#LK&g9QmY1XLLDQIwrRh_?(}npexHW5H zgE@t1DW4+i(Qcmv9~9bdOO)SsFMDJX=wl1OG3|_Bi$hl@#yPm)I7eXahuauW1mMI- z9p)C?Z(S3B2I%QpvC7JWy@n$oaRR-T0?=VB?JRV7OxGsPGzO&M@j5Op{J`LJ<-H2| z8JokDOmJY(FbO5`ZU1oY#C$to5(K@MoGbu5A^_sXw%RRndaG=g4c`|7)PstOl=gOY zeFK9HY587~o2q&vydOs?iPz12W)$W4<{9 literal 0 HcmV?d00001 diff --git a/apps/OpenSpace/ext/launcher/resources/images/cogwheel.png b/apps/OpenSpace/ext/launcher/resources/images/cogwheel.png new file mode 100644 index 0000000000000000000000000000000000000000..9f3465f332762f485ec9a7055abda85dddcc84c1 GIT binary patch literal 9197 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 qss/launcher.qss - images/openspace-horiz-logo-small.png + images/cogwheel.png + images/cogwheel-highlight.png images/launcher-background.png + images/openspace-horiz-logo-small.png images/outline_locked.png images/outline_unlocked.png diff --git a/apps/OpenSpace/ext/launcher/src/launcherwindow.cpp b/apps/OpenSpace/ext/launcher/src/launcherwindow.cpp index f1352e7444..75525bdf7f 100644 --- a/apps/OpenSpace/ext/launcher/src/launcherwindow.cpp +++ b/apps/OpenSpace/ext/launcher/src/launcherwindow.cpp @@ -25,6 +25,7 @@ #include "launcherwindow.h" #include "profile/profileedit.h" +#include "settingsdialog.h" #include #include @@ -60,6 +61,8 @@ namespace { constexpr int SmallItemWidth = 100; constexpr int SmallItemHeight = SmallItemWidth / 4; + constexpr int SettingsIconSize = 35; + namespace geometry { constexpr QRect BackgroundImage(0, 0, ScreenWidth, ScreenHeight); constexpr QRect LogoImage(LeftRuler, TopRuler, ItemWidth, ItemHeight); @@ -85,6 +88,12 @@ namespace { constexpr QRect VersionString( 5, ScreenHeight - SmallItemHeight, ItemWidth, SmallItemHeight ); + constexpr QRect SettingsButton( + ScreenWidth - SettingsIconSize - 5, + ScreenHeight - SettingsIconSize - 5, + SettingsIconSize, + SettingsIconSize + ); } // geometry std::optional loadProfileFromFile(QWidget* parent, std::string filename) { @@ -204,7 +213,7 @@ namespace { using namespace openspace; LauncherWindow::LauncherWindow(bool profileEnabled, - const configuration::Configuration& globalConfig, + const Configuration& globalConfig, bool sgctConfigEnabled, std::string sgctConfigName, QWidget* parent) : QMainWindow(parent) @@ -376,6 +385,31 @@ QWidget* LauncherWindow::createCentralWidget() { versionLabel->setObjectName("version-info"); versionLabel->setGeometry(geometry::VersionString); + QPushButton* settingsButton = new QPushButton(centralWidget); + settingsButton->setObjectName("settings"); + settingsButton->setGeometry(geometry::SettingsButton); + settingsButton->setIconSize(QSize(SettingsIconSize, SettingsIconSize)); + connect( + settingsButton, + &QPushButton::released, + [this]() { + using namespace openspace; + + Settings settings = loadSettings(); + + SettingsDialog dialog(std::move(settings), this); + connect( + &dialog, + &SettingsDialog::saveSettings, + [](Settings settings) { + saveSettings(settings, findSettings()); + } + ); + + dialog.exec(); + } + ); + return centralWidget; } diff --git a/apps/OpenSpace/ext/launcher/src/settingsdialog.cpp b/apps/OpenSpace/ext/launcher/src/settingsdialog.cpp new file mode 100644 index 0000000000..42ea8a0c70 --- /dev/null +++ b/apps/OpenSpace/ext/launcher/src/settingsdialog.cpp @@ -0,0 +1,410 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2023 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include "settingsdialog.h" + +#include "profile/line.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +SettingsDialog::SettingsDialog(openspace::Settings settings, + QWidget* parent) + : QDialog(parent) + , _currentEdit(settings) +{ + setWindowTitle("Settings"); + createWidgets(); + loadFromSettings(settings); + + // Setting the startup values for the control will have caused the Save button to be + // enabled, so we need to manually disable it again here + _dialogButtons->button(QDialogButtonBox::Save)->setEnabled(false); +} + +void SettingsDialog::createWidgets() { + // Layout of this dialog: + // + // ------------------------------------------------------- + // | Profile | + // | Starting Profile: | [oooooooooooooooooooo] | + // | [] Keep Last Profile | + // | Configuration | + // | Starting Configuration: | [oooooooooooooooooooo] | + // | [] Keep Last Configuration | + // | User Interface | + // | Property Visibility | DDDDDDDDDDDDDDDDDDDDD> | + // | [] Bypass Launcher | + // | Informational text about undoing the bypass setting | + // | MRF Caching | + // | [] Enable caching | + // | Cache location | [oooooooooooooooooooo] | + // | | | + // ------------------------------------------------------- + + QGridLayout* layout = new QGridLayout(this); + layout->setSizeConstraint(QLayout::SetFixedSize); + + { + QLabel* label = new QLabel("Profile"); + label->setObjectName("heading"); + layout->addWidget(label, 0, 0, 1, 2); + + QLabel* conf = new QLabel("Starting Profile"); + conf->setToolTip( + "With this setting, you can choose a profile that will be loaded the next " + "time you start the application" + ); + layout->addWidget(conf, 1, 0); + + _profile = new QLineEdit; + _profile->setToolTip(conf->toolTip()); + connect( + _profile, + &QLineEdit::textChanged, + [this]() { + std::string v = _profile->text().toStdString(); + if (v.empty()) { + _currentEdit.profile = std::nullopt; + } + else { + _currentEdit.profile = v; + } + + updateSaveButton(); + } + ); + layout->addWidget(_profile, 1, 1); + + _rememberLastProfile = new QCheckBox("Keep Last Profile"); + _rememberLastProfile->setToolTip( + "If this setting is checked, the application will remember the profile that " + "was loaded into OpenSpace and will use it at the next startup as well" + ); + connect( + _rememberLastProfile, + &QCheckBox::stateChanged, + [this]() { + if (_rememberLastProfile->isChecked()) { + _currentEdit.rememberLastProfile = true; + } + else { + _currentEdit.rememberLastProfile = std::nullopt; + } + + _profile->setDisabled(_rememberLastProfile->isChecked()); + updateSaveButton(); + } + ); + layout->addWidget(_rememberLastProfile, 2, 0, 1, 2); + } + + layout->addWidget(new Line(), 3, 0, 1, 2); + + { + QLabel* label = new QLabel("Configuration"); + label->setObjectName("heading"); + layout->addWidget(label, 4, 0, 1, 2); + + QLabel* conf = new QLabel("Starting Configuration"); + conf->setToolTip( + "With this setting, you can choose a window configuration that will be " + "loaded the next time you start the application" + ); + layout->addWidget(conf, 5, 0); + + _configuration = new QLineEdit; + _configuration->setToolTip(conf->toolTip()); + connect( + _configuration, + &QLineEdit::textChanged, + [this]() { + std::string v = _configuration->text().toStdString(); + if (v.empty()) { + _currentEdit.configuration = std::nullopt; + } + else { + _currentEdit.configuration = v; + } + + updateSaveButton(); + } + ); + layout->addWidget(_configuration, 5, 1); + + _rememberLastConfiguration = new QCheckBox("Keep Last Configuration"); + _rememberLastConfiguration->setToolTip( + "If this setting is checked, the application will remember the window " + "configuration and will use it at the next startup as well" + ); + connect( + _rememberLastConfiguration, + &QCheckBox::stateChanged, + [this]() { + if (_rememberLastConfiguration->isChecked()) { + _currentEdit.rememberLastConfiguration = true; + } + else { + _currentEdit.rememberLastConfiguration = std::nullopt; + } + _configuration->setDisabled(_rememberLastConfiguration->isChecked()); + updateSaveButton(); + } + ); + layout->addWidget(_rememberLastConfiguration, 6, 0, 1, 2); + } + + layout->addWidget(new Line(), 7, 0, 1, 2); + + { + QLabel* label = new QLabel("User Interface"); + label->setObjectName("heading"); + layout->addWidget(label, 8, 0, 1, 2); + + QLabel* conf = new QLabel("Property Visibility"); + conf->setToolTip( + "This setting sets the default visibility for properties in the application. " + "Note that these values are ordered, so all properties shown as a 'Novice " + "User' are also visible when selecting 'User', etc." + ); + layout->addWidget(conf, 9, 0); + + _propertyVisibility = new QComboBox; + _propertyVisibility->setToolTip(conf->toolTip()); + _propertyVisibility->addItems({ + "Novice User", + "User", + "Advanced User", + "Developer" + }); + _propertyVisibility->setCurrentText("User"); + connect( + _propertyVisibility, + &QComboBox::textActivated, + [this](const QString& value) { + using Visibility = openspace::properties::Property::Visibility; + if (value == "Novice User") { + _currentEdit.visibility = Visibility::NoviceUser; + } + else if (value == "User") { + // This is the default value + _currentEdit.visibility = std::nullopt; + } + else if (value == "Advanced User") { + _currentEdit.visibility = Visibility::AdvancedUser; + } + else if (value == "Developer") { + _currentEdit.visibility = Visibility::Developer; + } + else { + throw ghoul::MissingCaseException(); + } + + updateSaveButton(); + } + ); + layout->addWidget(_propertyVisibility, 9, 1); + + _bypassLauncher = new QCheckBox("Bypass Launcher"); + _bypassLauncher->setToolTip( + "If this value is selected, the Launcher will no longer be shown at startup. " + "Note that this also means that it will not be easy to get back to this " + "setting to reenable the Launcher either." + ); + connect( + _bypassLauncher, + &QCheckBox::stateChanged, + [this]() { + if (_bypassLauncher->isChecked()) { + _currentEdit.bypassLauncher = _bypassLauncher->isChecked(); + } + else { + _currentEdit.bypassLauncher = std::nullopt; + } + _bypassInformation->setVisible(_bypassLauncher->isChecked()); + updateSaveButton(); + } + ); + layout->addWidget(_bypassLauncher, 10, 0, 1, 2); + + _bypassInformation = new QLabel( + "Saving the settings with the bypass launcher enabled will cause this window " + "to not show up again, making it harder to undo this change. In case you " + "need to undo it, you need to open the settings.json and remove the line " + "that says '\"bypass\": true,'" + ); + _bypassInformation->setObjectName("information"); + _bypassInformation->setHidden(true); + _bypassInformation->setWordWrap(true); + layout->addWidget(_bypassInformation, 11, 0, 1, 2); + } + + layout->addWidget(new Line(), 12, 0, 1, 2); + + { + QLabel* label = new QLabel("MRF Caching"); + label->setObjectName("heading"); + layout->addWidget(label, 13, 0, 1, 2); + + _mrf.isEnabled = new QCheckBox("Enable Caching"); + _mrf.isEnabled->setToolTip( + "If this setting is checked, the MRF caching for globe layers will be " + "enabled. This means that all planetary images that are loaded over the " + "internet will also be cached locally and stored between application runs. " + "This will speedup the loading the second time at the expense of hard disk " + "space." + ); + connect( + _mrf.isEnabled, + &QCheckBox::stateChanged, + [this]() { + if (_mrf.isEnabled->isChecked()) { + _currentEdit.mrf.isEnabled = _mrf.isEnabled->isChecked(); + } + else { + _currentEdit.mrf.isEnabled = std::nullopt; + } + + _mrf.location->setDisabled(!_mrf.isEnabled->isChecked()); + updateSaveButton(); + } + ); + layout->addWidget(_mrf.isEnabled, 14, 0, 1, 2); + + QLabel* conf = new QLabel("Cache Location"); + conf->setToolTip( + "This is the place where the MRF cache files are located. Please note that " + "these files can potentially become quite large when using OpenSpace for a " + "long while and when visiting new places regularly. If this value is left " + "blank, the cached files will be stored in the 'mrf_cache' folder in the " + "OpenSpace base folder." + ); + layout->addWidget(conf, 15, 0); + + _mrf.location = new QLineEdit; + _mrf.location->setToolTip(conf->toolTip()); + _mrf.location->setDisabled(true); + connect( + _mrf.location, + &QLineEdit::editingFinished, + [this]() { + if (_mrf.location->text().isEmpty()) { + _currentEdit.mrf.location = std::nullopt; + } + else { + _currentEdit.mrf.location = _mrf.location->text().toStdString(); + } + updateSaveButton(); + } + ); + layout->addWidget(_mrf.location, 15, 1); + } + + layout->addWidget(new Line(), 16, 0, 1, 2); + + _dialogButtons = new QDialogButtonBox; + _dialogButtons->setStandardButtons( + QDialogButtonBox::Save | QDialogButtonBox::Cancel + ); + QObject::connect( + _dialogButtons, &QDialogButtonBox::accepted, + this, &SettingsDialog::save + ); + QObject::connect( + _dialogButtons, &QDialogButtonBox::rejected, + this, &SettingsDialog::reject + ); + layout->addWidget(_dialogButtons, 17, 1, 1, 1, Qt::AlignRight); +} + +void SettingsDialog::loadFromSettings(const openspace::Settings& settings) { + using namespace openspace; + + if (settings.configuration.has_value()) { + _configuration->setText(QString::fromStdString(*settings.configuration)); + } + if (settings.rememberLastConfiguration.has_value()) { + _rememberLastConfiguration->setChecked(*settings.rememberLastConfiguration); + } + + if (settings.profile.has_value()) { + _profile->setText(QString::fromStdString(*settings.profile)); + } + if (settings.rememberLastProfile.has_value()) { + _rememberLastProfile->setChecked(*settings.rememberLastProfile); + } + + if (settings.visibility.has_value()) { + using Visibility = openspace::properties::Property::Visibility; + Visibility vis = *settings.visibility; + switch (vis) { + case Visibility::NoviceUser: + _propertyVisibility->setCurrentText("Novice User"); + break; + case Visibility::User: + _propertyVisibility->setCurrentText("User"); + break; + case Visibility::AdvancedUser: + _propertyVisibility->setCurrentText("Advanced User"); + break; + case Visibility::Developer: + _propertyVisibility->setCurrentText("Developer"); + break; + case Visibility::Always: + case Visibility::Hidden: + break; + } + } + + if (settings.bypassLauncher.has_value()) { + _bypassLauncher->setChecked(*settings.bypassLauncher); + } + + if (settings.mrf.isEnabled.has_value()) { + _mrf.isEnabled->setChecked(*settings.mrf.isEnabled); + } + if (settings.mrf.location.has_value()) { + _mrf.location->setText(QString::fromStdString(*settings.mrf.location)); + } +} + +void SettingsDialog::updateSaveButton() { + _dialogButtons->button(QDialogButtonBox::Save)->setEnabled(true); +} + +void SettingsDialog::save() { + emit saveSettings(_currentEdit); + _dialogButtons->button(QDialogButtonBox::Save)->setEnabled(false); + QDialog::accept(); +} + +void SettingsDialog::reject() { + QDialog::reject(); +} diff --git a/apps/OpenSpace/main.cpp b/apps/OpenSpace/main.cpp index f618d43216..5618fc3522 100644 --- a/apps/OpenSpace/main.cpp +++ b/apps/OpenSpace/main.cpp @@ -22,10 +22,11 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include #include +#include #include #include +#include #include #include #include @@ -288,7 +289,7 @@ void mainInitFunc(GLFWwindow*) { // to them later in the RenderEngine std::filesystem::path screenshotPath = absPath("${SCREENSHOTS}"); FileSys.registerPathToken("${STARTUP_SCREENSHOT}", screenshotPath); - Settings::instance().setCapturePath(screenshotPath.string()); + sgct::Settings::instance().setCapturePath(screenshotPath.string()); LDEBUG("Initializing OpenSpace Engine started"); global::openSpaceEngine->initialize(); @@ -899,7 +900,7 @@ void setSgctDelegateFunctions() { sgctDelegate.takeScreenshot = [](bool applyWarping, std::vector windowIds) { ZoneScoped; - Settings::instance().setCaptureFromBackBuffer(applyWarping); + sgct::Settings::instance().setCaptureFromBackBuffer(applyWarping); Engine::instance().takeScreenshot(std::move(windowIds)); return Engine::instance().screenShotNumber(); }; @@ -976,7 +977,7 @@ void setSgctDelegateFunctions() { return currentWindow->swapGroupFrameNumber(); }; sgctDelegate.setScreenshotFolder = [](std::string path) { - Settings::instance().setCapturePath(std::move(path)); + sgct::Settings::instance().setCapturePath(std::move(path)); }; sgctDelegate.showStatistics = [](bool enabled) { Engine::instance().setStatsGraphVisibility(enabled); @@ -1049,7 +1050,7 @@ void checkCommandLineForSettings(int& argc, char** argv, bool& hasSGCT, bool& ha std::string setWindowConfigPresetForGui(const std::string labelFromCfgFile, bool haveCliSGCTConfig) { - configuration::Configuration& config = *global::configuration; + openspace::Configuration& config = *global::configuration; std::string preset; bool sgctConfigFileSpecifiedByLuaFunction = !config.sgctConfigNameInitialized.empty(); @@ -1225,7 +1226,7 @@ int main(int argc, char* argv[]) { } else { LDEBUG("Finding configuration"); - configurationFilePath = configuration::findConfiguration(); + configurationFilePath = findConfiguration(); } if (!std::filesystem::is_regular_file(configurationFilePath)) { @@ -1259,8 +1260,9 @@ int main(int argc, char* argv[]) { // Loading configuration from disk LDEBUG("Loading configuration from disk"); - *global::configuration = configuration::loadConfigurationFromFile( + *global::configuration = loadConfigurationFromFile( configurationFilePath.string(), + findSettings(), size ); @@ -1413,7 +1415,7 @@ int main(int argc, char* argv[]) { LDEBUG("Creating SGCT Engine"); std::vector arg(argv + 1, argv + argc); - Configuration config = parseArguments(arg); + sgct::Configuration config = parseArguments(arg); config::Cluster cluster = loadCluster(absPath(windowConfiguration).string()); Engine::Callbacks callbacks; @@ -1479,6 +1481,27 @@ int main(int argc, char* argv[]) { // Only timeout after 15 minutes Engine::instance().setSyncParameters(false, 15.f * 60.f); + { + openspace::Settings settings = loadSettings(); + settings.hasStartedBefore = true; + + if (settings.rememberLastProfile) { + std::filesystem::path p = global::configuration->profile; + std::filesystem::path reducedName = p.filename().replace_extension(); + settings.profile = reducedName.string(); + } + + if (settings.rememberLastConfiguration && + !global::configuration->sgctConfigNameInitialized.empty()) + { + // We only want to store the window configuration if it was not a dynamically + // created one + settings.configuration = global::configuration->windowConfiguration; + } + + saveSettings(settings, findSettings()); + } + LINFO("Starting rendering loop"); Engine::instance().exec(); LINFO("Ending rendering loop"); diff --git a/include/openspace/engine/configuration.h b/include/openspace/engine/configuration.h index 16e301e43c..20232bf756 100644 --- a/include/openspace/engine/configuration.h +++ b/include/openspace/engine/configuration.h @@ -34,9 +34,9 @@ #include #include -namespace openspace::documentation { struct Documentation; } +namespace openspace { -namespace openspace::configuration { +namespace documentation { struct Documentation; } struct Configuration { Configuration() = default; @@ -147,9 +147,10 @@ struct Configuration { std::filesystem::path findConfiguration(const std::string& filename = "openspace.cfg"); -Configuration loadConfigurationFromFile(const std::filesystem::path& filename, +Configuration loadConfigurationFromFile(const std::filesystem::path& configurationFile, + const std::filesystem::path& settingsFile, const glm::ivec2& primaryMonitorResolution); -} // namespace openspace::configuration +} // namespace openspace #endif // __OPENSPACE_CORE___CONFIGURATION___H__ diff --git a/include/openspace/engine/globals.h b/include/openspace/engine/globals.h index f6e294552b..55a1cda83e 100644 --- a/include/openspace/engine/globals.h +++ b/include/openspace/engine/globals.h @@ -33,6 +33,7 @@ namespace ghoul::fontrendering { class FontManager; } namespace openspace { +struct Configuration; class Dashboard; class DeferredcasterManager; class DownloadManager; @@ -50,7 +51,6 @@ class SyncEngine; class TimeManager; class VersionChecker; struct WindowDelegate; -namespace configuration { struct Configuration; } namespace interaction { struct JoystickInputStates; struct WebsocketInputStates; @@ -88,7 +88,7 @@ inline SyncEngine* syncEngine; inline TimeManager* timeManager; inline VersionChecker* versionChecker; inline WindowDelegate* windowDelegate; -inline configuration::Configuration* configuration; +inline Configuration* configuration; inline interaction::ActionManager* actionManager; inline interaction::InteractionMonitor* interactionMonitor; inline interaction::JoystickInputStates* joystickInputStates; diff --git a/include/openspace/engine/settings.h b/include/openspace/engine/settings.h new file mode 100644 index 0000000000..c43178def4 --- /dev/null +++ b/include/openspace/engine/settings.h @@ -0,0 +1,62 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2023 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_CORE___SETTINGS___H__ +#define __OPENSPACE_CORE___SETTINGS___H__ + +#include +#include +#include + +namespace openspace { + +struct Settings { + auto operator<=>(const Settings&) const = default; + + std::optional hasStartedBefore; + + std::optional configuration; + std::optional rememberLastConfiguration; + std::optional profile; + std::optional rememberLastProfile; + std::optional visibility; + std::optional bypassLauncher; + + struct MRF { + auto operator<=>(const MRF&) const = default; + + std::optional isEnabled; + std::optional location; + }; + MRF mrf; +}; + +std::filesystem::path findSettings(const std::string& filename = "settings.json"); + +Settings loadSettings(const std::filesystem::path& filename = findSettings()); +void saveSettings(const Settings& settings, const std::filesystem::path& filename); + +} // namespace openspace + +#endif // __OPENSPACE_CORE___SETTINGS___H__ diff --git a/openspace.cfg b/openspace.cfg index 9d39a87a04..29ad549d1e 100644 --- a/openspace.cfg +++ b/openspace.cfg @@ -161,19 +161,11 @@ ModuleConfigurations = { } } }, - WebBrowser = { - Enabled = true - }, WebGui = { Address = "localhost", HttpPort = 4680, WebSocketInterface = "DefaultWebSocketInterface" }, - CefWebGui = { - -- GuiScale = 2.0, - Enabled = true, - Visible = true - }, Space = { ShowExceptions = false } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c54bb478af..c0c383cbe2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -40,6 +40,7 @@ set(OPENSPACE_SOURCE engine/moduleengine_lua.inl engine/openspaceengine.cpp engine/openspaceengine_lua.inl + engine/settings.cpp engine/syncengine.cpp events/event.cpp events/eventengine.cpp @@ -221,6 +222,7 @@ set(OPENSPACE_HEADER ${PROJECT_SOURCE_DIR}/include/openspace/engine/moduleengine.h ${PROJECT_SOURCE_DIR}/include/openspace/engine/moduleengine.inl ${PROJECT_SOURCE_DIR}/include/openspace/engine/openspaceengine.h + ${PROJECT_SOURCE_DIR}/include/openspace/engine/settings.h ${PROJECT_SOURCE_DIR}/include/openspace/engine/syncengine.h ${PROJECT_SOURCE_DIR}/include/openspace/engine/windowdelegate.h ${PROJECT_SOURCE_DIR}/include/openspace/events/event.h diff --git a/src/engine/configuration.cpp b/src/engine/configuration.cpp index 8ddb7a6711..780a4298c6 100644 --- a/src/engine/configuration.cpp +++ b/src/engine/configuration.cpp @@ -25,12 +25,14 @@ #include #include +#include #include #include #include #include #include #include +#include #include namespace { @@ -309,7 +311,7 @@ namespace { #include "configuration_codegen.cpp" } // namespace -namespace openspace::configuration { +namespace openspace { void parseLuaState(Configuration& configuration) { using namespace ghoul::lua; @@ -456,6 +458,38 @@ void parseLuaState(Configuration& configuration) { c.bypassLauncher = p.bypassLauncher.value_or(c.bypassLauncher); } +void patchConfiguration(Configuration& configuration, const Settings& settings) { + if (settings.configuration.has_value()) { + configuration.windowConfiguration = *settings.configuration; + configuration.sgctConfigNameInitialized.clear(); + } + if (settings.profile.has_value()) { + configuration.profile = *settings.profile; + } + if (settings.visibility.has_value()) { + configuration.propertyVisibility = *settings.visibility; + } + if (settings.bypassLauncher.has_value()) { + configuration.bypassLauncher = *settings.bypassLauncher; + } + auto it = configuration.moduleConfigurations.find("GlobeBrowsing"); + // Just in case we have a configuration file that does not specify anything + // about the globebrowsing module + if (it == configuration.moduleConfigurations.end()) { + configuration.moduleConfigurations["GlobeBrowsing"] = ghoul::Dictionary(); + } + if (settings.mrf.isEnabled.has_value()) { + configuration.moduleConfigurations["GlobeBrowsing"].setValue( + "MRFCacheEnabled", *settings.mrf.isEnabled + ); + } + if (settings.mrf.location.has_value()) { + configuration.moduleConfigurations["GlobeBrowsing"].setValue( + "MRFCacheLocation", *settings.mrf.location + ); + } +} + documentation::Documentation Configuration::Documentation = codegen::doc("core_configuration"); @@ -483,10 +517,11 @@ std::filesystem::path findConfiguration(const std::string& filename) { } } -Configuration loadConfigurationFromFile(const std::filesystem::path& filename, +Configuration loadConfigurationFromFile(const std::filesystem::path& configurationFile, + const std::filesystem::path& settingsFile, const glm::ivec2& primaryMonitorResolution) { - ghoul_assert(std::filesystem::is_regular_file(filename), "File must exist"); + ghoul_assert(std::filesystem::is_regular_file(configurationFile), "File must exist"); Configuration result; @@ -503,11 +538,17 @@ Configuration loadConfigurationFromFile(const std::filesystem::path& filename, } // Load the configuration file into the state - ghoul::lua::runScriptFile(result.state, filename.string()); + ghoul::lua::runScriptFile(result.state, configurationFile.string()); parseLuaState(result); + if (std::filesystem::is_regular_file(settingsFile)) { + Settings settings = loadSettings(settingsFile); + + patchConfiguration(result, settings); + } + return result; } -} // namespace openspace::configuration +} // namespace openspace diff --git a/src/engine/globals.cpp b/src/engine/globals.cpp index 0d88e41d5f..047fd8a6a5 100644 --- a/src/engine/globals.cpp +++ b/src/engine/globals.cpp @@ -89,7 +89,7 @@ namespace { sizeof(TimeManager) + sizeof(VersionChecker) + sizeof(WindowDelegate) + - sizeof(configuration::Configuration) + + sizeof(Configuration) + sizeof(interaction::ActionManager) + sizeof(interaction::InteractionMonitor) + sizeof(interaction::WebsocketInputStates) + @@ -266,11 +266,11 @@ void create() { #endif // WIN32 #ifdef WIN32 - configuration = new (currentPos) configuration::Configuration; + configuration = new (currentPos) Configuration; ghoul_assert(configuration, "No configuration"); - currentPos += sizeof(configuration::Configuration); + currentPos += sizeof(Configuration); #else // ^^^ WIN32 / !WIN32 vvv - configuration = new configuration::Configuration; + configuration = new Configuration; #endif // WIN32 #ifdef WIN32 diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index b6eb9c8103..c273cf3bcc 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -321,7 +321,7 @@ void OpenSpaceEngine::initialize() { DocEng.addDocumentation(doc); } } - DocEng.addDocumentation(configuration::Configuration::Documentation); + DocEng.addDocumentation(Configuration::Documentation); // Register the provided shader directories ghoul::opengl::ShaderPreprocessor::addIncludePath(absPath("${SHADERS}")); @@ -519,7 +519,7 @@ void OpenSpaceEngine::initializeGL() { bool synchronous = global::configuration->openGLDebugContext.isSynchronous; setDebugOutput(DebugOutput(debugActive), SynchronousOutput(synchronous)); - for (const configuration::Configuration::OpenGLDebugContext::IdentifierFilter& f : + for (const Configuration::OpenGLDebugContext::IdentifierFilter& f : global::configuration->openGLDebugContext.identifierFilters) { setDebugMessageControl( diff --git a/src/engine/settings.cpp b/src/engine/settings.cpp new file mode 100644 index 0000000000..6ae9451721 --- /dev/null +++ b/src/engine/settings.cpp @@ -0,0 +1,184 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2023 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include + +namespace openspace { + +namespace { +template +std::optional get_to(nlohmann::json& obj, const std::string& key) +{ + auto it = obj.find(key); + if (it != obj.end()) { + return it->get(); + } + else { + return std::nullopt; + } +} +} // namespace + +namespace version1 { + Settings parseSettings(nlohmann::json json) { + ghoul_assert(json.at("version").get() == 1, "Wrong value"); + + Settings settings; + settings.hasStartedBefore = get_to(json, "started-before"); + settings.configuration = get_to(json, "config"); + settings.rememberLastConfiguration = get_to(json, "config-remember"); + settings.profile = get_to(json, "profile"); + settings.rememberLastProfile = get_to(json, "profile-remember"); + std::optional visibility = get_to(json, "visibility"); + if (visibility.has_value()) { + if (*visibility == "NoviceUser") { + settings.visibility = properties::Property::Visibility::NoviceUser; + } + else if (*visibility == "User") { + settings.visibility = properties::Property::Visibility::User; + } + else if (*visibility == "AdvancedUser") { + settings.visibility = properties::Property::Visibility::AdvancedUser; + } + else if (*visibility == "Developer") { + settings.visibility = properties::Property::Visibility::Developer; + } + else { + throw ghoul::RuntimeError(fmt::format( + "Unknown visibility value {}", *visibility + )); + } + } + settings.bypassLauncher = get_to(json, "bypass"); + + if (auto it = json.find("mrf"); it != json.end()) { + if (!it->is_object()) { + throw ghoul::RuntimeError("'mrf' is not an object"); + } + Settings::MRF mrf; + mrf.isEnabled = get_to(*it, "enabled"); + mrf.location = get_to(*it, "location"); + + if (mrf.isEnabled.has_value() || mrf.location.has_value()) { + settings.mrf = mrf; + } + } + + return settings; + } +} // namespace version1 + +std::filesystem::path findSettings(const std::string& filename) { + // Right now the settings file lives next to the openspace.cfg file + + std::filesystem::path path = findConfiguration(); + std::filesystem::path result = path.parent_path() / filename; + return result; +} + +Settings loadSettings(const std::filesystem::path& filename) { + if (!std::filesystem::is_regular_file(filename)) { + return Settings(); + } + + std::ifstream f(filename); + std::stringstream buffer; + buffer << f.rdbuf(); + std::string contents = buffer.str(); + + nlohmann::json setting = nlohmann::json::parse(contents); + if (setting.empty()) { + return Settings(); + } + + int version = setting.at("version").get(); + if (version == 1) { + return version1::parseSettings(setting); + } + + throw ghoul::RuntimeError(fmt::format( + "Unrecognized version for setting: {}", version + )); +} + +void saveSettings(const Settings& settings, const std::filesystem::path& filename) { + nlohmann::json json = nlohmann::json::object(); + + json["version"] = 1; + + if (settings.hasStartedBefore.has_value()) { + json["started-before"] = *settings.hasStartedBefore; + } + if (settings.configuration.has_value()) { + json["config"] = *settings.configuration; + } + if (settings.rememberLastConfiguration.has_value()) { + json["config-remember"] = *settings.rememberLastConfiguration; + } + if (settings.profile.has_value()) { + json["profile"] = *settings.profile; + } + if (settings.rememberLastProfile.has_value()) { + json["profile-remember"] = *settings.rememberLastProfile; + } + if (settings.visibility.has_value()) { + switch (*settings.visibility) { + case properties::Property::Visibility::NoviceUser: + json["visibility"] = "NoviceUser"; + break; + case properties::Property::Visibility::User: + json["visibility"] = "User"; + break; + case properties::Property::Visibility::AdvancedUser: + json["visibility"] = "AdvancedUser"; + break; + case properties::Property::Visibility::Developer: + json["visibility"] = "Developer"; + break; + + } + } + if (settings.bypassLauncher.has_value()) { + json["bypass"] = *settings.bypassLauncher; + } + nlohmann::json mrf = nlohmann::json::object(); + if (settings.mrf.isEnabled.has_value()) { + mrf["enabled"] = *settings.mrf.isEnabled; + } + if (settings.mrf.location.has_value()) { + mrf["location"] = *settings.mrf.location; + } + + if (!mrf.empty()) { + json["mrf"] = mrf; + } + + std::string content = json.dump(2); + std::ofstream f(filename); + f << content; +} + +} // namespace openspace diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index 2061c8b5e1..ec526678e3 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -523,7 +523,7 @@ void RenderEngine::initializeGL() { // initialized window _horizFieldOfView = static_cast(global::windowDelegate->getHorizFieldOfView()); - configuration::Configuration::FontSizes fontSize = global::configuration->fontSize; + Configuration::FontSizes fontSize = global::configuration->fontSize; { ZoneScopedN("Fonts"); TracyGpuZone("Fonts"); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ed0d17c326..98bc129461 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -38,6 +38,7 @@ add_executable( test_profile.cpp test_rawvolumeio.cpp test_scriptscheduler.cpp + test_settings.cpp test_sgctedit.cpp test_spicemanager.cpp test_timeconversion.cpp diff --git a/tests/main.cpp b/tests/main.cpp index a6afbb6ad7..0aa736f681 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -58,13 +58,14 @@ int main(int argc, char** argv) { ghoul::filesystem::FileSystem::Override::Yes ); - std::filesystem::path configFile = configuration::findConfiguration(); + std::filesystem::path configFile = findConfiguration(); // Register the base path as the directory where 'filename' lives std::filesystem::path base = configFile.parent_path(); FileSys.registerPathToken("${BASE}", base); - *global::configuration = configuration::loadConfigurationFromFile( + *global::configuration = loadConfigurationFromFile( configFile.string(), + "", glm::ivec2(0) ); global::openSpaceEngine->registerPathTokens(); diff --git a/tests/test_settings.cpp b/tests/test_settings.cpp new file mode 100644 index 0000000000..81875594be --- /dev/null +++ b/tests/test_settings.cpp @@ -0,0 +1,863 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2023 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include +#include +#include + +#include +#include +#include +#include + +using namespace openspace; + +TEST_CASE("Settings Load: Empty", "[settings]") { + constexpr std::string_view Source = R"( +{ + "version": 1 +} +)"; + + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_load_empty.json"; + { + std::ofstream f(file); + f << Source; + } + + Settings settings = loadSettings(file); + + CHECK(!settings.hasStartedBefore.has_value()); + CHECK(!settings.configuration.has_value()); + CHECK(!settings.rememberLastConfiguration.has_value()); + CHECK(!settings.profile.has_value()); + CHECK(!settings.rememberLastProfile.has_value()); + CHECK(!settings.visibility.has_value()); + CHECK(!settings.bypassLauncher.has_value()); + CHECK(!settings.mrf.isEnabled.has_value()); + CHECK(!settings.mrf.location.has_value()); +} + +TEST_CASE("Settings Load: Really Empty", "[settings]") { + constexpr std::string_view Source = R"( +{ +} +)"; + + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_load_really-empty.json"; + { + std::ofstream f(file); + f << Source; + } + + Settings settings = loadSettings(file); + + CHECK(!settings.hasStartedBefore.has_value()); + CHECK(!settings.configuration.has_value()); + CHECK(!settings.rememberLastConfiguration.has_value()); + CHECK(!settings.profile.has_value()); + CHECK(!settings.rememberLastProfile.has_value()); + CHECK(!settings.visibility.has_value()); + CHECK(!settings.bypassLauncher.has_value()); + CHECK(!settings.mrf.isEnabled.has_value()); + CHECK(!settings.mrf.location.has_value()); +} + +TEST_CASE("Settings Save: Empty", "[settings]") { + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_save_empty.json"; + + Settings srcSettings; + saveSettings(srcSettings, file); + + Settings cmpSettings = loadSettings(file); + CHECK(srcSettings == cmpSettings); +} + +TEST_CASE("Settings Load: Started Before", "[settings]") { + constexpr std::string_view Source = R"( +{ + "version": 1, + "started-before": false +} +)"; + + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_load_started-before.json"; + { + std::ofstream f(file); + f << Source; + } + + Settings settings = loadSettings(file); + + REQUIRE(settings.hasStartedBefore.has_value()); + CHECK(*settings.hasStartedBefore == false); + CHECK(!settings.configuration.has_value()); + CHECK(!settings.rememberLastConfiguration.has_value()); + CHECK(!settings.profile.has_value()); + CHECK(!settings.rememberLastProfile.has_value()); + CHECK(!settings.visibility.has_value()); + CHECK(!settings.bypassLauncher.has_value()); + CHECK(!settings.mrf.isEnabled.has_value()); + CHECK(!settings.mrf.location.has_value()); +} + +TEST_CASE("Settings Save: Started Before", "[settings]") { + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_save_started-before.json"; + + Settings srcSettings = { + .hasStartedBefore = false + }; + saveSettings(srcSettings, file); + + Settings cmpSettings = loadSettings(file); + CHECK(srcSettings == cmpSettings); +} + +TEST_CASE("Settings Load: Configuration", "[settings]") { + constexpr std::string_view Source = R"( +{ + "version": 1, + "config": "abc" +} +)"; + + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_load_config.json"; + { + std::ofstream f(file); + f << Source; + } + + Settings settings = loadSettings(file); + + CHECK(!settings.hasStartedBefore.has_value()); + REQUIRE(settings.configuration.has_value()); + CHECK(*settings.configuration == "abc"); + CHECK(!settings.rememberLastConfiguration.has_value()); + CHECK(!settings.profile.has_value()); + CHECK(!settings.rememberLastProfile.has_value()); + CHECK(!settings.visibility.has_value()); + CHECK(!settings.bypassLauncher.has_value()); + CHECK(!settings.mrf.isEnabled.has_value()); + CHECK(!settings.mrf.location.has_value()); +} + +TEST_CASE("Settings Save: Configuration", "[settings]") { + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_save_config.json"; + + Settings srcSettings = { + .configuration = "abc" + }; + saveSettings(srcSettings, file); + + Settings cmpSettings = loadSettings(file); + CHECK(srcSettings == cmpSettings); +} + +TEST_CASE("Settings Load: Configuration Remember", "[settings]") { + constexpr std::string_view Source = R"( +{ + "version": 1, + "config-remember": true +} +)"; + + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_load_config_remember.json"; + { + std::ofstream f(file); + f << Source; + } + + Settings settings = loadSettings(file); + + CHECK(!settings.hasStartedBefore.has_value()); + CHECK(!settings.configuration.has_value()); + REQUIRE(settings.rememberLastConfiguration.has_value()); + CHECK(*settings.rememberLastConfiguration == true); + CHECK(!settings.profile.has_value()); + CHECK(!settings.rememberLastProfile.has_value()); + CHECK(!settings.visibility.has_value()); + CHECK(!settings.bypassLauncher.has_value()); + CHECK(!settings.mrf.isEnabled.has_value()); + CHECK(!settings.mrf.location.has_value()); +} + +TEST_CASE("Settings Save: Configuration Remember", "[settings]") { + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_save_config_remember.json"; + + Settings srcSettings = { + .rememberLastConfiguration = true + }; + saveSettings(srcSettings, file); + + Settings cmpSettings = loadSettings(file); + CHECK(srcSettings == cmpSettings); +} + +TEST_CASE("Settings Load: Profile", "[settings]") { + constexpr std::string_view Source = R"( +{ + "version": 1, + "profile": "def" +} +)"; + + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_load_profile.json"; + { + std::ofstream f(file); + f << Source; + } + + Settings settings = loadSettings(file); + + CHECK(!settings.hasStartedBefore.has_value()); + CHECK(!settings.configuration.has_value()); + CHECK(!settings.rememberLastConfiguration.has_value()); + REQUIRE(settings.profile.has_value()); + CHECK(*settings.profile == "def"); + CHECK(!settings.rememberLastProfile.has_value()); + CHECK(!settings.visibility.has_value()); + CHECK(!settings.bypassLauncher.has_value()); + CHECK(!settings.mrf.isEnabled.has_value()); + CHECK(!settings.mrf.location.has_value()); +} + +TEST_CASE("Settings Save: Profile", "[settings]") { + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_save_profile.json"; + + Settings srcSettings = { + .profile = "def" + }; + saveSettings(srcSettings, file); + + Settings cmpSettings = loadSettings(file); + CHECK(srcSettings == cmpSettings); +} + +TEST_CASE("Settings Load: Profile Remember", "[settings]") { + constexpr std::string_view Source = R"( +{ + "version": 1, + "profile-remember": false +} +)"; + + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_load_profile_remember.json"; + { + std::ofstream f(file); + f << Source; + } + + Settings settings = loadSettings(file); + + CHECK(!settings.hasStartedBefore.has_value()); + CHECK(!settings.configuration.has_value()); + CHECK(!settings.rememberLastConfiguration.has_value()); + CHECK(!settings.profile.has_value()); + REQUIRE(settings.rememberLastProfile.has_value()); + CHECK(*settings.rememberLastProfile == false); + CHECK(!settings.visibility.has_value()); + CHECK(!settings.bypassLauncher.has_value()); + CHECK(!settings.mrf.isEnabled.has_value()); + CHECK(!settings.mrf.location.has_value()); +} + +TEST_CASE("Settings Save: Profile Remember", "[settings]") { + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_save_profile.json"; + + Settings srcSettings = { + .rememberLastProfile = false + }; + saveSettings(srcSettings, file); + + Settings cmpSettings = loadSettings(file); + CHECK(srcSettings == cmpSettings); +} + +TEST_CASE("Settings Load: Visibility/NoviceUser", "[settings]") { + constexpr std::string_view Source = R"( +{ + "version": 1, + "visibility": "NoviceUser" +} +)"; + + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_load_visibility_novice.json"; + { + std::ofstream f(file); + f << Source; + } + + Settings settings = loadSettings(file); + + CHECK(!settings.hasStartedBefore.has_value()); + CHECK(!settings.configuration.has_value()); + CHECK(!settings.rememberLastConfiguration.has_value()); + CHECK(!settings.profile.has_value()); + CHECK(!settings.rememberLastProfile.has_value()); + REQUIRE(settings.visibility.has_value()); + CHECK(*settings.visibility == properties::Property::Visibility::NoviceUser); + CHECK(!settings.bypassLauncher.has_value()); + CHECK(!settings.mrf.isEnabled.has_value()); + CHECK(!settings.mrf.location.has_value()); +} + +TEST_CASE("Settings Save: Visibility/NoviceUser", "[settings]") { + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_save_noviceuser.json"; + + Settings srcSettings = { + .visibility = openspace::properties::Property::Visibility::NoviceUser + }; + saveSettings(srcSettings, file); + + Settings cmpSettings = loadSettings(file); + CHECK(srcSettings == cmpSettings); +} + +TEST_CASE("Settings Load: Visibility/User", "[settings]") { + constexpr std::string_view Source = R"( +{ + "version": 1, + "visibility": "User" +} +)"; + + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_load_visibility_user.json"; + { + std::ofstream f(file); + f << Source; + } + + Settings settings = loadSettings(file); + + CHECK(!settings.hasStartedBefore.has_value()); + CHECK(!settings.configuration.has_value()); + CHECK(!settings.rememberLastConfiguration.has_value()); + CHECK(!settings.profile.has_value()); + CHECK(!settings.rememberLastProfile.has_value()); + REQUIRE(settings.visibility.has_value()); + CHECK(*settings.visibility == properties::Property::Visibility::User); + CHECK(!settings.bypassLauncher.has_value()); + CHECK(!settings.mrf.isEnabled.has_value()); + CHECK(!settings.mrf.location.has_value()); +} + +TEST_CASE("Settings Save: Visibility/User", "[settings]") { + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_save_user.json"; + + Settings srcSettings = { + .visibility = openspace::properties::Property::Visibility::User + }; + saveSettings(srcSettings, file); + + Settings cmpSettings = loadSettings(file); + CHECK(srcSettings == cmpSettings); +} + +TEST_CASE("Settings Load: Visibility/AdvancedUser", "[settings]") { + constexpr std::string_view Source = R"( +{ + "version": 1, + "visibility": "AdvancedUser" +} +)"; + + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_load_visibility_advanced.json"; + { + std::ofstream f(file); + f << Source; + } + + Settings settings = loadSettings(file); + + CHECK(!settings.hasStartedBefore.has_value()); + CHECK(!settings.configuration.has_value()); + CHECK(!settings.rememberLastConfiguration.has_value()); + CHECK(!settings.profile.has_value()); + CHECK(!settings.rememberLastProfile.has_value()); + REQUIRE(settings.visibility.has_value()); + CHECK(*settings.visibility == properties::Property::Visibility::AdvancedUser); + CHECK(!settings.bypassLauncher.has_value()); + CHECK(!settings.mrf.isEnabled.has_value()); + CHECK(!settings.mrf.location.has_value()); +} + +TEST_CASE("Settings Save: Visibility/AdvancedUser", "[settings]") { + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_save_advanceduser.json"; + + Settings srcSettings = { + .visibility = openspace::properties::Property::Visibility::AdvancedUser + }; + saveSettings(srcSettings, file); + + Settings cmpSettings = loadSettings(file); + CHECK(srcSettings == cmpSettings); +} + +TEST_CASE("Settings Load: Visibility/Developer", "[settings]") { + constexpr std::string_view Source = R"( +{ + "version": 1, + "visibility": "Developer" +} +)"; + + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_load_visibility_developer.json"; + { + std::ofstream f(file); + f << Source; + } + + Settings settings = loadSettings(file); + + CHECK(!settings.hasStartedBefore.has_value()); + CHECK(!settings.configuration.has_value()); + CHECK(!settings.rememberLastConfiguration.has_value()); + CHECK(!settings.profile.has_value()); + CHECK(!settings.rememberLastProfile.has_value()); + REQUIRE(settings.visibility.has_value()); + CHECK(*settings.visibility == properties::Property::Visibility::Developer); + CHECK(!settings.bypassLauncher.has_value()); + CHECK(!settings.mrf.isEnabled.has_value()); + CHECK(!settings.mrf.location.has_value()); +} + +TEST_CASE("Settings Save: Visibility/Developer", "[settings]") { + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_save_developer.json"; + + Settings srcSettings = { + .visibility = openspace::properties::Property::Visibility::Developer + }; + saveSettings(srcSettings, file); + + Settings cmpSettings = loadSettings(file); + CHECK(srcSettings == cmpSettings); +} + +TEST_CASE("Settings Load: Bypass Launcher", "[settings]") { + constexpr std::string_view Source = R"( +{ + "version": 1, + "bypass": false +} +)"; + + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_load_bypass.json"; + { + std::ofstream f(file); + f << Source; + } + + Settings settings = loadSettings(file); + + CHECK(!settings.hasStartedBefore.has_value()); + CHECK(!settings.configuration.has_value()); + CHECK(!settings.rememberLastConfiguration.has_value()); + CHECK(!settings.profile.has_value()); + CHECK(!settings.rememberLastProfile.has_value()); + CHECK(!settings.visibility.has_value()); + REQUIRE(settings.bypassLauncher.has_value()); + CHECK(*settings.bypassLauncher == false); + CHECK(!settings.mrf.isEnabled.has_value()); + CHECK(!settings.mrf.location.has_value()); +} + +TEST_CASE("Settings Save: Bypass Launcher", "[settings]") { + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_save_bypass.json"; + + Settings srcSettings = { + .bypassLauncher = false + }; + saveSettings(srcSettings, file); + + Settings cmpSettings = loadSettings(file); + CHECK(srcSettings == cmpSettings); +} + +TEST_CASE("Settings Load: MRF IsEnabled", "[settings]") { + constexpr std::string_view Source = R"( +{ + "version": 1, + "mrf": { + "enabled": true + } +} +)"; + + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_load_mrf_isenabled.json"; + { + std::ofstream f(file); + f << Source; + } + + Settings settings = loadSettings(file); + + CHECK(!settings.hasStartedBefore.has_value()); + CHECK(!settings.configuration.has_value()); + CHECK(!settings.rememberLastConfiguration.has_value()); + CHECK(!settings.profile.has_value()); + CHECK(!settings.rememberLastProfile.has_value()); + CHECK(!settings.visibility.has_value()); + CHECK(!settings.bypassLauncher.has_value()); + REQUIRE(settings.mrf.isEnabled.has_value()); + CHECK(*settings.mrf.isEnabled == true); + CHECK(!settings.mrf.location.has_value()); +} + +TEST_CASE("Settings Save: MRF IsEnabled", "[settings]") { + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_save_mrf_isenabled.json"; + + Settings srcSettings = { + .mrf = Settings::MRF { + .isEnabled = true + } + }; + saveSettings(srcSettings, file); + + Settings cmpSettings = loadSettings(file); + CHECK(srcSettings == cmpSettings); +} + +TEST_CASE("Settings Load: MRF Location", "[settings]") { + constexpr std::string_view Source = R"( +{ + "version": 1, + "mrf": { + "location": "ghi" + } +} +)"; + + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_load_mrf_location.json"; + { + std::ofstream f(file); + f << Source; + } + + Settings settings = loadSettings(file); + + CHECK(!settings.hasStartedBefore.has_value()); + CHECK(!settings.configuration.has_value()); + CHECK(!settings.rememberLastConfiguration.has_value()); + CHECK(!settings.profile.has_value()); + CHECK(!settings.rememberLastProfile.has_value()); + CHECK(!settings.visibility.has_value()); + CHECK(!settings.bypassLauncher.has_value()); + CHECK(!settings.mrf.isEnabled.has_value()); + REQUIRE(settings.mrf.location.has_value()); + CHECK(*settings.mrf.location == "ghi"); +} + +TEST_CASE("Settings Save: MRF Location", "[settings]") { + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_save_mrf_location.json"; + + Settings srcSettings = { + .mrf = Settings::MRF { + .location = "ghi" + } + }; + saveSettings(srcSettings, file); + + Settings cmpSettings = loadSettings(file); + CHECK(srcSettings == cmpSettings); +} + +TEST_CASE("Settings Load: Full", "[settings]") { + constexpr std::string_view Source = R"( +{ + "version": 1, + "started-before": false, + "config": "abc", + "config-remember": true, + "profile": "def", + "profile-remember": false, + "visibility": "NoviceUser", + "bypass": false, + "mrf": { + "enabled": true, + "location": "ghi" + } +} +)"; + + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_load_full.json"; + { + std::ofstream f(file); + f << Source; + } + + Settings settings = loadSettings(file); + + REQUIRE(settings.hasStartedBefore.has_value()); + CHECK(*settings.hasStartedBefore == false); + REQUIRE(settings.configuration.has_value()); + CHECK(*settings.configuration == "abc"); + REQUIRE(settings.rememberLastConfiguration.has_value()); + CHECK(*settings.rememberLastConfiguration == true); + REQUIRE(settings.profile.has_value()); + CHECK(*settings.profile == "def"); + REQUIRE(settings.rememberLastProfile.has_value()); + CHECK(*settings.rememberLastProfile == false); + REQUIRE(settings.visibility.has_value()); + CHECK(*settings.visibility == properties::Property::Visibility::NoviceUser); + REQUIRE(settings.bypassLauncher.has_value()); + CHECK(*settings.bypassLauncher == false); + REQUIRE(settings.mrf.isEnabled.has_value()); + CHECK(*settings.mrf.isEnabled == true); + REQUIRE(settings.mrf.location.has_value()); + CHECK(*settings.mrf.location == "ghi"); +} + +TEST_CASE("Settings Save: Full", "[settings]") { + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_save_full.json"; + + Settings srcSettings = { + .hasStartedBefore = false, + .configuration = "abc", + .rememberLastConfiguration = true, + .profile = "def", + .rememberLastProfile = false, + .visibility = openspace::properties::Property::Visibility::NoviceUser, + .bypassLauncher = false, + .mrf = Settings::MRF { + .isEnabled = true, + .location = "ghi" + } + }; + saveSettings(srcSettings, file); + + Settings cmpSettings = loadSettings(file); + CHECK(srcSettings == cmpSettings); +} + +TEST_CASE("Settings Load Fail: Illegal version", "[settings]") { + constexpr std::string_view Source = R"( +{ + "version": -1 +} +)"; + + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_load_fail_illegal_version.json"; + { + std::ofstream f(file); + f << Source; + } + + CHECK_THROWS(loadSettings(file)); +} + +TEST_CASE("Settings Load Fail: Started before", "[settings]") { + constexpr std::string_view Source = R"( +{ + "version": 1, + "started-before": "abc" +} +)"; + + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_load_fail_started-before.json"; + { + std::ofstream f(file); + f << Source; + } + + CHECK_THROWS(loadSettings(file)); +} + +TEST_CASE("Settings Load Fail: Config", "[settings]") { + constexpr std::string_view Source = R"( +{ + "version": 1, + "config": 1 +} +)"; + + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_load_fail_config.json"; + { + std::ofstream f(file); + f << Source; + } + + CHECK_THROWS(loadSettings(file)); +} + +TEST_CASE("Settings Load Fail: Profile", "[settings]") { + constexpr std::string_view Source = R"( +{ + "version": 1, + "profile": 1 +} +)"; + + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_load_fail_profile.json"; + { + std::ofstream f(file); + f << Source; + } + + CHECK_THROWS(loadSettings(file)); +} + +TEST_CASE("Settings Load Fail: Visibility type", "[settings]") { + constexpr std::string_view Source = R"( +{ + "version": 1, + "visibility": 1 +} +)"; + + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_load_fail_visibility_type.json"; + { + std::ofstream f(file); + f << Source; + } + + CHECK_THROWS(loadSettings(file)); +} + +TEST_CASE("Settings Load Fail: Visibility value", "[settings]") { + constexpr std::string_view Source = R"( +{ + "version": 1, + "visibility": "abc" +} +)"; + + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_load_fail_visibility_value.json"; + { + std::ofstream f(file); + f << Source; + } + + CHECK_THROWS(loadSettings(file)); +} + +TEST_CASE("Settings Load Fail: Bypass Launcher", "[settings]") { + constexpr std::string_view Source = R"( +{ + "version": 1, + "bypass": 1 +} +)"; + + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_load_fail_bypass.json"; + { + std::ofstream f(file); + f << Source; + } + + CHECK_THROWS(loadSettings(file)); +} + +TEST_CASE("Settings Load Fail: MRF", "[settings]") { + constexpr std::string_view Source = R"( +{ + "version": 1, + "mrf": 1 +} +)"; + + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_load_fail_mrf.json"; + { + std::ofstream f(file); + f << Source; + } + + CHECK_THROWS(loadSettings(file)); +} + +TEST_CASE("Settings Load Fail: MRF/enabled", "[settings]") { + constexpr std::string_view Source = R"( +{ + "version": 1, + "mrf": { + "enabled": 1 + } +} +)"; + + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_load_fail_mrf_enabled.json"; + { + std::ofstream f(file); + f << Source; + } + + CHECK_THROWS(loadSettings(file)); +} + +TEST_CASE("Settings Load Fail: MRF/location", "[settings]") { + constexpr std::string_view Source = R"( +{ + "version": 1, + "mrf": { + "location": 1 + } +} +)"; + + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::filesystem::path file = path / "test_settings_load_fail_mrf_location.json"; + { + std::ofstream f(file); + f << Source; + } + + CHECK_THROWS(loadSettings(file)); +} diff --git a/tests/test_sgctedit.cpp b/tests/test_sgctedit.cpp index ea224bf9c0..bca8b65e8d 100644 --- a/tests/test_sgctedit.cpp +++ b/tests/test_sgctedit.cpp @@ -34,7 +34,7 @@ #include #include -using namespace openspace::configuration; +using namespace openspace; namespace { std::string stringify(const std::string filename) {