xref: /haiku/src/tests/kits/storage/testapps/PathMonitorTest2.cpp (revision 9f81ca838ce7b92b5689e57d3f86765db4705a7b)
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