Files
transmission/tests/qt/prefs-test.cc
Charles Kerr 21874392a9 refactor: string-utils, file-utils, env (#8417)
* refactor: add libtransmission/string-utils

* refactor: add libtransmission/file-utils

* refactor: add libtransmission/env

* build: update Xcode
2026-02-11 11:21:45 -06:00

295 lines
9.8 KiB
C++

// 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>
#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/string-utils.h>
#include <libtransmission/variant.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(tr_quark const key, std::string_view const valstr)
{
auto const json_key = tr_quark_get_string_view(key);
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, tr_quark const key, 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(key, 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(), prefs.keyval(idx).first, 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(prefs.keyval(idx).first, 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);
}
static void verify_variant_json(tr_variant const& var, std::string_view const expected)
{
auto serde = tr_variant_serde::json();
serde.compact();
auto const str = serde.to_string(var);
QCOMPARE_EQ(str, std::string{ expected });
}
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 keyval_returns_key_and_value()
{
auto prefs = Prefs{};
{
auto constexpr Idx = Prefs::MAIN_WINDOW_HEIGHT;
auto constexpr Val = 4242;
prefs.set(Idx, Val);
auto const [key, var] = prefs.keyval(Idx);
QCOMPARE_EQ(key, TR_KEY_main_window_height);
verify_variant_json(var, fmt::format("{}", Val));
}
{
auto constexpr Idx = Prefs::DOWNLOAD_DIR;
auto const val = QStringLiteral("/tmp/transmission-test-download-dir");
prefs.set(Idx, val);
auto const [key, var] = prefs.keyval(Idx);
QCOMPARE_EQ(key, TR_KEY_download_dir);
verify_variant_json(var, fmt::format(R"("{}")", val.toStdString()));
}
}
// ---
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"