refactor: move StatsMode to tr::app (#8330)

* refactor: add StatsMode to libtransmission-app

* test: add display-mode unit tests

* feat: add apicompat for StatsMode

* refactor: use StatsMode in Qt client

* refactor: add template method for gtr_set_pref()

* refactor: use StatsMode in GTK client

* ci: use the all-tests alias instead of hardcoding tests

* ci: copy runtime DLLS into test output directory for Windows test binaries
This commit is contained in:
Charles Kerr
2026-02-03 09:52:49 -06:00
committed by GitHub
parent b4c4047ab7
commit 90402fd97b
21 changed files with 357 additions and 206 deletions

View File

@@ -170,7 +170,7 @@ jobs:
-DUSE_SYSTEM_UTP=OFF `# Not packaged in Ubuntu` \
-DUSE_SYSTEM_WIDE_INTEGER=OFF `# Not packaged in Ubuntu`
- name: Make
run: cmake --build obj --config Debug --target libtransmission-test transmission-show
run: cmake --build obj --config Debug --target all-tests
- name: Test with sanitizers
uses: ./.github/actions/run-tests
with:
@@ -221,7 +221,7 @@ jobs:
-DUSE_SYSTEM_UTP=OFF `# Not packaged in Homebrew` \
-DUSE_SYSTEM_WIDE_INTEGER=OFF `# Not packaged in Homebrew`
- name: Make
run: cmake --build obj --config Debug --target libtransmission-test transmission-show
run: cmake --build obj --config Debug --target all-tests
- name: Test with sanitizers
uses: ./.github/actions/run-tests
with:

View File

@@ -1,6 +1,34 @@
include(CheckFunctionExists)
include(CheckIncludeFile)
macro(tr_setup_gtest_target TARGET_NAME TEST_PREFIX)
if(WIN32)
cmake_policy(PUSH)
cmake_minimum_required(VERSION 3.21 FATAL_ERROR)
add_custom_command(
TARGET ${TARGET_NAME} POST_BUILD
COMMAND
${CMAKE_COMMAND}
-E copy_if_different
$<TARGET_RUNTIME_DLLS:${TARGET_NAME}>
$<TARGET_FILE_DIR:${TARGET_NAME}>
COMMAND_EXPAND_LISTS
)
cmake_policy(POP)
endif()
if(NOT CMAKE_CROSSCOMPILING OR CMAKE_CROSSCOMPILING_EMULATOR)
gtest_discover_tests(${TARGET_NAME}
TEST_PREFIX "${TEST_PREFIX}")
else()
add_test(
NAME ${TARGET_NAME}
COMMAND ${TARGET_NAME})
endif()
endmacro()
macro(tr_auto_option_changed NAME ACC VAL FIL STK)
if(NOT ("${VAL}" STREQUAL "AUTO" OR "${VAL}" STREQUAL "ON" OR "${VAL}" STREQUAL "OFF"))
if("${VAL}" STREQUAL "0" OR "${VAL}" STREQUAL "NO" OR "${VAL}" STREQUAL "FALSE" OR "${VAL}" STREQUAL "N")

View File

@@ -18,6 +18,8 @@
#include "TorrentCellRenderer.h"
#endif
#include <libtransmission-app/display-modes.h>
#include <libtransmission/transmission.h>
#include <libtransmission/values.h>
@@ -62,6 +64,7 @@
using namespace std::string_literals;
using namespace std::string_view_literals;
using namespace tr::Values;
using namespace tr::app;
using VariantInt = Glib::Variant<int>;
using VariantDouble = Glib::Variant<double>;
@@ -132,7 +135,6 @@ private:
void syncAltSpeedButton();
void status_menu_toggled_cb(std::string const& action_name, Glib::ustring const& val);
void onOptionsClicked();
void alt_speed_toggled_cb();
void onAltSpeedToggledIdle();
@@ -144,7 +146,6 @@ private:
sigc::signal<void()> signal_selection_changed_;
Glib::RefPtr<Gio::ActionGroup> options_actions_;
Glib::RefPtr<Gio::ActionGroup> stats_actions_;
std::array<OptionMenuInfo, 2> speed_menu_info_;
OptionMenuInfo ratio_menu_info_;
@@ -369,12 +370,6 @@ MainWindow::Impl::~Impl()
pref_handler_id_.disconnect();
}
void MainWindow::Impl::status_menu_toggled_cb(std::string const& action_name, Glib::ustring const& val)
{
stats_actions_->change_action_state(action_name, VariantString::create(val));
core_->set_pref(TR_KEY_statusbar_stats, val.raw());
}
void MainWindow::Impl::syncAltSpeedButton()
{
bool const b = gtr_pref_flag_get(TR_KEY_alt_speed_enabled);
@@ -589,38 +584,45 @@ void MainWindow::Impl::onOptionsClicked()
Glib::RefPtr<Gio::MenuModel> MainWindow::Impl::createStatsMenu()
{
struct StatsModeInfo
using StatsType = gint32;
using StatsVariant = Glib::Variant<StatsType>;
auto const to_var = [](auto const mode)
{
char const* val;
char const* i18n;
return StatsVariant::create(static_cast<StatsType>(mode));
};
static auto const stats_modes = std::array<StatsModeInfo, 4>({ {
{ "total-ratio", N_("Total Ratio") },
{ "session-ratio", N_("Session Ratio") },
{ "total-transfer", N_("Total Transfer") },
{ "session-transfer", N_("Session Transfer") },
} });
auto top = Gio::Menu::create();
auto actions = Gio::SimpleActionGroup::create();
// build the action group
static auto constexpr Key = TR_KEY_statusbar_stats;
auto const action_name = "stats-mode"s;
auto const full_action_name = fmt::format("{}.{}", StatsMenuActionGroupName, action_name);
auto stats_mode_action = actions->add_action_radio_string(
auto const current_value = gtr_pref_get<StatsMode>(Key).value_or(DefaultStatsMode);
auto actions = Gio::SimpleActionGroup::create();
actions->add_action_radio_integer(
action_name,
[this, action_name](Glib::ustring const& value) { status_menu_toggled_cb(action_name, value); },
gtr_pref_string_get(TR_KEY_statusbar_stats));
[this, action_name, actions, to_var](gint32 const ival)
{
actions->change_action_state(action_name, to_var(ival));
core_->set_pref(Key, static_cast<StatsMode>(ival));
},
static_cast<StatsType>(current_value));
for (auto const& mode : stats_modes)
// build the menu
auto const full_action_name = fmt::format("{}.{}", StatsMenuActionGroupName, action_name);
auto top = Gio::Menu::create();
auto const stats_modes = std::array<std::pair<StatsMode, char const*>, StatsModeCount>({ {
{ StatsMode::TotalRatio, N_("Total Ratio") },
{ StatsMode::SessionRatio, N_("Session Ratio") },
{ StatsMode::TotalTransfer, N_("Total Transfer") },
{ StatsMode::SessionTransfer, N_("Session Transfer") },
} });
for (auto const& [mode, display_name] : stats_modes)
{
auto item = Gio::MenuItem::create(_(mode.i18n), full_action_name);
item->set_action_and_target(full_action_name, VariantString::create(mode.val));
top->append_item(item);
auto item = Gio::MenuItem::create(_(display_name), full_action_name);
item->set_action_and_target(full_action_name, to_var(mode));
top->append_item(std::move(item));
}
window_.insert_action_group(std::string(StatsMenuActionGroupName), actions);
stats_actions_ = actions;
return top;
}
@@ -755,40 +757,24 @@ MainWindow::Impl::Impl(
#endif
}
namespace
{
} // namespace
void MainWindow::Impl::updateStats()
{
Glib::ustring buf;
auto const* const session = core_->get_session();
/* update the stats */
if (auto const pch = gtr_pref_string_get(TR_KEY_statusbar_stats); pch == "session-ratio")
{
auto const stats = tr_sessionGetStats(session);
buf = fmt::format(fmt::runtime(_("Ratio: {ratio}")), fmt::arg("ratio", tr_strlratio(stats.ratio)));
}
else if (pch == "session-transfer")
{
auto const stats = tr_sessionGetStats(session);
buf = fmt::format(
fmt::runtime(C_("current session totals", "Down: {downloaded_size}, Up: {uploaded_size}")),
fmt::arg("downloaded_size", tr_strlsize(stats.downloadedBytes)),
fmt::arg("uploaded_size", tr_strlsize(stats.uploadedBytes)));
}
else if (pch == "total-transfer")
{
auto const stats = tr_sessionGetCumulativeStats(session);
buf = fmt::format(
fmt::runtime(C_("all-time totals", "Down: {downloaded_size}, Up: {uploaded_size}")),
fmt::arg("downloaded_size", tr_strlsize(stats.downloadedBytes)),
fmt::arg("uploaded_size", tr_strlsize(stats.uploadedBytes)));
}
else /* default is total-ratio */
{
auto const stats = tr_sessionGetCumulativeStats(session);
buf = fmt::format(fmt::runtime(_("Ratio: {ratio}")), fmt::arg("ratio", tr_strlratio(stats.ratio)));
}
stats_lb_->set_text(buf);
static_assert(StatsModeCount == 4U && "StatsMode changed: update this code");
auto const mode = gtr_pref_get<StatsMode>(TR_KEY_statusbar_stats).value_or(DefaultStatsMode);
auto const use_session_stats = mode == StatsMode::SessionRatio || mode == StatsMode::SessionTransfer;
auto const* const ses = core_->get_session();
auto const stats = use_session_stats ? tr_sessionGetStats(ses) : tr_sessionGetCumulativeStats(ses);
stats_lb_->set_text(
mode == StatsMode::SessionTransfer || mode == StatsMode::TotalTransfer ?
fmt::format(
fmt::runtime(_("Down: {downloaded_size}, Up: {uploaded_size}")),
fmt::arg("downloaded_size", tr_strlsize(stats.downloadedBytes)),
fmt::arg("uploaded_size", tr_strlsize(stats.uploadedBytes))) :
fmt::format(fmt::runtime(_("Ratio: {ratio}")), fmt::arg("ratio", tr_strlratio(stats.ratio))));
}
void MainWindow::Impl::updateSpeeds()

View File

@@ -73,7 +73,7 @@ std::string gl_confdir;
map.try_emplace(TR_KEY_show_tracker_scrapes, false);
map.try_emplace(TR_KEY_sort_mode, to_variant(DefaultSortMode));
map.try_emplace(TR_KEY_sort_reversed, false);
map.try_emplace(TR_KEY_statusbar_stats, "total-ratio"sv);
map.try_emplace(TR_KEY_statusbar_stats, to_variant(DefaultStatsMode));
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);

View File

@@ -25,6 +25,14 @@ template<typename T>
return iter != std::end(map) ? to_value<T>(iter->second) : std::nullopt;
}
template<typename T>
void gtr_pref_set(tr_quark const key, T const& val)
{
using namespace tr::serializer;
auto& map = gtr_pref_get_map();
map.insert_or_assign(key, to_variant(val));
}
void gtr_pref_init(std::string_view config_dir);
int64_t gtr_pref_int_get(tr_quark key);

View File

@@ -1140,52 +1140,6 @@ void Session::Impl::maybe_inhibit_hibernation()
set_hibernation_allowed(hibernation_allowed);
}
/**
*** Prefs
**/
void Session::Impl::commit_prefs_change(tr_quark const key)
{
signal_prefs_changed_.emit(key);
gtr_pref_save(session_);
}
void Session::set_pref(tr_quark const key, std::string const& newval)
{
if (newval != gtr_pref_string_get(key))
{
gtr_pref_string_set(key, newval);
impl_->commit_prefs_change(key);
}
}
void Session::set_pref(tr_quark const key, bool newval)
{
if (newval != gtr_pref_flag_get(key))
{
gtr_pref_flag_set(key, newval);
impl_->commit_prefs_change(key);
}
}
void Session::set_pref(tr_quark const key, int newval)
{
if (newval != gtr_pref_int_get(key))
{
gtr_pref_int_set(key, newval);
impl_->commit_prefs_change(key);
}
}
void Session::set_pref(tr_quark const key, double newval)
{
if (std::fabs(newval - gtr_pref_double_get(key)) >= 0.0001)
{
gtr_pref_double_set(key, newval);
impl_->commit_prefs_change(key);
}
}
/***
****
**** RPC Interface

View File

@@ -5,11 +5,13 @@
#pragma once
#include "GtkCompat.h"
#include "Prefs.h"
#include "Torrent.h"
#include <libtransmission-app/favicon-cache.h>
#include <libtransmission/transmission.h>
#include <libtransmission/serializer.h>
#include <libtransmission/variant.h>
#include <gdkmm/pixbuf.h>
@@ -133,10 +135,16 @@ public:
*** Set a preference value, save the prefs file, and emit the "prefs-changed" signal
**/
void set_pref(tr_quark key, std::string const& val);
void set_pref(tr_quark key, bool val);
void set_pref(tr_quark key, int val);
void set_pref(tr_quark key, double val);
template<typename T>
void set_pref(tr_quark const key, T const& val)
{
if (gtr_pref_get<T>(key) != val)
{
gtr_pref_set<T>(key, val);
signal_prefs_changed().emit(key);
gtr_pref_save(get_session());
}
}
// ---

View File

@@ -116,6 +116,49 @@ tr_variant from_sort_mode(SortMode const& src)
return from_sort_mode(DefaultSortMode);
}
// ---
auto constexpr StatsKeys = std::array<std::pair<std::string_view, StatsMode>, StatsModeCount>{ {
{ "session_ratio", StatsMode::SessionRatio },
{ "session_transfer", StatsMode::SessionTransfer },
{ "total_ratio", StatsMode::TotalRatio },
{ "total_transfer", StatsMode::TotalTransfer },
} };
bool to_stats_mode(tr_variant const& src, StatsMode* tgt)
{
static constexpr auto& Keys = StatsKeys;
if (auto const str = src.value_if<std::string_view>())
{
for (auto const& [key, val] : Keys)
{
if (str == key)
{
*tgt = val;
return true;
}
}
}
return false;
}
tr_variant from_stats_mode(StatsMode const& src)
{
static constexpr auto& Keys = StatsKeys;
for (auto const& [key, val] : Keys)
{
if (src == val)
{
return tr_variant::unmanaged_string(key);
}
}
return from_stats_mode(DefaultStatsMode);
}
} // unnamed namespace
void register_app_converters()
@@ -128,6 +171,7 @@ void register_app_converters()
using Converters = tr::serializer::Converters;
Converters::add(to_show_mode, from_show_mode);
Converters::add(to_sort_mode, from_sort_mode);
Converters::add(to_stats_mode, from_stats_mode);
});
}

View File

@@ -37,4 +37,14 @@ enum class SortMode
inline auto constexpr SortModeCount = 10U;
inline auto constexpr DefaultSortMode = SortMode::SortByName;
enum class StatsMode
{
TotalRatio,
TotalTransfer,
SessionRatio,
SessionTransfer,
};
inline auto constexpr StatsModeCount = 4U;
inline auto constexpr DefaultStatsMode = StatsMode::TotalRatio;
} // namespace tr::app

View File

@@ -639,6 +639,23 @@ struct State
}
}
if (state.is_settings && state.current_key_is_any_of({ TR_KEY_statusbar_stats, TR_KEY_statusbar_stats_kebab_APICOMPAT }))
{
static auto constexpr Strings = std::array<std::pair<std::string_view, std::string_view>, 4U>{ {
{ "total_ratio", "total-ratio" },
{ "total_transfer", "total-transfer" },
{ "session_ratio", "session-ratio" },
{ "session_transfer", "session-transfer" },
} };
for (auto const& [current, legacy] : Strings)
{
if (src == current || src == legacy)
{
return state.style == Style::Tr5 ? current : legacy;
}
}
}
// TODO(ckerr): replace `new_key == TR_KEY_TORRENTS` here to turn on convert
// if it's an array inside an array val whose key was `torrents`.
// This is for the edge case of table mode: `torrents : [ [ 'key1', 'key2' ], [ ... ] ]`

View File

@@ -410,21 +410,21 @@ QMenu* MainWindow::createOptionsMenu()
QMenu* MainWindow::createStatsModeMenu()
{
auto const stats_modes = std::array<std::pair<QAction*, QString>, 4>{ {
{ ui_.action_TotalRatio, total_ratio_stats_mode_name_ },
{ ui_.action_TotalTransfer, total_transfer_stats_mode_name_ },
{ ui_.action_SessionRatio, session_ratio_stats_mode_name_ },
{ ui_.action_SessionTransfer, session_transfer_stats_mode_name_ },
auto const stats_modes = std::array<std::pair<QAction*, StatsMode>, StatsModeCount>{ {
{ ui_.action_TotalRatio, StatsMode::TotalRatio },
{ ui_.action_TotalTransfer, StatsMode::TotalTransfer },
{ ui_.action_SessionRatio, StatsMode::SessionRatio },
{ ui_.action_SessionTransfer, StatsMode::SessionTransfer },
} };
auto* action_group = new QActionGroup{ this };
auto* menu = new QMenu{ this };
for (auto const& mode : stats_modes)
for (auto const& [action, mode] : stats_modes)
{
mode.first->setProperty(StatsModeKey, QString{ mode.second });
action_group->addAction(mode.first);
menu->addAction(mode.first);
action->setProperty(StatsModeKey, QVariant::fromValue(mode));
action_group->addAction(action);
menu->addAction(action);
}
connect(action_group, &QActionGroup::triggered, this, &MainWindow::onStatsModeChanged);
@@ -753,34 +753,16 @@ void MainWindow::refreshStatusBar(TransferStats const& stats)
ui_.downloadSpeedLabel->setText(stats.speed_down.to_download_qstring());
ui_.downloadSpeedLabel->setVisible(stats.peers_sending);
auto const mode = prefs_.get<QString>(Prefs::STATUSBAR_STATS);
auto str = QString{};
if (mode == session_ratio_stats_mode_name_)
{
str = tr("Ratio: %1").arg(Formatter::ratio_to_string(session_.getStats().ratio));
}
else if (mode == session_transfer_stats_mode_name_)
{
auto const& st = session_.getStats();
str = tr("Down: %1, Up: %2")
.arg(Formatter::storage_to_string(st.downloadedBytes))
.arg(Formatter::storage_to_string(st.uploadedBytes));
}
else if (mode == total_transfer_stats_mode_name_)
{
auto const& st = session_.getCumulativeStats();
str = tr("Down: %1, Up: %2")
.arg(Formatter::storage_to_string(st.downloadedBytes))
.arg(Formatter::storage_to_string(st.uploadedBytes));
}
else // default is "total-ratio"
{
assert(mode == total_ratio_stats_mode_name_);
str = tr("Ratio: %1").arg(Formatter::ratio_to_string(session_.getCumulativeStats().ratio));
}
ui_.statsLabel->setText(str);
static_assert(StatsModeCount == 4U && "StatsMode changed: update this code");
auto const mode = prefs_.get<StatsMode>(Prefs::STATUSBAR_STATS);
auto const use_session_stats = mode == StatsMode::SessionRatio || mode == StatsMode::SessionTransfer;
auto const& st = use_session_stats ? session_.getStats() : session_.getCumulativeStats();
ui_.statsLabel->setText(
mode == StatsMode::SessionTransfer || mode == StatsMode::TotalTransfer ?
tr("Down: %1, Up: %2")
.arg(Formatter::storage_to_string(st.downloadedBytes))
.arg(Formatter::storage_to_string(st.uploadedBytes)) :
tr("Ratio: %1").arg(Formatter::ratio_to_string(st.ratio)));
}
void MainWindow::refreshTorrentViewHeader()
@@ -1028,7 +1010,7 @@ void MainWindow::reannounceSelected()
void MainWindow::onStatsModeChanged(QAction const* action)
{
prefs_.set(Prefs::STATUSBAR_STATS, action->property(StatsModeKey).toString());
prefs_.set(Prefs::STATUSBAR_STATS, action->property(StatsModeKey).value<StatsMode>());
}
/**
@@ -1109,31 +1091,33 @@ void MainWindow::trayActivated(QSystemTrayIcon::ActivationReason reason)
}
}
void MainWindow::refreshPref(int key)
void MainWindow::refreshPref(int const idx)
{
auto b = bool{};
auto str = QString{};
switch (key)
switch (idx)
{
case Prefs::STATUSBAR_STATS:
str = prefs_.get<QString>(key);
for (auto* action : ui_.action_TotalRatio->actionGroup()->actions())
{
action->setChecked(str == action->property(StatsModeKey).toString());
}
auto const needle = QVariant::fromValue(prefs_.get<StatsMode>(idx));
refreshSoon(REFRESH_STATUS_BAR);
for (auto* action : ui_.action_TotalRatio->actionGroup()->actions())
{
action->setChecked(needle == action->property(StatsModeKey));
}
refreshSoon(REFRESH_STATUS_BAR);
}
break;
case Prefs::SORT_REVERSED:
ui_.action_ReverseSortOrder->setChecked(prefs_.get<bool>(key));
ui_.action_ReverseSortOrder->setChecked(prefs_.get<bool>(idx));
break;
case Prefs::SORT_MODE:
{
auto const sort_mode = prefs_.get<SortMode>(key);
auto const sort_mode = prefs_.get<SortMode>(idx);
for (auto* action : ui_.action_SortByActivity->actionGroup()->actions())
{
action->setChecked(sort_mode == action->property(SortModeKey).value<SortMode>());
@@ -1143,51 +1127,51 @@ void MainWindow::refreshPref(int key)
break;
case Prefs::DSPEED_ENABLED:
(prefs_.get<bool>(key) ? dlimit_on_action_ : dlimit_off_action_)->setChecked(true);
(prefs_.get<bool>(idx) ? dlimit_on_action_ : dlimit_off_action_)->setChecked(true);
break;
case Prefs::DSPEED:
dlimit_on_action_->setText(
tr("Limited at %1").arg(Speed{ prefs_.get<unsigned int>(key), Speed::Units::KByps }.to_qstring()));
tr("Limited at %1").arg(Speed{ prefs_.get<unsigned int>(idx), Speed::Units::KByps }.to_qstring()));
break;
case Prefs::USPEED_ENABLED:
(prefs_.get<bool>(key) ? ulimit_on_action_ : ulimit_off_action_)->setChecked(true);
(prefs_.get<bool>(idx) ? ulimit_on_action_ : ulimit_off_action_)->setChecked(true);
break;
case Prefs::USPEED:
ulimit_on_action_->setText(
tr("Limited at %1").arg(Speed{ prefs_.get<unsigned int>(key), Speed::Units::KByps }.to_qstring()));
tr("Limited at %1").arg(Speed{ prefs_.get<unsigned int>(idx), Speed::Units::KByps }.to_qstring()));
break;
case Prefs::RATIO_ENABLED:
(prefs_.get<bool>(key) ? ratio_on_action_ : ratio_off_action_)->setChecked(true);
(prefs_.get<bool>(idx) ? ratio_on_action_ : ratio_off_action_)->setChecked(true);
break;
case Prefs::RATIO:
ratio_on_action_->setText(tr("Stop at Ratio (%1)").arg(Formatter::ratio_to_string(prefs_.get<double>(key))));
ratio_on_action_->setText(tr("Stop at Ratio (%1)").arg(Formatter::ratio_to_string(prefs_.get<double>(idx))));
break;
case Prefs::FILTERBAR:
b = prefs_.get<bool>(key);
b = prefs_.get<bool>(idx);
filter_bar_->setVisible(b);
ui_.action_Filterbar->setChecked(b);
break;
case Prefs::STATUSBAR:
b = prefs_.get<bool>(key);
b = prefs_.get<bool>(idx);
ui_.statusBar->setVisible(b);
ui_.action_Statusbar->setChecked(b);
break;
case Prefs::TOOLBAR:
b = prefs_.get<bool>(key);
b = prefs_.get<bool>(idx);
ui_.toolBar->setVisible(b);
ui_.action_Toolbar->setChecked(b);
break;
case Prefs::SHOW_TRAY_ICON:
b = prefs_.get<bool>(key);
b = prefs_.get<bool>(idx);
ui_.action_TrayIcon->setChecked(b);
tray_icon_.setVisible(b);
QApplication::setQuitOnLastWindowClosed(!b);
@@ -1195,7 +1179,7 @@ void MainWindow::refreshPref(int key)
break;
case Prefs::COMPACT_VIEW:
b = prefs_.get<bool>(key);
b = prefs_.get<bool>(idx);
ui_.action_CompactView->setChecked(b);
ui_.listView->setItemDelegate(b ? torrent_delegate_min_ : torrent_delegate_);
break;

View File

@@ -113,7 +113,7 @@ private slots:
void openStats();
void openTorrent();
void openURL();
void refreshPref(int key);
void refreshPref(int idx);
void removeTorrents(bool const delete_files);
void setLocation();
void setSortAscendingPref(bool);
@@ -179,10 +179,6 @@ private:
bool auto_add_clipboard_links_ = {};
QStringList clipboard_processed_keys_ = {};
QString const total_ratio_stats_mode_name_ = QStringLiteral("total-ratio");
QString const total_transfer_stats_mode_name_ = QStringLiteral("total-transfer");
QString const session_ratio_stats_mode_name_ = QStringLiteral("session-ratio");
QString const session_transfer_stats_mode_name_ = QStringLiteral("session-transfer");
QString const show_options_checkbox_name_ = QStringLiteral("show-options-checkbox");
struct TransferStats

View File

@@ -49,6 +49,9 @@ template<typename T>
case UserMetaType::SortModeType:
return qvarFromOptional(ser::to_value<SortMode>(var));
case UserMetaType::StatsModeType:
return qvarFromOptional(ser::to_value<StatsMode>(var));
case UserMetaType::ShowModeType:
return qvarFromOptional(ser::to_value<ShowMode>(var));
@@ -89,6 +92,9 @@ template<typename T>
case UserMetaType::ShowModeType:
return ser::to_variant(var.value<ShowMode>());
case UserMetaType::StatsModeType:
return ser::to_variant(var.value<StatsMode>());
case QMetaType::QString:
return ser::to_variant(var.value<QString>());

View File

@@ -199,7 +199,7 @@ private:
{ COMPACT_VIEW, TR_KEY_compact_view, QMetaType::Bool },
{ FILTERBAR, TR_KEY_show_filterbar, QMetaType::Bool },
{ STATUSBAR, TR_KEY_show_statusbar, QMetaType::Bool },
{ STATUSBAR_STATS, TR_KEY_statusbar_stats, QMetaType::QString },
{ STATUSBAR_STATS, TR_KEY_statusbar_stats, UserMetaType::StatsModeType },
{ SHOW_TRACKER_SCRAPES, TR_KEY_show_tracker_scrapes, QMetaType::Bool },
{ SHOW_BACKUP_TRACKERS, TR_KEY_show_backup_trackers, QMetaType::Bool },
{ TOOLBAR, TR_KEY_show_toolbar, QMetaType::Bool },

View File

@@ -18,6 +18,7 @@ public:
{
ShowModeType = QMetaType::User,
SortModeType,
StatsModeType,
EncryptionModeType,
};
};
@@ -32,3 +33,8 @@ inline auto constexpr ShowModeCount = tr::app::ShowModeCount;
using SortMode = tr::app::SortMode;
Q_DECLARE_METATYPE(SortMode)
inline auto constexpr DefaultSortMode = tr::app::DefaultSortMode;
using StatsMode = tr::app::StatsMode;
Q_DECLARE_METATYPE(StatsMode)
inline auto constexpr DefaultStatsMode = tr::app::DefaultStatsMode;
inline auto constexpr StatsModeCount = tr::app::StatsModeCount;

View File

@@ -1,6 +1,7 @@
include(TrGTest)
add_subdirectory(libtransmission)
add_subdirectory(libtransmission-app)
if(ENABLE_UTILS)
add_subdirectory(utils)
endif()
@@ -19,6 +20,8 @@ if(TARGET libtransmission-test)
add_dependencies(all-tests libtransmission-test)
endif()
add_dependencies(all-tests libtransmission-app-test)
if(TARGET qt-tests)
add_dependencies(all-tests qt-tests)
endif()

View File

@@ -0,0 +1,20 @@
include(GoogleTest)
add_executable(libtransmission-app-test)
target_sources(libtransmission-app-test
PRIVATE
display-mode-tests.cc
test-fixtures.h)
set_property(
TARGET libtransmission-app-test
PROPERTY FOLDER "tests")
target_link_libraries(libtransmission-app-test
PRIVATE
${TR_NAME}-app
${TR_NAME}
GTest::gtest_main)
tr_setup_gtest_target(libtransmission-app-test "LTA.")

View File

@@ -0,0 +1,86 @@
// 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 <array>
#include <string_view>
#include <gtest/gtest.h>
#include <libtransmission/serializer.h>
#include <libtransmission/variant.h>
#include "libtransmission-app/display-modes.h"
#include "test-fixtures.h"
using DisplayModeTest = TransmissionTest;
using namespace std::literals;
using tr::serializer::Converters;
namespace
{
template<typename T, size_t N>
void testModeRoundtrip(std::array<std::pair<std::string_view, T>, N> const& items)
{
for (auto const& [key, mode] : items)
{
auto const var = Converters::serialize(mode);
EXPECT_TRUE(var.template holds_alternative<std::string_view>());
EXPECT_EQ(var.template value_if<std::string_view>().value_or(""sv), key);
auto out = T{};
EXPECT_TRUE(Converters::deserialize(tr_variant{ key }, &out));
EXPECT_EQ(out, mode);
}
}
} // namespace
TEST_F(DisplayModeTest, showModeStringsRoundtrip)
{
auto constexpr Items = std::array<std::pair<std::string_view, tr::app::ShowMode>, tr::app::ShowModeCount>{ {
{ "show_active", tr::app::ShowMode::ShowActive },
{ "show_all", tr::app::ShowMode::ShowAll },
{ "show_downloading", tr::app::ShowMode::ShowDownloading },
{ "show_error", tr::app::ShowMode::ShowError },
{ "show_finished", tr::app::ShowMode::ShowFinished },
{ "show_paused", tr::app::ShowMode::ShowPaused },
{ "show_seeding", tr::app::ShowMode::ShowSeeding },
{ "show_verifying", tr::app::ShowMode::ShowVerifying },
} };
testModeRoundtrip(Items);
}
TEST_F(DisplayModeTest, sortModeStringsRoundtrip)
{
auto constexpr Items = std::array<std::pair<std::string_view, tr::app::SortMode>, tr::app::SortModeCount>{ {
{ "sort_by_activity", tr::app::SortMode::SortByActivity },
{ "sort_by_age", tr::app::SortMode::SortByAge },
{ "sort_by_eta", tr::app::SortMode::SortByEta },
{ "sort_by_id", tr::app::SortMode::SortById },
{ "sort_by_name", tr::app::SortMode::SortByName },
{ "sort_by_progress", tr::app::SortMode::SortByProgress },
{ "sort_by_queue", tr::app::SortMode::SortByQueue },
{ "sort_by_ratio", tr::app::SortMode::SortByRatio },
{ "sort_by_size", tr::app::SortMode::SortBySize },
{ "sort_by_state", tr::app::SortMode::SortByState },
} };
testModeRoundtrip(Items);
}
TEST_F(DisplayModeTest, statsModeStringsRoundtrip)
{
auto constexpr Items = std::array<std::pair<std::string_view, tr::app::StatsMode>, tr::app::StatsModeCount>{ {
{ "total_ratio", tr::app::StatsMode::TotalRatio },
{ "total_transfer", tr::app::StatsMode::TotalTransfer },
{ "session_ratio", tr::app::StatsMode::SessionRatio },
{ "session_transfer", tr::app::StatsMode::SessionTransfer },
} };
testModeRoundtrip(Items);
}

View File

@@ -0,0 +1,19 @@
// 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
#include <gtest/gtest.h>
#include "libtransmission-app/app.h"
class TransmissionTest : public ::testing::Test
{
protected:
static void SetUpTestSuite()
{
tr::app::init();
}
};

View File

@@ -87,31 +87,7 @@ target_link_libraries(libtransmission-test
libevent::event
WideInteger::WideInteger)
if (WIN32)
cmake_policy(PUSH)
cmake_minimum_required(VERSION 3.21 FATAL_ERROR)
add_custom_command(
TARGET libtransmission-test POST_BUILD
COMMAND
${CMAKE_COMMAND}
-E copy_if_different
$<TARGET_RUNTIME_DLLS:libtransmission-test>
$<TARGET_FILE_DIR:libtransmission-test>
COMMAND_EXPAND_LISTS
)
cmake_policy(POP)
endif ()
if(NOT CMAKE_CROSSCOMPILING OR CMAKE_CROSSCOMPILING_EMULATOR)
gtest_discover_tests(libtransmission-test
TEST_PREFIX "LT.")
else()
add_test(
NAME libtransmission-test
COMMAND libtransmission-test)
endif()
tr_setup_gtest_target(libtransmission-test "LT.")
add_custom_command(
TARGET libtransmission-test

View File

@@ -747,7 +747,7 @@ constexpr std::string_view CurrentSettingsJson = R"json({
"speed_limit_up_enabled": false,
"start_added_torrents": true,
"start_minimized": false,
"statusbar_stats": "total-ratio",
"statusbar_stats": "total_ratio",
"torrent_added_notification_enabled": true,
"torrent_complete_notification_enabled": true,
"torrent_complete_sound_command": [