mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-24 05:55:59 +03:00
866ffc2a6b
Signed-off-by: Kevin Ottens <kevin.ottens@nextcloud.com>
230 lines
9.5 KiB
C++
230 lines
9.5 KiB
C++
/*
|
|
* This software is in the public domain, furnished "as is", without technical
|
|
* support, and with no warranty, express or implied, as to its usefulness for
|
|
* any purpose.
|
|
*
|
|
*/
|
|
|
|
#include <QtTest>
|
|
#include "syncenginetestutils.h"
|
|
#include <syncengine.h>
|
|
#include <configfile.h>
|
|
|
|
using namespace OCC;
|
|
|
|
|
|
static void changeAllFileId(FileInfo &info) {
|
|
info.fileId = generateFileId();
|
|
if (!info.isDir)
|
|
return;
|
|
info.etag = generateEtag();
|
|
for (auto &child : info.children) {
|
|
changeAllFileId(child);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This test ensure that the SyncEngine::aboutToRemoveAllFiles is correctly called and that when
|
|
* we the user choose to remove all files SyncJournalDb::clearFileTable makes works as expected
|
|
*/
|
|
class TestAllFilesDeleted : public QObject
|
|
{
|
|
Q_OBJECT
|
|
|
|
private slots:
|
|
|
|
void testAllFilesDeletedKeep_data()
|
|
{
|
|
QTest::addColumn<bool>("deleteOnRemote");
|
|
QTest::newRow("local") << false;
|
|
QTest::newRow("remote") << true;
|
|
|
|
}
|
|
|
|
/*
|
|
* In this test, all files are deleted in the client, or the server, and we simulate
|
|
* that the users press "keep"
|
|
*/
|
|
void testAllFilesDeletedKeep()
|
|
{
|
|
QFETCH(bool, deleteOnRemote);
|
|
FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()};
|
|
ConfigFile config;
|
|
config.setPromptDeleteFiles(true);
|
|
|
|
//Just set a blacklist so we can check it is still there. This directory does not exists but
|
|
// that does not matter for our purposes.
|
|
QStringList selectiveSyncBlackList = { "Q/" };
|
|
fakeFolder.syncEngine().journal()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList,
|
|
selectiveSyncBlackList);
|
|
|
|
auto initialState = fakeFolder.currentLocalState();
|
|
int aboutToRemoveAllFilesCalled = 0;
|
|
QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveAllFiles,
|
|
[&](SyncFileItem::Direction dir, bool *cancel) {
|
|
QCOMPARE(aboutToRemoveAllFilesCalled, 0);
|
|
aboutToRemoveAllFilesCalled++;
|
|
QCOMPARE(dir, deleteOnRemote ? SyncFileItem::Down : SyncFileItem::Up);
|
|
*cancel = true;
|
|
fakeFolder.syncEngine().journal()->clearFileTable(); // That's what Folder is doing
|
|
});
|
|
|
|
auto &modifier = deleteOnRemote ? fakeFolder.remoteModifier() : fakeFolder.localModifier();
|
|
for (const auto &s : fakeFolder.currentRemoteState().children.keys())
|
|
modifier.remove(s);
|
|
|
|
QVERIFY(!fakeFolder.syncOnce()); // Should fail because we cancel the sync
|
|
QCOMPARE(aboutToRemoveAllFilesCalled, 1);
|
|
|
|
// Next sync should recover all files
|
|
QVERIFY(fakeFolder.syncOnce());
|
|
QCOMPARE(fakeFolder.currentLocalState(), initialState);
|
|
QCOMPARE(fakeFolder.currentRemoteState(), initialState);
|
|
|
|
// The selective sync blacklist should be not have been deleted.
|
|
bool ok = true;
|
|
QCOMPARE(fakeFolder.syncEngine().journal()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, &ok),
|
|
selectiveSyncBlackList);
|
|
}
|
|
|
|
void testAllFilesDeletedDelete_data()
|
|
{
|
|
testAllFilesDeletedKeep_data();
|
|
}
|
|
|
|
/*
|
|
* This test is like the previous one but we simulate that the user presses "delete"
|
|
*/
|
|
void testAllFilesDeletedDelete()
|
|
{
|
|
QFETCH(bool, deleteOnRemote);
|
|
FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()};
|
|
|
|
int aboutToRemoveAllFilesCalled = 0;
|
|
QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveAllFiles,
|
|
[&](SyncFileItem::Direction dir, bool *cancel) {
|
|
QCOMPARE(aboutToRemoveAllFilesCalled, 0);
|
|
aboutToRemoveAllFilesCalled++;
|
|
QCOMPARE(dir, deleteOnRemote ? SyncFileItem::Down : SyncFileItem::Up);
|
|
*cancel = false;
|
|
});
|
|
|
|
auto &modifier = deleteOnRemote ? fakeFolder.remoteModifier() : fakeFolder.localModifier();
|
|
for (const auto &s : fakeFolder.currentRemoteState().children.keys())
|
|
modifier.remove(s);
|
|
|
|
QVERIFY(fakeFolder.syncOnce()); // Should succeed, and all files must then be deleted
|
|
|
|
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
|
|
QCOMPARE(fakeFolder.currentLocalState().children.count(), 0);
|
|
|
|
// Try another sync to be sure.
|
|
|
|
QVERIFY(fakeFolder.syncOnce()); // Should succeed (doing nothing)
|
|
QCOMPARE(aboutToRemoveAllFilesCalled, 1); // should not have been called.
|
|
|
|
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
|
|
QCOMPARE(fakeFolder.currentLocalState().children.count(), 0);
|
|
}
|
|
|
|
void testNotDeleteMetaDataChange() {
|
|
/**
|
|
* This test make sure that we don't popup a file deleted message if all the metadata have
|
|
* been updated (for example when the server is upgraded or something)
|
|
**/
|
|
|
|
FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()};
|
|
// We never remove all files.
|
|
QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveAllFiles,
|
|
[&] { QVERIFY(false); });
|
|
QVERIFY(fakeFolder.syncOnce());
|
|
|
|
for (const auto &s : fakeFolder.currentRemoteState().children.keys())
|
|
fakeFolder.syncJournal().avoidRenamesOnNextSync(s); // clears all the fileid and inodes.
|
|
fakeFolder.localModifier().remove("A/a1");
|
|
auto expectedState = fakeFolder.currentLocalState();
|
|
QVERIFY(fakeFolder.syncOnce());
|
|
QCOMPARE(fakeFolder.currentLocalState(), expectedState);
|
|
QCOMPARE(fakeFolder.currentRemoteState(), expectedState);
|
|
|
|
fakeFolder.remoteModifier().remove("B/b1");
|
|
changeAllFileId(fakeFolder.remoteModifier());
|
|
expectedState = fakeFolder.currentRemoteState();
|
|
QVERIFY(fakeFolder.syncOnce());
|
|
QCOMPARE(fakeFolder.currentLocalState(), expectedState);
|
|
QCOMPARE(fakeFolder.currentRemoteState(), expectedState);
|
|
}
|
|
|
|
void testDataFingetPrint_data()
|
|
{
|
|
QTest::addColumn<bool>("hasInitialFingerPrint");
|
|
QTest::newRow("initial finger print") << true;
|
|
QTest::newRow("no initial finger print") << false;
|
|
}
|
|
|
|
void testDataFingetPrint()
|
|
{
|
|
QFETCH(bool, hasInitialFingerPrint);
|
|
FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() };
|
|
fakeFolder.remoteModifier().setContents("C/c1", 'N');
|
|
fakeFolder.remoteModifier().setModTime("C/c1", QDateTime::currentDateTimeUtc().addDays(-2));
|
|
fakeFolder.remoteModifier().remove("C/c2");
|
|
if (hasInitialFingerPrint) {
|
|
fakeFolder.remoteModifier().extraDavProperties = "<oc:data-fingerprint>initial_finger_print</oc:data-fingerprint>";
|
|
} else {
|
|
//Server support finger print, but none is set.
|
|
fakeFolder.remoteModifier().extraDavProperties = "<oc:data-fingerprint></oc:data-fingerprint>";
|
|
}
|
|
QVERIFY(fakeFolder.syncOnce());
|
|
// First sync, we did not change the finger print, so the file should be downloaded as normal
|
|
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
|
|
QCOMPARE(fakeFolder.currentRemoteState().find("C/c1")->contentChar, 'N');
|
|
QVERIFY(!fakeFolder.currentRemoteState().find("C/c2"));
|
|
|
|
/* Simulate a backup restoration */
|
|
|
|
// A/a1 is an old file
|
|
fakeFolder.remoteModifier().setContents("A/a1", 'O');
|
|
fakeFolder.remoteModifier().setModTime("A/a1", QDateTime::currentDateTimeUtc().addDays(-2));
|
|
// B/b1 did not exist at the time of the backup
|
|
fakeFolder.remoteModifier().remove("B/b1");
|
|
// B/b2 was uploaded by another user in the mean time.
|
|
fakeFolder.remoteModifier().setContents("B/b2", 'N');
|
|
fakeFolder.remoteModifier().setModTime("B/b2", QDateTime::currentDateTimeUtc().addDays(2));
|
|
|
|
// C/c3 was removed since we made the backup
|
|
fakeFolder.remoteModifier().insert("C/c3_removed");
|
|
// C/c4 was moved to A/a2 since we made the backup
|
|
fakeFolder.remoteModifier().rename("A/a2", "C/old_a2_location");
|
|
|
|
// The admin sets the data-fingerprint property
|
|
fakeFolder.remoteModifier().extraDavProperties = "<oc:data-fingerprint>new_finger_print</oc:data-fingerprint>";
|
|
|
|
QVERIFY(fakeFolder.syncOnce());
|
|
auto currentState = fakeFolder.currentLocalState();
|
|
// Altough the local file is kept as a conflict, the server file is downloaded
|
|
QCOMPARE(currentState.find("A/a1")->contentChar, 'O');
|
|
auto conflict = findConflict(currentState, "A/a1");
|
|
QVERIFY(conflict);
|
|
QCOMPARE(conflict->contentChar, 'W');
|
|
fakeFolder.localModifier().remove(conflict->path());
|
|
// b1 was restored (re-uploaded)
|
|
QVERIFY(currentState.find("B/b1"));
|
|
|
|
// b2 has the new content (was not restored), since its mode time goes forward in time
|
|
QCOMPARE(currentState.find("B/b2")->contentChar, 'N');
|
|
conflict = findConflict(currentState, "B/b2");
|
|
QVERIFY(conflict); // Just to be sure, we kept the old file in a conflict
|
|
QCOMPARE(conflict->contentChar, 'W');
|
|
fakeFolder.localModifier().remove(conflict->path());
|
|
|
|
// We actually do not remove files that technically should have been removed (we don't want data-loss)
|
|
QVERIFY(currentState.find("C/c3_removed"));
|
|
QVERIFY(currentState.find("C/old_a2_location"));
|
|
|
|
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
|
|
}
|
|
};
|
|
|
|
QTEST_GUILESS_MAIN(TestAllFilesDeleted)
|
|
#include "testallfilesdeleted.moc"
|