addressbooktests.cpp raw
1 // Copyright (c) 2017-2022 The Limenka 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 <qt/test/addressbooktests.h>
6 #include <qt/test/util.h>
7 #include <test/util/setup_common.h>
8
9 #include <interfaces/chain.h>
10 #include <interfaces/node.h>
11 #include <qt/addressbookpage.h>
12 #include <qt/clientmodel.h>
13 #include <qt/editaddressdialog.h>
14 #include <qt/optionsmodel.h>
15 #include <qt/platformstyle.h>
16 #include <qt/qvalidatedlineedit.h>
17 #include <qt/walletmodel.h>
18
19 #include <key.h>
20 #include <key_io.h>
21 #include <wallet/wallet.h>
22 #include <wallet/test/util.h>
23 #include <walletinitinterface.h>
24
25 #include <chrono>
26
27 #include <QApplication>
28 #include <QLineEdit>
29 #include <QMessageBox>
30 #include <QTableView>
31 #include <QTimer>
32
33 using wallet::AddWallet;
34 using wallet::CWallet;
35 using wallet::CreateMockableWalletDatabase;
36 using wallet::RemoveWallet;
37 using wallet::WALLET_FLAG_DESCRIPTORS;
38 using wallet::WalletContext;
39
40 namespace
41 {
42
43 /**
44 * Fill the edit address dialog box with data, submit it, and ensure that
45 * the resulting message meets expectations.
46 */
47 void EditAddressAndSubmit(
48 EditAddressDialog* dialog,
49 const QString& label, const QString& address, QString expected_msg)
50 {
51 QString warning_text;
52
53 dialog->findChild<QLineEdit*>("labelEdit")->setText(label);
54 dialog->findChild<QValidatedLineEdit*>("addressEdit")->setText(address);
55
56 ConfirmMessage(&warning_text, 5ms);
57 dialog->accept();
58 QCOMPARE(warning_text, expected_msg);
59 }
60
61 /**
62 * Test adding various send addresses to the address book.
63 *
64 * There are three cases tested:
65 *
66 * - new_address: a new address which should add as a send address successfully.
67 * - existing_s_address: an existing sending address which won't add successfully.
68 * - existing_r_address: an existing receiving address which won't add successfully.
69 *
70 * In each case, verify the resulting state of the address book and optionally
71 * the warning message presented to the user.
72 */
73 void TestAddAddressesToSendBook(interfaces::Node& node)
74 {
75 TestChain100Setup test;
76 auto wallet_loader = interfaces::MakeWalletLoader(*test.m_node.chain, *Assert(test.m_node.args));
77 test.m_node.wallet_loader = wallet_loader.get();
78 node.setContext(&test.m_node);
79 const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockableWalletDatabase());
80 wallet->LoadWallet();
81 wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
82 {
83 LOCK(wallet->cs_wallet);
84 wallet->SetupDescriptorScriptPubKeyMans();
85 }
86
87 auto build_address = [&wallet]() {
88 CKey key = GenerateRandomKey();
89 CTxDestination dest(GetDestinationForKey(
90 key.GetPubKey(), wallet->m_default_address_type));
91
92 return std::make_pair(dest, QString::fromStdString(EncodeDestination(dest)));
93 };
94
95 CTxDestination r_key_dest, s_key_dest;
96
97 // Add a preexisting "receive" entry in the address book.
98 QString preexisting_r_address;
99 QString r_label("already here (r)");
100
101 // Add a preexisting "send" entry in the address book.
102 QString preexisting_s_address;
103 QString s_label("already here (s)");
104
105 // Define a new address (which should add to the address book successfully).
106 QString new_address_a;
107 QString new_address_b;
108
109 std::tie(r_key_dest, preexisting_r_address) = build_address();
110 std::tie(s_key_dest, preexisting_s_address) = build_address();
111 std::tie(std::ignore, new_address_a) = build_address();
112 std::tie(std::ignore, new_address_b) = build_address();
113
114 {
115 LOCK(wallet->cs_wallet);
116 wallet->SetAddressBook(r_key_dest, r_label.toStdString(), wallet::AddressPurpose::RECEIVE);
117 wallet->SetAddressBook(s_key_dest, s_label.toStdString(), wallet::AddressPurpose::SEND);
118 }
119
120 auto check_addbook_size = [&wallet](int expected_size) {
121 LOCK(wallet->cs_wallet);
122 QCOMPARE(static_cast<int>(wallet->m_address_book.size()), expected_size);
123 };
124
125 // We should start with the two addresses we added earlier and nothing else.
126 check_addbook_size(2);
127
128 // Initialize relevant QT models.
129 std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other"));
130 OptionsModel optionsModel(node);
131 bilingual_str error;
132 QVERIFY(optionsModel.Init(error));
133 ClientModel clientModel(node, &optionsModel, *platformStyle);
134 WalletContext& context = *node.walletLoader().context();
135 AddWallet(context, wallet);
136 WalletModel walletModel(interfaces::MakeWallet(context, wallet), clientModel, platformStyle.get());
137 RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt);
138 EditAddressDialog editAddressDialog(EditAddressDialog::NewSendingAddress);
139 editAddressDialog.setModel(walletModel.getAddressTableModel());
140
141 AddressBookPage address_book{platformStyle.get(), AddressBookPage::ForEditing, AddressBookPage::SendingTab};
142 address_book.setModel(walletModel.getAddressTableModel());
143 auto table_view = address_book.findChild<QTableView*>("tableView");
144 QCOMPARE(table_view->model()->rowCount(), 1);
145
146 EditAddressAndSubmit(
147 &editAddressDialog, QString("uhoh"), preexisting_r_address,
148 QString(
149 "Address \"%1\" already exists as a receiving address with label "
150 "\"%2\" and so cannot be added as a sending address."
151 ).arg(preexisting_r_address).arg(r_label));
152 check_addbook_size(2);
153 QCOMPARE(table_view->model()->rowCount(), 1);
154
155 EditAddressAndSubmit(
156 &editAddressDialog, QString("uhoh, different"), preexisting_s_address,
157 QString(
158 "The entered address \"%1\" is already in the address book with "
159 "label \"%2\"."
160 ).arg(preexisting_s_address).arg(s_label));
161 check_addbook_size(2);
162 QCOMPARE(table_view->model()->rowCount(), 1);
163
164 // Submit a new address which should add successfully - we expect the
165 // warning message to be blank.
166 EditAddressAndSubmit(
167 &editAddressDialog, QString("io - new A"), new_address_a, QString(""));
168 check_addbook_size(3);
169 QCOMPARE(table_view->model()->rowCount(), 2);
170
171 EditAddressAndSubmit(
172 &editAddressDialog, QString("io - new B"), new_address_b, QString(""));
173 check_addbook_size(4);
174 QCOMPARE(table_view->model()->rowCount(), 3);
175
176 auto search_line = address_book.findChild<QLineEdit*>("searchLineEdit");
177
178 search_line->setText(r_label);
179 QCOMPARE(table_view->model()->rowCount(), 0);
180
181 search_line->setText(s_label);
182 QCOMPARE(table_view->model()->rowCount(), 1);
183
184 search_line->setText("io");
185 QCOMPARE(table_view->model()->rowCount(), 2);
186
187 // Check wildcard "?".
188 search_line->setText("io?new");
189 QCOMPARE(table_view->model()->rowCount(), 0);
190 search_line->setText("io???new");
191 QCOMPARE(table_view->model()->rowCount(), 2);
192
193 // Check wildcard "*".
194 search_line->setText("io*new");
195 QCOMPARE(table_view->model()->rowCount(), 2);
196 search_line->setText("*");
197 QCOMPARE(table_view->model()->rowCount(), 3);
198
199 search_line->setText(preexisting_r_address);
200 QCOMPARE(table_view->model()->rowCount(), 0);
201
202 search_line->setText(preexisting_s_address);
203 QCOMPARE(table_view->model()->rowCount(), 1);
204
205 search_line->setText(new_address_a);
206 QCOMPARE(table_view->model()->rowCount(), 1);
207
208 search_line->setText(new_address_b);
209 QCOMPARE(table_view->model()->rowCount(), 1);
210
211 search_line->setText("");
212 QCOMPARE(table_view->model()->rowCount(), 3);
213 }
214
215 } // namespace
216
217 void AddressBookTests::addressBookTests()
218 {
219 #ifdef Q_OS_MACOS
220 if (QApplication::platformName() == "minimal") {
221 // Disable for mac on "minimal" platform to avoid crashes inside the Qt
222 // framework when it tries to look up unimplemented cocoa functions,
223 // and fails to handle returned nulls
224 // (https://bugreports.qt.io/browse/QTBUG-49686).
225 qWarning() << "Skipping AddressBookTests on mac build with 'minimal' platform set due to Qt bugs. To run AppTests, invoke "
226 "with 'QT_QPA_PLATFORM=cocoa test_limenka-qt' on mac, or else use a linux or windows build.";
227 return;
228 }
229 #endif
230 TestAddAddressesToSendBook(m_node);
231 }
232