Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
251// manually running the event loop, e.g.:252//253// sendCoinsDialog.show();254// QEventLoop().exec();255//256// This also requires overriding the default minimal Qt platform:257//258// QT_QPA_PLATFORM=xcb build/bin/test_bitcoin-qt # Linux259// QT_QPA_PLATFORM=windows build/bin/test_bitcoin-qt # Windows260// QT_QPA_PLATFORM=cocoa build/bin/test_bitcoin-qt # macOS261void TestGUI(interfaces::Node& node, const std::shared_ptr<CWallet>& wallet)262{263// Create widgets for sending coins and listing transactions.264std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other"));265MiniGUI mini_gui(node, platformStyle.get());266mini_gui.initModelForWallet(node, wallet, platformStyle.get());267WalletModel& walletModel = *mini_gui.walletModel;268SendCoinsDialog& sendCoinsDialog = mini_gui.sendCoinsDialog;269TransactionView& transactionView = mini_gui.transactionView;271// Update walletModel cached balance which will trigger an update for the 'labelBalance' QLabel.272walletModel.pollBalanceChanged();273// Check balance in send dialog274CompareBalance(walletModel, walletModel.wallet().getBalance(), sendCoinsDialog.findChild<QLabel*>("labelBalance"));276// Check 'UseAvailableBalance' functionality277VerifyUseAvailableBalance(sendCoinsDialog, walletModel);279// Send two transactions, and verify they are added to transaction list.280TransactionTableModel* transactionTableModel = walletModel.getTransactionTableModel();281QCOMPARE(transactionTableModel->rowCount({}), 105);282Txid txid1 = SendCoins(*wallet.get(), sendCoinsDialog, PKHash(), 5 * COIN);283Txid txid2 = SendCoins(*wallet.get(), sendCoinsDialog, PKHash(), 10 * COIN);284// Transaction table model updates on a QueuedConnection, so process events to ensure it's updated.285qApp->processEvents();286QCOMPARE(transactionTableModel->rowCount({}), 107);287QVERIFY(FindTx(*transactionTableModel, txid1).isValid());288QVERIFY(FindTx(*transactionTableModel, txid2).isValid());290// Call bumpfee. Test canceled fullrbf bump, canceled bip-125-rbf bump, passing bump, and then failing bump.291BumpFee(transactionView, txid1, /*expectDisabled=*/false, /*expectError=*/{}, /*cancel=*/true);292BumpFee(transactionView, txid2, /*expectDisabled=*/false, /*expectError=*/{}, /*cancel=*/true);293BumpFee(transactionView, txid2, /*expectDisabled=*/false, /*expectError=*/{}, /*cancel=*/false);294BumpFee(transactionView, txid2, /*expectDisabled=*/true, /*expectError=*/"already bumped", /*cancel=*/false);296// Check current balance on OverviewPage297OverviewPage overviewPage(platformStyle.get());298overviewPage.setWalletModel(&walletModel);299walletModel.pollBalanceChanged(); // Manual balance polling update300CompareBalance(walletModel, walletModel.wallet().getBalance(), overviewPage.findChild<QLabel*>("labelBalance"));302// Check Request Payment button303ReceiveCoinsDialog receiveCoinsDialog(platformStyle.get());304receiveCoinsDialog.setModel(&walletModel);305RecentRequestsTableModel* requestTableModel = walletModel.getRecentRequestsTableModel();307// Label input308QLineEdit* labelInput = receiveCoinsDialog.findChild<QLineEdit*>("reqLabel");309labelInput->setText("TEST_LABEL_1");311// Amount input312BitcoinAmountField* amountInput = receiveCoinsDialog.findChild<BitcoinAmountField*>("reqAmount");313amountInput->setValue(1);315// Message input316QLineEdit* messageInput = receiveCoinsDialog.findChild<QLineEdit*>("reqMessage");317messageInput->setText("TEST_MESSAGE_1");318int initialRowCount = requestTableModel->rowCount({});319QPushButton* requestPaymentButton = receiveCoinsDialog.findChild<QPushButton*>("receiveButton");320requestPaymentButton->click();321QString address;322for (QWidget* widget : QApplication::topLevelWidgets()) {323if (widget->inherits("ReceiveRequestDialog")) {324ReceiveRequestDialog* receiveRequestDialog = qobject_cast<ReceiveRequestDialog*>(widget);325QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("payment_header")->text(), QString("Payment information"));326QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("uri_tag")->text(), QString("URI:"));327QString uri = receiveRequestDialog->QObject::findChild<QLabel*>("uri_content")->text();328QCOMPARE(uri.count("bitcoin:"), 2);329QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("address_tag")->text(), QString("Address:"));330QVERIFY(address.isEmpty());331address = receiveRequestDialog->QObject::findChild<QLabel*>("address_content")->text();332QVERIFY(!address.isEmpty());334QCOMPARE(uri.count("amount=0.00000001"), 2);335QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("amount_tag")->text(), QString("Amount:"));336QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("amount_content")->text(), QString::fromStdString("0.00000001 " + CURRENCY_UNIT));338QCOMPARE(uri.count("label=TEST_LABEL_1"), 2);339QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("label_tag")->text(), QString("Label:"));340QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("label_content")->text(), QString("TEST_LABEL_1"));342QCOMPARE(uri.count("message=TEST_MESSAGE_1"), 2);343QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("message_tag")->text(), QString("Message:"));344QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("message_content")->text(), QString("TEST_MESSAGE_1"));345}346}348// Clear button349QPushButton* clearButton = receiveCoinsDialog.findChild<QPushButton*>("clearButton");350clearButton->click();351QCOMPARE(labelInput->text(), QString(""));352QCOMPARE(amountInput->value(), CAmount(0));353QCOMPARE(messageInput->text(), QString(""));355// Check addition to history356int currentRowCount = requestTableModel->rowCount({});357QCOMPARE(currentRowCount, initialRowCount+1);359// Check addition to wallet360std::vector<std::string> requests = walletModel.wallet().getAddressReceiveRequests();361QCOMPARE(requests.size(), size_t{1});362RecentRequestEntry entry;363SpanReader{MakeByteSpan(requests[0])} >> entry;364QCOMPARE(entry.nVersion, int{1});365QCOMPARE(entry.id, int64_t{1});366QVERIFY(entry.date.isValid());367QCOMPARE(entry.recipient.address, address);368QCOMPARE(entry.recipient.label, QString{"TEST_LABEL_1"});369QCOMPARE(entry.recipient.amount, CAmount{1});370QCOMPARE(entry.recipient.message, QString{"TEST_MESSAGE_1"});371QCOMPARE(entry.recipient.sPaymentRequest, std::string{});372QCOMPARE(entry.recipient.authenticatedMerchant, QString{});374// Check Remove button375QTableView* table = receiveCoinsDialog.findChild<QTableView*>("recentRequestsView");376table->selectRow(currentRowCount-1);377QPushButton* removeRequestButton = receiveCoinsDialog.findChild<QPushButton*>("removeRequestButton");378removeRequestButton->click();379QCOMPARE(requestTableModel->rowCount({}), currentRowCount-1);381// Check removal from wallet382QCOMPARE(walletModel.wallet().getAddressReceiveRequests().size(), size_t{0});383}385void TestGUIWatchOnly(interfaces::Node& node, TestChain100Setup& test)386{387const std::shared_ptr<CWallet>& wallet = SetupDescriptorsWallet(node, test, /*watch_only=*/true);389// Create widgets and init models390std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other"));391MiniGUI mini_gui(node, platformStyle.get());392mini_gui.initModelForWallet(node, wallet, platformStyle.get());393WalletModel& walletModel = *mini_gui.walletModel;394SendCoinsDialog& sendCoinsDialog = mini_gui.sendCoinsDialog;396// Update walletModel cached balance which will trigger an update for the 'labelBalance' QLabel.397walletModel.pollBalanceChanged();398// Check balance in send dialog399CompareBalance(walletModel, walletModel.wallet().getBalances().balance,400sendCoinsDialog.findChild<QLabel*>("labelBalance"));402// Set change address403sendCoinsDialog.getCoinControl()->destChange = PKHash{test.coinbaseKey.GetPubKey()};405// Time to reject "save" PSBT dialog ('SendCoins' locks the main thread until the dialog receives the event).406QTimer timer;407timer.setInterval(500);408QObject::connect(&timer, &QTimer::timeout, [&](){409for (QWidget* widget : QApplication::topLevelWidgets()) {410if (widget->inherits("QMessageBox") && widget->objectName().compare("psbt_copied_message") == 0) {411QMessageBox* dialog = qobject_cast<QMessageBox*>(widget);412QAbstractButton* button = dialog->button(QMessageBox::Discard);413button->setEnabled(true);414button->click();415timer.stop();416break;417}418}419});420timer.start(500);422// Send tx and verify PSBT copied to the clipboard.423SendCoins(*wallet.get(), sendCoinsDialog, PKHash(), 5 * COIN, QMessageBox::Save);424const std::string& psbt_string = QApplication::clipboard()->text().toStdString();425QVERIFY(!psbt_string.empty());427// Decode psbt428std::optional<std::vector<unsigned char>> decoded_psbt = DecodeBase64(psbt_string);429QVERIFY(decoded_psbt);430util::Result<PartiallySignedTransaction> psbt = DecodeRawPSBT(MakeByteSpan(*decoded_psbt));431QVERIFY(psbt);432}434void TestGUI(interfaces::Node& node)435{436// Set up wallet and chain with 105 blocks (5 mature blocks for spending).437TestChain100Setup test;438for (int i = 0; i < 5; ++i) {439test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));440}441auto wallet_loader = interfaces::MakeWalletLoader(*test.m_node.chain, *Assert(test.m_node.args));442test.m_node.wallet_loader = wallet_loader.get();443node.setContext(&test.m_node);445// "Full" GUI tests, use descriptor wallet446const std::shared_ptr<CWallet>& desc_wallet = SetupDescriptorsWallet(node, test);447TestGUI(node, desc_wallet);449// Legacy watch-only wallet test450// Verify PSBT creation.451TestGUIWatchOnly(node, test);452}454} // namespace456void WalletTests::walletTests()457{458TestGUI(m_node);459}
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.