From 9e58f8cd372406ca671d54d00b10a5c78b89b02e Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Mon, 27 Jan 2025 03:32:36 +0000 Subject: [PATCH 01/16] merge bitcoin-core/gui#408: Add missing mnemonics in menu bar options --- src/qt/bitcoingui.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 587822dc23841..25434c693a17f 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -410,7 +410,7 @@ void BitcoinGUI::createActions() verifyMessageAction->setStatusTip(tr("Verify messages to ensure they were signed with specified Dash addresses")); m_load_psbt_action = new QAction(tr("&Load PSBT from file…"), this); m_load_psbt_action->setStatusTip(tr("Load Partially Signed Blockchain Transaction")); - m_load_psbt_clipboard_action = new QAction(tr("Load PSBT from clipboard…"), this); + m_load_psbt_clipboard_action = new QAction(tr("Load PSBT from &clipboard…"), this); m_load_psbt_clipboard_action->setStatusTip(tr("Load Partially Signed Blockchain Transaction from clipboard")); openInfoAction = new QAction(tr("&Information"), this); @@ -423,7 +423,7 @@ void BitcoinGUI::createActions() openPeersAction->setStatusTip(tr("Show peers info")); openRepairAction = new QAction(tr("Wallet &Repair"), this); openRepairAction->setStatusTip(tr("Show wallet repair options")); - openConfEditorAction = new QAction(tr("Open Wallet &Configuration File"), this); + openConfEditorAction = new QAction(tr("Open &wallet configuration file"), this); openConfEditorAction->setStatusTip(tr("Open configuration file")); // override TextHeuristicRole set by default which confuses this action with application settings openConfEditorAction->setMenuRole(QAction::NoRole); @@ -610,7 +610,7 @@ void BitcoinGUI::createMenuBar() QMenu* window_menu = appMenuBar->addMenu(tr("&Window")); - QAction* minimize_action = window_menu->addAction(tr("Minimize")); + QAction* minimize_action = window_menu->addAction(tr("&Minimize")); minimize_action->setShortcut(QKeySequence(tr("Ctrl+M"))); connect(minimize_action, &QAction::triggered, [] { QApplication::activeWindow()->showMinimized(); From 3f9dca5b26474ca5d04ab84f4fc21630d8705e6b Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sat, 7 Aug 2021 17:01:16 +0300 Subject: [PATCH 02/16] merge bitcoin-core/gui#398: Pass WalletModel object to the WalletView constructor --- src/qt/bitcoingui.cpp | 4 +- src/qt/walletframe.cpp | 9 ++--- src/qt/walletframe.h | 2 +- src/qt/walletview.cpp | 92 +++++++++++++++++------------------------- src/qt/walletview.h | 16 ++++---- 5 files changed, 51 insertions(+), 72 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 25434c693a17f..c133de32eca5d 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -915,8 +915,8 @@ void BitcoinGUI::addWallet(WalletModel* walletModel) { if (!walletFrame) return; - WalletView* wallet_view = new WalletView(walletFrame); - if (!walletFrame->addWallet(walletModel, wallet_view)) return; + WalletView* wallet_view = new WalletView(walletModel, walletFrame); + if (!walletFrame->addView(wallet_view)) return; rpcConsole->addWallet(walletModel); if (m_wallet_selector->count() == 0) { diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp index f3781d1621c00..b52152b4c2202 100644 --- a/src/qt/walletframe.cpp +++ b/src/qt/walletframe.cpp @@ -78,14 +78,13 @@ void WalletFrame::setClientModel(ClientModel *_clientModel) } } -bool WalletFrame::addWallet(WalletModel* walletModel, WalletView* walletView) +bool WalletFrame::addView(WalletView* walletView) { - if (!clientModel || !walletModel) return false; + if (!clientModel) return false; - if (mapWalletViews.count(walletModel) > 0) return false; + if (mapWalletViews.count(walletView->getWalletModel()) > 0) return false; walletView->setClientModel(clientModel); - walletView->setWalletModel(walletModel); walletView->showOutOfSyncWarning(bOutOfSync); WalletView* current_wallet_view = currentWalletView(); @@ -96,7 +95,7 @@ bool WalletFrame::addWallet(WalletModel* walletModel, WalletView* walletView) } walletStack->addWidget(walletView); - mapWalletViews[walletModel] = walletView; + mapWalletViews[walletView->getWalletModel()] = walletView; return true; } diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h index 948af49ad6d82..7be1e3d467e8a 100644 --- a/src/qt/walletframe.h +++ b/src/qt/walletframe.h @@ -37,7 +37,7 @@ class WalletFrame : public QFrame void setClientModel(ClientModel *clientModel); - bool addWallet(WalletModel* walletModel, WalletView* walletView); + bool addView(WalletView* walletView); void setCurrentWallet(WalletModel* wallet_model); void removeWallet(WalletModel* wallet_model); void removeAllWallets(); diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index 390df73576c18..899f0bc39c5af 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -35,18 +35,23 @@ #include #include -WalletView::WalletView(QWidget* parent) : - QStackedWidget(parent), - clientModel(nullptr), - walletModel(nullptr) +WalletView::WalletView(WalletModel* wallet_model, QWidget* parent) + : QStackedWidget(parent), + clientModel(nullptr), + walletModel(wallet_model) { + assert(walletModel); + // Create tabs overviewPage = new OverviewPage(); + overviewPage->setWalletModel(walletModel); transactionsPage = new QWidget(this); QVBoxLayout *vbox = new QVBoxLayout(); QHBoxLayout *hbox_buttons = new QHBoxLayout(); transactionView = new TransactionView(this); + transactionView->setModel(walletModel); + vbox->addWidget(transactionView); QPushButton *exportButton = new QPushButton(tr("&Export"), this); exportButton->setToolTip(tr("Export the data in the current tab to a file")); @@ -75,11 +80,19 @@ WalletView::WalletView(QWidget* parent) : transactionsPage->setLayout(vbox); receiveCoinsPage = new ReceiveCoinsDialog(); + receiveCoinsPage->setModel(walletModel); + sendCoinsPage = new SendCoinsDialog(); + sendCoinsPage->setModel(walletModel); + coinJoinCoinsPage = new SendCoinsDialog(true); + coinJoinCoinsPage->setModel(walletModel); usedSendingAddressesPage = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::SendingTab, this); + usedSendingAddressesPage->setModel(walletModel->getAddressTableModel()); + usedReceivingAddressesPage = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::ReceivingTab, this); + usedReceivingAddressesPage->setModel(walletModel->getAddressTableModel()); addWidget(overviewPage); addWidget(transactionsPage); @@ -90,6 +103,7 @@ WalletView::WalletView(QWidget* parent) : QSettings settings; if (settings.value("fShowMasternodesTab").toBool()) { masternodeListPage = new MasternodeList(); + masternodeListPage->setWalletModel(walletModel); addWidget(masternodeListPage); } if (settings.value("fShowGovernanceTab").toBool()) { @@ -123,6 +137,21 @@ WalletView::WalletView(QWidget* parent) : connect(this, &WalletView::setPrivacy, overviewPage, &OverviewPage::setPrivacy); + // Receive and pass through messages from wallet model + connect(walletModel, &WalletModel::message, this, &WalletView::message); + + // Handle changes in encryption status + connect(walletModel, &WalletModel::encryptionStatusChanged, this, &WalletView::encryptionStatusChanged); + + // Balloon pop-up for new transaction + connect(walletModel->getTransactionTableModel(), &TransactionTableModel::rowsInserted, this, &WalletView::processNewTransaction); + + // Ask for passphrase if needed + connect(walletModel, &WalletModel::requireUnlock, this, &WalletView::unlockWallet); + + // Show progress dialog + connect(walletModel, &WalletModel::showProgress, this, &WalletView::showProgress); + GUIUtil::disableMacFocusRect(this); } @@ -150,50 +179,15 @@ void WalletView::setClientModel(ClientModel *_clientModel) if (settings.value("fShowGovernanceTab").toBool() && governanceListPage != nullptr) { governanceListPage->setClientModel(_clientModel); } - if (walletModel) walletModel->setClientModel(_clientModel); -} - -void WalletView::setWalletModel(WalletModel *_walletModel) -{ - this->walletModel = _walletModel; - - // Put transaction list in tabs - transactionView->setModel(_walletModel); - overviewPage->setWalletModel(_walletModel); - QSettings settings; - if (settings.value("fShowMasternodesTab").toBool()) { - masternodeListPage->setWalletModel(_walletModel); - } - receiveCoinsPage->setModel(_walletModel); - sendCoinsPage->setModel(_walletModel); - coinJoinCoinsPage->setModel(_walletModel); - usedReceivingAddressesPage->setModel(_walletModel ? _walletModel->getAddressTableModel() : nullptr); - usedSendingAddressesPage->setModel(_walletModel ? _walletModel->getAddressTableModel() : nullptr); - - if (_walletModel) - { - // Receive and pass through messages from wallet model - connect(_walletModel, &WalletModel::message, this, &WalletView::message); - - // Handle changes in encryption status - connect(_walletModel, &WalletModel::encryptionStatusChanged, this, &WalletView::encryptionStatusChanged); - - // Balloon pop-up for new transaction - connect(_walletModel->getTransactionTableModel(), &TransactionTableModel::rowsInserted, this, &WalletView::processNewTransaction); - - // Ask for passphrase if needed - connect(_walletModel, &WalletModel::requireUnlock, this, &WalletView::unlockWallet); - - // Show progress dialog - connect(_walletModel, &WalletModel::showProgress, this, &WalletView::showProgress); - } + walletModel->setClientModel(_clientModel); } void WalletView::processNewTransaction(const QModelIndex& parent, int start, int /*end*/) { // Prevent balloon-spam when initial block download is in progress - if (!walletModel || !clientModel || clientModel->node().isInitialBlockDownload()) + if (!clientModel || clientModel->node().isInitialBlockDownload()) { return; + } TransactionTableModel *ttm = walletModel->getTransactionTableModel(); if (!ttm || ttm->processingQueuedTransactions()) @@ -302,8 +296,6 @@ void WalletView::showOutOfSyncWarning(bool fShow) void WalletView::encryptWallet() { - if(!walletModel) - return; AskPassphraseDialog dlg(AskPassphraseDialog::Encrypt, this); dlg.setModel(walletModel); dlg.exec(); @@ -340,10 +332,7 @@ void WalletView::changePassphrase() void WalletView::unlockWallet(bool fForMixingOnly) { - if(!walletModel) - return; // Unlock wallet when requested by wallet model - if (walletModel->getEncryptionStatus() == WalletModel::Locked || walletModel->getEncryptionStatus() == WalletModel::UnlockedForMixingOnly) { AskPassphraseDialog dlg(fForMixingOnly ? AskPassphraseDialog::UnlockMixing : AskPassphraseDialog::Unlock, this); @@ -354,25 +343,16 @@ void WalletView::unlockWallet(bool fForMixingOnly) void WalletView::lockWallet() { - if(!walletModel) - return; - walletModel->setWalletLocked(true); } void WalletView::usedSendingAddresses() { - if(!walletModel) - return; - GUIUtil::bringToFront(usedSendingAddressesPage); } void WalletView::usedReceivingAddresses() { - if(!walletModel) - return; - GUIUtil::bringToFront(usedReceivingAddressesPage); } diff --git a/src/qt/walletview.h b/src/qt/walletview.h index fa281690a7472..6c0f232b57595 100644 --- a/src/qt/walletview.h +++ b/src/qt/walletview.h @@ -37,19 +37,14 @@ class WalletView : public QStackedWidget Q_OBJECT public: - explicit WalletView(QWidget* parent); + explicit WalletView(WalletModel* wallet_model, QWidget* parent); ~WalletView(); /** Set the client model. The client model represents the part of the core that communicates with the P2P network, and is wallet-agnostic. */ void setClientModel(ClientModel *clientModel); - WalletModel *getWalletModel() { return walletModel; } - /** Set the wallet model. - The wallet model represents a bitcoin wallet, and offers access to the list of transactions, address book and sending - functionality. - */ - void setWalletModel(WalletModel *walletModel); + WalletModel* getWalletModel() const noexcept { return walletModel; } bool handlePaymentRequest(const SendCoinsRecipient& recipient); @@ -57,7 +52,12 @@ class WalletView : public QStackedWidget private: ClientModel *clientModel; - WalletModel *walletModel; + + //! + //! The wallet model represents a bitcoin wallet, and offers access to + //! the list of transactions, address book and sending functionality. + //! + WalletModel* const walletModel; OverviewPage *overviewPage; QWidget *transactionsPage; From 33da874fc5ae5683b70cfc9b63bf6a40de23d6c8 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sun, 26 Jan 2025 12:57:23 +0000 Subject: [PATCH 03/16] merge bitcoin-core/gui#434: Keep InitExecutor in main gui thread --- src/qt/initexecutor.cpp | 73 +++++++++++++++++++++------------------ src/qt/initexecutor.h | 1 + src/qt/test/test_main.cpp | 1 - 3 files changed, 41 insertions(+), 34 deletions(-) diff --git a/src/qt/initexecutor.cpp b/src/qt/initexecutor.cpp index 87d9ecda5b987..4db90d041ebc2 100644 --- a/src/qt/initexecutor.cpp +++ b/src/qt/initexecutor.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -20,7 +21,7 @@ InitExecutor::InitExecutor(interfaces::Node& node) : QObject(), m_node(node) { - this->moveToThread(&m_thread); + m_context.moveToThread(&m_thread); m_thread.start(); } @@ -40,46 +41,52 @@ void InitExecutor::handleRunawayException(const std::exception_ptr e) void InitExecutor::initialize() { - try { - util::ThreadRename("qt-init"); - qDebug() << __func__ << ": Running initialization in thread"; - interfaces::BlockAndHeaderTipInfo tip_info; - bool rv = m_node.appInitMain(&tip_info); - Q_EMIT initializeResult(rv, tip_info); - } catch (...) { - handleRunawayException(std::current_exception()); - } + GUIUtil::ObjectInvoke(&m_context, [this] { + try { + util::ThreadRename("qt-init"); + qDebug() << "Running initialization in thread"; + interfaces::BlockAndHeaderTipInfo tip_info; + bool rv = m_node.appInitMain(&tip_info); + Q_EMIT initializeResult(rv, tip_info); + } catch (...) { + handleRunawayException(std::current_exception()); + } + }); } void InitExecutor::restart(QStringList args) { - static bool executing_restart{false}; + GUIUtil::ObjectInvoke(&m_context, [this, args] { + static bool executing_restart{false}; - if(!executing_restart) { // Only restart 1x, no matter how often a user clicks on a restart-button - executing_restart = true; - try { - qDebug() << __func__ << ": Running Restart in thread"; - m_node.appPrepareShutdown(); - qDebug() << __func__ << ": Shutdown finished"; - Q_EMIT shutdownResult(); - CExplicitNetCleanup::callCleanup(); - QProcess::startDetached(QApplication::applicationFilePath(), args); - qDebug() << __func__ << ": Restart initiated..."; - QApplication::quit(); - } catch (...) { - handleRunawayException(std::current_exception()); + if (!executing_restart) { // Only restart 1x, no matter how often a user clicks on a restart-button + executing_restart = true; + try { + qDebug() << "Running Restart in thread"; + m_node.appPrepareShutdown(); + qDebug() << "Shutdown finished"; + Q_EMIT shutdownResult(); + CExplicitNetCleanup::callCleanup(); + QProcess::startDetached(QApplication::applicationFilePath(), args); + qDebug() << "Restart initiated..."; + QApplication::quit(); + } catch (...) { + handleRunawayException(std::current_exception()); + } } - } + }); } void InitExecutor::shutdown() { - try { - qDebug() << __func__ << ": Running Shutdown in thread"; - m_node.appShutdown(); - qDebug() << __func__ << ": Shutdown finished"; - Q_EMIT shutdownResult(); - } catch (...) { - handleRunawayException(std::current_exception()); - } + GUIUtil::ObjectInvoke(&m_context, [this] { + try { + qDebug() << "Running Shutdown in thread"; + m_node.appShutdown(); + qDebug() << "Shutdown finished"; + Q_EMIT shutdownResult(); + } catch (...) { + handleRunawayException(std::current_exception()); + } + }); } diff --git a/src/qt/initexecutor.h b/src/qt/initexecutor.h index f4f11fca8b127..378b42a10434c 100644 --- a/src/qt/initexecutor.h +++ b/src/qt/initexecutor.h @@ -41,6 +41,7 @@ public Q_SLOTS: void handleRunawayException(const std::exception_ptr e); interfaces::Node& m_node; + QObject m_context; QThread m_thread; }; diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp index 465bc62060fa8..4bf49ba8a03eb 100644 --- a/src/qt/test/test_main.cpp +++ b/src/qt/test/test_main.cpp @@ -9,7 +9,6 @@ #include #include -#include #include #include #include From 3db335f5398491e288f26ee56d85ff0b01b872d7 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Wed, 29 Sep 2021 21:57:07 +0300 Subject: [PATCH 04/16] merge bitcoin-core/gui#439: Do not show unused widgets at startup --- src/qt/bitcoingui.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index c133de32eca5d..62f011f1b9a02 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -175,7 +175,9 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const NetworkStyle* networkStyle, frameBlocksLayout->addWidget(unitDisplayControl); frameBlocksLayout->addStretch(); frameBlocksLayout->addWidget(labelWalletHDStatusIcon); + labelWalletHDStatusIcon->hide(); frameBlocksLayout->addWidget(labelWalletEncryptionIcon); + labelWalletEncryptionIcon->hide(); } frameBlocksLayout->addWidget(labelProxyIcon); frameBlocksLayout->addStretch(); From c02483ce49dc2e40ea81b0a79b9b698bac57be7e Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Mon, 27 Jan 2025 04:26:14 +0000 Subject: [PATCH 05/16] merge bitcoin-core/gui#319: Paste button in Open URI dialog --- src/qt/forms/openuridialog.ui | 20 +++++++++++++++++++- src/qt/openuridialog.cpp | 23 +++++++++++++++++------ src/qt/openuridialog.h | 5 +++-- src/qt/res/css/general.css | 7 +++++++ 4 files changed, 46 insertions(+), 9 deletions(-) diff --git a/src/qt/forms/openuridialog.ui b/src/qt/forms/openuridialog.ui index 5defbdd4e7101..8a1789dec4765 100644 --- a/src/qt/forms/openuridialog.ui +++ b/src/qt/forms/openuridialog.ui @@ -30,6 +30,22 @@ + + + + Paste address from clipboard + + + + + + + 22 + 22 + + + + @@ -64,7 +80,9 @@
qt/qvalidatedlineedit.h
- + + + buttonBox diff --git a/src/qt/openuridialog.cpp b/src/qt/openuridialog.cpp index a7881f8a6c92b..6ede8902ca30c 100644 --- a/src/qt/openuridialog.cpp +++ b/src/qt/openuridialog.cpp @@ -9,16 +9,19 @@ #include #include +#include +#include #include -OpenURIDialog::OpenURIDialog(QWidget *parent) : - QDialog(parent, GUIUtil::dialog_flags), - ui(new Ui::OpenURIDialog) +OpenURIDialog::OpenURIDialog(QWidget* parent) : QDialog(parent, GUIUtil::dialog_flags), + ui(new Ui::OpenURIDialog) { ui->setupUi(this); + GUIUtil::setIcon(ui->pasteButton, "editpaste"); + QObject::connect(ui->pasteButton, &QAbstractButton::clicked, ui->uriEdit, &QLineEdit::paste); + GUIUtil::updateFonts(); GUIUtil::disableMacFocusRect(this); - GUIUtil::handleCloseWindowShortcut(this); } @@ -35,11 +38,19 @@ QString OpenURIDialog::getURI() void OpenURIDialog::accept() { SendCoinsRecipient rcp; - if(GUIUtil::parseBitcoinURI(getURI(), &rcp)) - { + if (GUIUtil::parseBitcoinURI(getURI(), &rcp)) { /* Only accept value URIs */ QDialog::accept(); } else { ui->uriEdit->setValid(false); } } + +void OpenURIDialog::changeEvent(QEvent* e) +{ + QDialog::changeEvent(e); + if (e->type() == QEvent::StyleChange) { + // Adjust button icon colors on theme changes + GUIUtil::setIcon(ui->pasteButton, "editpaste"); + } +} diff --git a/src/qt/openuridialog.h b/src/qt/openuridialog.h index efe4b86f3721d..14654c30e9d4d 100644 --- a/src/qt/openuridialog.h +++ b/src/qt/openuridialog.h @@ -16,16 +16,17 @@ class OpenURIDialog : public QDialog Q_OBJECT public: - explicit OpenURIDialog(QWidget *parent); + explicit OpenURIDialog(QWidget* parent); ~OpenURIDialog(); QString getURI(); protected Q_SLOTS: void accept() override; + void changeEvent(QEvent* e) override; private: - Ui::OpenURIDialog *ui; + Ui::OpenURIDialog* ui; }; #endif // BITCOIN_QT_OPENURIDIALOG_H diff --git a/src/qt/res/css/general.css b/src/qt/res/css/general.css index af65262129052..b0eea68c0d61c 100644 --- a/src/qt/res/css/general.css +++ b/src/qt/res/css/general.css @@ -1259,6 +1259,13 @@ QDialog#OpenURIDialog { QDialog#OpenURIDialog QLabel#label { /* URI Label */ } +QDialog#OpenURIDialog .QToolButton { /* General Settings for Pay To Icons */ + background-color: #00000000; + margin-left: 5px; + margin-right: 5px; + border: 0; +} + /****************************************************** OptionsDialog ******************************************************/ From d451246ac2aa5dead724d633a70bd63d45d26f4b Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sun, 26 Jan 2025 19:17:00 +0000 Subject: [PATCH 06/16] merge bitcoin-core/gui#404: Fix various edge case bugs in QValidatedLineEdit --- src/qt/qvalidatedlineedit.cpp | 9 ++++++++- src/qt/qvalidatedlineedit.h | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/qt/qvalidatedlineedit.cpp b/src/qt/qvalidatedlineedit.cpp index d19a88653c390..e66e8a5d328d5 100644 --- a/src/qt/qvalidatedlineedit.cpp +++ b/src/qt/qvalidatedlineedit.cpp @@ -14,6 +14,12 @@ QValidatedLineEdit::QValidatedLineEdit(QWidget *parent) : connect(this, &QValidatedLineEdit::textChanged, this, &QValidatedLineEdit::markValid); } +void QValidatedLineEdit::setText(const QString& text) +{ + QLineEdit::setText(text); + checkValidity(); +} + void QValidatedLineEdit::setValid(bool _valid) { if(_valid == this->valid) @@ -27,7 +33,7 @@ void QValidatedLineEdit::setValid(bool _valid) } else { - setStyleSheet(GUIUtil::getThemedStyleQString(GUIUtil::ThemedStyle::TS_INVALID)); + setStyleSheet("QValidatedLineEdit { " + GUIUtil::getThemedStyleQString(GUIUtil::ThemedStyle::TS_INVALID) + " }"); } this->valid = _valid; } @@ -105,6 +111,7 @@ void QValidatedLineEdit::checkValidity() void QValidatedLineEdit::setCheckValidator(const QValidator *v) { checkValidator = v; + checkValidity(); } bool QValidatedLineEdit::isValid() diff --git a/src/qt/qvalidatedlineedit.h b/src/qt/qvalidatedlineedit.h index b32305f5e193f..12d35aa2645c8 100644 --- a/src/qt/qvalidatedlineedit.h +++ b/src/qt/qvalidatedlineedit.h @@ -29,6 +29,7 @@ class QValidatedLineEdit : public QLineEdit const QValidator *checkValidator; public Q_SLOTS: + void setText(const QString&); void setValid(bool valid); void setEnabled(bool enabled); From 817a95a66519dba192f227a0508f4e15d5b22ed4 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sun, 26 Jan 2025 19:18:02 +0000 Subject: [PATCH 07/16] merge bitcoin#24041: Restore GetIntArg saturating behavior --- src/test/fuzz/string.cpp | 10 ++---- src/test/getarg_tests.cpp | 6 ++++ src/test/util_tests.cpp | 55 +++++++++++++++++++++-------- src/util/strencodings.h | 19 ++++++++-- test/lint/lint-locale-dependence.py | 1 + 5 files changed, 66 insertions(+), 25 deletions(-) diff --git a/src/test/fuzz/string.cpp b/src/test/fuzz/string.cpp index d4b30767dab9b..80d6625e67003 100644 --- a/src/test/fuzz/string.cpp +++ b/src/test/fuzz/string.cpp @@ -280,20 +280,14 @@ FUZZ_TARGET(string) } { - const int atoi_result = atoi(random_string_1.c_str()); const int locale_independent_atoi_result = LocaleIndependentAtoi(random_string_1); const int64_t atoi64_result = atoi64_legacy(random_string_1); - const bool out_of_range = atoi64_result < std::numeric_limits::min() || atoi64_result > std::numeric_limits::max(); - if (out_of_range) { - assert(locale_independent_atoi_result == 0); - } else { - assert(atoi_result == locale_independent_atoi_result); - } + assert(locale_independent_atoi_result == std::clamp(atoi64_result, std::numeric_limits::min(), std::numeric_limits::max())); } { const int64_t atoi64_result = atoi64_legacy(random_string_1); const int64_t locale_independent_atoi_result = LocaleIndependentAtoi(random_string_1); - assert(atoi64_result == locale_independent_atoi_result || locale_independent_atoi_result == 0); + assert(atoi64_result == locale_independent_atoi_result); } } diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp index a2c3e1876a81b..d5e80669de6fb 100644 --- a/src/test/getarg_tests.cpp +++ b/src/test/getarg_tests.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -144,6 +145,11 @@ BOOST_AUTO_TEST_CASE(intarg) BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", 11), 0); BOOST_CHECK_EQUAL(m_local_args.GetArg("-bar", 11), 0); + // Check under-/overflow behavior. + ResetArgs("-foo=-9223372036854775809 -bar=9223372036854775808"); + BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", 0), std::numeric_limits::min()); + BOOST_CHECK_EQUAL(m_local_args.GetArg("-bar", 0), std::numeric_limits::max()); + ResetArgs("-foo=11 -bar=12"); BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", 0), 11); BOOST_CHECK_EQUAL(m_local_args.GetArg("-bar", 11), 12); diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 699cd04fbe078..107361b50ded6 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include #include @@ -1737,6 +1739,11 @@ BOOST_AUTO_TEST_CASE(test_ToIntegral) BOOST_CHECK(!ToIntegral("256")); } +int64_t atoi64_legacy(const std::string& str) +{ + return strtoll(str.c_str(), nullptr, 10); +} + BOOST_AUTO_TEST_CASE(test_LocaleIndependentAtoi) { BOOST_CHECK_EQUAL(LocaleIndependentAtoi("1234"), 1'234); @@ -1764,48 +1771,68 @@ BOOST_AUTO_TEST_CASE(test_LocaleIndependentAtoi) BOOST_CHECK_EQUAL(LocaleIndependentAtoi(""), 0); BOOST_CHECK_EQUAL(LocaleIndependentAtoi("aap"), 0); BOOST_CHECK_EQUAL(LocaleIndependentAtoi("0x1"), 0); - BOOST_CHECK_EQUAL(LocaleIndependentAtoi("-32482348723847471234"), 0); - BOOST_CHECK_EQUAL(LocaleIndependentAtoi("32482348723847471234"), 0); + BOOST_CHECK_EQUAL(LocaleIndependentAtoi("-32482348723847471234"), -2'147'483'647 - 1); + BOOST_CHECK_EQUAL(LocaleIndependentAtoi("32482348723847471234"), 2'147'483'647); - BOOST_CHECK_EQUAL(LocaleIndependentAtoi("-9223372036854775809"), 0); + BOOST_CHECK_EQUAL(LocaleIndependentAtoi("-9223372036854775809"), -9'223'372'036'854'775'807LL - 1LL); BOOST_CHECK_EQUAL(LocaleIndependentAtoi("-9223372036854775808"), -9'223'372'036'854'775'807LL - 1LL); BOOST_CHECK_EQUAL(LocaleIndependentAtoi("9223372036854775807"), 9'223'372'036'854'775'807); - BOOST_CHECK_EQUAL(LocaleIndependentAtoi("9223372036854775808"), 0); + BOOST_CHECK_EQUAL(LocaleIndependentAtoi("9223372036854775808"), 9'223'372'036'854'775'807); + + std::map atoi64_test_pairs = { + {"-9223372036854775809", std::numeric_limits::min()}, + {"-9223372036854775808", -9'223'372'036'854'775'807LL - 1LL}, + {"9223372036854775807", 9'223'372'036'854'775'807}, + {"9223372036854775808", std::numeric_limits::max()}, + {"+-", 0}, + {"0x1", 0}, + {"ox1", 0}, + {"", 0}, + }; + + for (const auto& pair : atoi64_test_pairs) { + BOOST_CHECK_EQUAL(LocaleIndependentAtoi(pair.first), pair.second); + } + + // Ensure legacy compatibility with previous versions of Bitcoin Core's atoi64 + for (const auto& pair : atoi64_test_pairs) { + BOOST_CHECK_EQUAL(LocaleIndependentAtoi(pair.first), atoi64_legacy(pair.first)); + } BOOST_CHECK_EQUAL(LocaleIndependentAtoi("-1"), 0U); BOOST_CHECK_EQUAL(LocaleIndependentAtoi("0"), 0U); BOOST_CHECK_EQUAL(LocaleIndependentAtoi("18446744073709551615"), 18'446'744'073'709'551'615ULL); - BOOST_CHECK_EQUAL(LocaleIndependentAtoi("18446744073709551616"), 0U); + BOOST_CHECK_EQUAL(LocaleIndependentAtoi("18446744073709551616"), 18'446'744'073'709'551'615ULL); - BOOST_CHECK_EQUAL(LocaleIndependentAtoi("-2147483649"), 0); + BOOST_CHECK_EQUAL(LocaleIndependentAtoi("-2147483649"), -2'147'483'648LL); BOOST_CHECK_EQUAL(LocaleIndependentAtoi("-2147483648"), -2'147'483'648LL); BOOST_CHECK_EQUAL(LocaleIndependentAtoi("2147483647"), 2'147'483'647); - BOOST_CHECK_EQUAL(LocaleIndependentAtoi("2147483648"), 0); + BOOST_CHECK_EQUAL(LocaleIndependentAtoi("2147483648"), 2'147'483'647); BOOST_CHECK_EQUAL(LocaleIndependentAtoi("-1"), 0U); BOOST_CHECK_EQUAL(LocaleIndependentAtoi("0"), 0U); BOOST_CHECK_EQUAL(LocaleIndependentAtoi("4294967295"), 4'294'967'295U); - BOOST_CHECK_EQUAL(LocaleIndependentAtoi("4294967296"), 0U); + BOOST_CHECK_EQUAL(LocaleIndependentAtoi("4294967296"), 4'294'967'295U); - BOOST_CHECK_EQUAL(LocaleIndependentAtoi("-32769"), 0); + BOOST_CHECK_EQUAL(LocaleIndependentAtoi("-32769"), -32'768); BOOST_CHECK_EQUAL(LocaleIndependentAtoi("-32768"), -32'768); BOOST_CHECK_EQUAL(LocaleIndependentAtoi("32767"), 32'767); - BOOST_CHECK_EQUAL(LocaleIndependentAtoi("32768"), 0); + BOOST_CHECK_EQUAL(LocaleIndependentAtoi("32768"), 32'767); BOOST_CHECK_EQUAL(LocaleIndependentAtoi("-1"), 0U); BOOST_CHECK_EQUAL(LocaleIndependentAtoi("0"), 0U); BOOST_CHECK_EQUAL(LocaleIndependentAtoi("65535"), 65'535U); - BOOST_CHECK_EQUAL(LocaleIndependentAtoi("65536"), 0U); + BOOST_CHECK_EQUAL(LocaleIndependentAtoi("65536"), 65'535U); - BOOST_CHECK_EQUAL(LocaleIndependentAtoi("-129"), 0); + BOOST_CHECK_EQUAL(LocaleIndependentAtoi("-129"), -128); BOOST_CHECK_EQUAL(LocaleIndependentAtoi("-128"), -128); BOOST_CHECK_EQUAL(LocaleIndependentAtoi("127"), 127); - BOOST_CHECK_EQUAL(LocaleIndependentAtoi("128"), 0); + BOOST_CHECK_EQUAL(LocaleIndependentAtoi("128"), 127); BOOST_CHECK_EQUAL(LocaleIndependentAtoi("-1"), 0U); BOOST_CHECK_EQUAL(LocaleIndependentAtoi("0"), 0U); BOOST_CHECK_EQUAL(LocaleIndependentAtoi("255"), 255U); - BOOST_CHECK_EQUAL(LocaleIndependentAtoi("256"), 0U); + BOOST_CHECK_EQUAL(LocaleIndependentAtoi("256"), 255U); } BOOST_AUTO_TEST_CASE(test_ParseInt64) diff --git a/src/util/strencodings.h b/src/util/strencodings.h index 003aa539122ef..f283879db5987 100644 --- a/src/util/strencodings.h +++ b/src/util/strencodings.h @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -91,8 +92,12 @@ void SplitHostPort(std::string_view in, uint16_t &portOut, std::string &hostOut) // New code should use ToIntegral or the ParseInt* functions // which provide parse error feedback. // -// The goal of LocaleIndependentAtoi is to replicate the exact defined behaviour -// of atoi and atoi64 as they behave under the "C" locale. +// The goal of LocaleIndependentAtoi is to replicate the defined behaviour of +// std::atoi as it behaves under the "C" locale, and remove some undefined +// behavior. If the parsed value is bigger than the integer type's maximum +// value, or smaller than the integer type's minimum value, std::atoi has +// undefined behavior, while this function returns the maximum or minimum +// values, respectively. template T LocaleIndependentAtoi(std::string_view str) { @@ -107,7 +112,15 @@ T LocaleIndependentAtoi(std::string_view str) s = s.substr(1); } auto [_, error_condition] = std::from_chars(s.data(), s.data() + s.size(), result); - if (error_condition != std::errc{}) { + if (error_condition == std::errc::result_out_of_range) { + if (s.length() >= 1 && s[0] == '-') { + // Saturate underflow, per strtoll's behavior. + return std::numeric_limits::min(); + } else { + // Saturate overflow, per strtoll's behavior. + return std::numeric_limits::max(); + } + } else if (error_condition != std::errc{}) { return 0; } return result; diff --git a/test/lint/lint-locale-dependence.py b/test/lint/lint-locale-dependence.py index 3e674c5b1daa1..ec27b620bb3b4 100755 --- a/test/lint/lint-locale-dependence.py +++ b/test/lint/lint-locale-dependence.py @@ -49,6 +49,7 @@ "src/dbwrapper.cpp:.*vsnprintf", "src/test/fuzz/locale.cpp", "src/test/fuzz/string.cpp", + "src/test/util_tests.cpp", "src/util/strencodings.cpp:.*strtoll", "src/util/system.cpp:.*fprintf", "src/wallet/bdb.cpp:.*DbEnv::strerror", # False positive From 40b09ddc7c34eedfdb44b2dbf07d3d991a8fdcd5 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sat, 12 Feb 2022 17:32:47 +0100 Subject: [PATCH 08/16] merge bitcoin#24375: Do not use `LocalTestingSetup` in getarg_tests test file --- src/test/getarg_tests.cpp | 366 +++++++++++++++++++------------------- 1 file changed, 184 insertions(+), 182 deletions(-) diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp index d5e80669de6fb..998c697b0dd89 100644 --- a/src/test/getarg_tests.cpp +++ b/src/test/getarg_tests.cpp @@ -13,18 +13,9 @@ #include -namespace getarg_tests{ - class LocalTestingSetup : BasicTestingSetup { - protected: - void SetupArgs(const std::vector>& args); - void ResetArgs(const std::string& strArg); - ArgsManager m_local_args; - }; -} - -BOOST_FIXTURE_TEST_SUITE(getarg_tests, LocalTestingSetup) +BOOST_FIXTURE_TEST_SUITE(getarg_tests, BasicTestingSetup) -void LocalTestingSetup :: ResetArgs(const std::string& strArg) +void ResetArgs(ArgsManager& local_args, const std::string& strArg) { std::vector vecArg; if (strArg.size()) { @@ -40,215 +31,220 @@ void LocalTestingSetup :: ResetArgs(const std::string& strArg) vecChar.push_back(s.c_str()); std::string error; - BOOST_CHECK(m_local_args.ParseParameters(vecChar.size(), vecChar.data(), error)); + BOOST_CHECK(local_args.ParseParameters(vecChar.size(), vecChar.data(), error)); } -void LocalTestingSetup :: SetupArgs(const std::vector>& args) +void SetupArgs(ArgsManager& local_args, const std::vector>& args) { - m_local_args.ClearArgs(); for (const auto& arg : args) { - m_local_args.AddArg(arg.first, "", arg.second, OptionsCategory::OPTIONS); + local_args.AddArg(arg.first, "", arg.second, OptionsCategory::OPTIONS); } } BOOST_AUTO_TEST_CASE(boolarg) { + ArgsManager local_args; + const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_ANY); - SetupArgs({foo}); - ResetArgs("-foo"); - BOOST_CHECK(m_local_args.GetBoolArg("-foo", false)); - BOOST_CHECK(m_local_args.GetBoolArg("-foo", true)); + SetupArgs(local_args, {foo}); + ResetArgs(local_args, "-foo"); + BOOST_CHECK(local_args.GetBoolArg("-foo", false)); + BOOST_CHECK(local_args.GetBoolArg("-foo", true)); - BOOST_CHECK(!m_local_args.GetBoolArg("-fo", false)); - BOOST_CHECK(m_local_args.GetBoolArg("-fo", true)); + BOOST_CHECK(!local_args.GetBoolArg("-fo", false)); + BOOST_CHECK(local_args.GetBoolArg("-fo", true)); - BOOST_CHECK(!m_local_args.GetBoolArg("-fooo", false)); - BOOST_CHECK(m_local_args.GetBoolArg("-fooo", true)); + BOOST_CHECK(!local_args.GetBoolArg("-fooo", false)); + BOOST_CHECK(local_args.GetBoolArg("-fooo", true)); - ResetArgs("-foo=0"); - BOOST_CHECK(!m_local_args.GetBoolArg("-foo", false)); - BOOST_CHECK(!m_local_args.GetBoolArg("-foo", true)); + ResetArgs(local_args, "-foo=0"); + BOOST_CHECK(!local_args.GetBoolArg("-foo", false)); + BOOST_CHECK(!local_args.GetBoolArg("-foo", true)); - ResetArgs("-foo=1"); - BOOST_CHECK(m_local_args.GetBoolArg("-foo", false)); - BOOST_CHECK(m_local_args.GetBoolArg("-foo", true)); + ResetArgs(local_args, "-foo=1"); + BOOST_CHECK(local_args.GetBoolArg("-foo", false)); + BOOST_CHECK(local_args.GetBoolArg("-foo", true)); // New 0.6 feature: auto-map -nosomething to !-something: - ResetArgs("-nofoo"); - BOOST_CHECK(!m_local_args.GetBoolArg("-foo", false)); - BOOST_CHECK(!m_local_args.GetBoolArg("-foo", true)); + ResetArgs(local_args, "-nofoo"); + BOOST_CHECK(!local_args.GetBoolArg("-foo", false)); + BOOST_CHECK(!local_args.GetBoolArg("-foo", true)); - ResetArgs("-nofoo=1"); - BOOST_CHECK(!m_local_args.GetBoolArg("-foo", false)); - BOOST_CHECK(!m_local_args.GetBoolArg("-foo", true)); + ResetArgs(local_args, "-nofoo=1"); + BOOST_CHECK(!local_args.GetBoolArg("-foo", false)); + BOOST_CHECK(!local_args.GetBoolArg("-foo", true)); - ResetArgs("-foo -nofoo"); // -nofoo should win - BOOST_CHECK(!m_local_args.GetBoolArg("-foo", false)); - BOOST_CHECK(!m_local_args.GetBoolArg("-foo", true)); + ResetArgs(local_args, "-foo -nofoo"); // -nofoo should win + BOOST_CHECK(!local_args.GetBoolArg("-foo", false)); + BOOST_CHECK(!local_args.GetBoolArg("-foo", true)); - ResetArgs("-foo=1 -nofoo=1"); // -nofoo should win - BOOST_CHECK(!m_local_args.GetBoolArg("-foo", false)); - BOOST_CHECK(!m_local_args.GetBoolArg("-foo", true)); + ResetArgs(local_args, "-foo=1 -nofoo=1"); // -nofoo should win + BOOST_CHECK(!local_args.GetBoolArg("-foo", false)); + BOOST_CHECK(!local_args.GetBoolArg("-foo", true)); - ResetArgs("-foo=0 -nofoo=0"); // -nofoo=0 should win - BOOST_CHECK(m_local_args.GetBoolArg("-foo", false)); - BOOST_CHECK(m_local_args.GetBoolArg("-foo", true)); + ResetArgs(local_args, "-foo=0 -nofoo=0"); // -nofoo=0 should win + BOOST_CHECK(local_args.GetBoolArg("-foo", false)); + BOOST_CHECK(local_args.GetBoolArg("-foo", true)); // New 0.6 feature: treat -- same as -: - ResetArgs("--foo=1"); - BOOST_CHECK(m_local_args.GetBoolArg("-foo", false)); - BOOST_CHECK(m_local_args.GetBoolArg("-foo", true)); - - ResetArgs("--nofoo=1"); - BOOST_CHECK(!m_local_args.GetBoolArg("-foo", false)); - BOOST_CHECK(!m_local_args.GetBoolArg("-foo", true)); + ResetArgs(local_args, "--foo=1"); + BOOST_CHECK(local_args.GetBoolArg("-foo", false)); + BOOST_CHECK(local_args.GetBoolArg("-foo", true)); + ResetArgs(local_args, "--nofoo=1"); + BOOST_CHECK(!local_args.GetBoolArg("-foo", false)); + BOOST_CHECK(!local_args.GetBoolArg("-foo", true)); } BOOST_AUTO_TEST_CASE(stringarg) { + ArgsManager local_args; + const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_ANY); const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY); - SetupArgs({foo, bar}); - ResetArgs(""); - BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", ""), ""); - BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", "eleven"), "eleven"); - - ResetArgs("-foo -bar"); - BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", ""), ""); - BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", "eleven"), ""); - - ResetArgs("-foo="); - BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", ""), ""); - BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", "eleven"), ""); - - ResetArgs("-foo=11"); - BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", ""), "11"); - BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", "eleven"), "11"); - - ResetArgs("-foo=eleven"); - BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", ""), "eleven"); - BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", "eleven"), "eleven"); - + SetupArgs(local_args, {foo, bar}); + ResetArgs(local_args, ""); + BOOST_CHECK_EQUAL(local_args.GetArg("-foo", ""), ""); + BOOST_CHECK_EQUAL(local_args.GetArg("-foo", "eleven"), "eleven"); + + ResetArgs(local_args, "-foo -bar"); + BOOST_CHECK_EQUAL(local_args.GetArg("-foo", ""), ""); + BOOST_CHECK_EQUAL(local_args.GetArg("-foo", "eleven"), ""); + + ResetArgs(local_args, "-foo="); + BOOST_CHECK_EQUAL(local_args.GetArg("-foo", ""), ""); + BOOST_CHECK_EQUAL(local_args.GetArg("-foo", "eleven"), ""); + + ResetArgs(local_args, "-foo=11"); + BOOST_CHECK_EQUAL(local_args.GetArg("-foo", ""), "11"); + BOOST_CHECK_EQUAL(local_args.GetArg("-foo", "eleven"), "11"); + + ResetArgs(local_args, "-foo=eleven"); + BOOST_CHECK_EQUAL(local_args.GetArg("-foo", ""), "eleven"); + BOOST_CHECK_EQUAL(local_args.GetArg("-foo", "eleven"), "eleven"); } BOOST_AUTO_TEST_CASE(intarg) { + ArgsManager local_args; + const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_ANY); const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY); - SetupArgs({foo, bar}); - ResetArgs(""); - BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", 11), 11); - BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", 0), 0); + SetupArgs(local_args, {foo, bar}); + ResetArgs(local_args, ""); + BOOST_CHECK_EQUAL(local_args.GetArg("-foo", 11), 11); + BOOST_CHECK_EQUAL(local_args.GetArg("-foo", 0), 0); - ResetArgs("-foo -bar"); - BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", 11), 0); - BOOST_CHECK_EQUAL(m_local_args.GetArg("-bar", 11), 0); + ResetArgs(local_args, "-foo -bar"); + BOOST_CHECK_EQUAL(local_args.GetArg("-foo", 11), 0); + BOOST_CHECK_EQUAL(local_args.GetArg("-bar", 11), 0); // Check under-/overflow behavior. - ResetArgs("-foo=-9223372036854775809 -bar=9223372036854775808"); - BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", 0), std::numeric_limits::min()); - BOOST_CHECK_EQUAL(m_local_args.GetArg("-bar", 0), std::numeric_limits::max()); + ResetArgs(local_args, "-foo=-9223372036854775809 -bar=9223372036854775808"); + BOOST_CHECK_EQUAL(local_args.GetArg("-foo", 0), std::numeric_limits::min()); + BOOST_CHECK_EQUAL(local_args.GetArg("-bar", 0), std::numeric_limits::max()); - ResetArgs("-foo=11 -bar=12"); - BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", 0), 11); - BOOST_CHECK_EQUAL(m_local_args.GetArg("-bar", 11), 12); + ResetArgs(local_args, "-foo=11 -bar=12"); + BOOST_CHECK_EQUAL(local_args.GetArg("-foo", 0), 11); + BOOST_CHECK_EQUAL(local_args.GetArg("-bar", 11), 12); - ResetArgs("-foo=NaN -bar=NotANumber"); - BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", 1), 0); - BOOST_CHECK_EQUAL(m_local_args.GetArg("-bar", 11), 0); + ResetArgs(local_args, "-foo=NaN -bar=NotANumber"); + BOOST_CHECK_EQUAL(local_args.GetArg("-foo", 1), 0); + BOOST_CHECK_EQUAL(local_args.GetArg("-bar", 11), 0); } BOOST_AUTO_TEST_CASE(patharg) { + ArgsManager local_args; + const auto dir = std::make_pair("-dir", ArgsManager::ALLOW_ANY); - SetupArgs({dir}); - ResetArgs(""); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), fs::path{}); + SetupArgs(local_args, {dir}); + ResetArgs(local_args, ""); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir"), fs::path{}); const fs::path root_path{"/"}; - ResetArgs("-dir=/"); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), root_path); + ResetArgs(local_args, "-dir=/"); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir"), root_path); - ResetArgs("-dir=/."); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), root_path); + ResetArgs(local_args, "-dir=/."); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir"), root_path); - ResetArgs("-dir=/./"); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), root_path); + ResetArgs(local_args, "-dir=/./"); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir"), root_path); - ResetArgs("-dir=/.//"); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), root_path); + ResetArgs(local_args, "-dir=/.//"); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir"), root_path); #ifdef WIN32 const fs::path win_root_path{"C:\\"}; - ResetArgs("-dir=C:\\"); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), win_root_path); + ResetArgs(local_args, "-dir=C:\\"); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir"), win_root_path); - ResetArgs("-dir=C:/"); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), win_root_path); + ResetArgs(local_args, "-dir=C:/"); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir"), win_root_path); - ResetArgs("-dir=C:\\\\"); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), win_root_path); + ResetArgs(local_args, "-dir=C:\\\\"); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir"), win_root_path); - ResetArgs("-dir=C:\\."); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), win_root_path); + ResetArgs(local_args, "-dir=C:\\."); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir"), win_root_path); - ResetArgs("-dir=C:\\.\\"); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), win_root_path); + ResetArgs(local_args, "-dir=C:\\.\\"); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir"), win_root_path); - ResetArgs("-dir=C:\\.\\\\"); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), win_root_path); + ResetArgs(local_args, "-dir=C:\\.\\\\"); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir"), win_root_path); #endif const fs::path absolute_path{"/home/user/.bitcoin"}; - ResetArgs("-dir=/home/user/.bitcoin"); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), absolute_path); + ResetArgs(local_args, "-dir=/home/user/.bitcoin"); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir"), absolute_path); - ResetArgs("-dir=/root/../home/user/.bitcoin"); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), absolute_path); + ResetArgs(local_args, "-dir=/root/../home/user/.bitcoin"); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir"), absolute_path); - ResetArgs("-dir=/home/./user/.bitcoin"); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), absolute_path); + ResetArgs(local_args, "-dir=/home/./user/.bitcoin"); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir"), absolute_path); - ResetArgs("-dir=/home/user/.bitcoin/"); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), absolute_path); + ResetArgs(local_args, "-dir=/home/user/.bitcoin/"); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir"), absolute_path); - ResetArgs("-dir=/home/user/.bitcoin//"); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), absolute_path); + ResetArgs(local_args, "-dir=/home/user/.bitcoin//"); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir"), absolute_path); - ResetArgs("-dir=/home/user/.bitcoin/."); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), absolute_path); + ResetArgs(local_args, "-dir=/home/user/.bitcoin/."); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir"), absolute_path); - ResetArgs("-dir=/home/user/.bitcoin/./"); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), absolute_path); + ResetArgs(local_args, "-dir=/home/user/.bitcoin/./"); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir"), absolute_path); - ResetArgs("-dir=/home/user/.bitcoin/.//"); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), absolute_path); + ResetArgs(local_args, "-dir=/home/user/.bitcoin/.//"); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir"), absolute_path); const fs::path relative_path{"user/.bitcoin"}; - ResetArgs("-dir=user/.bitcoin"); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), relative_path); + ResetArgs(local_args, "-dir=user/.bitcoin"); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir"), relative_path); - ResetArgs("-dir=somewhere/../user/.bitcoin"); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), relative_path); + ResetArgs(local_args, "-dir=somewhere/../user/.bitcoin"); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir"), relative_path); - ResetArgs("-dir=user/./.bitcoin"); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), relative_path); + ResetArgs(local_args, "-dir=user/./.bitcoin"); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir"), relative_path); - ResetArgs("-dir=user/.bitcoin/"); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), relative_path); + ResetArgs(local_args, "-dir=user/.bitcoin/"); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir"), relative_path); - ResetArgs("-dir=user/.bitcoin//"); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), relative_path); + ResetArgs(local_args, "-dir=user/.bitcoin//"); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir"), relative_path); - ResetArgs("-dir=user/.bitcoin/."); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), relative_path); + ResetArgs(local_args, "-dir=user/.bitcoin/."); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir"), relative_path); - ResetArgs("-dir=user/.bitcoin/./"); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), relative_path); + ResetArgs(local_args, "-dir=user/.bitcoin/./"); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir"), relative_path); - ResetArgs("-dir=user/.bitcoin/.//"); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), relative_path); + ResetArgs(local_args, "-dir=user/.bitcoin/.//"); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir"), relative_path); // Check negated and default argument handling. Specifying an empty argument // is the same as not specifying the argument. This is convenient for @@ -257,65 +253,71 @@ BOOST_AUTO_TEST_CASE(patharg) // distinguished from -dir case with no assignment, but #16545 would add the // ability to distinguish these in the future (and treat the no-assign case // like an imperative command or an error). - ResetArgs(""); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir", "default"), fs::path{"default"}); - ResetArgs("-dir=override"); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir", "default"), fs::path{"override"}); - ResetArgs("-dir="); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir", "default"), fs::path{"default"}); - ResetArgs("-dir"); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir", "default"), fs::path{"default"}); - ResetArgs("-nodir"); - BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir", "default"), fs::path{""}); + ResetArgs(local_args, ""); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir", "default"), fs::path{"default"}); + ResetArgs(local_args, "-dir=override"); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir", "default"), fs::path{"override"}); + ResetArgs(local_args, "-dir="); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir", "default"), fs::path{"default"}); + ResetArgs(local_args, "-dir"); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir", "default"), fs::path{"default"}); + ResetArgs(local_args, "-nodir"); + BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir", "default"), fs::path{""}); } BOOST_AUTO_TEST_CASE(doubledash) { + ArgsManager local_args; + const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_ANY); const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY); - SetupArgs({foo, bar}); - ResetArgs("--foo"); - BOOST_CHECK_EQUAL(m_local_args.GetBoolArg("-foo", false), true); + SetupArgs(local_args, {foo, bar}); + ResetArgs(local_args, "--foo"); + BOOST_CHECK_EQUAL(local_args.GetBoolArg("-foo", false), true); - ResetArgs("--foo=verbose --bar=1"); - BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", ""), "verbose"); - BOOST_CHECK_EQUAL(m_local_args.GetArg("-bar", 0), 1); + ResetArgs(local_args, "--foo=verbose --bar=1"); + BOOST_CHECK_EQUAL(local_args.GetArg("-foo", ""), "verbose"); + BOOST_CHECK_EQUAL(local_args.GetArg("-bar", 0), 1); } BOOST_AUTO_TEST_CASE(boolargno) { + ArgsManager local_args; + const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_ANY); const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY); - SetupArgs({foo, bar}); - ResetArgs("-nofoo"); - BOOST_CHECK(!m_local_args.GetBoolArg("-foo", true)); - BOOST_CHECK(!m_local_args.GetBoolArg("-foo", false)); - - ResetArgs("-nofoo=1"); - BOOST_CHECK(!m_local_args.GetBoolArg("-foo", true)); - BOOST_CHECK(!m_local_args.GetBoolArg("-foo", false)); - - ResetArgs("-nofoo=0"); - BOOST_CHECK(m_local_args.GetBoolArg("-foo", true)); - BOOST_CHECK(m_local_args.GetBoolArg("-foo", false)); - - ResetArgs("-foo --nofoo"); // --nofoo should win - BOOST_CHECK(!m_local_args.GetBoolArg("-foo", true)); - BOOST_CHECK(!m_local_args.GetBoolArg("-foo", false)); - - ResetArgs("-nofoo -foo"); // foo always wins: - BOOST_CHECK(m_local_args.GetBoolArg("-foo", true)); - BOOST_CHECK(m_local_args.GetBoolArg("-foo", false)); + SetupArgs(local_args, {foo, bar}); + ResetArgs(local_args, "-nofoo"); + BOOST_CHECK(!local_args.GetBoolArg("-foo", true)); + BOOST_CHECK(!local_args.GetBoolArg("-foo", false)); + + ResetArgs(local_args, "-nofoo=1"); + BOOST_CHECK(!local_args.GetBoolArg("-foo", true)); + BOOST_CHECK(!local_args.GetBoolArg("-foo", false)); + + ResetArgs(local_args, "-nofoo=0"); + BOOST_CHECK(local_args.GetBoolArg("-foo", true)); + BOOST_CHECK(local_args.GetBoolArg("-foo", false)); + + ResetArgs(local_args, "-foo --nofoo"); // --nofoo should win + BOOST_CHECK(!local_args.GetBoolArg("-foo", true)); + BOOST_CHECK(!local_args.GetBoolArg("-foo", false)); + + ResetArgs(local_args, "-nofoo -foo"); // foo always wins: + BOOST_CHECK(local_args.GetBoolArg("-foo", true)); + BOOST_CHECK(local_args.GetBoolArg("-foo", false)); } BOOST_AUTO_TEST_CASE(logargs) { + ArgsManager local_args; + const auto okaylog_bool = std::make_pair("-okaylog-bool", ArgsManager::ALLOW_BOOL); const auto okaylog_negbool = std::make_pair("-okaylog-negbool", ArgsManager::ALLOW_BOOL); const auto okaylog = std::make_pair("-okaylog", ArgsManager::ALLOW_ANY); const auto dontlog = std::make_pair("-dontlog", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE); - SetupArgs({okaylog_bool, okaylog_negbool, okaylog, dontlog}); - ResetArgs("-okaylog-bool -nookaylog-negbool -okaylog=public -dontlog=private"); + SetupArgs(local_args, {okaylog_bool, okaylog_negbool, okaylog, dontlog}); + ResetArgs(local_args, "-okaylog-bool -nookaylog-negbool -okaylog=public -dontlog=private"); // Everything logged to debug.log will also append to str std::string str; @@ -325,7 +327,7 @@ BOOST_AUTO_TEST_CASE(logargs) }); // Log the arguments - m_local_args.LogArgs(); + local_args.LogArgs(); LogInstance().DeleteCallback(print_connection); // Check that what should appear does, and what shouldn't doesn't. From f9b761401573333313f1836a4634b607fd2baa77 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sun, 26 Jan 2025 18:47:40 +0000 Subject: [PATCH 09/16] merge bitcoin#24498: Avoid crash on startup if int specified in settings.json --- src/Makefile.qttest.include | 3 + src/qt/test/optiontests.cpp | 31 ++++++++++ src/qt/test/optiontests.h | 25 ++++++++ src/qt/test/test_main.cpp | 5 ++ src/test/getarg_tests.cpp | 112 ++++++++++++++++++++++++++++++++++++ src/util/system.cpp | 2 +- 6 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 src/qt/test/optiontests.cpp create mode 100644 src/qt/test/optiontests.h diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include index 8022b8efcb195..97d15057d2287 100644 --- a/src/Makefile.qttest.include +++ b/src/Makefile.qttest.include @@ -8,6 +8,7 @@ TESTS += qt/test/test_dash-qt TEST_QT_MOC_CPP = \ qt/test/moc_apptests.cpp \ + qt/test/moc_optiontests.cpp \ qt/test/moc_rpcnestedtests.cpp \ qt/test/moc_trafficgraphdatatests.cpp \ qt/test/moc_uritests.cpp @@ -21,6 +22,7 @@ endif # ENABLE_WALLET TEST_QT_H = \ qt/test/addressbooktests.h \ qt/test/apptests.h \ + qt/test/optiontests.h \ qt/test/rpcnestedtests.h \ qt/test/uritests.h \ qt/test/util.h \ @@ -32,6 +34,7 @@ qt_test_test_dash_qt_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_QT_ qt_test_test_dash_qt_SOURCES = \ qt/test/apptests.cpp \ + qt/test/optiontests.cpp \ qt/test/rpcnestedtests.cpp \ qt/test/test_main.cpp \ qt/test/trafficgraphdatatests.cpp \ diff --git a/src/qt/test/optiontests.cpp b/src/qt/test/optiontests.cpp new file mode 100644 index 0000000000000..51894e1915f3f --- /dev/null +++ b/src/qt/test/optiontests.cpp @@ -0,0 +1,31 @@ +// Copyright (c) 2018 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 +#include +#include +#include + +#include +#include + +#include + +//! Entry point for BitcoinApplication tests. +void OptionTests::optionTests() +{ + // Test regression https://github.com/bitcoin/bitcoin/issues/24457. Ensure + // that setting integer prune value doesn't cause an exception to be thrown + // in the OptionsModel constructor + gArgs.LockSettings([&](util::Settings& settings) { + settings.forced_settings.erase("prune"); + settings.rw_settings["prune"] = 3814; + }); + gArgs.WriteSettingsFile(); + OptionsModel{}; + gArgs.LockSettings([&](util::Settings& settings) { + settings.rw_settings.erase("prune"); + }); + gArgs.WriteSettingsFile(); +} diff --git a/src/qt/test/optiontests.h b/src/qt/test/optiontests.h new file mode 100644 index 0000000000000..779d4cc209a27 --- /dev/null +++ b/src/qt/test/optiontests.h @@ -0,0 +1,25 @@ +// Copyright (c) 2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_QT_TEST_OPTIONTESTS_H +#define BITCOIN_QT_TEST_OPTIONTESTS_H + +#include + +#include + +class OptionTests : public QObject +{ + Q_OBJECT +public: + explicit OptionTests(interfaces::Node& node) : m_node(node) {} + +private Q_SLOTS: + void optionTests(); + +private: + interfaces::Node& m_node; +}; + +#endif // BITCOIN_QT_TEST_OPTIONTESTS_H diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp index 4bf49ba8a03eb..6ccc5632cfa91 100644 --- a/src/qt/test/test_main.cpp +++ b/src/qt/test/test_main.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -86,6 +87,10 @@ int main(int argc, char* argv[]) if (QTest::qExec(&app_tests) != 0) { fInvalid = true; } + OptionTests options_tests(app.node()); + if (QTest::qExec(&options_tests) != 0) { + fInvalid = true; + } URITests test1; if (QTest::qExec(&test1) != 0) { fInvalid = true; diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp index 998c697b0dd89..5a6d17eec14a3 100644 --- a/src/test/getarg_tests.cpp +++ b/src/test/getarg_tests.cpp @@ -3,6 +3,8 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include +#include +#include #include #include @@ -41,6 +43,116 @@ void SetupArgs(ArgsManager& local_args, const std::vector Date: Thu, 24 Mar 2022 18:32:22 +0100 Subject: [PATCH 10/16] merge bitcoin-core/gui#569: add regression test for bitcoin-core/gui#567 --- src/qt/optionsmodel.cpp | 21 +++++++++++++++++++-- src/qt/test/optiontests.cpp | 37 +++++++++++++++++++++++++++++++++++++ src/qt/test/optiontests.h | 1 + 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 7b4b517886a11..f818ced681cf1 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -262,9 +262,26 @@ void OptionsModel::Init(bool resetSettings) if (!settings.contains("fListen")) settings.setValue("fListen", DEFAULT_LISTEN); - if (!gArgs.SoftSetBoolArg("-listen", settings.value("fListen").toBool())) { + const bool listen{settings.value("fListen").toBool()}; + if (!gArgs.SoftSetBoolArg("-listen", listen)) { addOverriddenOption("-listen"); - } else if (!settings.value("fListen").toBool()) { + } else if (!listen) { + // We successfully set -listen=0, thus mimic the logic from InitParameterInteraction(): + // "parameter interaction: -listen=0 -> setting -listenonion=0". + // + // Both -listen and -listenonion default to true. + // + // The call order is: + // + // InitParameterInteraction() + // would set -listenonion=0 if it sees -listen=0, but for bitcoin-qt with + // fListen=false -listen is 1 at this point + // + // OptionsModel::Init() + // (this method) can flip -listen from 1 to 0 if fListen=false + // + // AppInitParameterInteraction() + // raises an error if -listen=0 and -listenonion=1 gArgs.SoftSetBoolArg("-listenonion", false); } diff --git a/src/qt/test/optiontests.cpp b/src/qt/test/optiontests.cpp index 51894e1915f3f..4a943a6343585 100644 --- a/src/qt/test/optiontests.cpp +++ b/src/qt/test/optiontests.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include #include #include #include @@ -29,3 +30,39 @@ void OptionTests::optionTests() }); gArgs.WriteSettingsFile(); } + +void OptionTests::parametersInteraction() +{ + // Test that the bug https://github.com/bitcoin-core/gui/issues/567 does not resurface. + // It was fixed via https://github.com/bitcoin-core/gui/pull/568. + // With fListen=false in ~/.config/Bitcoin/Bitcoin-Qt.conf and all else left as default, + // bitcoin-qt should set both -listen and -listenonion to false and start successfully. + gArgs.ClearPathCache(); + + gArgs.LockSettings([&](util::Settings& s) { + s.forced_settings.erase("listen"); + s.forced_settings.erase("listenonion"); + }); + QVERIFY(!gArgs.IsArgSet("-listen")); + QVERIFY(!gArgs.IsArgSet("-listenonion")); + + QSettings settings; + settings.setValue("fListen", false); + + OptionsModel{}; + + const bool expected{false}; + + QVERIFY(gArgs.IsArgSet("-listen")); + QCOMPARE(gArgs.GetBoolArg("-listen", !expected), expected); + + QVERIFY(gArgs.IsArgSet("-listenonion")); + QCOMPARE(gArgs.GetBoolArg("-listenonion", !expected), expected); + + QVERIFY(AppInitParameterInteraction(gArgs)); + + // cleanup + settings.remove("fListen"); + QVERIFY(!settings.contains("fListen")); + gArgs.ClearPathCache(); +} diff --git a/src/qt/test/optiontests.h b/src/qt/test/optiontests.h index 779d4cc209a27..39c1612c8f88c 100644 --- a/src/qt/test/optiontests.h +++ b/src/qt/test/optiontests.h @@ -17,6 +17,7 @@ class OptionTests : public QObject private Q_SLOTS: void optionTests(); + void parametersInteraction(); private: interfaces::Node& m_node; From b25f165ac8ea78151516c7b4bc20ce22a291dcc6 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Wed, 6 Apr 2022 22:43:11 +0200 Subject: [PATCH 11/16] merge bitcoin-core/gui#576: Add qt unit test runner summary --- src/qt/test/test_main.cpp | 48 +++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp index 6ccc5632cfa91..9ce423dd4c818 100644 --- a/src/qt/test/test_main.cpp +++ b/src/qt/test/test_main.cpp @@ -22,8 +22,10 @@ #endif // ENABLE_WALLET #include +#include #include #include + #include #if defined(QT_STATIC) @@ -67,8 +69,6 @@ int main(int argc, char* argv[]) gArgs.ForceSetArg("-upnp", "0"); gArgs.ForceSetArg("-natpmp", "0"); - bool fInvalid = false; - // Prefer the "minimal" platform for the test instead of the normal default // platform ("xcb", "windows", or "cocoa") so tests can't unintentionally // interfere with any background GUIs and don't require extra resources. @@ -83,35 +83,35 @@ int main(int argc, char* argv[]) app.setApplicationName("Dash-Qt-test"); app.node().context()->args = &gArgs; // Make gArgs available in the NodeContext + + int num_test_failures{0}; + AppTests app_tests(app); - if (QTest::qExec(&app_tests) != 0) { - fInvalid = true; - } + num_test_failures += QTest::qExec(&app_tests); + OptionTests options_tests(app.node()); - if (QTest::qExec(&options_tests) != 0) { - fInvalid = true; - } + num_test_failures += QTest::qExec(&options_tests); + URITests test1; - if (QTest::qExec(&test1) != 0) { - fInvalid = true; - } + num_test_failures += QTest::qExec(&test1); + RPCNestedTests test3(app.node()); - if (QTest::qExec(&test3) != 0) { - fInvalid = true; - } + num_test_failures += QTest::qExec(&test3); + #ifdef ENABLE_WALLET WalletTests test5(app.node()); - if (QTest::qExec(&test5) != 0) { - fInvalid = true; - } + num_test_failures += QTest::qExec(&test5); + AddressBookTests test6(app.node()); - if (QTest::qExec(&test6) != 0) { - fInvalid = true; - } + num_test_failures += QTest::qExec(&test6); #endif - TrafficGraphDataTests test7; - if (QTest::qExec(&test7) != 0) - fInvalid = true; - return fInvalid; + num_test_failures += QTest::qExec(&test7); + + if (num_test_failures) { + qWarning("\nFailed tests: %d\n", num_test_failures); + } else { + qDebug("\nAll tests passed.\n"); + } + return num_test_failures; } From 6e4eee05a5ba58a4f741294c29a9d0137a7b8872 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Tue, 14 Jun 2022 12:40:57 +0200 Subject: [PATCH 12/16] merge bitcoin-core/gui#618: Add `transactionoverviewwidget.cpp` source file --- src/Makefile.qt.include | 1 + src/qt/transactionoverviewwidget.cpp | 27 +++++++++++++++++++++++++++ src/qt/transactionoverviewwidget.h | 19 +++---------------- 3 files changed, 31 insertions(+), 16 deletions(-) create mode 100644 src/qt/transactionoverviewwidget.cpp diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 4e41925193670..c1ac84cacf6c8 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -268,6 +268,7 @@ BITCOIN_QT_WALLET_CPP = \ qt/transactiondesc.cpp \ qt/transactiondescdialog.cpp \ qt/transactionfilterproxy.cpp \ + qt/transactionoverviewwidget.cpp \ qt/transactionrecord.cpp \ qt/transactiontablemodel.cpp \ qt/transactionview.cpp \ diff --git a/src/qt/transactionoverviewwidget.cpp b/src/qt/transactionoverviewwidget.cpp new file mode 100644 index 0000000000000..360a1364fb423 --- /dev/null +++ b/src/qt/transactionoverviewwidget.cpp @@ -0,0 +1,27 @@ +// Copyright (c) 2021 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 + +#include + +#include +#include +#include + +TransactionOverviewWidget::TransactionOverviewWidget(QWidget* parent) + : QListView(parent) {} + +QSize TransactionOverviewWidget::sizeHint() const +{ + return {sizeHintForColumn(TransactionTableModel::ToAddress), QListView::sizeHint().height()}; +} + +void TransactionOverviewWidget::showEvent(QShowEvent* event) +{ + Q_UNUSED(event); + QSizePolicy sp = sizePolicy(); + sp.setHorizontalPolicy(QSizePolicy::Minimum); + setSizePolicy(sp); +} diff --git a/src/qt/transactionoverviewwidget.h b/src/qt/transactionoverviewwidget.h index 2bdead7bc40e9..0572e84090f6e 100644 --- a/src/qt/transactionoverviewwidget.h +++ b/src/qt/transactionoverviewwidget.h @@ -5,11 +5,8 @@ #ifndef BITCOIN_QT_TRANSACTIONOVERVIEWWIDGET_H #define BITCOIN_QT_TRANSACTIONOVERVIEWWIDGET_H -#include - #include #include -#include QT_BEGIN_NAMESPACE class QShowEvent; @@ -21,21 +18,11 @@ class TransactionOverviewWidget : public QListView Q_OBJECT public: - explicit TransactionOverviewWidget(QWidget* parent = nullptr) : QListView(parent) {} - - QSize sizeHint() const override - { - return {sizeHintForColumn(TransactionTableModel::ToAddress), QListView::sizeHint().height()}; - } + explicit TransactionOverviewWidget(QWidget* parent = nullptr); + QSize sizeHint() const override; protected: - void showEvent(QShowEvent* event) override - { - Q_UNUSED(event); - QSizePolicy sp = sizePolicy(); - sp.setHorizontalPolicy(QSizePolicy::Minimum); - setSizePolicy(sp); - } + void showEvent(QShowEvent* event) override; }; #endif // BITCOIN_QT_TRANSACTIONOVERVIEWWIDGET_H From 18d15236c3aa491194f6ede246378c0d5994020b Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Tue, 21 Jun 2022 18:53:03 -0300 Subject: [PATCH 13/16] merge bitcoin-core/gui#620: Replace `QRegExp` with `QRegularExpression` --- src/qt/guiutil.cpp | 30 +++++++++++++++--------------- src/qt/guiutil.h | 8 ++++++++ src/qt/test/optiontests.cpp | 10 ++++++++++ src/qt/test/optiontests.h | 1 + src/qt/utilitydialog.cpp | 8 ++++---- 5 files changed, 38 insertions(+), 19 deletions(-) diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 2574adee48212..33037b997c388 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -61,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -512,6 +513,17 @@ QString getDefaultDataDirectory() return PathToQString(GetDefaultDataDir()); } +QString ExtractFirstSuffixFromFilter(const QString& filter) +{ + QRegularExpression filter_re(QStringLiteral(".* \\(\\*\\.(.*)[ \\)]"), QRegularExpression::InvertedGreedinessOption); + QString suffix; + QRegularExpressionMatch m = filter_re.match(filter); + if (m.hasMatch()) { + suffix = m.captured(1); + } + return suffix; +} + QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedSuffixOut) @@ -529,13 +541,7 @@ QString getSaveFileName(QWidget *parent, const QString &caption, const QString & /* Directly convert path to native OS path separators */ QString result = QDir::toNativeSeparators(QFileDialog::getSaveFileName(parent, caption, myDir, filter, &selectedFilter)); - /* Extract first suffix from filter pattern "Description (*.foo)" or "Description (*.foo *.bar ...) */ - QRegExp filter_re(".* \\(\\*\\.(.*)[ \\)]"); - QString selectedSuffix; - if(filter_re.exactMatch(selectedFilter)) - { - selectedSuffix = filter_re.cap(1); - } + QString selectedSuffix = ExtractFirstSuffixFromFilter(selectedFilter); /* Add suffix if needed */ QFileInfo info(result); @@ -577,14 +583,8 @@ QString getOpenFileName(QWidget *parent, const QString &caption, const QString & if(selectedSuffixOut) { - /* Extract first suffix from filter pattern "Description (*.foo)" or "Description (*.foo *.bar ...) */ - QRegExp filter_re(".* \\(\\*\\.(.*)[ \\)]"); - QString selectedSuffix; - if(filter_re.exactMatch(selectedFilter)) - { - selectedSuffix = filter_re.cap(1); - } - *selectedSuffixOut = selectedSuffix; + *selectedSuffixOut = ExtractFirstSuffixFromFilter(selectedFilter); + ; } return result; } diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 438d170cfc769..26dbda32f145b 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -191,6 +191,14 @@ namespace GUIUtil */ QString getDefaultDataDirectory(); + /** + * Extract first suffix from filter pattern "Description (*.foo)" or "Description (*.foo *.bar ...). + * + * @param[in] filter Filter specification such as "Comma Separated Files (*.csv)" + * @return QString + */ + QString ExtractFirstSuffixFromFilter(const QString& filter); + /** Get save filename, mimics QFileDialog::getSaveFileName, except that it appends a default suffix when no suffix is provided by the user. diff --git a/src/qt/test/optiontests.cpp b/src/qt/test/optiontests.cpp index 4a943a6343585..343755103cf2d 100644 --- a/src/qt/test/optiontests.cpp +++ b/src/qt/test/optiontests.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -66,3 +67,12 @@ void OptionTests::parametersInteraction() QVERIFY(!settings.contains("fListen")); gArgs.ClearPathCache(); } + +void OptionTests::extractFilter() +{ + QString filter = QString("Partially Signed Transaction (Binary) (*.psbt)"); + QCOMPARE(GUIUtil::ExtractFirstSuffixFromFilter(filter), "psbt"); + + filter = QString("Image (*.png *.jpg)"); + QCOMPARE(GUIUtil::ExtractFirstSuffixFromFilter(filter), "png"); +} diff --git a/src/qt/test/optiontests.h b/src/qt/test/optiontests.h index 39c1612c8f88c..d5a3bfd6b4a07 100644 --- a/src/qt/test/optiontests.h +++ b/src/qt/test/optiontests.h @@ -18,6 +18,7 @@ class OptionTests : public QObject private Q_SLOTS: void optionTests(); void parametersInteraction(); + void extractFilter(); private: interfaces::Node& m_node; diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp index 83743878b3d2b..d21ae4312bb55 100644 --- a/src/qt/utilitydialog.cpp +++ b/src/qt/utilitydialog.cpp @@ -23,7 +23,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -48,9 +49,8 @@ HelpMessageDialog::HelpMessageDialog(QWidget *parent, HelpMode helpMode) : QString licenseInfoHTML = QString::fromStdString(licenseInfo); // Make URLs clickable - QRegExp uri("<(.*)>", Qt::CaseSensitive, QRegExp::RegExp2); - uri.setMinimal(true); // use non-greedy matching - licenseInfoHTML.replace(uri, QString("\\1").arg(GUIUtil::getThemedStyleQString(GUIUtil::ThemedStyle::TS_COMMAND))); + QRegularExpression uri(QStringLiteral("<(.*)>"), QRegularExpression::InvertedGreedinessOption); + licenseInfoHTML.replace(uri, QString("\\1").arg(GUIUtil::getThemedStyleQString(GUIUtil::ThemedStyle::TS_COMMAND))); // Replace newlines with HTML breaks licenseInfoHTML.replace("\n", "
"); From 75a101640a19b2104c6db0df462f395df612e8c6 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Fri, 15 Jul 2022 11:34:21 -0400 Subject: [PATCH 14/16] merge bitcoin-core/gui#631: Disallow encryption of watchonly wallets --- src/qt/bitcoingui.cpp | 6 ++++++ src/qt/walletmodel.cpp | 5 +++++ src/qt/walletmodel.h | 1 + 3 files changed, 12 insertions(+) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 62f011f1b9a02..57aad51ab279c 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -1876,6 +1876,12 @@ void BitcoinGUI::setEncryptionStatus(int status) { switch(status) { + case WalletModel::NoKeys: + labelWalletEncryptionIcon->hide(); + encryptWalletAction->setChecked(false); + changePassphraseAction->setEnabled(false); + encryptWalletAction->setEnabled(false); + break; case WalletModel::Unencrypted: labelWalletEncryptionIcon->show(); labelWalletEncryptionIcon->setPixmap(GUIUtil::getIcon("lock_open", GUIUtil::ThemedColor::RED).pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index d4a88de83b97a..fd1f2c2d0696e 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -356,6 +356,11 @@ WalletModel::EncryptionStatus WalletModel::getEncryptionStatus() const { if(!m_wallet->isCrypted()) { + // A previous bug allowed for watchonly wallets to be encrypted (encryption keys set, but nothing is actually encrypted). + // To avoid misrepresenting the encryption status of such wallets, we only return NoKeys for watchonly wallets that are unencrypted. + if (m_wallet->privateKeysDisabled()) { + return NoKeys; + } return Unencrypted; } else if(m_wallet->isLocked(true)) diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index d15f65af91595..ce86fbfcf61e4 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -70,6 +70,7 @@ class WalletModel : public QObject enum EncryptionStatus { + NoKeys, // wallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) Unencrypted, // !wallet->IsCrypted() Locked, // wallet->IsCrypted() && wallet->IsLocked(true) UnlockedForMixingOnly, // wallet->IsCrypted() && !wallet->IsLocked(true) && wallet->IsLocked() From 6bf485428e4c843dd493bec8769a7f7bd46f06e3 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sun, 12 Dec 2021 08:15:06 +0100 Subject: [PATCH 15/16] partial bitcoin#23757: fix GUI not loading on Qt 5.15 excludes: - 27f353d8efca19bc2f7dc79b09d4737c9401a768 --- src/Makefile.qttest.include | 2 +- src/qt/bitcoin.cpp | 2 ++ src/qt/test/test_main.cpp | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include index 97d15057d2287..8c4a950a5707f 100644 --- a/src/Makefile.qttest.include +++ b/src/Makefile.qttest.include @@ -58,7 +58,7 @@ if ENABLE_ZMQ qt_test_test_dash_qt_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS) endif qt_test_test_dash_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBDASHBLS) $(LIBUNIVALUE) $(LIBLEVELDB) \ - $(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BACKTRACE_LIB) $(QT_DBUS_LIBS) $(QT_TEST_LIBS) $(QT_LIBS) \ + $(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BACKTRACE_LIB) $(QT_LIBS) $(QT_DBUS_LIBS) $(QT_TEST_LIBS) \ $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(SQLITE_LIBS) $(LIBSECP256K1) \ $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(GMP_LIBS) qt_test_test_dash_qt_LDFLAGS = $(LDFLAGS_WRAP_EXCEPTIONS) $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS) diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index e6e75ae37f19c..472fbd7bdd0bb 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -68,6 +68,8 @@ Q_IMPORT_PLUGIN(QWindowsVistaStylePlugin); #elif defined(QT_QPA_PLATFORM_COCOA) Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin); Q_IMPORT_PLUGIN(QMacStylePlugin); +#elif defined(QT_QPA_PLATFORM_ANDROID) +Q_IMPORT_PLUGIN(QAndroidPlatformIntegrationPlugin) #endif #endif diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp index 9ce423dd4c818..af73890799421 100644 --- a/src/qt/test/test_main.cpp +++ b/src/qt/test/test_main.cpp @@ -39,6 +39,8 @@ Q_IMPORT_PLUGIN(QXcbIntegrationPlugin); Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin); #elif defined(QT_QPA_PLATFORM_COCOA) Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin); +#elif defined(QT_QPA_PLATFORM_ANDROID) +Q_IMPORT_PLUGIN(QAndroidPlatformIntegrationPlugin) #endif #endif From 0cb272419c69f4346be7d53846dfa53baf884519 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sun, 26 Jan 2025 19:47:23 +0000 Subject: [PATCH 16/16] merge bitcoin-core/gui#591: Add tests for `tableView` in `AddressBookPage` dialog --- src/qt/test/addressbooktests.cpp | 15 +++++++++++---- src/qt/walletmodel.cpp | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp index 3e0be6da11a69..fe623b8435eeb 100644 --- a/src/qt/test/addressbooktests.cpp +++ b/src/qt/test/addressbooktests.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -22,8 +23,9 @@ #include #include -#include #include +#include +#include namespace { @@ -114,14 +116,19 @@ void TestAddAddressesToSendBook(interfaces::Node& node) EditAddressDialog editAddressDialog(EditAddressDialog::NewSendingAddress); editAddressDialog.setModel(walletModel.getAddressTableModel()); + AddressBookPage address_book{AddressBookPage::ForEditing, AddressBookPage::SendingTab}; + address_book.setModel(walletModel.getAddressTableModel()); + auto table_view = address_book.findChild("tableView"); + QCOMPARE(table_view->model()->rowCount(), 1); + EditAddressAndSubmit( &editAddressDialog, QString("uhoh"), preexisting_r_address, QString( "Address \"%1\" already exists as a receiving address with label " "\"%2\" and so cannot be added as a sending address." ).arg(preexisting_r_address).arg(r_label)); - check_addbook_size(2); + QCOMPARE(table_view->model()->rowCount(), 1); EditAddressAndSubmit( &editAddressDialog, QString("uhoh, different"), preexisting_s_address, @@ -129,15 +136,15 @@ void TestAddAddressesToSendBook(interfaces::Node& node) "The entered address \"%1\" is already in the address book with " "label \"%2\"." ).arg(preexisting_s_address).arg(s_label)); - check_addbook_size(2); + QCOMPARE(table_view->model()->rowCount(), 1); // Submit a new address which should add successfully - we expect the // warning message to be blank. EditAddressAndSubmit( &editAddressDialog, QString("new"), new_address, QString("")); - check_addbook_size(3); + QCOMPARE(table_view->model()->rowCount(), 2); } } // namespace diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index fd1f2c2d0696e..e89f4e363d9b0 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -441,7 +441,7 @@ static void NotifyAddressBookChanged(WalletModel *walletmodel, QString strPurpose = QString::fromStdString(purpose); qDebug() << "NotifyAddressBookChanged: " + strAddress + " " + strLabel + " isMine=" + QString::number(isMine) + " purpose=" + strPurpose + " status=" + QString::number(status); - bool invoked = QMetaObject::invokeMethod(walletmodel, "updateAddressBook", Qt::QueuedConnection, + bool invoked = QMetaObject::invokeMethod(walletmodel, "updateAddressBook", Q_ARG(QString, strAddress), Q_ARG(QString, strLabel), Q_ARG(bool, isMine),