addressbooktests.cpp
1 // Copyright (c) 2017-present 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 <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->SetWalletFlag(WALLET_FLAG_DESCRIPTORS); 81 { 82 LOCK(wallet->cs_wallet); 83 wallet->SetupDescriptorScriptPubKeyMans(); 84 } 85 86 auto build_address{[]() { 87 const WitnessV0KeyHash dest{GenerateRandomKey().GetPubKey()}; 88 return std::make_pair(dest, QString::fromStdString(EncodeDestination(dest))); 89 }}; 90 91 CTxDestination r_key_dest, s_key_dest; 92 93 // Add a preexisting "receive" entry in the address book. 94 QString preexisting_r_address; 95 QString r_label("already here (r)"); 96 97 // Add a preexisting "send" entry in the address book. 98 QString preexisting_s_address; 99 QString s_label("already here (s)"); 100 101 // Define a new address (which should add to the address book successfully). 102 QString new_address_a; 103 QString new_address_b; 104 105 std::tie(r_key_dest, preexisting_r_address) = build_address(); 106 std::tie(s_key_dest, preexisting_s_address) = build_address(); 107 std::tie(std::ignore, new_address_a) = build_address(); 108 std::tie(std::ignore, new_address_b) = build_address(); 109 110 { 111 LOCK(wallet->cs_wallet); 112 wallet->SetAddressBook(r_key_dest, r_label.toStdString(), wallet::AddressPurpose::RECEIVE); 113 wallet->SetAddressBook(s_key_dest, s_label.toStdString(), wallet::AddressPurpose::SEND); 114 } 115 116 auto check_addbook_size = [&wallet](int expected_size) { 117 LOCK(wallet->cs_wallet); 118 QCOMPARE(static_cast<int>(wallet->m_address_book.size()), expected_size); 119 }; 120 121 // We should start with the two addresses we added earlier and nothing else. 122 check_addbook_size(2); 123 124 // Initialize relevant QT models. 125 std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other")); 126 OptionsModel optionsModel(node); 127 bilingual_str error; 128 QVERIFY(optionsModel.Init(error)); 129 ClientModel clientModel(node, &optionsModel); 130 WalletContext& context = *node.walletLoader().context(); 131 AddWallet(context, wallet); 132 WalletModel walletModel(interfaces::MakeWallet(context, wallet), clientModel, platformStyle.get()); 133 RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt); 134 EditAddressDialog editAddressDialog(EditAddressDialog::NewSendingAddress); 135 editAddressDialog.setModel(walletModel.getAddressTableModel()); 136 137 AddressBookPage address_book{platformStyle.get(), AddressBookPage::ForEditing, AddressBookPage::SendingTab}; 138 address_book.setModel(walletModel.getAddressTableModel()); 139 auto table_view = address_book.findChild<QTableView*>("tableView"); 140 QCOMPARE(table_view->model()->rowCount(), 1); 141 142 EditAddressAndSubmit( 143 &editAddressDialog, QString("uhoh"), preexisting_r_address, 144 QString( 145 "Address \"%1\" already exists as a receiving address with label " 146 "\"%2\" and so cannot be added as a sending address." 147 ).arg(preexisting_r_address).arg(r_label)); 148 check_addbook_size(2); 149 QCOMPARE(table_view->model()->rowCount(), 1); 150 151 EditAddressAndSubmit( 152 &editAddressDialog, QString("uhoh, different"), preexisting_s_address, 153 QString( 154 "The entered address \"%1\" is already in the address book with " 155 "label \"%2\"." 156 ).arg(preexisting_s_address).arg(s_label)); 157 check_addbook_size(2); 158 QCOMPARE(table_view->model()->rowCount(), 1); 159 160 // Submit a new address which should add successfully - we expect the 161 // warning message to be blank. 162 EditAddressAndSubmit( 163 &editAddressDialog, QString("io - new A"), new_address_a, QString("")); 164 check_addbook_size(3); 165 QCOMPARE(table_view->model()->rowCount(), 2); 166 167 EditAddressAndSubmit( 168 &editAddressDialog, QString("io - new B"), new_address_b, QString("")); 169 check_addbook_size(4); 170 QCOMPARE(table_view->model()->rowCount(), 3); 171 172 auto search_line = address_book.findChild<QLineEdit*>("searchLineEdit"); 173 174 search_line->setText(r_label); 175 QCOMPARE(table_view->model()->rowCount(), 0); 176 177 search_line->setText(s_label); 178 QCOMPARE(table_view->model()->rowCount(), 1); 179 180 search_line->setText("io"); 181 QCOMPARE(table_view->model()->rowCount(), 2); 182 183 // Check wildcard "?". 184 search_line->setText("io?new"); 185 QCOMPARE(table_view->model()->rowCount(), 0); 186 search_line->setText("io???new"); 187 QCOMPARE(table_view->model()->rowCount(), 2); 188 189 // Check wildcard "*". 190 search_line->setText("io*new"); 191 QCOMPARE(table_view->model()->rowCount(), 2); 192 search_line->setText("*"); 193 QCOMPARE(table_view->model()->rowCount(), 3); 194 195 search_line->setText(preexisting_r_address); 196 QCOMPARE(table_view->model()->rowCount(), 0); 197 198 search_line->setText(preexisting_s_address); 199 QCOMPARE(table_view->model()->rowCount(), 1); 200 201 search_line->setText(new_address_a); 202 QCOMPARE(table_view->model()->rowCount(), 1); 203 204 search_line->setText(new_address_b); 205 QCOMPARE(table_view->model()->rowCount(), 1); 206 207 search_line->setText(""); 208 QCOMPARE(table_view->model()->rowCount(), 3); 209 } 210 211 } // namespace 212 213 void AddressBookTests::addressBookTests() 214 { 215 #ifdef Q_OS_MACOS 216 if (QApplication::platformName() == "minimal") { 217 // Disable for mac on "minimal" platform to avoid crashes inside the Qt 218 // framework when it tries to look up unimplemented cocoa functions, 219 // and fails to handle returned nulls 220 // (https://bugreports.qt.io/browse/QTBUG-49686). 221 qWarning() << "Skipping AddressBookTests on mac build with 'minimal' platform set due to Qt bugs. To run AppTests, invoke " 222 "with 'QT_QPA_PLATFORM=cocoa test_bitcoin-qt' on mac, or else use a linux or windows build."; 223 return; 224 } 225 #endif 226 TestAddAddressesToSendBook(m_node); 227 }