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