v31.1-roots.1 · Commit 5: [PATCH 05/17] feat(net): preserve Roots peer controls and health view on v31
src/qt/test/apptests.cpp
Before: src/qt/test/apptests.cpp · After: src/qt/test/apptests.cpp
File metadata
index 752c877381031e47a453ae93f38126fe929da2a0..9ec8aa0a19d79762ef432f8476795808a0aed605 100644
Download original patch · Release notes
Diff rows 1–192 of 192. Line numbers refer to the original before/after files.
+ Added− RemovedUnmarked lines are context.
Scroll the code area horizontally to read long lines. The two line-number columns refer to before and after.
src/qt/test/apptests.cpp
CHANGED
|
@@ -11,8 +11,23 @@
|
|
|
11
11
|
#include <qt/bitcoingui.h>
|
|
12
12
|
#include <qt/networkstyle.h>
|
|
13
13
|
#include <qt/rpcconsole.h>
|
|
14
|
+
#include <qt/peertablemodel.h>
|
|
15
|
+
#include <QLabel>
|
|
16
|
+
#include <QTableView>
|
|
17
|
+
#include <QSortFilterProxyModel>
|
|
14
18
|
#include <test/util/setup_common.h>
|
|
15
19
|
#include <validation.h>
|
|
20
|
+
#include <interfaces/node.h>
|
|
21
|
+
#include <univalue.h>
|
|
22
|
+
#include <QDebug>
|
|
23
|
+
#include <QDir>
|
|
24
|
+
#include <QTabWidget>
|
|
25
|
+
#include <QSplitter>
|
|
26
|
+
#include <QScrollArea>
|
|
27
|
+
#include <QScrollBar>
|
|
28
|
+
#include <QItemSelectionModel>
|
|
29
|
+
#include <future>
|
|
30
|
+
#include <thread>
|
|
16
31
|
|
|
17
32
|
#include <QAction>
|
|
18
33
|
#include <QLineEdit>
|
|
@@ -49,6 +64,149 @@ void TestRpcCommand(RPCConsole* console)
|
|
|
49
64
|
const QString pattern = QStringLiteral("\"chain\": \"(\\w+)\"");
|
|
50
65
|
QCOMPARE(FindInConsole(output, pattern), QString("regtest"));
|
|
51
66
|
}
|
|
67
|
+
void TestPeerRefreshState(RPCConsole* console)
|
|
68
|
+
{
|
|
69
|
+
auto* peer_widget = console->findChild<QTableView*>("peerWidget");
|
|
70
|
+
QVERIFY(peer_widget);
|
|
71
|
+
auto* peer_proxy = qobject_cast<QSortFilterProxyModel*>(peer_widget->model());
|
|
72
|
+
QVERIFY(peer_proxy);
|
|
73
|
+
auto* peer_model = qobject_cast<PeerTableModel*>(peer_proxy->sourceModel());
|
|
74
|
+
QVERIFY(peer_model);
|
|
75
|
+
QCOMPARE(peer_model->rowCount(), 0);
|
|
76
|
+
|
|
77
|
+
QSignalSpy data_changed_spy(peer_model, &QAbstractItemModel::dataChanged);
|
|
78
|
+
QVERIFY(data_changed_spy.isValid());
|
|
79
|
+
peer_model->refresh();
|
|
80
|
+
QCOMPARE(data_changed_spy.count(), 0);
|
|
81
|
+
|
|
82
|
+
const auto set_label = [console](const char* name) {
|
|
83
|
+
QLabel* label = console->findChild<QLabel*>(name);
|
|
84
|
+
if (label) label->setText(QStringLiteral("stale"));
|
|
85
|
+
return label;
|
|
86
|
+
};
|
|
87
|
+
QLabel* time_offset = set_label("timeoffset");
|
|
88
|
+
QLabel* services = set_label("peerServices");
|
|
89
|
+
QLabel* sync_height = set_label("peerSyncHeight");
|
|
90
|
+
QLabel* common_height = set_label("peerCommonHeight");
|
|
91
|
+
QLabel* starting_height = set_label("peerHeight");
|
|
92
|
+
QLabel* ping_wait = set_label("peerPingWait");
|
|
93
|
+
QLabel* addr_relay = set_label("peerAddrRelayEnabled");
|
|
94
|
+
QLabel* addr_processed = set_label("peerAddrProcessed");
|
|
95
|
+
QLabel* addr_rate_limited = set_label("peerAddrRateLimited");
|
|
96
|
+
QLabel* relay_txs = set_label("peerRelayTxes");
|
|
97
|
+
|
|
98
|
+
QVERIFY(time_offset);
|
|
99
|
+
QVERIFY(services);
|
|
100
|
+
QVERIFY(sync_height);
|
|
101
|
+
QVERIFY(common_height);
|
|
102
|
+
QVERIFY(starting_height);
|
|
103
|
+
QVERIFY(ping_wait);
|
|
104
|
+
QVERIFY(addr_relay);
|
|
105
|
+
QVERIFY(addr_processed);
|
|
106
|
+
QVERIFY(addr_rate_limited);
|
|
107
|
+
QVERIFY(relay_txs);
|
|
108
|
+
QVERIFY(QMetaObject::invokeMethod(console, "resetPeerStateStats"));
|
|
109
|
+
QCOMPARE(time_offset->text(), QStringLiteral("N/A"));
|
|
110
|
+
QCOMPARE(services->text(), QStringLiteral("N/A"));
|
|
111
|
+
QCOMPARE(sync_height->text(), QStringLiteral("Unknown"));
|
|
112
|
+
QCOMPARE(common_height->text(), QStringLiteral("Unknown"));
|
|
113
|
+
QCOMPARE(starting_height->text(), QStringLiteral("Unknown"));
|
|
114
|
+
QCOMPARE(ping_wait->text(), QStringLiteral("N/A"));
|
|
115
|
+
QCOMPARE(addr_relay->text(), QStringLiteral("N/A"));
|
|
116
|
+
QCOMPARE(addr_processed->text(), QStringLiteral("N/A"));
|
|
117
|
+
QCOMPARE(addr_rate_limited->text(), QStringLiteral("N/A"));
|
|
118
|
+
QCOMPARE(relay_txs->text(), QStringLiteral("N/A"));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Optional runtime evidence profile. Peers are real regtest nodes supplied by
|
|
122
|
+
// the caller; unavailable state is obtained through the production try-lock.
|
|
123
|
+
void TestLivePeerView(RPCConsole* console, interfaces::Node& node)
|
|
124
|
+
{
|
|
125
|
+
const auto endpoints{qEnvironmentVariable("ROOTS_PEER_ENDPOINTS").split(',', Qt::SkipEmptyParts)};
|
|
126
|
+
if (endpoints.isEmpty()) return;
|
|
127
|
+
QVERIFY(endpoints.size() >= 2);
|
|
128
|
+
auto* table = console->findChild<QTableView*>("peerWidget");
|
|
129
|
+
auto* tabs = console->findChild<QTabWidget*>("tabWidget");
|
|
130
|
+
QVERIFY(table && tabs);
|
|
131
|
+
auto* proxy = qobject_cast<QSortFilterProxyModel*>(table->model());
|
|
132
|
+
QVERIFY(proxy);
|
|
133
|
+
auto* model = qobject_cast<PeerTableModel*>(proxy->sourceModel());
|
|
134
|
+
QVERIFY(model);
|
|
135
|
+
model->stopAutoRefresh();
|
|
136
|
+
for (const auto& endpoint : endpoints) {
|
|
137
|
+
UniValue params{UniValue::VARR};
|
|
138
|
+
params.push_back(endpoint.toStdString());
|
|
139
|
+
params.push_back("onetry");
|
|
140
|
+
node.executeRpc("addnode", params, "");
|
|
141
|
+
}
|
|
142
|
+
QTRY_VERIFY_WITH_TIMEOUT((model->refresh(), model->rowCount() == endpoints.size()), 10000);
|
|
143
|
+
QTRY_VERIFY_WITH_TIMEOUT((model->refresh(), model->index(0, 0).data(PeerTableModel::StatsRole).value<CNodeCombinedStats*>()->nodeStats.nVersion > 0), 10000);
|
|
144
|
+
const auto size{qEnvironmentVariable("ROOTS_PEER_CAPTURE_SIZE", "1280x800").split('x')};
|
|
145
|
+
QCOMPARE(size.size(), 2);
|
|
146
|
+
console->resize(size[0].toInt(), size[1].toInt());
|
|
147
|
+
tabs->setCurrentIndex(3); // Native Peers tab.
|
|
148
|
+
console->show();
|
|
149
|
+
QTest::qWait(100);
|
|
150
|
+
QCOMPARE(console->size(), QSize(size[0].toInt(), size[1].toInt()));
|
|
151
|
+
qInfo() << "Live peer capture:" << console->size() << "font points" << table->font().pointSizeF() << "device pixel ratio" << console->devicePixelRatioF();
|
|
152
|
+
const auto capture_dir{qEnvironmentVariable("ROOTS_PEER_CAPTURE_DIR")};
|
|
153
|
+
const auto capture = [&](const QString& name) {
|
|
154
|
+
if (capture_dir.isEmpty()) return true;
|
|
155
|
+
return QDir().mkpath(capture_dir) && console->grab().save(capture_dir + '/' + name + ".png");
|
|
156
|
+
};
|
|
157
|
+
table->clearSelection();
|
|
158
|
+
QVERIFY(capture("table"));
|
|
159
|
+
table->selectRow(0);
|
|
160
|
+
const auto selected_id{table->selectionModel()->selectedRows().first().data().toLongLong()};
|
|
161
|
+
table->sortByColumn(PeerTableModel::Received, Qt::DescendingOrder);
|
|
162
|
+
model->refresh();
|
|
163
|
+
QCOMPARE(table->selectionModel()->selectedRows().size(), 1);
|
|
164
|
+
QCOMPARE(table->selectionModel()->selectedRows().first().data().toLongLong(), selected_id);
|
|
165
|
+
QVERIFY(console->findChild<QLabel*>("peerSubversion")->text() != QStringLiteral("N/A"));
|
|
166
|
+
// Exercise the native resizable splitter so unavailable values are legible
|
|
167
|
+
// at 150% font scale as well as in the full-width table capture.
|
|
168
|
+
console->findChild<QSplitter*>("splitter")->setSizes({console->width() / 2, console->width() / 2});
|
|
169
|
+
QTest::qWait(100); // Let the shown detail pane receive its layout geometry.
|
|
170
|
+
QVERIFY(console->findChild<QWidget*>("peersTabRightPanel")->isVisible());
|
|
171
|
+
QVERIFY(capture("detail"));
|
|
172
|
+
|
|
173
|
+
std::promise<void> locked, release;
|
|
174
|
+
auto released = release.get_future();
|
|
175
|
+
std::thread blocker{[&] {
|
|
176
|
+
LOCK(cs_main);
|
|
177
|
+
locked.set_value();
|
|
178
|
+
released.wait();
|
|
179
|
+
}};
|
|
180
|
+
locked.get_future().wait();
|
|
181
|
+
model->refresh();
|
|
182
|
+
const auto previous_row{table->selectionModel()->selectedRows().first().row()};
|
|
183
|
+
table->selectRow(previous_row == 0 ? 1 : 0);
|
|
184
|
+
const bool unavailable{!table->selectionModel()->selectedRows().first().data(PeerTableModel::StatsRole).value<CNodeCombinedStats*>()->fNodeStateStatsAvailable};
|
|
185
|
+
const bool cleared{console->findChild<QLabel*>("peerServices")->text() == QStringLiteral("N/A") &&
|
|
186
|
+
console->findChild<QLabel*>("peerSyncHeight")->text() == QStringLiteral("Unknown") &&
|
|
187
|
+
console->findChild<QLabel*>("peerHeight")->text() == QStringLiteral("Unknown") &&
|
|
188
|
+
console->findChild<QLabel*>("timeoffset")->text() == QStringLiteral("N/A")};
|
|
189
|
+
const bool captured{capture("unavailable")};
|
|
190
|
+
auto* scroll = console->findChild<QScrollArea*>("scrollArea");
|
|
191
|
+
scroll->verticalScrollBar()->setValue(scroll->verticalScrollBar()->maximum());
|
|
192
|
+
const bool captured_bottom{capture("unavailable-bottom")};
|
|
193
|
+
scroll->verticalScrollBar()->setValue(0);
|
|
194
|
+
release.set_value();
|
|
195
|
+
blocker.join();
|
|
196
|
+
QVERIFY(unavailable);
|
|
197
|
+
QVERIFY(cleared);
|
|
198
|
+
QVERIFY(captured);
|
|
199
|
+
QVERIFY(captured_bottom);
|
|
200
|
+
model->refresh();
|
|
201
|
+
QVERIFY(console->findChild<QLabel*>("peerServices")->text() != QStringLiteral("N/A"));
|
|
202
|
+
// Removing the selected peer must not leave its detail pane visible.
|
|
203
|
+
UniValue params{UniValue::VARR};
|
|
204
|
+
params.push_back("");
|
|
205
|
+
params.push_back(table->selectionModel()->selectedRows().first().data().toLongLong());
|
|
206
|
+
node.executeRpc("disconnectnode", params, "");
|
|
207
|
+
QTRY_VERIFY_WITH_TIMEOUT((model->refresh(), model->rowCount() == endpoints.size() - 1), 10000);
|
|
208
|
+
QVERIFY(table->selectionModel()->selectedRows().isEmpty());
|
|
209
|
+
}
|
|
52
210
|
} // namespace
|
|
53
211
|
|
|
54
212
|
//! Entry point for BitcoinApplication tests.
|
|
@@ -67,6 +225,12 @@ void AppTests::appTests()
|
|
|
67
225
|
#endif
|
|
68
226
|
|
|
69
227
|
qRegisterMetaType<interfaces::BlockAndHeaderTipInfo>("interfaces::BlockAndHeaderTipInfo");
|
|
228
|
+
const auto font_scale{qEnvironmentVariable("ROOTS_PEER_FONT_SCALE", "1.0").toDouble()};
|
|
229
|
+
if (font_scale != 1.0) {
|
|
230
|
+
auto font = m_app.font();
|
|
231
|
+
font.setPointSizeF(font.pointSizeF() * font_scale);
|
|
232
|
+
m_app.setFont(font);
|
|
233
|
+
}
|
|
70
234
|
m_app.parameterSetup();
|
|
71
235
|
QVERIFY(m_app.createOptionsModel(/*resetSettings=*/true));
|
|
72
236
|
QScopedPointer<const NetworkStyle> style(NetworkStyle::instantiate(Params().GetChainType()));
|
|
@@ -99,6 +263,8 @@ void AppTests::consoleTests(RPCConsole* console)
|
|
|
99
263
|
{
|
|
100
264
|
HandleCallback callback{"consoleTests", *this};
|
|
101
265
|
TestRpcCommand(console);
|
|
266
|
+
TestPeerRefreshState(console);
|
|
267
|
+
TestLivePeerView(console, m_app.node());
|
|
102
268
|
}
|
|
103
269
|
|
|
104
270
|
//! Destructor to shut down after the last expected callback completes.
|