Files
transmission/tests/qt/prefs-test.cc
Charles Kerr d177f9f903 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
2026-01-11 19:23:00 -06:00

263 lines
8.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_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"