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 { 68*cc4d194aSIngo Weinhold TestException(const BString& message) 69*cc4d194aSIngo Weinhold : 70*cc4d194aSIngo Weinhold fMessage(message) 71*cc4d194aSIngo Weinhold { 72*cc4d194aSIngo Weinhold } 73*cc4d194aSIngo Weinhold 74*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 { 85*cc4d194aSIngo Weinhold FatalException(const BString& message) 86*cc4d194aSIngo Weinhold : 87*cc4d194aSIngo Weinhold fMessage(message) 88*cc4d194aSIngo Weinhold { 89*cc4d194aSIngo Weinhold } 90*cc4d194aSIngo Weinhold 91*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 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 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 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 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 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 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 { 294*cc4d194aSIngo Weinhold MonitoringInfo() 295*cc4d194aSIngo Weinhold { 296*cc4d194aSIngo Weinhold } 297*cc4d194aSIngo Weinhold 298*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 305*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 317*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 351*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: 424*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 { 445*cc4d194aSIngo Weinhold MonitoringInfoSet() 446*cc4d194aSIngo Weinhold { 447*cc4d194aSIngo Weinhold } 448*cc4d194aSIngo Weinhold 449*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 456*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 462*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 469*cc4d194aSIngo Weinhold bool IsEmpty() const 470*cc4d194aSIngo Weinhold { 471*cc4d194aSIngo Weinhold return fInfos.empty(); 472*cc4d194aSIngo Weinhold } 473*cc4d194aSIngo Weinhold 474*cc4d194aSIngo Weinhold int32 CountInfos() const 475*cc4d194aSIngo Weinhold { 476*cc4d194aSIngo Weinhold return fInfos.size(); 477*cc4d194aSIngo Weinhold } 478*cc4d194aSIngo Weinhold 479*cc4d194aSIngo Weinhold const MonitoringInfo& InfoAt(int32 index) const 480*cc4d194aSIngo Weinhold { 481*cc4d194aSIngo Weinhold return fInfos[index]; 482*cc4d194aSIngo Weinhold } 483*cc4d194aSIngo Weinhold 484*cc4d194aSIngo Weinhold void Remove(int32 index) 485*cc4d194aSIngo Weinhold { 486*cc4d194aSIngo Weinhold fInfos.erase(fInfos.begin() + index); 487*cc4d194aSIngo Weinhold } 488*cc4d194aSIngo Weinhold 489*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 { 507*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 518*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 537*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 550*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 576*cc4d194aSIngo Weinhold const BString& Name() const 577*cc4d194aSIngo Weinhold { 578*cc4d194aSIngo Weinhold return fName; 579*cc4d194aSIngo Weinhold } 580*cc4d194aSIngo Weinhold 581*cc4d194aSIngo Weinhold protected: 582*cc4d194aSIngo Weinhold ~Test() 583*cc4d194aSIngo Weinhold { 584*cc4d194aSIngo Weinhold } 585*cc4d194aSIngo Weinhold 586*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 594*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 601*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 610*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 620*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 632*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 641*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 672*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: 724*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: 739*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 745*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 764*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: 786*cc4d194aSIngo Weinhold TestBase(const char* name) 787*cc4d194aSIngo Weinhold : 788*cc4d194aSIngo Weinhold Test(name) 789*cc4d194aSIngo Weinhold { 790*cc4d194aSIngo Weinhold } 791*cc4d194aSIngo Weinhold 792*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 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 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 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