1 /*
2     SPDX-FileCopyrightText: 2014 Vishesh Handa <me@vhanda.in>
3 
4     SPDX-License-Identifier: LGPL-2.1-or-later
5 */
6 
7 #include "filewatch.h"
8 #include "fileindexerconfigutils.h"
9 #include "database.h"
10 #include "fileindexerconfig.h"
11 #include "pendingfilequeue.h"
12 
13 #include <QTest>
14 #include <QSignalSpy>
15 #include <QTemporaryDir>
16 #include <KFileMetaData/UserMetaData>
17 
18 namespace Baloo {
19 
20 class FileWatchTest : public QObject
21 {
22     Q_OBJECT
23 private Q_SLOTS:
24 
25     void testFileCreation();
26     void testConfigChange();
27 };
28 
29 }
30 
31 using namespace Baloo;
32 
33 namespace {
createFile(const QString & fileUrl)34     bool createFile(const QString& fileUrl) {
35         QFile f1(fileUrl);
36         f1.open(QIODevice::WriteOnly);
37         f1.close();
38         return QFile::exists(fileUrl);
39     }
40 
modifyFile(const QString & fileUrl)41     void modifyFile(const QString& fileUrl) {
42         QFile f1(fileUrl);
43         f1.open(QIODevice::Append | QIODevice::Text);
44 
45         QTextStream stream(&f1);
46         stream << "1";
47     }
48 }
49 
testFileCreation()50 void FileWatchTest::testFileCreation()
51 {
52     QTemporaryDir includeDir;
53 
54     QStringList includeFolders;
55     includeFolders << includeDir.path();
56 
57     QStringList excludeFolders;
58     Test::writeIndexerConfig(includeFolders, excludeFolders);
59 
60     QTemporaryDir dbDir;
61     Database db(dbDir.path());
62     db.open(Baloo::Database::CreateDatabase);
63 
64     FileIndexerConfig config;
65 
66     FileWatch fileWatch(&db, &config);
67     fileWatch.m_pendingFileQueue->setMaximumTimeout(0);
68     fileWatch.m_pendingFileQueue->setMinimumTimeout(0);
69     fileWatch.m_pendingFileQueue->setTrackingTime(0);
70 
71     QSignalSpy spy(&fileWatch, SIGNAL(installedWatches()));
72     QVERIFY(spy.isValid());
73 
74     fileWatch.updateIndexedFoldersWatches();
75     QVERIFY(spy.count() || spy.wait());
76 
77     QSignalSpy spyIndexNew(&fileWatch, SIGNAL(indexNewFile(QString)));
78     QSignalSpy spyIndexModified(&fileWatch, SIGNAL(indexModifiedFile(QString)));
79     QSignalSpy spyIndexXattr(&fileWatch, SIGNAL(indexXAttr(QString)));
80 
81     QVERIFY(spyIndexNew.isValid());
82     QVERIFY(spyIndexModified.isValid());
83     QVERIFY(spyIndexXattr.isValid());
84 
85     // Create a file and see if it is indexed
86     QString fileUrl(includeDir.path() + QStringLiteral("/t1"));
87     QVERIFY(createFile(fileUrl));
88 
89     QVERIFY(spyIndexNew.wait());
90     QCOMPARE(spyIndexNew.count(), 1);
91     QCOMPARE(spyIndexModified.count(), 0);
92     QCOMPARE(spyIndexXattr.count(), 0);
93 
94     spyIndexNew.clear();
95     spyIndexModified.clear();
96     spyIndexXattr.clear();
97 
98     //
99     // Modify the file
100     //
101     modifyFile(fileUrl);
102 
103     QVERIFY(spyIndexModified.wait());
104     QCOMPARE(spyIndexNew.count(), 0);
105     QCOMPARE(spyIndexModified.count(), 1);
106     QCOMPARE(spyIndexXattr.count(), 0);
107 
108     spyIndexNew.clear();
109     spyIndexModified.clear();
110     spyIndexXattr.clear();
111 
112     //
113     // Set an Xattr
114     //
115     KFileMetaData::UserMetaData umd(fileUrl);
116     if (!umd.isSupported()) {
117         qWarning() << "Xattr not supported on this filesystem:" << fileUrl;
118         return;
119     }
120 
121     const QString userComment(QStringLiteral("UserComment"));
122     QVERIFY(umd.setUserComment(userComment) == KFileMetaData::UserMetaData::NoError);
123 
124     QVERIFY(spyIndexXattr.wait());
125     QCOMPARE(spyIndexNew.count(), 0);
126     QCOMPARE(spyIndexModified.count(), 0);
127     QCOMPARE(spyIndexXattr.count(), 1);
128 
129     spyIndexNew.clear();
130     spyIndexModified.clear();
131     spyIndexXattr.clear();
132 }
133 
testConfigChange()134 void FileWatchTest::testConfigChange()
135 {
136     QTemporaryDir tmpDir;
137 
138     Database db(tmpDir.path());
139     db.open(Baloo::Database::CreateDatabase);
140 
141     QString d1 = tmpDir.path() + QStringLiteral("/d1");
142     QString d2 = tmpDir.path() + QStringLiteral("/d2");
143     QString d11 = tmpDir.path() + QStringLiteral("/d1/d11");
144     QString d21 = tmpDir.path() + QStringLiteral("/d2/d21");
145     QString d22 = tmpDir.path() + QStringLiteral("/d2/d22");
146 
147     QDir().mkpath(d11);
148     QDir().mkpath(d21);
149     QDir().mkpath(d22);
150 
151     // parameters: includeFolders list, excludeFolders list
152     Test::writeIndexerConfig({d1, d2}, {d11, d21});
153     FileIndexerConfig config;
154 
155     FileWatch fileWatch(&db, &config);
156     fileWatch.m_pendingFileQueue->setMaximumTimeout(0);
157     fileWatch.m_pendingFileQueue->setMinimumTimeout(0);
158     fileWatch.m_pendingFileQueue->setTrackingTime(0);
159 
160     QSignalSpy spy(&fileWatch, SIGNAL(installedWatches()));
161     QVERIFY(spy.isValid());
162 
163     fileWatch.updateIndexedFoldersWatches();
164     QVERIFY(spy.count() || spy.wait());
165 
166     QSignalSpy spyIndexNew(&fileWatch, SIGNAL(indexNewFile(QString)));
167     QVERIFY(spyIndexNew.isValid());
168     QVERIFY(createFile(d1 + QStringLiteral("/t1")));
169     QVERIFY(createFile(d2 + QStringLiteral("/t2")));
170 
171     QVERIFY(spyIndexNew.wait());
172     QCOMPARE(spyIndexNew.count(), 2);
173     spyIndexNew.clear();
174 
175     // dir d22 is not yet excluded, so one event is expected
176     QVERIFY(createFile(d11 + QStringLiteral("/tx1")));
177     QVERIFY(createFile(d21 + QStringLiteral("/tx2")));
178     QVERIFY(createFile(d22 + QStringLiteral("/tx3")));
179 
180     QVERIFY(spyIndexNew.wait());
181     QCOMPARE(spyIndexNew.count(), 1);
182     QList<QVariant> event = spyIndexNew.at(0);
183     QCOMPARE(event.at(0).toString(), d22 + QStringLiteral("/tx3"));
184     spyIndexNew.clear();
185 
186     Test::writeIndexerConfig({d2}, {d22});
187     config.forceConfigUpdate();
188     fileWatch.updateIndexedFoldersWatches();
189 
190     // dir d1 is no longer included
191     QVERIFY(createFile(d1 + QStringLiteral("/tx1a")));
192     QVERIFY(createFile(d2 + QStringLiteral("/tx2a")));
193     QVERIFY(spyIndexNew.wait());
194     QList<QString> result;
195     for (const QList<QVariant>& event : std::as_const(spyIndexNew)) {
196 	result.append(event.at(0).toString());
197     }
198     QCOMPARE(result, {d2 + QStringLiteral("/tx2a")});
199     spyIndexNew.clear();
200     result.clear();
201 
202     // d11 is implicitly excluded, as d1 is no longer included
203     // d22 is explicitly excluded now, d21 is included
204     QVERIFY(createFile(d11 + QStringLiteral("/tx1b")));
205     QVERIFY(createFile(d21 + QStringLiteral("/tx2b")));
206     QVERIFY(createFile(d22 + QStringLiteral("/tx3b")));
207 
208     QVERIFY(spyIndexNew.wait(500));
209     for (const QList<QVariant>& event : std::as_const(spyIndexNew)) {
210 	result.append(event.at(0).toString());
211     }
212     QCOMPARE(result, {d21 + QStringLiteral("/tx2b")});
213 }
214 
215 QTEST_MAIN(FileWatchTest)
216 
217 #include "filewatchtest.moc"
218