Browse Source

Merge #10420: Add Qt tests for wallet spends & bumpfee

5749a4882 Add Qt tests for wallet spends & bumpfee (Russell Yanofsky)

Tree-SHA512: 026785e7b5ab662f37029d0694916757e46e68bf10e1a7bf1e8538a36593ada0768c6cf3c810c66d65fad891c137fc8bb13904ed09ab3bcffd6cf43d09e48621
0.15
Jonas Schnelli 8 years ago
parent
commit
4314544d46
No known key found for this signature in database
GPG Key ID: 1EB776BB03C7922D
  1. 80
      src/qt/test/wallettests.cpp
  2. 7
      src/qt/transactionview.cpp

80
src/qt/test/wallettests.cpp

@ -8,26 +8,46 @@
#include "qt/sendcoinsdialog.h" #include "qt/sendcoinsdialog.h"
#include "qt/sendcoinsentry.h" #include "qt/sendcoinsentry.h"
#include "qt/transactiontablemodel.h" #include "qt/transactiontablemodel.h"
#include "qt/transactionview.h"
#include "qt/walletmodel.h" #include "qt/walletmodel.h"
#include "test/test_bitcoin.h" #include "test/test_bitcoin.h"
#include "validation.h" #include "validation.h"
#include "wallet/wallet.h" #include "wallet/wallet.h"
#include <QAbstractButton> #include <QAbstractButton>
#include <QAction>
#include <QApplication> #include <QApplication>
#include <QCheckBox>
#include <QPushButton>
#include <QTimer> #include <QTimer>
#include <QVBoxLayout> #include <QVBoxLayout>
namespace namespace
{ {
//! Press "Yes" button in modal send confirmation dialog. //! Press "Ok" button in message box dialog.
void ConfirmSend() void ConfirmMessage(QString* text = nullptr)
{ {
QTimer::singleShot(0, makeCallback([](Callback* callback) { QTimer::singleShot(0, makeCallback([text](Callback* callback) {
for (QWidget* widget : QApplication::topLevelWidgets()) {
if (widget->inherits("QMessageBox")) {
QMessageBox* messageBox = qobject_cast<QMessageBox*>(widget);
if (text) *text = messageBox->text();
messageBox->defaultButton()->click();
}
}
delete callback;
}), SLOT(call()));
}
//! Press "Yes" or "Cancel" buttons in modal send confirmation dialog.
void ConfirmSend(QString* text = nullptr, bool cancel = false)
{
QTimer::singleShot(0, makeCallback([text, cancel](Callback* callback) {
for (QWidget* widget : QApplication::topLevelWidgets()) { for (QWidget* widget : QApplication::topLevelWidgets()) {
if (widget->inherits("SendConfirmationDialog")) { if (widget->inherits("SendConfirmationDialog")) {
SendConfirmationDialog* dialog = qobject_cast<SendConfirmationDialog*>(widget); SendConfirmationDialog* dialog = qobject_cast<SendConfirmationDialog*>(widget);
QAbstractButton* button = dialog->button(QMessageBox::Yes); if (text) *text = dialog->text();
QAbstractButton* button = dialog->button(cancel ? QMessageBox::Cancel : QMessageBox::Yes);
button->setEnabled(true); button->setEnabled(true);
button->click(); button->click();
} }
@ -37,12 +57,16 @@ void ConfirmSend()
} }
//! Send coins to address and return txid. //! Send coins to address and return txid.
uint256 SendCoins(CWallet& wallet, SendCoinsDialog& sendCoinsDialog, const CBitcoinAddress& address, CAmount amount) uint256 SendCoins(CWallet& wallet, SendCoinsDialog& sendCoinsDialog, const CBitcoinAddress& address, CAmount amount, bool rbf)
{ {
QVBoxLayout* entries = sendCoinsDialog.findChild<QVBoxLayout*>("entries"); QVBoxLayout* entries = sendCoinsDialog.findChild<QVBoxLayout*>("entries");
SendCoinsEntry* entry = qobject_cast<SendCoinsEntry*>(entries->itemAt(0)->widget()); SendCoinsEntry* entry = qobject_cast<SendCoinsEntry*>(entries->itemAt(0)->widget());
entry->findChild<QValidatedLineEdit*>("payTo")->setText(QString::fromStdString(address.ToString())); entry->findChild<QValidatedLineEdit*>("payTo")->setText(QString::fromStdString(address.ToString()));
entry->findChild<BitcoinAmountField*>("payAmount")->setValue(amount); entry->findChild<BitcoinAmountField*>("payAmount")->setValue(amount);
sendCoinsDialog.findChild<QFrame*>("frameFee")
->findChild<QFrame*>("frameFeeSelection")
->findChild<QCheckBox*>("optInRBF")
->setCheckState(rbf ? Qt::Checked : Qt::Unchecked);
uint256 txid; uint256 txid;
boost::signals2::scoped_connection c(wallet.NotifyTransactionChanged.connect([&txid](CWallet*, const uint256& hash, ChangeType status) { boost::signals2::scoped_connection c(wallet.NotifyTransactionChanged.connect([&txid](CWallet*, const uint256& hash, ChangeType status) {
if (status == CT_NEW) txid = hash; if (status == CT_NEW) txid = hash;
@ -66,6 +90,32 @@ QModelIndex FindTx(const QAbstractItemModel& model, const uint256& txid)
return {}; return {};
} }
//! Invoke bumpfee on txid and check results.
void 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. //! Simple qt wallet tests.
// //
// Test widgets can be debugged interactively calling show() on them and // Test widgets can be debugged interactively calling show() on them and
@ -81,9 +131,11 @@ QModelIndex FindTx(const QAbstractItemModel& model, const uint256& txid)
// src/qt/test/test_bitcoin-qt -platform cocoa # macOS // src/qt/test/test_bitcoin-qt -platform cocoa # macOS
void TestSendCoins() void TestSendCoins()
{ {
// Set up wallet and chain with 101 blocks (1 mature block for spending). // Set up wallet and chain with 105 blocks (5 mature blocks for spending).
TestChain100Setup test; TestChain100Setup test;
for (int i = 0; i < 5; ++i) {
test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey())); test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));
}
bitdb.MakeMock(); bitdb.MakeMock();
std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, "wallet_test.dat")); std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, "wallet_test.dat"));
CWallet wallet(std::move(dbw)); CWallet wallet(std::move(dbw));
@ -100,19 +152,27 @@ void TestSendCoins()
// Create widgets for sending coins and listing transactions. // Create widgets for sending coins and listing transactions.
std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other")); std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other"));
SendCoinsDialog sendCoinsDialog(platformStyle.get()); SendCoinsDialog sendCoinsDialog(platformStyle.get());
TransactionView transactionView(platformStyle.get());
OptionsModel optionsModel; OptionsModel optionsModel;
WalletModel walletModel(platformStyle.get(), &wallet, &optionsModel); WalletModel walletModel(platformStyle.get(), &wallet, &optionsModel);
sendCoinsDialog.setModel(&walletModel); sendCoinsDialog.setModel(&walletModel);
transactionView.setModel(&walletModel);
// Send two transactions, and verify they are added to transaction list. // Send two transactions, and verify they are added to transaction list.
TransactionTableModel* transactionTableModel = walletModel.getTransactionTableModel(); TransactionTableModel* transactionTableModel = walletModel.getTransactionTableModel();
QCOMPARE(transactionTableModel->rowCount({}), 101); QCOMPARE(transactionTableModel->rowCount({}), 105);
uint256 txid1 = SendCoins(wallet, sendCoinsDialog, CBitcoinAddress(CKeyID()), 5 * COIN); uint256 txid1 = SendCoins(wallet, sendCoinsDialog, CBitcoinAddress(CKeyID()), 5 * COIN, false /* rbf */);
uint256 txid2 = SendCoins(wallet, sendCoinsDialog, CBitcoinAddress(CKeyID()), 10 * COIN); uint256 txid2 = SendCoins(wallet, sendCoinsDialog, CBitcoinAddress(CKeyID()), 10 * COIN, true /* rbf */);
QCOMPARE(transactionTableModel->rowCount({}), 103); QCOMPARE(transactionTableModel->rowCount({}), 107);
QVERIFY(FindTx(*transactionTableModel, txid1).isValid()); QVERIFY(FindTx(*transactionTableModel, txid1).isValid());
QVERIFY(FindTx(*transactionTableModel, txid2).isValid()); QVERIFY(FindTx(*transactionTableModel, txid2).isValid());
// Call bumpfee. Test disabled, canceled, enabled, then failing cases.
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, false /* expect disabled */, "already bumped" /* expected error */, false /* cancel */);
bitdb.Flush(true); bitdb.Flush(true);
bitdb.Reset(); bitdb.Reset();
} }

7
src/qt/transactionview.cpp

@ -136,10 +136,12 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
view->installEventFilter(this); view->installEventFilter(this);
transactionView = view; transactionView = view;
transactionView->setObjectName("transactionView");
// Actions // Actions
abandonAction = new QAction(tr("Abandon transaction"), this); abandonAction = new QAction(tr("Abandon transaction"), this);
bumpFeeAction = new QAction(tr("Increase transaction fee"), this); bumpFeeAction = new QAction(tr("Increase transaction fee"), this);
bumpFeeAction->setObjectName("bumpFeeAction");
QAction *copyAddressAction = new QAction(tr("Copy address"), this); QAction *copyAddressAction = new QAction(tr("Copy address"), this);
QAction *copyLabelAction = new QAction(tr("Copy label"), this); QAction *copyLabelAction = new QAction(tr("Copy label"), this);
QAction *copyAmountAction = new QAction(tr("Copy amount"), this); QAction *copyAmountAction = new QAction(tr("Copy amount"), this);
@ -150,6 +152,7 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
QAction *showDetailsAction = new QAction(tr("Show transaction details"), this); QAction *showDetailsAction = new QAction(tr("Show transaction details"), this);
contextMenu = new QMenu(this); contextMenu = new QMenu(this);
contextMenu->setObjectName("contextMenu");
contextMenu->addAction(copyAddressAction); contextMenu->addAction(copyAddressAction);
contextMenu->addAction(copyLabelAction); contextMenu->addAction(copyLabelAction);
contextMenu->addAction(copyAmountAction); contextMenu->addAction(copyAmountAction);
@ -380,7 +383,7 @@ void TransactionView::contextualMenu(const QPoint &point)
if(index.isValid()) if(index.isValid())
{ {
contextMenu->exec(QCursor::pos()); contextMenu->popup(transactionView->viewport()->mapToGlobal(point));
} }
} }
@ -416,7 +419,7 @@ void TransactionView::bumpFee()
// Bump tx fee over the walletModel // Bump tx fee over the walletModel
if (model->bumpFee(hash)) { if (model->bumpFee(hash)) {
// Update the table // Update the table
model->getTransactionTableModel()->updateTransaction(hashQStr, CT_UPDATED, false); model->getTransactionTableModel()->updateTransaction(hashQStr, CT_UPDATED, true);
} }
} }

Loading…
Cancel
Save