File indexing completed on 2024-04-28 03:51:41
0001 /* 0002 This file is part of the KDE Baloo Project 0003 SPDX-FileCopyrightText: 2013-2015 Vishesh Handa <vhanda@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.1-or-later 0006 */ 0007 0008 #include "metadatamover.h" 0009 0010 #include "database.h" 0011 #include "transaction.h" 0012 #include "document.h" 0013 #include "termgenerator.h" 0014 0015 #include <memory> 0016 #include <QDir> 0017 #include <QTemporaryDir> 0018 #include <QTest> 0019 0020 using namespace Baloo; 0021 0022 class MetadataMoverTest : public QObject 0023 { 0024 Q_OBJECT 0025 public: 0026 MetadataMoverTest(QObject* parent = nullptr); 0027 0028 private Q_SLOTS: 0029 0030 void init(); 0031 void cleanup(); 0032 0033 void testRemoveFile(); 0034 void testRenameFile(); 0035 void testMoveFile(); 0036 void testMoveRenameFile(); 0037 void testMoveFolder(); 0038 void testMoveTwiceSequential(); 0039 void testMoveDeleteFile(); 0040 void testMoveFileRenameParent(); 0041 void testRenameMoveFileRenameParent(); 0042 void testMoveFileMoveParent(); 0043 0044 private: 0045 quint64 insertDoc(const QString& url, quint64 parentId); 0046 0047 std::unique_ptr<QTemporaryDir> m_tempDir; 0048 std::unique_ptr<Database> m_db; 0049 0050 struct DocumentInfo { 0051 QString url; 0052 DocumentTimeDB::TimeInfo timeInfo; 0053 QVector<QByteArray> docTerms; 0054 QVector<QByteArray> filenameTerms; 0055 }; 0056 DocumentInfo documentInfo(quint64 docId); 0057 }; 0058 0059 MetadataMoverTest::MetadataMoverTest(QObject* parent) 0060 : QObject(parent) 0061 { 0062 } 0063 0064 void MetadataMoverTest::cleanup() 0065 { 0066 m_db.reset(); 0067 m_tempDir.reset(); 0068 } 0069 0070 void MetadataMoverTest::init() 0071 { 0072 m_tempDir = std::make_unique<QTemporaryDir>(); 0073 m_db = std::make_unique<Database>(m_tempDir->path()); 0074 m_db->open(Database::CreateDatabase); 0075 QVERIFY(m_db->isOpen()); 0076 } 0077 0078 quint64 MetadataMoverTest::insertDoc(const QString& url, quint64 parentId) 0079 { 0080 static quint64 fileId = 1; 0081 fileId++; 0082 0083 Document doc; 0084 const QByteArray path = QFile::encodeName(url); 0085 doc.setUrl(path); 0086 doc.setId(fileId); 0087 doc.setParentId(parentId); 0088 0089 // Docterms are mandatory. Use "Type:Empty" as placeholder 0090 doc.addTerm("T0"); 0091 0092 auto lastSlash = path.lastIndexOf('/'); 0093 const QByteArray fileName = path.mid(lastSlash + 1); 0094 TermGenerator tg(doc); 0095 tg.indexFileNameText(QFile::decodeName(fileName)); 0096 0097 Transaction tr(m_db.get(), Transaction::ReadWrite); 0098 tr.addDocument(doc); 0099 tr.commit(); 0100 return doc.id(); 0101 } 0102 0103 MetadataMoverTest::DocumentInfo MetadataMoverTest::documentInfo(quint64 id) 0104 { 0105 Transaction tr(m_db.get(), Transaction::ReadOnly); 0106 if (!tr.hasDocument(id)) { 0107 return DocumentInfo(); 0108 } 0109 0110 DocumentInfo docInfo; 0111 docInfo.url = QFile::decodeName(tr.documentUrl(id)); 0112 docInfo.timeInfo = tr.documentTimeInfo(id); 0113 docInfo.docTerms = tr.documentTerms(id); 0114 docInfo.filenameTerms = tr.documentFileNameTerms(id); 0115 0116 return docInfo; 0117 } 0118 0119 void MetadataMoverTest::testRemoveFile() 0120 { 0121 QString url = QStringLiteral("/somefile"); 0122 quint64 fid = insertDoc(url, 99); 0123 0124 { 0125 Transaction tr(m_db.get(), Transaction::ReadOnly); 0126 QVERIFY(tr.hasDocument(fid)); 0127 } 0128 0129 MetadataMover mover(m_db.get(), this); 0130 mover.removeFileMetadata(url); 0131 0132 { 0133 Transaction tr(m_db.get(), Transaction::ReadOnly); 0134 QVERIFY(!tr.hasDocument(fid)); 0135 } 0136 } 0137 0138 // Rename "file" to "file2" 0139 // mv <tmpdir>/file <tmpdir>/file2 0140 void MetadataMoverTest::testRenameFile() 0141 { 0142 QTemporaryDir dir; 0143 auto parentId = filePathToId(QFile::encodeName(dir.path())); 0144 0145 QString url = dir.path() + QStringLiteral("/file"); 0146 quint64 fid = insertDoc(url, parentId); 0147 0148 DocumentInfo oldInfo = documentInfo(fid); 0149 QVERIFY(!oldInfo.url.isEmpty()); 0150 QCOMPARE(oldInfo.url, url); 0151 0152 MetadataMover mover(m_db.get(), this); 0153 QString url2 = dir.path() + QStringLiteral("/file2"); 0154 mover.moveFileMetadata(url, url2); 0155 0156 DocumentInfo newInfo = documentInfo(fid); 0157 QCOMPARE(newInfo.url, url2); 0158 QCOMPARE(newInfo.docTerms, oldInfo.docTerms); 0159 QVERIFY(newInfo.filenameTerms != oldInfo.filenameTerms); 0160 QVERIFY(!newInfo.filenameTerms.empty()); 0161 } 0162 0163 // Change parent directory of "file" 0164 // mv <tmpdir>/a/b/c/file <tmpdir>/file 0165 void MetadataMoverTest::testMoveFile() 0166 { 0167 QTemporaryDir dir; 0168 auto parentId = filePathToId(QFile::encodeName(dir.path())); 0169 0170 auto idA = insertDoc(dir.path() + QStringLiteral("/a"), parentId); 0171 auto idB = insertDoc(dir.path() + QStringLiteral("/a/b"), idA); 0172 auto idC = insertDoc(dir.path() + QStringLiteral("/a/b/c"), idB); 0173 0174 QString url = dir.path() + QStringLiteral("/a/b/c/file"); 0175 quint64 fid = insertDoc(url, idC); 0176 0177 DocumentInfo oldInfo = documentInfo(fid); 0178 QVERIFY(!oldInfo.url.isEmpty()); 0179 QCOMPARE(oldInfo.url, url); 0180 0181 MetadataMover mover(m_db.get(), this); 0182 QString url2 = dir.path() + QStringLiteral("/file"); 0183 mover.moveFileMetadata(url, url2); 0184 0185 DocumentInfo newInfo = documentInfo(fid); 0186 QCOMPARE(newInfo.url, url2); 0187 QCOMPARE(newInfo.docTerms, oldInfo.docTerms); 0188 QCOMPARE(newInfo.filenameTerms, oldInfo.filenameTerms); 0189 } 0190 0191 // Change parent directory of "file", and rename to "file2" 0192 // mv <tmpdir>/a/b/c/file <tmpdir>/file2 0193 void MetadataMoverTest::testMoveRenameFile() 0194 { 0195 QTemporaryDir dir; 0196 auto parentId = filePathToId(QFile::encodeName(dir.path())); 0197 0198 auto idA = insertDoc(dir.path() + QStringLiteral("/a"), parentId); 0199 auto idB = insertDoc(dir.path() + QStringLiteral("/a/b"), idA); 0200 auto idC = insertDoc(dir.path() + QStringLiteral("/a/b/c"), idB); 0201 0202 QString url = dir.path() + QStringLiteral("/a/b/c/file"); 0203 quint64 fid = insertDoc(url, idC); 0204 0205 DocumentInfo oldInfo = documentInfo(fid); 0206 QVERIFY(!oldInfo.url.isEmpty()); 0207 QCOMPARE(oldInfo.url, url); 0208 0209 MetadataMover mover(m_db.get(), this); 0210 QString url2 = dir.path() + QStringLiteral("/file2"); 0211 mover.moveFileMetadata(url, url2); 0212 0213 DocumentInfo newInfo = documentInfo(fid); 0214 QCOMPARE(newInfo.url, url2); 0215 QCOMPARE(newInfo.docTerms, oldInfo.docTerms); 0216 QVERIFY(newInfo.filenameTerms != oldInfo.filenameTerms); 0217 QVERIFY(!newInfo.filenameTerms.empty()); 0218 } 0219 0220 // Rename parent directory of "file" 0221 // mv <tmpdir>/folder <tmpdir>/dir 0222 // New path of <tmpdir>/folder/file becomes <tmpdir>/dir/file 0223 void MetadataMoverTest::testMoveFolder() 0224 { 0225 QTemporaryDir dir; 0226 auto parentId = filePathToId(QFile::encodeName(dir.path())); 0227 0228 QString folder = dir.path() + QStringLiteral("/folder"); 0229 quint64 did = insertDoc(folder, parentId); 0230 0231 QString fileUrl = folder + QStringLiteral("/file"); 0232 quint64 fid = insertDoc(fileUrl, did); 0233 0234 { 0235 Transaction tr(m_db.get(), Transaction::ReadOnly); 0236 QVERIFY(tr.hasDocument(did)); 0237 QVERIFY(tr.hasDocument(fid)); 0238 } 0239 0240 QString newFolderUrl = dir.path() + QStringLiteral("/dir"); 0241 MetadataMover mover(m_db.get(), this); 0242 mover.moveFileMetadata(folder, newFolderUrl); 0243 0244 { 0245 Transaction tr(m_db.get(), Transaction::ReadOnly); 0246 QVERIFY(tr.hasDocument(did)); 0247 QVERIFY(tr.hasDocument(fid)); 0248 QCOMPARE(tr.documentUrl(did), QFile::encodeName(newFolderUrl)); 0249 QCOMPARE(tr.documentUrl(fid), QFile::encodeName(newFolderUrl) + "/file"); 0250 } 0251 } 0252 0253 // Rename a file twice in a row 0254 // Mimic Inotify/Filewatch behavior as if there was sufficient 0255 // time to process the first rename before the second happens 0256 void MetadataMoverTest::testMoveTwiceSequential() 0257 { 0258 QTemporaryDir dir; 0259 auto did = filePathToId(QFile::encodeName(dir.path())); 0260 0261 const QString fileUrl = dir.path() + QStringLiteral("/file"); 0262 const quint64 fid = insertDoc(fileUrl, did); 0263 0264 { 0265 Transaction tr(m_db.get(), Transaction::ReadOnly); 0266 QVERIFY(tr.hasDocument(did)); 0267 QVERIFY(tr.hasDocument(fid)); 0268 } 0269 0270 MetadataMover mover(m_db.get(), this); 0271 0272 // First rename 0273 const QString fileUrl1 = dir.path() + QStringLiteral("/file1"); 0274 mover.moveFileMetadata(fileUrl, fileUrl1); 0275 0276 // Second rename 0277 const QString fileUrl2 = dir.path() + QStringLiteral("/file2"); 0278 mover.moveFileMetadata(fileUrl1, fileUrl2); 0279 0280 // Check result 0281 { 0282 Transaction tr(m_db.get(), Transaction::ReadOnly); 0283 QVERIFY(tr.hasDocument(did)); 0284 QVERIFY(tr.hasDocument(fid)); 0285 QCOMPARE(tr.documentUrl(did), QFile::encodeName(dir.path())); 0286 QCOMPARE(tr.documentUrl(fid), QFile::encodeName(fileUrl2)); 0287 } 0288 } 0289 0290 // Rename a file and then immediately delete it 0291 void MetadataMoverTest::testMoveDeleteFile() 0292 { 0293 QTemporaryDir dir; 0294 auto did = filePathToId(QFile::encodeName(dir.path())); 0295 0296 const QString fileUrl = dir.path() + QStringLiteral("/file"); 0297 const quint64 fid = insertDoc(fileUrl, did); 0298 0299 { 0300 Transaction tr(m_db.get(), Transaction::ReadOnly); 0301 QVERIFY(tr.hasDocument(did)); 0302 QVERIFY(tr.hasDocument(fid)); 0303 } 0304 0305 // Rename 0306 const QString fileUrl1 = dir.path() + QStringLiteral("/file1"); 0307 0308 MetadataMover mover(m_db.get(), this); 0309 mover.moveFileMetadata(fileUrl, fileUrl1); 0310 0311 // Delete 0312 mover.removeFileMetadata(fileUrl1); 0313 0314 // Check result 0315 { 0316 Transaction tr(m_db.get(), Transaction::ReadOnly); 0317 QVERIFY(tr.hasDocument(did)); 0318 QCOMPARE(tr.documentUrl(did), QFile::encodeName(dir.path())); 0319 QVERIFY(!tr.hasDocument(fid)); 0320 } 0321 } 0322 0323 // Move a file to a different parent directory and rename the new parent 0324 void MetadataMoverTest::testMoveFileRenameParent() 0325 { 0326 QTemporaryDir dir; 0327 auto did = filePathToId(QFile::encodeName(dir.path())); 0328 0329 quint64 did_a = insertDoc(dir.path() + QStringLiteral("/a"), did); 0330 quint64 did_b = insertDoc(dir.path() + QStringLiteral("/b"), did); 0331 0332 const QString fileUrl = dir.path() + QStringLiteral("/a/file"); 0333 const quint64 fid = insertDoc(fileUrl, did_a); 0334 0335 { 0336 Transaction tr(m_db.get(), Transaction::ReadOnly); 0337 QVERIFY(tr.hasDocument(did)); 0338 QVERIFY(tr.hasDocument(did_a)); 0339 QVERIFY(tr.hasDocument(did_b)); 0340 QVERIFY(tr.hasDocument(fid)); 0341 } 0342 0343 // Move to new parent 0344 const QString fileUrl_b1 = dir.path() + QStringLiteral("/b/file1"); 0345 0346 MetadataMover mover(m_db.get(), this); 0347 mover.moveFileMetadata(fileUrl, fileUrl_b1); 0348 0349 // Rename parent (delete old dir first) 0350 mover.removeFileMetadata(dir.path() + QStringLiteral("/a")); 0351 mover.moveFileMetadata(dir.path() + QStringLiteral("/b"), dir.path() + QStringLiteral("/a")); 0352 0353 // Check result 0354 { 0355 Transaction tr(m_db.get(), Transaction::ReadOnly); 0356 QVERIFY(tr.hasDocument(did)); 0357 QCOMPARE(tr.documentUrl(did), QFile::encodeName(dir.path())); 0358 QCOMPARE(tr.documentUrl(did_b), QFile::encodeName(dir.path()) + "/a"); 0359 QVERIFY(tr.hasDocument(fid)); 0360 QCOMPARE(tr.documentUrl(fid), QFile::encodeName(dir.path()) + "/a/file1"); 0361 } 0362 } 0363 0364 // Rename a file, move to a different parent directory and rename the new parent 0365 void MetadataMoverTest::testRenameMoveFileRenameParent() 0366 { 0367 QTemporaryDir dir; 0368 auto did = filePathToId(QFile::encodeName(dir.path())); 0369 0370 quint64 did_a = insertDoc(dir.path() + QStringLiteral("/a"), did); 0371 quint64 did_b = insertDoc(dir.path() + QStringLiteral("/b"), did); 0372 0373 const QString fileUrl = dir.path() + QStringLiteral("/a/file"); 0374 const quint64 fid = insertDoc(fileUrl, did_a); 0375 0376 { 0377 Transaction tr(m_db.get(), Transaction::ReadOnly); 0378 QVERIFY(tr.hasDocument(did)); 0379 QVERIFY(tr.hasDocument(did_a)); 0380 QVERIFY(tr.hasDocument(did_b)); 0381 QVERIFY(tr.hasDocument(fid)); 0382 } 0383 0384 // First rename 0385 const QString fileUrl_a1 = dir.path() + QStringLiteral("/a/file1"); 0386 0387 MetadataMover mover(m_db.get(), this); 0388 mover.moveFileMetadata(fileUrl, fileUrl_a1); 0389 0390 // Move to new parent 0391 const QString fileUrl_b1 = dir.path() + QStringLiteral("/b/file1"); 0392 mover.moveFileMetadata(fileUrl_a1, fileUrl_b1); 0393 0394 // Rename parent (delete old dir first) 0395 mover.removeFileMetadata(dir.path() + QStringLiteral("/a")); 0396 mover.moveFileMetadata(dir.path() + QStringLiteral("/b"), dir.path() + QStringLiteral("/a")); 0397 0398 // Check result 0399 { 0400 Transaction tr(m_db.get(), Transaction::ReadOnly); 0401 QVERIFY(tr.hasDocument(did)); 0402 QCOMPARE(tr.documentUrl(did), QFile::encodeName(dir.path())); 0403 QCOMPARE(tr.documentUrl(did_b), QFile::encodeName(dir.path()) + "/a"); 0404 QVERIFY(tr.hasDocument(fid)); 0405 QCOMPARE(tr.documentUrl(fid), QFile::encodeName(dir.path()) + "/a/file1"); 0406 } 0407 } 0408 0409 // Rename a file and then immediately rename a parent directory 0410 void MetadataMoverTest::testMoveFileMoveParent() 0411 { 0412 QTemporaryDir dir; 0413 auto did = filePathToId(QFile::encodeName(dir.path())); 0414 0415 quint64 did_a = insertDoc(dir.path() + QStringLiteral("/a"), did); 0416 0417 const QString fileUrl = dir.path() + QStringLiteral("/a/file"); 0418 const quint64 fid = insertDoc(fileUrl, did_a); 0419 0420 { 0421 Transaction tr(m_db.get(), Transaction::ReadOnly); 0422 QVERIFY(tr.hasDocument(did)); 0423 QVERIFY(tr.hasDocument(did_a)); 0424 QVERIFY(tr.hasDocument(fid)); 0425 } 0426 0427 // First rename 0428 const QString fileUrl1 = dir.path() + QStringLiteral("/a/file1"); 0429 0430 MetadataMover mover(m_db.get(), this); 0431 mover.moveFileMetadata(fileUrl, fileUrl1); 0432 0433 // Rename parent 0434 mover.moveFileMetadata(dir.path() + QStringLiteral("/a"), dir.path() + QStringLiteral("/b")); 0435 0436 // Check result 0437 { 0438 Transaction tr(m_db.get(), Transaction::ReadOnly); 0439 QVERIFY(tr.hasDocument(did)); 0440 QVERIFY(tr.hasDocument(did_a)); 0441 QCOMPARE(tr.documentUrl(did), QFile::encodeName(dir.path())); 0442 QCOMPARE(tr.documentUrl(did_a), QFile::encodeName(dir.path()) + "/b"); 0443 QVERIFY(tr.hasDocument(fid)); 0444 QCOMPARE(tr.documentUrl(fid), QFile::encodeName(dir.path()) + "/b/file1"); 0445 } 0446 } 0447 0448 QTEST_GUILESS_MAIN(MetadataMoverTest) 0449 0450 #include "metadatamovertest.moc"