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 16*f09fa651SNicolas Bonnefon #include "watchtower.h" 17*f09fa651SNicolas Bonnefon 1887e05652SNicolas Bonnefon #ifdef _WIN32 19*f09fa651SNicolas Bonnefon # include "winwatchtowerdriver.h" 20*f09fa651SNicolas Bonnefon using PlatformWatchTower = WatchTower<WinWatchTowerDriver>; 2187e05652SNicolas Bonnefon #else 22b278d183SNicolas Bonnefon # include "inotifywatchtowerdriver.h" 23*f09fa651SNicolas 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 9084af0c9bSNicolas Bonnefon string file_name; 9196bde7d5SNicolas Bonnefon Registration registration; 9284af0c9bSNicolas Bonnefon 9384af0c9bSNicolas Bonnefon mutex mutex_; 9484af0c9bSNicolas Bonnefon condition_variable cv_; 958f031b5aSNicolas Bonnefon int notification_received = 0; 9684af0c9bSNicolas Bonnefon 9796bde7d5SNicolas Bonnefon Registration registerFile( const string& filename ) { 98b827fa8eSNicolas Bonnefon weak_ptr<void> weakHeartbeat( heartbeat_ ); 99b827fa8eSNicolas Bonnefon 10084b2179eSNicolas Bonnefon auto reg = watch_tower->addFile( filename, [this, weakHeartbeat] (void) { 101b827fa8eSNicolas Bonnefon // Ensure the fixture object is still alive using the heartbeat 102b827fa8eSNicolas Bonnefon if ( auto keep = weakHeartbeat.lock() ) { 10384af0c9bSNicolas Bonnefon unique_lock<mutex> lock(mutex_); 1048f031b5aSNicolas Bonnefon ++notification_received; 105b827fa8eSNicolas Bonnefon cv_.notify_one(); 106b827fa8eSNicolas Bonnefon } } ); 107b827fa8eSNicolas Bonnefon 108b827fa8eSNicolas Bonnefon return reg; 109b827fa8eSNicolas Bonnefon } 110b827fa8eSNicolas Bonnefon 11184b2179eSNicolas Bonnefon WatchTowerSingleFile() 11284b2179eSNicolas Bonnefon : heartbeat_( shared_ptr<void>( (void*) 0xDEADC0DE, [] (void*) {} ) ) 11384b2179eSNicolas Bonnefon { 114b827fa8eSNicolas Bonnefon file_name = createTempEmptyFile(); 115b827fa8eSNicolas Bonnefon registration = registerFile( file_name ); 11684af0c9bSNicolas Bonnefon } 11784af0c9bSNicolas Bonnefon 11884b2179eSNicolas Bonnefon ~WatchTowerSingleFile() { 11984b2179eSNicolas Bonnefon remove( file_name.c_str() ); 12084b2179eSNicolas Bonnefon } 12184b2179eSNicolas Bonnefon 1228f031b5aSNicolas Bonnefon bool waitNotificationReceived( int number = 1 ) { 12384af0c9bSNicolas Bonnefon unique_lock<mutex> lock(mutex_); 1248f031b5aSNicolas Bonnefon bool result = ( cv_.wait_for( lock, std::chrono::milliseconds(TIMEOUT), 1258f031b5aSNicolas Bonnefon [this, number] { return notification_received >= number; } ) ); 126b827fa8eSNicolas Bonnefon 127b827fa8eSNicolas Bonnefon // Reinit the notification 1288f031b5aSNicolas Bonnefon notification_received = 0; 129b827fa8eSNicolas Bonnefon 1308f031b5aSNicolas Bonnefon return result; 13184af0c9bSNicolas Bonnefon } 13284af0c9bSNicolas Bonnefon 13384af0c9bSNicolas Bonnefon void appendDataToFile( const string& file_name ) { 13484af0c9bSNicolas Bonnefon static const char* string = "Test line\n"; 13584af0c9bSNicolas Bonnefon int fd = open( file_name.c_str(), O_WRONLY | O_APPEND ); 13684af0c9bSNicolas Bonnefon write( fd, (void*) string, strlen( string ) ); 13784af0c9bSNicolas Bonnefon close( fd ); 13884af0c9bSNicolas Bonnefon } 139b827fa8eSNicolas Bonnefon 140b827fa8eSNicolas Bonnefon private: 141b827fa8eSNicolas Bonnefon // Heartbeat ensures the object is still alive 142b827fa8eSNicolas Bonnefon shared_ptr<void> heartbeat_; 14384af0c9bSNicolas Bonnefon }; 14484af0c9bSNicolas Bonnefon 145a0936e1eSNicolas Bonnefon #ifdef _WIN32 146*f09fa651SNicolas Bonnefon const int WatchTowerSingleFile::TIMEOUT = 1000; 147a0936e1eSNicolas Bonnefon #else 148b827fa8eSNicolas Bonnefon const int WatchTowerSingleFile::TIMEOUT = 20; 149a0936e1eSNicolas Bonnefon #endif 150a0936e1eSNicolas Bonnefon 15184b2179eSNicolas Bonnefon TEST_F( WatchTowerSingleFile, SignalsWhenAWatchedFileIsAppended ) { 15284af0c9bSNicolas Bonnefon appendDataToFile( file_name ); 15384af0c9bSNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 15484af0c9bSNicolas Bonnefon } 15584af0c9bSNicolas Bonnefon 156b827fa8eSNicolas Bonnefon TEST_F( WatchTowerSingleFile, SignalsWhenAWatchedFileIsRemoved) { 15784af0c9bSNicolas Bonnefon remove( file_name.c_str() ); 15884af0c9bSNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 15984af0c9bSNicolas Bonnefon } 160b827fa8eSNicolas Bonnefon 161b827fa8eSNicolas Bonnefon TEST_F( WatchTowerSingleFile, SignalsWhenADeletedFileReappears ) { 162b827fa8eSNicolas Bonnefon remove( file_name.c_str() ); 163b827fa8eSNicolas Bonnefon waitNotificationReceived(); 164b827fa8eSNicolas Bonnefon createTempEmptyFile( file_name ); 165b827fa8eSNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 166b827fa8eSNicolas Bonnefon } 167b827fa8eSNicolas Bonnefon 168b827fa8eSNicolas Bonnefon TEST_F( WatchTowerSingleFile, StopSignalingWhenWatchDeleted ) { 169b827fa8eSNicolas Bonnefon auto second_file_name = createTempEmptyFile(); 170b827fa8eSNicolas Bonnefon { 171b827fa8eSNicolas Bonnefon auto second_registration = registerFile( second_file_name ); 172b827fa8eSNicolas Bonnefon appendDataToFile( second_file_name ); 173b827fa8eSNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 174b827fa8eSNicolas Bonnefon } 175b827fa8eSNicolas Bonnefon 176b827fa8eSNicolas Bonnefon // The registration will be removed here. 177b827fa8eSNicolas Bonnefon appendDataToFile( second_file_name ); 178b827fa8eSNicolas Bonnefon ASSERT_FALSE( waitNotificationReceived() ); 179b827fa8eSNicolas Bonnefon 180b827fa8eSNicolas Bonnefon remove( second_file_name.c_str() ); 181b827fa8eSNicolas Bonnefon } 182b827fa8eSNicolas Bonnefon 183*f09fa651SNicolas Bonnefon TEST_F( WatchTowerSingleFile, SignalsWhenSameFileIsFollowedMultipleTimes ) { 184*f09fa651SNicolas Bonnefon auto second_file_name = createTempEmptyFile(); 185*f09fa651SNicolas Bonnefon 186*f09fa651SNicolas Bonnefon for ( int i = 0; i < 1000; i++ ) 187*f09fa651SNicolas Bonnefon { 188*f09fa651SNicolas Bonnefon std::this_thread::sleep_for( std::chrono::milliseconds( 50 ) ); 189*f09fa651SNicolas Bonnefon auto second_registration = registerFile( second_file_name ); 190*f09fa651SNicolas Bonnefon appendDataToFile( second_file_name ); 191*f09fa651SNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 192*f09fa651SNicolas Bonnefon std::this_thread::sleep_for( std::chrono::milliseconds( 50 ) ); 193*f09fa651SNicolas Bonnefon appendDataToFile( second_file_name ); 194*f09fa651SNicolas Bonnefon } 195*f09fa651SNicolas Bonnefon 196*f09fa651SNicolas Bonnefon // The registration will be removed here. 197*f09fa651SNicolas Bonnefon appendDataToFile( second_file_name ); 198*f09fa651SNicolas Bonnefon ASSERT_FALSE( waitNotificationReceived() ); 199*f09fa651SNicolas Bonnefon 200*f09fa651SNicolas Bonnefon remove( second_file_name.c_str() ); 201*f09fa651SNicolas Bonnefon } 202*f09fa651SNicolas Bonnefon 203a0b17fd1SNicolas Bonnefon TEST_F( WatchTowerSingleFile, TwoWatchesOnSameFileYieldsTwoNotifications ) { 2048f031b5aSNicolas Bonnefon auto second_registration = registerFile( file_name ); 2058f031b5aSNicolas Bonnefon appendDataToFile( file_name ); 2068f031b5aSNicolas Bonnefon 2078f031b5aSNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived( 2 ) ); 2088f031b5aSNicolas Bonnefon } 2098f031b5aSNicolas Bonnefon 210a0b17fd1SNicolas Bonnefon TEST_F( WatchTowerSingleFile, RemovingOneWatchOfTwoStillYieldsOneNotification ) { 2118f031b5aSNicolas Bonnefon { 2128f031b5aSNicolas Bonnefon auto second_registration = registerFile( file_name ); 2138f031b5aSNicolas Bonnefon } 2148f031b5aSNicolas Bonnefon 2158f031b5aSNicolas Bonnefon appendDataToFile( file_name ); 2168f031b5aSNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived( 1 ) ); 2178f031b5aSNicolas Bonnefon } 2188f031b5aSNicolas Bonnefon 219a0b17fd1SNicolas Bonnefon TEST_F( WatchTowerSingleFile, RenamingTheFileYieldsANotification ) { 220a0b17fd1SNicolas Bonnefon auto new_file_name = createTempName(); 2218f031b5aSNicolas Bonnefon 222a0b17fd1SNicolas Bonnefon rename( file_name.c_str(), new_file_name ); 2238f031b5aSNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 2248f031b5aSNicolas Bonnefon 225a0b17fd1SNicolas Bonnefon rename( new_file_name, file_name.c_str() ); 2268f031b5aSNicolas Bonnefon } 2278f031b5aSNicolas Bonnefon 228a0b17fd1SNicolas Bonnefon TEST_F( WatchTowerSingleFile, RenamingAFileToTheWatchedNameYieldsANotification ) { 2298f031b5aSNicolas Bonnefon remove( file_name.c_str() ); 2308f031b5aSNicolas Bonnefon waitNotificationReceived(); 2318f031b5aSNicolas Bonnefon 232a0b17fd1SNicolas Bonnefon std::string new_file_name = createTempEmptyFile(); 2338f031b5aSNicolas Bonnefon appendDataToFile( new_file_name ); 2348f031b5aSNicolas Bonnefon 2358f031b5aSNicolas Bonnefon rename( new_file_name.c_str(), file_name.c_str() ); 2368f031b5aSNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 2378f031b5aSNicolas Bonnefon } 2388f031b5aSNicolas Bonnefon 2398f031b5aSNicolas Bonnefon /*****/ 2408f031b5aSNicolas Bonnefon 24187e05652SNicolas Bonnefon #ifdef HAVE_SYMLINK 2423af68c64SNicolas Bonnefon class WatchTowerSymlink: public WatchTowerSingleFile { 2433af68c64SNicolas Bonnefon public: 2443af68c64SNicolas Bonnefon string symlink_name; 2453af68c64SNicolas Bonnefon 2463af68c64SNicolas Bonnefon void SetUp() override { 2473af68c64SNicolas Bonnefon file_name = createTempEmptyFile(); 2483af68c64SNicolas Bonnefon symlink_name = createTempEmptyFile(); 2493af68c64SNicolas Bonnefon remove( symlink_name.c_str() ); 2503af68c64SNicolas Bonnefon symlink( file_name.c_str(), symlink_name.c_str() ); 2513af68c64SNicolas Bonnefon 2523af68c64SNicolas Bonnefon registration = registerFile( symlink_name ); 2533af68c64SNicolas Bonnefon } 2543af68c64SNicolas Bonnefon 2553af68c64SNicolas Bonnefon void TearDown() override { 2563af68c64SNicolas Bonnefon remove( symlink_name.c_str() ); 2573af68c64SNicolas Bonnefon remove( file_name.c_str() ); 2583af68c64SNicolas Bonnefon } 2593af68c64SNicolas Bonnefon }; 2603af68c64SNicolas Bonnefon 2613af68c64SNicolas Bonnefon TEST_F( WatchTowerSymlink, AppendingToTheSymlinkYieldsANotification ) { 2623af68c64SNicolas Bonnefon appendDataToFile( symlink_name ); 2633af68c64SNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 2643af68c64SNicolas Bonnefon } 2653af68c64SNicolas Bonnefon 2663af68c64SNicolas Bonnefon TEST_F( WatchTowerSymlink, AppendingToTheTargetYieldsANotification ) { 2673af68c64SNicolas Bonnefon appendDataToFile( file_name ); 2683af68c64SNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 2693af68c64SNicolas Bonnefon } 2703af68c64SNicolas Bonnefon 2713af68c64SNicolas Bonnefon TEST_F( WatchTowerSymlink, RemovingTheSymlinkYieldsANotification ) { 2723af68c64SNicolas Bonnefon remove( symlink_name.c_str() ); 2733af68c64SNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 2743af68c64SNicolas Bonnefon } 2753af68c64SNicolas Bonnefon 2763af68c64SNicolas Bonnefon TEST_F( WatchTowerSymlink, RemovingTheTargetYieldsANotification ) { 2773af68c64SNicolas Bonnefon remove( file_name.c_str() ); 2783af68c64SNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 2793af68c64SNicolas Bonnefon } 2803af68c64SNicolas Bonnefon 2813af68c64SNicolas Bonnefon TEST_F( WatchTowerSymlink, ReappearingSymlinkYieldsANotification ) { 2823af68c64SNicolas Bonnefon auto new_target = createTempEmptyFile(); 2833af68c64SNicolas Bonnefon remove( symlink_name.c_str() ); 2843af68c64SNicolas Bonnefon waitNotificationReceived(); 2853af68c64SNicolas Bonnefon 2863af68c64SNicolas Bonnefon symlink( new_target.c_str(), symlink_name.c_str() ); 2873af68c64SNicolas Bonnefon ASSERT_TRUE( waitNotificationReceived() ); 2883af68c64SNicolas Bonnefon 2893af68c64SNicolas Bonnefon remove( new_target.c_str() ); 2903af68c64SNicolas Bonnefon } 29187e05652SNicolas Bonnefon #endif //HAVE_SYMLINK 2923af68c64SNicolas Bonnefon 2933af68c64SNicolas Bonnefon /*****/ 2943af68c64SNicolas Bonnefon 295b827fa8eSNicolas Bonnefon TEST( WatchTowerLifetime, RegistrationCanBeDeletedWhenWeAreDead ) { 29696bde7d5SNicolas Bonnefon auto mortal_watch_tower = new PlatformWatchTower(); 297b827fa8eSNicolas Bonnefon auto reg = mortal_watch_tower->addFile( "/tmp/test_file", [] (void) { } ); 298b827fa8eSNicolas Bonnefon 299b827fa8eSNicolas Bonnefon delete mortal_watch_tower; 3008f031b5aSNicolas Bonnefon // reg will be destroyed after the watch_tower 301b827fa8eSNicolas Bonnefon } 302af7adfddSNicolas Bonnefon 303af7adfddSNicolas Bonnefon /*****/ 304af7adfddSNicolas Bonnefon 305a0b17fd1SNicolas Bonnefon #ifdef _WIN32 306af7adfddSNicolas Bonnefon class WinNotificationInfoListTest : public testing::Test { 307af7adfddSNicolas Bonnefon public: 308af7adfddSNicolas Bonnefon using Action = WinNotificationInfo::Action; 309af7adfddSNicolas Bonnefon 310af7adfddSNicolas Bonnefon struct Buffer { 311af7adfddSNicolas Bonnefon uint32_t next_addr; 312af7adfddSNicolas Bonnefon uint32_t action; 313af7adfddSNicolas Bonnefon uint32_t filename_length; 314af7adfddSNicolas Bonnefon wchar_t filename[13]; 315af7adfddSNicolas Bonnefon }; 316af7adfddSNicolas Bonnefon static struct Buffer buffer[2]; 317af7adfddSNicolas Bonnefon 318af7adfddSNicolas Bonnefon WinNotificationInfoList list { reinterpret_cast<char*>( buffer ), sizeof( buffer ) }; 319af7adfddSNicolas Bonnefon WinNotificationInfoList::iterator iterator { std::begin( list ) }; 320af7adfddSNicolas Bonnefon }; 321af7adfddSNicolas Bonnefon 322af7adfddSNicolas Bonnefon struct WinNotificationInfoListTest::Buffer WinNotificationInfoListTest::buffer[] = 323af7adfddSNicolas Bonnefon { { 40, 1, 26, L"Filename.txt" }, 324af7adfddSNicolas Bonnefon { 0, 3, 18, L"file2.txt" } }; 325af7adfddSNicolas Bonnefon 326af7adfddSNicolas Bonnefon TEST_F( WinNotificationInfoListTest, FirstNotificationCanBeObtained ) { 327af7adfddSNicolas Bonnefon auto notification = *iterator; 328af7adfddSNicolas Bonnefon ASSERT_THAT( ¬ification, NotNull() ); 329af7adfddSNicolas Bonnefon } 330af7adfddSNicolas Bonnefon 331af7adfddSNicolas Bonnefon TEST_F( WinNotificationInfoListTest, FirstNotificationHasRightAction ) { 332af7adfddSNicolas Bonnefon ASSERT_THAT( (*iterator).action(), Eq( Action::ADDED ) ); 333af7adfddSNicolas Bonnefon } 334af7adfddSNicolas Bonnefon 335af7adfddSNicolas Bonnefon TEST_F( WinNotificationInfoListTest, FirstNotificationHasRightFileName ) { 336af7adfddSNicolas Bonnefon ASSERT_THAT( (*iterator).fileName(), Eq( std::wstring( L"Filename.txt\0", 13 ) ) ); 337af7adfddSNicolas Bonnefon } 338af7adfddSNicolas Bonnefon 339af7adfddSNicolas Bonnefon TEST_F( WinNotificationInfoListTest, SecondNotificationCanBeObtained ) { 340af7adfddSNicolas Bonnefon ++iterator; 341af7adfddSNicolas Bonnefon auto notification = *iterator; 342af7adfddSNicolas Bonnefon ASSERT_THAT( ¬ification, NotNull() ); 343af7adfddSNicolas Bonnefon } 344af7adfddSNicolas Bonnefon 345af7adfddSNicolas Bonnefon TEST_F( WinNotificationInfoListTest, SecondNotificationIsCorrect ) { 346af7adfddSNicolas Bonnefon iterator++; 347af7adfddSNicolas Bonnefon ASSERT_THAT( iterator->action(), Eq( Action::MODIFIED ) ); 348af7adfddSNicolas Bonnefon ASSERT_THAT( iterator->fileName(), Eq( L"file2.txt" ) ); 349af7adfddSNicolas Bonnefon } 350af7adfddSNicolas Bonnefon 351af7adfddSNicolas Bonnefon TEST_F( WinNotificationInfoListTest, CanBeIteratedByFor ) { 352af7adfddSNicolas Bonnefon for ( auto notification : list ) { 353af7adfddSNicolas Bonnefon notification.action(); 354af7adfddSNicolas Bonnefon } 355af7adfddSNicolas Bonnefon } 356a0b17fd1SNicolas Bonnefon #endif 357