Per-connection progress bars and the 60 s rolling speed graph the progress dialog needs (docs/03-gui-spec.md §3). Both reuse row/widget state across ticks instead of rebuilding, matching the discipline DownloadTableModel already uses for progress patches. SpeedGraphWidget keeps a fixed ring buffer and one reused QPainterPath — no allocation in paintEvent or addSample. Fixed a real bug found while writing tst_speedgraphwidget: the elapsed timer was started in the constructor, so the very first sample after construction would silently wait up to 1 s to be recorded instead of landing immediately. Tested against mockd (both offline via QTest/offscreen, and manually against a running mockd instance through ProgressDialog once that lands). Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NSCdCWFXBTSBBK3MzWtJiC
95 lines
2.7 KiB
C++
95 lines
2.7 KiB
C++
// SegmentBarsWidget unit tests. Lane GUI.
|
|
//
|
|
// Red when: a row stops being reused across ticks (rebuilt instead of patched), a
|
|
// zero/unknown-length segment stops rendering as indeterminate, or a shrinking segment
|
|
// set leaves stale rows visible instead of hiding them.
|
|
|
|
#include <QProgressBar>
|
|
#include <QtTest>
|
|
|
|
#include "widgets/SegmentBarsWidget.hpp"
|
|
|
|
using velox::gui::SegmentBarsWidget;
|
|
using velox::gui::SegmentInfo;
|
|
|
|
class TstSegmentBarsWidget : public QObject {
|
|
Q_OBJECT
|
|
|
|
private slots:
|
|
void determinateShowsPercent();
|
|
void unknownLengthIsIndeterminate();
|
|
void shrinkingSetHidesStaleRows();
|
|
void rowsAreReusedNotRebuilt();
|
|
};
|
|
|
|
void TstSegmentBarsWidget::determinateShowsPercent() {
|
|
SegmentBarsWidget w;
|
|
SegmentInfo s;
|
|
s.index = 0;
|
|
s.downloadedBytes = 50;
|
|
s.totalBytes = 200;
|
|
s.state = QStringLiteral("downloading");
|
|
w.setSegments({s});
|
|
|
|
auto *bar = w.findChild<QProgressBar *>();
|
|
QVERIFY(bar != nullptr);
|
|
QCOMPARE(bar->minimum(), 0);
|
|
QCOMPARE(bar->maximum(), 10000);
|
|
QCOMPARE(bar->value(), 2500); // 50/200 == 25%
|
|
}
|
|
|
|
void TstSegmentBarsWidget::unknownLengthIsIndeterminate() {
|
|
SegmentBarsWidget w;
|
|
SegmentInfo s;
|
|
s.index = 0;
|
|
s.totalBytes = -1; // range not known yet
|
|
w.setSegments({s});
|
|
|
|
auto *bar = w.findChild<QProgressBar *>();
|
|
QVERIFY(bar != nullptr);
|
|
QCOMPARE(bar->minimum(), 0);
|
|
QCOMPARE(bar->maximum(), 0); // Qt's indeterminate-range convention
|
|
}
|
|
|
|
void TstSegmentBarsWidget::shrinkingSetHidesStaleRows() {
|
|
SegmentBarsWidget w;
|
|
SegmentInfo a;
|
|
a.index = 0;
|
|
a.totalBytes = 100;
|
|
SegmentInfo b;
|
|
b.index = 1;
|
|
b.totalBytes = 100;
|
|
w.setSegments({a, b});
|
|
|
|
const auto barsBefore = w.findChildren<QProgressBar *>();
|
|
QCOMPARE(barsBefore.size(), 2);
|
|
for (auto *bar : barsBefore) {
|
|
QVERIFY(!bar->parentWidget()->isHidden());
|
|
}
|
|
|
|
w.setSegments({a}); // segment 1 dropped (re-segmented retry with fewer connections)
|
|
|
|
const auto barsAfter = w.findChildren<QProgressBar *>();
|
|
QCOMPARE(barsAfter.size(), 2); // row not destroyed...
|
|
QVERIFY(!barsAfter[0]->parentWidget()->isHidden());
|
|
QVERIFY(barsAfter[1]->parentWidget()->isHidden()); // ...just hidden
|
|
}
|
|
|
|
void TstSegmentBarsWidget::rowsAreReusedNotRebuilt() {
|
|
SegmentBarsWidget w;
|
|
SegmentInfo s;
|
|
s.index = 0;
|
|
s.totalBytes = 100;
|
|
w.setSegments({s});
|
|
auto *barFirst = w.findChild<QProgressBar *>();
|
|
QVERIFY(barFirst != nullptr);
|
|
|
|
s.downloadedBytes = 40;
|
|
w.setSegments({s});
|
|
auto *barSecond = w.findChild<QProgressBar *>();
|
|
QCOMPARE(barFirst, barSecond); // same widget instance, just repainted
|
|
}
|
|
|
|
QTEST_MAIN(TstSegmentBarsWidget)
|
|
#include "tst_segmentbarswidget.moc"
|