mirror of https://github.com/qTox/qTox.git
Browse Source
Mick Sayson (3): fix(filetransfer): Fix UI inconsistencies with pause/resume refactor(filetransfer): Move file transfer progress into ToxFile feat(filesform): Add in progress transfers to files formreviewable/pr6427/r1
29 changed files with 1771 additions and 230 deletions
@ -1,93 +0,0 @@
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
Copyright © 2018-2019 by The qTox Project Contributors |
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox. |
||||
|
||||
qTox is libre software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
qTox is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
|
||||
#include "toxfileprogress.h" |
||||
|
||||
#include "src/core/toxfile.h" |
||||
|
||||
bool ToxFileProgress::needsUpdate() const |
||||
{ |
||||
QTime now = QTime::currentTime(); |
||||
qint64 dt = lastTick.msecsTo(now); // ms
|
||||
|
||||
if (dt < 1000) { |
||||
return false; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
void ToxFileProgress::addSample(ToxFile const& file) |
||||
{ |
||||
QTime now = QTime::currentTime(); |
||||
qint64 dt = lastTick.msecsTo(now); // ms
|
||||
|
||||
if (dt < 1000) { |
||||
return; |
||||
} |
||||
|
||||
// ETA, speed
|
||||
qreal deltaSecs = dt / 1000.0; |
||||
|
||||
// (can't use ::abs or ::max on unsigned types substraction, they'd just overflow)
|
||||
quint64 deltaBytes = file.bytesSent > lastBytesSent ? file.bytesSent - lastBytesSent |
||||
: lastBytesSent - file.bytesSent; |
||||
qreal bytesPerSec = static_cast<int>(static_cast<qreal>(deltaBytes) / deltaSecs); |
||||
|
||||
// Update member variables
|
||||
meanIndex = meanIndex % TRANSFER_ROLLING_AVG_COUNT; |
||||
meanData[meanIndex++] = bytesPerSec; |
||||
|
||||
double meanBytesPerSec = 0.0; |
||||
for (size_t i = 0; i < TRANSFER_ROLLING_AVG_COUNT; ++i) { |
||||
meanBytesPerSec += meanData[i]; |
||||
} |
||||
meanBytesPerSec /= static_cast<qreal>(TRANSFER_ROLLING_AVG_COUNT); |
||||
|
||||
lastTick = now; |
||||
|
||||
progress = static_cast<double>(file.bytesSent) / static_cast<double>(file.filesize); |
||||
speedBytesPerSecond = meanBytesPerSec; |
||||
timeLeftSeconds = (file.filesize - file.bytesSent) / getSpeed(); |
||||
|
||||
lastBytesSent = file.bytesSent; |
||||
} |
||||
|
||||
void ToxFileProgress::resetSpeed() |
||||
{ |
||||
meanIndex = 0; |
||||
for (auto& item : meanData) { |
||||
item = 0; |
||||
} |
||||
} |
||||
|
||||
double ToxFileProgress::getProgress() const |
||||
{ |
||||
return progress; |
||||
} |
||||
|
||||
double ToxFileProgress::getSpeed() const |
||||
{ |
||||
return speedBytesPerSecond; |
||||
} |
||||
|
||||
double ToxFileProgress::getTimeLeftSeconds() const |
||||
{ |
||||
return timeLeftSeconds; |
||||
} |
@ -0,0 +1,137 @@
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
Copyright © 2021 by The qTox Project Contributors |
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox. |
||||
|
||||
qTox is libre software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
qTox is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
|
||||
#include "toxfileprogress.h" |
||||
|
||||
#include <limits> |
||||
|
||||
ToxFileProgress::ToxFileProgress(uint64_t filesize, int samplePeriodMs) |
||||
: filesize(filesize) |
||||
, samplePeriodMs(samplePeriodMs) |
||||
{ |
||||
if (samplePeriodMs < 0) { |
||||
qWarning("Invalid sample rate, healing to 1000ms"); |
||||
this->samplePeriodMs = 1000; |
||||
} |
||||
} |
||||
|
||||
QTime ToxFileProgress::lastSampleTime() const |
||||
{ |
||||
return samples[activeSample].timestamp; |
||||
} |
||||
|
||||
bool ToxFileProgress::addSample(uint64_t bytesSent, QTime now) |
||||
{ |
||||
if (bytesSent > filesize) { |
||||
qWarning("Bytes sent exceeds file size, ignoring sample"); |
||||
return false; |
||||
} |
||||
|
||||
auto* active = &samples[activeSample]; |
||||
auto* inactive = &samples[!activeSample]; |
||||
|
||||
if (bytesSent < active->bytesSent || bytesSent < inactive->bytesSent) { |
||||
qWarning("Bytes sent has decreased since last sample, ignoring sample"); |
||||
return false; |
||||
} |
||||
|
||||
if (now < active->timestamp || now < inactive->timestamp) { |
||||
qWarning("Sample time has gone backwards, clearing progress buffer"); |
||||
resetSpeed(); |
||||
} |
||||
|
||||
// Ensure both samples are initialized
|
||||
if (inactive->timestamp == QTime()) { |
||||
inactive->bytesSent = bytesSent; |
||||
inactive->timestamp = now; |
||||
} |
||||
|
||||
if (active->timestamp == QTime()) { |
||||
active->bytesSent = bytesSent; |
||||
active->timestamp = now; |
||||
} |
||||
|
||||
if (active->timestamp.msecsTo(now) >= samplePeriodMs) { |
||||
// Swap samples and set the newly active sample
|
||||
activeSample = !activeSample; |
||||
std::swap(active, inactive); |
||||
} |
||||
|
||||
active->bytesSent = bytesSent; |
||||
active->timestamp = now; |
||||
|
||||
return true; |
||||
} |
||||
|
||||
void ToxFileProgress::resetSpeed() |
||||
{ |
||||
for (auto& sample : samples) { |
||||
sample.timestamp = QTime(); |
||||
} |
||||
} |
||||
|
||||
uint64_t ToxFileProgress::getBytesSent() const |
||||
{ |
||||
return samples[activeSample].bytesSent; |
||||
} |
||||
|
||||
double ToxFileProgress::getProgress() const |
||||
{ |
||||
return double(samples[activeSample].bytesSent) / filesize; |
||||
} |
||||
|
||||
double ToxFileProgress::getSpeed() const |
||||
{ |
||||
if (samples.size() > 0 |
||||
&& samples[activeSample].bytesSent == filesize) { |
||||
return 0.0f; |
||||
} |
||||
|
||||
const auto sampleTimeInvalid = [](const Sample& sample) { |
||||
return sample.timestamp == QTime(); |
||||
}; |
||||
|
||||
if (std::any_of(samples.cbegin(), samples.cend(), sampleTimeInvalid)) { |
||||
return 0.0f; |
||||
} |
||||
|
||||
if (samples[0].timestamp == samples[1].timestamp) { |
||||
return 0.0f; |
||||
} |
||||
|
||||
const auto& active = samples[activeSample]; |
||||
const auto& inactive = samples[!activeSample]; |
||||
|
||||
return (active.bytesSent - inactive.bytesSent) / double(inactive.timestamp.msecsTo(active.timestamp)) * 1000.0; |
||||
} |
||||
|
||||
double ToxFileProgress::getTimeLeftSeconds() const |
||||
{ |
||||
if (samples.size() > 0 |
||||
&& samples[activeSample].bytesSent == filesize) { |
||||
return 0; |
||||
} |
||||
|
||||
const auto speed = getSpeed(); |
||||
if (speed == 0.0f) { |
||||
return std::numeric_limits<float>::infinity(); |
||||
} |
||||
|
||||
return double(filesize - samples[activeSample].bytesSent) / getSpeed(); |
||||
} |
@ -0,0 +1,341 @@
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
Copyright © 2021 by The qTox Project Contributors |
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox. |
||||
|
||||
qTox is libre software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
qTox is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
|
||||
#include "src/core/toxfileprogress.h" |
||||
|
||||
#include <QTest> |
||||
#include <limits> |
||||
|
||||
class TestFileProgress : public QObject |
||||
{ |
||||
Q_OBJECT |
||||
private slots: |
||||
void testSpeed(); |
||||
void testSpeedReset(); |
||||
void testDiscardedSample(); |
||||
void testProgress(); |
||||
void testRemainingTime(); |
||||
void testBytesSentPersistence(); |
||||
void testFileSizePersistence(); |
||||
void testNoSamples(); |
||||
void testSpeedUnevenIntervals(); |
||||
void testDefaultTimeLessThanNow(); |
||||
void testTimeChange(); |
||||
void testFinishedSpeed(); |
||||
void testSamplePeriod(); |
||||
void testInvalidSamplePeriod(); |
||||
}; |
||||
|
||||
/**
|
||||
* @brief Test that our speeds are sane while we're on our first few samples |
||||
*/ |
||||
void TestFileProgress::testSpeed() |
||||
{ |
||||
auto progress = ToxFileProgress(100, 1000); |
||||
|
||||
auto nextSampleTime = QTime(1, 0, 0); |
||||
QVERIFY(progress.addSample(0, nextSampleTime)); |
||||
// 1 sample has no speed
|
||||
QCOMPARE(progress.getSpeed(), 0.0); |
||||
|
||||
// Swap buffers. Time should be valid
|
||||
nextSampleTime = nextSampleTime.addMSecs(500); |
||||
// 10 bytes over 0.5s
|
||||
QVERIFY(progress.addSample(10, nextSampleTime)); |
||||
QCOMPARE(progress.getSpeed(), 20.0); |
||||
|
||||
// This should evict the first sample, so our time should be relative to the
|
||||
// first 10 bytes
|
||||
nextSampleTime = nextSampleTime.addMSecs(1000); |
||||
QVERIFY(progress.addSample(20, nextSampleTime)); |
||||
// 10 bytes over 1s
|
||||
QCOMPARE(progress.getSpeed(), 10.0); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Test that resetting our speed puts us back into a sane default state |
||||
*/ |
||||
void TestFileProgress::testSpeedReset() |
||||
{ |
||||
auto progress = ToxFileProgress(100, 1000); |
||||
|
||||
auto nextSampleTime = QTime(1, 0, 0); |
||||
QVERIFY(progress.addSample(0, nextSampleTime)); |
||||
|
||||
// Push enough samples that all samples are initialized
|
||||
nextSampleTime = nextSampleTime.addMSecs(1000); |
||||
QVERIFY(progress.addSample(10, nextSampleTime)); |
||||
nextSampleTime = nextSampleTime.addMSecs(1000); |
||||
QVERIFY(progress.addSample(20, nextSampleTime)); |
||||
|
||||
QCOMPARE(progress.getSpeed(), 10.0); |
||||
|
||||
progress.resetSpeed(); |
||||
QCOMPARE(progress.getSpeed(), 0.0); |
||||
QCOMPARE(progress.lastSampleTime(), QTime()); |
||||
QCOMPARE(progress.getBytesSent(), uint64_t(20)); |
||||
QCOMPARE(progress.getProgress(), 0.2); |
||||
|
||||
// Ensure that pushing new samples after reset works correectly
|
||||
nextSampleTime = nextSampleTime.addMSecs(1000); |
||||
QVERIFY(progress.addSample(30, nextSampleTime)); |
||||
|
||||
// 1 sample has no speed
|
||||
QCOMPARE(progress.getSpeed(), 0.0); |
||||
|
||||
nextSampleTime = nextSampleTime.addMSecs(1000); |
||||
QVERIFY(progress.addSample(40, nextSampleTime)); |
||||
QCOMPARE(progress.getSpeed(), 10.0); |
||||
} |
||||
|
||||
|
||||
/**
|
||||
* @brief Test that invalid samples are discarded |
||||
*/ |
||||
void TestFileProgress::testDiscardedSample() |
||||
{ |
||||
auto progress = ToxFileProgress(100, 1000); |
||||
|
||||
auto nextSampleTime = QTime(1, 0, 0); |
||||
QVERIFY(progress.addSample(0, nextSampleTime)); |
||||
|
||||
nextSampleTime = nextSampleTime.addMSecs(1000); |
||||
QVERIFY(progress.addSample(20, nextSampleTime)); |
||||
|
||||
nextSampleTime = nextSampleTime.addMSecs(1000); |
||||
|
||||
// Sample should be discarded because it's too large
|
||||
QVERIFY(!progress.addSample(300, nextSampleTime)); |
||||
QCOMPARE(progress.lastSampleTime(), QTime(1, 0, 1)); |
||||
|
||||
// Sample should be discarded because we're going backwards
|
||||
QVERIFY(!progress.addSample(10, nextSampleTime)); |
||||
QCOMPARE(progress.lastSampleTime(), QTime(1, 0, 1)); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Test that progress is reported correctly |
||||
*/ |
||||
void TestFileProgress::testProgress() |
||||
{ |
||||
auto progress = ToxFileProgress(100, 4000); |
||||
|
||||
auto nextSampleTime = QTime(1, 0, 0); |
||||
QVERIFY(progress.addSample(0, nextSampleTime)); |
||||
QCOMPARE(progress.getProgress(), 0.0); |
||||
|
||||
nextSampleTime = nextSampleTime.addMSecs(1000); |
||||
QVERIFY(progress.addSample(10, nextSampleTime)); |
||||
QCOMPARE(progress.getProgress(), 0.1); |
||||
|
||||
nextSampleTime = nextSampleTime.addMSecs(1000); |
||||
QVERIFY(progress.addSample(100, nextSampleTime)); |
||||
QCOMPARE(progress.getProgress(), 1.0); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Test that remaining time is predicted reasonably |
||||
*/ |
||||
void TestFileProgress::testRemainingTime() |
||||
{ |
||||
auto progress = ToxFileProgress(100, 2000); |
||||
|
||||
auto nextSampleTime = QTime(1, 0, 0); |
||||
QVERIFY(progress.addSample(0, nextSampleTime)); |
||||
|
||||
nextSampleTime = nextSampleTime.addMSecs(1000); |
||||
QVERIFY(progress.addSample(10, nextSampleTime)); |
||||
|
||||
// 10% over 1s, 90% should take 9 more seconds
|
||||
QCOMPARE(progress.getTimeLeftSeconds(), 9.0); |
||||
|
||||
nextSampleTime = nextSampleTime.addMSecs(10000); |
||||
QVERIFY(progress.addSample(100, nextSampleTime)); |
||||
// Even with a slow final sample, we should have 0 seconds remaining when we
|
||||
// are complete
|
||||
QCOMPARE(progress.getTimeLeftSeconds(), 0.0); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Test that the sent bytes keeps the last sample |
||||
*/ |
||||
void TestFileProgress::testBytesSentPersistence() |
||||
{ |
||||
auto progress = ToxFileProgress(100, 1000); |
||||
|
||||
auto nextSampleTime = QTime(1, 0, 0); |
||||
QVERIFY(progress.addSample(10, nextSampleTime)); |
||||
|
||||
// First sample
|
||||
QCOMPARE(progress.getBytesSent(), uint64_t(10)); |
||||
|
||||
nextSampleTime = nextSampleTime.addMSecs(1000); |
||||
QVERIFY(progress.addSample(20, nextSampleTime)); |
||||
// Second sample
|
||||
QCOMPARE(progress.getBytesSent(), uint64_t(20)); |
||||
|
||||
nextSampleTime = nextSampleTime.addMSecs(1000); |
||||
QVERIFY(progress.addSample(30, nextSampleTime)); |
||||
// After rollover
|
||||
QCOMPARE(progress.getBytesSent(), uint64_t(30)); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Check that the reported file size matches what was given |
||||
*/ |
||||
void TestFileProgress::testFileSizePersistence() |
||||
{ |
||||
auto progress = ToxFileProgress(33, 1000); |
||||
QCOMPARE(progress.getFileSize(), uint64_t(33)); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Test that we have sane stats when no samples have been added |
||||
*/ |
||||
void TestFileProgress::testNoSamples() |
||||
{ |
||||
auto progress = ToxFileProgress(100, 1000); |
||||
QCOMPARE(progress.getSpeed(), 0.0); |
||||
QVERIFY(progress.getTimeLeftSeconds() == std::numeric_limits<double>::infinity()); |
||||
QCOMPARE(progress.getProgress(), 0.0); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Test that statistics are being average over the entire range of time |
||||
* no matter the sample frequency |
||||
*/ |
||||
void TestFileProgress::testSpeedUnevenIntervals() |
||||
{ |
||||
auto progress = ToxFileProgress(100, 4000); |
||||
|
||||
auto nextSampleTime = QTime(1, 0, 0); |
||||
QVERIFY(progress.addSample(10, nextSampleTime)); |
||||
nextSampleTime = nextSampleTime.addMSecs(1000); |
||||
QVERIFY(progress.addSample(20, nextSampleTime)); |
||||
nextSampleTime = nextSampleTime.addMSecs(3000); |
||||
QVERIFY(progress.addSample(50, nextSampleTime)); |
||||
|
||||
// 10->50 over 4 seconds
|
||||
QCOMPARE(progress.getSpeed(), 10.0); |
||||
} |
||||
|
||||
void TestFileProgress::testDefaultTimeLessThanNow() |
||||
{ |
||||
auto progress = ToxFileProgress(100, 1000); |
||||
QVERIFY(progress.lastSampleTime() < QTime::currentTime()); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Test that changing the time resets the speed count. Note that it would |
||||
* be better to use the monotonic clock, but it's not trivial to get the |
||||
* monotonic clock from Qt's time API |
||||
*/ |
||||
void TestFileProgress::testTimeChange() |
||||
{ |
||||
auto progress = ToxFileProgress(100, 1000); |
||||
|
||||
auto nextSampleTime = QTime(1, 0, 0); |
||||
QVERIFY(progress.addSample(10, nextSampleTime)); |
||||
|
||||
nextSampleTime = QTime(0, 0, 0); |
||||
QVERIFY(progress.addSample(20, nextSampleTime)); |
||||
|
||||
QCOMPARE(progress.getSpeed(), 0.0); |
||||
QCOMPARE(progress.getProgress(), 0.2); |
||||
|
||||
nextSampleTime = QTime(0, 0, 1); |
||||
QVERIFY(progress.addSample(30, nextSampleTime)); |
||||
QCOMPARE(progress.getSpeed(), 10.0); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Test that when a file is complete it's speed is set to 0 |
||||
*/ |
||||
|
||||
void TestFileProgress::testFinishedSpeed() |
||||
{ |
||||
auto progress = ToxFileProgress(100, 1000); |
||||
|
||||
auto nextSampleTime = QTime(1, 0, 0); |
||||
QVERIFY(progress.addSample(10, nextSampleTime)); |
||||
|
||||
nextSampleTime = nextSampleTime.addMSecs(1000); |
||||
QVERIFY(progress.addSample(100, nextSampleTime)); |
||||
QCOMPARE(progress.getSpeed(), 0.0); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Test that we are averaged over the past period * samples time, and |
||||
* when we roll we lose one sample period of data |
||||
*/ |
||||
void TestFileProgress::testSamplePeriod() |
||||
{ |
||||
// No matter the number of samples, we should always be averaging over 2s
|
||||
auto progress = ToxFileProgress(100, 2000); |
||||
|
||||
auto nextSampleTime = QTime(1, 0, 0); |
||||
QVERIFY(progress.addSample(0, nextSampleTime)); |
||||
|
||||
nextSampleTime = QTime(1, 0, 0, 500); |
||||
QVERIFY(progress.addSample(10, nextSampleTime)); |
||||
|
||||
// Even with less than a sample period our speed and size should be updated
|
||||
QCOMPARE(progress.getSpeed(), 20.0); |
||||
QCOMPARE(progress.getBytesSent(), uint64_t(10)); |
||||
|
||||
// Add a new sample at 1s, this should replace the previous sample
|
||||
nextSampleTime = QTime(1, 0, 1); |
||||
QVERIFY(progress.addSample(30, nextSampleTime)); |
||||
QCOMPARE(progress.getSpeed(), 30.0); |
||||
QCOMPARE(progress.getBytesSent(), uint64_t(30)); |
||||
|
||||
// Add a new sample at 2s, our time should still be relative to 0
|
||||
nextSampleTime = QTime(1, 0, 2); |
||||
QVERIFY(progress.addSample(50, nextSampleTime)); |
||||
// 50 - 0 over 2s
|
||||
QCOMPARE(progress.getSpeed(), 25.0); |
||||
QCOMPARE(progress.getBytesSent(), uint64_t(50)); |
||||
} |
||||
|
||||
void TestFileProgress::testInvalidSamplePeriod() |
||||
{ |
||||
auto progress = ToxFileProgress(100, -1); |
||||
|
||||
// Sample period should be healed to 1000
|
||||
auto nextSampleTime = QTime(1, 0, 0); |
||||
QVERIFY(progress.addSample(0, nextSampleTime)); |
||||
|
||||
nextSampleTime = QTime(1, 0, 0, 500); |
||||
QVERIFY(progress.addSample(10, nextSampleTime)); |
||||
QCOMPARE(progress.getSpeed(), 20.0); |
||||
|
||||
// Second sample should be removed and we should average over the full
|
||||
// second
|
||||
nextSampleTime = QTime(1, 0, 1); |
||||
QVERIFY(progress.addSample(30, nextSampleTime)); |
||||
QCOMPARE(progress.getSpeed(), 30.0); |
||||
|
||||
// First sample should be evicted and we should have an updated speed
|
||||
nextSampleTime = QTime(1, 0, 2); |
||||
QVERIFY(progress.addSample(90, nextSampleTime)); |
||||
QCOMPARE(progress.getSpeed(), 60.0); |
||||
} |
||||
|
||||
QTEST_GUILESS_MAIN(TestFileProgress) |
||||
#include "fileprogress_test.moc" |
@ -0,0 +1,282 @@
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
Copyright © 2021 by The qTox Project Contributors |
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox. |
||||
|
||||
qTox is libre software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
qTox is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
|
||||
#include "src/widget/form/filesform.h" |
||||
#include "src/friendlist.h" |
||||
#include "src/model/friend.h" |
||||
|
||||
#include <QTest> |
||||
#include <limits> |
||||
|
||||
class TestFileTransferList : public QObject |
||||
{ |
||||
Q_OBJECT |
||||
private slots: |
||||
|
||||
void testFileTransferListConversion(); |
||||
void testEditorActionConversion(); |
||||
|
||||
void testFileName(); |
||||
// NOTE: Testing contact return requires a lookup in FriendList which goes
|
||||
// down a large dependency chain that is not linked to this test
|
||||
// void testContact();
|
||||
void testProgress(); |
||||
void testSize(); |
||||
void testSpeed(); |
||||
void testStatus(); |
||||
void testControl(); |
||||
void testAvatarIgnored(); |
||||
void testMultipleFiles(); |
||||
void testFileRemoval(); |
||||
}; |
||||
|
||||
using namespace FileTransferList; |
||||
|
||||
void TestFileTransferList::testFileTransferListConversion() |
||||
{ |
||||
Model model; |
||||
for (int i = 0; i < model.columnCount(); ++i) { |
||||
QVERIFY(toFileTransferListColumn(i) != Column::invalid); |
||||
} |
||||
QCOMPARE(toFileTransferListColumn(100), Column::invalid); |
||||
} |
||||
|
||||
void TestFileTransferList::testEditorActionConversion() |
||||
{ |
||||
QCOMPARE(toEditorAction(static_cast<int>(EditorAction::pause)), EditorAction::pause); |
||||
QCOMPARE(toEditorAction(static_cast<int>(EditorAction::cancel)), EditorAction::cancel); |
||||
QCOMPARE(toEditorAction(55), EditorAction::invalid); |
||||
} |
||||
|
||||
void TestFileTransferList::testFileName() |
||||
{ |
||||
Model model; |
||||
|
||||
ToxFile file; |
||||
file.fileKind = TOX_FILE_KIND_DATA; |
||||
file.fileName = "Test"; |
||||
model.onFileUpdated(file); |
||||
|
||||
const auto idx = model.index(0, static_cast<int>(Column::fileName)); |
||||
const auto fileName = idx.data(); |
||||
|
||||
QCOMPARE(fileName.toString(), QString("Test")); |
||||
} |
||||
|
||||
void TestFileTransferList::testProgress() |
||||
{ |
||||
Model model; |
||||
|
||||
ToxFile file(0, 0, "", "", 1000, ToxFile::FileDirection::SENDING); |
||||
file.progress.addSample(100, QTime(1, 0, 0)); |
||||
model.onFileUpdated(file); |
||||
|
||||
const auto idx = model.index(0, static_cast<int>(Column::progress)); |
||||
const auto progress = idx.data(); |
||||
|
||||
// Progress should be in percent units, 100/1000 == 10
|
||||
QCOMPARE(progress.toFloat(), 10.0f); |
||||
} |
||||
|
||||
void TestFileTransferList::testSize() |
||||
{ |
||||
Model model; |
||||
|
||||
ToxFile file(0, 0, "", "", 1000, ToxFile::FileDirection::SENDING); |
||||
model.onFileUpdated(file); |
||||
|
||||
const auto idx = model.index(0, static_cast<int>(Column::size)); |
||||
auto size = idx.data(); |
||||
|
||||
// Size should be a human readable string
|
||||
QCOMPARE(size.toString(), QString("1000B")); |
||||
|
||||
// 1GB + a little to avoid floating point inaccuracy
|
||||
file = ToxFile(0, 0, "", "", 1024 * 1024 * 1024 + 2, ToxFile::FileDirection::SENDING); |
||||
model.onFileUpdated(file); |
||||
size = idx.data(); |
||||
QCOMPARE(size.toString(), QString("1.00GiB")); |
||||
} |
||||
|
||||
void TestFileTransferList::testSpeed() |
||||
{ |
||||
Model model; |
||||
|
||||
ToxFile file(0, 0, "", "", 1024 * 1024, ToxFile::FileDirection::SENDING); |
||||
file.progress.addSample(100 * 1024, QTime(1, 0, 0)); |
||||
file.progress.addSample(200 * 1024, QTime(1, 0, 1)); |
||||
model.onFileUpdated(file); |
||||
|
||||
const auto idx = model.index(0, static_cast<int>(Column::speed)); |
||||
const auto speed = idx.data(); |
||||
|
||||
// Speed should be a human readable string
|
||||
QCOMPARE(speed.toString(), QString("100KiB/s")); |
||||
} |
||||
|
||||
void TestFileTransferList::testStatus() |
||||
{ |
||||
Model model; |
||||
|
||||
ToxFile file(0, 0, "", "", 1024 * 1024, ToxFile::FileDirection::SENDING); |
||||
file.status = ToxFile::TRANSMITTING; |
||||
model.onFileUpdated(file); |
||||
|
||||
const auto idx = model.index(0, static_cast<int>(Column::status)); |
||||
auto status = idx.data(); |
||||
|
||||
QCOMPARE(status.toString(), QString("Transmitting")); |
||||
|
||||
file.status = ToxFile::PAUSED; |
||||
file.pauseStatus.remotePause(); |
||||
model.onFileUpdated(file); |
||||
status = idx.data(); |
||||
|
||||
QCOMPARE(status.toString(), QString("Remote paused")); |
||||
|
||||
file.status = ToxFile::PAUSED; |
||||
file.pauseStatus.localPause(); |
||||
file.pauseStatus.remoteResume(); |
||||
model.onFileUpdated(file); |
||||
status = idx.data(); |
||||
|
||||
QCOMPARE(status.toString(), QString("Paused")); |
||||
} |
||||
|
||||
void TestFileTransferList::testControl() |
||||
{ |
||||
Model model; |
||||
bool cancelCalled = false; |
||||
bool pauseCalled = false; |
||||
|
||||
QObject::connect(&model, &Model::cancel, [&] (ToxFile file) { |
||||
cancelCalled = true; |
||||
}); |
||||
|
||||
QObject::connect(&model, &Model::togglePause, [&] (ToxFile file) { |
||||
pauseCalled = true; |
||||
}); |
||||
|
||||
ToxFile file(0, 0, "", "", 1024 * 1024, ToxFile::FileDirection::SENDING); |
||||
file.status = ToxFile::TRANSMITTING; |
||||
model.onFileUpdated(file); |
||||
|
||||
const auto idx = model.index(0, static_cast<int>(Column::control)); |
||||
model.setData(idx, static_cast<int>(EditorAction::pause)); |
||||
|
||||
QVERIFY(pauseCalled); |
||||
QVERIFY(!cancelCalled); |
||||
|
||||
pauseCalled = false; |
||||
model.setData(idx, static_cast<int>(EditorAction::cancel)); |
||||
QVERIFY(!pauseCalled); |
||||
QVERIFY(cancelCalled); |
||||
|
||||
file.status = ToxFile::TRANSMITTING; |
||||
model.onFileUpdated(file); |
||||
// True if paused
|
||||
QCOMPARE(idx.data().toBool(), false); |
||||
|
||||
file.status = ToxFile::PAUSED; |
||||
file.pauseStatus.localPause(); |
||||
model.onFileUpdated(file); |
||||
// True if _local_ paused
|
||||
QCOMPARE(idx.data().toBool(), true); |
||||
} |
||||
|
||||
void TestFileTransferList::testAvatarIgnored() |
||||
{ |
||||
Model model; |
||||
|
||||
ToxFile file; |
||||
file.fileKind = TOX_FILE_KIND_AVATAR; |
||||
model.onFileUpdated(file); |
||||
|
||||
QCOMPARE(model.rowCount(), 0); |
||||
} |
||||
|
||||
void TestFileTransferList::testMultipleFiles() |
||||
{ |
||||
Model model; |
||||
|
||||
ToxFile file; |
||||
file.resumeFileId = QByteArray(); |
||||
file.fileKind = TOX_FILE_KIND_DATA; |
||||
file.status = ToxFile::TRANSMITTING; |
||||
file.fileName = "a"; |
||||
model.onFileUpdated(file); |
||||
|
||||
// File map keys off resume file ID
|
||||
file.resumeFileId = QByteArray("asdfasdf"); |
||||
file.fileName = "b"; |
||||
model.onFileUpdated(file); |
||||
|
||||
QCOMPARE(model.rowCount(), 2); |
||||
|
||||
auto idx = model.index(0, static_cast<int>(Column::fileName)); |
||||
QCOMPARE(idx.data().toString(), QString("a")); |
||||
|
||||
idx = model.index(1, static_cast<int>(Column::fileName)); |
||||
QCOMPARE(idx.data().toString(), QString("b")); |
||||
|
||||
// File name should be updated instead of inserting a new file since the
|
||||
// resume file ID is the same
|
||||
file.fileName = "c"; |
||||
model.onFileUpdated(file); |
||||
QCOMPARE(model.rowCount(), 2); |
||||
QCOMPARE(idx.data().toString(), QString("c")); |
||||
} |
||||
|
||||
void TestFileTransferList::testFileRemoval() |
||||
{ |
||||
// Model should keep files in the list if they are finished, but not if they
|
||||
// were broken or canceled
|
||||
|
||||
Model model; |
||||
|
||||
ToxFile file; |
||||
file.fileKind = TOX_FILE_KIND_DATA; |
||||
file.status = ToxFile::TRANSMITTING; |
||||
model.onFileUpdated(file); |
||||
|
||||
QCOMPARE(model.rowCount(), 1); |
||||
|
||||
file.status = ToxFile::BROKEN; |
||||
model.onFileUpdated(file); |
||||
QCOMPARE(model.rowCount(), 0); |
||||
|
||||
file.status = ToxFile::TRANSMITTING; |
||||
model.onFileUpdated(file); |
||||
QCOMPARE(model.rowCount(), 1); |
||||
|
||||
file.status = ToxFile::CANCELED; |
||||
model.onFileUpdated(file); |
||||
QCOMPARE(model.rowCount(), 0); |
||||
|
||||
file.status = ToxFile::TRANSMITTING; |
||||
model.onFileUpdated(file); |
||||
QCOMPARE(model.rowCount(), 1); |
||||
|
||||
file.status = ToxFile::FINISHED; |
||||
model.onFileUpdated(file); |
||||
QCOMPARE(model.rowCount(), 1); |
||||
} |
||||
|
||||
QTEST_GUILESS_MAIN(TestFileTransferList) |
||||
#include "filesform_test.moc" |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.9 KiB |
@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
Copyright © 2021 by The qTox Project Contributors |
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox. |
||||
|
||||
qTox is libre software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
qTox is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include <QString> |
||||
|
||||
#include <cstdint> |
||||
|
||||
QString getHumanReadableSize(uint64_t sizeBytes); |
@ -0,0 +1,34 @@
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
Copyright © 2021 by The qTox Project Contributors |
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox. |
||||
|
||||
qTox is libre software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
qTox is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
|
||||
#include "util/display.h" |
||||
|
||||
#include <cmath> |
||||
|
||||
QString getHumanReadableSize(uint64_t size) |
||||
{ |
||||
static const char* suffix[] = {"B", "KiB", "MiB", "GiB", "TiB"}; |
||||
int exp = 0; |
||||
|
||||
if (size > 0) { |
||||
exp = std::min(static_cast<int>(log(size) / log(1024)), static_cast<int>(sizeof(suffix) / sizeof(suffix[0]) - 1)); |
||||
} |
||||
|
||||
return QString().setNum(size / pow(1024, exp), 'f', exp > 1 ? 2 : 0).append(suffix[exp]); |
||||
} |
Loading…
Reference in new issue