WatcherTests.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. #include <wil/filesystem.h>
  2. #include <wil/registry.h>
  3. #include <wil/resource.h>
  4. #include <memory> // For shared_event_watcher
  5. #include <wil/resource.h>
  6. #include "common.h"
  7. TEST_CASE("EventWatcherTests::Construction", "[resource][event_watcher]")
  8. {
  9. SECTION("Create unique_event_watcher_nothrow without event")
  10. {
  11. auto watcher = wil::make_event_watcher_nothrow([]{});
  12. REQUIRE(watcher != nullptr);
  13. }
  14. SECTION("Create unique_event_watcher_nothrow with unique_event_nothrow")
  15. {
  16. wil::unique_event_nothrow eventToPass;
  17. FAIL_FAST_IF_FAILED(eventToPass.create(wil::EventOptions::None));
  18. auto watcher = wil::make_event_watcher_nothrow(wistd::move(eventToPass), []{});
  19. REQUIRE(watcher != nullptr);
  20. REQUIRE(eventToPass.get() == nullptr); // move construction must take it
  21. }
  22. SECTION("Create unique_event_watcher_nothrow with handle")
  23. {
  24. wil::unique_event_nothrow eventToDupe;
  25. FAIL_FAST_IF_FAILED(eventToDupe.create(wil::EventOptions::None));
  26. auto watcher = wil::make_event_watcher_nothrow(eventToDupe.get(), []{});
  27. REQUIRE(watcher != nullptr);
  28. REQUIRE(eventToDupe.get() != nullptr); // handle duped in this case
  29. }
  30. #ifdef WIL_ENABLE_EXCEPTIONS
  31. SECTION("Create unique_event_watcher_nothrow with unique_event")
  32. {
  33. wil::unique_event eventToPass(wil::EventOptions::None);
  34. auto watcher = wil::make_event_watcher_nothrow(wistd::move(eventToPass), []{});
  35. REQUIRE(watcher != nullptr);
  36. REQUIRE(eventToPass.get() == nullptr); // move construction must take it
  37. }
  38. SECTION("Create unique_event_watcher without event")
  39. {
  40. auto watcher = wil::make_event_watcher([]{});
  41. }
  42. SECTION("Create unique_event_watcher with unique_event_nothrow")
  43. {
  44. wil::unique_event_nothrow eventToPass;
  45. THROW_IF_FAILED(eventToPass.create(wil::EventOptions::None));
  46. auto watcher = wil::make_event_watcher(wistd::move(eventToPass), []{});
  47. REQUIRE(eventToPass.get() == nullptr); // move construction must take it
  48. }
  49. SECTION("Create unique_event_watcher with unique_event")
  50. {
  51. wil::unique_event eventToPass(wil::EventOptions::None);
  52. auto watcher = wil::make_event_watcher(wistd::move(eventToPass), []{});
  53. REQUIRE(eventToPass.get() == nullptr); // move construction must take it
  54. }
  55. SECTION("Create unique_event_watcher with handle")
  56. {
  57. wil::unique_event eventToDupe(wil::EventOptions::None);
  58. auto watcher = wil::make_event_watcher(eventToDupe.get(), []{});
  59. REQUIRE(eventToDupe.get() != nullptr); // handle duped in this case
  60. }
  61. SECTION("Create unique_event_watcher shared watcher")
  62. {
  63. wil::shared_event_watcher sharedWatcher = wil::make_event_watcher([]{});
  64. }
  65. #endif
  66. }
  67. static auto make_event(wil::EventOptions options = wil::EventOptions::None)
  68. {
  69. wil::unique_event_nothrow result;
  70. FAIL_FAST_IF_FAILED(result.create(options));
  71. return result;
  72. }
  73. TEST_CASE("EventWatcherTests::VerifyDelivery", "[resource][event_watcher]")
  74. {
  75. auto notificationReceived = make_event();
  76. int volatile countObserved = 0;
  77. auto watcher = wil::make_event_watcher_nothrow([&]
  78. {
  79. countObserved = countObserved + 1;
  80. notificationReceived.SetEvent();
  81. });
  82. REQUIRE(watcher != nullptr);
  83. watcher.SetEvent();
  84. REQUIRE(notificationReceived.wait(5000)); // 5 second max wait
  85. watcher.SetEvent();
  86. REQUIRE(notificationReceived.wait(5000)); // 5 second max wait
  87. REQUIRE(countObserved == 2);
  88. }
  89. TEST_CASE("EventWatcherTests::VerifyLastChangeObserved", "[resource][event_watcher]")
  90. {
  91. wil::EventOptions const eventOptions[] =
  92. {
  93. wil::EventOptions::None,
  94. wil::EventOptions::ManualReset,
  95. wil::EventOptions::Signaled,
  96. wil::EventOptions::ManualReset | wil::EventOptions::Signaled,
  97. };
  98. for (auto const &eventOption : eventOptions)
  99. {
  100. auto allChangesMade = make_event(wil::EventOptions::ManualReset); // ManualReset to avoid hang in case where 2 callbacks are generated (a test failure).
  101. auto processedChange = make_event();
  102. DWORD volatile stateToObserve = 0;
  103. DWORD volatile lastObservedState = 0;
  104. int volatile countObserved = 0;
  105. auto watcher = wil::make_event_watcher_nothrow(make_event(eventOption), [&]
  106. {
  107. allChangesMade.wait();
  108. countObserved = countObserved + 1;
  109. lastObservedState = stateToObserve;
  110. processedChange.SetEvent();
  111. });
  112. REQUIRE(watcher != nullptr);
  113. stateToObserve = 1;
  114. watcher.SetEvent();
  115. stateToObserve = 2;
  116. watcher.SetEvent();
  117. allChangesMade.SetEvent();
  118. REQUIRE(processedChange.wait(5000));
  119. REQUIRE((countObserved == 1 || countObserved == 2)); // ensure the race worked how we wanted it to
  120. REQUIRE(lastObservedState == stateToObserve);
  121. }
  122. }
  123. #define ROOT_KEY_PAIR HKEY_CURRENT_USER, L"Software\\Microsoft\\RegistryWatcherTest"
  124. TEST_CASE("RegistryWatcherTests::Construction", "[registry][registry_watcher]")
  125. {
  126. SECTION("Create unique_registry_watcher_nothrow with string")
  127. {
  128. auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind){});
  129. REQUIRE(watcher);
  130. }
  131. SECTION("Create unique_registry_watcher_nothrow with unique_hkey")
  132. {
  133. wil::unique_hkey keyToMove;
  134. REQUIRE_SUCCEEDED(HRESULT_FROM_WIN32(RegCreateKeyExW(ROOT_KEY_PAIR, 0, nullptr, 0, KEY_NOTIFY, nullptr, &keyToMove, nullptr)));
  135. auto watcher = wil::make_registry_watcher_nothrow(wistd::move(keyToMove), true, [&](wil::RegistryChangeKind){});
  136. REQUIRE(watcher);
  137. REQUIRE(keyToMove.get() == nullptr); // ownership is transferred
  138. }
  139. SECTION("Create unique_registry_watcher_nothrow with handle")
  140. {
  141. // construct with just an open registry key
  142. wil::unique_hkey rootKey;
  143. REQUIRE_SUCCEEDED(HRESULT_FROM_WIN32(RegCreateKeyExW(ROOT_KEY_PAIR, 0, nullptr, 0, KEY_NOTIFY, nullptr, &rootKey, nullptr)));
  144. auto watcher = wil::make_registry_watcher_nothrow(rootKey.get(), L"", true, [&](wil::RegistryChangeKind){});
  145. REQUIRE(watcher);
  146. }
  147. #ifdef WIL_ENABLE_EXCEPTIONS
  148. SECTION("Create unique_registry_watcher with string")
  149. {
  150. REQUIRE_NOTHROW(wil::make_registry_watcher(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind){}));
  151. }
  152. SECTION("Create unique_registry_watcher with unique_hkey")
  153. {
  154. wil::unique_hkey keyToMove;
  155. THROW_IF_FAILED(HRESULT_FROM_WIN32(RegCreateKeyExW(ROOT_KEY_PAIR, 0, nullptr, 0, KEY_NOTIFY, nullptr, &keyToMove, nullptr)));
  156. REQUIRE_NOTHROW(wil::make_registry_watcher(wistd::move(keyToMove), true, [&](wil::RegistryChangeKind){}));
  157. REQUIRE(keyToMove.get() == nullptr); // ownership is transferred
  158. }
  159. #endif
  160. }
  161. void SetRegistryValue(
  162. _In_ HKEY hKey,
  163. _In_opt_ LPCWSTR lpSubKey,
  164. _In_opt_ LPCWSTR lpValueName,
  165. _In_ DWORD dwType,
  166. _In_reads_bytes_opt_(cbData) LPCVOID lpData,
  167. _In_ DWORD cbData)
  168. {
  169. wil::unique_hkey key;
  170. REQUIRE(RegOpenKeyExW(hKey, lpSubKey, 0, KEY_WRITE, &key) == ERROR_SUCCESS);
  171. REQUIRE(RegSetValueExW(key.get(), lpValueName, 0, dwType, static_cast<BYTE const*>(lpData), cbData) == ERROR_SUCCESS);
  172. }
  173. TEST_CASE("RegistryWatcherTests::VerifyDelivery", "[registry][registry_watcher]")
  174. {
  175. RegDeleteTreeW(ROOT_KEY_PAIR); // So that we get the 'Modify' event
  176. auto notificationReceived = make_event();
  177. int volatile countObserved = 0;
  178. auto volatile observedChangeType = wil::RegistryChangeKind::Delete;
  179. auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType)
  180. {
  181. countObserved = countObserved + 1;
  182. observedChangeType = changeType;
  183. notificationReceived.SetEvent();
  184. });
  185. REQUIRE(watcher);
  186. DWORD value = 1;
  187. SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
  188. REQUIRE(notificationReceived.wait(5000));
  189. REQUIRE(observedChangeType == wil::RegistryChangeKind::Modify);
  190. value++;
  191. SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
  192. REQUIRE(notificationReceived.wait(5000));
  193. REQUIRE(countObserved == 2);
  194. REQUIRE(observedChangeType == wil::RegistryChangeKind::Modify);
  195. }
  196. TEST_CASE("RegistryWatcherTests::VerifyLastChangeObserved", "[registry][registry_watcher]")
  197. {
  198. RegDeleteTreeW(ROOT_KEY_PAIR);
  199. auto allChangesMade = make_event(wil::EventOptions::ManualReset); // ManualReset for the case where both registry operations result in a callback.
  200. auto processedChange = make_event();
  201. DWORD volatile stateToObserve = 0;
  202. DWORD volatile lastObservedState = 0;
  203. DWORD volatile lastObservedValue = 0;
  204. int volatile countObserved = 0;
  205. auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&, called = false](wil::RegistryChangeKind) mutable
  206. {
  207. // This callback may be called more than once (since we modify the key twice), but we're holding references to
  208. // local variables. Therefore, bail out if this is not the first time we're called
  209. if (called)
  210. {
  211. return;
  212. }
  213. called = true;
  214. allChangesMade.wait();
  215. countObserved = countObserved + 1;
  216. lastObservedState = stateToObserve;
  217. DWORD value, cbValue = sizeof(value);
  218. RegGetValueW(ROOT_KEY_PAIR, L"value", RRF_RT_REG_DWORD, nullptr, &value, &cbValue);
  219. lastObservedValue = value;
  220. processedChange.SetEvent();
  221. });
  222. REQUIRE(watcher);
  223. DWORD value;
  224. // make 2 changes and verify that only the last gets observed
  225. stateToObserve = 1;
  226. value = 0;
  227. SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
  228. stateToObserve = 2;
  229. value = 1;
  230. SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
  231. allChangesMade.SetEvent();
  232. REQUIRE(processedChange.wait(5000));
  233. REQUIRE(countObserved >= 1); // Sometimes 2 events are observed, see if this can be eliminated.
  234. REQUIRE(lastObservedState == stateToObserve);
  235. REQUIRE(lastObservedValue == 1);
  236. }
  237. TEST_CASE("RegistryWatcherTests::VerifyDeleteBehavior", "[registry][registry_watcher]")
  238. {
  239. auto notificationReceived = make_event();
  240. int volatile countObserved = 0;
  241. auto volatile observedChangeType = wil::RegistryChangeKind::Modify;
  242. auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType)
  243. {
  244. countObserved = countObserved + 1;
  245. observedChangeType = changeType;
  246. notificationReceived.SetEvent();
  247. });
  248. REQUIRE(watcher);
  249. RegDeleteTreeW(ROOT_KEY_PAIR); // delete the key to signal the watcher with the special error case
  250. REQUIRE(notificationReceived.wait(5000));
  251. REQUIRE(countObserved == 1);
  252. REQUIRE(observedChangeType == wil::RegistryChangeKind::Delete);
  253. }
  254. TEST_CASE("RegistryWatcherTests::VerifyResetInCallback", "[registry][registry_watcher]")
  255. {
  256. auto notificationReceived = make_event();
  257. wil::unique_registry_watcher_nothrow watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, TRUE, [&](wil::RegistryChangeKind)
  258. {
  259. watcher.reset();
  260. DWORD value = 2;
  261. SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
  262. notificationReceived.SetEvent();
  263. });
  264. REQUIRE(watcher);
  265. DWORD value = 1;
  266. SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
  267. REQUIRE(notificationReceived.wait(5000));
  268. }
  269. // Stress test, disabled by default
  270. TEST_CASE("RegistryWatcherTests::VerifyResetInCallbackStress", "[LocalOnly][registry][registry_watcher][stress]")
  271. {
  272. for (DWORD value = 0; value < 10000; ++value)
  273. {
  274. wil::srwlock lock;
  275. auto notificationReceived = make_event();
  276. wil::unique_registry_watcher_nothrow watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, TRUE, [&](wil::RegistryChangeKind)
  277. {
  278. {
  279. auto al = lock.lock_exclusive();
  280. watcher.reset(); // get m_refCount to 1 to ensure the Release happens on the background thread
  281. }
  282. ++value;
  283. SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
  284. notificationReceived.SetEvent();
  285. });
  286. REQUIRE(watcher);
  287. SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
  288. notificationReceived.wait();
  289. {
  290. auto al = lock.lock_exclusive();
  291. watcher.reset();
  292. }
  293. }
  294. }
  295. TEST_CASE("RegistryWatcherTests::VerifyResetAfterDelete", "[registry][registry_watcher]")
  296. {
  297. auto notificationReceived = make_event();
  298. int volatile countObserved = 0;
  299. auto volatile observedChangeType = wil::RegistryChangeKind::Modify;
  300. wil::unique_registry_watcher_nothrow watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType)
  301. {
  302. countObserved = countObserved + 1;
  303. observedChangeType = changeType;
  304. notificationReceived.SetEvent();
  305. watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType)
  306. {
  307. countObserved = countObserved + 1;
  308. observedChangeType = changeType;
  309. notificationReceived.SetEvent();
  310. });
  311. REQUIRE(watcher);
  312. });
  313. REQUIRE(watcher);
  314. RegDeleteTreeW(ROOT_KEY_PAIR); // delete the key to signal the watcher with the special error case
  315. notificationReceived.wait();
  316. REQUIRE(countObserved == 1);
  317. REQUIRE(observedChangeType == wil::RegistryChangeKind::Delete);
  318. // wait for the reset to finish. The constructor creates the registry key
  319. notificationReceived.wait(300);
  320. DWORD value = 1;
  321. SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
  322. notificationReceived.wait();
  323. REQUIRE(countObserved == 2);
  324. REQUIRE(observedChangeType == wil::RegistryChangeKind::Modify);
  325. }
  326. TEST_CASE("RegistryWatcherTests::VerifyCallbackFinishesBeforeFreed", "[registry][registry_watcher]")
  327. {
  328. auto notificationReceived = make_event();
  329. auto deleteNotification = make_event();
  330. int volatile deleteObserved = 0;
  331. auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind)
  332. {
  333. notificationReceived.SetEvent();
  334. // ensure that the callback is still being executed while the watcher is reset().
  335. deleteNotification.wait(200);
  336. deleteObserved = deleteObserved + 1;
  337. notificationReceived.SetEvent();
  338. });
  339. RegDeleteTreeW(ROOT_KEY_PAIR); // delete the key to signal the watcher with the special error case
  340. REQUIRE(notificationReceived.wait(5000));
  341. watcher.reset();
  342. deleteNotification.SetEvent();
  343. REQUIRE(notificationReceived.wait(5000));
  344. REQUIRE(deleteObserved == 1);
  345. }
  346. TEST_CASE("FileSystemWatcherTests::Construction", "[resource][folder_watcher]")
  347. {
  348. SECTION("Create unique_folder_watcher_nothrow with valid path")
  349. {
  350. auto watcher = wil::make_folder_watcher_nothrow(L"C:\\Windows\\System32", true, wil::FolderChangeEvents::All, []{});
  351. REQUIRE(watcher);
  352. }
  353. SECTION("Create unique_folder_watcher_nothrow with invalid path")
  354. {
  355. auto watcher = wil::make_folder_watcher_nothrow(L"X:\\invalid path", true, wil::FolderChangeEvents::All, []{});
  356. REQUIRE(!watcher);
  357. }
  358. #ifdef WIL_ENABLE_EXCEPTIONS
  359. SECTION("Create unique_folder_watcher with valid path")
  360. {
  361. REQUIRE_NOTHROW(wil::make_folder_watcher(L"C:\\Windows\\System32", true, wil::FolderChangeEvents::All, []{}));
  362. }
  363. SECTION("Create unique_folder_watcher with invalid path")
  364. {
  365. REQUIRE_THROWS(wil::make_folder_watcher(L"X:\\invalid path", true, wil::FolderChangeEvents::All, []{}));
  366. }
  367. #endif
  368. }
  369. TEST_CASE("FileSystemWatcherTests::VerifyDelivery", "[resource][folder_watcher]")
  370. {
  371. witest::TestFolder folder;
  372. REQUIRE(folder);
  373. auto notificationEvent = make_event();
  374. int observedCount = 0;
  375. auto watcher = wil::make_folder_watcher_nothrow(folder.Path(), true, wil::FolderChangeEvents::All, [&]
  376. {
  377. ++observedCount;
  378. notificationEvent.SetEvent();
  379. });
  380. REQUIRE(watcher);
  381. witest::TestFile file(folder.Path(), L"file.txt");
  382. REQUIRE(file);
  383. REQUIRE(notificationEvent.wait(5000));
  384. REQUIRE(observedCount == 1);
  385. witest::TestFile file2(folder.Path(), L"file2.txt");
  386. REQUIRE(file2);
  387. REQUIRE(notificationEvent.wait(5000));
  388. REQUIRE(observedCount == 2);
  389. }
  390. TEST_CASE("FolderChangeReaderTests::Construction", "[resource][folder_change_reader]")
  391. {
  392. SECTION("Create folder_change_reader_nothrow with valid path")
  393. {
  394. auto reader = wil::make_folder_change_reader_nothrow(L"C:\\Windows\\System32", true, wil::FolderChangeEvents::All, [](auto, auto) {});
  395. REQUIRE(reader);
  396. }
  397. SECTION("Create folder_change_reader_nothrow with invalid path")
  398. {
  399. auto reader = wil::make_folder_change_reader_nothrow(L"X:\\invalid path", true, wil::FolderChangeEvents::All, [](auto, auto) {});
  400. REQUIRE(!reader);
  401. }
  402. #ifdef WIL_ENABLE_EXCEPTIONS
  403. SECTION("Create folder_change_reader with valid path")
  404. {
  405. REQUIRE_NOTHROW(wil::make_folder_change_reader(L"C:\\Windows\\System32", true, wil::FolderChangeEvents::All, [](auto, auto) {}));
  406. }
  407. SECTION("Create folder_change_reader with invalid path")
  408. {
  409. REQUIRE_THROWS(wil::make_folder_change_reader(L"X:\\invalid path", true, wil::FolderChangeEvents::All, [](auto, auto) {}));
  410. }
  411. #endif
  412. }
  413. TEST_CASE("FolderChangeReaderTests::VerifyDelivery", "[resource][folder_change_reader]")
  414. {
  415. witest::TestFolder folder;
  416. REQUIRE(folder);
  417. auto notificationEvent = make_event();
  418. wil::FolderChangeEvent observedEvent;
  419. wchar_t observedFileName[MAX_PATH] = L"";
  420. auto reader = wil::make_folder_change_reader_nothrow(folder.Path(), true, wil::FolderChangeEvents::All,
  421. [&](wil::FolderChangeEvent event, PCWSTR fileName)
  422. {
  423. observedEvent = event;
  424. StringCchCopyW(observedFileName, ARRAYSIZE(observedFileName), fileName);
  425. notificationEvent.SetEvent();
  426. });
  427. REQUIRE(reader);
  428. witest::TestFile testFile(folder.Path(), L"file.txt");
  429. REQUIRE(testFile);
  430. REQUIRE(notificationEvent.wait(5000));
  431. REQUIRE(observedEvent == wil::FolderChangeEvent::Added);
  432. REQUIRE(wcscmp(observedFileName, L"file.txt") == 0);
  433. witest::TestFile testFile2(folder.Path(), L"file2.txt");
  434. REQUIRE(testFile2);
  435. REQUIRE(notificationEvent.wait(5000));
  436. REQUIRE(observedEvent == wil::FolderChangeEvent::Added);
  437. REQUIRE(wcscmp(observedFileName, L"file2.txt") == 0);
  438. }