xref: /glogg/tests/watchtowerTest.cpp (revision 91f7c70525aef93239c2facfe440e65d1672a468)
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 
1238f031b5aSNicolas Bonnefon     bool waitNotificationReceived( int number = 1 ) {
12484af0c9bSNicolas Bonnefon         unique_lock<mutex> lock(mutex_);
1258f031b5aSNicolas Bonnefon         bool result = ( cv_.wait_for( lock, std::chrono::milliseconds(TIMEOUT),
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 
169*91f7c705SNicolas Bonnefon TEST_F( WatchTowerSingleFile, SignalsWhenAReappearedFileIsAppended ) {
170*91f7c705SNicolas Bonnefon     remove( file_name.c_str() );
171*91f7c705SNicolas Bonnefon     waitNotificationReceived();
172*91f7c705SNicolas Bonnefon     createTempEmptyFile( file_name );
173*91f7c705SNicolas Bonnefon     waitNotificationReceived();
174*91f7c705SNicolas Bonnefon 
175*91f7c705SNicolas Bonnefon     appendDataToFile( file_name );
176*91f7c705SNicolas Bonnefon     ASSERT_TRUE( waitNotificationReceived() );
177*91f7c705SNicolas Bonnefon }
178*91f7c705SNicolas 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 }
301*91f7c705SNicolas Bonnefon 
302*91f7c705SNicolas Bonnefon TEST_F( WatchTowerSymlink, DataAddedInAReappearingSymlinkYieldsANotification ) {
303*91f7c705SNicolas Bonnefon     auto new_target = createTempEmptyFile();
304*91f7c705SNicolas Bonnefon     remove( symlink_name.c_str() );
305*91f7c705SNicolas Bonnefon     waitNotificationReceived();
306*91f7c705SNicolas Bonnefon     symlink( new_target.c_str(), symlink_name.c_str() );
307*91f7c705SNicolas Bonnefon     waitNotificationReceived();
308*91f7c705SNicolas Bonnefon 
309*91f7c705SNicolas Bonnefon     appendDataToFile( new_target );
310*91f7c705SNicolas Bonnefon     ASSERT_TRUE( waitNotificationReceived() );
311*91f7c705SNicolas Bonnefon 
312*91f7c705SNicolas Bonnefon     remove( new_target.c_str() );
313*91f7c705SNicolas 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 
405a0b17fd1SNicolas Bonnefon #ifdef _WIN32
406af7adfddSNicolas Bonnefon class WinNotificationInfoListTest : public testing::Test {
407af7adfddSNicolas Bonnefon   public:
408af7adfddSNicolas Bonnefon     using Action = WinNotificationInfo::Action;
409af7adfddSNicolas Bonnefon 
410af7adfddSNicolas Bonnefon     struct Buffer {
411af7adfddSNicolas Bonnefon         uint32_t next_addr;
412af7adfddSNicolas Bonnefon         uint32_t action;
413af7adfddSNicolas Bonnefon         uint32_t filename_length;
414af7adfddSNicolas Bonnefon         wchar_t filename[13];
415af7adfddSNicolas Bonnefon     };
416af7adfddSNicolas Bonnefon     static struct Buffer buffer[2];
417af7adfddSNicolas Bonnefon 
418af7adfddSNicolas Bonnefon     WinNotificationInfoList list { reinterpret_cast<char*>( buffer ), sizeof( buffer ) };
419af7adfddSNicolas Bonnefon     WinNotificationInfoList::iterator iterator { std::begin( list ) };
420af7adfddSNicolas Bonnefon };
421af7adfddSNicolas Bonnefon 
422af7adfddSNicolas Bonnefon struct WinNotificationInfoListTest::Buffer WinNotificationInfoListTest::buffer[] =
423af7adfddSNicolas Bonnefon     { { 40, 1, 26, L"Filename.txt" },
424af7adfddSNicolas Bonnefon       { 0, 3, 18, L"file2.txt" } };
425af7adfddSNicolas Bonnefon 
426af7adfddSNicolas Bonnefon TEST_F( WinNotificationInfoListTest, FirstNotificationCanBeObtained ) {
427af7adfddSNicolas Bonnefon     auto notification = *iterator;
428af7adfddSNicolas Bonnefon     ASSERT_THAT( &notification, NotNull() );
429af7adfddSNicolas Bonnefon }
430af7adfddSNicolas Bonnefon 
431af7adfddSNicolas Bonnefon TEST_F( WinNotificationInfoListTest, FirstNotificationHasRightAction ) {
432af7adfddSNicolas Bonnefon     ASSERT_THAT( (*iterator).action(), Eq( Action::ADDED ) );
433af7adfddSNicolas Bonnefon }
434af7adfddSNicolas Bonnefon 
435af7adfddSNicolas Bonnefon TEST_F( WinNotificationInfoListTest, FirstNotificationHasRightFileName ) {
436af7adfddSNicolas Bonnefon     ASSERT_THAT( (*iterator).fileName(), Eq( std::wstring( L"Filename.txt\0", 13 ) ) );
437af7adfddSNicolas Bonnefon }
438af7adfddSNicolas Bonnefon 
439af7adfddSNicolas Bonnefon TEST_F( WinNotificationInfoListTest, SecondNotificationCanBeObtained ) {
440af7adfddSNicolas Bonnefon     ++iterator;
441af7adfddSNicolas Bonnefon     auto notification = *iterator;
442af7adfddSNicolas Bonnefon     ASSERT_THAT( &notification, NotNull() );
443af7adfddSNicolas Bonnefon }
444af7adfddSNicolas Bonnefon 
445af7adfddSNicolas Bonnefon TEST_F( WinNotificationInfoListTest, SecondNotificationIsCorrect ) {
446af7adfddSNicolas Bonnefon     iterator++;
447af7adfddSNicolas Bonnefon     ASSERT_THAT( iterator->action(), Eq( Action::MODIFIED ) );
448af7adfddSNicolas Bonnefon     ASSERT_THAT( iterator->fileName(), Eq( L"file2.txt" ) );
449af7adfddSNicolas Bonnefon }
450af7adfddSNicolas Bonnefon 
451af7adfddSNicolas Bonnefon TEST_F( WinNotificationInfoListTest, CanBeIteratedByFor ) {
452af7adfddSNicolas Bonnefon     for ( auto notification : list ) {
453af7adfddSNicolas Bonnefon         notification.action();
454af7adfddSNicolas Bonnefon     }
455af7adfddSNicolas Bonnefon }
456a0b17fd1SNicolas Bonnefon #endif
457