Skip to content

Commit b79976d

Browse files
committed
Issue 02: add wallet list model unit-test seam and coverage
1 parent 73bfd1f commit b79976d

File tree

5 files changed

+160
-8
lines changed

5 files changed

+160
-8
lines changed

qml/models/walletlistmodel.cpp

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,42 @@
55
#include <qml/models/walletlistmodel.h>
66

77
#include <interfaces/node.h>
8+
#include <interfaces/wallet.h>
89

910
#include <QSet>
1011

11-
WalletListModel::WalletListModel(interfaces::Node& node, QObject *parent)
12-
: QAbstractListModel(parent)
13-
, m_node(node)
12+
#include <cassert>
13+
#include <memory>
14+
15+
namespace {
16+
class WalletLoaderDataSource final : public WalletListModel::DataSource
17+
{
18+
public:
19+
explicit WalletLoaderDataSource(interfaces::Node& node)
20+
: m_node(node)
21+
{
22+
}
23+
24+
WalletListModel::WalletDirEntries listWalletDir() override
25+
{
26+
return m_node.walletLoader().listWalletDir();
27+
}
28+
29+
private:
30+
interfaces::Node& m_node;
31+
};
32+
} // namespace
33+
34+
WalletListModel::WalletListModel(interfaces::Node& node, QObject* parent)
35+
: WalletListModel(std::make_unique<WalletLoaderDataSource>(node), parent)
36+
{
37+
}
38+
39+
WalletListModel::WalletListModel(std::unique_ptr<DataSource> data_source, QObject* parent)
40+
: QAbstractListModel(parent)
41+
, m_data_source(std::move(data_source))
1442
{
43+
assert(m_data_source);
1544
}
1645

1746
void WalletListModel::listWalletDir()
@@ -23,10 +52,12 @@ void WalletListModel::listWalletDir()
2352
existing_names.insert(name);
2453
}
2554

26-
for (const auto& [path, info] : m_node.walletLoader().listWalletDir()) {
55+
for (const auto& [path, info] : m_data_source->listWalletDir()) {
56+
Q_UNUSED(info);
2757
QString qname = QString::fromStdString(path);
2858
if (!existing_names.contains(qname)) {
29-
addItem({ qname });
59+
addItem({qname});
60+
existing_names.insert(qname);
3061
}
3162
}
3263
}

qml/models/walletlistmodel.h

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@
55
#ifndef BITCOIN_QML_MODELS_WALLETLISTMODEL_H
66
#define BITCOIN_QML_MODELS_WALLETLISTMODEL_H
77

8-
#include <interfaces/wallet.h>
98
#include <QAbstractListModel>
109
#include <QList>
1110

11+
#include <memory>
12+
#include <string>
13+
#include <utility>
14+
#include <vector>
15+
1216
namespace interfaces {
1317
class Node;
1418
}
@@ -18,7 +22,17 @@ class WalletListModel : public QAbstractListModel
1822
Q_OBJECT
1923

2024
public:
21-
WalletListModel(interfaces::Node& node, QObject *parent = nullptr);
25+
using WalletDirEntries = std::vector<std::pair<std::string, std::string>>;
26+
27+
class DataSource
28+
{
29+
public:
30+
virtual ~DataSource() = default;
31+
virtual WalletDirEntries listWalletDir() = 0;
32+
};
33+
34+
WalletListModel(interfaces::Node& node, QObject* parent = nullptr);
35+
WalletListModel(std::unique_ptr<DataSource> data_source, QObject* parent = nullptr);
2236
~WalletListModel() = default;
2337

2438
enum Roles {
@@ -40,7 +54,7 @@ public Q_SLOTS:
4054
void addItem(const Item &item);
4155

4256
QList<Item> m_items;
43-
interfaces::Node& m_node;
57+
std::unique_ptr<DataSource> m_data_source;
4458
};
4559

4660
#endif // BITCOIN_QML_MODELS_WALLETLISTMODEL_H

test/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ add_executable(bitcoinqml_unit_tests
3030
test_proxysettings.cpp
3131
test_sendrecipientslistmodel.cpp
3232
test_desktopwindowbehaviormodel.cpp
33+
test_walletlistmodel.cpp
3334
test_walletloaderror.cpp
3435
test_walletlifecycleadapter.cpp
3536
test_walletrestoreadapter.cpp
@@ -68,6 +69,7 @@ add_executable(bitcoinqml_unit_tests
6869
${CMAKE_CURRENT_SOURCE_DIR}/../qml/models/rpccommandexecutor.cpp
6970
${CMAKE_CURRENT_SOURCE_DIR}/../qml/models/proxysettings.cpp
7071
${CMAKE_CURRENT_SOURCE_DIR}/../qml/models/desktopwindowbehaviormodel.cpp
72+
${CMAKE_CURRENT_SOURCE_DIR}/../qml/models/walletlistmodel.cpp
7173
${CMAKE_CURRENT_SOURCE_DIR}/../qml/models/walletloaderror.cpp
7274
${CMAKE_CURRENT_SOURCE_DIR}/../qml/models/walletlifecycleadapter.cpp
7375
${CMAKE_CURRENT_SOURCE_DIR}/../qml/models/walletrestoreadapter.cpp

test/test_unit_tests_main.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ int RunSendRecipientTests(int argc, char* argv[]);
3030
int RunAddressBookModelTests(int argc, char* argv[]);
3131
int RunSendRecipientsListModelTests(int argc, char* argv[]);
3232
int RunDesktopWindowBehaviorModelTests(int argc, char* argv[]);
33+
int RunWalletListModelTests(int argc, char* argv[]);
3334
int RunWalletLoadErrorTests(int argc, char* argv[]);
3435
int RunWalletLifecycleAdapterTests(int argc, char* argv[]);
3536
int RunWalletRestoreAdapterTests(int argc, char* argv[]);
@@ -74,6 +75,7 @@ int main(int argc, char* argv[])
7475
status |= RunAddressBookModelTests(argc, argv);
7576
status |= RunSendRecipientsListModelTests(argc, argv);
7677
status |= RunDesktopWindowBehaviorModelTests(argc, argv);
78+
status |= RunWalletListModelTests(argc, argv);
7779
status |= RunWalletLoadErrorTests(argc, argv);
7880
status |= RunWalletLifecycleAdapterTests(argc, argv);
7981
status |= RunWalletRestoreAdapterTests(argc, argv);

test/test_walletlistmodel.cpp

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Copyright (c) 2026 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <qml/models/walletlistmodel.h>
6+
7+
#include <QtTest/QtTest>
8+
9+
#include <memory>
10+
11+
class FakeWalletListDataSource final : public WalletListModel::DataSource
12+
{
13+
public:
14+
WalletListModel::WalletDirEntries m_entries{};
15+
16+
WalletListModel::WalletDirEntries listWalletDir() override
17+
{
18+
return m_entries;
19+
}
20+
};
21+
22+
class WalletListModelTests : public QObject
23+
{
24+
Q_OBJECT
25+
26+
private Q_SLOTS:
27+
void role_names_expose_name_key();
28+
void list_wallet_dir_adds_entries_and_filters_duplicates();
29+
void subsequent_list_wallet_dir_only_appends_new_wallets();
30+
void invalid_index_returns_empty_variant();
31+
};
32+
33+
void WalletListModelTests::role_names_expose_name_key()
34+
{
35+
auto source = std::make_unique<FakeWalletListDataSource>();
36+
WalletListModel model(std::move(source));
37+
38+
const QHash<int, QByteArray> roles = model.roleNames();
39+
QVERIFY(roles.contains(WalletListModel::NameRole));
40+
QCOMPARE(roles.value(WalletListModel::NameRole), QByteArrayLiteral("name"));
41+
}
42+
43+
void WalletListModelTests::list_wallet_dir_adds_entries_and_filters_duplicates()
44+
{
45+
auto source = std::make_unique<FakeWalletListDataSource>();
46+
source->m_entries = {
47+
{"wallet-alpha", "sqlite"},
48+
{"wallet-beta", "sqlite"},
49+
{"wallet-alpha", "sqlite"},
50+
};
51+
52+
WalletListModel model(std::move(source));
53+
model.listWalletDir();
54+
55+
QCOMPARE(model.rowCount(), 2);
56+
QCOMPARE(model.data(model.index(0, 0), WalletListModel::NameRole).toString(), QStringLiteral("wallet-alpha"));
57+
QCOMPARE(model.data(model.index(1, 0), WalletListModel::NameRole).toString(), QStringLiteral("wallet-beta"));
58+
}
59+
60+
void WalletListModelTests::subsequent_list_wallet_dir_only_appends_new_wallets()
61+
{
62+
auto source = std::make_unique<FakeWalletListDataSource>();
63+
auto* source_ptr = source.get();
64+
source_ptr->m_entries = {
65+
{"wallet-alpha", "sqlite"},
66+
{"wallet-beta", "sqlite"},
67+
};
68+
69+
WalletListModel model(std::move(source));
70+
model.listWalletDir();
71+
72+
source_ptr->m_entries = {
73+
{"wallet-beta", "sqlite"},
74+
{"wallet-gamma", "sqlite"},
75+
};
76+
model.listWalletDir();
77+
78+
QCOMPARE(model.rowCount(), 3);
79+
QCOMPARE(model.data(model.index(2, 0), WalletListModel::NameRole).toString(), QStringLiteral("wallet-gamma"));
80+
}
81+
82+
void WalletListModelTests::invalid_index_returns_empty_variant()
83+
{
84+
auto source = std::make_unique<FakeWalletListDataSource>();
85+
source->m_entries = {{"wallet-alpha", "sqlite"}};
86+
87+
WalletListModel model(std::move(source));
88+
model.listWalletDir();
89+
90+
QVERIFY(!model.data(QModelIndex{}, WalletListModel::NameRole).isValid());
91+
QVERIFY(!model.data(model.index(5, 0), WalletListModel::NameRole).isValid());
92+
}
93+
94+
int RunWalletListModelTests(int argc, char* argv[])
95+
{
96+
WalletListModelTests tests;
97+
return QTest::qExec(&tests, argc, argv);
98+
}
99+
100+
#ifndef BITCOINQML_NO_TEST_MAIN
101+
QTEST_MAIN(WalletListModelTests)
102+
#endif
103+
#include "test_walletlistmodel.moc"

0 commit comments

Comments
 (0)