184af0c9bSNicolas Bonnefon #include "gmock/gmock.h" 284af0c9bSNicolas Bonnefon 3*87e05652SNicolas Bonnefon #include "config.h" 4*87e05652SNicolas 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 14*87e05652SNicolas Bonnefon #ifdef _WIN32 15*87e05652SNicolas Bonnefon # include "winwatchtower.h" 16*87e05652SNicolas Bonnefon #else 1784af0c9bSNicolas Bonnefon # include "inotifywatchtower.h" 18*87e05652SNicolas Bonnefon #endif 1984af0c9bSNicolas Bonnefon 2084af0c9bSNicolas Bonnefon using namespace std; 2184af0c9bSNicolas Bonnefon using namespace testing; 2284af0c9bSNicolas Bonnefon 2384af0c9bSNicolas Bonnefon class WatchTowerBehaviour: public testing::Test { 2484af0c9bSNicolas Bonnefon public: 2584af0c9bSNicolas Bonnefon #ifdef _WIN32 26*87e05652SNicolas Bonnefon shared_ptr<WatchTower> watch_tower = make_shared<WinWatchTower>(); 27*87e05652SNicolas Bonnefon #else 28*87e05652SNicolas Bonnefon shared_ptr<WatchTower> watch_tower = make_shared<INotifyWatchTower>(); 2984af0c9bSNicolas Bonnefon #endif 3084af0c9bSNicolas Bonnefon 31b827fa8eSNicolas Bonnefon string createTempEmptyFile( string file_name = "" ) { 32b827fa8eSNicolas Bonnefon const char* name; 33b827fa8eSNicolas Bonnefon 34b827fa8eSNicolas Bonnefon if ( ! file_name.empty() ) { 35b827fa8eSNicolas Bonnefon name = file_name.c_str(); 36b827fa8eSNicolas Bonnefon } 37b827fa8eSNicolas Bonnefon else { 3884af0c9bSNicolas Bonnefon // I know tmpnam is bad but I need control over the file 3984af0c9bSNicolas Bonnefon // and it is the only one which exits on Windows. 40b827fa8eSNicolas Bonnefon name = tmpnam( nullptr ); 41b827fa8eSNicolas Bonnefon } 42b827fa8eSNicolas Bonnefon int fd = creat( name, S_IRUSR | S_IWUSR ); 4384af0c9bSNicolas Bonnefon close( fd ); 4484af0c9bSNicolas Bonnefon 45b827fa8eSNicolas Bonnefon return string( name ); 4684af0c9bSNicolas Bonnefon } 4784af0c9bSNicolas Bonnefon 4884af0c9bSNicolas Bonnefon string getNonExistingFileName() { 4984af0c9bSNicolas Bonnefon return string( tmpnam( nullptr ) ); 5084af0c9bSNicolas Bonnefon } 5184af0c9bSNicolas Bonnefon }; 5284af0c9bSNicolas Bonnefon 5384af0c9bSNicolas Bonnefon TEST_F( WatchTowerBehaviour, AcceptsAnExistingFileToWatch ) { 5484af0c9bSNicolas Bonnefon auto file_name = createTempEmptyFile(); 55*87e05652SNicolas Bonnefon auto registration = watch_tower->addFile( file_name, [] (void) { } ); 5684af0c9bSNicolas Bonnefon } 5784af0c9bSNicolas Bonnefon 5884af0c9bSNicolas Bonnefon TEST_F( WatchTowerBehaviour, AcceptsANonExistingFileToWatch ) { 59*87e05652SNicolas Bonnefon auto registration = watch_tower->addFile( getNonExistingFileName(), [] (void) { } ); 6084af0c9bSNicolas Bonnefon } 6184af0c9bSNicolas Bonnefon 62b827fa8eSNicolas Bonnefon /*****/ 63b827fa8eSNicolas Bonnefon 6484af0c9bSNicolas Bonnefon class WatchTowerSingleFile: public WatchTowerBehaviour { 6584af0c9bSNicolas Bonnefon public: 66b827fa8eSNicolas Bonnefon static const int TIMEOUT; 6784af0c9bSNicolas Bonnefon 6884af0c9bSNicolas Bonnefon string file_name; 69*87e05652SNicolas Bonnefon WatchTower::Registration registration; 7084af0c9bSNicolas Bonnefon 7184af0c9bSNicolas Bonnefon mutex mutex_; 7284af0c9bSNicolas Bonnefon condition_variable cv_; 738f031b5aSNicolas Bonnefon int notification_received = 0; 7484af0c9bSNicolas Bonnefon 75*87e05652SNicolas Bonnefon WatchTower::Registration registerFile( const string& file_name ) { 76b827fa8eSNicolas Bonnefon weak_ptr<void> weakHeartbeat( heartbeat_ ); 77b827fa8eSNicolas Bonnefon 78*87e05652SNicolas Bonnefon auto reg = watch_tower->addFile( file_name, [this, weakHeartbeat] (void) { 79b827fa8eSNicolas Bonnefon // Ensure the fixture object is still alive using the heartbeat 80b827fa8eSNicolas Bonnefon if ( auto keep = weakHeartbeat.lock() ) { 8184af0c9bSNicolas Bonnefon unique_lock<mutex> lock(mutex_); 828f031b5aSNicolas Bonnefon ++notification_received; 83b827fa8eSNicolas Bonnefon cv_.notify_one(); 84b827fa8eSNicolas Bonnefon } } ); 85b827fa8eSNicolas Bonnefon 86b827fa8eSNicolas Bonnefon return reg; 87b827fa8eSNicolas Bonnefon } 88b827fa8eSNicolas Bonnefon 89b827fa8eSNicolas Bonnefon WatchTowerSingleFile() : WatchTowerBehaviour(), 90b827fa8eSNicolas Bonnefon heartbeat_( shared_ptr<void>( (void*) 0xDEADC0DE, [] (void*) {} ) ) 91b827fa8eSNicolas Bonnefon { } 92b827fa8eSNicolas Bonnefon 93b827fa8eSNicolas Bonnefon void SetUp() override { 94b827fa8eSNicolas Bonnefon file_name = createTempEmptyFile(); 95b827fa8eSNicolas Bonnefon registration = registerFile( file_name ); 9684af0c9bSNicolas Bonnefon } 9784af0c9bSNicolas Bonnefon 988f031b5aSNicolas Bonnefon bool waitNotificationReceived( int number = 1 ) { 9984af0c9bSNicolas Bonnefon unique_lock<mutex> lock(mutex_); 1008f031b5aSNicolas Bonnefon bool result = ( cv_.wait_for( lock, std::chrono::milliseconds(TIMEOUT), 1018f031b5aSNicolas Bonnefon [this, number] { return notification_received >= number; } ) ); 102b827fa8eSNicolas Bonnefon 103b827fa8eSNicolas Bonnefon // Reinit the notification 1048f031b5aSNicolas Bonnefon notification_received = 0; 105b827fa8eSNicolas Bonnefon 1068f031b5aSNicolas Bonnefon return result; 10784af0c9bSNicolas Bonnefon } 10884af0c9bSNicolas Bonnefon 10984af0c9bSNicolas Bonnefon void appendDataToFile( const string& file_name ) { 11084af0c9bSNicolas Bonnefon static const char* string = "Test line\n"; 11184af0c9bSNicolas Bonnefon int fd = open( file_name.c_str(), O_WRONLY | O_APPEND ); 11284af0c9bSNicolas Bonnefon write( fd, (void*) string, strlen( string ) ); 11384af0c9bSNicolas Bonnefon close( fd ); 11484af0c9bSNicolas Bonnefon } 115b827fa8eSNicolas Bonnefon 116b827fa8eSNicolas Bonnefon void TearDown() override { 117b827fa8eSNicolas Bonnefon remove( file_name.c_str() ); 118b827fa8eSNicolas Bonnefon } 119b827fa8eSNicolas Bonnefon 120b827fa8eSNicolas Bonnefon private: 121b827fa8eSNicolas Bonnefon // Heartbeat ensures the object is still alive 122b827fa8eSNicolas Bonnefon shared_ptr<void> heartbeat_; 12384af0c9bSNicolas Bonnefon }; 12484af0c9bSNicolas Bonnefon 125b827fa8eSNicolas Bonnefon const int WatchTowerSingleFile::TIMEOUT = 20; 126b827fa8eSNicolas Bonnefon 12784af0c9bSNicolas Bonnefon TEST_F( WatchTowerSingleFile, SignalsWhenAWatchedFileIsAppended ) { 12884af0c9bSNicolas Bonnefon appendDataToFile( file_name ); 12984af0c9bSNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 13084af0c9bSNicolas Bonnefon } 13184af0c9bSNicolas Bonnefon 132b827fa8eSNicolas Bonnefon TEST_F( WatchTowerSingleFile, SignalsWhenAWatchedFileIsRemoved) { 13384af0c9bSNicolas Bonnefon remove( file_name.c_str() ); 13484af0c9bSNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 13584af0c9bSNicolas Bonnefon } 136b827fa8eSNicolas Bonnefon 137b827fa8eSNicolas Bonnefon TEST_F( WatchTowerSingleFile, SignalsWhenADeletedFileReappears ) { 138b827fa8eSNicolas Bonnefon remove( file_name.c_str() ); 139b827fa8eSNicolas Bonnefon waitNotificationReceived(); 140b827fa8eSNicolas Bonnefon createTempEmptyFile( file_name ); 141b827fa8eSNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 142b827fa8eSNicolas Bonnefon } 143b827fa8eSNicolas Bonnefon 144b827fa8eSNicolas Bonnefon TEST_F( WatchTowerSingleFile, StopSignalingWhenWatchDeleted ) { 145b827fa8eSNicolas Bonnefon auto second_file_name = createTempEmptyFile(); 146b827fa8eSNicolas Bonnefon { 147b827fa8eSNicolas Bonnefon auto second_registration = registerFile( second_file_name ); 148b827fa8eSNicolas Bonnefon appendDataToFile( second_file_name ); 149b827fa8eSNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 150b827fa8eSNicolas Bonnefon } 151b827fa8eSNicolas Bonnefon 152b827fa8eSNicolas Bonnefon // The registration will be removed here. 153b827fa8eSNicolas Bonnefon appendDataToFile( second_file_name ); 154b827fa8eSNicolas Bonnefon ASSERT_FALSE( waitNotificationReceived() ); 155b827fa8eSNicolas Bonnefon 156b827fa8eSNicolas Bonnefon remove( second_file_name.c_str() ); 157b827fa8eSNicolas Bonnefon } 158b827fa8eSNicolas Bonnefon 1598f031b5aSNicolas Bonnefon TEST_F( WatchTowerSingleFile, TwoWatchesOnSameFileYieldsTwoNotifications ) { 1608f031b5aSNicolas Bonnefon auto second_registration = registerFile( file_name ); 1618f031b5aSNicolas Bonnefon appendDataToFile( file_name ); 1628f031b5aSNicolas Bonnefon 1638f031b5aSNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived( 2 ) ); 1648f031b5aSNicolas Bonnefon } 1658f031b5aSNicolas Bonnefon 1668f031b5aSNicolas Bonnefon TEST_F( WatchTowerSingleFile, RemovingOneWatchOfTwoStillYieldsOneNotification ) { 1678f031b5aSNicolas Bonnefon { 1688f031b5aSNicolas Bonnefon auto second_registration = registerFile( file_name ); 1698f031b5aSNicolas Bonnefon } 1708f031b5aSNicolas Bonnefon 1718f031b5aSNicolas Bonnefon appendDataToFile( file_name ); 1728f031b5aSNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived( 1 ) ); 1738f031b5aSNicolas Bonnefon } 1748f031b5aSNicolas Bonnefon 1758f031b5aSNicolas Bonnefon TEST_F( WatchTowerSingleFile, RenamingTheFileYieldsANotification ) { 1768f031b5aSNicolas Bonnefon auto new_file_name = createTempEmptyFile(); 1778f031b5aSNicolas Bonnefon remove( new_file_name.c_str() ); 1788f031b5aSNicolas Bonnefon 1798f031b5aSNicolas Bonnefon rename( file_name.c_str(), new_file_name.c_str() ); 1808f031b5aSNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 1818f031b5aSNicolas Bonnefon 1828f031b5aSNicolas Bonnefon rename( new_file_name.c_str(), file_name.c_str() ); 1838f031b5aSNicolas Bonnefon } 1848f031b5aSNicolas Bonnefon 1858f031b5aSNicolas Bonnefon TEST_F( WatchTowerSingleFile, RenamingAFileToTheWatchedNameYieldsANotification ) { 1868f031b5aSNicolas Bonnefon remove( file_name.c_str() ); 1878f031b5aSNicolas Bonnefon waitNotificationReceived(); 1888f031b5aSNicolas Bonnefon 1898f031b5aSNicolas Bonnefon auto new_file_name = createTempEmptyFile(); 1908f031b5aSNicolas Bonnefon appendDataToFile( new_file_name ); 1918f031b5aSNicolas Bonnefon 1928f031b5aSNicolas Bonnefon rename( new_file_name.c_str(), file_name.c_str() ); 1938f031b5aSNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 1948f031b5aSNicolas Bonnefon } 1958f031b5aSNicolas Bonnefon 1968f031b5aSNicolas Bonnefon /*****/ 1978f031b5aSNicolas Bonnefon 198*87e05652SNicolas Bonnefon #ifdef HAVE_SYMLINK 1993af68c64SNicolas Bonnefon class WatchTowerSymlink: public WatchTowerSingleFile { 2003af68c64SNicolas Bonnefon public: 2013af68c64SNicolas Bonnefon string symlink_name; 2023af68c64SNicolas Bonnefon 2033af68c64SNicolas Bonnefon void SetUp() override { 2043af68c64SNicolas Bonnefon file_name = createTempEmptyFile(); 2053af68c64SNicolas Bonnefon symlink_name = createTempEmptyFile(); 2063af68c64SNicolas Bonnefon remove( symlink_name.c_str() ); 2073af68c64SNicolas Bonnefon symlink( file_name.c_str(), symlink_name.c_str() ); 2083af68c64SNicolas Bonnefon 2093af68c64SNicolas Bonnefon registration = registerFile( symlink_name ); 2103af68c64SNicolas Bonnefon } 2113af68c64SNicolas Bonnefon 2123af68c64SNicolas Bonnefon void TearDown() override { 2133af68c64SNicolas Bonnefon remove( symlink_name.c_str() ); 2143af68c64SNicolas Bonnefon remove( file_name.c_str() ); 2153af68c64SNicolas Bonnefon } 2163af68c64SNicolas Bonnefon }; 2173af68c64SNicolas Bonnefon 2183af68c64SNicolas Bonnefon TEST_F( WatchTowerSymlink, AppendingToTheSymlinkYieldsANotification ) { 2193af68c64SNicolas Bonnefon appendDataToFile( symlink_name ); 2203af68c64SNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 2213af68c64SNicolas Bonnefon } 2223af68c64SNicolas Bonnefon 2233af68c64SNicolas Bonnefon TEST_F( WatchTowerSymlink, AppendingToTheTargetYieldsANotification ) { 2243af68c64SNicolas Bonnefon appendDataToFile( file_name ); 2253af68c64SNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 2263af68c64SNicolas Bonnefon } 2273af68c64SNicolas Bonnefon 2283af68c64SNicolas Bonnefon TEST_F( WatchTowerSymlink, RemovingTheSymlinkYieldsANotification ) { 2293af68c64SNicolas Bonnefon remove( symlink_name.c_str() ); 2303af68c64SNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 2313af68c64SNicolas Bonnefon } 2323af68c64SNicolas Bonnefon 2333af68c64SNicolas Bonnefon TEST_F( WatchTowerSymlink, RemovingTheTargetYieldsANotification ) { 2343af68c64SNicolas Bonnefon remove( file_name.c_str() ); 2353af68c64SNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 2363af68c64SNicolas Bonnefon } 2373af68c64SNicolas Bonnefon 2383af68c64SNicolas Bonnefon TEST_F( WatchTowerSymlink, ReappearingSymlinkYieldsANotification ) { 2393af68c64SNicolas Bonnefon auto new_target = createTempEmptyFile(); 2403af68c64SNicolas Bonnefon remove( symlink_name.c_str() ); 2413af68c64SNicolas Bonnefon waitNotificationReceived(); 2423af68c64SNicolas Bonnefon 2433af68c64SNicolas Bonnefon symlink( new_target.c_str(), symlink_name.c_str() ); 2443af68c64SNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 2453af68c64SNicolas Bonnefon 2463af68c64SNicolas Bonnefon remove( new_target.c_str() ); 2473af68c64SNicolas Bonnefon } 248*87e05652SNicolas Bonnefon #endif //HAVE_SYMLINK 2493af68c64SNicolas Bonnefon 2503af68c64SNicolas Bonnefon /*****/ 2513af68c64SNicolas Bonnefon 252b827fa8eSNicolas Bonnefon TEST( WatchTowerLifetime, RegistrationCanBeDeletedWhenWeAreDead ) { 253*87e05652SNicolas Bonnefon #if 0 254b827fa8eSNicolas Bonnefon auto mortal_watch_tower = new INotifyWatchTower(); 255b827fa8eSNicolas Bonnefon auto reg = mortal_watch_tower->addFile( "/tmp/test_file", [] (void) { } ); 256b827fa8eSNicolas Bonnefon 257b827fa8eSNicolas Bonnefon delete mortal_watch_tower; 2588f031b5aSNicolas Bonnefon // reg will be destroyed after the watch_tower 259*87e05652SNicolas Bonnefon #endif 260b827fa8eSNicolas Bonnefon } 261