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