184af0c9bSNicolas Bonnefon #include "gmock/gmock.h" 284af0c9bSNicolas Bonnefon 387e05652SNicolas Bonnefon #include "config.h" 487e05652SNicolas Bonnefon 5b827fa8eSNicolas Bonnefon #include <cstdio> 684af0c9bSNicolas Bonnefon #include <fcntl.h> 784af0c9bSNicolas Bonnefon 884af0c9bSNicolas Bonnefon #include <memory> 984af0c9bSNicolas Bonnefon #include <condition_variable> 1084af0c9bSNicolas Bonnefon #include <thread> 1184af0c9bSNicolas Bonnefon #include <mutex> 1284af0c9bSNicolas Bonnefon #include <chrono> 1384af0c9bSNicolas Bonnefon 1484b2179eSNicolas Bonnefon #include "log.h" 1584b2179eSNicolas Bonnefon 16f09fa651SNicolas Bonnefon #include "watchtower.h" 17f09fa651SNicolas Bonnefon 1887e05652SNicolas Bonnefon #ifdef _WIN32 19f09fa651SNicolas Bonnefon # include "winwatchtowerdriver.h" 20f09fa651SNicolas Bonnefon using PlatformWatchTower = WatchTower<WinWatchTowerDriver>; 2187e05652SNicolas Bonnefon #else 22b278d183SNicolas Bonnefon # include "inotifywatchtowerdriver.h" 23f09fa651SNicolas Bonnefon using PlatformWatchTower = WatchTower<INotifyWatchTowerDriver>; 2487e05652SNicolas Bonnefon #endif 2584af0c9bSNicolas Bonnefon 2684af0c9bSNicolas Bonnefon using namespace std; 2784af0c9bSNicolas Bonnefon using namespace testing; 2884af0c9bSNicolas Bonnefon 2984af0c9bSNicolas Bonnefon class WatchTowerBehaviour: public testing::Test { 3084af0c9bSNicolas Bonnefon public: 3196bde7d5SNicolas Bonnefon shared_ptr<PlatformWatchTower> watch_tower = make_shared<PlatformWatchTower>(); 3284af0c9bSNicolas Bonnefon 33a0b17fd1SNicolas Bonnefon const char* createTempName() 34a0b17fd1SNicolas Bonnefon { 35a0b17fd1SNicolas Bonnefon const char* name; 36a0b17fd1SNicolas Bonnefon #if _WIN32 37a0b17fd1SNicolas Bonnefon name = _tempnam( "c:\\temp", "glogg_test" ); 38a0b17fd1SNicolas Bonnefon #else 39a0b17fd1SNicolas Bonnefon name = tmpnam( nullptr ); 40a0b17fd1SNicolas Bonnefon #endif 41a0b17fd1SNicolas Bonnefon return name; 42a0b17fd1SNicolas Bonnefon } 43a0b17fd1SNicolas Bonnefon 44b827fa8eSNicolas Bonnefon string createTempEmptyFile( string file_name = "" ) { 45b827fa8eSNicolas Bonnefon const char* name; 46b827fa8eSNicolas Bonnefon 47b827fa8eSNicolas Bonnefon if ( ! file_name.empty() ) { 48b827fa8eSNicolas Bonnefon name = file_name.c_str(); 49b827fa8eSNicolas Bonnefon } 50b827fa8eSNicolas Bonnefon else { 5184af0c9bSNicolas Bonnefon // I know tmpnam is bad but I need control over the file 5284af0c9bSNicolas Bonnefon // and it is the only one which exits on Windows. 53a0b17fd1SNicolas Bonnefon name = createTempName(); 54b827fa8eSNicolas Bonnefon } 55b827fa8eSNicolas Bonnefon int fd = creat( name, S_IRUSR | S_IWUSR ); 5684af0c9bSNicolas Bonnefon close( fd ); 5784af0c9bSNicolas Bonnefon 58b827fa8eSNicolas Bonnefon return string( name ); 5984af0c9bSNicolas Bonnefon } 6084af0c9bSNicolas Bonnefon 6184af0c9bSNicolas Bonnefon string getNonExistingFileName() { 6284b2179eSNicolas Bonnefon #if _WIN32 6384b2179eSNicolas Bonnefon return string( _tempnam( "c:\\temp", "inexistant" ) ); 6484b2179eSNicolas Bonnefon #else 6584af0c9bSNicolas Bonnefon return string( tmpnam( nullptr ) ); 6684b2179eSNicolas Bonnefon #endif 6784b2179eSNicolas Bonnefon } 6884b2179eSNicolas Bonnefon 6984b2179eSNicolas Bonnefon WatchTowerBehaviour() { 7084b2179eSNicolas Bonnefon // Default to quiet, but increase to debug 715d489994SNicolas Bonnefon FILELog::setReportingLevel( logERROR ); 7284af0c9bSNicolas Bonnefon } 7384af0c9bSNicolas Bonnefon }; 7484af0c9bSNicolas Bonnefon 7584af0c9bSNicolas Bonnefon TEST_F( WatchTowerBehaviour, AcceptsAnExistingFileToWatch ) { 7684af0c9bSNicolas Bonnefon auto file_name = createTempEmptyFile(); 7787e05652SNicolas Bonnefon auto registration = watch_tower->addFile( file_name, [] (void) { } ); 7884af0c9bSNicolas Bonnefon } 7984af0c9bSNicolas Bonnefon 8084af0c9bSNicolas Bonnefon TEST_F( WatchTowerBehaviour, AcceptsANonExistingFileToWatch ) { 8187e05652SNicolas Bonnefon auto registration = watch_tower->addFile( getNonExistingFileName(), [] (void) { } ); 8284af0c9bSNicolas Bonnefon } 8384af0c9bSNicolas Bonnefon 84b827fa8eSNicolas Bonnefon /*****/ 85b827fa8eSNicolas Bonnefon 8684af0c9bSNicolas Bonnefon class WatchTowerSingleFile: public WatchTowerBehaviour { 8784af0c9bSNicolas Bonnefon public: 88b827fa8eSNicolas Bonnefon static const int TIMEOUT; 8984af0c9bSNicolas Bonnefon 903104b268SNicolas Bonnefon mutex mutex_; 913104b268SNicolas Bonnefon condition_variable cv_; 923104b268SNicolas Bonnefon 9384af0c9bSNicolas Bonnefon string file_name; 9496bde7d5SNicolas Bonnefon Registration registration; 9584af0c9bSNicolas Bonnefon 968f031b5aSNicolas Bonnefon int notification_received = 0; 9784af0c9bSNicolas Bonnefon 9896bde7d5SNicolas Bonnefon Registration registerFile( const string& filename ) { 99b827fa8eSNicolas Bonnefon weak_ptr<void> weakHeartbeat( heartbeat_ ); 100b827fa8eSNicolas Bonnefon 10184b2179eSNicolas Bonnefon auto reg = watch_tower->addFile( filename, [this, weakHeartbeat] (void) { 102b827fa8eSNicolas Bonnefon // Ensure the fixture object is still alive using the heartbeat 103b827fa8eSNicolas Bonnefon if ( auto keep = weakHeartbeat.lock() ) { 10484af0c9bSNicolas Bonnefon unique_lock<mutex> lock(mutex_); 1058f031b5aSNicolas Bonnefon ++notification_received; 106b827fa8eSNicolas Bonnefon cv_.notify_one(); 107b827fa8eSNicolas Bonnefon } } ); 108b827fa8eSNicolas Bonnefon 109b827fa8eSNicolas Bonnefon return reg; 110b827fa8eSNicolas Bonnefon } 111b827fa8eSNicolas Bonnefon 11284b2179eSNicolas Bonnefon WatchTowerSingleFile() 11384b2179eSNicolas Bonnefon : heartbeat_( shared_ptr<void>( (void*) 0xDEADC0DE, [] (void*) {} ) ) 11484b2179eSNicolas Bonnefon { 115b827fa8eSNicolas Bonnefon file_name = createTempEmptyFile(); 116b827fa8eSNicolas Bonnefon registration = registerFile( file_name ); 11784af0c9bSNicolas Bonnefon } 11884af0c9bSNicolas Bonnefon 11984b2179eSNicolas Bonnefon ~WatchTowerSingleFile() { 12084b2179eSNicolas Bonnefon remove( file_name.c_str() ); 12184b2179eSNicolas Bonnefon } 12284b2179eSNicolas Bonnefon 123*fcaa7557SNicolas Bonnefon bool waitNotificationReceived( int number = 1, int timeout_ms = TIMEOUT ) { 12484af0c9bSNicolas Bonnefon unique_lock<mutex> lock(mutex_); 125*fcaa7557SNicolas Bonnefon bool result = ( cv_.wait_for( lock, std::chrono::milliseconds(timeout_ms), 1268f031b5aSNicolas Bonnefon [this, number] { return notification_received >= number; } ) ); 127b827fa8eSNicolas Bonnefon 128b827fa8eSNicolas Bonnefon // Reinit the notification 1298f031b5aSNicolas Bonnefon notification_received = 0; 130b827fa8eSNicolas Bonnefon 1318f031b5aSNicolas Bonnefon return result; 13284af0c9bSNicolas Bonnefon } 13384af0c9bSNicolas Bonnefon 13484af0c9bSNicolas Bonnefon void appendDataToFile( const string& file_name ) { 13584af0c9bSNicolas Bonnefon static const char* string = "Test line\n"; 13684af0c9bSNicolas Bonnefon int fd = open( file_name.c_str(), O_WRONLY | O_APPEND ); 13784af0c9bSNicolas Bonnefon write( fd, (void*) string, strlen( string ) ); 13884af0c9bSNicolas Bonnefon close( fd ); 13984af0c9bSNicolas Bonnefon } 140b827fa8eSNicolas Bonnefon 141b827fa8eSNicolas Bonnefon private: 142b827fa8eSNicolas Bonnefon // Heartbeat ensures the object is still alive 143b827fa8eSNicolas Bonnefon shared_ptr<void> heartbeat_; 14484af0c9bSNicolas Bonnefon }; 14584af0c9bSNicolas Bonnefon 146a0936e1eSNicolas Bonnefon #ifdef _WIN32 1473104b268SNicolas Bonnefon const int WatchTowerSingleFile::TIMEOUT = 2000; 148a0936e1eSNicolas Bonnefon #else 149b827fa8eSNicolas Bonnefon const int WatchTowerSingleFile::TIMEOUT = 20; 150a0936e1eSNicolas Bonnefon #endif 151a0936e1eSNicolas Bonnefon 15284b2179eSNicolas Bonnefon TEST_F( WatchTowerSingleFile, SignalsWhenAWatchedFileIsAppended ) { 15384af0c9bSNicolas Bonnefon appendDataToFile( file_name ); 15484af0c9bSNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 15584af0c9bSNicolas Bonnefon } 15684af0c9bSNicolas Bonnefon 157b827fa8eSNicolas Bonnefon TEST_F( WatchTowerSingleFile, SignalsWhenAWatchedFileIsRemoved) { 15884af0c9bSNicolas Bonnefon remove( file_name.c_str() ); 15984af0c9bSNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 16084af0c9bSNicolas Bonnefon } 161b827fa8eSNicolas Bonnefon 162b827fa8eSNicolas Bonnefon TEST_F( WatchTowerSingleFile, SignalsWhenADeletedFileReappears ) { 163b827fa8eSNicolas Bonnefon remove( file_name.c_str() ); 164b827fa8eSNicolas Bonnefon waitNotificationReceived(); 165b827fa8eSNicolas Bonnefon createTempEmptyFile( file_name ); 166b827fa8eSNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 167b827fa8eSNicolas Bonnefon } 168b827fa8eSNicolas Bonnefon 16991f7c705SNicolas Bonnefon TEST_F( WatchTowerSingleFile, SignalsWhenAReappearedFileIsAppended ) { 17091f7c705SNicolas Bonnefon remove( file_name.c_str() ); 17191f7c705SNicolas Bonnefon waitNotificationReceived(); 17291f7c705SNicolas Bonnefon createTempEmptyFile( file_name ); 17391f7c705SNicolas Bonnefon waitNotificationReceived(); 17491f7c705SNicolas Bonnefon 17591f7c705SNicolas Bonnefon appendDataToFile( file_name ); 17691f7c705SNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 17791f7c705SNicolas Bonnefon } 17891f7c705SNicolas Bonnefon 179b827fa8eSNicolas Bonnefon TEST_F( WatchTowerSingleFile, StopSignalingWhenWatchDeleted ) { 180b827fa8eSNicolas Bonnefon auto second_file_name = createTempEmptyFile(); 1813104b268SNicolas Bonnefon std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) ); 1823104b268SNicolas Bonnefon // Ensure file creation has been 'digested' 183b827fa8eSNicolas Bonnefon { 184b827fa8eSNicolas Bonnefon auto second_registration = registerFile( second_file_name ); 185b827fa8eSNicolas Bonnefon appendDataToFile( second_file_name ); 186b827fa8eSNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 187b827fa8eSNicolas Bonnefon } 188b827fa8eSNicolas Bonnefon 189b827fa8eSNicolas Bonnefon // The registration will be removed here. 190b827fa8eSNicolas Bonnefon appendDataToFile( second_file_name ); 191b827fa8eSNicolas Bonnefon ASSERT_FALSE( waitNotificationReceived() ); 192b827fa8eSNicolas Bonnefon 193b827fa8eSNicolas Bonnefon remove( second_file_name.c_str() ); 194b827fa8eSNicolas Bonnefon } 195b827fa8eSNicolas Bonnefon 196f09fa651SNicolas Bonnefon TEST_F( WatchTowerSingleFile, SignalsWhenSameFileIsFollowedMultipleTimes ) { 197f09fa651SNicolas Bonnefon auto second_file_name = createTempEmptyFile(); 198f09fa651SNicolas Bonnefon 1993104b268SNicolas Bonnefon for ( int i = 0; i < 100; i++ ) 200f09fa651SNicolas Bonnefon { 201f09fa651SNicolas Bonnefon auto second_registration = registerFile( second_file_name ); 202f09fa651SNicolas Bonnefon appendDataToFile( second_file_name ); 203f09fa651SNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 204f09fa651SNicolas Bonnefon } 205f09fa651SNicolas Bonnefon 206f09fa651SNicolas Bonnefon // The registration will be removed here. 207f09fa651SNicolas Bonnefon appendDataToFile( second_file_name ); 208f09fa651SNicolas Bonnefon ASSERT_FALSE( waitNotificationReceived() ); 209f09fa651SNicolas Bonnefon 210f09fa651SNicolas Bonnefon remove( second_file_name.c_str() ); 211f09fa651SNicolas Bonnefon } 212f09fa651SNicolas Bonnefon 213a0b17fd1SNicolas Bonnefon TEST_F( WatchTowerSingleFile, TwoWatchesOnSameFileYieldsTwoNotifications ) { 2148f031b5aSNicolas Bonnefon auto second_registration = registerFile( file_name ); 2158f031b5aSNicolas Bonnefon appendDataToFile( file_name ); 2168f031b5aSNicolas Bonnefon 2178f031b5aSNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived( 2 ) ); 2188f031b5aSNicolas Bonnefon } 2198f031b5aSNicolas Bonnefon 220a0b17fd1SNicolas Bonnefon TEST_F( WatchTowerSingleFile, RemovingOneWatchOfTwoStillYieldsOneNotification ) { 2218f031b5aSNicolas Bonnefon { 2228f031b5aSNicolas Bonnefon auto second_registration = registerFile( file_name ); 2238f031b5aSNicolas Bonnefon } 2248f031b5aSNicolas Bonnefon 2258f031b5aSNicolas Bonnefon appendDataToFile( file_name ); 2268f031b5aSNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived( 1 ) ); 2278f031b5aSNicolas Bonnefon } 2288f031b5aSNicolas Bonnefon 229a0b17fd1SNicolas Bonnefon TEST_F( WatchTowerSingleFile, RenamingTheFileYieldsANotification ) { 230a0b17fd1SNicolas Bonnefon auto new_file_name = createTempName(); 2318f031b5aSNicolas Bonnefon 232a0b17fd1SNicolas Bonnefon rename( file_name.c_str(), new_file_name ); 2338f031b5aSNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 2348f031b5aSNicolas Bonnefon 235a0b17fd1SNicolas Bonnefon rename( new_file_name, file_name.c_str() ); 2368f031b5aSNicolas Bonnefon } 2378f031b5aSNicolas Bonnefon 238a0b17fd1SNicolas Bonnefon TEST_F( WatchTowerSingleFile, RenamingAFileToTheWatchedNameYieldsANotification ) { 2398f031b5aSNicolas Bonnefon remove( file_name.c_str() ); 2408f031b5aSNicolas Bonnefon waitNotificationReceived(); 2418f031b5aSNicolas Bonnefon 242a0b17fd1SNicolas Bonnefon std::string new_file_name = createTempEmptyFile(); 2438f031b5aSNicolas Bonnefon appendDataToFile( new_file_name ); 2448f031b5aSNicolas Bonnefon 2458f031b5aSNicolas Bonnefon rename( new_file_name.c_str(), file_name.c_str() ); 2468f031b5aSNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 2478f031b5aSNicolas Bonnefon } 2488f031b5aSNicolas Bonnefon 2498f031b5aSNicolas Bonnefon /*****/ 2508f031b5aSNicolas Bonnefon 25187e05652SNicolas Bonnefon #ifdef HAVE_SYMLINK 2523af68c64SNicolas Bonnefon class WatchTowerSymlink: public WatchTowerSingleFile { 2533af68c64SNicolas Bonnefon public: 2543af68c64SNicolas Bonnefon string symlink_name; 2553af68c64SNicolas Bonnefon 2563af68c64SNicolas Bonnefon void SetUp() override { 2573af68c64SNicolas Bonnefon file_name = createTempEmptyFile(); 2583af68c64SNicolas Bonnefon symlink_name = createTempEmptyFile(); 2593af68c64SNicolas Bonnefon remove( symlink_name.c_str() ); 2603af68c64SNicolas Bonnefon symlink( file_name.c_str(), symlink_name.c_str() ); 2613af68c64SNicolas Bonnefon 2623af68c64SNicolas Bonnefon registration = registerFile( symlink_name ); 2633af68c64SNicolas Bonnefon } 2643af68c64SNicolas Bonnefon 2653af68c64SNicolas Bonnefon void TearDown() override { 2663af68c64SNicolas Bonnefon remove( symlink_name.c_str() ); 2673af68c64SNicolas Bonnefon remove( file_name.c_str() ); 2683af68c64SNicolas Bonnefon } 2693af68c64SNicolas Bonnefon }; 2703af68c64SNicolas Bonnefon 2713af68c64SNicolas Bonnefon TEST_F( WatchTowerSymlink, AppendingToTheSymlinkYieldsANotification ) { 2723af68c64SNicolas Bonnefon appendDataToFile( symlink_name ); 2733af68c64SNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 2743af68c64SNicolas Bonnefon } 2753af68c64SNicolas Bonnefon 2763af68c64SNicolas Bonnefon TEST_F( WatchTowerSymlink, AppendingToTheTargetYieldsANotification ) { 2773af68c64SNicolas Bonnefon appendDataToFile( file_name ); 2783af68c64SNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 2793af68c64SNicolas Bonnefon } 2803af68c64SNicolas Bonnefon 2813af68c64SNicolas Bonnefon TEST_F( WatchTowerSymlink, RemovingTheSymlinkYieldsANotification ) { 2823af68c64SNicolas Bonnefon remove( symlink_name.c_str() ); 2833af68c64SNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 2843af68c64SNicolas Bonnefon } 2853af68c64SNicolas Bonnefon 2863af68c64SNicolas Bonnefon TEST_F( WatchTowerSymlink, RemovingTheTargetYieldsANotification ) { 2873af68c64SNicolas Bonnefon remove( file_name.c_str() ); 2883af68c64SNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 2893af68c64SNicolas Bonnefon } 2903af68c64SNicolas Bonnefon 2913af68c64SNicolas Bonnefon TEST_F( WatchTowerSymlink, ReappearingSymlinkYieldsANotification ) { 2923af68c64SNicolas Bonnefon auto new_target = createTempEmptyFile(); 2933af68c64SNicolas Bonnefon remove( symlink_name.c_str() ); 2943af68c64SNicolas Bonnefon waitNotificationReceived(); 2953af68c64SNicolas Bonnefon 2963af68c64SNicolas Bonnefon symlink( new_target.c_str(), symlink_name.c_str() ); 2973af68c64SNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 2983af68c64SNicolas Bonnefon 2993af68c64SNicolas Bonnefon remove( new_target.c_str() ); 3003af68c64SNicolas Bonnefon } 30191f7c705SNicolas Bonnefon 30291f7c705SNicolas Bonnefon TEST_F( WatchTowerSymlink, DataAddedInAReappearingSymlinkYieldsANotification ) { 30391f7c705SNicolas Bonnefon auto new_target = createTempEmptyFile(); 30491f7c705SNicolas Bonnefon remove( symlink_name.c_str() ); 30591f7c705SNicolas Bonnefon waitNotificationReceived(); 30691f7c705SNicolas Bonnefon symlink( new_target.c_str(), symlink_name.c_str() ); 30791f7c705SNicolas Bonnefon waitNotificationReceived(); 30891f7c705SNicolas Bonnefon 30991f7c705SNicolas Bonnefon appendDataToFile( new_target ); 31091f7c705SNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 31191f7c705SNicolas Bonnefon 31291f7c705SNicolas Bonnefon remove( new_target.c_str() ); 31391f7c705SNicolas Bonnefon } 31487e05652SNicolas Bonnefon #endif //HAVE_SYMLINK 3153af68c64SNicolas Bonnefon 3163af68c64SNicolas Bonnefon /*****/ 3173af68c64SNicolas Bonnefon 318b827fa8eSNicolas Bonnefon TEST( WatchTowerLifetime, RegistrationCanBeDeletedWhenWeAreDead ) { 31996bde7d5SNicolas Bonnefon auto mortal_watch_tower = new PlatformWatchTower(); 320b827fa8eSNicolas Bonnefon auto reg = mortal_watch_tower->addFile( "/tmp/test_file", [] (void) { } ); 321b827fa8eSNicolas Bonnefon 322b827fa8eSNicolas Bonnefon delete mortal_watch_tower; 3238f031b5aSNicolas Bonnefon // reg will be destroyed after the watch_tower 324b827fa8eSNicolas Bonnefon } 325af7adfddSNicolas Bonnefon 326af7adfddSNicolas Bonnefon /*****/ 327af7adfddSNicolas Bonnefon 3283104b268SNicolas Bonnefon class WatchTowerDirectories: public WatchTowerSingleFile { 3293104b268SNicolas Bonnefon public: 3303104b268SNicolas Bonnefon string second_dir_name; 3313104b268SNicolas Bonnefon string second_file_name; 3323104b268SNicolas Bonnefon string third_file_name; 3333104b268SNicolas Bonnefon 3343104b268SNicolas Bonnefon Registration registration_two; 3353104b268SNicolas Bonnefon Registration registration_three; 3363104b268SNicolas Bonnefon 3373104b268SNicolas Bonnefon WatchTowerDirectories() { 3383104b268SNicolas Bonnefon second_dir_name = createTempDir(); 3393104b268SNicolas Bonnefon second_file_name = createTempEmptyFileInDir( second_dir_name ); 3403104b268SNicolas Bonnefon third_file_name = createTempEmptyFileInDir( second_dir_name ); 3413104b268SNicolas Bonnefon } 3423104b268SNicolas Bonnefon 3433104b268SNicolas Bonnefon ~WatchTowerDirectories() { 3443104b268SNicolas Bonnefon remove( third_file_name.c_str() ); 3453104b268SNicolas Bonnefon remove( second_file_name.c_str() ); 3463104b268SNicolas Bonnefon 3473104b268SNicolas Bonnefon removeDir( second_dir_name ); 3483104b268SNicolas Bonnefon } 3493104b268SNicolas Bonnefon 3503104b268SNicolas Bonnefon string createTempDir() { 3513104b268SNicolas Bonnefon #ifdef _WIN32 3523104b268SNicolas Bonnefon static int counter = 1; 3533104b268SNicolas Bonnefon char temp_dir[255]; 3543104b268SNicolas Bonnefon 3553104b268SNicolas Bonnefon GetTempPath( sizeof temp_dir, temp_dir ); 3563104b268SNicolas Bonnefon 3573104b268SNicolas Bonnefon string dir_name = string { temp_dir } + string { "\\test" } + to_string( counter++ ); 3583104b268SNicolas Bonnefon mkdir( dir_name.c_str() ); 3593104b268SNicolas Bonnefon return dir_name; 3603104b268SNicolas Bonnefon #else 3613104b268SNicolas Bonnefon char dir_template[] = "/tmp/XXXXXX"; 3623104b268SNicolas Bonnefon return { mkdtemp( dir_template ) }; 3633104b268SNicolas Bonnefon #endif 3643104b268SNicolas Bonnefon } 3653104b268SNicolas Bonnefon 3663104b268SNicolas Bonnefon string createTempEmptyFileInDir( const string& dir ) { 3673104b268SNicolas Bonnefon static int counter = 1; 3683104b268SNicolas Bonnefon return createTempEmptyFile( dir + std::string { "/temp" } + to_string( counter++ ) ); 3693104b268SNicolas Bonnefon } 3703104b268SNicolas Bonnefon 3713104b268SNicolas Bonnefon void removeDir( const string& name ) { 3723104b268SNicolas Bonnefon rmdir( name.c_str() ); 3733104b268SNicolas Bonnefon } 3743104b268SNicolas Bonnefon }; 3753104b268SNicolas Bonnefon 3763104b268SNicolas Bonnefon TEST_F( WatchTowerDirectories, FollowThreeFilesInTwoDirs ) { 3773104b268SNicolas Bonnefon registration_two = registerFile( second_file_name ); 3783104b268SNicolas Bonnefon registration_three = registerFile( third_file_name ); 3793104b268SNicolas Bonnefon 3803104b268SNicolas Bonnefon ASSERT_THAT( watch_tower->numberWatchedDirectories(), Eq( 2 ) ); 3813104b268SNicolas Bonnefon } 3823104b268SNicolas Bonnefon 3833104b268SNicolas Bonnefon TEST_F( WatchTowerDirectories, FollowTwoFilesInTwoDirs ) { 3843104b268SNicolas Bonnefon registration_two = registerFile( second_file_name ); 3853104b268SNicolas Bonnefon { 3863104b268SNicolas Bonnefon auto temp_registration_three = registerFile( third_file_name ); 3873104b268SNicolas Bonnefon } 3883104b268SNicolas Bonnefon 3893104b268SNicolas Bonnefon ASSERT_THAT( watch_tower->numberWatchedDirectories(), Eq( 2 ) ); 3903104b268SNicolas Bonnefon } 3913104b268SNicolas Bonnefon 3923104b268SNicolas Bonnefon TEST_F( WatchTowerDirectories, FollowOneFileInOneDir ) { 3933104b268SNicolas Bonnefon { 3943104b268SNicolas Bonnefon auto temp_registration_two = registerFile( second_file_name ); 3953104b268SNicolas Bonnefon auto temp_registration_three = registerFile( third_file_name ); 3963104b268SNicolas Bonnefon 3973104b268SNicolas Bonnefon ASSERT_THAT( watch_tower->numberWatchedDirectories(), Eq( 2 ) ); 3983104b268SNicolas Bonnefon } 3993104b268SNicolas Bonnefon 4003104b268SNicolas Bonnefon ASSERT_THAT( watch_tower->numberWatchedDirectories(), Eq( 1 ) ); 4013104b268SNicolas Bonnefon } 4023104b268SNicolas Bonnefon 4033104b268SNicolas Bonnefon /*****/ 4043104b268SNicolas Bonnefon 4058b11848fSNicolas Bonnefon class WatchTowerInexistantDirectory: public WatchTowerDirectories { 4068b11848fSNicolas Bonnefon public: 4078b11848fSNicolas Bonnefon WatchTowerInexistantDirectory() { 4088b11848fSNicolas Bonnefon test_dir = createTempDir(); 4098b11848fSNicolas Bonnefon rmdir( test_dir.c_str() ); 4108b11848fSNicolas Bonnefon } 4118b11848fSNicolas Bonnefon 4128b11848fSNicolas Bonnefon ~WatchTowerInexistantDirectory() { 4138b11848fSNicolas Bonnefon rmdir( test_dir.c_str() ); 4148b11848fSNicolas Bonnefon } 4158b11848fSNicolas Bonnefon 4168b11848fSNicolas Bonnefon string test_dir; 4178b11848fSNicolas Bonnefon }; 4188b11848fSNicolas Bonnefon 4198b11848fSNicolas Bonnefon TEST_F( WatchTowerInexistantDirectory, LaterCreatedDirIsFollowed ) { 4208b11848fSNicolas Bonnefon /* Dir (and file) don't exist */ 4218b11848fSNicolas Bonnefon auto file_name = createTempEmptyFileInDir( test_dir ); 4228b11848fSNicolas Bonnefon { 4238b11848fSNicolas Bonnefon auto registration = registerFile( file_name ); 4248b11848fSNicolas Bonnefon 4258b11848fSNicolas Bonnefon #ifdef _WIN32 4268b11848fSNicolas Bonnefon mkdir( test_dir.c_str() ); 4278b11848fSNicolas Bonnefon #else 4288b11848fSNicolas Bonnefon mkdir( test_dir.c_str(), 0777 ); 4298b11848fSNicolas Bonnefon #endif 4308b11848fSNicolas Bonnefon createTempEmptyFile( file_name ); 4318b11848fSNicolas Bonnefon } 4328b11848fSNicolas Bonnefon 4338b11848fSNicolas Bonnefon auto registration2 = registerFile( file_name ); 4348b11848fSNicolas Bonnefon 4358b11848fSNicolas Bonnefon appendDataToFile( file_name ); 4368b11848fSNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 4378b11848fSNicolas Bonnefon 4388b11848fSNicolas Bonnefon remove( file_name.c_str() ); 4398b11848fSNicolas Bonnefon } 4408b11848fSNicolas Bonnefon 4418b11848fSNicolas Bonnefon /*****/ 4428b11848fSNicolas Bonnefon 443a0b17fd1SNicolas Bonnefon #ifdef _WIN32 444af7adfddSNicolas Bonnefon class WinNotificationInfoListTest : public testing::Test { 445af7adfddSNicolas Bonnefon public: 446af7adfddSNicolas Bonnefon using Action = WinNotificationInfo::Action; 447af7adfddSNicolas Bonnefon 448af7adfddSNicolas Bonnefon struct Buffer { 449af7adfddSNicolas Bonnefon uint32_t next_addr; 450af7adfddSNicolas Bonnefon uint32_t action; 451af7adfddSNicolas Bonnefon uint32_t filename_length; 452af7adfddSNicolas Bonnefon wchar_t filename[13]; 453af7adfddSNicolas Bonnefon }; 454af7adfddSNicolas Bonnefon static struct Buffer buffer[2]; 455af7adfddSNicolas Bonnefon 456af7adfddSNicolas Bonnefon WinNotificationInfoList list { reinterpret_cast<char*>( buffer ), sizeof( buffer ) }; 457af7adfddSNicolas Bonnefon WinNotificationInfoList::iterator iterator { std::begin( list ) }; 458af7adfddSNicolas Bonnefon }; 459af7adfddSNicolas Bonnefon 460af7adfddSNicolas Bonnefon struct WinNotificationInfoListTest::Buffer WinNotificationInfoListTest::buffer[] = 461af7adfddSNicolas Bonnefon { { 40, 1, 26, L"Filename.txt" }, 462af7adfddSNicolas Bonnefon { 0, 3, 18, L"file2.txt" } }; 463af7adfddSNicolas Bonnefon 464af7adfddSNicolas Bonnefon TEST_F( WinNotificationInfoListTest, FirstNotificationCanBeObtained ) { 465af7adfddSNicolas Bonnefon auto notification = *iterator; 466af7adfddSNicolas Bonnefon ASSERT_THAT( ¬ification, NotNull() ); 467af7adfddSNicolas Bonnefon } 468af7adfddSNicolas Bonnefon 469af7adfddSNicolas Bonnefon TEST_F( WinNotificationInfoListTest, FirstNotificationHasRightAction ) { 470af7adfddSNicolas Bonnefon ASSERT_THAT( (*iterator).action(), Eq( Action::ADDED ) ); 471af7adfddSNicolas Bonnefon } 472af7adfddSNicolas Bonnefon 473af7adfddSNicolas Bonnefon TEST_F( WinNotificationInfoListTest, FirstNotificationHasRightFileName ) { 474af7adfddSNicolas Bonnefon ASSERT_THAT( (*iterator).fileName(), Eq( std::wstring( L"Filename.txt\0", 13 ) ) ); 475af7adfddSNicolas Bonnefon } 476af7adfddSNicolas Bonnefon 477af7adfddSNicolas Bonnefon TEST_F( WinNotificationInfoListTest, SecondNotificationCanBeObtained ) { 478af7adfddSNicolas Bonnefon ++iterator; 479af7adfddSNicolas Bonnefon auto notification = *iterator; 480af7adfddSNicolas Bonnefon ASSERT_THAT( ¬ification, NotNull() ); 481af7adfddSNicolas Bonnefon } 482af7adfddSNicolas Bonnefon 483af7adfddSNicolas Bonnefon TEST_F( WinNotificationInfoListTest, SecondNotificationIsCorrect ) { 484af7adfddSNicolas Bonnefon iterator++; 485af7adfddSNicolas Bonnefon ASSERT_THAT( iterator->action(), Eq( Action::MODIFIED ) ); 486af7adfddSNicolas Bonnefon ASSERT_THAT( iterator->fileName(), Eq( L"file2.txt" ) ); 487af7adfddSNicolas Bonnefon } 488af7adfddSNicolas Bonnefon 489af7adfddSNicolas Bonnefon TEST_F( WinNotificationInfoListTest, CanBeIteratedByFor ) { 490af7adfddSNicolas Bonnefon for ( auto notification : list ) { 491af7adfddSNicolas Bonnefon notification.action(); 492af7adfddSNicolas Bonnefon } 493af7adfddSNicolas Bonnefon } 494a0b17fd1SNicolas Bonnefon #endif 495*fcaa7557SNicolas Bonnefon 496*fcaa7557SNicolas Bonnefon /*****/ 497*fcaa7557SNicolas Bonnefon 498*fcaa7557SNicolas Bonnefon #ifdef _WIN32 499*fcaa7557SNicolas Bonnefon class WatchTowerPolling : public WatchTowerSingleFile { 500*fcaa7557SNicolas Bonnefon public: 501*fcaa7557SNicolas Bonnefon WatchTowerPolling() : WatchTowerSingleFile() { 502*fcaa7557SNicolas Bonnefon // FILELog::setReportingLevel( logDEBUG ); 503*fcaa7557SNicolas Bonnefon 504*fcaa7557SNicolas Bonnefon fd_ = open( file_name.c_str(), O_WRONLY | O_APPEND ); 505*fcaa7557SNicolas Bonnefon } 506*fcaa7557SNicolas Bonnefon 507*fcaa7557SNicolas Bonnefon ~WatchTowerPolling() { 508*fcaa7557SNicolas Bonnefon close( fd_ ); 509*fcaa7557SNicolas Bonnefon } 510*fcaa7557SNicolas Bonnefon 511*fcaa7557SNicolas Bonnefon void appendDataToFileWoClosing() { 512*fcaa7557SNicolas Bonnefon static const char* string = "Test line\n"; 513*fcaa7557SNicolas Bonnefon write( fd_, (void*) string, strlen( string ) ); 514*fcaa7557SNicolas Bonnefon } 515*fcaa7557SNicolas Bonnefon 516*fcaa7557SNicolas Bonnefon int fd_; 517*fcaa7557SNicolas Bonnefon }; 518*fcaa7557SNicolas Bonnefon 519*fcaa7557SNicolas Bonnefon TEST_F( WatchTowerPolling, OpenFileDoesNotGenerateImmediateNotification ) { 520*fcaa7557SNicolas Bonnefon appendDataToFileWoClosing(); 521*fcaa7557SNicolas Bonnefon ASSERT_FALSE( waitNotificationReceived() ); 522*fcaa7557SNicolas Bonnefon } 523*fcaa7557SNicolas Bonnefon 524*fcaa7557SNicolas Bonnefon TEST_F( WatchTowerPolling, OpenFileYieldsAPollNotification ) { 525*fcaa7557SNicolas Bonnefon std::this_thread::sleep_for( std::chrono::milliseconds( 500 ) ); 526*fcaa7557SNicolas Bonnefon watch_tower->setPollingInterval( 500 ); 527*fcaa7557SNicolas Bonnefon appendDataToFileWoClosing(); 528*fcaa7557SNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 529*fcaa7557SNicolas Bonnefon } 530*fcaa7557SNicolas Bonnefon 531*fcaa7557SNicolas Bonnefon TEST_F( WatchTowerPolling, UnchangedFileDoesNotYieldANotification ) { 532*fcaa7557SNicolas Bonnefon watch_tower->setPollingInterval( 500 ); 533*fcaa7557SNicolas Bonnefon ASSERT_FALSE( waitNotificationReceived() ); 534*fcaa7557SNicolas Bonnefon } 535*fcaa7557SNicolas Bonnefon 536*fcaa7557SNicolas Bonnefon TEST_F( WatchTowerPolling, FileYieldsAnImmediateNotification ) { 537*fcaa7557SNicolas Bonnefon watch_tower->setPollingInterval( 4000 ); 538*fcaa7557SNicolas Bonnefon appendDataToFile( file_name ); 539*fcaa7557SNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived( 1, 2000 ) ); 540*fcaa7557SNicolas Bonnefon } 541*fcaa7557SNicolas Bonnefon 542*fcaa7557SNicolas Bonnefon TEST_F( WatchTowerPolling, PollIsDelayedIfImmediateNotification ) { 543*fcaa7557SNicolas Bonnefon watch_tower->setPollingInterval( 500 ); 544*fcaa7557SNicolas Bonnefon appendDataToFile( file_name ); 545*fcaa7557SNicolas Bonnefon waitNotificationReceived(); 546*fcaa7557SNicolas Bonnefon appendDataToFileWoClosing(); 547*fcaa7557SNicolas Bonnefon std::this_thread::sleep_for( std::chrono::milliseconds( 400 ) ); 548*fcaa7557SNicolas Bonnefon ASSERT_FALSE( waitNotificationReceived( 1, 250 ) ); 549*fcaa7557SNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 550*fcaa7557SNicolas Bonnefon } 551*fcaa7557SNicolas Bonnefon 552*fcaa7557SNicolas Bonnefon #endif 553