xref: /glogg/tests/watchtowerTest.cpp (revision f869e41d2c129cd0f2f3eccb5e9d0d80a5998201)
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>;
21*f869e41dSNicolas Bonnefon #elif defined(__APPLE__)
22*f869e41dSNicolas Bonnefon #  include "kqueuewatchtowerdriver.h"
23*f869e41dSNicolas Bonnefon using PlatformWatchTower = WatchTower<KQueueWatchTowerDriver>;
2487e05652SNicolas Bonnefon #else
25b278d183SNicolas Bonnefon #  include "inotifywatchtowerdriver.h"
26f09fa651SNicolas Bonnefon using PlatformWatchTower = WatchTower<INotifyWatchTowerDriver>;
2787e05652SNicolas Bonnefon #endif
2884af0c9bSNicolas Bonnefon 
2984af0c9bSNicolas Bonnefon using namespace std;
3084af0c9bSNicolas Bonnefon using namespace testing;
3184af0c9bSNicolas Bonnefon 
3284af0c9bSNicolas Bonnefon class WatchTowerBehaviour: public testing::Test {
3384af0c9bSNicolas Bonnefon   public:
3496bde7d5SNicolas Bonnefon     shared_ptr<PlatformWatchTower> watch_tower = make_shared<PlatformWatchTower>();
3584af0c9bSNicolas Bonnefon 
createTempName()36a0b17fd1SNicolas Bonnefon     const char* createTempName()
37a0b17fd1SNicolas Bonnefon     {
38a0b17fd1SNicolas Bonnefon         const char* name;
39a0b17fd1SNicolas Bonnefon #if _WIN32
40a0b17fd1SNicolas Bonnefon         name = _tempnam( "c:\\temp", "glogg_test" );
41a0b17fd1SNicolas Bonnefon #else
42a0b17fd1SNicolas Bonnefon         name = tmpnam( nullptr );
43a0b17fd1SNicolas Bonnefon #endif
44a0b17fd1SNicolas Bonnefon         return name;
45a0b17fd1SNicolas Bonnefon     }
46a0b17fd1SNicolas Bonnefon 
createTempEmptyFile(string file_name="")47b827fa8eSNicolas Bonnefon     string createTempEmptyFile( string file_name = "" ) {
48b827fa8eSNicolas Bonnefon         const char* name;
49b827fa8eSNicolas Bonnefon 
50b827fa8eSNicolas Bonnefon         if ( ! file_name.empty() ) {
51b827fa8eSNicolas Bonnefon             name = file_name.c_str();
52b827fa8eSNicolas Bonnefon         }
53b827fa8eSNicolas Bonnefon         else {
5484af0c9bSNicolas Bonnefon             // I know tmpnam is bad but I need control over the file
5584af0c9bSNicolas Bonnefon             // and it is the only one which exits on Windows.
56a0b17fd1SNicolas Bonnefon             name = createTempName();
57b827fa8eSNicolas Bonnefon         }
58b827fa8eSNicolas Bonnefon         int fd = creat( name, S_IRUSR | S_IWUSR );
5984af0c9bSNicolas Bonnefon         close( fd );
6084af0c9bSNicolas Bonnefon 
61b827fa8eSNicolas Bonnefon         return string( name );
6284af0c9bSNicolas Bonnefon     }
6384af0c9bSNicolas Bonnefon 
getNonExistingFileName()6484af0c9bSNicolas Bonnefon     string getNonExistingFileName() {
6584b2179eSNicolas Bonnefon #if _WIN32
6684b2179eSNicolas Bonnefon         return string( _tempnam( "c:\\temp", "inexistant" ) );
6784b2179eSNicolas Bonnefon #else
6884af0c9bSNicolas Bonnefon         return string( tmpnam( nullptr ) );
6984b2179eSNicolas Bonnefon #endif
7084b2179eSNicolas Bonnefon     }
7184b2179eSNicolas Bonnefon 
WatchTowerBehaviour()7284b2179eSNicolas Bonnefon     WatchTowerBehaviour() {
7384b2179eSNicolas Bonnefon         // Default to quiet, but increase to debug
745d489994SNicolas Bonnefon         FILELog::setReportingLevel( logERROR );
7584af0c9bSNicolas Bonnefon     }
7684af0c9bSNicolas Bonnefon };
7784af0c9bSNicolas Bonnefon 
TEST_F(WatchTowerBehaviour,AcceptsAnExistingFileToWatch)7884af0c9bSNicolas Bonnefon TEST_F( WatchTowerBehaviour, AcceptsAnExistingFileToWatch ) {
7984af0c9bSNicolas Bonnefon     auto file_name = createTempEmptyFile();
8087e05652SNicolas Bonnefon     auto registration = watch_tower->addFile( file_name, [] (void) { } );
8184af0c9bSNicolas Bonnefon }
8284af0c9bSNicolas Bonnefon 
TEST_F(WatchTowerBehaviour,AcceptsANonExistingFileToWatch)8384af0c9bSNicolas Bonnefon TEST_F( WatchTowerBehaviour, AcceptsANonExistingFileToWatch ) {
8487e05652SNicolas Bonnefon     auto registration = watch_tower->addFile( getNonExistingFileName(), [] (void) { } );
8584af0c9bSNicolas Bonnefon }
8684af0c9bSNicolas Bonnefon 
87b827fa8eSNicolas Bonnefon /*****/
88b827fa8eSNicolas Bonnefon 
8984af0c9bSNicolas Bonnefon class WatchTowerSingleFile: public WatchTowerBehaviour {
9084af0c9bSNicolas Bonnefon   public:
91b827fa8eSNicolas Bonnefon     static const int TIMEOUT;
9284af0c9bSNicolas Bonnefon 
933104b268SNicolas Bonnefon     mutex mutex_;
943104b268SNicolas Bonnefon     condition_variable cv_;
953104b268SNicolas Bonnefon 
9684af0c9bSNicolas Bonnefon     string file_name;
9796bde7d5SNicolas Bonnefon     Registration registration;
9884af0c9bSNicolas Bonnefon 
998f031b5aSNicolas Bonnefon     int notification_received = 0;
10084af0c9bSNicolas Bonnefon 
registerFile(const string & filename)10196bde7d5SNicolas Bonnefon     Registration registerFile( const string& filename ) {
102b827fa8eSNicolas Bonnefon         weak_ptr<void> weakHeartbeat( heartbeat_ );
103b827fa8eSNicolas Bonnefon 
10484b2179eSNicolas Bonnefon         auto reg = watch_tower->addFile( filename, [this, weakHeartbeat] (void) {
105b827fa8eSNicolas Bonnefon             // Ensure the fixture object is still alive using the heartbeat
106b827fa8eSNicolas Bonnefon             if ( auto keep = weakHeartbeat.lock() ) {
10784af0c9bSNicolas Bonnefon                 unique_lock<mutex> lock(mutex_);
1088f031b5aSNicolas Bonnefon                 ++notification_received;
109b827fa8eSNicolas Bonnefon                 cv_.notify_one();
110b827fa8eSNicolas Bonnefon             } } );
111b827fa8eSNicolas Bonnefon 
112b827fa8eSNicolas Bonnefon         return reg;
113b827fa8eSNicolas Bonnefon     }
114b827fa8eSNicolas Bonnefon 
WatchTowerSingleFile()11584b2179eSNicolas Bonnefon     WatchTowerSingleFile()
11684b2179eSNicolas Bonnefon         : heartbeat_( shared_ptr<void>( (void*) 0xDEADC0DE, [] (void*) {} ) )
11784b2179eSNicolas Bonnefon     {
118b827fa8eSNicolas Bonnefon         file_name = createTempEmptyFile();
119b827fa8eSNicolas Bonnefon         registration = registerFile( file_name );
12084af0c9bSNicolas Bonnefon     }
12184af0c9bSNicolas Bonnefon 
~WatchTowerSingleFile()12284b2179eSNicolas Bonnefon     ~WatchTowerSingleFile() {
12384b2179eSNicolas Bonnefon         remove( file_name.c_str() );
12484b2179eSNicolas Bonnefon     }
12584b2179eSNicolas Bonnefon 
waitNotificationReceived(int number=1,int timeout_ms=TIMEOUT)126fcaa7557SNicolas Bonnefon     bool waitNotificationReceived( int number = 1, int timeout_ms = TIMEOUT ) {
12784af0c9bSNicolas Bonnefon         unique_lock<mutex> lock(mutex_);
128fcaa7557SNicolas Bonnefon         bool result = ( cv_.wait_for( lock, std::chrono::milliseconds(timeout_ms),
1298f031b5aSNicolas Bonnefon                 [this, number] { return notification_received >= number; } ) );
130b827fa8eSNicolas Bonnefon 
131b827fa8eSNicolas Bonnefon         // Reinit the notification
1328f031b5aSNicolas Bonnefon         notification_received = 0;
133b827fa8eSNicolas Bonnefon 
1348f031b5aSNicolas Bonnefon         return result;
13584af0c9bSNicolas Bonnefon     }
13684af0c9bSNicolas Bonnefon 
appendDataToFile(const string & file_name)13784af0c9bSNicolas Bonnefon     void appendDataToFile( const string& file_name ) {
13884af0c9bSNicolas Bonnefon         static const char* string = "Test line\n";
13984af0c9bSNicolas Bonnefon         int fd = open( file_name.c_str(), O_WRONLY | O_APPEND );
14084af0c9bSNicolas Bonnefon         write( fd, (void*) string, strlen( string ) );
14184af0c9bSNicolas Bonnefon         close( fd );
14284af0c9bSNicolas Bonnefon     }
143b827fa8eSNicolas Bonnefon 
144b827fa8eSNicolas Bonnefon   private:
145b827fa8eSNicolas Bonnefon     // Heartbeat ensures the object is still alive
146b827fa8eSNicolas Bonnefon     shared_ptr<void> heartbeat_;
14784af0c9bSNicolas Bonnefon };
14884af0c9bSNicolas Bonnefon 
149a0936e1eSNicolas Bonnefon #ifdef _WIN32
1503104b268SNicolas Bonnefon const int WatchTowerSingleFile::TIMEOUT = 2000;
151a0936e1eSNicolas Bonnefon #else
152b827fa8eSNicolas Bonnefon const int WatchTowerSingleFile::TIMEOUT = 20;
153a0936e1eSNicolas Bonnefon #endif
154a0936e1eSNicolas Bonnefon 
TEST_F(WatchTowerSingleFile,SignalsWhenAWatchedFileIsAppended)15584b2179eSNicolas Bonnefon TEST_F( WatchTowerSingleFile, SignalsWhenAWatchedFileIsAppended ) {
15684af0c9bSNicolas Bonnefon     appendDataToFile( file_name );
15784af0c9bSNicolas Bonnefon     ASSERT_TRUE( waitNotificationReceived() );
15884af0c9bSNicolas Bonnefon }
15984af0c9bSNicolas Bonnefon 
TEST_F(WatchTowerSingleFile,SignalsWhenAWatchedFileIsRemoved)160b827fa8eSNicolas Bonnefon TEST_F( WatchTowerSingleFile, SignalsWhenAWatchedFileIsRemoved) {
16184af0c9bSNicolas Bonnefon     remove( file_name.c_str() );
16284af0c9bSNicolas Bonnefon     ASSERT_TRUE( waitNotificationReceived() );
16384af0c9bSNicolas Bonnefon }
164b827fa8eSNicolas Bonnefon 
TEST_F(WatchTowerSingleFile,SignalsWhenADeletedFileReappears)165b827fa8eSNicolas Bonnefon TEST_F( WatchTowerSingleFile, SignalsWhenADeletedFileReappears ) {
166b827fa8eSNicolas Bonnefon     remove( file_name.c_str() );
167b827fa8eSNicolas Bonnefon     waitNotificationReceived();
168b827fa8eSNicolas Bonnefon     createTempEmptyFile( file_name );
169b827fa8eSNicolas Bonnefon     ASSERT_TRUE( waitNotificationReceived() );
170b827fa8eSNicolas Bonnefon }
171b827fa8eSNicolas Bonnefon 
TEST_F(WatchTowerSingleFile,SignalsWhenAReappearedFileIsAppended)17291f7c705SNicolas Bonnefon TEST_F( WatchTowerSingleFile, SignalsWhenAReappearedFileIsAppended ) {
17391f7c705SNicolas Bonnefon     remove( file_name.c_str() );
17491f7c705SNicolas Bonnefon     waitNotificationReceived();
17591f7c705SNicolas Bonnefon     createTempEmptyFile( file_name );
17691f7c705SNicolas Bonnefon     waitNotificationReceived();
17791f7c705SNicolas Bonnefon 
17891f7c705SNicolas Bonnefon     appendDataToFile( file_name );
17991f7c705SNicolas Bonnefon     ASSERT_TRUE( waitNotificationReceived() );
18091f7c705SNicolas Bonnefon }
18191f7c705SNicolas Bonnefon 
TEST_F(WatchTowerSingleFile,StopSignalingWhenWatchDeleted)182b827fa8eSNicolas Bonnefon TEST_F( WatchTowerSingleFile, StopSignalingWhenWatchDeleted ) {
183b827fa8eSNicolas Bonnefon     auto second_file_name = createTempEmptyFile();
1843104b268SNicolas Bonnefon     std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
1853104b268SNicolas Bonnefon     // Ensure file creation has been 'digested'
186b827fa8eSNicolas Bonnefon     {
187b827fa8eSNicolas Bonnefon         auto second_registration = registerFile( second_file_name );
188b827fa8eSNicolas Bonnefon         appendDataToFile( second_file_name );
189b827fa8eSNicolas Bonnefon         ASSERT_TRUE( waitNotificationReceived() );
190b827fa8eSNicolas Bonnefon     }
191b827fa8eSNicolas Bonnefon 
192b827fa8eSNicolas Bonnefon     // The registration will be removed here.
193b827fa8eSNicolas Bonnefon     appendDataToFile( second_file_name );
194b827fa8eSNicolas Bonnefon     ASSERT_FALSE( waitNotificationReceived() );
195b827fa8eSNicolas Bonnefon 
196b827fa8eSNicolas Bonnefon     remove( second_file_name.c_str() );
197b827fa8eSNicolas Bonnefon }
198b827fa8eSNicolas Bonnefon 
TEST_F(WatchTowerSingleFile,SignalsWhenSameFileIsFollowedMultipleTimes)199f09fa651SNicolas Bonnefon TEST_F( WatchTowerSingleFile, SignalsWhenSameFileIsFollowedMultipleTimes ) {
200f09fa651SNicolas Bonnefon     auto second_file_name = createTempEmptyFile();
201f09fa651SNicolas Bonnefon 
2023104b268SNicolas Bonnefon     for ( int i = 0; i < 100; i++ )
203f09fa651SNicolas Bonnefon     {
204f09fa651SNicolas Bonnefon         auto second_registration = registerFile( second_file_name );
205f09fa651SNicolas Bonnefon         appendDataToFile( second_file_name );
206f09fa651SNicolas Bonnefon         ASSERT_TRUE( waitNotificationReceived() );
207f09fa651SNicolas Bonnefon     }
208f09fa651SNicolas Bonnefon 
209f09fa651SNicolas Bonnefon     // The registration will be removed here.
210f09fa651SNicolas Bonnefon     appendDataToFile( second_file_name );
211f09fa651SNicolas Bonnefon     ASSERT_FALSE( waitNotificationReceived() );
212f09fa651SNicolas Bonnefon 
213f09fa651SNicolas Bonnefon     remove( second_file_name.c_str() );
214f09fa651SNicolas Bonnefon }
215f09fa651SNicolas Bonnefon 
TEST_F(WatchTowerSingleFile,TwoWatchesOnSameFileYieldsTwoNotifications)216a0b17fd1SNicolas Bonnefon TEST_F( WatchTowerSingleFile, TwoWatchesOnSameFileYieldsTwoNotifications ) {
2178f031b5aSNicolas Bonnefon     auto second_registration = registerFile( file_name );
2188f031b5aSNicolas Bonnefon     appendDataToFile( file_name );
2198f031b5aSNicolas Bonnefon 
2208f031b5aSNicolas Bonnefon     ASSERT_TRUE( waitNotificationReceived( 2 ) );
2218f031b5aSNicolas Bonnefon }
2228f031b5aSNicolas Bonnefon 
TEST_F(WatchTowerSingleFile,RemovingOneWatchOfTwoStillYieldsOneNotification)223a0b17fd1SNicolas Bonnefon TEST_F( WatchTowerSingleFile, RemovingOneWatchOfTwoStillYieldsOneNotification ) {
2248f031b5aSNicolas Bonnefon     {
2258f031b5aSNicolas Bonnefon         auto second_registration = registerFile( file_name );
2268f031b5aSNicolas Bonnefon     }
2278f031b5aSNicolas Bonnefon 
2288f031b5aSNicolas Bonnefon     appendDataToFile( file_name );
2298f031b5aSNicolas Bonnefon     ASSERT_TRUE( waitNotificationReceived( 1 ) );
2308f031b5aSNicolas Bonnefon }
2318f031b5aSNicolas Bonnefon 
TEST_F(WatchTowerSingleFile,RenamingTheFileYieldsANotification)232a0b17fd1SNicolas Bonnefon TEST_F( WatchTowerSingleFile, RenamingTheFileYieldsANotification ) {
233a0b17fd1SNicolas Bonnefon     auto new_file_name = createTempName();
2348f031b5aSNicolas Bonnefon 
235a0b17fd1SNicolas Bonnefon     rename( file_name.c_str(), new_file_name );
2368f031b5aSNicolas Bonnefon     ASSERT_TRUE( waitNotificationReceived() );
2378f031b5aSNicolas Bonnefon 
238a0b17fd1SNicolas Bonnefon     rename( new_file_name, file_name.c_str() );
2398f031b5aSNicolas Bonnefon }
2408f031b5aSNicolas Bonnefon 
TEST_F(WatchTowerSingleFile,RenamingAFileToTheWatchedNameYieldsANotification)241a0b17fd1SNicolas Bonnefon TEST_F( WatchTowerSingleFile, RenamingAFileToTheWatchedNameYieldsANotification ) {
2428f031b5aSNicolas Bonnefon     remove( file_name.c_str() );
2438f031b5aSNicolas Bonnefon     waitNotificationReceived();
2448f031b5aSNicolas Bonnefon 
245a0b17fd1SNicolas Bonnefon     std::string new_file_name = createTempEmptyFile();
2468f031b5aSNicolas Bonnefon     appendDataToFile( new_file_name );
2478f031b5aSNicolas Bonnefon 
2488f031b5aSNicolas Bonnefon     rename( new_file_name.c_str(), file_name.c_str() );
2498f031b5aSNicolas Bonnefon     ASSERT_TRUE( waitNotificationReceived() );
2508f031b5aSNicolas Bonnefon }
2518f031b5aSNicolas Bonnefon 
2528f031b5aSNicolas Bonnefon /*****/
2538f031b5aSNicolas Bonnefon 
25487e05652SNicolas Bonnefon #ifdef HAVE_SYMLINK
2553af68c64SNicolas Bonnefon class WatchTowerSymlink: public WatchTowerSingleFile {
2563af68c64SNicolas Bonnefon   public:
2573af68c64SNicolas Bonnefon     string symlink_name;
2583af68c64SNicolas Bonnefon 
SetUp()2593af68c64SNicolas Bonnefon     void SetUp() override {
2603af68c64SNicolas Bonnefon         file_name = createTempEmptyFile();
2613af68c64SNicolas Bonnefon         symlink_name = createTempEmptyFile();
2623af68c64SNicolas Bonnefon         remove( symlink_name.c_str() );
2633af68c64SNicolas Bonnefon         symlink( file_name.c_str(), symlink_name.c_str() );
2643af68c64SNicolas Bonnefon 
2653af68c64SNicolas Bonnefon         registration = registerFile( symlink_name );
2663af68c64SNicolas Bonnefon     }
2673af68c64SNicolas Bonnefon 
TearDown()2683af68c64SNicolas Bonnefon     void TearDown() override {
2693af68c64SNicolas Bonnefon         remove( symlink_name.c_str() );
2703af68c64SNicolas Bonnefon         remove( file_name.c_str() );
2713af68c64SNicolas Bonnefon     }
2723af68c64SNicolas Bonnefon };
2733af68c64SNicolas Bonnefon 
TEST_F(WatchTowerSymlink,AppendingToTheSymlinkYieldsANotification)2743af68c64SNicolas Bonnefon TEST_F( WatchTowerSymlink, AppendingToTheSymlinkYieldsANotification ) {
2753af68c64SNicolas Bonnefon     appendDataToFile( symlink_name );
2763af68c64SNicolas Bonnefon     ASSERT_TRUE( waitNotificationReceived() );
2773af68c64SNicolas Bonnefon }
2783af68c64SNicolas Bonnefon 
TEST_F(WatchTowerSymlink,AppendingToTheTargetYieldsANotification)2793af68c64SNicolas Bonnefon TEST_F( WatchTowerSymlink, AppendingToTheTargetYieldsANotification ) {
2803af68c64SNicolas Bonnefon     appendDataToFile( file_name );
2813af68c64SNicolas Bonnefon     ASSERT_TRUE( waitNotificationReceived() );
2823af68c64SNicolas Bonnefon }
2833af68c64SNicolas Bonnefon 
TEST_F(WatchTowerSymlink,RemovingTheSymlinkYieldsANotification)2843af68c64SNicolas Bonnefon TEST_F( WatchTowerSymlink, RemovingTheSymlinkYieldsANotification ) {
2853af68c64SNicolas Bonnefon     remove( symlink_name.c_str() );
2863af68c64SNicolas Bonnefon     ASSERT_TRUE( waitNotificationReceived() );
2873af68c64SNicolas Bonnefon }
2883af68c64SNicolas Bonnefon 
TEST_F(WatchTowerSymlink,RemovingTheTargetYieldsANotification)2893af68c64SNicolas Bonnefon TEST_F( WatchTowerSymlink, RemovingTheTargetYieldsANotification ) {
2903af68c64SNicolas Bonnefon     remove( file_name.c_str() );
2913af68c64SNicolas Bonnefon     ASSERT_TRUE( waitNotificationReceived() );
2923af68c64SNicolas Bonnefon }
2933af68c64SNicolas Bonnefon 
TEST_F(WatchTowerSymlink,ReappearingSymlinkYieldsANotification)2943af68c64SNicolas Bonnefon TEST_F( WatchTowerSymlink, ReappearingSymlinkYieldsANotification ) {
2953af68c64SNicolas Bonnefon     auto new_target = createTempEmptyFile();
2963af68c64SNicolas Bonnefon     remove( symlink_name.c_str() );
2973af68c64SNicolas Bonnefon     waitNotificationReceived();
2983af68c64SNicolas Bonnefon 
2993af68c64SNicolas Bonnefon     symlink( new_target.c_str(), symlink_name.c_str() );
3003af68c64SNicolas Bonnefon     ASSERT_TRUE( waitNotificationReceived() );
3013af68c64SNicolas Bonnefon 
3023af68c64SNicolas Bonnefon     remove( new_target.c_str() );
3033af68c64SNicolas Bonnefon }
30491f7c705SNicolas Bonnefon 
TEST_F(WatchTowerSymlink,DataAddedInAReappearingSymlinkYieldsANotification)30591f7c705SNicolas Bonnefon TEST_F( WatchTowerSymlink, DataAddedInAReappearingSymlinkYieldsANotification ) {
30691f7c705SNicolas Bonnefon     auto new_target = createTempEmptyFile();
30791f7c705SNicolas Bonnefon     remove( symlink_name.c_str() );
30891f7c705SNicolas Bonnefon     waitNotificationReceived();
30991f7c705SNicolas Bonnefon     symlink( new_target.c_str(), symlink_name.c_str() );
31091f7c705SNicolas Bonnefon     waitNotificationReceived();
31191f7c705SNicolas Bonnefon 
31291f7c705SNicolas Bonnefon     appendDataToFile( new_target );
31391f7c705SNicolas Bonnefon     ASSERT_TRUE( waitNotificationReceived() );
31491f7c705SNicolas Bonnefon 
31591f7c705SNicolas Bonnefon     remove( new_target.c_str() );
31691f7c705SNicolas Bonnefon }
31787e05652SNicolas Bonnefon #endif //HAVE_SYMLINK
3183af68c64SNicolas Bonnefon 
3193af68c64SNicolas Bonnefon /*****/
3203af68c64SNicolas Bonnefon 
TEST(WatchTowerLifetime,RegistrationCanBeDeletedWhenWeAreDead)321b827fa8eSNicolas Bonnefon TEST( WatchTowerLifetime, RegistrationCanBeDeletedWhenWeAreDead ) {
32296bde7d5SNicolas Bonnefon     auto mortal_watch_tower = new PlatformWatchTower();
323b827fa8eSNicolas Bonnefon     auto reg = mortal_watch_tower->addFile( "/tmp/test_file", [] (void) { } );
324b827fa8eSNicolas Bonnefon 
325b827fa8eSNicolas Bonnefon     delete mortal_watch_tower;
3268f031b5aSNicolas Bonnefon     // reg will be destroyed after the watch_tower
327b827fa8eSNicolas Bonnefon }
328af7adfddSNicolas Bonnefon 
329af7adfddSNicolas Bonnefon /*****/
330af7adfddSNicolas Bonnefon 
3313104b268SNicolas Bonnefon class WatchTowerDirectories: public WatchTowerSingleFile {
3323104b268SNicolas Bonnefon   public:
3333104b268SNicolas Bonnefon     string second_dir_name;
3343104b268SNicolas Bonnefon     string second_file_name;
3353104b268SNicolas Bonnefon     string third_file_name;
3363104b268SNicolas Bonnefon 
3373104b268SNicolas Bonnefon     Registration registration_two;
3383104b268SNicolas Bonnefon     Registration registration_three;
3393104b268SNicolas Bonnefon 
WatchTowerDirectories()3403104b268SNicolas Bonnefon     WatchTowerDirectories() {
3413104b268SNicolas Bonnefon         second_dir_name = createTempDir();
3423104b268SNicolas Bonnefon         second_file_name = createTempEmptyFileInDir( second_dir_name );
3433104b268SNicolas Bonnefon         third_file_name  = createTempEmptyFileInDir( second_dir_name );
3443104b268SNicolas Bonnefon     }
3453104b268SNicolas Bonnefon 
~WatchTowerDirectories()3463104b268SNicolas Bonnefon     ~WatchTowerDirectories() {
3473104b268SNicolas Bonnefon         remove( third_file_name.c_str() );
3483104b268SNicolas Bonnefon         remove( second_file_name.c_str() );
3493104b268SNicolas Bonnefon 
3503104b268SNicolas Bonnefon         removeDir( second_dir_name );
3513104b268SNicolas Bonnefon     }
3523104b268SNicolas Bonnefon 
createTempDir()3533104b268SNicolas Bonnefon     string createTempDir() {
3543104b268SNicolas Bonnefon #ifdef _WIN32
3553104b268SNicolas Bonnefon         static int counter = 1;
3563104b268SNicolas Bonnefon         char temp_dir[255];
3573104b268SNicolas Bonnefon 
3583104b268SNicolas Bonnefon         GetTempPath( sizeof temp_dir, temp_dir );
3593104b268SNicolas Bonnefon 
3603104b268SNicolas Bonnefon         string dir_name = string { temp_dir } + string { "\\test" } + to_string( counter++ );
3613104b268SNicolas Bonnefon         mkdir( dir_name.c_str() );
3623104b268SNicolas Bonnefon         return dir_name;
3633104b268SNicolas Bonnefon #else
3643104b268SNicolas Bonnefon         char dir_template[] = "/tmp/XXXXXX";
3653104b268SNicolas Bonnefon         return { mkdtemp( dir_template ) };
3663104b268SNicolas Bonnefon #endif
3673104b268SNicolas Bonnefon     }
3683104b268SNicolas Bonnefon 
createTempEmptyFileInDir(const string & dir)3693104b268SNicolas Bonnefon     string createTempEmptyFileInDir( const string& dir ) {
3703104b268SNicolas Bonnefon         static int counter = 1;
3713104b268SNicolas Bonnefon         return createTempEmptyFile( dir + std::string { "/temp" } + to_string( counter++ ) );
3723104b268SNicolas Bonnefon     }
3733104b268SNicolas Bonnefon 
removeDir(const string & name)3743104b268SNicolas Bonnefon     void removeDir( const string& name ) {
3753104b268SNicolas Bonnefon         rmdir( name.c_str() );
3763104b268SNicolas Bonnefon     }
3773104b268SNicolas Bonnefon };
3783104b268SNicolas Bonnefon 
TEST_F(WatchTowerDirectories,FollowThreeFilesInTwoDirs)3793104b268SNicolas Bonnefon TEST_F( WatchTowerDirectories, FollowThreeFilesInTwoDirs ) {
3803104b268SNicolas Bonnefon     registration_two   = registerFile( second_file_name );
3813104b268SNicolas Bonnefon     registration_three = registerFile( third_file_name );
3823104b268SNicolas Bonnefon 
3833104b268SNicolas Bonnefon     ASSERT_THAT( watch_tower->numberWatchedDirectories(), Eq( 2 ) );
3843104b268SNicolas Bonnefon }
3853104b268SNicolas Bonnefon 
TEST_F(WatchTowerDirectories,FollowTwoFilesInTwoDirs)3863104b268SNicolas Bonnefon TEST_F( WatchTowerDirectories, FollowTwoFilesInTwoDirs ) {
3873104b268SNicolas Bonnefon     registration_two   = registerFile( second_file_name );
3883104b268SNicolas Bonnefon     {
3893104b268SNicolas Bonnefon         auto temp_registration_three = registerFile( third_file_name );
3903104b268SNicolas Bonnefon     }
3913104b268SNicolas Bonnefon 
3923104b268SNicolas Bonnefon     ASSERT_THAT( watch_tower->numberWatchedDirectories(), Eq( 2 ) );
3933104b268SNicolas Bonnefon }
3943104b268SNicolas Bonnefon 
TEST_F(WatchTowerDirectories,FollowOneFileInOneDir)3953104b268SNicolas Bonnefon TEST_F( WatchTowerDirectories, FollowOneFileInOneDir ) {
3963104b268SNicolas Bonnefon     {
3973104b268SNicolas Bonnefon         auto temp_registration_two   = registerFile( second_file_name );
3983104b268SNicolas Bonnefon         auto temp_registration_three = registerFile( third_file_name );
3993104b268SNicolas Bonnefon 
4003104b268SNicolas Bonnefon         ASSERT_THAT( watch_tower->numberWatchedDirectories(), Eq( 2 ) );
4013104b268SNicolas Bonnefon     }
4023104b268SNicolas Bonnefon 
4033104b268SNicolas Bonnefon     ASSERT_THAT( watch_tower->numberWatchedDirectories(), Eq( 1 ) );
4043104b268SNicolas Bonnefon }
4053104b268SNicolas Bonnefon 
4063104b268SNicolas Bonnefon /*****/
4073104b268SNicolas Bonnefon 
4088b11848fSNicolas Bonnefon class WatchTowerInexistantDirectory: public WatchTowerDirectories {
4098b11848fSNicolas Bonnefon   public:
WatchTowerInexistantDirectory()4108b11848fSNicolas Bonnefon     WatchTowerInexistantDirectory() {
4118b11848fSNicolas Bonnefon         test_dir = createTempDir();
4128b11848fSNicolas Bonnefon         rmdir( test_dir.c_str() );
4138b11848fSNicolas Bonnefon     }
4148b11848fSNicolas Bonnefon 
~WatchTowerInexistantDirectory()4158b11848fSNicolas Bonnefon     ~WatchTowerInexistantDirectory() {
4168b11848fSNicolas Bonnefon         rmdir( test_dir.c_str() );
4178b11848fSNicolas Bonnefon     }
4188b11848fSNicolas Bonnefon 
4198b11848fSNicolas Bonnefon     string test_dir;
4208b11848fSNicolas Bonnefon };
4218b11848fSNicolas Bonnefon 
TEST_F(WatchTowerInexistantDirectory,LaterCreatedDirIsFollowed)4228b11848fSNicolas Bonnefon TEST_F( WatchTowerInexistantDirectory, LaterCreatedDirIsFollowed ) {
4238b11848fSNicolas Bonnefon     /* Dir (and file) don't exist */
4248b11848fSNicolas Bonnefon     auto file_name = createTempEmptyFileInDir( test_dir );
4258b11848fSNicolas Bonnefon     {
4268b11848fSNicolas Bonnefon         auto registration = registerFile( file_name );
4278b11848fSNicolas Bonnefon 
4288b11848fSNicolas Bonnefon #ifdef _WIN32
4298b11848fSNicolas Bonnefon         mkdir( test_dir.c_str() );
4308b11848fSNicolas Bonnefon #else
4318b11848fSNicolas Bonnefon         mkdir( test_dir.c_str(), 0777 );
4328b11848fSNicolas Bonnefon #endif
4338b11848fSNicolas Bonnefon         createTempEmptyFile( file_name );
4348b11848fSNicolas Bonnefon     }
4358b11848fSNicolas Bonnefon 
4368b11848fSNicolas Bonnefon     auto registration2 = registerFile( file_name );
4378b11848fSNicolas Bonnefon 
4388b11848fSNicolas Bonnefon     appendDataToFile( file_name );
4398b11848fSNicolas Bonnefon     ASSERT_TRUE( waitNotificationReceived() );
4408b11848fSNicolas Bonnefon 
4418b11848fSNicolas Bonnefon     remove( file_name.c_str() );
4428b11848fSNicolas Bonnefon }
4438b11848fSNicolas Bonnefon 
4448b11848fSNicolas Bonnefon /*****/
4458b11848fSNicolas Bonnefon 
446a0b17fd1SNicolas Bonnefon #ifdef _WIN32
447af7adfddSNicolas Bonnefon class WinNotificationInfoListTest : public testing::Test {
448af7adfddSNicolas Bonnefon   public:
449af7adfddSNicolas Bonnefon     using Action = WinNotificationInfo::Action;
450af7adfddSNicolas Bonnefon 
451af7adfddSNicolas Bonnefon     struct Buffer {
452af7adfddSNicolas Bonnefon         uint32_t next_addr;
453af7adfddSNicolas Bonnefon         uint32_t action;
454af7adfddSNicolas Bonnefon         uint32_t filename_length;
455af7adfddSNicolas Bonnefon         wchar_t filename[13];
456af7adfddSNicolas Bonnefon     };
457af7adfddSNicolas Bonnefon     static struct Buffer buffer[2];
458af7adfddSNicolas Bonnefon 
459af7adfddSNicolas Bonnefon     WinNotificationInfoList list { reinterpret_cast<char*>( buffer ), sizeof( buffer ) };
460af7adfddSNicolas Bonnefon     WinNotificationInfoList::iterator iterator { std::begin( list ) };
461af7adfddSNicolas Bonnefon };
462af7adfddSNicolas Bonnefon 
463af7adfddSNicolas Bonnefon struct WinNotificationInfoListTest::Buffer WinNotificationInfoListTest::buffer[] =
464af7adfddSNicolas Bonnefon     { { 40, 1, 26, L"Filename.txt" },
465af7adfddSNicolas Bonnefon       { 0, 3, 18, L"file2.txt" } };
466af7adfddSNicolas Bonnefon 
TEST_F(WinNotificationInfoListTest,FirstNotificationCanBeObtained)467af7adfddSNicolas Bonnefon TEST_F( WinNotificationInfoListTest, FirstNotificationCanBeObtained ) {
468af7adfddSNicolas Bonnefon     auto notification = *iterator;
469af7adfddSNicolas Bonnefon     ASSERT_THAT( &notification, NotNull() );
470af7adfddSNicolas Bonnefon }
471af7adfddSNicolas Bonnefon 
TEST_F(WinNotificationInfoListTest,FirstNotificationHasRightAction)472af7adfddSNicolas Bonnefon TEST_F( WinNotificationInfoListTest, FirstNotificationHasRightAction ) {
473af7adfddSNicolas Bonnefon     ASSERT_THAT( (*iterator).action(), Eq( Action::ADDED ) );
474af7adfddSNicolas Bonnefon }
475af7adfddSNicolas Bonnefon 
TEST_F(WinNotificationInfoListTest,FirstNotificationHasRightFileName)476af7adfddSNicolas Bonnefon TEST_F( WinNotificationInfoListTest, FirstNotificationHasRightFileName ) {
477af7adfddSNicolas Bonnefon     ASSERT_THAT( (*iterator).fileName(), Eq( std::wstring( L"Filename.txt\0", 13 ) ) );
478af7adfddSNicolas Bonnefon }
479af7adfddSNicolas Bonnefon 
TEST_F(WinNotificationInfoListTest,SecondNotificationCanBeObtained)480af7adfddSNicolas Bonnefon TEST_F( WinNotificationInfoListTest, SecondNotificationCanBeObtained ) {
481af7adfddSNicolas Bonnefon     ++iterator;
482af7adfddSNicolas Bonnefon     auto notification = *iterator;
483af7adfddSNicolas Bonnefon     ASSERT_THAT( &notification, NotNull() );
484af7adfddSNicolas Bonnefon }
485af7adfddSNicolas Bonnefon 
TEST_F(WinNotificationInfoListTest,SecondNotificationIsCorrect)486af7adfddSNicolas Bonnefon TEST_F( WinNotificationInfoListTest, SecondNotificationIsCorrect ) {
487af7adfddSNicolas Bonnefon     iterator++;
488af7adfddSNicolas Bonnefon     ASSERT_THAT( iterator->action(), Eq( Action::MODIFIED ) );
489af7adfddSNicolas Bonnefon     ASSERT_THAT( iterator->fileName(), Eq( L"file2.txt" ) );
490af7adfddSNicolas Bonnefon }
491af7adfddSNicolas Bonnefon 
TEST_F(WinNotificationInfoListTest,CanBeIteratedByFor)492af7adfddSNicolas Bonnefon TEST_F( WinNotificationInfoListTest, CanBeIteratedByFor ) {
493af7adfddSNicolas Bonnefon     for ( auto notification : list ) {
494af7adfddSNicolas Bonnefon         notification.action();
495af7adfddSNicolas Bonnefon     }
496af7adfddSNicolas Bonnefon }
497a0b17fd1SNicolas Bonnefon #endif
498fcaa7557SNicolas Bonnefon 
499fcaa7557SNicolas Bonnefon /*****/
500fcaa7557SNicolas Bonnefon 
501fcaa7557SNicolas Bonnefon #ifdef _WIN32
502fcaa7557SNicolas Bonnefon class WatchTowerPolling : public WatchTowerSingleFile {
503fcaa7557SNicolas Bonnefon   public:
WatchTowerPolling()504fcaa7557SNicolas Bonnefon     WatchTowerPolling() : WatchTowerSingleFile() {
505fcaa7557SNicolas Bonnefon         // FILELog::setReportingLevel( logDEBUG );
506fcaa7557SNicolas Bonnefon 
507fcaa7557SNicolas Bonnefon         fd_ = open( file_name.c_str(), O_WRONLY | O_APPEND );
508fcaa7557SNicolas Bonnefon     }
509fcaa7557SNicolas Bonnefon 
~WatchTowerPolling()510fcaa7557SNicolas Bonnefon     ~WatchTowerPolling() {
511fcaa7557SNicolas Bonnefon         close( fd_ );
512fcaa7557SNicolas Bonnefon     }
513fcaa7557SNicolas Bonnefon 
appendDataToFileWoClosing()514fcaa7557SNicolas Bonnefon     void appendDataToFileWoClosing() {
515fcaa7557SNicolas Bonnefon         static const char* string = "Test line\n";
516fcaa7557SNicolas Bonnefon         write( fd_, (void*) string, strlen( string ) );
517fcaa7557SNicolas Bonnefon     }
518fcaa7557SNicolas Bonnefon 
519fcaa7557SNicolas Bonnefon     int fd_;
520fcaa7557SNicolas Bonnefon };
521fcaa7557SNicolas Bonnefon 
TEST_F(WatchTowerPolling,OpenFileDoesNotGenerateImmediateNotification)522fcaa7557SNicolas Bonnefon TEST_F( WatchTowerPolling, OpenFileDoesNotGenerateImmediateNotification ) {
523fcaa7557SNicolas Bonnefon     appendDataToFileWoClosing();
524fcaa7557SNicolas Bonnefon     ASSERT_FALSE( waitNotificationReceived() );
525fcaa7557SNicolas Bonnefon }
526fcaa7557SNicolas Bonnefon 
TEST_F(WatchTowerPolling,OpenFileYieldsAPollNotification)527fcaa7557SNicolas Bonnefon TEST_F( WatchTowerPolling, OpenFileYieldsAPollNotification ) {
528fcaa7557SNicolas Bonnefon     std::this_thread::sleep_for( std::chrono::milliseconds( 500 ) );
529fcaa7557SNicolas Bonnefon     watch_tower->setPollingInterval( 500 );
530fcaa7557SNicolas Bonnefon     appendDataToFileWoClosing();
531fcaa7557SNicolas Bonnefon     ASSERT_TRUE( waitNotificationReceived() );
532fcaa7557SNicolas Bonnefon }
533fcaa7557SNicolas Bonnefon 
TEST_F(WatchTowerPolling,UnchangedFileDoesNotYieldANotification)534fcaa7557SNicolas Bonnefon TEST_F( WatchTowerPolling, UnchangedFileDoesNotYieldANotification ) {
535fcaa7557SNicolas Bonnefon     watch_tower->setPollingInterval( 500 );
536fcaa7557SNicolas Bonnefon     ASSERT_FALSE( waitNotificationReceived() );
537fcaa7557SNicolas Bonnefon }
538fcaa7557SNicolas Bonnefon 
TEST_F(WatchTowerPolling,FileYieldsAnImmediateNotification)539fcaa7557SNicolas Bonnefon TEST_F( WatchTowerPolling, FileYieldsAnImmediateNotification ) {
540fcaa7557SNicolas Bonnefon     watch_tower->setPollingInterval( 4000 );
541fcaa7557SNicolas Bonnefon     appendDataToFile( file_name );
542fcaa7557SNicolas Bonnefon     ASSERT_TRUE( waitNotificationReceived( 1, 2000 ) );
543fcaa7557SNicolas Bonnefon }
544fcaa7557SNicolas Bonnefon 
TEST_F(WatchTowerPolling,PollIsDelayedIfImmediateNotification)545fcaa7557SNicolas Bonnefon TEST_F( WatchTowerPolling, PollIsDelayedIfImmediateNotification ) {
546fcaa7557SNicolas Bonnefon     watch_tower->setPollingInterval( 500 );
547fcaa7557SNicolas Bonnefon     appendDataToFile( file_name );
548fcaa7557SNicolas Bonnefon     waitNotificationReceived();
549fcaa7557SNicolas Bonnefon     appendDataToFileWoClosing();
550fcaa7557SNicolas Bonnefon     std::this_thread::sleep_for( std::chrono::milliseconds( 400 ) );
551fcaa7557SNicolas Bonnefon     ASSERT_FALSE( waitNotificationReceived( 1, 250 ) );
552fcaa7557SNicolas Bonnefon     ASSERT_TRUE( waitNotificationReceived() );
553fcaa7557SNicolas Bonnefon }
554fcaa7557SNicolas Bonnefon 
555fcaa7557SNicolas Bonnefon #endif
556