1 /*
2 * This software is in the public domain, furnished "as is", without technical
3 * support, and with no warranty, express or implied, as to its usefulness for
4 * any purpose.
5 *
6 */
7
8 #include <QtTest>
9
10 #include "folderwatcher.h"
11 #include "common/utility.h"
12
touch(const QString & file)13 void touch(const QString &file)
14 {
15 #ifdef Q_OS_WIN
16 OCC::Utility::writeRandomFile(file);
17 #else
18 QString cmd;
19 cmd = QString("touch %1").arg(file);
20 qDebug() << "Command: " << cmd;
21 system(cmd.toLocal8Bit());
22 #endif
23 }
24
mkdir(const QString & file)25 void mkdir(const QString &file)
26 {
27 #ifdef Q_OS_WIN
28 QDir dir;
29 dir.mkdir(file);
30 #else
31 QString cmd = QString("mkdir %1").arg(file);
32 qDebug() << "Command: " << cmd;
33 system(cmd.toLocal8Bit());
34 #endif
35 }
36
rmdir(const QString & file)37 void rmdir(const QString &file)
38 {
39 #ifdef Q_OS_WIN
40 QDir dir;
41 dir.rmdir(file);
42 #else
43 QString cmd = QString("rmdir %1").arg(file);
44 qDebug() << "Command: " << cmd;
45 system(cmd.toLocal8Bit());
46 #endif
47 }
48
rm(const QString & file)49 void rm(const QString &file)
50 {
51 #ifdef Q_OS_WIN
52 QFile::remove(file);
53 #else
54 QString cmd = QString("rm %1").arg(file);
55 qDebug() << "Command: " << cmd;
56 system(cmd.toLocal8Bit());
57 #endif
58 }
59
mv(const QString & file1,const QString & file2)60 void mv(const QString &file1, const QString &file2)
61 {
62 #ifdef Q_OS_WIN
63 QFile::rename(file1, file2);
64 #else
65 QString cmd = QString("mv %1 %2").arg(file1, file2);
66 qDebug() << "Command: " << cmd;
67 system(cmd.toLocal8Bit());
68 #endif
69 }
70
71 using namespace OCC;
72
73 class TestFolderWatcher : public QObject
74 {
75 Q_OBJECT
76
77 QTemporaryDir _root;
78 QString _rootPath;
79 QScopedPointer<FolderWatcher> _watcher;
80 QScopedPointer<QSignalSpy> _pathChangedSpy;
81
waitForPathChanged(const QString & path)82 bool waitForPathChanged(const QString &path)
83 {
84 QElapsedTimer t;
85 t.start();
86 while (t.elapsed() < 5000) {
87 // Check if it was already reported as changed by the watcher
88 for (int i = 0; i < _pathChangedSpy->size(); ++i) {
89 const auto &args = _pathChangedSpy->at(i);
90 if (args.first().toString() == path)
91 return true;
92 }
93 // Wait a bit and test again (don't bother checking if we timed out or not)
94 _pathChangedSpy->wait(200);
95 }
96 return false;
97 }
98
99 #ifdef Q_OS_LINUX
100 #define CHECK_WATCH_COUNT(n) QCOMPARE(_watcher->testLinuxWatchCount(), (n))
101 #else
102 #define CHECK_WATCH_COUNT(n) do {} while (false)
103 #endif
104
105 public:
TestFolderWatcher()106 TestFolderWatcher()
107 {
108 QDir rootDir(_root.path());
109 _rootPath = rootDir.canonicalPath();
110 qDebug() << "creating test directory tree in " << _rootPath;
111
112 rootDir.mkpath("a1/b1/c1");
113 rootDir.mkpath("a1/b1/c2");
114 rootDir.mkpath("a1/b2/c1");
115 rootDir.mkpath("a1/b3/c3");
116 rootDir.mkpath("a2/b3/c3");
117 Utility::writeRandomFile( _rootPath+"/a1/random.bin");
118 Utility::writeRandomFile( _rootPath+"/a1/b2/todelete.bin");
119 Utility::writeRandomFile( _rootPath+"/a2/renamefile");
120 Utility::writeRandomFile( _rootPath+"/a1/movefile");
121
122 _watcher.reset(new FolderWatcher);
123 _watcher->init(_rootPath);
124 _pathChangedSpy.reset(new QSignalSpy(_watcher.data(), SIGNAL(pathChanged(QString))));
125 }
126
countFolders(const QString & path)127 int countFolders(const QString &path)
128 {
129 int n = 0;
130 for (const auto &sub : QDir(path).entryList(QDir::Dirs | QDir::NoDotAndDotDot))
131 n += 1 + countFolders(path + '/' + sub);
132 return n;
133 }
134
135 private slots:
init()136 void init()
137 {
138 _pathChangedSpy->clear();
139 CHECK_WATCH_COUNT(countFolders(_rootPath) + 1);
140 }
141
cleanup()142 void cleanup()
143 {
144 CHECK_WATCH_COUNT(countFolders(_rootPath) + 1);
145 }
146
testACreate()147 void testACreate() { // create a new file
148 QString file(_rootPath + "/foo.txt");
149 QString cmd;
150 cmd = QString("echo \"xyz\" > %1").arg(file);
151 qDebug() << "Command: " << cmd;
152 system(cmd.toLocal8Bit());
153
154 QVERIFY(waitForPathChanged(file));
155 }
156
testATouch()157 void testATouch() { // touch an existing file.
158 QString file(_rootPath + "/a1/random.bin");
159 touch(file);
160 QVERIFY(waitForPathChanged(file));
161 }
162
testMove3LevelDirWithFile()163 void testMove3LevelDirWithFile() {
164 QString file(_rootPath + "/a0/b/c/empty.txt");
165 mkdir(_rootPath + "/a0");
166 mkdir(_rootPath + "/a0/b");
167 mkdir(_rootPath + "/a0/b/c");
168 touch(file);
169 mv(_rootPath + "/a0", _rootPath + "/a");
170 QVERIFY(waitForPathChanged(_rootPath + "/a/b/c/empty.txt"));
171 }
172
173
testCreateADir()174 void testCreateADir() {
175 QString file(_rootPath+"/a1/b1/new_dir");
176 mkdir(file);
177 QVERIFY(waitForPathChanged(file));
178
179 // Notifications from that new folder arrive too
180 QString file2(_rootPath + "/a1/b1/new_dir/contained");
181 touch(file2);
182 QVERIFY(waitForPathChanged(file2));
183 }
184
testRemoveADir()185 void testRemoveADir() {
186 QString file(_rootPath+"/a1/b3/c3");
187 rmdir(file);
188 QVERIFY(waitForPathChanged(file));
189 }
190
testRemoveAFile()191 void testRemoveAFile() {
192 QString file(_rootPath+"/a1/b2/todelete.bin");
193 QVERIFY(QFile::exists(file));
194 rm(file);
195 QVERIFY(!QFile::exists(file));
196
197 QVERIFY(waitForPathChanged(file));
198 }
199
testRenameAFile()200 void testRenameAFile() {
201 QString file1(_rootPath+"/a2/renamefile");
202 QString file2(_rootPath+"/a2/renamefile.renamed");
203 QVERIFY(QFile::exists(file1));
204 mv(file1, file2);
205 QVERIFY(QFile::exists(file2));
206
207 QVERIFY(waitForPathChanged(file1));
208 QVERIFY(waitForPathChanged(file2));
209 }
210
testMoveAFile()211 void testMoveAFile() {
212 QString old_file(_rootPath+"/a1/movefile");
213 QString new_file(_rootPath+"/a2/movefile.renamed");
214 QVERIFY(QFile::exists(old_file));
215 mv(old_file, new_file);
216 QVERIFY(QFile::exists(new_file));
217
218 QVERIFY(waitForPathChanged(old_file));
219 QVERIFY(waitForPathChanged(new_file));
220 }
221
testRenameDirectorySameBase()222 void testRenameDirectorySameBase() {
223 QString old_file(_rootPath+"/a1/b1");
224 QString new_file(_rootPath+"/a1/brename");
225 QVERIFY(QFile::exists(old_file));
226 mv(old_file, new_file);
227 QVERIFY(QFile::exists(new_file));
228
229 QVERIFY(waitForPathChanged(old_file));
230 QVERIFY(waitForPathChanged(new_file));
231
232 // Verify that further notifications end up with the correct paths
233
234 QString file(_rootPath+"/a1/brename/c1/random.bin");
235 touch(file);
236 QVERIFY(waitForPathChanged(file));
237
238 QString dir(_rootPath+"/a1/brename/newfolder");
239 mkdir(dir);
240 QVERIFY(waitForPathChanged(dir));
241 }
242
testRenameDirectoryDifferentBase()243 void testRenameDirectoryDifferentBase() {
244 QString old_file(_rootPath+"/a1/brename");
245 QString new_file(_rootPath+"/bren");
246 QVERIFY(QFile::exists(old_file));
247 mv(old_file, new_file);
248 QVERIFY(QFile::exists(new_file));
249
250 QVERIFY(waitForPathChanged(old_file));
251 QVERIFY(waitForPathChanged(new_file));
252
253 // Verify that further notifications end up with the correct paths
254
255 QString file(_rootPath+"/bren/c1/random.bin");
256 touch(file);
257 QVERIFY(waitForPathChanged(file));
258
259 QString dir(_rootPath+"/bren/newfolder2");
260 mkdir(dir);
261 QVERIFY(waitForPathChanged(dir));
262 }
263 };
264
265 #ifdef Q_OS_MAC
266 QTEST_MAIN(TestFolderWatcher)
267 #else
268 QTEST_GUILESS_MAIN(TestFolderWatcher)
269 #endif
270
271 #include "testfolderwatcher.moc"
272