1 /*
2 * Copyright 2013, Haiku, Inc.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Ingo Weinhold, ingo_weinhold@gmx.de
7 */
8
9
10 #include <errno.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <sys/time.h>
14
15 #include <algorithm>
16 #include <set>
17 #include <vector>
18
19 #include <Directory.h>
20 #include <Entry.h>
21 #include <File.h>
22 #include <Looper.h>
23 #include <ObjectList.h>
24 #include <Path.h>
25 #include <String.h>
26
27 #include <AutoDeleter.h>
28 #include <AutoLocker.h>
29 #include <NotOwningEntryRef.h>
30 #include <PathMonitor.h>
31
32
33 using BPrivate::BPathMonitor;
34
35
36 static const char* const kTestBasePath = "/tmp/path-monitor-test";
37 static const bigtime_t kMaxNotificationDelay = 100000;
38
39
40 #define FATAL(...) \
41 do { \
42 throw FatalException( \
43 BString().SetToFormat("%s:%d: ", __FILE__, __LINE__) \
44 << BString().SetToFormat(__VA_ARGS__)); \
45 } while (false)
46
47 #define FATAL_IF_ERROR(error, ...) \
48 do { \
49 status_t _fatalError = (error); \
50 if (_fatalError < 0) { \
51 throw FatalException( \
52 BString().SetToFormat("%s:%d: ", __FILE__, __LINE__) \
53 << BString().SetToFormat(__VA_ARGS__) \
54 << BString().SetToFormat( \
55 ": %s\n", strerror(_fatalError))); \
56 } \
57 } while (false)
58
59 #define FATAL_IF_POSIX_ERROR(error, ...) \
60 if ((error) < 0) \
61 FATAL_IF_ERROR(errno, __VA_ARGS__)
62
63 #define FAIL(...) \
64 throw TestException(BString().SetToFormat(__VA_ARGS__))
65
66
67 struct TestException {
TestExceptionTestException68 TestException(const BString& message)
69 :
70 fMessage(message)
71 {
72 }
73
MessageTestException74 const BString& Message() const
75 {
76 return fMessage;
77 }
78
79 private:
80 BString fMessage;
81 };
82
83
84 struct FatalException {
FatalExceptionFatalException85 FatalException(const BString& message)
86 :
87 fMessage(message)
88 {
89 }
90
MessageFatalException91 const BString& Message() const
92 {
93 return fMessage;
94 }
95
96 private:
97 BString fMessage;
98 };
99
100
101 static BString
test_path(const BString & maybeRelativePath)102 test_path(const BString& maybeRelativePath)
103 {
104 if (maybeRelativePath.ByteAt(0) == '/')
105 return maybeRelativePath;
106
107 BString path;
108 path.SetToFormat("%s/%s", kTestBasePath, maybeRelativePath.String());
109 if (path.IsEmpty())
110 FATAL_IF_ERROR(B_NO_MEMORY, "Failed to make absolute path");
111 return path;
112 }
113
114
115 static BString
node_ref_to_string(const node_ref & nodeRef)116 node_ref_to_string(const node_ref& nodeRef)
117 {
118 return BString().SetToFormat("%" B_PRIdDEV ":%" B_PRIdINO, nodeRef.device,
119 nodeRef.node);
120 }
121
122
123 static BString
entry_ref_to_string(const entry_ref & entryRef)124 entry_ref_to_string(const entry_ref& entryRef)
125 {
126 return BString().SetToFormat("%" B_PRIdDEV ":%" B_PRIdINO ":\"%s\"",
127 entryRef.device, entryRef.directory, entryRef.name);
128 }
129
130
131 static BString
indented_string(const char * string,const char * indent,const char * firstIndent=NULL)132 indented_string(const char* string, const char* indent,
133 const char* firstIndent = NULL)
134 {
135 const char* end = string + strlen(string);
136 BString result;
137 const char* line = string;
138 while (line < end) {
139 const char* lineEnd = strchr(line, '\n');
140 lineEnd = lineEnd != NULL ? lineEnd + 1 : end;
141 result
142 << (line == string && firstIndent != NULL ? firstIndent : indent);
143 result.Append(line, lineEnd - line);
144 line = lineEnd;
145 }
146
147 return result;
148 }
149
150
151 static BString
message_to_string(const BMessage & message)152 message_to_string(const BMessage& message)
153 {
154 BString result;
155
156 char* name;
157 type_code typeCode;
158 int32 count;
159 for (int32 i = 0;
160 message.GetInfo(B_ANY_TYPE, i, &name, &typeCode, &count) == B_OK;
161 i++) {
162 if (i > 0)
163 result << '\n';
164
165 result << '"' << name << '"';
166 BString type;
167
168 switch (typeCode) {
169 case B_UINT8_TYPE:
170 case B_INT8_TYPE:
171 type << "int8";
172 break;
173
174 case B_UINT16_TYPE:
175 type = "u";
176 case B_INT16_TYPE:
177 type << "int16";
178 break;
179
180 case B_UINT32_TYPE:
181 type = "u";
182 case B_INT32_TYPE:
183 type << "int32";
184 break;
185
186 case B_UINT64_TYPE:
187 type = "u";
188 case B_INT64_TYPE:
189 type << "int64";
190 break;
191
192 case B_STRING_TYPE:
193 type = "string";
194 break;
195
196 default:
197 {
198 int code = (int)typeCode;
199 type.SetToFormat("'%02x%02x%02x%02x'", code >> 24,
200 (code >> 16) & 0xff, (code >> 8) & 0xff, code & 0xff);
201 break;
202 }
203 }
204
205 result << " (" << type << "):";
206
207 for (int32 k = 0; k < count; k++) {
208 BString value;
209 switch (typeCode) {
210 case B_UINT8_TYPE:
211 value << message.GetUInt8(name, k, 0);
212 break;
213 case B_INT8_TYPE:
214 value << message.GetInt8(name, k, 0);
215 break;
216 case B_UINT16_TYPE:
217 value << message.GetUInt16(name, k, 0);
218 break;
219 case B_INT16_TYPE:
220 value << message.GetInt16(name, k, 0);
221 break;
222 case B_UINT32_TYPE:
223 value << message.GetUInt32(name, k, 0);
224 break;
225 case B_INT32_TYPE:
226 value << message.GetInt32(name, k, 0);
227 break;
228 case B_UINT64_TYPE:
229 value << message.GetUInt64(name, k, 0);
230 break;
231 case B_INT64_TYPE:
232 value << message.GetInt64(name, k, 0);
233 break;
234 case B_STRING_TYPE:
235 value.SetToFormat("\"%s\"", message.GetString(name, k, ""));
236 break;
237 default:
238 {
239 const void* data;
240 ssize_t size;
241 if (message.FindData(name, typeCode, k, &data, &size)
242 != B_OK) {
243 value = "???";
244 break;
245 }
246
247 for (ssize_t l = 0; l < size; l++) {
248 uint8 v = ((const uint8*)data)[l];
249 value << BString().SetToFormat("%02x", v);
250 }
251 break;
252 }
253 }
254
255 if (k == 0 && count == 1) {
256 result << ' ' << value;
257 } else {
258 result << BString().SetToFormat("\n [%2" B_PRId32 "] ", k)
259 << value;
260 }
261 }
262 }
263
264 return result;
265 }
266
267
268 static BString
watch_flags_to_string(uint32 flags)269 watch_flags_to_string(uint32 flags)
270 {
271 BString result;
272 if ((flags & B_WATCH_NAME) != 0)
273 result << "name ";
274 if ((flags & B_WATCH_STAT) != 0)
275 result << "stat ";
276 if ((flags & B_WATCH_ATTR) != 0)
277 result << "attr ";
278 if ((flags & B_WATCH_DIRECTORY) != 0)
279 result << "dir ";
280 if ((flags & B_WATCH_RECURSIVELY) != 0)
281 result << "recursive ";
282 if ((flags & B_WATCH_FILES_ONLY) != 0)
283 result << "files-only ";
284 if ((flags & B_WATCH_DIRECTORIES_ONLY) != 0)
285 result << "dirs-only ";
286
287 if (!result.IsEmpty())
288 result.Truncate(result.Length() - 1);
289 return result;
290 }
291
292
293 struct MonitoringInfo {
MonitoringInfoMonitoringInfo294 MonitoringInfo()
295 {
296 }
297
MonitoringInfoMonitoringInfo298 MonitoringInfo(int32 opcode, const char* path)
299 :
300 fOpcode(opcode)
301 {
302 _Init(opcode, path);
303 }
304
MonitoringInfoMonitoringInfo305 MonitoringInfo(int32 opcode, const char* fromPath, const char* toPath)
306 {
307 _Init(opcode, toPath);
308
309 // init fFromEntryRef
310 BEntry entry;
311 FATAL_IF_ERROR(entry.SetTo(fromPath),
312 "Failed to init BEntry for \"%s\"", fromPath);
313 FATAL_IF_ERROR(entry.GetRef(&fFromEntryRef),
314 "Failed to get entry_ref for \"%s\"", fromPath);
315 }
316
ToStringMonitoringInfo317 BString ToString() const
318 {
319 switch (fOpcode) {
320 case B_ENTRY_CREATED:
321 case B_ENTRY_REMOVED:
322 return BString().SetToFormat("%s %s at %s",
323 fOpcode == B_ENTRY_CREATED ? "created" : "removed",
324 node_ref_to_string(fNodeRef).String(),
325 entry_ref_to_string(fEntryRef).String());
326
327 case B_ENTRY_MOVED:
328 return BString().SetToFormat("moved %s from %s to %s",
329 node_ref_to_string(fNodeRef).String(),
330 entry_ref_to_string(fFromEntryRef).String(),
331 entry_ref_to_string(fEntryRef).String());
332
333 case B_STAT_CHANGED:
334 return BString().SetToFormat("stat changed for %s",
335 node_ref_to_string(fNodeRef).String());
336
337 case B_ATTR_CHANGED:
338 return BString().SetToFormat("attr changed for %s",
339 node_ref_to_string(fNodeRef).String());
340
341 case B_DEVICE_MOUNTED:
342 return BString().SetToFormat("volume mounted");
343
344 case B_DEVICE_UNMOUNTED:
345 return BString().SetToFormat("volume unmounted");
346 }
347
348 return BString();
349 }
350
MatchesMonitoringInfo351 bool Matches(const BMessage& message) const
352 {
353 if (fOpcode != message.GetInt32("opcode", -1))
354 return false;
355
356 switch (fOpcode) {
357 case B_ENTRY_CREATED:
358 case B_ENTRY_REMOVED:
359 {
360 NotOwningEntryRef entryRef;
361 node_ref nodeRef;
362
363 if (message.FindInt32("device", &nodeRef.device) != B_OK
364 || message.FindInt64("node", &nodeRef.node) != B_OK
365 || message.FindInt64("directory", &entryRef.directory)
366 != B_OK
367 || message.FindString("name", (const char**)&entryRef.name)
368 != B_OK) {
369 return false;
370 }
371 entryRef.device = nodeRef.device;
372
373 return nodeRef == fNodeRef && entryRef == fEntryRef;
374 }
375
376 case B_ENTRY_MOVED:
377 {
378 NotOwningEntryRef fromEntryRef;
379 NotOwningEntryRef toEntryRef;
380 node_ref nodeRef;
381
382 if (message.FindInt32("node device", &nodeRef.device) != B_OK
383 || message.FindInt64("node", &nodeRef.node) != B_OK
384 || message.FindInt32("device", &fromEntryRef.device)
385 != B_OK
386 || message.FindInt64("from directory",
387 &fromEntryRef.directory) != B_OK
388 || message.FindInt64("to directory", &toEntryRef.directory)
389 != B_OK
390 || message.FindString("from name",
391 (const char**)&fromEntryRef.name) != B_OK
392 || message.FindString("name",
393 (const char**)&toEntryRef.name) != B_OK) {
394 return false;
395 }
396 toEntryRef.device = fromEntryRef.device;
397
398 return nodeRef == fNodeRef && toEntryRef == fEntryRef
399 && fromEntryRef == fFromEntryRef;
400 }
401
402 case B_STAT_CHANGED:
403 case B_ATTR_CHANGED:
404 {
405 node_ref nodeRef;
406
407 if (message.FindInt32("device", &nodeRef.device) != B_OK
408 || message.FindInt64("node", &nodeRef.node) != B_OK) {
409 return false;
410 }
411
412 return nodeRef == fNodeRef;
413 }
414
415 case B_DEVICE_MOUNTED:
416 case B_DEVICE_UNMOUNTED:
417 return true;
418 }
419
420 return false;
421 }
422
423 private:
_InitMonitoringInfo424 void _Init(int32 opcode, const char* path)
425 {
426 fOpcode = opcode;
427 BEntry entry;
428 FATAL_IF_ERROR(entry.SetTo(path), "Failed to init BEntry for \"%s\"",
429 path);
430 FATAL_IF_ERROR(entry.GetRef(&fEntryRef),
431 "Failed to get entry_ref for \"%s\"", path);
432 FATAL_IF_ERROR(entry.GetNodeRef(&fNodeRef),
433 "Failed to get node_ref for \"%s\"", path);
434 }
435
436 private:
437 int32 fOpcode;
438 node_ref fNodeRef;
439 entry_ref fEntryRef;
440 entry_ref fFromEntryRef;
441 };
442
443
444 struct MonitoringInfoSet {
MonitoringInfoSetMonitoringInfoSet445 MonitoringInfoSet()
446 {
447 }
448
AddMonitoringInfoSet449 MonitoringInfoSet& Add(const MonitoringInfo& info, bool expected = true)
450 {
451 if (expected)
452 fInfos.push_back(info);
453 return *this;
454 }
455
AddMonitoringInfoSet456 MonitoringInfoSet& Add(int32 opcode, const BString& path,
457 bool expected = true)
458 {
459 return Add(MonitoringInfo(opcode, test_path(path)), expected);
460 }
461
AddMonitoringInfoSet462 MonitoringInfoSet& Add(int32 opcode, const BString& fromPath,
463 const BString& toPath, bool expected = true)
464 {
465 return Add(MonitoringInfo(opcode, test_path(fromPath),
466 test_path(toPath)), expected);
467 }
468
IsEmptyMonitoringInfoSet469 bool IsEmpty() const
470 {
471 return fInfos.empty();
472 }
473
CountInfosMonitoringInfoSet474 int32 CountInfos() const
475 {
476 return fInfos.size();
477 }
478
InfoAtMonitoringInfoSet479 const MonitoringInfo& InfoAt(int32 index) const
480 {
481 return fInfos[index];
482 }
483
RemoveMonitoringInfoSet484 void Remove(int32 index)
485 {
486 fInfos.erase(fInfos.begin() + index);
487 }
488
ToStringMonitoringInfoSet489 BString ToString() const
490 {
491 BString result;
492 for (int32 i = 0; i < CountInfos(); i++) {
493 const MonitoringInfo& info = InfoAt(i);
494 if (i > 0)
495 result << '\n';
496 result << info.ToString();
497 }
498 return result;
499 }
500
501 private:
502 std::vector<MonitoringInfo> fInfos;
503 };
504
505
506 struct Test : private BLooper {
TestTest507 Test(const char* name)
508 :
509 fName(name),
510 fFlags(0),
511 fLooperThread(-1),
512 fNotifications(10, true),
513 fProcessedMonitoringInfos(),
514 fIsWatching(false)
515 {
516 }
517
InitTest518 void Init(uint32 flags)
519 {
520 fFlags = flags;
521
522 // delete and re-create the test directory
523 BEntry entry;
524 FATAL_IF_ERROR(entry.SetTo(kTestBasePath),
525 "Failed to init entry to \"%s\"", kTestBasePath);
526
527 if (entry.Exists())
528 _RemoveRecursively(entry);
529
530 _CreateDirectory(kTestBasePath);
531
532 fLooperThread = BLooper::Run();
533 if (fLooperThread < 0)
534 FATAL_IF_ERROR(fLooperThread, "Failed to init looper");
535 }
536
DeleteTest537 void Delete()
538 {
539 if (fIsWatching)
540 BPathMonitor::StopWatching(this);
541
542 if (fLooperThread < 0) {
543 delete this;
544 } else {
545 PostMessage(B_QUIT_REQUESTED);
546 wait_for_thread(fLooperThread, NULL);
547 }
548 }
549
DoTest550 void Do()
551 {
552 bool recursive = (fFlags & B_WATCH_RECURSIVELY) != 0;
553 DoInternal(recursive && (fFlags & B_WATCH_DIRECTORIES_ONLY) != 0,
554 recursive && (fFlags & B_WATCH_FILES_ONLY) != 0, recursive,
555 !recursive && (fFlags & B_WATCH_DIRECTORY) == 0,
556 (fFlags & B_WATCH_STAT) != 0);
557
558 // verify that there aren't any spurious notifications
559 snooze(kMaxNotificationDelay);
560
561 AutoLocker<BLooper> locker(this);
562 if (fNotifications.IsEmpty())
563 return;
564
565 BString pendingNotifications
566 = "unexpected notification(s) at end of test:";
567 for (int32 i = 0; BMessage* message = fNotifications.ItemAt(i); i++) {
568 pendingNotifications << '\n'
569 << indented_string(message_to_string(*message), " ", " * ");
570 }
571
572 FAIL("%s%s", pendingNotifications.String(),
573 _ProcessedInfosString().String());
574 }
575
NameTest576 const BString& Name() const
577 {
578 return fName;
579 }
580
581 protected:
~TestTest582 ~Test()
583 {
584 }
585
StartWatchingTest586 void StartWatching(const char* path)
587 {
588 BString absolutePath(test_path(path));
589 FATAL_IF_ERROR(BPathMonitor::StartWatching(absolutePath, fFlags, this),
590 "Failed to start watching \"%s\"", absolutePath.String());
591 fIsWatching = true;
592 }
593
CreateDirectoryTest594 MonitoringInfo CreateDirectory(const char* path)
595 {
596 BString absolutePath(test_path(path));
597 _CreateDirectory(absolutePath);
598 return MonitoringInfo(B_ENTRY_CREATED, absolutePath);
599 }
600
CreateFileTest601 MonitoringInfo CreateFile(const char* path)
602 {
603 BString absolutePath(test_path(path));
604 FATAL_IF_ERROR(
605 BFile().SetTo(absolutePath, B_CREATE_FILE | B_READ_WRITE),
606 "Failed to create file \"%s\"", absolutePath.String());
607 return MonitoringInfo(B_ENTRY_CREATED, absolutePath);
608 }
609
MoveEntryTest610 MonitoringInfo MoveEntry(const char* fromPath, const char* toPath)
611 {
612 BString absoluteFromPath(test_path(fromPath));
613 BString absoluteToPath(test_path(toPath));
614 FATAL_IF_POSIX_ERROR(rename(absoluteFromPath, absoluteToPath),
615 "Failed to move \"%s\" to \"%s\"", absoluteFromPath.String(),
616 absoluteToPath.String());
617 return MonitoringInfo(B_ENTRY_MOVED, absoluteFromPath, absoluteToPath);
618 }
619
RemoveEntryTest620 MonitoringInfo RemoveEntry(const char* path)
621 {
622 BString absolutePath(test_path(path));
623 MonitoringInfo info(B_ENTRY_REMOVED, absolutePath);
624 BEntry entry;
625 FATAL_IF_ERROR(entry.SetTo(absolutePath),
626 "Failed to init BEntry for \"%s\"", absolutePath.String());
627 FATAL_IF_ERROR(entry.Remove(),
628 "Failed to remove entry \"%s\"", absolutePath.String());
629 return info;
630 }
631
TouchEntryTest632 MonitoringInfo TouchEntry(const char* path)
633 {
634 BString absolutePath(test_path(path));
635 FATAL_IF_POSIX_ERROR(utimes(absolutePath, NULL),
636 "Failed to touch \"%s\"", absolutePath.String());
637 MonitoringInfo info(B_STAT_CHANGED, absolutePath);
638 return info;
639 }
640
ExpectNotificationTest641 void ExpectNotification(const MonitoringInfo& info, bool expected = true)
642 {
643 if (!expected)
644 return;
645
646 AutoLocker<BLooper> locker(this);
647 if (fNotifications.IsEmpty()) {
648 locker.Unlock();
649 snooze(kMaxNotificationDelay);
650 locker.Lock();
651 }
652
653 if (fNotifications.IsEmpty()) {
654 FAIL("missing notification, expected:\n %s",
655 info.ToString().String());
656 }
657
658 BMessage* message = fNotifications.RemoveItemAt(0);
659 ObjectDeleter<BMessage> messageDeleter(message);
660
661 if (!info.Matches(*message)) {
662 BString processedInfosString(_ProcessedInfosString());
663 FAIL("unexpected notification:\n expected:\n %s\n got:\n%s%s",
664 info.ToString().String(),
665 indented_string(message_to_string(*message), " ").String(),
666 processedInfosString.String());
667 }
668
669 fProcessedMonitoringInfos.Add(info);
670 }
671
ExpectNotificationsTest672 void ExpectNotifications(MonitoringInfoSet infos)
673 {
674 bool waited = false;
675 AutoLocker<BLooper> locker(this);
676
677 while (!infos.IsEmpty()) {
678 if (fNotifications.IsEmpty()) {
679 locker.Unlock();
680 if (!waited) {
681 snooze(kMaxNotificationDelay);
682 waited = true;
683 }
684 locker.Lock();
685 }
686
687 if (fNotifications.IsEmpty()) {
688 FAIL("missing notification(s), expected:\n%s",
689 indented_string(infos.ToString(), " ").String());
690 }
691
692 BMessage* message = fNotifications.RemoveItemAt(0);
693 ObjectDeleter<BMessage> messageDeleter(message);
694
695 bool foundMatch = false;
696 for (int32 i = 0; i < infos.CountInfos(); i++) {
697 const MonitoringInfo& info = infos.InfoAt(i);
698 if (info.Matches(*message)) {
699 infos.Remove(i);
700 foundMatch = true;
701 break;
702 }
703 }
704
705 if (foundMatch)
706 continue;
707
708 BString processedInfosString(_ProcessedInfosString());
709 FAIL("unexpected notification:\n expected:\n%s\n got:\n%s%s",
710 indented_string(infos.ToString(), " ").String(),
711 indented_string(message_to_string(*message), " ").String(),
712 processedInfosString.String());
713 }
714 }
715
716 virtual void DoInternal(bool directoriesOnly, bool filesOnly,
717 bool recursive, bool pathOnly, bool watchStat) = 0;
718
719 private:
720 typedef BObjectList<BMessage> MessageList;
721 typedef BObjectList<MonitoringInfo> MonitoringInfoList;
722
723 private:
MessageReceivedTest724 virtual void MessageReceived(BMessage* message)
725 {
726 switch (message->what) {
727 case B_PATH_MONITOR:
728 if (!fNotifications.AddItem(new BMessage(*message)))
729 FATAL_IF_ERROR(B_NO_MEMORY, "Failed to store notification");
730 break;
731
732 default:
733 BLooper::MessageReceived(message);
734 break;
735 }
736 }
737
738 private:
_CreateDirectoryTest739 void _CreateDirectory(const char* path)
740 {
741 FATAL_IF_ERROR(create_directory(path, 0755),
742 "Failed to create directory \"%s\"", path);
743 }
744
_RemoveRecursivelyTest745 void _RemoveRecursively(BEntry& entry)
746 {
747 // recurse, if the entry is a directory
748 if (entry.IsDirectory()) {
749 BDirectory directory;
750 FATAL_IF_ERROR(directory.SetTo(&entry),
751 "Failed to init BDirectory for \"%s\"",
752 BPath(&entry).Path());
753
754 BEntry childEntry;
755 while (directory.GetNextEntry(&childEntry) == B_OK)
756 _RemoveRecursively(childEntry);
757 }
758
759 // remove the entry
760 FATAL_IF_ERROR(entry.Remove(), "Failed to remove entry \"%s\"",
761 BPath(&entry).Path());
762 }
763
_ProcessedInfosStringTest764 BString _ProcessedInfosString() const
765 {
766 BString processedInfosString;
767 if (!fProcessedMonitoringInfos.IsEmpty()) {
768 processedInfosString << "\nprocessed so far:\n"
769 << indented_string(fProcessedMonitoringInfos.ToString(), " ");
770 }
771 return processedInfosString;
772 }
773
774 protected:
775 BString fName;
776 uint32 fFlags;
777 thread_id fLooperThread;
778 MessageList fNotifications;
779 MonitoringInfoSet fProcessedMonitoringInfos;
780 bool fIsWatching;
781 };
782
783
784 struct TestBase : Test {
785 protected:
TestBaseTestBase786 TestBase(const char* name)
787 :
788 Test(name)
789 {
790 }
791
StandardSetupTestBase792 void StandardSetup()
793 {
794 CreateDirectory("base");
795 CreateDirectory("base/dir1");
796 CreateDirectory("base/dir1/dir0");
797 CreateFile("base/file0");
798 CreateFile("base/dir1/file0.0");
799 }
800 };
801
802
803 #define CREATE_TEST_WITH_CUSTOM_SETUP(name, code) \
804 struct Test##name : TestBase { \
805 Test##name() : TestBase(#name) {} \
806 virtual void DoInternal(bool directoriesOnly, bool filesOnly, \
807 bool recursive, bool pathOnly, bool watchStat) \
808 { \
809 code \
810 } \
811 }; \
812 tests.push_back(new Test##name);
813
814 #define CREATE_TEST(name, code) \
815 CREATE_TEST_WITH_CUSTOM_SETUP(name, \
816 StandardSetup(); \
817 StartWatching("base"); \
818 code \
819 )
820
821
822 static void
create_tests(std::vector<Test * > & tests)823 create_tests(std::vector<Test*>& tests)
824 {
825 // test coverage:
826 // - file/directory outside
827 // - file/directory at top level
828 // - file/directory at sub level
829 // - move file/directory into/within/out of
830 // - move non-empty directory into/within/out of
831 // - create/move ancestor folder
832 // - remove/move ancestor folder
833 // - touch path, file/directory at top and sub level
834 // - base file instead of directory
835 //
836 // not covered (yet):
837 // - mount/unmount below/in our path
838 // - test symlink in watched path
839 // - attribute watching (should be similar to stat watching)
840
841 CREATE_TEST(FileOutside,
842 CreateFile("file1");
843 MoveEntry("file1", "file2");
844 RemoveEntry("file2");
845 )
846
847 CREATE_TEST(DirectoryOutside,
848 CreateDirectory("dir1");
849 MoveEntry("dir1", "dir2");
850 RemoveEntry("dir2");
851 )
852
853 CREATE_TEST(FileTopLevel,
854 ExpectNotification(CreateFile("base/file1"),
855 !directoriesOnly && !pathOnly);
856 ExpectNotification(MoveEntry("base/file1", "base/file2"),
857 !directoriesOnly && !pathOnly);
858 ExpectNotification(RemoveEntry("base/file2"),
859 !directoriesOnly && !pathOnly);
860 )
861
862 CREATE_TEST(DirectoryTopLevel,
863 ExpectNotification(CreateDirectory("base/dir2"),
864 !filesOnly && !pathOnly);
865 ExpectNotification(MoveEntry("base/dir2", "base/dir3"),
866 !filesOnly && !pathOnly);
867 ExpectNotification(RemoveEntry("base/dir3"),
868 !filesOnly && !pathOnly);
869 )
870
871 CREATE_TEST(FileSubLevel,
872 ExpectNotification(CreateFile("base/dir1/file1"),
873 recursive && !directoriesOnly);
874 ExpectNotification(MoveEntry("base/dir1/file1", "base/dir1/file2"),
875 recursive && !directoriesOnly);
876 ExpectNotification(RemoveEntry("base/dir1/file2"),
877 recursive && !directoriesOnly);
878 )
879
880 CREATE_TEST(DirectorySubLevel,
881 ExpectNotification(CreateDirectory("base/dir1/dir2"),
882 recursive && !filesOnly);
883 ExpectNotification(MoveEntry("base/dir1/dir2", "base/dir1/dir3"),
884 recursive && !filesOnly);
885 ExpectNotification(RemoveEntry("base/dir1/dir3"),
886 recursive && !filesOnly);
887 )
888
889 CREATE_TEST(FileMoveIntoTopLevel,
890 CreateFile("file1");
891 ExpectNotification(MoveEntry("file1", "base/file2"),
892 !directoriesOnly && !pathOnly);
893 ExpectNotification(RemoveEntry("base/file2"),
894 !directoriesOnly && !pathOnly);
895 )
896
897 CREATE_TEST(DirectoryMoveIntoTopLevel,
898 CreateDirectory("dir2");
899 ExpectNotification(MoveEntry("dir2", "base/dir3"),
900 !filesOnly && !pathOnly);
901 ExpectNotification(RemoveEntry("base/dir3"),
902 !filesOnly && !pathOnly);
903 )
904
905 CREATE_TEST(FileMoveIntoSubLevel,
906 CreateFile("file1");
907 ExpectNotification(MoveEntry("file1", "base/dir1/file2"),
908 recursive && !directoriesOnly);
909 ExpectNotification(RemoveEntry("base/dir1/file2"),
910 recursive && !directoriesOnly);
911 )
912
913 CREATE_TEST(DirectoryMoveIntoSubLevel,
914 CreateDirectory("dir2");
915 ExpectNotification(MoveEntry("dir2", "base/dir1/dir3"),
916 recursive && !filesOnly);
917 ExpectNotification(RemoveEntry("base/dir1/dir3"),
918 recursive && !filesOnly);
919 )
920
921 CREATE_TEST(FileMoveOutOfTopLevel,
922 ExpectNotification(CreateFile("base/file1"),
923 !directoriesOnly && !pathOnly);
924 ExpectNotification(MoveEntry("base/file1", "file2"),
925 !directoriesOnly && !pathOnly);
926 RemoveEntry("file2");
927 )
928
929 CREATE_TEST(DirectoryMoveOutOfTopLevel,
930 ExpectNotification(CreateDirectory("base/dir2"),
931 !filesOnly && !pathOnly);
932 ExpectNotification(MoveEntry("base/dir2", "dir3"),
933 !filesOnly && !pathOnly);
934 RemoveEntry("dir3");
935 )
936
937 CREATE_TEST(FileMoveOutOfSubLevel,
938 ExpectNotification(CreateFile("base/dir1/file1"),
939 recursive && !directoriesOnly);
940 ExpectNotification(MoveEntry("base/dir1/file1", "file2"),
941 recursive && !directoriesOnly);
942 RemoveEntry("file2");
943 )
944
945 CREATE_TEST(DirectoryMoveOutOfSubLevel,
946 ExpectNotification(CreateDirectory("base/dir1/dir2"),
947 recursive && !filesOnly);
948 ExpectNotification(MoveEntry("base/dir1/dir2", "dir3"),
949 recursive && !filesOnly);
950 RemoveEntry("dir3");
951 )
952
953 CREATE_TEST(FileMoveToTopLevel,
954 ExpectNotification(CreateFile("base/dir1/file1"),
955 !directoriesOnly && recursive);
956 ExpectNotification(MoveEntry("base/dir1/file1", "base/file2"),
957 !directoriesOnly && !pathOnly);
958 ExpectNotification(RemoveEntry("base/file2"),
959 !directoriesOnly && !pathOnly);
960 )
961
962 CREATE_TEST(DirectoryMoveToTopLevel,
963 ExpectNotification(CreateDirectory("base/dir1/dir2"),
964 !filesOnly && recursive);
965 ExpectNotification(MoveEntry("base/dir1/dir2", "base/dir3"),
966 !filesOnly && !pathOnly);
967 ExpectNotification(RemoveEntry("base/dir3"),
968 !filesOnly && !pathOnly);
969 )
970
971 CREATE_TEST(FileMoveToSubLevel,
972 ExpectNotification(CreateFile("base/file1"),
973 !directoriesOnly && !pathOnly);
974 ExpectNotification(MoveEntry("base/file1", "base/dir1/file2"),
975 !directoriesOnly && !pathOnly);
976 ExpectNotification(RemoveEntry("base/dir1/file2"),
977 !directoriesOnly && recursive);
978 )
979
980 CREATE_TEST(DirectoryMoveToSubLevel,
981 ExpectNotification(CreateDirectory("base/dir2"),
982 !filesOnly && !pathOnly);
983 ExpectNotification(MoveEntry("base/dir2", "base/dir1/dir3"),
984 !filesOnly && !pathOnly);
985 ExpectNotification(RemoveEntry("base/dir1/dir3"),
986 !filesOnly && recursive);
987 )
988
989 CREATE_TEST(NonEmptyDirectoryMoveIntoTopLevel,
990 CreateDirectory("dir2");
991 CreateDirectory("dir2/dir3");
992 CreateDirectory("dir2/dir4");
993 CreateFile("dir2/file1");
994 CreateFile("dir2/dir3/file2");
995 ExpectNotification(MoveEntry("dir2", "base/dir5"),
996 !filesOnly && !pathOnly);
997 if (recursive && filesOnly) {
998 ExpectNotifications(MonitoringInfoSet()
999 .Add(B_ENTRY_CREATED, "base/dir5/file1")
1000 .Add(B_ENTRY_CREATED, "base/dir5/dir3/file2"));
1001 }
1002 )
1003
1004 CREATE_TEST(NonEmptyDirectoryMoveIntoSubLevel,
1005 CreateDirectory("dir2");
1006 CreateDirectory("dir2/dir3");
1007 CreateDirectory("dir2/dir4");
1008 CreateFile("dir2/file1");
1009 CreateFile("dir2/dir3/file2");
1010 ExpectNotification(MoveEntry("dir2", "base/dir1/dir5"),
1011 !filesOnly && recursive);
1012 if (recursive && filesOnly) {
1013 ExpectNotifications(MonitoringInfoSet()
1014 .Add(B_ENTRY_CREATED, "base/dir1/dir5/file1")
1015 .Add(B_ENTRY_CREATED, "base/dir1/dir5/dir3/file2"));
1016 }
1017 )
1018
1019 CREATE_TEST_WITH_CUSTOM_SETUP(NonEmptyDirectoryMoveOutOfTopLevel,
1020 StandardSetup();
1021 CreateDirectory("base/dir2");
1022 CreateDirectory("base/dir2/dir3");
1023 CreateDirectory("base/dir2/dir4");
1024 CreateFile("base/dir2/file1");
1025 CreateFile("base/dir2/dir3/file2");
1026 StartWatching("base");
1027 MonitoringInfoSet filesRemoved;
1028 if (recursive && filesOnly) {
1029 filesRemoved
1030 .Add(B_ENTRY_REMOVED, "base/dir2/file1")
1031 .Add(B_ENTRY_REMOVED, "base/dir2/dir3/file2");
1032 }
1033 ExpectNotification(MoveEntry("base/dir2", "dir5"),
1034 !filesOnly && !pathOnly);
1035 ExpectNotifications(filesRemoved);
1036 )
1037
1038 CREATE_TEST_WITH_CUSTOM_SETUP(NonEmptyDirectoryMoveOutOfSubLevel,
1039 StandardSetup();
1040 CreateDirectory("base/dir1/dir2");
1041 CreateDirectory("base/dir1/dir2/dir3");
1042 CreateDirectory("base/dir1/dir2/dir4");
1043 CreateFile("base/dir1/dir2/file1");
1044 CreateFile("base/dir1/dir2/dir3/file2");
1045 StartWatching("base");
1046 MonitoringInfoSet filesRemoved;
1047 if (recursive && filesOnly) {
1048 filesRemoved
1049 .Add(B_ENTRY_REMOVED, "base/dir1/dir2/file1")
1050 .Add(B_ENTRY_REMOVED, "base/dir1/dir2/dir3/file2");
1051 }
1052 ExpectNotification(MoveEntry("base/dir1/dir2", "dir5"),
1053 !filesOnly && recursive);
1054 ExpectNotifications(filesRemoved);
1055 )
1056
1057 CREATE_TEST_WITH_CUSTOM_SETUP(NonEmptyDirectoryMoveToTopLevel,
1058 StandardSetup();
1059 CreateDirectory("base/dir1/dir2");
1060 CreateDirectory("base/dir1/dir2/dir3");
1061 CreateDirectory("base/dir1/dir2/dir4");
1062 CreateFile("base/dir1/dir2/file1");
1063 CreateFile("base/dir1/dir2/dir3/file2");
1064 StartWatching("base");
1065 MonitoringInfoSet filesMoved;
1066 if (recursive && filesOnly) {
1067 filesMoved
1068 .Add(B_ENTRY_REMOVED, "base/dir1/dir2/file1")
1069 .Add(B_ENTRY_REMOVED, "base/dir1/dir2/dir3/file2");
1070 }
1071 ExpectNotification(MoveEntry("base/dir1/dir2", "base/dir5"),
1072 !filesOnly && !pathOnly);
1073 if (recursive && filesOnly) {
1074 filesMoved
1075 .Add(B_ENTRY_CREATED, "base/dir5/file1")
1076 .Add(B_ENTRY_CREATED, "base/dir5/dir3/file2");
1077 }
1078 ExpectNotifications(filesMoved);
1079 )
1080
1081 CREATE_TEST_WITH_CUSTOM_SETUP(NonEmptyDirectoryMoveToSubLevel,
1082 StandardSetup();
1083 CreateDirectory("base/dir2");
1084 CreateDirectory("base/dir2/dir3");
1085 CreateDirectory("base/dir2/dir4");
1086 CreateFile("base/dir2/file1");
1087 CreateFile("base/dir2/dir3/file2");
1088 StartWatching("base");
1089 MonitoringInfoSet filesMoved;
1090 if (recursive && filesOnly) {
1091 filesMoved
1092 .Add(B_ENTRY_REMOVED, "base/dir2/file1")
1093 .Add(B_ENTRY_REMOVED, "base/dir2/dir3/file2");
1094 }
1095 ExpectNotification(MoveEntry("base/dir2", "base/dir1/dir5"),
1096 !filesOnly && !pathOnly);
1097 if (recursive && filesOnly) {
1098 filesMoved
1099 .Add(B_ENTRY_CREATED, "base/dir1/dir5/file1")
1100 .Add(B_ENTRY_CREATED, "base/dir1/dir5/dir3/file2");
1101 }
1102 ExpectNotifications(filesMoved);
1103 )
1104
1105 CREATE_TEST_WITH_CUSTOM_SETUP(CreateAncestor,
1106 StartWatching("ancestor/base");
1107 CreateDirectory("ancestor");
1108 )
1109
1110 CREATE_TEST_WITH_CUSTOM_SETUP(MoveCreateAncestor,
1111 CreateDirectory("ancestorSibling");
1112 StartWatching("ancestor/base");
1113 MoveEntry("ancestorSibling", "ancestor");
1114 )
1115
1116 CREATE_TEST_WITH_CUSTOM_SETUP(MoveCreateAncestorWithBase,
1117 CreateDirectory("ancestorSibling");
1118 CreateDirectory("ancestorSibling/base");
1119 StartWatching("ancestor/base");
1120 MoveEntry("ancestorSibling", "ancestor");
1121 MonitoringInfoSet entriesCreated;
1122 if (!filesOnly)
1123 entriesCreated.Add(B_ENTRY_CREATED, "ancestor/base");
1124 ExpectNotifications(entriesCreated);
1125 )
1126
1127 CREATE_TEST_WITH_CUSTOM_SETUP(MoveCreateAncestorWithBaseAndFile,
1128 CreateDirectory("ancestorSibling");
1129 CreateDirectory("ancestorSibling/base");
1130 CreateFile("ancestorSibling/base/file1");
1131 StartWatching("ancestor/base");
1132 MoveEntry("ancestorSibling", "ancestor");
1133 MonitoringInfoSet entriesCreated;
1134 if (!filesOnly)
1135 entriesCreated.Add(B_ENTRY_CREATED, "ancestor/base");
1136 else if (!pathOnly)
1137 entriesCreated.Add(B_ENTRY_CREATED, "ancestor/base/file1");
1138 ExpectNotifications(entriesCreated);
1139 )
1140
1141 CREATE_TEST_WITH_CUSTOM_SETUP(MoveCreateAncestorWithBaseAndDirectory,
1142 CreateDirectory("ancestorSibling");
1143 CreateDirectory("ancestorSibling/base");
1144 CreateDirectory("ancestorSibling/base/dir1");
1145 CreateFile("ancestorSibling/base/dir1/file1");
1146 StartWatching("ancestor/base");
1147 MoveEntry("ancestorSibling", "ancestor");
1148 MonitoringInfoSet entriesCreated;
1149 if (!filesOnly) {
1150 entriesCreated.Add(B_ENTRY_CREATED, "ancestor/base");
1151 } else if (recursive)
1152 entriesCreated.Add(B_ENTRY_CREATED, "ancestor/base/dir1/file1");
1153 ExpectNotifications(entriesCreated);
1154 )
1155
1156 CREATE_TEST_WITH_CUSTOM_SETUP(CreateBase,
1157 CreateDirectory("ancestor");
1158 StartWatching("ancestor/base");
1159 ExpectNotification(CreateDirectory("ancestor/base"),
1160 !filesOnly);
1161 )
1162
1163 CREATE_TEST_WITH_CUSTOM_SETUP(MoveCreateBase,
1164 CreateDirectory("ancestor");
1165 CreateDirectory("ancestor/baseSibling");
1166 StartWatching("ancestor/base");
1167 ExpectNotification(MoveEntry("ancestor/baseSibling", "ancestor/base"),
1168 !filesOnly);
1169 )
1170
1171 CREATE_TEST_WITH_CUSTOM_SETUP(MoveCreateBaseWithFile,
1172 CreateDirectory("ancestor");
1173 CreateDirectory("ancestor/baseSibling");
1174 CreateFile("ancestor/baseSibling/file1");
1175 StartWatching("ancestor/base");
1176 ExpectNotification(MoveEntry("ancestor/baseSibling", "ancestor/base"),
1177 !filesOnly);
1178 MonitoringInfoSet entriesCreated;
1179 if (filesOnly && !pathOnly)
1180 entriesCreated.Add(B_ENTRY_CREATED, "ancestor/base/file1");
1181 ExpectNotifications(entriesCreated);
1182 )
1183
1184 CREATE_TEST_WITH_CUSTOM_SETUP(MoveCreateBaseWithDirectory,
1185 CreateDirectory("ancestor");
1186 CreateDirectory("ancestor/baseSibling");
1187 CreateDirectory("ancestor/baseSibling/dir1");
1188 CreateFile("ancestor/baseSibling/dir1/file1");
1189 StartWatching("ancestor/base");
1190 ExpectNotification(MoveEntry("ancestor/baseSibling", "ancestor/base"),
1191 !filesOnly);
1192 MonitoringInfoSet entriesCreated;
1193 if (filesOnly && recursive)
1194 entriesCreated.Add(B_ENTRY_CREATED, "ancestor/base/dir1/file1");
1195 ExpectNotifications(entriesCreated);
1196 )
1197
1198 CREATE_TEST_WITH_CUSTOM_SETUP(MoveRemoveAncestorWithBaseAndFile,
1199 CreateDirectory("ancestor");
1200 CreateDirectory("ancestor/base");
1201 CreateFile("ancestor/base/file1");
1202 StartWatching("ancestor/base");
1203 MonitoringInfoSet entriesRemoved;
1204 if (!filesOnly)
1205 entriesRemoved.Add(B_ENTRY_REMOVED, "ancestor/base");
1206 else if (!pathOnly)
1207 entriesRemoved.Add(B_ENTRY_REMOVED, "ancestor/base/file1");
1208 MoveEntry("ancestor", "ancestorSibling");
1209 ExpectNotifications(entriesRemoved);
1210 )
1211
1212 CREATE_TEST_WITH_CUSTOM_SETUP(MoveRemoveAncestorWithBaseAndDirectory,
1213 CreateDirectory("ancestor");
1214 CreateDirectory("ancestor/base");
1215 CreateDirectory("ancestor/base/dir1");
1216 CreateFile("ancestor/base/dir1/file1");
1217 StartWatching("ancestor/base");
1218 MonitoringInfoSet entriesRemoved;
1219 if (!filesOnly)
1220 entriesRemoved.Add(B_ENTRY_REMOVED, "ancestor/base");
1221 else if (recursive)
1222 entriesRemoved.Add(B_ENTRY_REMOVED, "ancestor/base/dir1/file1");
1223 MoveEntry("ancestor", "ancestorSibling");
1224 ExpectNotifications(entriesRemoved);
1225 )
1226
1227 CREATE_TEST_WITH_CUSTOM_SETUP(MoveRemoveBaseWithFile,
1228 CreateDirectory("ancestor");
1229 CreateDirectory("ancestor/base");
1230 CreateFile("ancestor/base/file1");
1231 StartWatching("ancestor/base");
1232 MonitoringInfoSet entriesRemoved;
1233 if (filesOnly && !pathOnly)
1234 entriesRemoved.Add(B_ENTRY_REMOVED, "ancestor/base/file1");
1235 ExpectNotification(MoveEntry("ancestor/base", "ancestor/baseSibling"),
1236 !filesOnly);
1237 ExpectNotifications(entriesRemoved);
1238 )
1239
1240 CREATE_TEST_WITH_CUSTOM_SETUP(MoveRemoveBaseWithDirectory,
1241 CreateDirectory("ancestor");
1242 CreateDirectory("ancestor/base");
1243 CreateDirectory("ancestor/base/dir1");
1244 CreateFile("ancestor/base/dir1/file1");
1245 StartWatching("ancestor/base");
1246 MonitoringInfoSet entriesRemoved;
1247 if (filesOnly && recursive)
1248 entriesRemoved.Add(B_ENTRY_REMOVED, "ancestor/base/dir1/file1");
1249 ExpectNotification(MoveEntry("ancestor/base", "ancestor/baseSibling"),
1250 !filesOnly);
1251 ExpectNotifications(entriesRemoved);
1252 )
1253
1254 CREATE_TEST(TouchBase,
1255 ExpectNotification(TouchEntry("base"), watchStat && !filesOnly);
1256 )
1257
1258 CREATE_TEST(TouchFileTopLevel,
1259 ExpectNotification(TouchEntry("base/file0"),
1260 watchStat && recursive && !directoriesOnly);
1261 )
1262
1263 CREATE_TEST(TouchFileSubLevel,
1264 ExpectNotification(TouchEntry("base/dir1/file0.0"),
1265 watchStat && recursive && !directoriesOnly);
1266 )
1267
1268 CREATE_TEST(TouchDirectoryTopLevel,
1269 ExpectNotification(TouchEntry("base/dir1"),
1270 watchStat && recursive && !filesOnly);
1271 )
1272
1273 CREATE_TEST(TouchDirectorySubLevel,
1274 ExpectNotification(TouchEntry("base/dir1/dir0"),
1275 watchStat && recursive && !filesOnly);
1276 )
1277
1278 CREATE_TEST_WITH_CUSTOM_SETUP(CreateFileBase,
1279 StartWatching("file");
1280 ExpectNotification(CreateFile("file"),
1281 !directoriesOnly);
1282 )
1283
1284 CREATE_TEST_WITH_CUSTOM_SETUP(MoveCreateFileBase,
1285 CreateFile("fileSibling");
1286 StartWatching("file");
1287 ExpectNotification(MoveEntry("fileSibling", "file"),
1288 !directoriesOnly);
1289 )
1290
1291 CREATE_TEST_WITH_CUSTOM_SETUP(RemoveFileBase,
1292 CreateFile("file");
1293 StartWatching("file");
1294 ExpectNotification(RemoveEntry("file"),
1295 !directoriesOnly);
1296 )
1297
1298 CREATE_TEST_WITH_CUSTOM_SETUP(MoveRemoveFileBase,
1299 CreateFile("file");
1300 StartWatching("file");
1301 ExpectNotification(MoveEntry("file", "fileSibling"),
1302 !directoriesOnly);
1303 )
1304
1305 CREATE_TEST_WITH_CUSTOM_SETUP(TouchFileBase,
1306 CreateFile("file");
1307 StartWatching("file");
1308 ExpectNotification(TouchEntry("file"),
1309 watchStat && !directoriesOnly);
1310 )
1311 }
1312
1313
1314 static void
run_tests(std::set<BString> testNames,uint32 watchFlags,size_t & totalTests,size_t & succeededTests)1315 run_tests(std::set<BString> testNames, uint32 watchFlags,
1316 size_t& totalTests, size_t& succeededTests)
1317 {
1318 std::vector<Test*> tests;
1319 create_tests(tests);
1320
1321 // filter the tests, if test names have been specified
1322 size_t testCount = tests.size();
1323 if (!testNames.empty()) {
1324 for (size_t i = 0; i < testCount;) {
1325 Test* test = tests[i];
1326 std::set<BString>::iterator it = testNames.find(test->Name());
1327 if (it != testNames.end()) {
1328 testNames.erase(it);
1329 i++;
1330 } else {
1331 tests.erase(tests.begin() + i);
1332 test->Delete();
1333 testCount--;
1334 }
1335 }
1336
1337 if (!testNames.empty()) {
1338 printf("no such test(s):\n");
1339 for (std::set<BString>::iterator it = testNames.begin();
1340 it != testNames.end(); ++it) {
1341 printf(" %s\n", it->String());
1342 exit(1);
1343 }
1344 }
1345 }
1346
1347 printf("\nrunning tests with flags: %s\n",
1348 watch_flags_to_string(watchFlags).String());
1349
1350 int32 longestTestName = 0;
1351
1352 for (size_t i = 0; i < testCount; i++) {
1353 Test* test = tests[i];
1354 longestTestName = std::max(longestTestName, test->Name().Length());
1355 }
1356
1357 for (size_t i = 0; i < testCount; i++) {
1358 Test* test = tests[i];
1359 bool terminate = false;
1360
1361 try {
1362 totalTests++;
1363 test->Init(watchFlags);
1364 printf(" %s: %*s", test->Name().String(),
1365 int(longestTestName - test->Name().Length()), "");
1366 fflush(stdout);
1367 test->Do();
1368 printf("SUCCEEDED\n");
1369 succeededTests++;
1370 } catch (FatalException& exception) {
1371 printf("FAILED FATALLY\n");
1372 printf("%s\n",
1373 indented_string(exception.Message(), " ").String());
1374 terminate = true;
1375 } catch (TestException& exception) {
1376 printf("FAILED\n");
1377 printf("%s\n",
1378 indented_string(exception.Message(), " ").String());
1379 }
1380
1381 test->Delete();
1382
1383 if (terminate)
1384 exit(1);
1385 }
1386 }
1387
1388
1389 int
main(int argc,const char * const * argv)1390 main(int argc, const char* const* argv)
1391 {
1392 // any args are test names
1393 std::set<BString> testNames;
1394 for (int i = 1; i < argc; i++)
1395 testNames.insert(argv[i]);
1396
1397 // flags that can be combined arbitrarily
1398 const uint32 kFlags[] = {
1399 B_WATCH_NAME,
1400 B_WATCH_STAT,
1401 // not that interesting, since similar to B_WATCH_STAT: B_WATCH_ATTR
1402 B_WATCH_DIRECTORY,
1403 B_WATCH_RECURSIVELY,
1404 };
1405 const size_t kFlagCount = sizeof(kFlags) / sizeof(kFlags[0]);
1406
1407 size_t totalTests = 0;
1408 size_t succeededTests = 0;
1409
1410 for (size_t i = 0; i < 1 << kFlagCount; i++) {
1411 // construct flags mask
1412 uint32 flags = 0;
1413 for (size_t k = 0; k < kFlagCount; k++) {
1414 if ((i & (1 << k)) != 0)
1415 flags |= kFlags[k];
1416 }
1417
1418 // run tests -- in recursive mode do that additionally for the mutually
1419 // B_WATCH_FILES_ONLY and B_WATCH_DIRECTORIES_ONLY flags.
1420 run_tests(testNames, flags, totalTests, succeededTests);
1421 if ((flags & B_WATCH_RECURSIVELY) != 0) {
1422 run_tests(testNames, flags | B_WATCH_FILES_ONLY, totalTests,
1423 succeededTests);
1424 run_tests(testNames, flags | B_WATCH_DIRECTORIES_ONLY, totalTests,
1425 succeededTests);
1426 }
1427 }
1428
1429 printf("\n");
1430 if (succeededTests == totalTests) {
1431 printf("ALL TESTS SUCCEEDED\n");
1432 } else {
1433 printf("%zu of %zu TESTS FAILED\n", totalTests - succeededTests,
1434 totalTests);
1435 }
1436
1437 return 0;
1438 }
1439