Skip to content

Commit 4314544

Browse files
committed
Merge #10420: Add Qt tests for wallet spends & bumpfee
5749a48 Add Qt tests for wallet spends & bumpfee (Russell Yanofsky) Tree-SHA512: 026785e7b5ab662f37029d0694916757e46e68bf10e1a7bf1e8538a36593ada0768c6cf3c810c66d65fad891c137fc8bb13904ed09ab3bcffd6cf43d09e48621
2 parents 4cb8757 + 5749a48 commit 4314544

File tree

2 files changed

+76
-13
lines changed

2 files changed

+76
-13
lines changed

src/qt/test/wallettests.cpp

Lines changed: 71 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,46 @@
88
#include "qt/sendcoinsdialog.h"
99
#include "qt/sendcoinsentry.h"
1010
#include "qt/transactiontablemodel.h"
11+
#include "qt/transactionview.h"
1112
#include "qt/walletmodel.h"
1213
#include "test/test_bitcoin.h"
1314
#include "validation.h"
1415
#include "wallet/wallet.h"
1516

1617
#include <QAbstractButton>
18+
#include <QAction>
1719
#include <QApplication>
20+
#include <QCheckBox>
21+
#include <QPushButton>
1822
#include <QTimer>
1923
#include <QVBoxLayout>
2024

2125
namespace
2226
{
23-
//! Press "Yes" button in modal send confirmation dialog.
24-
void ConfirmSend()
27+
//! Press "Ok" button in message box dialog.
28+
void ConfirmMessage(QString* text = nullptr)
2529
{
26-
QTimer::singleShot(0, makeCallback([](Callback* callback) {
30+
QTimer::singleShot(0, makeCallback([text](Callback* callback) {
31+
for (QWidget* widget : QApplication::topLevelWidgets()) {
32+
if (widget->inherits("QMessageBox")) {
33+
QMessageBox* messageBox = qobject_cast<QMessageBox*>(widget);
34+
if (text) *text = messageBox->text();
35+
messageBox->defaultButton()->click();
36+
}
37+
}
38+
delete callback;
39+
}), SLOT(call()));
40+
}
41+
42+
//! Press "Yes" or "Cancel" buttons in modal send confirmation dialog.
43+
void ConfirmSend(QString* text = nullptr, bool cancel = false)
44+
{
45+
QTimer::singleShot(0, makeCallback([text, cancel](Callback* callback) {
2746
for (QWidget* widget : QApplication::topLevelWidgets()) {
2847
if (widget->inherits("SendConfirmationDialog")) {
2948
SendConfirmationDialog* dialog = qobject_cast<SendConfirmationDialog*>(widget);
30-
QAbstractButton* button = dialog->button(QMessageBox::Yes);
49+
if (text) *text = dialog->text();
50+
QAbstractButton* button = dialog->button(cancel ? QMessageBox::Cancel : QMessageBox::Yes);
3151
button->setEnabled(true);
3252
button->click();
3353
}
@@ -37,12 +57,16 @@ void ConfirmSend()
3757
}
3858

3959
//! Send coins to address and return txid.
40-
uint256 SendCoins(CWallet& wallet, SendCoinsDialog& sendCoinsDialog, const CBitcoinAddress& address, CAmount amount)
60+
uint256 SendCoins(CWallet& wallet, SendCoinsDialog& sendCoinsDialog, const CBitcoinAddress& address, CAmount amount, bool rbf)
4161
{
4262
QVBoxLayout* entries = sendCoinsDialog.findChild<QVBoxLayout*>("entries");
4363
SendCoinsEntry* entry = qobject_cast<SendCoinsEntry*>(entries->itemAt(0)->widget());
4464
entry->findChild<QValidatedLineEdit*>("payTo")->setText(QString::fromStdString(address.ToString()));
4565
entry->findChild<BitcoinAmountField*>("payAmount")->setValue(amount);
66+
sendCoinsDialog.findChild<QFrame*>("frameFee")
67+
->findChild<QFrame*>("frameFeeSelection")
68+
->findChild<QCheckBox*>("optInRBF")
69+
->setCheckState(rbf ? Qt::Checked : Qt::Unchecked);
4670
uint256 txid;
4771
boost::signals2::scoped_connection c(wallet.NotifyTransactionChanged.connect([&txid](CWallet*, const uint256& hash, ChangeType status) {
4872
if (status == CT_NEW) txid = hash;
@@ -66,6 +90,32 @@ QModelIndex FindTx(const QAbstractItemModel& model, const uint256& txid)
6690
return {};
6791
}
6892

93+
//! Invoke bumpfee on txid and check results.
94+
void BumpFee(TransactionView& view, const uint256& txid, bool expectDisabled, std::string expectError, bool cancel)
95+
{
96+
QTableView* table = view.findChild<QTableView*>("transactionView");
97+
QModelIndex index = FindTx(*table->selectionModel()->model(), txid);
98+
QVERIFY2(index.isValid(), "Could not find BumpFee txid");
99+
100+
// Select row in table, invoke context menu, and make sure bumpfee action is
101+
// enabled or disabled as expected.
102+
QAction* action = view.findChild<QAction*>("bumpFeeAction");
103+
table->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
104+
action->setEnabled(expectDisabled);
105+
table->customContextMenuRequested({});
106+
QCOMPARE(action->isEnabled(), !expectDisabled);
107+
108+
action->setEnabled(true);
109+
QString text;
110+
if (expectError.empty()) {
111+
ConfirmSend(&text, cancel);
112+
} else {
113+
ConfirmMessage(&text);
114+
}
115+
action->trigger();
116+
QVERIFY(text.indexOf(QString::fromStdString(expectError)) != -1);
117+
}
118+
69119
//! Simple qt wallet tests.
70120
//
71121
// Test widgets can be debugged interactively calling show() on them and
@@ -81,9 +131,11 @@ QModelIndex FindTx(const QAbstractItemModel& model, const uint256& txid)
81131
// src/qt/test/test_bitcoin-qt -platform cocoa # macOS
82132
void TestSendCoins()
83133
{
84-
// Set up wallet and chain with 101 blocks (1 mature block for spending).
134+
// Set up wallet and chain with 105 blocks (5 mature blocks for spending).
85135
TestChain100Setup test;
86-
test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));
136+
for (int i = 0; i < 5; ++i) {
137+
test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));
138+
}
87139
bitdb.MakeMock();
88140
std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, "wallet_test.dat"));
89141
CWallet wallet(std::move(dbw));
@@ -100,19 +152,27 @@ void TestSendCoins()
100152
// Create widgets for sending coins and listing transactions.
101153
std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other"));
102154
SendCoinsDialog sendCoinsDialog(platformStyle.get());
155+
TransactionView transactionView(platformStyle.get());
103156
OptionsModel optionsModel;
104157
WalletModel walletModel(platformStyle.get(), &wallet, &optionsModel);
105158
sendCoinsDialog.setModel(&walletModel);
159+
transactionView.setModel(&walletModel);
106160

107161
// Send two transactions, and verify they are added to transaction list.
108162
TransactionTableModel* transactionTableModel = walletModel.getTransactionTableModel();
109-
QCOMPARE(transactionTableModel->rowCount({}), 101);
110-
uint256 txid1 = SendCoins(wallet, sendCoinsDialog, CBitcoinAddress(CKeyID()), 5 * COIN);
111-
uint256 txid2 = SendCoins(wallet, sendCoinsDialog, CBitcoinAddress(CKeyID()), 10 * COIN);
112-
QCOMPARE(transactionTableModel->rowCount({}), 103);
163+
QCOMPARE(transactionTableModel->rowCount({}), 105);
164+
uint256 txid1 = SendCoins(wallet, sendCoinsDialog, CBitcoinAddress(CKeyID()), 5 * COIN, false /* rbf */);
165+
uint256 txid2 = SendCoins(wallet, sendCoinsDialog, CBitcoinAddress(CKeyID()), 10 * COIN, true /* rbf */);
166+
QCOMPARE(transactionTableModel->rowCount({}), 107);
113167
QVERIFY(FindTx(*transactionTableModel, txid1).isValid());
114168
QVERIFY(FindTx(*transactionTableModel, txid2).isValid());
115169

170+
// Call bumpfee. Test disabled, canceled, enabled, then failing cases.
171+
BumpFee(transactionView, txid1, true /* expect disabled */, "not BIP 125 replaceable" /* expected error */, false /* cancel */);
172+
BumpFee(transactionView, txid2, false /* expect disabled */, {} /* expected error */, true /* cancel */);
173+
BumpFee(transactionView, txid2, false /* expect disabled */, {} /* expected error */, false /* cancel */);
174+
BumpFee(transactionView, txid2, false /* expect disabled */, "already bumped" /* expected error */, false /* cancel */);
175+
116176
bitdb.Flush(true);
117177
bitdb.Reset();
118178
}

src/qt/transactionview.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,10 +136,12 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
136136
view->installEventFilter(this);
137137

138138
transactionView = view;
139+
transactionView->setObjectName("transactionView");
139140

140141
// Actions
141142
abandonAction = new QAction(tr("Abandon transaction"), this);
142143
bumpFeeAction = new QAction(tr("Increase transaction fee"), this);
144+
bumpFeeAction->setObjectName("bumpFeeAction");
143145
QAction *copyAddressAction = new QAction(tr("Copy address"), this);
144146
QAction *copyLabelAction = new QAction(tr("Copy label"), this);
145147
QAction *copyAmountAction = new QAction(tr("Copy amount"), this);
@@ -150,6 +152,7 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
150152
QAction *showDetailsAction = new QAction(tr("Show transaction details"), this);
151153

152154
contextMenu = new QMenu(this);
155+
contextMenu->setObjectName("contextMenu");
153156
contextMenu->addAction(copyAddressAction);
154157
contextMenu->addAction(copyLabelAction);
155158
contextMenu->addAction(copyAmountAction);
@@ -380,7 +383,7 @@ void TransactionView::contextualMenu(const QPoint &point)
380383

381384
if(index.isValid())
382385
{
383-
contextMenu->exec(QCursor::pos());
386+
contextMenu->popup(transactionView->viewport()->mapToGlobal(point));
384387
}
385388
}
386389

@@ -416,7 +419,7 @@ void TransactionView::bumpFee()
416419
// Bump tx fee over the walletModel
417420
if (model->bumpFee(hash)) {
418421
// Update the table
419-
model->getTransactionTableModel()->updateTransaction(hashQStr, CT_UPDATED, false);
422+
model->getTransactionTableModel()->updateTransaction(hashQStr, CT_UPDATED, true);
420423
}
421424
}
422425

0 commit comments

Comments
 (0)