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