git / moonbite-core / src/qt/test/wallettests.cpp · raw

// Copyright (c) 2015-2020 The Bitcoin Core developers// Distributed under the MIT software license, see the accompanying// file COPYING or http://www.opensource.org/licenses/mit-license.php.#include <qt/test/wallettests.h>#include <qt/test/util.h>#include <interfaces/chain.h>#include <interfaces/node.h>#include <qt/bitcoinamountfield.h>#include <qt/clientmodel.h>#include <qt/optionsmodel.h>#include <qt/platformstyle.h>#include <qt/qvalidatedlineedit.h>#include <qt/sendcoinsdialog.h>#include <qt/sendcoinsentry.h>#include <qt/transactiontablemodel.h>#include <qt/transactionview.h>#include <qt/walletmodel.h>#include <key_io.h>#include <test/util/setup_common.h>#include <validation.h>#include <wallet/wallet.h>#include <qt/overviewpage.h>#include <qt/receivecoinsdialog.h>#include <qt/recentrequeststablemodel.h>#include <qt/receiverequestdialog.h>#include <memory>#include <QAbstractButton>#include <QAction>#include <QApplication>#include <QCheckBox>#include <QPushButton>#include <QTimer>#include <QVBoxLayout>#include <QTextEdit>#include <QListView>#include <QDialogButtonBox>namespace{//! Press "Yes" or "Cancel" buttons in modal send confirmation dialog.void ConfirmSend(QString* text = nullptr, bool cancel = false){    QTimer::singleShot(0, [text, cancel]() {        for (QWidget* widget : QApplication::topLevelWidgets()) {            if (widget->inherits("SendConfirmationDialog")) {                SendConfirmationDialog* dialog = qobject_cast<SendConfirmationDialog*>(widget);                if (text) *text = dialog->text();                QAbstractButton* button = dialog->button(cancel ? QMessageBox::Cancel : QMessageBox::Yes);                button->setEnabled(true);                button->click();            }        }    });}//! Send coins to address and return txid.uint256 SendCoins(CWallet& wallet, SendCoinsDialog& sendCoinsDialog, const CTxDestination& address, CAmount amount, bool rbf){    QVBoxLayout* entries = sendCoinsDialog.findChild<QVBoxLayout*>("entries");    SendCoinsEntry* entry = qobject_cast<SendCoinsEntry*>(entries->itemAt(0)->widget());    entry->findChild<QValidatedLineEdit*>("payTo")->setText(QString::fromStdString(EncodeDestination(address)));    entry->findChild<BitcoinAmountField*>("payAmount")->setValue(amount);    /* Litecon: Disabled RBF UI    sendCoinsDialog.findChild<QFrame*>("frameFee")        ->findChild<QFrame*>("frameFeeSelection")        ->findChild<QCheckBox*>("optInRBF")        ->setCheckState(rbf ? Qt::Checked : Qt::Unchecked);    */    uint256 txid;    boost::signals2::scoped_connection c(wallet.NotifyTransactionChanged.connect([&txid](CWallet*, const uint256& hash, ChangeType status) {        if (status == CT_NEW) txid = hash;    }));    ConfirmSend();    bool invoked = QMetaObject::invokeMethod(&sendCoinsDialog, "on_sendButton_clicked");    assert(invoked);    return txid;}//! Find index of txid in transaction list.QModelIndex FindTx(const QAbstractItemModel& model, const uint256& txid){    QString hash = QString::fromStdString(txid.ToString());    int rows = model.rowCount({});    for (int row = 0; row < rows; ++row) {        QModelIndex index = model.index(row, 0, {});        if (model.data(index, TransactionTableModel::TxHashRole) == hash) {            return index;        }    }    return {};}//! Invoke bumpfee on txid and check results./* Litecoin: Disable RBFvoid BumpFee(TransactionView& view, const uint256& txid, bool expectDisabled, std::string expectError, bool cancel){    QTableView* table = view.findChild<QTableView*>("transactionView");    QModelIndex index = FindTx(*table->selectionModel()->model(), txid);    QVERIFY2(index.isValid(), "Could not find BumpFee txid");    // Select row in table, invoke context menu, and make sure bumpfee action is    // enabled or disabled as expected.    QAction* action = view.findChild<QAction*>("bumpFeeAction");    table->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);    action->setEnabled(expectDisabled);    table->customContextMenuRequested({});    QCOMPARE(action->isEnabled(), !expectDisabled);    action->setEnabled(true);    QString text;    if (expectError.empty()) {        ConfirmSend(&text, cancel);    } else {        ConfirmMessage(&text);    }    action->trigger();    QVERIFY(text.indexOf(QString::fromStdString(expectError)) != -1);}*///! Simple qt wallet tests.//// Test widgets can be debugged interactively calling show() on them and// manually running the event loop, e.g.:////     sendCoinsDialog.show();//     QEventLoop().exec();//// This also requires overriding the default minimal Qt platform:////     QT_QPA_PLATFORM=xcb     src/qt/test/test_bitcoin-qt  # Linux//     QT_QPA_PLATFORM=windows src/qt/test/test_bitcoin-qt  # Windows//     QT_QPA_PLATFORM=cocoa   src/qt/test/test_bitcoin-qt  # macOSvoid TestGUI(interfaces::Node& node){    // Set up wallet and chain with 105 blocks (5 mature blocks for spending).    TestChain100Setup test;    for (int i = 0; i < 5; ++i) {        test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));    }    node.setContext(&test.m_node);    std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockWalletDatabase());    bool firstRun;    wallet->LoadWallet(firstRun);    {        auto spk_man = wallet->GetOrCreateLegacyScriptPubKeyMan();        LOCK2(wallet->cs_wallet, spk_man->cs_KeyStore);        wallet->SetAddressBook(GetDestinationForKey(test.coinbaseKey.GetPubKey(), wallet->m_default_address_type, spk_man->GetScanSecret()), "", "receive");        spk_man->AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey());        wallet->SetLastBlockProcessed(105, ::ChainActive().Tip()->GetBlockHash());    }    {        WalletRescanReserver reserver(*wallet);        reserver.reserve();        CWallet::ScanResult result = wallet->ScanForWalletTransactions(Params().GetConsensus().hashGenesisBlock, 0 /* block height */, {} /* max height */, reserver, true /* fUpdate */);        QCOMPARE(result.status, CWallet::ScanResult::SUCCESS);        QCOMPARE(result.last_scanned_block, ::ChainActive().Tip()->GetBlockHash());        QVERIFY(result.last_failed_block.IsNull());    }    wallet->SetBroadcastTransactions(true);    // Create widgets for sending coins and listing transactions.    std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other"));    SendCoinsDialog sendCoinsDialog(platformStyle.get());    TransactionView transactionView(platformStyle.get());    OptionsModel optionsModel;    ClientModel clientModel(node, &optionsModel);    AddWallet(wallet);    WalletModel walletModel(interfaces::MakeWallet(wallet), clientModel, platformStyle.get());    RemoveWallet(wallet, nullopt);    sendCoinsDialog.setModel(&walletModel);    transactionView.setModel(&walletModel);    {        // Check balance in send dialog        QLabel* balanceLabel = sendCoinsDialog.findChild<QLabel*>("labelBalance");        QString balanceText = balanceLabel->text();        int unit = walletModel.getOptionsModel()->getDisplayUnit();        CAmount balance = walletModel.wallet().getBalance();        QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::SeparatorStyle::ALWAYS);        QCOMPARE(balanceText, balanceComparison);    }    // Send two transactions, and verify they are added to transaction list.    TransactionTableModel* transactionTableModel = walletModel.getTransactionTableModel();    QCOMPARE(transactionTableModel->rowCount({}), 105);    uint256 txid1 = SendCoins(*wallet.get(), sendCoinsDialog, PKHash(), 5 * COIN, false /* rbf */);    uint256 txid2 = SendCoins(*wallet.get(), sendCoinsDialog, PKHash(), 10 * COIN, true /* rbf */);    QCOMPARE(transactionTableModel->rowCount({}), 107);    QVERIFY(FindTx(*transactionTableModel, txid1).isValid());    QVERIFY(FindTx(*transactionTableModel, txid2).isValid());    // Call bumpfee. Test disabled, canceled, enabled, then failing cases.    // Litecoin: Disable BumpFee tests    // BumpFee(transactionView, txid1, true /* expect disabled */, "not BIP 125 replaceable" /* expected error */, false /* cancel */);    // BumpFee(transactionView, txid2, false /* expect disabled */, {} /* expected error */, true /* cancel */);    // BumpFee(transactionView, txid2, false /* expect disabled */, {} /* expected error */, false /* cancel */);    // BumpFee(transactionView, txid2, true /* expect disabled */, "already bumped" /* expected error */, false /* cancel */);    // Check current balance on OverviewPage    OverviewPage overviewPage(platformStyle.get());    overviewPage.setWalletModel(&walletModel);    QLabel* balanceLabel = overviewPage.findChild<QLabel*>("labelBalance");    QString balanceText = balanceLabel->text().trimmed();    int unit = walletModel.getOptionsModel()->getDisplayUnit();    CAmount balance = walletModel.wallet().getBalance();    QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::SeparatorStyle::ALWAYS);    QCOMPARE(balanceText, balanceComparison);    // Check Request Payment button    ReceiveCoinsDialog receiveCoinsDialog(platformStyle.get());    receiveCoinsDialog.setModel(&walletModel);    RecentRequestsTableModel* requestTableModel = walletModel.getRecentRequestsTableModel();    // Label input    QLineEdit* labelInput = receiveCoinsDialog.findChild<QLineEdit*>("reqLabel");    labelInput->setText("TEST_LABEL_1");    // Amount input    BitcoinAmountField* amountInput = receiveCoinsDialog.findChild<BitcoinAmountField*>("reqAmount");    amountInput->setValue(1);    // Message input    QLineEdit* messageInput = receiveCoinsDialog.findChild<QLineEdit*>("reqMessage");    messageInput->setText("TEST_MESSAGE_1");    int initialRowCount = requestTableModel->rowCount({});    QPushButton* requestPaymentButton = receiveCoinsDialog.findChild<QPushButton*>("receiveButton");    requestPaymentButton->click();    for (QWidget* widget : QApplication::topLevelWidgets()) {        if (widget->inherits("ReceiveRequestDialog")) {            ReceiveRequestDialog* receiveRequestDialog = qobject_cast<ReceiveRequestDialog*>(widget);            QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("payment_header")->text(), QString("Payment information"));            QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("uri_tag")->text(), QString("URI:"));            QString uri = receiveRequestDialog->QObject::findChild<QLabel*>("uri_content")->text();            QCOMPARE(uri.count("moonbite:"), 2);            QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("address_tag")->text(), QString("Address:"));            QCOMPARE(uri.count("amount=0.00000001"), 2);            QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("amount_tag")->text(), QString("Amount:"));            QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("amount_content")->text(), QString("0.00000001 ") + QString::fromStdString(CURRENCY_UNIT));            QCOMPARE(uri.count("label=TEST_LABEL_1"), 2);            QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("label_tag")->text(), QString("Label:"));            QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("label_content")->text(), QString("TEST_LABEL_1"));            QCOMPARE(uri.count("message=TEST_MESSAGE_1"), 2);            QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("message_tag")->text(), QString("Message:"));            QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("message_content")->text(), QString("TEST_MESSAGE_1"));        }    }    // Clear button    QPushButton* clearButton = receiveCoinsDialog.findChild<QPushButton*>("clearButton");    clearButton->click();    QCOMPARE(labelInput->text(), QString(""));    QCOMPARE(amountInput->value(), CAmount(0));    QCOMPARE(messageInput->text(), QString(""));    // Check addition to history    int currentRowCount = requestTableModel->rowCount({});    QCOMPARE(currentRowCount, initialRowCount+1);    // Check Remove button    QTableView* table = receiveCoinsDialog.findChild<QTableView*>("recentRequestsView");    table->selectRow(currentRowCount-1);    QPushButton* removeRequestButton = receiveCoinsDialog.findChild<QPushButton*>("removeRequestButton");    removeRequestButton->click();    QCOMPARE(requestTableModel->rowCount({}), currentRowCount-1);}} // namespacevoid WalletTests::walletTests(){#ifdef Q_OS_MAC    if (QApplication::platformName() == "minimal") {        // Disable for mac on "minimal" platform to avoid crashes inside the Qt        // framework when it tries to look up unimplemented cocoa functions,        // and fails to handle returned nulls        // (https://bugreports.qt.io/browse/QTBUG-49686).        QWARN("Skipping WalletTests on mac build with 'minimal' platform set due to Qt bugs. To run AppTests, invoke "              "with 'QT_QPA_PLATFORM=cocoa test_bitcoin-qt' on mac, or else use a linux or windows build.");        return;    }#endif    TestGUI(m_node);}