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