diff options
Diffstat (limited to 'src/libs/dutil/test/DUtilUnitTest/MonUtilTest.cpp')
-rw-r--r-- | src/libs/dutil/test/DUtilUnitTest/MonUtilTest.cpp | 487 |
1 files changed, 487 insertions, 0 deletions
diff --git a/src/libs/dutil/test/DUtilUnitTest/MonUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/MonUtilTest.cpp new file mode 100644 index 00000000..273f2eb6 --- /dev/null +++ b/src/libs/dutil/test/DUtilUnitTest/MonUtilTest.cpp | |||
@@ -0,0 +1,487 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | #include "precomp.h" | ||
4 | #undef RemoveDirectory | ||
5 | |||
6 | using namespace System; | ||
7 | using namespace System::Collections::Generic; | ||
8 | using namespace System::Runtime::InteropServices; | ||
9 | using namespace Xunit; | ||
10 | using namespace WixBuildTools::TestSupport; | ||
11 | |||
12 | namespace DutilTests | ||
13 | { | ||
14 | const int PREWAIT = 20; | ||
15 | const int POSTWAIT = 480; | ||
16 | const int FULLWAIT = 500; | ||
17 | const int SILENCEPERIOD = 100; | ||
18 | |||
19 | struct RegKey | ||
20 | { | ||
21 | HRESULT hr; | ||
22 | HKEY hkRoot; | ||
23 | LPCWSTR wzSubKey; | ||
24 | REG_KEY_BITNESS kbKeyBitness; | ||
25 | BOOL fRecursive; | ||
26 | }; | ||
27 | struct Directory | ||
28 | { | ||
29 | HRESULT hr; | ||
30 | LPCWSTR wzPath; | ||
31 | BOOL fRecursive; | ||
32 | }; | ||
33 | struct Results | ||
34 | { | ||
35 | RegKey *rgRegKeys; | ||
36 | DWORD cRegKeys; | ||
37 | Directory *rgDirectories; | ||
38 | DWORD cDirectories; | ||
39 | }; | ||
40 | |||
41 | public delegate void MonGeneralDelegate(HRESULT, LPVOID); | ||
42 | |||
43 | public delegate void MonDriveStatusDelegate(WCHAR, BOOL, LPVOID); | ||
44 | |||
45 | public delegate void MonDirectoryDelegate(HRESULT, LPCWSTR, BOOL, LPVOID, LPVOID); | ||
46 | |||
47 | public delegate void MonRegKeyDelegate(HRESULT, HKEY, LPCWSTR, REG_KEY_BITNESS, BOOL, LPVOID, LPVOID); | ||
48 | |||
49 | static void MonGeneral( | ||
50 | __in HRESULT /*hrResult*/, | ||
51 | __in_opt LPVOID /*pvContext*/ | ||
52 | ) | ||
53 | { | ||
54 | Assert::True(false); | ||
55 | } | ||
56 | |||
57 | static void MonDriveStatus( | ||
58 | __in WCHAR /*chDrive*/, | ||
59 | __in BOOL /*fArriving*/, | ||
60 | __in_opt LPVOID /*pvContext*/ | ||
61 | ) | ||
62 | { | ||
63 | } | ||
64 | |||
65 | static void MonDirectory( | ||
66 | __in HRESULT hrResult, | ||
67 | __in_z LPCWSTR wzPath, | ||
68 | __in_z BOOL fRecursive, | ||
69 | __in_opt LPVOID pvContext, | ||
70 | __in_opt LPVOID pvDirectoryContext | ||
71 | ) | ||
72 | { | ||
73 | Assert::Equal(S_OK, hrResult); | ||
74 | Assert::Equal<DWORD_PTR>(0, reinterpret_cast<DWORD_PTR>(pvDirectoryContext)); | ||
75 | |||
76 | HRESULT hr = S_OK; | ||
77 | Results *pResults = reinterpret_cast<Results *>(pvContext); | ||
78 | |||
79 | hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pResults->rgDirectories), pResults->cDirectories + 1, sizeof(Directory), 5); | ||
80 | NativeAssert::ValidReturnCode(hr, S_OK); | ||
81 | ++pResults->cDirectories; | ||
82 | |||
83 | pResults->rgDirectories[pResults->cDirectories - 1].hr = hrResult; | ||
84 | pResults->rgDirectories[pResults->cDirectories - 1].wzPath = wzPath; | ||
85 | pResults->rgDirectories[pResults->cDirectories - 1].fRecursive = fRecursive; | ||
86 | } | ||
87 | |||
88 | static void MonRegKey( | ||
89 | __in HRESULT hrResult, | ||
90 | __in HKEY hkRoot, | ||
91 | __in_z LPCWSTR wzSubKey, | ||
92 | __in REG_KEY_BITNESS kbKeyBitness, | ||
93 | __in_z BOOL fRecursive, | ||
94 | __in_opt LPVOID pvContext, | ||
95 | __in_opt LPVOID pvRegKeyContext | ||
96 | ) | ||
97 | { | ||
98 | Assert::Equal<HRESULT>(S_OK, hrResult); | ||
99 | Assert::Equal<DWORD_PTR>(0, reinterpret_cast<DWORD_PTR>(pvRegKeyContext)); | ||
100 | |||
101 | HRESULT hr = S_OK; | ||
102 | Results *pResults = reinterpret_cast<Results *>(pvContext); | ||
103 | |||
104 | hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pResults->rgRegKeys), pResults->cRegKeys + 1, sizeof(RegKey), 5); | ||
105 | NativeAssert::ValidReturnCode(hr, S_OK); | ||
106 | ++pResults->cRegKeys; | ||
107 | |||
108 | pResults->rgRegKeys[pResults->cRegKeys - 1].hr = hrResult; | ||
109 | pResults->rgRegKeys[pResults->cRegKeys - 1].hkRoot = hkRoot; | ||
110 | pResults->rgRegKeys[pResults->cRegKeys - 1].wzSubKey = wzSubKey; | ||
111 | pResults->rgRegKeys[pResults->cRegKeys - 1].kbKeyBitness = kbKeyBitness; | ||
112 | pResults->rgRegKeys[pResults->cRegKeys - 1].fRecursive = fRecursive; | ||
113 | } | ||
114 | |||
115 | public ref class MonUtil | ||
116 | { | ||
117 | public: | ||
118 | void ClearResults(Results *pResults) | ||
119 | { | ||
120 | ReleaseNullMem(pResults->rgDirectories); | ||
121 | pResults->cDirectories = 0; | ||
122 | ReleaseNullMem(pResults->rgRegKeys); | ||
123 | pResults->cRegKeys = 0; | ||
124 | } | ||
125 | |||
126 | void RemoveDirectory(LPCWSTR wzPath) | ||
127 | { | ||
128 | DWORD dwRetryCount = 0; | ||
129 | const DWORD c_dwMaxRetryCount = 100; | ||
130 | const DWORD c_dwRetryInterval = 50; | ||
131 | |||
132 | HRESULT hr = DirEnsureDelete(wzPath, TRUE, TRUE); | ||
133 | |||
134 | // Monitoring a directory opens a handle to that directory, which means delete requests for that directory will succeed | ||
135 | // (and deletion will be "pending" until our monitor handle is closed) | ||
136 | // but deletion of the directory containing that directory cannot complete until the handle is closed. This means DirEnsureDelete() | ||
137 | // can sometimes encounter HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY) failures, which just means it needs to retry a bit later | ||
138 | // (after the waiter thread wakes up, it will release the handle) | ||
139 | while (HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY) == hr && c_dwMaxRetryCount > dwRetryCount) | ||
140 | { | ||
141 | ::Sleep(c_dwRetryInterval); | ||
142 | ++dwRetryCount; | ||
143 | hr = DirEnsureDelete(wzPath, TRUE, TRUE); | ||
144 | } | ||
145 | |||
146 | NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE, E_PATHNOTFOUND); | ||
147 | } | ||
148 | |||
149 | void TestDirectory(MON_HANDLE handle, Results *pResults) | ||
150 | { | ||
151 | HRESULT hr = S_OK; | ||
152 | LPWSTR sczShallowPath = NULL; | ||
153 | LPWSTR sczParentPath = NULL; | ||
154 | LPWSTR sczDeepPath = NULL; | ||
155 | LPWSTR sczChildPath = NULL; | ||
156 | LPWSTR sczChildFilePath = NULL; | ||
157 | |||
158 | try | ||
159 | { | ||
160 | hr = PathExpand(&sczShallowPath, L"%TEMP%\\MonUtilTest\\", PATH_EXPAND_ENVIRONMENT); | ||
161 | NativeAssert::ValidReturnCode(hr, S_OK); | ||
162 | |||
163 | hr = PathExpand(&sczParentPath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\", PATH_EXPAND_ENVIRONMENT); | ||
164 | NativeAssert::ValidReturnCode(hr, S_OK); | ||
165 | |||
166 | hr = PathExpand(&sczDeepPath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\", PATH_EXPAND_ENVIRONMENT); | ||
167 | NativeAssert::ValidReturnCode(hr, S_OK); | ||
168 | |||
169 | hr = PathExpand(&sczChildPath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\some\\sub\\folder\\", PATH_EXPAND_ENVIRONMENT); | ||
170 | NativeAssert::ValidReturnCode(hr, S_OK); | ||
171 | |||
172 | hr = PathExpand(&sczChildFilePath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\some\\sub\\folder\\file.txt", PATH_EXPAND_ENVIRONMENT); | ||
173 | NativeAssert::ValidReturnCode(hr, S_OK); | ||
174 | |||
175 | RemoveDirectory(sczShallowPath); | ||
176 | |||
177 | hr = MonAddDirectory(handle, sczDeepPath, TRUE, SILENCEPERIOD, NULL); | ||
178 | NativeAssert::ValidReturnCode(hr, S_OK); | ||
179 | |||
180 | hr = DirEnsureExists(sczParentPath, NULL); | ||
181 | NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); | ||
182 | // Make sure creating the parent directory does nothing, even after silence period | ||
183 | ::Sleep(FULLWAIT); | ||
184 | Assert::Equal<DWORD>(0, pResults->cDirectories); | ||
185 | |||
186 | // Now create the target path, no notification until after the silence period | ||
187 | hr = DirEnsureExists(sczDeepPath, NULL); | ||
188 | NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); | ||
189 | ::Sleep(PREWAIT); | ||
190 | Assert::Equal<DWORD>(0, pResults->cDirectories); | ||
191 | |||
192 | // Now after the full silence period, it should have triggered | ||
193 | ::Sleep(POSTWAIT); | ||
194 | Assert::Equal<DWORD>(1, pResults->cDirectories); | ||
195 | NativeAssert::ValidReturnCode(pResults->rgDirectories[0].hr, S_OK); | ||
196 | |||
197 | // Now delete the directory, along with a ton of parents. This verifies MonUtil will keep watching the closest parent that still exists. | ||
198 | RemoveDirectory(sczShallowPath); | ||
199 | |||
200 | ::Sleep(FULLWAIT); | ||
201 | Assert::Equal<DWORD>(2, pResults->cDirectories); | ||
202 | NativeAssert::ValidReturnCode(pResults->rgDirectories[1].hr, S_OK); | ||
203 | |||
204 | // Create the parent directory again, still should be nothing even after full silence period | ||
205 | hr = DirEnsureExists(sczParentPath, NULL); | ||
206 | NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); | ||
207 | ::Sleep(FULLWAIT); | ||
208 | Assert::Equal<DWORD>(2, pResults->cDirectories); | ||
209 | |||
210 | hr = DirEnsureExists(sczChildPath, NULL); | ||
211 | NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); | ||
212 | ::Sleep(PREWAIT); | ||
213 | Assert::Equal<DWORD>(2, pResults->cDirectories); | ||
214 | |||
215 | ::Sleep(POSTWAIT); | ||
216 | Assert::Equal<DWORD>(3, pResults->cDirectories); | ||
217 | NativeAssert::ValidReturnCode(pResults->rgDirectories[2].hr, S_OK); | ||
218 | |||
219 | // Write a file to a deep child subfolder, and make sure it's detected | ||
220 | hr = FileFromString(sczChildFilePath, 0, L"contents", FILE_ENCODING_UTF16_WITH_BOM); | ||
221 | NativeAssert::ValidReturnCode(hr, S_OK); | ||
222 | ::Sleep(PREWAIT); | ||
223 | Assert::Equal<DWORD>(3, pResults->cDirectories); | ||
224 | |||
225 | ::Sleep(POSTWAIT); | ||
226 | Assert::Equal<DWORD>(4, pResults->cDirectories); | ||
227 | NativeAssert::ValidReturnCode(pResults->rgDirectories[2].hr, S_OK); | ||
228 | |||
229 | RemoveDirectory(sczParentPath); | ||
230 | |||
231 | ::Sleep(FULLWAIT); | ||
232 | Assert::Equal<DWORD>(5, pResults->cDirectories); | ||
233 | NativeAssert::ValidReturnCode(pResults->rgDirectories[3].hr, S_OK); | ||
234 | |||
235 | // Now remove the directory from the list of things to monitor, and confirm changes are no longer tracked | ||
236 | hr = MonRemoveDirectory(handle, sczDeepPath, TRUE); | ||
237 | NativeAssert::ValidReturnCode(hr, S_OK); | ||
238 | ::Sleep(PREWAIT); | ||
239 | |||
240 | hr = DirEnsureExists(sczDeepPath, NULL); | ||
241 | NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); | ||
242 | ::Sleep(FULLWAIT); | ||
243 | Assert::Equal<DWORD>(5, pResults->cDirectories); | ||
244 | NativeAssert::ValidReturnCode(pResults->rgDirectories[3].hr, S_OK); | ||
245 | |||
246 | // Finally, add it back so we can test multiple things to monitor at once | ||
247 | hr = MonAddDirectory(handle, sczDeepPath, TRUE, SILENCEPERIOD, NULL); | ||
248 | NativeAssert::ValidReturnCode(hr, S_OK); | ||
249 | } | ||
250 | finally | ||
251 | { | ||
252 | ReleaseStr(sczShallowPath); | ||
253 | ReleaseStr(sczDeepPath); | ||
254 | ReleaseStr(sczParentPath); | ||
255 | } | ||
256 | } | ||
257 | |||
258 | void TestRegKey(MON_HANDLE handle, Results *pResults) | ||
259 | { | ||
260 | HRESULT hr = S_OK; | ||
261 | LPCWSTR wzShallowRegKey = L"Software\\MonUtilTest\\"; | ||
262 | LPCWSTR wzParentRegKey = L"Software\\MonUtilTest\\sub\\folder\\that\\might\\not\\"; | ||
263 | LPCWSTR wzDeepRegKey = L"Software\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\"; | ||
264 | LPCWSTR wzChildRegKey = L"Software\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\some\\sub\\folder\\"; | ||
265 | HKEY hk = NULL; | ||
266 | |||
267 | try | ||
268 | { | ||
269 | hr = RegDelete(HKEY_CURRENT_USER, wzShallowRegKey, REG_KEY_32BIT, TRUE); | ||
270 | NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE, E_PATHNOTFOUND); | ||
271 | |||
272 | hr = MonAddRegKey(handle, HKEY_CURRENT_USER, wzDeepRegKey, REG_KEY_DEFAULT, TRUE, SILENCEPERIOD, NULL); | ||
273 | NativeAssert::ValidReturnCode(hr, S_OK); | ||
274 | |||
275 | hr = RegCreate(HKEY_CURRENT_USER, wzParentRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk); | ||
276 | ReleaseRegKey(hk); | ||
277 | // Make sure creating the parent key does nothing, even after silence period | ||
278 | ::Sleep(FULLWAIT); | ||
279 | NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); | ||
280 | Assert::Equal<DWORD>(0, pResults->cRegKeys); | ||
281 | |||
282 | // Now create the target path, no notification until after the silence period | ||
283 | hr = RegCreate(HKEY_CURRENT_USER, wzDeepRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk); | ||
284 | NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); | ||
285 | ReleaseRegKey(hk); | ||
286 | ::Sleep(PREWAIT); | ||
287 | Assert::Equal<DWORD>(0, pResults->cRegKeys); | ||
288 | |||
289 | // Now after the full silence period, it should have triggered | ||
290 | ::Sleep(POSTWAIT); | ||
291 | Assert::Equal<DWORD>(1, pResults->cRegKeys); | ||
292 | NativeAssert::ValidReturnCode(pResults->rgRegKeys[0].hr, S_OK); | ||
293 | |||
294 | // Now delete the directory, along with a ton of parents. This verifies MonUtil will keep watching the closest parent that still exists. | ||
295 | hr = RegDelete(HKEY_CURRENT_USER, wzShallowRegKey, REG_KEY_32BIT, TRUE); | ||
296 | NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE, E_PATHNOTFOUND); | ||
297 | ::Sleep(PREWAIT); | ||
298 | Assert::Equal<DWORD>(1, pResults->cRegKeys); | ||
299 | |||
300 | ::Sleep(FULLWAIT); | ||
301 | Assert::Equal<DWORD>(2, pResults->cRegKeys); | ||
302 | NativeAssert::ValidReturnCode(pResults->rgRegKeys[1].hr, S_OK); | ||
303 | |||
304 | // Create the parent directory again, still should be nothing even after full silence period | ||
305 | hr = RegCreate(HKEY_CURRENT_USER, wzParentRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk); | ||
306 | NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); | ||
307 | ReleaseRegKey(hk); | ||
308 | ::Sleep(FULLWAIT); | ||
309 | Assert::Equal<DWORD>(2, pResults->cRegKeys); | ||
310 | |||
311 | hr = RegCreate(HKEY_CURRENT_USER, wzChildRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk); | ||
312 | NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); | ||
313 | ::Sleep(PREWAIT); | ||
314 | Assert::Equal<DWORD>(2, pResults->cRegKeys); | ||
315 | |||
316 | ::Sleep(FULLWAIT); | ||
317 | Assert::Equal<DWORD>(3, pResults->cRegKeys); | ||
318 | NativeAssert::ValidReturnCode(pResults->rgRegKeys[2].hr, S_OK); | ||
319 | |||
320 | // Write a registry value to some deep child subkey, and make sure it's detected | ||
321 | hr = RegWriteString(hk, L"valuename", L"testvalue"); | ||
322 | NativeAssert::ValidReturnCode(hr, S_OK); | ||
323 | ReleaseRegKey(hk); | ||
324 | ::Sleep(PREWAIT); | ||
325 | Assert::Equal<DWORD>(3, pResults->cRegKeys); | ||
326 | |||
327 | ::Sleep(FULLWAIT); | ||
328 | Assert::Equal<DWORD>(4, pResults->cRegKeys); | ||
329 | NativeAssert::ValidReturnCode(pResults->rgRegKeys[2].hr, S_OK); | ||
330 | |||
331 | hr = RegDelete(HKEY_CURRENT_USER, wzDeepRegKey, REG_KEY_32BIT, TRUE); | ||
332 | NativeAssert::ValidReturnCode(hr, S_OK); | ||
333 | |||
334 | ::Sleep(FULLWAIT); | ||
335 | Assert::Equal<DWORD>(5, pResults->cRegKeys); | ||
336 | |||
337 | // Now remove the regkey from the list of things to monitor, and confirm changes are no longer tracked | ||
338 | hr = MonRemoveRegKey(handle, HKEY_CURRENT_USER, wzDeepRegKey, REG_KEY_DEFAULT, TRUE); | ||
339 | NativeAssert::ValidReturnCode(hr, S_OK); | ||
340 | |||
341 | hr = RegCreate(HKEY_CURRENT_USER, wzDeepRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk); | ||
342 | NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); | ||
343 | ReleaseRegKey(hk); | ||
344 | ::Sleep(FULLWAIT); | ||
345 | Assert::Equal<DWORD>(5, pResults->cRegKeys); | ||
346 | } | ||
347 | finally | ||
348 | { | ||
349 | ReleaseRegKey(hk); | ||
350 | } | ||
351 | } | ||
352 | |||
353 | void TestMoreThan64(MON_HANDLE handle, Results *pResults) | ||
354 | { | ||
355 | HRESULT hr = S_OK; | ||
356 | LPWSTR sczBaseDir = NULL; | ||
357 | LPWSTR sczDir = NULL; | ||
358 | LPWSTR sczFile = NULL; | ||
359 | |||
360 | try | ||
361 | { | ||
362 | hr = PathExpand(&sczBaseDir, L"%TEMP%\\ScalabilityTest\\", PATH_EXPAND_ENVIRONMENT); | ||
363 | NativeAssert::ValidReturnCode(hr, S_OK); | ||
364 | |||
365 | for (DWORD i = 0; i < 200; ++i) | ||
366 | { | ||
367 | hr = StrAllocFormatted(&sczDir, L"%ls%u\\", sczBaseDir, i); | ||
368 | NativeAssert::ValidReturnCode(hr, S_OK); | ||
369 | |||
370 | hr = DirEnsureExists(sczDir, NULL); | ||
371 | NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE); | ||
372 | |||
373 | hr = MonAddDirectory(handle, sczDir, FALSE, SILENCEPERIOD, NULL); | ||
374 | NativeAssert::ValidReturnCode(hr, S_OK); | ||
375 | } | ||
376 | |||
377 | hr = PathConcat(sczDir, L"file.txt", &sczFile); | ||
378 | NativeAssert::ValidReturnCode(hr, S_OK); | ||
379 | |||
380 | hr = FileFromString(sczFile, 0, L"contents", FILE_ENCODING_UTF16_WITH_BOM); | ||
381 | NativeAssert::ValidReturnCode(hr, S_OK); | ||
382 | |||
383 | ::Sleep(FULLWAIT); | ||
384 | Assert::Equal<DWORD>(1, pResults->cDirectories); | ||
385 | |||
386 | for (DWORD i = 0; i < 199; ++i) | ||
387 | { | ||
388 | hr = StrAllocFormatted(&sczDir, L"%ls%u\\", sczBaseDir, i); | ||
389 | NativeAssert::ValidReturnCode(hr, S_OK); | ||
390 | |||
391 | hr = MonRemoveDirectory(handle, sczDir, FALSE); | ||
392 | NativeAssert::ValidReturnCode(hr, S_OK); | ||
393 | } | ||
394 | ::Sleep(FULLWAIT); | ||
395 | |||
396 | hr = FileFromString(sczFile, 0, L"contents2", FILE_ENCODING_UTF16_WITH_BOM); | ||
397 | NativeAssert::ValidReturnCode(hr, S_OK); | ||
398 | |||
399 | ::Sleep(FULLWAIT); | ||
400 | Assert::Equal<DWORD>(2, pResults->cDirectories); | ||
401 | |||
402 | for (DWORD i = 0; i < 199; ++i) | ||
403 | { | ||
404 | hr = StrAllocFormatted(&sczDir, L"%ls%u\\", sczBaseDir, i); | ||
405 | NativeAssert::ValidReturnCode(hr, S_OK); | ||
406 | |||
407 | hr = MonAddDirectory(handle, sczDir, FALSE, SILENCEPERIOD, NULL); | ||
408 | NativeAssert::ValidReturnCode(hr, S_OK); | ||
409 | } | ||
410 | ::Sleep(FULLWAIT); | ||
411 | |||
412 | hr = FileFromString(sczFile, 0, L"contents3", FILE_ENCODING_UTF16_WITH_BOM); | ||
413 | NativeAssert::ValidReturnCode(hr, S_OK); | ||
414 | |||
415 | ::Sleep(FULLWAIT); | ||
416 | Assert::Equal<DWORD>(3, pResults->cDirectories); | ||
417 | } | ||
418 | finally | ||
419 | { | ||
420 | ReleaseStr(sczBaseDir); | ||
421 | ReleaseStr(sczDir); | ||
422 | ReleaseStr(sczFile); | ||
423 | } | ||
424 | } | ||
425 | |||
426 | [Fact(Skip = "Test demonstrates failure")] | ||
427 | void MonUtilTest() | ||
428 | { | ||
429 | HRESULT hr = S_OK; | ||
430 | MON_HANDLE handle = NULL; | ||
431 | List<GCHandle>^ gcHandles = gcnew List<GCHandle>(); | ||
432 | Results *pResults = (Results *)MemAlloc(sizeof(Results), TRUE); | ||
433 | Assert::True(NULL != pResults); | ||
434 | |||
435 | try | ||
436 | { | ||
437 | // These ensure the function pointers we send point to this thread's appdomain, which helps with assembly binding when running tests within msbuild | ||
438 | MonGeneralDelegate^ fpMonGeneral = gcnew MonGeneralDelegate(MonGeneral); | ||
439 | GCHandle gchMonGeneral = GCHandle::Alloc(fpMonGeneral); | ||
440 | gcHandles->Add(gchMonGeneral); | ||
441 | IntPtr ipMonGeneral = Marshal::GetFunctionPointerForDelegate(fpMonGeneral); | ||
442 | |||
443 | MonDriveStatusDelegate^ fpMonDriveStatus = gcnew MonDriveStatusDelegate(MonDriveStatus); | ||
444 | GCHandle gchMonDriveStatus = GCHandle::Alloc(fpMonDriveStatus); | ||
445 | gcHandles->Add(gchMonDriveStatus); | ||
446 | IntPtr ipMonDriveStatus = Marshal::GetFunctionPointerForDelegate(fpMonDriveStatus); | ||
447 | |||
448 | MonDirectoryDelegate^ fpMonDirectory = gcnew MonDirectoryDelegate(MonDirectory); | ||
449 | GCHandle gchMonDirectory = GCHandle::Alloc(fpMonDirectory); | ||
450 | gcHandles->Add(gchMonDirectory); | ||
451 | IntPtr ipMonDirectory = Marshal::GetFunctionPointerForDelegate(fpMonDirectory); | ||
452 | |||
453 | MonRegKeyDelegate^ fpMonRegKey = gcnew MonRegKeyDelegate(MonRegKey); | ||
454 | GCHandle gchMonRegKey = GCHandle::Alloc(fpMonRegKey); | ||
455 | gcHandles->Add(gchMonRegKey); | ||
456 | IntPtr ipMonRegKey = Marshal::GetFunctionPointerForDelegate(fpMonRegKey); | ||
457 | |||
458 | // "Silence period" is 100 ms | ||
459 | hr = MonCreate(&handle, static_cast<PFN_MONGENERAL>(ipMonGeneral.ToPointer()), static_cast<PFN_MONDRIVESTATUS>(ipMonDriveStatus.ToPointer()), static_cast<PFN_MONDIRECTORY>(ipMonDirectory.ToPointer()), static_cast<PFN_MONREGKEY>(ipMonRegKey.ToPointer()), pResults); | ||
460 | NativeAssert::ValidReturnCode(hr, S_OK); | ||
461 | |||
462 | hr = RegInitialize(); | ||
463 | NativeAssert::ValidReturnCode(hr, S_OK); | ||
464 | |||
465 | TestDirectory(handle, pResults); | ||
466 | ClearResults(pResults); | ||
467 | TestRegKey(handle, pResults); | ||
468 | ClearResults(pResults); | ||
469 | TestMoreThan64(handle, pResults); | ||
470 | ClearResults(pResults); | ||
471 | } | ||
472 | finally | ||
473 | { | ||
474 | ReleaseMon(handle); | ||
475 | |||
476 | for each (GCHandle gcHandle in gcHandles) | ||
477 | { | ||
478 | gcHandle.Free(); | ||
479 | } | ||
480 | |||
481 | ReleaseMem(pResults->rgDirectories); | ||
482 | ReleaseMem(pResults->rgRegKeys); | ||
483 | ReleaseMem(pResults); | ||
484 | } | ||
485 | } | ||
486 | }; | ||
487 | } | ||