xref: /glogg/tests/watchtowerTest.cpp (revision b827fa8e48a40c9a02ee9edc78c3e8fef0bf062e)
1 #include "gmock/gmock.h"
2 
3 #include <cstdio>
4 #include <fcntl.h>
5 
6 #include <memory>
7 #include <condition_variable>
8 #include <thread>
9 #include <mutex>
10 #include <chrono>
11 
12 #include "inotifywatchtower.h"
13 
14 using namespace std;
15 using namespace testing;
16 
17 class WatchTowerBehaviour: public testing::Test {
18   public:
19     INotifyWatchTower watch_tower;
20 
21     void SetUp() override {
22 #ifdef _WIN32
23         file_watcher = make_shared<WinFileWatcher>();
24 #endif
25     }
26 
27     string createTempEmptyFile( string file_name = "" ) {
28         const char* name;
29 
30         if ( ! file_name.empty() ) {
31             name = file_name.c_str();
32         }
33         else {
34             // I know tmpnam is bad but I need control over the file
35             // and it is the only one which exits on Windows.
36             name = tmpnam( nullptr );
37         }
38         int fd = creat( name, S_IRUSR | S_IWUSR );
39         close( fd );
40 
41         return string( name );
42     }
43 
44     string getNonExistingFileName() {
45         return string( tmpnam( nullptr ) );
46     }
47 };
48 
49 TEST_F( WatchTowerBehaviour, AcceptsAnExistingFileToWatch ) {
50     auto file_name = createTempEmptyFile();
51     auto registration = watch_tower.addFile( file_name, [] (void) { } );
52 }
53 
54 TEST_F( WatchTowerBehaviour, AcceptsANonExistingFileToWatch ) {
55     auto registration = watch_tower.addFile( getNonExistingFileName(), [] (void) { } );
56 }
57 
58 /*****/
59 
60 class WatchTowerSingleFile: public WatchTowerBehaviour {
61   public:
62     static const int TIMEOUT;
63 
64     string file_name;
65     INotifyWatchTower::Registration registration;
66 
67     mutex mutex_;
68     condition_variable cv_;
69     bool notification_received = false;
70 
71     INotifyWatchTower::Registration registerFile( const string& file_name ) {
72         weak_ptr<void> weakHeartbeat( heartbeat_ );
73 
74         auto reg = watch_tower.addFile( file_name, [this, weakHeartbeat] (void) {
75             // Ensure the fixture object is still alive using the heartbeat
76             if ( auto keep = weakHeartbeat.lock() ) {
77                 unique_lock<mutex> lock(mutex_);
78                 notification_received = true;
79                 cv_.notify_one();
80             } } );
81 
82         return reg;
83     }
84 
85     WatchTowerSingleFile() : WatchTowerBehaviour(),
86         heartbeat_( shared_ptr<void>( (void*) 0xDEADC0DE, [] (void*) {} ) )
87     { }
88 
89     void SetUp() override {
90         file_name = createTempEmptyFile();
91         registration = registerFile( file_name );
92     }
93 
94     bool waitNotificationReceived() {
95         unique_lock<mutex> lock(mutex_);
96         bool success = cv_.wait_for( lock, std::chrono::milliseconds(TIMEOUT),
97                 [this] { return notification_received; } );
98 
99         // Reinit the notification
100         notification_received = false;
101 
102         return success;
103     }
104 
105     void appendDataToFile( const string& file_name ) {
106         static const char* string = "Test line\n";
107         int fd = open( file_name.c_str(), O_WRONLY | O_APPEND );
108         write( fd, (void*) string, strlen( string ) );
109         close( fd );
110     }
111 
112     void TearDown() override {
113         remove( file_name.c_str() );
114     }
115 
116   private:
117     // Heartbeat ensures the object is still alive
118     shared_ptr<void> heartbeat_;
119 };
120 
121 const int WatchTowerSingleFile::TIMEOUT = 20;
122 
123 TEST_F( WatchTowerSingleFile, SignalsWhenAWatchedFileIsAppended ) {
124     appendDataToFile( file_name );
125     ASSERT_TRUE( waitNotificationReceived() );
126 }
127 
128 TEST_F( WatchTowerSingleFile, SignalsWhenAWatchedFileIsRemoved) {
129     remove( file_name.c_str() );
130     ASSERT_TRUE( waitNotificationReceived() );
131 }
132 
133 TEST_F( WatchTowerSingleFile, SignalsWhenADeletedFileReappears ) {
134     remove( file_name.c_str() );
135     waitNotificationReceived();
136     createTempEmptyFile( file_name );
137     ASSERT_TRUE( waitNotificationReceived() );
138 }
139 
140 TEST_F( WatchTowerSingleFile, StopSignalingWhenWatchDeleted ) {
141     auto second_file_name = createTempEmptyFile();
142     {
143         auto second_registration = registerFile( second_file_name );
144         appendDataToFile( second_file_name );
145         ASSERT_TRUE( waitNotificationReceived() );
146     }
147 
148     // The registration will be removed here.
149     appendDataToFile( second_file_name );
150     ASSERT_FALSE( waitNotificationReceived() );
151 
152     remove( second_file_name.c_str() );
153 }
154 
155 TEST( WatchTowerLifetime, RegistrationCanBeDeletedWhenWeAreDead ) {
156     auto mortal_watch_tower = new INotifyWatchTower();
157     auto reg = mortal_watch_tower->addFile( "/tmp/test_file", [] (void) { } );
158 
159     delete mortal_watch_tower;
160 }
161