File indexing completed on 2024-09-15 03:35:44
0001 /* 0002 This file is part of the KDE Baloo project. 0003 SPDX-FileCopyrightText: 2015 Vishesh Handa <vhanda@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.1-or-later 0006 */ 0007 0008 #include "writetransaction.h" 0009 #include "dbstate.h" 0010 #include "database.h" 0011 #include "idutils.h" 0012 0013 #include <QTest> 0014 #include <QTemporaryDir> 0015 0016 using namespace Baloo; 0017 0018 class WriteTransactionTest : public QObject 0019 { 0020 Q_OBJECT 0021 private Q_SLOTS: 0022 void init() { 0023 dir = std::make_unique<QTemporaryDir>(); 0024 db = std::make_unique<Database>(dir->path()); 0025 db->open(Database::CreateDatabase); 0026 m_dirId = filePathToId(QFile::encodeName(dir->path())); 0027 QVERIFY(m_dirId); 0028 } 0029 0030 void cleanup() { 0031 db.reset(); 0032 dir.reset(); 0033 } 0034 0035 void testAddDocument(); 0036 void testAddDocumentTwoDocuments(); 0037 void testAddAndRemoveOneDocument(); 0038 void testAddAndReplaceOneDocument(); 0039 void testIdempotentDocumentChange(); 0040 0041 void testRemoveRecursively(); 0042 void testDocumentId(); 0043 void testTermPositions(); 0044 void testTransactionReset(); 0045 private: 0046 std::unique_ptr<QTemporaryDir> dir; 0047 std::unique_ptr<Database> db; 0048 quint64 m_dirId = 0; 0049 }; 0050 0051 void WriteTransactionTest::testAddDocument() 0052 { 0053 Transaction tr(db.get(), Transaction::ReadWrite); 0054 0055 const QString filePath(dir->path() + QStringLiteral("/file")); 0056 QByteArray url = QFile::encodeName(filePath); 0057 quint64 id = 99; 0058 0059 QCOMPARE(tr.hasDocument(id), false); 0060 0061 Document doc; 0062 doc.setId(id); 0063 doc.setParentId(m_dirId); 0064 doc.setUrl(url); 0065 doc.addTerm("a"); 0066 doc.addTerm("ab"); 0067 doc.addTerm("abc"); 0068 doc.addTerm("power"); 0069 doc.addFileNameTerm("link"); 0070 doc.addXattrTerm("system"); 0071 doc.setMTime(1); 0072 doc.setCTime(2); 0073 0074 tr.addDocument(doc); 0075 tr.commit(); 0076 0077 Transaction tr2(db.get(), Transaction::ReadOnly); 0078 0079 DBState state; 0080 state.postingDb = {{"a", {id}}, {"ab", {id}}, {"abc", {id}}, {"power", {id}}, {"system", {id}}, {"link", {id}}}; 0081 state.positionDb = {}; 0082 state.docTermsDb = {{id, {"a", "ab", "abc", "power"} }}; 0083 state.docFileNameTermsDb = {{id, {"link"} }}; 0084 state.docXAttrTermsDb = {{id, {"system"} }}; 0085 state.docTimeDb = {{id, DocumentTimeDB::TimeInfo(1, 2)}}; 0086 state.mtimeDb = {{1, id}}; 0087 0088 DBState actualState = DBState::fromTransaction(&tr2); 0089 QCOMPARE(actualState, state); 0090 } 0091 0092 static Document createDocument(const QString& filePath, quint32 mtime, quint32 ctime, const QVector<QByteArray>& terms, 0093 const QVector<QByteArray>& fileNameTerms, const QVector<QByteArray>& xattrTerms, quint64 parentId) 0094 { 0095 Document doc; 0096 0097 // Generate a unique file id. A monotonic counter is sufficient here. 0098 static quint64 fileid = parentId; 0099 fileid++; 0100 0101 const QByteArray url = QFile::encodeName(filePath); 0102 doc.setId(fileid); 0103 doc.setParentId(parentId); 0104 doc.setUrl(url); 0105 0106 for (const QByteArray& term: terms) { 0107 doc.addTerm(term); 0108 } 0109 for (const QByteArray& term: fileNameTerms) { 0110 doc.addFileNameTerm(term); 0111 } 0112 for (const QByteArray& term: xattrTerms) { 0113 doc.addXattrTerm(term); 0114 } 0115 doc.setMTime(mtime); 0116 doc.setCTime(ctime); 0117 0118 return doc; 0119 } 0120 0121 void WriteTransactionTest::testAddDocumentTwoDocuments() 0122 { 0123 const QString url1(dir->path() + QStringLiteral("/file1")); 0124 const QString url2(dir->path() + QStringLiteral("/file2")); 0125 0126 Document doc1 = createDocument(url1, 5, 1, {"a", "abc", "dab"}, {"file1"}, {}, m_dirId); 0127 Document doc2 = createDocument(url2, 6, 2, {"a", "abcd", "dab"}, {"file2"}, {}, m_dirId); 0128 0129 { 0130 Transaction tr(db.get(), Transaction::ReadWrite); 0131 tr.addDocument(doc1); 0132 tr.addDocument(doc2); 0133 tr.commit(); 0134 } 0135 0136 Transaction tr(db.get(), Transaction::ReadOnly); 0137 0138 quint64 id1 = doc1.id(); 0139 quint64 id2 = doc2.id(); 0140 0141 DBState state; 0142 state.postingDb = {{"a", {id1, id2}}, {"abc", {id1}}, {"abcd", {id2}}, {"dab", {id1, id2}}, {"file1", {id1}}, {"file2", {id2}}}; 0143 state.positionDb = {}; 0144 state.docTermsDb = {{id1, {"a", "abc", "dab"}}, {id2, {"a", "abcd", "dab"}}}; 0145 state.docFileNameTermsDb = {{id1, {"file1"}}, {id2, {"file2"}}}; 0146 state.docXAttrTermsDb = {}; 0147 state.docTimeDb = {{id1, DocumentTimeDB::TimeInfo(5, 1)}, {id2, DocumentTimeDB::TimeInfo(6, 2)}}; 0148 state.mtimeDb = {{5, id1}, {6, id2}}; 0149 0150 DBState actualState = DBState::fromTransaction(&tr); 0151 QVERIFY(DBState::debugCompare(actualState, state)); 0152 } 0153 0154 void WriteTransactionTest::testAddAndRemoveOneDocument() 0155 { 0156 const QString url1(dir->path() + QStringLiteral("/file1")); 0157 0158 Document doc1 = createDocument(url1, 5, 1, {"a", "abc", "dab"}, {"file1"}, {}, m_dirId); 0159 0160 { 0161 Transaction tr(db.get(), Transaction::ReadWrite); 0162 tr.addDocument(doc1); 0163 tr.commit(); 0164 } 0165 { 0166 Transaction tr(db.get(), Transaction::ReadWrite); 0167 tr.removeDocument(doc1.id()); 0168 tr.commit(); 0169 } 0170 0171 Transaction tr(db.get(), Transaction::ReadOnly); 0172 DBState actualState = DBState::fromTransaction(&tr); 0173 QVERIFY(DBState::debugCompare(actualState, DBState())); 0174 } 0175 0176 void WriteTransactionTest::testAddAndReplaceOneDocument() 0177 { 0178 const QString url1(dir->path() + QStringLiteral("/file1")); 0179 0180 Document doc1 = createDocument(url1, 5, 1, {"a", "abc", "dab"}, {"file1"}, {}, m_dirId); 0181 Document doc2 = createDocument(url1, 6, 2, {"a", "abc", "xxx"}, {"file1", "yyy"}, {}, m_dirId); 0182 quint64 id = doc1.id(); 0183 doc2.setId(id); 0184 0185 { 0186 Transaction tr(db.get(), Transaction::ReadWrite); 0187 tr.addDocument(doc1); 0188 tr.commit(); 0189 } 0190 0191 DBState state; 0192 state.postingDb = {{"a", {id}}, {"abc", {id}}, {"dab", {id}}, {"file1", {id}} }; 0193 state.positionDb = {}; 0194 state.docTermsDb = {{id, {"a", "abc", "dab"} }}; 0195 state.docFileNameTermsDb = {{id, {"file1"} }}; 0196 state.docXAttrTermsDb = {}; 0197 state.docTimeDb = {{id, DocumentTimeDB::TimeInfo(5, 1)}}; 0198 state.mtimeDb = {{5, id}}; 0199 0200 { 0201 Transaction tr(db.get(), Transaction::ReadOnly); 0202 DBState actualState = DBState::fromTransaction(&tr); 0203 QVERIFY(DBState::debugCompare(actualState, state)); 0204 } 0205 0206 { 0207 Transaction tr(db.get(), Transaction::ReadWrite); 0208 tr.replaceDocument(doc2, DocumentOperation::Everything); 0209 tr.commit(); 0210 } 0211 0212 state.postingDb = {{"a", {id}}, {"abc", {id}}, {"xxx", {id}}, {"file1", {id}}, {"yyy", {id}} }; 0213 state.docTermsDb = {{id, {"a", "abc", "xxx"} }}; 0214 state.docFileNameTermsDb = {{id, {"file1", "yyy"} }}; 0215 state.docTimeDb = {{id, DocumentTimeDB::TimeInfo(6, 2)}}; 0216 state.mtimeDb = {{6, id}}; 0217 0218 Transaction tr(db.get(), Transaction::ReadOnly); 0219 DBState actualState = DBState::fromTransaction(&tr); 0220 QVERIFY(DBState::debugCompare(actualState, state)); 0221 } 0222 0223 void WriteTransactionTest::testRemoveRecursively() 0224 { 0225 const QString path = dir->path(); 0226 const QString url1(path + QStringLiteral("/file1")); 0227 const QString dirPath(path + QStringLiteral("/dir")); 0228 const QString url2(dirPath + QStringLiteral("/file1")); 0229 0230 Document doc1 = createDocument(url1, 5, 1, {"a", "abc", "dab"}, {"file1"}, {}, m_dirId); 0231 Document doc2 = createDocument(dirPath, 5, 1, {"a"}, {"dir"}, {}, m_dirId); 0232 Document doc3 = createDocument(url2, 5, 1, {"a", "abc", "dab"}, {"file1"}, {}, doc2.id()); 0233 0234 { 0235 Transaction tr(db.get(), Transaction::ReadWrite); 0236 tr.addDocument(doc1); 0237 tr.addDocument(doc2); 0238 tr.addDocument(doc3); 0239 tr.commit(); 0240 } 0241 { 0242 Transaction tr(db.get(), Transaction::ReadWrite); 0243 tr.removeRecursively(m_dirId); 0244 tr.commit(); 0245 } 0246 0247 Transaction tr(db.get(), Transaction::ReadOnly); 0248 DBState actualState = DBState::fromTransaction(&tr); 0249 QVERIFY(DBState::debugCompare(actualState, DBState())); 0250 } 0251 0252 void WriteTransactionTest::testDocumentId() 0253 { 0254 const QString url1(dir->path() + QStringLiteral("/file1")); 0255 0256 Document doc1 = createDocument(url1, 5, 1, {"a", "abc", "dab"}, {"file1"}, {}, m_dirId); 0257 0258 { 0259 Transaction tr(db.get(), Transaction::ReadWrite); 0260 tr.addDocument(doc1); 0261 tr.commit(); 0262 } 0263 0264 Transaction tr(db.get(), Transaction::ReadOnly); 0265 QCOMPARE(tr.documentId(doc1.url()), doc1.id()); 0266 } 0267 0268 void WriteTransactionTest::testTermPositions() 0269 { 0270 const QString url1(dir->path() + QStringLiteral("/file1")); 0271 const QString url2(dir->path() + QStringLiteral("/file2")); 0272 const QString url3(dir->path() + QStringLiteral("/file3")); 0273 0274 Document doc1 = createDocument(url1, 5, 1, {"a", "abc", "dab"}, {"file1"}, {}, m_dirId); 0275 quint64 id1 = doc1.id(); 0276 Document doc2 = createDocument(url2, 5, 2, {"a", "abcd"}, {"file2"}, {}, m_dirId); 0277 quint64 id2 = doc2.id(); 0278 Document doc3 = createDocument(url3, 6, 3, {"dab"}, {"file3"}, {}, m_dirId); 0279 quint64 id3 = doc3.id(); 0280 0281 { 0282 Transaction tr(db.get(), Transaction::ReadWrite); 0283 tr.addDocument(doc1); 0284 tr.addDocument(doc2); 0285 tr.addDocument(doc3); 0286 tr.commit(); 0287 } 0288 0289 DBState state; 0290 state.postingDb = {{"a", {id1, id2}}, {"abc", {id1}}, {"abcd", {id2}}, {"dab", {id1, id3}}, {"file1", {id1}}, {"file2", {id2}}, {"file3", {id3}} }; 0291 state.positionDb = {}; 0292 state.docTermsDb = {{id1, {"a", "abc", "dab"}}, {id2, {"a", "abcd"}}, {id3, {"dab"}} }; 0293 state.docFileNameTermsDb = {{id1, {"file1"}}, {id2, {"file2"}}, {id3, {"file3"}} }; 0294 state.docXAttrTermsDb = {}; 0295 state.docTimeDb = {{id1, DocumentTimeDB::TimeInfo(5, 1)}, {id2, DocumentTimeDB::TimeInfo(5, 2)}, {id3, DocumentTimeDB::TimeInfo(6, 3)} }; 0296 state.mtimeDb = {{5, id1}, {5, id2}, {6, id3} }; 0297 0298 { 0299 Transaction tr(db.get(), Transaction::ReadOnly); 0300 DBState actualState = DBState::fromTransaction(&tr); 0301 QVERIFY(DBState::debugCompare(actualState, state)); 0302 } 0303 0304 Document doc1_clone = doc1; // save state for later reset 0305 0306 for (auto pos : {1, 3, 6}) { 0307 doc1.addPositionTerm("a", pos); 0308 } 0309 for (auto pos : {2, 4, 5}) { 0310 doc1.addPositionTerm("abc", pos); 0311 } 0312 for (auto pos : {12, 14, 15}) { 0313 doc1.addPositionTerm("dab", pos); 0314 } 0315 for (auto pos : {11, 12}) { 0316 doc3.addPositionTerm("dab", pos); 0317 } 0318 state.positionDb["a"] = {PositionInfo(id1, {1, 3, 6})}; 0319 state.positionDb["abc"] = {PositionInfo(id1, {2, 4, 5})}; 0320 if (id1 < id3) { 0321 state.positionDb["dab"] = {PositionInfo(id1, {12, 14, 15}), PositionInfo(id3, {11, 12})}; 0322 } else { 0323 state.positionDb["dab"] = {PositionInfo(id3, {11, 12}), PositionInfo(id1, {12, 14, 15})}; 0324 } 0325 0326 { 0327 Transaction tr(db.get(), Transaction::ReadWrite); 0328 tr.replaceDocument(doc1, DocumentOperation::Everything); 0329 tr.replaceDocument(doc3, DocumentOperation::Everything); 0330 tr.commit(); 0331 } 0332 { 0333 Transaction tr(db.get(), Transaction::ReadOnly); 0334 DBState actualState = DBState::fromTransaction(&tr); 0335 QVERIFY(DBState::debugCompare(actualState, state)); 0336 } 0337 0338 for (auto pos : {11, 12}) { // extend 0339 doc1.addPositionTerm("abc", pos); 0340 } 0341 for (auto pos : {16, 17}) { // extend, make sure positions for doc3 are kept 0342 doc1.addPositionTerm("dab", pos); 0343 } 0344 for (auto pos : {7, 8, 9}) { // add positions 0345 doc2.addPositionTerm("a", pos); 0346 } 0347 for (auto pos : {7, 8, 9}) { // add new term with positions 0348 doc2.addPositionTerm("abc", pos); 0349 } 0350 0351 Document doc2_clone = doc2; // save state for later reset 0352 doc2.addPositionTerm("abcd", 500); // add position for existing term 0353 0354 state.postingDb = {{"a", {id1, id2}}, {"abc", {id1, id2}}, {"abcd", {id2}}, {"dab", {id1, id3}}, {"file1", {id1}}, {"file2", {id2}}, {"file3", {id3}} }; 0355 state.docTermsDb = {{id1, {"a", "abc", "dab"}}, {id2, {"a", "abc", "abcd"}}, {id3, {"dab"}} }; 0356 if (id1 < id2) { 0357 state.positionDb["a"] = {PositionInfo(id1, {1, 3, 6}), PositionInfo(id2, {7, 8, 9})}; 0358 state.positionDb["abc"] = {PositionInfo(id1, {2, 4, 5, 11, 12}), PositionInfo(id2, {7, 8, 9})}; 0359 } else { 0360 state.positionDb["a"] = {PositionInfo(id2, {7, 8, 9}), PositionInfo(id1, {1, 3, 6})}; 0361 state.positionDb["abc"] = {PositionInfo(id2, {7, 8, 9}), PositionInfo(id1, {2, 4, 5, 11, 12})}; 0362 } 0363 if (id1 < id3) { 0364 state.positionDb["dab"] = {PositionInfo(id1, {12, 14, 15, 16, 17}), PositionInfo(id3, {11, 12})}; 0365 } else { 0366 state.positionDb["dab"] = {PositionInfo(id3, {11, 12}), PositionInfo(id1, {12, 14, 15, 16, 17})}; 0367 } 0368 state.positionDb["abcd"] = {PositionInfo(id2, {500})}; 0369 0370 { 0371 Transaction tr(db.get(), Transaction::ReadWrite); 0372 tr.replaceDocument(doc1, DocumentOperation::Everything); 0373 tr.replaceDocument(doc2, DocumentOperation::Everything); 0374 tr.commit(); 0375 } 0376 { 0377 Transaction tr(db.get(), Transaction::ReadOnly); 0378 DBState actualState = DBState::fromTransaction(&tr); 0379 QVERIFY(DBState::debugCompare(actualState, state)); 0380 } 0381 0382 // Reset some positions of doc1 0383 for (auto pos : {2, 4, 5, 11, 12}) { // keep "abc" 0384 doc1_clone.addPositionTerm("abc", pos); 0385 } 0386 for (auto pos : {12, 14, 17}) { // remove 15, 16 from dab 0387 doc1_clone.addPositionTerm("dab", pos); 0388 } 0389 state.positionDb["a"] = {PositionInfo(id2, {7, 8, 9})}; // doc1 removed 0390 state.positionDb.remove("abcd"); // positions for abcd removed 0391 if (id1 < id3) { 0392 state.positionDb["dab"] = {PositionInfo(id1, {12, 14, 17}), PositionInfo(id3, {11, 12})}; 0393 } else { 0394 state.positionDb["dab"] = {PositionInfo(id3, {11, 12}), PositionInfo(id1, {12, 14, 17})}; 0395 } 0396 0397 { 0398 Transaction tr(db.get(), Transaction::ReadWrite); 0399 tr.replaceDocument(doc1_clone, DocumentOperation::Everything); 0400 tr.replaceDocument(doc2_clone, DocumentOperation::Everything); 0401 tr.commit(); 0402 } 0403 { 0404 Transaction tr(db.get(), Transaction::ReadOnly); 0405 DBState actualState = DBState::fromTransaction(&tr); 0406 QVERIFY(DBState::debugCompare(actualState, state)); 0407 } 0408 } 0409 0410 void WriteTransactionTest::testIdempotentDocumentChange() 0411 { 0412 const QString url1(dir->path() + QStringLiteral("/file1")); 0413 0414 Document doc1 = createDocument(url1, 5, 1, {"a", "abc", "dab"}, {"file1"}, {}, m_dirId); 0415 Document doc2 = createDocument(url1, 5, 1, {"a", "abcd", "dab"}, {"file1"}, {}, m_dirId); 0416 quint64 id = doc1.id(); 0417 doc2.setId(doc1.id()); 0418 0419 { 0420 Transaction tr(db.get(), Transaction::ReadWrite); 0421 tr.addDocument(doc1); 0422 tr.commit(); 0423 } 0424 0425 DBState state; 0426 state.postingDb = {{"a", {id}}, {"abc", {id}}, {"dab", {id}}, {"file1", {id}} }; 0427 state.positionDb = {}; 0428 state.docTermsDb = {{id, {"a", "abc", "dab"} }}; 0429 state.docFileNameTermsDb = {{id, {"file1"} }}; 0430 state.docXAttrTermsDb = {}; 0431 state.docTimeDb = {{id, DocumentTimeDB::TimeInfo(5, 1)}}; 0432 state.mtimeDb = {{5, id}}; 0433 0434 { 0435 Transaction tr(db.get(), Transaction::ReadOnly); 0436 DBState actualState = DBState::fromTransaction(&tr); 0437 QVERIFY(DBState::debugCompare(actualState, state)); 0438 } 0439 0440 { 0441 Transaction tr(db.get(), Transaction::ReadWrite); 0442 tr.replaceDocument(doc2, DocumentOperation::Everything); 0443 tr.replaceDocument(doc2, DocumentOperation::Everything); 0444 tr.commit(); 0445 } 0446 0447 state.postingDb = {{"a", {id}}, {"abcd", {id}}, {"dab", {id}}, {"file1", {id}} }; 0448 state.docTermsDb = {{id, {"a", "abcd", "dab"} }}; 0449 0450 { 0451 Transaction tr(db.get(), Transaction::ReadOnly); 0452 DBState actualState = DBState::fromTransaction(&tr); 0453 QVERIFY(DBState::debugCompare(actualState, state)); 0454 } 0455 } 0456 0457 void WriteTransactionTest::testTransactionReset() 0458 { 0459 const QString url1(dir->path() + QStringLiteral("/file1")); 0460 0461 Document doc1 = createDocument(url1, 0, 1, {"a", "abc", "dab"}, {"file1"}, {}, m_dirId); 0462 0463 { 0464 Transaction tr(db.get(), Transaction::ReadWrite); 0465 tr.addDocument(doc1); 0466 tr.commit(); 0467 } 0468 0469 Transaction tr(db.get(), Transaction::ReadWrite); 0470 for (int i = 0; i < 3; i++) { 0471 doc1.setMTime(i); 0472 tr.replaceDocument(doc1, DocumentOperation::DocumentTime); 0473 if (i % 2) { 0474 tr.commit(); 0475 tr.reset(Transaction::ReadWrite); 0476 } 0477 } 0478 tr.commit(); 0479 } 0480 0481 QTEST_MAIN(WriteTransactionTest) 0482 0483 #include "writetransactiontest.moc"