You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
283 lines
11 KiB
283 lines
11 KiB
#include <qt/test/wallettests.h> |
|
|
|
#include <qt/bitcoinamountfield.h> |
|
#include <qt/callback.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/kevadialog.h> |
|
#include <qt/walletmodel.h> |
|
#include <test/test_bitcoin.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 "Ok" button in message box dialog. |
|
/* Kevacoin: Disable RBF |
|
void ConfirmMessage(QString* text = nullptr) |
|
{ |
|
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()) { |
|
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(); |
|
} |
|
} |
|
delete callback; |
|
}), SLOT(call())); |
|
} |
|
|
|
//! 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); |
|
/* Kevacoin: 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(); |
|
QMetaObject::invokeMethod(&sendCoinsDialog, "on_sendButton_clicked"); |
|
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 {}; |
|
} |
|
|
|
//! Request context menu (call method that is public in qt5, but protected in qt4). |
|
/* Kevacoin: Disable RBF |
|
void RequestContextMenu(QWidget* widget) |
|
{ |
|
class Qt4Hack : public QWidget |
|
{ |
|
public: |
|
using QWidget::customContextMenuRequested; |
|
}; |
|
static_cast<Qt4Hack*>(widget)->customContextMenuRequested({}); |
|
} |
|
|
|
//! 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); |
|
RequestContextMenu(table); |
|
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: |
|
// |
|
// src/qt/test/test_bitcoin-qt -platform xcb # Linux |
|
// src/qt/test/test_bitcoin-qt -platform windows # Windows |
|
// src/qt/test/test_bitcoin-qt -platform cocoa # macOS |
|
void TestGUI() |
|
{ |
|
g_address_type = OUTPUT_TYPE_P2SH_SEGWIT; |
|
g_change_type = OUTPUT_TYPE_P2SH_SEGWIT; |
|
|
|
// 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())); |
|
} |
|
bitdb.MakeMock(); |
|
std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, "wallet_test.dat")); |
|
CWallet wallet(std::move(dbw)); |
|
bool firstRun; |
|
wallet.LoadWallet(firstRun); |
|
{ |
|
LOCK(wallet.cs_wallet); |
|
wallet.SetAddressBook(GetDestinationForKey(test.coinbaseKey.GetPubKey(), g_address_type), "", "receive"); |
|
wallet.AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey()); |
|
} |
|
{ |
|
LOCK(cs_main); |
|
WalletRescanReserver reserver(&wallet); |
|
reserver.reserve(); |
|
wallet.ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, true); |
|
} |
|
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; |
|
WalletModel walletModel(platformStyle.get(), &wallet, &optionsModel); |
|
sendCoinsDialog.setModel(&walletModel); |
|
transactionView.setModel(&walletModel); |
|
|
|
// Send two transactions, and verify they are added to transaction list. |
|
TransactionTableModel* transactionTableModel = walletModel.getTransactionTableModel(); |
|
QCOMPARE(transactionTableModel->rowCount({}), 105); |
|
uint256 txid1 = SendCoins(wallet, sendCoinsDialog, CKeyID(), 5 * COIN, false /* rbf */); |
|
uint256 txid2 = SendCoins(wallet, sendCoinsDialog, CKeyID(), 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. |
|
// Kevacoin: 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(); |
|
int unit = walletModel.getOptionsModel()->getDisplayUnit(); |
|
CAmount balance = walletModel.getBalance(); |
|
QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::separatorAlways); |
|
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); |
|
QTextEdit* rlist = receiveRequestDialog->QObject::findChild<QTextEdit*>("outUri"); |
|
QString paymentText = rlist->toPlainText(); |
|
QStringList paymentTextList = paymentText.split('\n'); |
|
QCOMPARE(paymentTextList.at(0), QString("Payment information")); |
|
QVERIFY(paymentTextList.at(1).indexOf(QString("URI: kevacoin:")) != -1); |
|
QVERIFY(paymentTextList.at(2).indexOf(QString("Address:")) != -1); |
|
QCOMPARE(paymentTextList.at(3), QString("Amount: 0.00000001 ") + QString::fromStdString(CURRENCY_UNIT)); |
|
QCOMPARE(paymentTextList.at(4), QString("Label: TEST_LABEL_1")); |
|
QCOMPARE(paymentTextList.at(5), QString("Message: 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); |
|
|
|
bitdb.Flush(true); |
|
bitdb.Reset(); |
|
} |
|
|
|
} |
|
|
|
void WalletTests::walletTests() |
|
{ |
|
TestGUI(); |
|
}
|
|
|