mirror of
https://github.com/transmission/transmission.git
synced 2026-02-15 07:26:49 +00:00
test: add unit tests for Prefs (#8112)
* fix: hicpp-use-auto,modernize-use-auto * refactor: make Prefs::getKey() a static method refactor: make Prefs::isCore() a static method refactor: make Prefs::type() a static method * refactor: Application takes a Prefs& arg, not a std::unique_ptr<Prefs> arg * fix: bugprone-exception-escape save settings by calling prefs.save() from main() * refactor: load settings by calling prefs.load() from main() * refactor: use preferred declaration order in Prefs * fixup! fix: bugprone-exception-escape * refactor: add Prefs::current_values() * refactor: clean up namespace use in Prefs.cc * feat: add QString, QDateTime serializers * test: add scaffolding for testing Qt code test: add tests for Prefs * refactor: remove unused #includes * build: add clang-tidy rules to tests/qt/ * refactor: clean up the new test code a little * chore: add missing copyright statement * ci: ensure Qt6Test is installed build: check for QTest when ENABLE_TESTS + ENABLE_QT are ON * fixup! feat: add QString, QDateTime serializers * fix: Wswitch warning * build: do not disable tests in release/windows/build-qt5.psl, build-qt6.psl * ci: set QT_QPA_PLATFORM for running new Qt tests * test: build cleanly in Qt 5.15 * fixup! fixup! feat: add QString, QDateTime serializers fix QDateTime serializer on macOS * fixup! ci: set QT_QPA_PLATFORM for running new Qt tests install xcb-util-cursor on alpine
This commit is contained in:
11
.github/workflows/actions.yml
vendored
11
.github/workflows/actions.yml
vendored
@@ -461,7 +461,7 @@ jobs:
|
||||
run: apk add --upgrade glibmm-dev gtkmm3-dev
|
||||
- name: Get Dependencies (Qt6)
|
||||
if: ${{ needs.what-to-make.outputs.make-qt == 'true' }}
|
||||
run: apk add --upgrade qt6-qttools-dev qt6-qtsvg-dev
|
||||
run: apk add --upgrade qt6-qtbase-dev qt6-qttools-dev qt6-qtsvg-dev xcb-util-cursor
|
||||
- name: Get Source
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -494,6 +494,7 @@ jobs:
|
||||
if: ${{ needs.what-to-make.outputs.make-tests == 'true' }}
|
||||
env:
|
||||
TMPDIR: /private/tmp
|
||||
QT_QPA_PLATFORM: offscreen
|
||||
run: cmake -E chdir obj ctest -j $(nproc) --build-config RelWithDebInfo --output-on-failure
|
||||
- name: Install
|
||||
run: cmake --build obj --config RelWithDebInfo --target install/strip
|
||||
@@ -765,6 +766,8 @@ jobs:
|
||||
run: cmake --build obj --config RelWithDebInfo
|
||||
- name: Test
|
||||
if: ${{ needs.what-to-make.outputs.make-tests == 'true' }}
|
||||
env:
|
||||
QT_QPA_PLATFORM: offscreen
|
||||
run: cmake -E chdir obj ctest -j $(nproc) --build-config RelWithDebInfo --output-on-failure
|
||||
- name: Install
|
||||
run: cmake --build obj --config RelWithDebInfo --target install/strip
|
||||
@@ -814,7 +817,7 @@ jobs:
|
||||
run: dnf install -y glibmm2.68-devel gtkmm4.0-devel
|
||||
- name: Get Dependencies (Qt6)
|
||||
if: ${{ needs.what-to-make.outputs.make-qt == 'true' }}
|
||||
run: dnf install -y qt6-qtbase-devel qt6-qtsvg-devel qt6-qttools-devel
|
||||
run: dnf install -y qt6-qtbase-devel qt6-qtsvg-devel qt6-qttools-devel xcb-util-cursor
|
||||
- name: Get Source
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -845,6 +848,8 @@ jobs:
|
||||
run: cmake --build obj --config RelWithDebInfo
|
||||
- name: Test
|
||||
if: ${{ needs.what-to-make.outputs.make-tests == 'true' }}
|
||||
env:
|
||||
QT_QPA_PLATFORM: offscreen
|
||||
run: cmake -E chdir obj ctest -j $(nproc) --build-config RelWithDebInfo --output-on-failure
|
||||
- name: Install
|
||||
run: cmake --build obj --config RelWithDebInfo --target install/strip
|
||||
@@ -1014,7 +1019,7 @@ jobs:
|
||||
run: sudo apt-get install -y --no-install-recommends libglibmm-2.4-dev libgtkmm-3.0-dev
|
||||
- name: Get Dependencies (Qt6)
|
||||
if: ${{ needs.what-to-make.outputs.make-qt == 'true' }}
|
||||
run: sudo apt-get install -y --no-install-recommends qt6-svg-dev qt6-tools-dev
|
||||
run: sudo apt-get install -y --no-install-recommends qt6-base-dev qt6-svg-dev qt6-tools-dev
|
||||
- name: Get Source
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
|
||||
@@ -409,6 +409,9 @@ if(ENABLE_QT)
|
||||
Network
|
||||
Svg
|
||||
LinguistTools)
|
||||
if(ENABLE_TESTS)
|
||||
list(APPEND QT_REQUIRED_MODULES Test)
|
||||
endif()
|
||||
set(QT_OPTIONAL_MODULES
|
||||
DBus
|
||||
AxContainer
|
||||
@@ -429,7 +432,7 @@ if(ENABLE_QT)
|
||||
foreach(M ${QT_REQUIRED_MODULES})
|
||||
find_package(Qt${Qt_VERSION_MAJOR}${M} ${QT_MINIMUM} QUIET)
|
||||
if(Qt${Qt_VERSION_MAJOR}${M}_FOUND)
|
||||
if(NOT M STREQUAL "LinguistTools")
|
||||
if(NOT M STREQUAL "LinguistTools" AND NOT M STREQUAL "Test")
|
||||
list(APPEND QT_TARGETS Qt${Qt_VERSION_MAJOR}::${M})
|
||||
endif()
|
||||
else()
|
||||
@@ -811,11 +814,6 @@ if(RUN_CLANG_TIDY)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_TESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
function(tr_install_web DST_DIR)
|
||||
if(INSTALL_WEB)
|
||||
install(
|
||||
@@ -844,6 +842,11 @@ foreach(P cli daemon gtk mac qt utils)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(ENABLE_TESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
if(ENABLE_DAEMON OR ENABLE_GTK OR ENABLE_QT)
|
||||
tr_install_web(${CMAKE_INSTALL_DATAROOTDIR}/${TR_NAME})
|
||||
endif()
|
||||
|
||||
@@ -128,14 +128,14 @@ QAccessibleInterface* accessibleFactory(QString const& className, QObject* objec
|
||||
} // namespace
|
||||
|
||||
Application::Application(
|
||||
std::unique_ptr<Prefs> prefs,
|
||||
Prefs& prefs,
|
||||
bool minimized,
|
||||
QString const& config_dir,
|
||||
QStringList const& filenames,
|
||||
int& argc,
|
||||
char** argv)
|
||||
: QApplication{ argc, argv }
|
||||
, prefs_(std::move(prefs))
|
||||
, prefs_{ prefs }
|
||||
{
|
||||
setApplicationName(ConfigName);
|
||||
loadTranslations();
|
||||
@@ -162,9 +162,9 @@ Application::Application(
|
||||
QAccessible::installFactory(&accessibleFactory);
|
||||
#endif
|
||||
|
||||
session_ = std::make_unique<Session>(config_dir, *prefs_);
|
||||
model_ = std::make_unique<TorrentModel>(*prefs_);
|
||||
window_ = std::make_unique<MainWindow>(*session_, *prefs_, *model_, minimized);
|
||||
session_ = std::make_unique<Session>(config_dir, prefs_);
|
||||
model_ = std::make_unique<TorrentModel>(prefs_);
|
||||
window_ = std::make_unique<MainWindow>(*session_, prefs_, *model_, minimized);
|
||||
watch_dir_ = std::make_unique<WatchDir>(*model_);
|
||||
|
||||
connect(this, &QCoreApplication::aboutToQuit, this, &Application::saveGeometry);
|
||||
@@ -172,7 +172,7 @@ Application::Application(
|
||||
connect(model_.get(), &TorrentModel::torrentsCompleted, this, &Application::onTorrentsCompleted);
|
||||
connect(model_.get(), &TorrentModel::torrentsEdited, this, &Application::onTorrentsEdited);
|
||||
connect(model_.get(), &TorrentModel::torrentsNeedInfo, this, &Application::onTorrentsNeedInfo);
|
||||
connect(prefs_.get(), &Prefs::changed, this, &Application::refreshPref);
|
||||
connect(&prefs_, &Prefs::changed, this, &Application::refreshPref);
|
||||
connect(session_.get(), &Session::sourceChanged, this, &Application::onSessionSourceChanged);
|
||||
connect(session_.get(), &Session::torrentsRemoved, model_.get(), &TorrentModel::removeTorrents);
|
||||
connect(session_.get(), &Session::torrentsUpdated, model_.get(), &TorrentModel::updateTorrents);
|
||||
@@ -295,7 +295,7 @@ QStringList Application::getNames(torrent_ids_t const& torrent_ids) const
|
||||
|
||||
void Application::onTorrentsAdded(torrent_ids_t const& torrent_ids) const
|
||||
{
|
||||
if (!prefs_->get<bool>(Prefs::SHOW_NOTIFICATION_ON_ADD))
|
||||
if (!prefs_.get<bool>(Prefs::SHOW_NOTIFICATION_ON_ADD))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -308,19 +308,19 @@ void Application::onTorrentsAdded(torrent_ids_t const& torrent_ids) const
|
||||
|
||||
void Application::onTorrentsCompleted(torrent_ids_t const& torrent_ids) const
|
||||
{
|
||||
if (prefs_->get<bool>(Prefs::SHOW_NOTIFICATION_ON_COMPLETE))
|
||||
if (prefs_.get<bool>(Prefs::SHOW_NOTIFICATION_ON_COMPLETE))
|
||||
{
|
||||
auto const title = tr("Torrent(s) Completed", nullptr, static_cast<int>(std::size(torrent_ids)));
|
||||
auto const body = getNames(torrent_ids).join(QStringLiteral("\n"));
|
||||
notifyApp(title, body);
|
||||
}
|
||||
|
||||
if (prefs_->get<bool>(Prefs::COMPLETE_SOUND_ENABLED))
|
||||
if (prefs_.get<bool>(Prefs::COMPLETE_SOUND_ENABLED))
|
||||
{
|
||||
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
|
||||
beep();
|
||||
#else
|
||||
auto args = prefs_->get<QStringList>(Prefs::COMPLETE_SOUND_COMMAND);
|
||||
auto args = prefs_.get<QStringList>(Prefs::COMPLETE_SOUND_COMMAND);
|
||||
auto const command = args.takeFirst();
|
||||
QProcess::execute(command, args);
|
||||
#endif
|
||||
@@ -346,13 +346,13 @@ void Application::notifyTorrentAdded(Torrent const* tor) const
|
||||
|
||||
void Application::saveGeometry() const
|
||||
{
|
||||
if (prefs_ != nullptr && window_ != nullptr)
|
||||
if (window_ != nullptr)
|
||||
{
|
||||
auto const geometry = window_->geometry();
|
||||
prefs_->set(Prefs::MAIN_WINDOW_HEIGHT, std::max(100, geometry.height()));
|
||||
prefs_->set(Prefs::MAIN_WINDOW_WIDTH, std::max(100, geometry.width()));
|
||||
prefs_->set(Prefs::MAIN_WINDOW_X, geometry.x());
|
||||
prefs_->set(Prefs::MAIN_WINDOW_Y, geometry.y());
|
||||
prefs_.set(Prefs::MAIN_WINDOW_HEIGHT, std::max(100, geometry.height()));
|
||||
prefs_.set(Prefs::MAIN_WINDOW_WIDTH, std::max(100, geometry.width()));
|
||||
prefs_.set(Prefs::MAIN_WINDOW_X, geometry.x());
|
||||
prefs_.set(Prefs::MAIN_WINDOW_Y, geometry.y());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,7 +368,7 @@ void Application::refreshPref(int key) const
|
||||
|
||||
case Prefs::DIR_WATCH:
|
||||
case Prefs::DIR_WATCH_ENABLED:
|
||||
watch_dir_->setPath(prefs_->get<QString>(Prefs::DIR_WATCH), prefs_->get<bool>(Prefs::DIR_WATCH_ENABLED));
|
||||
watch_dir_->setPath(prefs_.get<QString>(Prefs::DIR_WATCH), prefs_.get<bool>(Prefs::DIR_WATCH_ENABLED));
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -378,19 +378,19 @@ void Application::refreshPref(int key) const
|
||||
|
||||
void Application::maybeUpdateBlocklist() const
|
||||
{
|
||||
if (!prefs_->get<bool>(Prefs::BLOCKLIST_UPDATES_ENABLED))
|
||||
if (!prefs_.get<bool>(Prefs::BLOCKLIST_UPDATES_ENABLED))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QDateTime const last_updated_at = prefs_->get<QDateTime>(Prefs::BLOCKLIST_DATE);
|
||||
QDateTime const next_update_at = last_updated_at.addDays(7);
|
||||
QDateTime const now = QDateTime::currentDateTime();
|
||||
auto const last_updated_at = prefs_.get<QDateTime>(Prefs::BLOCKLIST_DATE);
|
||||
auto const next_update_at = last_updated_at.addDays(7);
|
||||
auto const now = QDateTime::currentDateTime();
|
||||
|
||||
if (now < next_update_at)
|
||||
{
|
||||
session_->updateBlocklist();
|
||||
prefs_->set(Prefs::BLOCKLIST_DATE, now);
|
||||
prefs_.set(Prefs::BLOCKLIST_DATE, now);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,7 +426,7 @@ void Application::refreshTorrents()
|
||||
void Application::addWatchdirTorrent(QString const& filename) const
|
||||
{
|
||||
auto add_data = AddData{ filename };
|
||||
auto const disposal = prefs_->get<bool>(Prefs::TRASH_ORIGINAL) ? AddData::FilenameDisposal::Delete :
|
||||
auto const disposal = prefs_.get<bool>(Prefs::TRASH_ORIGINAL) ? AddData::FilenameDisposal::Delete :
|
||||
AddData::FilenameDisposal::Rename;
|
||||
add_data.setFileDisposal(disposal);
|
||||
addTorrent(std::move(add_data));
|
||||
@@ -441,18 +441,18 @@ void Application::addTorrent(AddData addme) const
|
||||
|
||||
// if there's not already a disposal action set,
|
||||
// then honor the `trash original` preference setting
|
||||
if (!addme.fileDisposal() && prefs_->get<bool>(Prefs::TRASH_ORIGINAL))
|
||||
if (!addme.fileDisposal() && prefs_.get<bool>(Prefs::TRASH_ORIGINAL))
|
||||
{
|
||||
addme.setFileDisposal(AddData::FilenameDisposal::Delete);
|
||||
}
|
||||
|
||||
if (!prefs_->get<bool>(Prefs::OPTIONS_PROMPT))
|
||||
if (!prefs_.get<bool>(Prefs::OPTIONS_PROMPT))
|
||||
{
|
||||
session_->addTorrent(addme);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto* o = new OptionsDialog{ *session_, *prefs_, addme, window_.get() };
|
||||
auto* o = new OptionsDialog{ *session_, prefs_, addme, window_.get() };
|
||||
o->show();
|
||||
}
|
||||
|
||||
|
||||
@@ -36,13 +36,7 @@ class Application : public QApplication
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Application(
|
||||
std::unique_ptr<Prefs> prefs,
|
||||
bool minimized,
|
||||
QString const& config_dir,
|
||||
QStringList const& filenames,
|
||||
int& argc,
|
||||
char** argv);
|
||||
Application(Prefs& prefs, bool minimized, QString const& config_dir, QStringList const& filenames, int& argc, char** argv);
|
||||
Application(Application&&) = delete;
|
||||
Application(Application const&) = delete;
|
||||
Application& operator=(Application&&) = delete;
|
||||
@@ -107,7 +101,7 @@ private:
|
||||
|
||||
std::unordered_set<QString> interned_strings_;
|
||||
|
||||
std::unique_ptr<Prefs> prefs_;
|
||||
Prefs& prefs_;
|
||||
std::unique_ptr<Session> session_;
|
||||
std::unique_ptr<TorrentModel> model_;
|
||||
std::unique_ptr<MainWindow> window_;
|
||||
|
||||
@@ -3,9 +3,9 @@ set_property(GLOBAL PROPERTY AUTOGEN_SOURCE_GROUP "Generated Files")
|
||||
# https://doc.qt.io/qt-6/macos.html#supported-versions
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 11)
|
||||
|
||||
add_executable(${TR_NAME}-qt WIN32)
|
||||
add_library(${TR_NAME}-qt-lib STATIC)
|
||||
|
||||
target_sources(${TR_NAME}-qt
|
||||
target_sources(${TR_NAME}-qt-lib
|
||||
PRIVATE
|
||||
AboutDialog.cc
|
||||
AboutDialog.h
|
||||
@@ -56,7 +56,6 @@ target_sources(${TR_NAME}-qt
|
||||
InteropObject.h
|
||||
LicenseDialog.cc
|
||||
LicenseDialog.h
|
||||
main.cc
|
||||
MainWindow.cc
|
||||
MainWindow.h
|
||||
MakeDialog.cc
|
||||
@@ -107,6 +106,8 @@ target_sources(${TR_NAME}-qt
|
||||
TrackerModel.h
|
||||
TrackerModelFilter.cc
|
||||
TrackerModelFilter.h
|
||||
TrQtInit.cc
|
||||
TrQtInit.h
|
||||
Typedefs.h
|
||||
Utils.cc
|
||||
Utils.h
|
||||
@@ -121,7 +122,7 @@ tr_allow_compile_if(
|
||||
[=[[ENABLE_QT_DBUS_INTEROP]]=]
|
||||
DBusInteropHelper.cc)
|
||||
|
||||
target_sources(${TR_NAME}-qt
|
||||
target_sources(${TR_NAME}-qt-lib
|
||||
PRIVATE
|
||||
AboutDialog.ui
|
||||
DetailsDialog.ui
|
||||
@@ -139,7 +140,7 @@ target_sources(${TR_NAME}-qt
|
||||
source_group(Ui
|
||||
REGULAR_EXPRESSION [[.*\.ui$]])
|
||||
|
||||
target_sources(${TR_NAME}-qt
|
||||
target_sources(${TR_NAME}-qt-lib
|
||||
PRIVATE
|
||||
application.qrc)
|
||||
|
||||
@@ -196,35 +197,49 @@ if(TS_FILES)
|
||||
tr_qt_add_translation(QM_FILES ${TS_FILES})
|
||||
endif()
|
||||
|
||||
target_sources(${TR_NAME}-qt
|
||||
target_sources(${TR_NAME}-qt-lib
|
||||
PRIVATE
|
||||
${QM_FILES})
|
||||
|
||||
if(ENABLE_QT_COM_INTEROP)
|
||||
tr_target_idl_files(${TR_NAME}-qt
|
||||
tr_target_idl_files(${TR_NAME}-qt-lib
|
||||
transmission-qt.idl)
|
||||
endif()
|
||||
|
||||
target_include_directories(${TR_NAME}-qt
|
||||
PRIVATE
|
||||
target_include_directories(${TR_NAME}-qt-lib
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
target_link_libraries(${TR_NAME}-qt
|
||||
PRIVATE
|
||||
target_link_libraries(${TR_NAME}-qt-lib
|
||||
PUBLIC
|
||||
${TR_NAME}-app
|
||||
${TR_NAME}
|
||||
transmission::qt_impl)
|
||||
|
||||
target_compile_definitions(${TR_NAME}-qt
|
||||
target_compile_definitions(${TR_NAME}-qt-lib
|
||||
PRIVATE
|
||||
"TRANSLATIONS_DIR=\"${CMAKE_INSTALL_FULL_DATADIR}/${TR_NAME}/translations\""
|
||||
QT_NO_CAST_FROM_ASCII
|
||||
$<$<BOOL:${ENABLE_QT_COM_INTEROP}>:ENABLE_COM_INTEROP>
|
||||
$<$<BOOL:${ENABLE_QT_DBUS_INTEROP}>:ENABLE_DBUS_INTEROP>)
|
||||
|
||||
if(MSVC)
|
||||
tr_append_target_property(${TR_NAME}-qt LINK_FLAGS "/ENTRY:mainCRTStartup")
|
||||
endif()
|
||||
set_target_properties(
|
||||
${TR_NAME}-qt-lib
|
||||
PROPERTIES
|
||||
AUTOMOC ON
|
||||
AUTORCC ON
|
||||
AUTOUIC ON)
|
||||
|
||||
add_executable(${TR_NAME}-qt WIN32)
|
||||
|
||||
target_sources(${TR_NAME}-qt
|
||||
PRIVATE
|
||||
main.cc
|
||||
application.qrc)
|
||||
|
||||
target_link_libraries(${TR_NAME}-qt
|
||||
PRIVATE
|
||||
${TR_NAME}-qt-lib)
|
||||
|
||||
set_target_properties(
|
||||
${TR_NAME}-qt
|
||||
@@ -233,6 +248,10 @@ set_target_properties(
|
||||
AUTORCC ON
|
||||
AUTOUIC ON)
|
||||
|
||||
if(MSVC)
|
||||
tr_append_target_property(${TR_NAME}-qt LINK_FLAGS "/ENTRY:mainCRTStartup")
|
||||
endif()
|
||||
|
||||
tr_win32_app_info(${TR_NAME}-qt
|
||||
"Transmission Qt Client"
|
||||
"${TR_NAME}-qt"
|
||||
|
||||
367
qt/Prefs.cc
367
qt/Prefs.cc
@@ -3,20 +3,14 @@
|
||||
// or any future license endorsed by Mnemosyne LLC.
|
||||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
#include <QStringDecoder>
|
||||
#else
|
||||
#include <QTextCodec>
|
||||
#endif
|
||||
|
||||
#include <libtransmission/transmission.h>
|
||||
|
||||
@@ -27,42 +21,108 @@
|
||||
#include "CustomVariantType.h"
|
||||
#include "Filters.h"
|
||||
#include "Prefs.h"
|
||||
#include "VariantHelpers.h"
|
||||
|
||||
namespace api_compat = libtransmission::api_compat;
|
||||
using libtransmission::serializer::to_value;
|
||||
using libtransmission::serializer::to_variant;
|
||||
using ::trqt::variant_helpers::dictAdd;
|
||||
using ::trqt::variant_helpers::getValue;
|
||||
namespace ser = libtransmission::serializer;
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
// ---
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
void ensureSoundCommandIsAList(tr_variant* dict)
|
||||
template<typename T>
|
||||
[[nodiscard]] QVariant qvarFromOptional(std::optional<T> const& val)
|
||||
{
|
||||
tr_quark const key = TR_KEY_torrent_complete_sound_command;
|
||||
|
||||
if (tr_variant* list = nullptr; tr_variantDictFindList(dict, key, &list))
|
||||
{
|
||||
return;
|
||||
return val ? QVariant::fromValue(*val) : QVariant{};
|
||||
}
|
||||
|
||||
tr_variantDictRemove(dict, key);
|
||||
dictAdd(
|
||||
dict,
|
||||
key,
|
||||
std::array<std::string_view, 5>{
|
||||
"canberra-gtk-play",
|
||||
[[nodiscard]] QVariant qvarFromTVar(tr_variant const& var, int const qt_metatype)
|
||||
{
|
||||
switch (qt_metatype)
|
||||
{
|
||||
case QMetaType::Int:
|
||||
return qvarFromOptional(ser::to_value<int64_t>(var));
|
||||
|
||||
case CustomVariantType::EncryptionModeType:
|
||||
return qvarFromOptional(ser::to_value<tr_encryption_mode>(var));
|
||||
|
||||
case CustomVariantType::SortModeType:
|
||||
return qvarFromOptional(ser::to_value<SortMode>(var));
|
||||
|
||||
case CustomVariantType::ShowModeType:
|
||||
return qvarFromOptional(ser::to_value<ShowMode>(var));
|
||||
|
||||
case QMetaType::QString:
|
||||
return qvarFromOptional(ser::to_value<QString>(var));
|
||||
|
||||
case QMetaType::QStringList:
|
||||
return qvarFromOptional(ser::to_value<QStringList>(var));
|
||||
|
||||
case QMetaType::Bool:
|
||||
return qvarFromOptional(ser::to_value<bool>(var));
|
||||
|
||||
case QMetaType::Double:
|
||||
return qvarFromOptional(ser::to_value<double>(var));
|
||||
|
||||
case QMetaType::QDateTime:
|
||||
return qvarFromOptional(ser::to_value<QDateTime>(var));
|
||||
|
||||
default:
|
||||
assert(false && "unhandled type");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] tr_variant trvarFromQVar(QVariant const& var, int const qt_metatype)
|
||||
{
|
||||
switch (qt_metatype)
|
||||
{
|
||||
case QMetaType::Int:
|
||||
return ser::to_variant(var.value<int>());
|
||||
|
||||
case CustomVariantType::EncryptionModeType:
|
||||
return ser::to_variant(var.value<tr_encryption_mode>());
|
||||
|
||||
case CustomVariantType::SortModeType:
|
||||
return ser::to_variant(var.value<SortMode>());
|
||||
|
||||
case CustomVariantType::ShowModeType:
|
||||
return ser::to_variant(var.value<ShowMode>());
|
||||
|
||||
case QMetaType::QString:
|
||||
return ser::to_variant(var.value<QString>());
|
||||
|
||||
case QMetaType::QStringList:
|
||||
return ser::to_variant(var.value<QStringList>());
|
||||
|
||||
case QMetaType::Bool:
|
||||
return ser::to_variant(var.value<bool>());
|
||||
|
||||
case QMetaType::Double:
|
||||
return ser::to_variant(var.value<double>());
|
||||
|
||||
case QMetaType::QDateTime:
|
||||
return ser::to_variant(var.value<QDateTime>());
|
||||
|
||||
default:
|
||||
assert(false && "unhandled type");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void ensureSoundCommandIsAList(tr_variant::Map& map)
|
||||
{
|
||||
auto constexpr Key = TR_KEY_torrent_complete_sound_command;
|
||||
auto constexpr DefaultVal = std::array<std::string_view, 5U>{ "canberra-gtk-play",
|
||||
"-i",
|
||||
"complete-download",
|
||||
"-d",
|
||||
"transmission torrent downloaded",
|
||||
});
|
||||
"transmission torrent downloaded" };
|
||||
if (map.find_if<tr_variant::Vector>(Key) == nullptr)
|
||||
{
|
||||
map.insert_or_assign(Key, ser::to_variant(DefaultVal));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::array<Prefs::PrefItem, Prefs::PREFS_COUNT> const Prefs::Items{
|
||||
@@ -182,224 +242,119 @@ namespace
|
||||
}
|
||||
} // namespace
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
// ---
|
||||
|
||||
Prefs::Prefs(QString config_dir)
|
||||
: config_dir_{ std::move(config_dir) }
|
||||
Prefs::Prefs()
|
||||
{
|
||||
static_assert(sizeof(Items) / sizeof(Items[0]) == PREFS_COUNT);
|
||||
|
||||
#ifndef NDEBUG
|
||||
for (int i = 0; i < PREFS_COUNT; ++i)
|
||||
{
|
||||
assert(Items[i].id == i);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
auto const app_defaults = get_default_app_settings();
|
||||
auto settings = tr_sessionLoadSettings(config_dir_.toStdString(), &app_defaults);
|
||||
ensureSoundCommandIsAList(&settings);
|
||||
|
||||
for (int i = 0; i < PREFS_COUNT; ++i)
|
||||
{
|
||||
tr_variant const* b = tr_variantDictFind(&settings, getKey(i));
|
||||
|
||||
switch (Items[i].type)
|
||||
{
|
||||
case QMetaType::Int:
|
||||
if (auto const value = getValue<int64_t>(b); value)
|
||||
{
|
||||
values_[i].setValue(*value);
|
||||
load(defaults());
|
||||
}
|
||||
break;
|
||||
|
||||
case CustomVariantType::EncryptionModeType:
|
||||
if (auto const val = to_value<tr_encryption_mode>(*b))
|
||||
void Prefs::loadFromConfigDir(QString const dir)
|
||||
{
|
||||
values_[i] = QVariant::fromValue(*val);
|
||||
}
|
||||
break;
|
||||
|
||||
case CustomVariantType::SortModeType:
|
||||
if (auto const val = to_value<SortMode>(*b))
|
||||
auto settings = tr_sessionLoadSettings(dir.toStdString());
|
||||
if (auto* const map = settings.get_if<tr_variant::Map>())
|
||||
{
|
||||
values_[i] = QVariant::fromValue(*val);
|
||||
ensureSoundCommandIsAList(*map);
|
||||
load(*map);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CustomVariantType::ShowModeType:
|
||||
if (auto const val = to_value<ShowMode>(*b))
|
||||
void Prefs::load(tr_variant::Map const& settings)
|
||||
{
|
||||
values_[i] = QVariant::fromValue(*val);
|
||||
}
|
||||
break;
|
||||
|
||||
case QMetaType::QString:
|
||||
if (auto const value = getValue<QString>(b); value)
|
||||
for (int idx = 0; idx < PREFS_COUNT; ++idx)
|
||||
{
|
||||
values_[i].setValue(*value);
|
||||
}
|
||||
break;
|
||||
|
||||
case QMetaType::QStringList:
|
||||
if (auto const value = getValue<QStringList>(b); value)
|
||||
if (auto const iter = settings.find(getKey(idx)); iter != settings.end())
|
||||
{
|
||||
values_[i].setValue(*value);
|
||||
}
|
||||
break;
|
||||
|
||||
case QMetaType::Bool:
|
||||
if (auto const value = getValue<bool>(b); value)
|
||||
{
|
||||
values_[i].setValue(*value);
|
||||
}
|
||||
break;
|
||||
|
||||
case QMetaType::Double:
|
||||
if (auto const value = getValue<double>(b); value)
|
||||
{
|
||||
values_[i].setValue(*value);
|
||||
}
|
||||
break;
|
||||
|
||||
case QMetaType::QDateTime:
|
||||
if (auto const value = getValue<time_t>(b); value)
|
||||
{
|
||||
values_[i].setValue(QDateTime::fromSecsSinceEpoch(*value));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false && "unhandled type");
|
||||
break;
|
||||
values_[idx] = qvarFromTVar(iter->second, Items[idx].type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Prefs::~Prefs()
|
||||
tr_variant::Map Prefs::current_settings() const
|
||||
{
|
||||
// make a dict from settings.json
|
||||
tr_variant current_settings;
|
||||
tr_variantInitDict(¤t_settings, PREFS_COUNT);
|
||||
auto map = tr_variant::Map{ PREFS_COUNT };
|
||||
|
||||
for (int i = 0; i < PREFS_COUNT; ++i)
|
||||
for (int idx = 0; idx < PREFS_COUNT; ++idx)
|
||||
{
|
||||
if (!prefIsSavable(i))
|
||||
if (prefIsSavable(idx))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto const key = getKey(i);
|
||||
auto const& val = values_[i];
|
||||
|
||||
switch (Items[i].type)
|
||||
{
|
||||
case QMetaType::Int:
|
||||
dictAdd(¤t_settings, key, val.toInt());
|
||||
break;
|
||||
|
||||
case CustomVariantType::EncryptionModeType:
|
||||
*tr_variantDictAdd(¤t_settings, key) = to_variant(val.value<tr_encryption_mode>());
|
||||
break;
|
||||
|
||||
case CustomVariantType::SortModeType:
|
||||
*tr_variantDictAdd(¤t_settings, key) = to_variant(val.value<SortMode>());
|
||||
break;
|
||||
|
||||
case CustomVariantType::ShowModeType:
|
||||
*tr_variantDictAdd(¤t_settings, key) = to_variant(val.value<ShowMode>());
|
||||
break;
|
||||
|
||||
case QMetaType::QString:
|
||||
dictAdd(¤t_settings, key, val.toString());
|
||||
break;
|
||||
|
||||
case QMetaType::QStringList:
|
||||
dictAdd(¤t_settings, key, val.toStringList());
|
||||
break;
|
||||
|
||||
case QMetaType::Bool:
|
||||
dictAdd(¤t_settings, key, val.toBool());
|
||||
break;
|
||||
|
||||
case QMetaType::Double:
|
||||
dictAdd(¤t_settings, key, val.toDouble());
|
||||
break;
|
||||
|
||||
case QMetaType::QDateTime:
|
||||
dictAdd(¤t_settings, key, int64_t{ val.toDateTime().toSecsSinceEpoch() });
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false && "unhandled type");
|
||||
break;
|
||||
map.try_emplace(Items[idx].key, trvarFromQVar(values_[idx], Items[idx].type));
|
||||
}
|
||||
}
|
||||
|
||||
// update settings.json with our settings
|
||||
return map;
|
||||
}
|
||||
|
||||
void Prefs::save(QString const& filename) const
|
||||
{
|
||||
auto const filename_str = filename.toStdString();
|
||||
auto serde = tr_variant_serde::json();
|
||||
auto const file = QFile{ QDir{ config_dir_ }.absoluteFilePath(QStringLiteral("settings.json")) };
|
||||
auto const filename = file.fileName().toStdString();
|
||||
auto settings = tr_variant::make_map(PREFS_COUNT);
|
||||
if (auto const file_settings = serde.parse_file(filename); file_settings)
|
||||
{
|
||||
settings.merge(*file_settings);
|
||||
}
|
||||
|
||||
settings.merge(current_settings);
|
||||
auto settings = tr_variant::make_map(PREFS_COUNT);
|
||||
if (auto const var = serde.parse_file(filename_str))
|
||||
{
|
||||
settings.merge(*var);
|
||||
}
|
||||
settings.merge(tr_variant{ current_settings() });
|
||||
api_compat::convert_outgoing_data(settings);
|
||||
serde.to_file(settings, filename);
|
||||
serde.to_file(settings, filename_str);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is where we initialize the preferences file with the default values.
|
||||
* If you add a new preferences key, you /must/ add a default value here.
|
||||
*/
|
||||
tr_variant Prefs::get_default_app_settings()
|
||||
// static
|
||||
tr_variant::Map Prefs::defaults()
|
||||
{
|
||||
auto const download_dir = tr_getDefaultDownloadDir();
|
||||
|
||||
auto settings = tr_variant::Map{ 64U };
|
||||
settings.try_emplace(TR_KEY_blocklist_date, 0);
|
||||
settings.try_emplace(TR_KEY_blocklist_updates_enabled, true);
|
||||
settings.try_emplace(TR_KEY_compact_view, false);
|
||||
settings.try_emplace(TR_KEY_download_dir, download_dir);
|
||||
settings.try_emplace(TR_KEY_filter_mode, to_variant(DefaultShowMode));
|
||||
settings.try_emplace(TR_KEY_inhibit_desktop_hibernation, false);
|
||||
settings.try_emplace(TR_KEY_main_window_height, 500);
|
||||
settings.try_emplace(TR_KEY_main_window_layout_order, tr_variant::unmanaged_string("menu,toolbar,filter,list,statusbar"sv));
|
||||
settings.try_emplace(TR_KEY_main_window_width, 600);
|
||||
settings.try_emplace(TR_KEY_main_window_x, 50);
|
||||
settings.try_emplace(TR_KEY_main_window_y, 50);
|
||||
settings.try_emplace(TR_KEY_open_dialog_dir, QDir::home().absolutePath().toStdString());
|
||||
settings.try_emplace(TR_KEY_prompt_before_exit, true);
|
||||
settings.try_emplace(TR_KEY_read_clipboard, false);
|
||||
settings.try_emplace(TR_KEY_remote_session_enabled, false);
|
||||
settings.try_emplace(TR_KEY_remote_session_host, tr_variant::unmanaged_string("localhost"sv));
|
||||
settings.try_emplace(TR_KEY_remote_session_https, false);
|
||||
settings.try_emplace(TR_KEY_remote_session_password, tr_variant::unmanaged_string(""sv));
|
||||
settings.try_emplace(TR_KEY_remote_session_port, TrDefaultRpcPort);
|
||||
settings.try_emplace(TR_KEY_remote_session_requires_authentication, false);
|
||||
settings.try_emplace(TR_KEY_remote_session_url_base_path, tr_variant::unmanaged_string(TrHttpServerDefaultBasePath));
|
||||
settings.try_emplace(TR_KEY_remote_session_username, tr_variant::unmanaged_string(""sv));
|
||||
settings.try_emplace(TR_KEY_show_backup_trackers, false);
|
||||
settings.try_emplace(TR_KEY_show_filterbar, true);
|
||||
settings.try_emplace(TR_KEY_show_notification_area_icon, false);
|
||||
settings.try_emplace(TR_KEY_show_options_window, true);
|
||||
settings.try_emplace(TR_KEY_show_statusbar, true);
|
||||
settings.try_emplace(TR_KEY_show_toolbar, true);
|
||||
settings.try_emplace(TR_KEY_show_tracker_scrapes, false);
|
||||
settings.try_emplace(TR_KEY_sort_mode, to_variant(DefaultSortMode));
|
||||
settings.try_emplace(TR_KEY_sort_reversed, false);
|
||||
settings.try_emplace(TR_KEY_start_minimized, false);
|
||||
settings.try_emplace(TR_KEY_statusbar_stats, tr_variant::unmanaged_string("total-ratio"));
|
||||
settings.try_emplace(TR_KEY_torrent_added_notification_enabled, true);
|
||||
settings.try_emplace(TR_KEY_torrent_complete_notification_enabled, true);
|
||||
settings.try_emplace(TR_KEY_torrent_complete_sound_enabled, true);
|
||||
settings.try_emplace(TR_KEY_watch_dir, download_dir);
|
||||
settings.try_emplace(TR_KEY_watch_dir_enabled, false);
|
||||
return tr_variant{ std::move(settings) };
|
||||
auto map = tr_variant::Map{ 64U };
|
||||
map.try_emplace(TR_KEY_blocklist_date, 0);
|
||||
map.try_emplace(TR_KEY_blocklist_updates_enabled, true);
|
||||
map.try_emplace(TR_KEY_compact_view, false);
|
||||
map.try_emplace(TR_KEY_download_dir, download_dir);
|
||||
map.try_emplace(TR_KEY_filter_mode, ser::to_variant(DefaultShowMode));
|
||||
map.try_emplace(TR_KEY_inhibit_desktop_hibernation, false);
|
||||
map.try_emplace(TR_KEY_main_window_height, 500);
|
||||
map.try_emplace(TR_KEY_main_window_layout_order, tr_variant::unmanaged_string("menu,toolbar,filter,list,statusbar"sv));
|
||||
map.try_emplace(TR_KEY_main_window_width, 600);
|
||||
map.try_emplace(TR_KEY_main_window_x, 50);
|
||||
map.try_emplace(TR_KEY_main_window_y, 50);
|
||||
map.try_emplace(TR_KEY_open_dialog_dir, QDir::home().absolutePath().toStdString());
|
||||
map.try_emplace(TR_KEY_prompt_before_exit, true);
|
||||
map.try_emplace(TR_KEY_read_clipboard, false);
|
||||
map.try_emplace(TR_KEY_remote_session_enabled, false);
|
||||
map.try_emplace(TR_KEY_remote_session_host, tr_variant::unmanaged_string("localhost"sv));
|
||||
map.try_emplace(TR_KEY_remote_session_https, false);
|
||||
map.try_emplace(TR_KEY_remote_session_password, tr_variant::unmanaged_string(""sv));
|
||||
map.try_emplace(TR_KEY_remote_session_port, TrDefaultRpcPort);
|
||||
map.try_emplace(TR_KEY_remote_session_requires_authentication, false);
|
||||
map.try_emplace(TR_KEY_remote_session_url_base_path, tr_variant::unmanaged_string(TrHttpServerDefaultBasePath));
|
||||
map.try_emplace(TR_KEY_remote_session_username, tr_variant::unmanaged_string(""sv));
|
||||
map.try_emplace(TR_KEY_show_backup_trackers, false);
|
||||
map.try_emplace(TR_KEY_show_filterbar, true);
|
||||
map.try_emplace(TR_KEY_show_notification_area_icon, false);
|
||||
map.try_emplace(TR_KEY_show_options_window, true);
|
||||
map.try_emplace(TR_KEY_show_statusbar, true);
|
||||
map.try_emplace(TR_KEY_show_toolbar, true);
|
||||
map.try_emplace(TR_KEY_show_tracker_scrapes, false);
|
||||
map.try_emplace(TR_KEY_sort_mode, ser::to_variant(DefaultSortMode));
|
||||
map.try_emplace(TR_KEY_sort_reversed, false);
|
||||
map.try_emplace(TR_KEY_start_minimized, false);
|
||||
map.try_emplace(TR_KEY_statusbar_stats, tr_variant::unmanaged_string("total-ratio"));
|
||||
map.try_emplace(TR_KEY_torrent_added_notification_enabled, true);
|
||||
map.try_emplace(TR_KEY_torrent_complete_notification_enabled, true);
|
||||
map.try_emplace(TR_KEY_torrent_complete_sound_enabled, true);
|
||||
map.try_emplace(TR_KEY_watch_dir, download_dir);
|
||||
map.try_emplace(TR_KEY_watch_dir_enabled, false);
|
||||
return map;
|
||||
}
|
||||
|
||||
57
qt/Prefs.h
57
qt/Prefs.h
@@ -12,16 +12,10 @@
|
||||
#include <QVariant>
|
||||
|
||||
#include <libtransmission/quark.h>
|
||||
#include <libtransmission/variant.h>
|
||||
|
||||
#include <libtransmission-app/display-modes.h>
|
||||
|
||||
class QDateTime;
|
||||
|
||||
extern "C"
|
||||
{
|
||||
struct tr_variant;
|
||||
}
|
||||
|
||||
class Prefs : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -131,37 +125,36 @@ public:
|
||||
PREFS_COUNT
|
||||
};
|
||||
|
||||
explicit Prefs(QString config_dir);
|
||||
Prefs();
|
||||
Prefs(Prefs&&) = delete;
|
||||
Prefs(Prefs const&) = delete;
|
||||
Prefs& operator=(Prefs&&) = delete;
|
||||
Prefs& operator=(Prefs const&) = delete;
|
||||
~Prefs() override;
|
||||
~Prefs() override = default;
|
||||
|
||||
[[nodiscard]] constexpr auto isCore(int key) const noexcept
|
||||
[[nodiscard]] static auto constexpr isCore(int const idx)
|
||||
{
|
||||
return FIRST_CORE_PREF <= key && key <= LAST_CORE_PREF;
|
||||
return FIRST_CORE_PREF <= idx && idx <= LAST_CORE_PREF;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto getKey(int i) const noexcept
|
||||
[[nodiscard]] static auto constexpr getKey(int const idx)
|
||||
{
|
||||
return Items[i].key;
|
||||
return Items[idx].key;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto type(int i) const noexcept
|
||||
[[nodiscard]] static auto constexpr type(int const idx)
|
||||
{
|
||||
return Items[i].type;
|
||||
return Items[idx].type;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto const& variant(int i) const noexcept
|
||||
{
|
||||
return values_[i];
|
||||
}
|
||||
void loadFromConfigDir(QString dir);
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] T get(int const key) const
|
||||
void load(tr_variant::Map const& settings);
|
||||
|
||||
// DEPRECATED
|
||||
[[nodiscard]] constexpr auto const& variant(int const idx) const noexcept
|
||||
{
|
||||
return values_[key].value<T>();
|
||||
return values_[idx];
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -177,8 +170,18 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] T get(int const idx) const
|
||||
{
|
||||
return values_[idx].value<T>();
|
||||
}
|
||||
|
||||
[[nodiscard]] tr_variant::Map current_settings() const;
|
||||
|
||||
void save(QString const& filename) const;
|
||||
|
||||
signals:
|
||||
void changed(int key);
|
||||
void changed(int idx);
|
||||
|
||||
private:
|
||||
struct PrefItem
|
||||
@@ -188,13 +191,11 @@ private:
|
||||
int type;
|
||||
};
|
||||
|
||||
[[nodiscard]] static tr_variant get_default_app_settings();
|
||||
static std::array<PrefItem, PREFS_COUNT> const Items;
|
||||
|
||||
[[nodiscard]] static tr_variant::Map defaults();
|
||||
|
||||
void set(int key, char const* value) = delete;
|
||||
|
||||
QString const config_dir_;
|
||||
|
||||
std::array<QVariant, PREFS_COUNT> mutable values_;
|
||||
|
||||
static std::array<PrefItem, PREFS_COUNT> const Items;
|
||||
};
|
||||
|
||||
@@ -63,7 +63,7 @@ void Session::sessionSet(tr_quark const key, QVariant const& value)
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
|
||||
switch (value.typeId())
|
||||
#else
|
||||
switch (static_cast<QMetaType::Type>(value.type()))
|
||||
switch (value.userType())
|
||||
#endif
|
||||
{
|
||||
case QMetaType::Bool:
|
||||
@@ -166,7 +166,7 @@ void Session::copyMagnetLinkToClipboard(int torrent_id)
|
||||
|
||||
void Session::updatePref(int key)
|
||||
{
|
||||
if (prefs_.isCore(key))
|
||||
if (Prefs::isCore(key))
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
@@ -209,11 +209,11 @@ void Session::updatePref(int key)
|
||||
case Prefs::USPEED:
|
||||
case Prefs::USPEED_ENABLED:
|
||||
case Prefs::UTP_ENABLED:
|
||||
sessionSet(prefs_.getKey(key), prefs_.variant(key));
|
||||
sessionSet(Prefs::getKey(key), prefs_.variant(key));
|
||||
break;
|
||||
|
||||
case Prefs::DOWNLOAD_DIR:
|
||||
sessionSet(prefs_.getKey(key), prefs_.variant(key));
|
||||
sessionSet(Prefs::getKey(key), prefs_.variant(key));
|
||||
/* this will change the 'freespace' argument, so refresh */
|
||||
refreshSessionInfo();
|
||||
break;
|
||||
@@ -890,14 +890,14 @@ void Session::updateInfo(tr_variant* args_dict)
|
||||
|
||||
for (int i = Prefs::FIRST_CORE_PREF; i <= Prefs::LAST_CORE_PREF; ++i)
|
||||
{
|
||||
tr_variant const* b(tr_variantDictFind(args_dict, prefs_.getKey(i)));
|
||||
tr_variant const* b(tr_variantDictFind(args_dict, Prefs::getKey(i)));
|
||||
|
||||
if (b == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (prefs_.type(i))
|
||||
switch (Prefs::type(i))
|
||||
{
|
||||
case QMetaType::Int:
|
||||
if (auto const value = getValue<int>(b); value)
|
||||
|
||||
21
qt/TrQtInit.cc
Normal file
21
qt/TrQtInit.cc
Normal file
@@ -0,0 +1,21 @@
|
||||
// This file Copyright © Mnemosyne LLC.
|
||||
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
|
||||
// or any future license endorsed by Mnemosyne LLC.
|
||||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#include "TrQtInit.h"
|
||||
|
||||
#include <libtransmission-app/app.h>
|
||||
|
||||
#include "VariantHelpers.h"
|
||||
|
||||
namespace trqt
|
||||
{
|
||||
|
||||
void trqt_init()
|
||||
{
|
||||
transmission::app::init();
|
||||
trqt::variant_helpers::register_qt_converters();
|
||||
}
|
||||
|
||||
} // namespace trqt
|
||||
13
qt/TrQtInit.h
Normal file
13
qt/TrQtInit.h
Normal file
@@ -0,0 +1,13 @@
|
||||
// This file Copyright © Mnemosyne LLC.
|
||||
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
|
||||
// or any future license endorsed by Mnemosyne LLC.
|
||||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace trqt
|
||||
{
|
||||
|
||||
void trqt_init();
|
||||
|
||||
} // namespace trqt
|
||||
@@ -43,7 +43,7 @@ QIcon Utils::getIconFromIndex(QModelIndex const& index)
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
|
||||
switch (variant.typeId())
|
||||
#else
|
||||
switch (static_cast<QMetaType::Type>(variant.type()))
|
||||
switch (variant.userType())
|
||||
#endif
|
||||
{
|
||||
case QMetaType::QIcon:
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <mutex>
|
||||
#include <string_view>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QUrl>
|
||||
|
||||
#include <libtransmission/serializer.h>
|
||||
@@ -19,6 +20,8 @@
|
||||
#include "Speed.h"
|
||||
#include "Torrent.h"
|
||||
|
||||
namespace ser = libtransmission::serializer;
|
||||
|
||||
namespace trqt::variant_helpers
|
||||
{
|
||||
|
||||
@@ -253,6 +256,42 @@ tr_variant fromInt(int const& val)
|
||||
{
|
||||
return static_cast<int64_t>(val);
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
bool toQDateTime(tr_variant const& src, QDateTime* tgt)
|
||||
{
|
||||
if (auto const val = ser::to_value<int64_t>(src))
|
||||
{
|
||||
*tgt = QDateTime::fromSecsSinceEpoch(*val);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
tr_variant fromQDateTime(QDateTime const& src)
|
||||
{
|
||||
return ser::to_variant(int64_t{ src.toSecsSinceEpoch() });
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
bool toQString(tr_variant const& src, QString* tgt)
|
||||
{
|
||||
if (auto const val = src.value_if<std::string_view>())
|
||||
{
|
||||
*tgt = QString::fromUtf8(std::data(*val), std::size(*val));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
tr_variant fromQString(QString const& val)
|
||||
{
|
||||
return val.toStdString();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void register_qt_converters()
|
||||
@@ -264,6 +303,8 @@ void register_qt_converters()
|
||||
{
|
||||
using namespace libtransmission::serializer;
|
||||
Converters::add(toInt, fromInt);
|
||||
Converters::add(toQDateTime, fromQDateTime);
|
||||
Converters::add(toQString, fromQString);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
29
qt/main.cc
29
qt/main.cc
@@ -7,6 +7,8 @@
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
#include <QDir>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <libtransmission/transmission.h>
|
||||
@@ -203,40 +205,41 @@ int tr_main(int argc, char** argv)
|
||||
}
|
||||
|
||||
// initialize the prefs
|
||||
auto prefs = std::make_unique<Prefs>(config_dir);
|
||||
auto prefs = Prefs{};
|
||||
prefs.loadFromConfigDir(config_dir);
|
||||
|
||||
if (!host.isNull())
|
||||
{
|
||||
prefs->set(Prefs::SESSION_REMOTE_HOST, host);
|
||||
prefs.set(Prefs::SESSION_REMOTE_HOST, host);
|
||||
}
|
||||
|
||||
if (!port.isNull())
|
||||
{
|
||||
prefs->set(Prefs::SESSION_REMOTE_PORT, port.toUInt());
|
||||
prefs.set(Prefs::SESSION_REMOTE_PORT, port.toUInt());
|
||||
}
|
||||
|
||||
if (!username.isNull())
|
||||
{
|
||||
prefs->set(Prefs::SESSION_REMOTE_USERNAME, username);
|
||||
prefs.set(Prefs::SESSION_REMOTE_USERNAME, username);
|
||||
}
|
||||
|
||||
if (!password.isNull())
|
||||
{
|
||||
prefs->set(Prefs::SESSION_REMOTE_PASSWORD, password);
|
||||
prefs.set(Prefs::SESSION_REMOTE_PASSWORD, password);
|
||||
}
|
||||
|
||||
if (!host.isNull() || !port.isNull() || !username.isNull() || !password.isNull())
|
||||
{
|
||||
prefs->set(Prefs::SESSION_IS_REMOTE, true);
|
||||
prefs.set(Prefs::SESSION_IS_REMOTE, true);
|
||||
}
|
||||
|
||||
if (prefs->get<bool>(Prefs::START_MINIMIZED))
|
||||
if (prefs.get<bool>(Prefs::START_MINIMIZED))
|
||||
{
|
||||
minimized = true;
|
||||
}
|
||||
|
||||
// start as minimized only if the system tray present
|
||||
if (!prefs->get<bool>(Prefs::SHOW_TRAY_ICON))
|
||||
if (!prefs.get<bool>(Prefs::SHOW_TRAY_ICON))
|
||||
{
|
||||
minimized = false;
|
||||
}
|
||||
@@ -247,8 +250,14 @@ int tr_main(int argc, char** argv)
|
||||
qt_argv.insert(qt_argv.end(), &argv[qt_args_start_idx], &argv[argc]);
|
||||
}
|
||||
|
||||
// run the app
|
||||
auto qt_argc = static_cast<int>(std::size(qt_argv));
|
||||
auto const app = Application{ prefs, minimized, config_dir, filenames, qt_argc, std::data(qt_argv) };
|
||||
auto const ret = QApplication::exec();
|
||||
|
||||
Application const app(std::move(prefs), minimized, config_dir, filenames, qt_argc, std::data(qt_argv));
|
||||
return QApplication::exec();
|
||||
// save prefs before exiting
|
||||
auto const filename = QDir{ config_dir }.absoluteFilePath(QStringLiteral("settings.json"));
|
||||
prefs.save(filename);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -58,8 +58,6 @@ function global:Build-Qt5([string] $PrefixDir, [string] $Arch, [string] $DepsPre
|
||||
'-no-sql-sqlite2'
|
||||
'-no-sql-tds'
|
||||
'-nomake'; 'examples'
|
||||
'-nomake'; 'tests'
|
||||
'-nomake'; 'tools'
|
||||
'-I'; (Join-Path $DepsPrefixDir include)
|
||||
'-L'; (Join-Path $DepsPrefixDir lib)
|
||||
)
|
||||
|
||||
@@ -89,7 +89,6 @@ function global:Build-Qt6([string] $PrefixDir, [string] $Arch, [string] $DepsPre
|
||||
'-no-feature-syntaxhighlighter'
|
||||
'-no-feature-systemsemaphore'
|
||||
'-no-feature-tablewidget'
|
||||
'-no-feature-testlib'
|
||||
'-no-feature-textmarkdownreader'
|
||||
'-no-feature-textmarkdownwriter'
|
||||
'-no-feature-textodfwriter'
|
||||
|
||||
@@ -3,3 +3,25 @@ add_subdirectory(libtransmission)
|
||||
if(ENABLE_UTILS)
|
||||
add_subdirectory(utils)
|
||||
endif()
|
||||
|
||||
if(ENABLE_QT)
|
||||
add_subdirectory(qt)
|
||||
endif()
|
||||
|
||||
add_custom_target(all-tests)
|
||||
|
||||
set_property(
|
||||
TARGET all-tests
|
||||
PROPERTY FOLDER "tests")
|
||||
|
||||
if(TARGET libtransmission-test)
|
||||
add_dependencies(all-tests libtransmission-test)
|
||||
endif()
|
||||
|
||||
if(TARGET qt-tests)
|
||||
add_dependencies(all-tests qt-tests)
|
||||
endif()
|
||||
|
||||
if(TARGET transmission-show)
|
||||
add_dependencies(all-tests transmission-show)
|
||||
endif()
|
||||
|
||||
60
tests/qt/.clang-tidy
Normal file
60
tests/qt/.clang-tidy
Normal file
@@ -0,0 +1,60 @@
|
||||
---
|
||||
HeaderFilterRegex: .*/libtransmission/.*
|
||||
|
||||
# TODO: Enable `portability-template-virtual-member-function` after https://github.com/llvm/llvm-project/issues/139031 is fixed
|
||||
# TODO: Enable `cppcoreguidelines-pro-bounds-pointer-arithmetic` after converting all pointers to std::span
|
||||
# PRs welcome to fix & re-enable any of these explicitly-disabled checks
|
||||
Checks: >
|
||||
bugprone-*,
|
||||
-bugprone-branch-clone,
|
||||
-bugprone-easily-swappable-parameters,
|
||||
-bugprone-implicit-widening-of-multiplication-result,
|
||||
-bugprone-narrowing-conversions,
|
||||
cert-*,
|
||||
-cert-err58-cpp,
|
||||
-cert-int09-c,
|
||||
clang-analyzer-*,
|
||||
cppcoreguidelines-*,
|
||||
-cppcoreguidelines-avoid-c-arrays,
|
||||
-cppcoreguidelines-avoid-const-or-ref-data-members,
|
||||
-cppcoreguidelines-avoid-magic-numbers,
|
||||
-cppcoreguidelines-avoid-non-const-global-variables,
|
||||
-cppcoreguidelines-macro-usage,
|
||||
-cppcoreguidelines-narrowing-conversions,
|
||||
-cppcoreguidelines-non-private-member-variables-in-classes,
|
||||
-cppcoreguidelines-owning-memory,
|
||||
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
|
||||
-cppcoreguidelines-pro-bounds-constant-array-index,
|
||||
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
|
||||
-cppcoreguidelines-pro-type-const-cast,
|
||||
-cppcoreguidelines-pro-type-reinterpret-cast,
|
||||
-cppcoreguidelines-pro-type-union-access,
|
||||
-cppcoreguidelines-pro-type-vararg,
|
||||
google-explicit-constructor,
|
||||
misc-*,
|
||||
-misc-include-cleaner,
|
||||
-misc-no-recursion,
|
||||
-misc-non-private-member-variables-in-classes,
|
||||
modernize-*,
|
||||
-modernize-use-trailing-return-type,
|
||||
performance-*,
|
||||
-performance-move-const-arg,
|
||||
portability-*,
|
||||
-portability-template-virtual-member-function,
|
||||
readability-*,
|
||||
-readability-enum-initial-value,
|
||||
-readability-function-cognitive-complexity,
|
||||
-readability-identifier-length,
|
||||
-readability-magic-numbers,
|
||||
-readability-qualified-auto,
|
||||
|
||||
CheckOptions:
|
||||
- { key: cppcoreguidelines-avoid-do-while.IgnoreMacros, value: true }
|
||||
- { key: cppcoreguidelines-rvalue-reference-param-not-moved.IgnoreUnnamedParams, value: true }
|
||||
- { key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor, value: true }
|
||||
- { key: readability-identifier-naming.ConstexprVariableCase, value: CamelCase }
|
||||
- { key: readability-identifier-naming.ParameterCase, value: lower_case }
|
||||
- { key: readability-identifier-naming.PrivateMemberSuffix, value: _ }
|
||||
- { key: readability-identifier-naming.ProtectedMemberSuffix, value: _ }
|
||||
- { key: readability-identifier-naming.VariableCase, value: lower_case }
|
||||
- { key: readability-implicit-bool-conversion.UseUpperCaseLiteralSuffix, value: true }
|
||||
27
tests/qt/CMakeLists.txt
Normal file
27
tests/qt/CMakeLists.txt
Normal file
@@ -0,0 +1,27 @@
|
||||
find_package(Qt${Qt_VERSION_MAJOR}Test ${QT_MINIMUM} REQUIRED)
|
||||
|
||||
set_property(DIRECTORY PROPERTY INCLUDE_DIRECTORIES
|
||||
${CMAKE_BINARY_DIR}/qt/${TR_NAME}-qt-lib_autogen/include)
|
||||
set_property(DIRECTORY PROPERTY LINK_LIBRARIES
|
||||
${TR_NAME}-qt-lib
|
||||
Qt${Qt_VERSION_MAJOR}::Test)
|
||||
|
||||
function(add_trqt_test source)
|
||||
get_filename_component(name_no_ext ${source} NAME_WE)
|
||||
string(REGEX REPLACE "-test$" "" test_name ${name_no_ext})
|
||||
set(target qt-test-${test_name})
|
||||
add_executable(${target} ${source} ${CMAKE_CURRENT_SOURCE_DIR}/../../qt/application.qrc)
|
||||
set_property(TARGET ${target} PROPERTY AUTOMOC ON)
|
||||
set_property(TARGET ${target} PROPERTY AUTORCC ON)
|
||||
add_test(NAME QT.${test_name} COMMAND ${target})
|
||||
endfunction()
|
||||
|
||||
add_trqt_test(prefs-test.cc)
|
||||
|
||||
add_custom_target(qt-tests
|
||||
DEPENDS
|
||||
qt-test-prefs)
|
||||
|
||||
set_property(
|
||||
TARGET qt-tests
|
||||
PROPERTY FOLDER "tests")
|
||||
262
tests/qt/prefs-test.cc
Normal file
262
tests/qt/prefs-test.cc
Normal file
@@ -0,0 +1,262 @@
|
||||
// This file Copyright © Mnemosyne LLC.
|
||||
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
|
||||
// or any future license endorsed by Mnemosyne LLC.
|
||||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDateTime>
|
||||
#include <QSignalSpy>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QTest>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <libtransmission/quark.h>
|
||||
#include <libtransmission/utils.h>
|
||||
#include <libtransmission/variant.h>
|
||||
|
||||
#include "CustomVariantType.h"
|
||||
#include "Filters.h"
|
||||
#include "Prefs.h"
|
||||
#include "TrQtInit.h"
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
|
||||
#define QCOMPARE_EQ(actual, expected) QCOMPARE(actual, expected)
|
||||
#define QCOMPARE_NE(actual, expected) QVERIFY((actual) != (expected))
|
||||
#endif
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
class PrefsTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
[[nodiscard]] static std::string get_json_member_str(int const idx, std::string_view const valstr)
|
||||
{
|
||||
auto const json_key = tr_quark_get_string_view(Prefs::getKey(idx));
|
||||
return fmt::format(R"("{:s}":{:s})", json_key, valstr);
|
||||
}
|
||||
|
||||
static void verify_json_contains(tr_variant const& var, std::string_view const substr)
|
||||
{
|
||||
auto serde = tr_variant_serde::json();
|
||||
serde.compact();
|
||||
auto const str = serde.to_string(var);
|
||||
QVERIFY2(tr_strv_contains(str, substr), str.c_str());
|
||||
}
|
||||
|
||||
static void verify_json_contains(tr_variant const& var, int const idx, std::string_view const val)
|
||||
{
|
||||
auto serde = tr_variant_serde::json();
|
||||
serde.compact();
|
||||
auto const str = serde.to_string(var);
|
||||
auto const substr = get_json_member_str(idx, val);
|
||||
QVERIFY2(tr_strv_contains(str, substr), str.c_str());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void verify_get_set_by_property(Prefs& prefs, int const idx, T const& val1, T const& val2)
|
||||
{
|
||||
QCOMPARE_NE(val1, val2);
|
||||
|
||||
prefs.set(idx, val1);
|
||||
QCOMPARE_EQ(prefs.get<T>(idx), val1);
|
||||
QCOMPARE_NE(prefs.get<T>(idx), val2);
|
||||
|
||||
prefs.set(idx, val2);
|
||||
QCOMPARE_NE(prefs.get<T>(idx), val1);
|
||||
QCOMPARE_EQ(prefs.get<T>(idx), val2);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void verify_get_by_json(Prefs& prefs, int const idx, T const& val, std::string_view const valstr)
|
||||
{
|
||||
prefs.set(idx, val);
|
||||
QCOMPARE_EQ(prefs.get<T>(idx), val);
|
||||
verify_json_contains(prefs.current_settings(), idx, valstr);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void verify_set_by_json(Prefs& prefs, int const idx, T const& val, std::string_view const valstr)
|
||||
{
|
||||
auto const json_object_str = fmt::format(R"({{{:s}}})", get_json_member_str(idx, valstr));
|
||||
auto serde = tr_variant_serde::json();
|
||||
auto const var = serde.parse(json_object_str);
|
||||
QVERIFY(var.has_value());
|
||||
// IDK why clang-tidy doesn't see the QVERIFY check above?
|
||||
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
||||
auto const* const map = var->get_if<tr_variant::Map>();
|
||||
QVERIFY(map != nullptr);
|
||||
prefs.load(*map);
|
||||
QCOMPARE_EQ(prefs.get<T>(idx), val);
|
||||
}
|
||||
|
||||
private slots:
|
||||
void handles_bool()
|
||||
{
|
||||
auto constexpr Idx = Prefs::SORT_REVERSED;
|
||||
auto constexpr ValA = false;
|
||||
auto constexpr ValAStr = "false"sv;
|
||||
auto constexpr ValB = true;
|
||||
auto constexpr ValBStr = "true"sv;
|
||||
|
||||
auto prefs = Prefs{};
|
||||
verify_get_set_by_property(prefs, Idx, ValA, ValB);
|
||||
verify_set_by_json(prefs, Idx, ValA, ValAStr);
|
||||
verify_get_by_json(prefs, Idx, ValB, ValBStr);
|
||||
}
|
||||
|
||||
void handles_int()
|
||||
{
|
||||
auto constexpr Idx = Prefs::MAIN_WINDOW_HEIGHT;
|
||||
auto constexpr ValA = 4242;
|
||||
auto constexpr ValAStr = "4242"sv;
|
||||
auto constexpr ValB = 2323;
|
||||
auto constexpr ValBStr = "2323"sv;
|
||||
|
||||
auto prefs = Prefs{};
|
||||
verify_get_set_by_property(prefs, Idx, ValA, ValB);
|
||||
verify_set_by_json(prefs, Idx, ValA, ValAStr);
|
||||
verify_get_by_json(prefs, Idx, ValB, ValBStr);
|
||||
}
|
||||
|
||||
void handles_double()
|
||||
{
|
||||
auto constexpr Idx = Prefs::RATIO;
|
||||
auto constexpr ValA = 1.234;
|
||||
auto constexpr ValB = 5.678;
|
||||
auto const val_a_str = fmt::format("{}", ValA);
|
||||
auto const val_b_str = fmt::format("{}", ValB);
|
||||
|
||||
auto prefs = Prefs{};
|
||||
verify_get_set_by_property(prefs, Idx, ValA, ValB);
|
||||
verify_set_by_json(prefs, Idx, ValA, val_a_str);
|
||||
verify_get_by_json(prefs, Idx, ValB, val_b_str);
|
||||
}
|
||||
|
||||
void handles_qstring()
|
||||
{
|
||||
auto constexpr Idx = Prefs::DOWNLOAD_DIR;
|
||||
auto constexpr ValAStr = R"("/tmp/transmission-test-download-dir")"sv;
|
||||
auto constexpr ValBStr = R"("/tmp/transmission-test-download-dir-b")"sv;
|
||||
auto const val_a = QStringLiteral("/tmp/transmission-test-download-dir");
|
||||
auto const val_b = QStringLiteral("/tmp/transmission-test-download-dir-b");
|
||||
|
||||
auto prefs = Prefs{};
|
||||
verify_get_set_by_property(prefs, Idx, val_a, val_b);
|
||||
verify_set_by_json(prefs, Idx, val_a, ValAStr);
|
||||
verify_get_by_json(prefs, Idx, val_b, ValBStr);
|
||||
}
|
||||
|
||||
void handles_qstringlist()
|
||||
{
|
||||
auto constexpr Idx = Prefs::COMPLETE_SOUND_COMMAND;
|
||||
auto constexpr ValAStr = R"(["one","two","three"])"sv;
|
||||
auto constexpr ValBStr = R"(["alpha","beta"])"sv;
|
||||
auto const val_a = QStringList{ QStringLiteral("one"), QStringLiteral("two"), QStringLiteral("three") };
|
||||
auto const val_b = QStringList{ QStringLiteral("alpha"), QStringLiteral("beta") };
|
||||
|
||||
auto prefs = Prefs{};
|
||||
verify_get_set_by_property(prefs, Idx, val_a, val_b);
|
||||
verify_set_by_json(prefs, Idx, val_a, ValAStr);
|
||||
verify_get_by_json(prefs, Idx, val_b, ValBStr);
|
||||
}
|
||||
|
||||
void handles_qdatetime()
|
||||
{
|
||||
auto constexpr Idx = Prefs::BLOCKLIST_DATE;
|
||||
auto const val_a = QDateTime::fromMSecsSinceEpoch(1700000000000LL).toUTC();
|
||||
auto const val_a_str = fmt::format("{}", val_a.toSecsSinceEpoch());
|
||||
auto const val_b = QDateTime::fromMSecsSinceEpoch(1700000000000LL + 123000LL).toUTC();
|
||||
auto const val_b_str = fmt::format("{}", val_b.toSecsSinceEpoch());
|
||||
|
||||
auto prefs = Prefs{};
|
||||
verify_get_set_by_property(prefs, Idx, val_a, val_b);
|
||||
verify_set_by_json(prefs, Idx, val_a, val_a_str);
|
||||
verify_get_by_json(prefs, Idx, val_b, val_b_str);
|
||||
}
|
||||
|
||||
void handles_sortmode()
|
||||
{
|
||||
auto constexpr Idx = Prefs::SORT_MODE;
|
||||
auto constexpr ValA = SortMode::SortBySize;
|
||||
auto constexpr ValAStr = R"("sort_by_size")"sv;
|
||||
auto constexpr ValB = SortMode::SortByName;
|
||||
auto constexpr ValBStr = R"("sort_by_name")"sv;
|
||||
|
||||
auto prefs = Prefs{};
|
||||
verify_get_set_by_property(prefs, Idx, ValA, ValB);
|
||||
verify_set_by_json(prefs, Idx, ValA, ValAStr);
|
||||
verify_get_by_json(prefs, Idx, ValB, ValBStr);
|
||||
}
|
||||
|
||||
void handles_showmode()
|
||||
{
|
||||
auto constexpr Idx = Prefs::FILTER_MODE;
|
||||
auto constexpr ValA = ShowMode::ShowAll;
|
||||
auto constexpr ValAStr = R"("show_all")"sv;
|
||||
auto constexpr ValB = ShowMode::ShowActive;
|
||||
auto constexpr ValBStr = R"("show_active")"sv;
|
||||
|
||||
auto prefs = Prefs{};
|
||||
verify_get_set_by_property(prefs, Idx, ValA, ValB);
|
||||
verify_set_by_json(prefs, Idx, ValA, ValAStr);
|
||||
verify_get_by_json(prefs, Idx, ValB, ValBStr);
|
||||
}
|
||||
|
||||
void handles_encryptionmode()
|
||||
{
|
||||
auto constexpr Idx = Prefs::ENCRYPTION;
|
||||
auto constexpr ValA = TR_ENCRYPTION_REQUIRED;
|
||||
auto constexpr ValAStr = R"("required")"sv;
|
||||
auto constexpr ValB = TR_ENCRYPTION_PREFERRED;
|
||||
auto constexpr ValBStr = R"("preferred")"sv;
|
||||
|
||||
auto prefs = Prefs{};
|
||||
verify_get_set_by_property(prefs, Idx, ValA, ValB);
|
||||
verify_set_by_json(prefs, Idx, ValA, ValAStr);
|
||||
verify_get_by_json(prefs, Idx, ValB, ValBStr);
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
static void changed_signal_emits_when_change()
|
||||
{
|
||||
static auto constexpr Idx = Prefs::SORT_REVERSED;
|
||||
|
||||
auto prefs = Prefs{};
|
||||
auto const spy = QSignalSpy{ &prefs, &Prefs::changed };
|
||||
|
||||
auto const old_value = prefs.get<bool>(Idx);
|
||||
auto const new_value = !old_value;
|
||||
prefs.set(Idx, new_value);
|
||||
QCOMPARE(spy.count(), 1);
|
||||
auto const& signal_args = spy.first();
|
||||
QCOMPARE(signal_args.at(0).toInt(), Idx);
|
||||
}
|
||||
|
||||
static void changed_signal_does_not_emit_when_unchanged()
|
||||
{
|
||||
static auto constexpr Idx = Prefs::SORT_REVERSED;
|
||||
|
||||
auto prefs = Prefs{};
|
||||
auto const spy = QSignalSpy{ &prefs, &Prefs::changed };
|
||||
|
||||
auto const current_value = prefs.get<bool>(Idx);
|
||||
prefs.set(Idx, current_value);
|
||||
QCOMPARE(spy.count(), 0);
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
trqt::trqt_init();
|
||||
auto const app = QApplication{ argc, argv };
|
||||
auto test = PrefsTest{};
|
||||
return QTest::qExec(&test, argc, argv);
|
||||
}
|
||||
|
||||
#include "prefs-test.moc"
|
||||
Reference in New Issue
Block a user