#include "cfapiwrapper.h"
#include "common/utility.h"
+#include "hydrationjob.h"
#include "vfs_cfapi.h"
#include <QDir>
}
});
loop.exec();
+ QObject::disconnect(vfs, nullptr, &loop, nullptr);
qCInfo(lcCfApiWrapper) << "VFS replied for hydration of" << path << requestId << "status was:" << hydrationRequestResult;
if (!hydrationRequestResult) {
}
qint64 offset = 0;
- while (socket.waitForReadyRead()) {
+
+ QObject::connect(&socket, &QLocalSocket::readyRead, &loop, [&] {
auto data = socket.readAll();
if (data.isEmpty()) {
qCWarning(lcCfApiWrapper) << "Unexpected empty data received" << requestId;
sendTransferError();
- break;
+ loop.quit();
+ return;
}
sendTransferInfo(data, offset);
offset += data.length();
- }
+ });
+
+ QObject::connect(vfs, &OCC::VfsCfApi::hydrationRequestFinished, &loop, [&](const QString &id, int s) {
+ if (requestId == id) {
+ const auto status = static_cast<OCC::HydrationJob::Status>(s);
+ qCInfo(lcCfApiWrapper) << "Hydration done for" << path << requestId << status;
+ if (status != OCC::HydrationJob::Success) {
+ sendTransferError();
+ }
+ loop.quit();
+ }
+ });
- qCInfo(lcCfApiWrapper) << "Hydration done for" << path << requestId;
+ loop.exec();
}
CF_CALLBACK_REGISTRATION cfApiCallbacks[] = {
_folderPath = folderPath;
}
+OCC::HydrationJob::Status OCC::HydrationJob::status() const
+{
+ return _status;
+}
+
void OCC::HydrationJob::start()
{
Q_ASSERT(_account);
const auto listenResult = _server->listen(_requestId);
if (!listenResult) {
qCCritical(lcHydration) << "Couldn't get server to listen" << _requestId << _localPath << _folderPath;
- emitFinished();
+ emitFinished(Error);
return;
}
connect(_server, &QLocalServer::newConnection, this, &HydrationJob::onNewConnection);
}
-void OCC::HydrationJob::emitFinished()
+void OCC::HydrationJob::emitFinished(Status status)
{
- _socket->disconnectFromServer();
- connect(_socket, &QLocalSocket::disconnected, this, [=]{
+ _status = status;
+ if (status == Success) {
+ _socket->disconnectFromServer();
+ connect(_socket, &QLocalSocket::disconnected, this, [=]{
+ _socket->close();
+ emit finished(this);
+ });
+ } else {
_socket->close();
emit finished(this);
- });
+ }
}
void OCC::HydrationJob::onNewConnection()
void OCC::HydrationJob::onGetFinished()
{
- qCInfo(lcHydration) << "GETFileJob finished" << _requestId << _folderPath << _job->errorStatus() << _job->errorString();
+ qCInfo(lcHydration) << "GETFileJob finished" << _requestId << _folderPath << _job->reply()->error();
- if (_job->errorStatus() != SyncFileItem::NoStatus && _job->errorStatus() != SyncFileItem::Success) {
- emitFinished();
+ if (_job->reply()->error()) {
+ emitFinished(Error);
return;
}
Q_ASSERT(record.isValid());
if (!record.isValid()) {
qCWarning(lcHydration) << "Couldn't find record to update after hydration" << _requestId << _folderPath;
- emitFinished();
+ emitFinished(Error);
return;
}
record._type = ItemTypeFile;
_journal->setFileRecord(record);
- emitFinished();
+ emitFinished(Success);
}
{
Q_OBJECT
public:
+ enum Status {
+ Success = 0,
+ Error,
+ };
+ Q_ENUM(Status)
+
explicit HydrationJob(QObject *parent = nullptr);
AccountPtr account() const;
QString folderPath() const;
void setFolderPath(const QString &folderPath);
+ Status status() const;
+
void start();
signals:
void finished(HydrationJob *job);
private:
- void emitFinished();
+ void emitFinished(Status status);
void onNewConnection();
void onGetFinished();
QLocalServer *_server = nullptr;
QLocalSocket *_socket = nullptr;
GETFileJob *_job = nullptr;
+ Status _status = Success;
};
} // namespace OCC
void VfsCfApi::onHydrationJobFinished(HydrationJob *job)
{
Q_ASSERT(d->hydrationJobs.contains(job));
+ qCInfo(lcCfApi) << "Hydration job finished" << job->requestId() << job->folderPath() << job->status();
+ emit hydrationRequestFinished(job->requestId(), job->status());
d->hydrationJobs.removeAll(job);
if (d->hydrationJobs.isEmpty()) {
emit doneHydrating();
signals:
void hydrationRequestReady(const QString &requestId);
void hydrationRequestFailed(const QString &requestId);
+ void hydrationRequestFinished(const QString &requestId, int status);
protected:
void startImpl(const VfsSetupParams ¶ms) override;
using namespace OCC;
+enum ErrorKind : int {
+ NoError = 0,
+ // Lower code are corresponding to HTTP error code
+ Timeout = 1000,
+};
+
void setPinState(const QString &path, PinState state, cfapi::SetPinRecurseMode mode)
{
Q_ASSERT(mode == cfapi::Recurse || mode == cfapi::NoRecurse);
CFVERIFY_VIRTUAL(fakeFolder, "local/file1");
}
+ void testOpeningOnlineFileTriggersDownload_data()
+ {
+ QTest::addColumn<int>("errorKind");
+ QTest::newRow("no error") << static_cast<int>(NoError);
+ QTest::newRow("400") << 400;
+ QTest::newRow("401") << 401;
+ QTest::newRow("403") << 403;
+ QTest::newRow("404") << 404;
+ QTest::newRow("500") << 500;
+ QTest::newRow("503") << 503;
+ QTest::newRow("Timeout") << static_cast<int>(Timeout);
+ }
+
void testOpeningOnlineFileTriggersDownload()
{
+ QFETCH(int, errorKind);
+
FakeFolder fakeFolder{ FileInfo() };
setupVfs(fakeFolder);
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
CFVERIFY_VIRTUAL(fakeFolder, "online/sub/file1");
+ // Setup error case if needed
+ if (errorKind == Timeout) {
+ fakeFolder.setServerOverride([&](QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *) -> QNetworkReply * {
+ if (req.url().path().endsWith("online/sub/file1")) {
+ return new FakeHangingReply(op, req, this);
+ }
+ return nullptr;
+ });
+ } else if (errorKind != NoError) {
+ fakeFolder.serverErrorPaths().append("online/sub/file1", errorKind);
+ }
+
+ // So the test that test timeout finishes fast
+ QScopedValueRollback<int> setHttpTimeout(AbstractNetworkJob::httpTimeout, errorKind == Timeout ? 1 : 10000);
+
// Simulate another process requesting the open
QEventLoop loop;
bool openResult = false;
loop.exec();
t.join();
- CFVERIFY_NONVIRTUAL(fakeFolder, "online/sub/file1");
+ if (errorKind == NoError) {
+ CFVERIFY_NONVIRTUAL(fakeFolder, "online/sub/file1");
+ } else {
+ CFVERIFY_VIRTUAL(fakeFolder, "online/sub/file1");
+ }
// Nothing should change
ItemCompletedSpy completeSpy(fakeFolder);
QVERIFY(fakeFolder.syncOnce());
QVERIFY(completeSpy.isEmpty());
- CFVERIFY_NONVIRTUAL(fakeFolder, "online/sub/file1");
+ if (errorKind == NoError) {
+ CFVERIFY_NONVIRTUAL(fakeFolder, "online/sub/file1");
+ } else {
+ CFVERIFY_VIRTUAL(fakeFolder, "online/sub/file1");
+ }
}
};