1 /*
2  * PROJECT:     ReactOS api tests
3  * LICENSE:     LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later)
4  * PURPOSE:     Test for SHChangeNotify
5  * COPYRIGHT:   Copyright 2020-2021 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
6  */
7 
8 // NOTE: This test program closes the Explorer windows before tests.
9 
10 #include "shelltest.h"
11 #include "SHChangeNotify.h"
12 #include <time.h>
13 #include <process.h>
14 #include <versionhelpers.h>
15 
16 // --- The selection of tests ---
17 #define DISABLE_THIS_TESTCASE
18 #define NO_TRIVIAL
19 //#define NO_INTERRUPT_LEVEL
20 //#define NO_SHELL_LEVEL
21 #define NEW_DELIVERY_ONLY
22 //#define RANDOM_HALF
23 #define RANDOM_QUARTER
24 
25 // --- Show the elapsed time by GetTickCount() ---
26 //#define ENTRY_TICK
27 #define GROUP_TICK
28 #define TOTAL_TICK
29 
30 static HWND s_hwnd = NULL;
31 static WCHAR s_szSubProgram[MAX_PATH];
32 static HANDLE s_hThread = NULL;
33 static HANDLE s_hEvent = NULL;
34 
35 static HWND DoWaitForWindow(LPCWSTR clsname, LPCWSTR text, BOOL bClosing, BOOL bForce)
36 {
37     HWND hwnd = NULL;
38     for (INT i = 0; i < 50; ++i)
39     {
40         hwnd = FindWindowW(clsname, text);
41         if (bClosing)
42         {
43             if (!hwnd)
44                 break;
45 
46             if (bForce)
47                 PostMessage(hwnd, WM_CLOSE, 0, 0);
48         }
49         else
50         {
51             if (hwnd)
52                 break;
53         }
54 
55         Sleep(1);
56     }
57     return hwnd;
58 }
59 
60 static BOOL DoCreateEmptyFile(LPCWSTR pszFileName)
61 {
62     FILE *fp = _wfopen(pszFileName, L"wb");
63     if (fp)
64         fclose(fp);
65     return fp != NULL;
66 }
67 
68 struct TEST_ENTRY;
69 
70 typedef BOOL (*ACTION)(const struct TEST_ENTRY *entry);
71 
72 typedef struct TEST_ENTRY
73 {
74     INT line;
75     DIRTYPE iWriteDir;
76     LPCSTR pattern;
77     LPCWSTR path1;
78     LPCWSTR path2;
79     ACTION action;
80 } TEST_ENTRY;
81 
82 #define TEST_FILE      L"_TEST_.txt"
83 #define TEST_FILE_KAI  L"_TEST_KAI_.txt"
84 #define TEST_DIR       L"_TESTDIR_"
85 #define TEST_DIR_KAI   L"_TESTDIR_KAI_"
86 #define MOVE_FILE(from, to) MoveFileW((from), (to))
87 
88 static BOOL DoAction1(const TEST_ENTRY *entry)
89 {
90     LPWSTR pszPath = DoGetDir(entry->iWriteDir);
91     PathAppendW(pszPath, TEST_FILE);
92     ok(DoCreateEmptyFile(pszPath), "Line %d: DoCreateEmptyFile failed\n", entry->line);
93     return TRUE;
94 }
95 
96 static BOOL DoAction2(const TEST_ENTRY *entry)
97 {
98     LPWSTR pszPath1 = DoGetDir(entry->iWriteDir), pszPath2 = DoGetDir(entry->iWriteDir);
99     PathAppendW(pszPath1, TEST_FILE);
100     PathAppendW(pszPath2, TEST_FILE_KAI);
101     ok(MOVE_FILE(pszPath1, pszPath2), "Line %d: MOVE_FILE(%ls, %ls) failed (%ld)\n",
102        entry->line, pszPath1, pszPath2, GetLastError());
103     return TRUE;
104 }
105 
106 static BOOL DoAction3(const TEST_ENTRY *entry)
107 {
108     LPWSTR pszPath1 = DoGetDir(entry->iWriteDir), pszPath2 = DoGetDir(entry->iWriteDir);
109     PathAppendW(pszPath1, TEST_FILE_KAI);
110     PathAppendW(pszPath2, TEST_FILE);
111     ok(MOVE_FILE(pszPath1, pszPath2), "Line %d: MOVE_FILE(%ls, %ls) failed (%ld)\n",
112        entry->line, pszPath1, pszPath2, GetLastError());
113     return TRUE;
114 }
115 
116 static BOOL DoAction4(const TEST_ENTRY *entry)
117 {
118     LPWSTR pszPath = DoGetDir(entry->iWriteDir);
119     PathAppendW(pszPath, TEST_FILE);
120     ok(DeleteFileW(pszPath), "Line %d: DeleteFileW(%ls) failed (%ld)\n",
121        entry->line, pszPath, GetLastError());
122     return TRUE;
123 }
124 
125 static BOOL DoAction5(const TEST_ENTRY *entry)
126 {
127     LPWSTR pszPath = DoGetDir(entry->iWriteDir);
128     PathAppendW(pszPath, TEST_DIR);
129     ok(CreateDirectoryW(pszPath, NULL), "Line %d: CreateDirectoryW(%ls) failed (%ld)\n",
130        entry->line, pszPath, GetLastError());
131     return TRUE;
132 }
133 
134 static BOOL DoAction6(const TEST_ENTRY *entry)
135 {
136     LPWSTR pszPath1 = DoGetDir(entry->iWriteDir), pszPath2 = DoGetDir(entry->iWriteDir);
137     PathAppendW(pszPath1, TEST_DIR);
138     PathAppendW(pszPath2, TEST_DIR_KAI);
139     ok(MOVE_FILE(pszPath1, pszPath2), "Line %d: MOVE_FILE(%ls, %ls) failed (%ld)\n",
140        entry->line, pszPath1, pszPath2, GetLastError());
141     return TRUE;
142 }
143 
144 static BOOL DoAction7(const TEST_ENTRY *entry)
145 {
146     LPWSTR pszPath1 = DoGetDir(entry->iWriteDir), pszPath2 = DoGetDir(entry->iWriteDir);
147     PathAppendW(pszPath1, TEST_DIR_KAI);
148     PathAppendW(pszPath2, TEST_DIR);
149     ok(MOVE_FILE(pszPath1, pszPath2), "Line %d: MOVE_FILE(%ls, %ls) failed (%ld)\n",
150        entry->line, pszPath1, pszPath2, GetLastError());
151     return TRUE;
152 }
153 
154 static BOOL DoAction8(const TEST_ENTRY *entry)
155 {
156     LPWSTR pszPath = DoGetDir(entry->iWriteDir);
157     PathAppendW(pszPath, TEST_DIR);
158     ok(RemoveDirectoryW(pszPath), "Line %d: RemoveDirectoryW(%ls) failed (%ld)\n",
159        entry->line, pszPath, GetLastError());
160     return TRUE;
161 }
162 
163 static BOOL DoAction9(const TEST_ENTRY *entry)
164 {
165     LPWSTR pszPath = DoGetDir(entry->iWriteDir);
166     PathAppendW(pszPath, TEST_FILE);
167     SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW | SHCNF_FLUSH, pszPath, NULL);
168     return FALSE;
169 }
170 
171 static BOOL DoAction10(const TEST_ENTRY *entry)
172 {
173     LPWSTR pszPath = DoGetDir(entry->iWriteDir);
174     PathAppendW(pszPath, TEST_FILE);
175     SHChangeNotify(SHCNE_DELETE, SHCNF_PATHW | SHCNF_FLUSH, pszPath, NULL);
176     return FALSE;
177 }
178 
179 static BOOL DoAction11(const TEST_ENTRY *entry)
180 {
181     LPWSTR pszPath = DoGetDir(entry->iWriteDir);
182     PathAppendW(pszPath, TEST_DIR);
183     SHChangeNotify(SHCNE_MKDIR, SHCNF_PATHW | SHCNF_FLUSH, pszPath, NULL);
184     return FALSE;
185 }
186 
187 static BOOL DoAction12(const TEST_ENTRY *entry)
188 {
189     LPWSTR pszPath = DoGetDir(entry->iWriteDir);
190     PathAppendW(pszPath, TEST_DIR);
191     SHChangeNotify(SHCNE_RMDIR, SHCNF_PATHW | SHCNF_FLUSH, pszPath, NULL);
192     return FALSE;
193 }
194 
195 #define WRITEDIR_0 DIRTYPE_DESKTOP
196 static WCHAR s_szDesktop[MAX_PATH];
197 static WCHAR s_szTestFile0[MAX_PATH];
198 static WCHAR s_szTestFile0Kai[MAX_PATH];
199 static WCHAR s_szTestDir0[MAX_PATH];
200 static WCHAR s_szTestDir0Kai[MAX_PATH];
201 
202 #define WRITEDIR_1 DIRTYPE_MYDOCUMENTS
203 static WCHAR s_szDocuments[MAX_PATH];
204 static WCHAR s_szTestFile1[MAX_PATH];
205 static WCHAR s_szTestFile1Kai[MAX_PATH];
206 static WCHAR s_szTestDir1[MAX_PATH];
207 static WCHAR s_szTestDir1Kai[MAX_PATH];
208 
209 static void DoDeleteFilesAndDirs(void)
210 {
211     DeleteFileW(TEMP_FILE);
212     DeleteFileW(s_szTestFile0);
213     DeleteFileW(s_szTestFile0Kai);
214     DeleteFileW(s_szTestFile1);
215     DeleteFileW(s_szTestFile1Kai);
216     RemoveDirectoryW(s_szTestDir0);
217     RemoveDirectoryW(s_szTestDir0Kai);
218     RemoveDirectoryW(s_szTestDir1);
219     RemoveDirectoryW(s_szTestDir1Kai);
220 }
221 
222 static const TEST_ENTRY s_group_00[] =
223 {
224     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction1 },
225     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction2 },
226     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction3 },
227     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction4 },
228     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction5 },
229     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction6 },
230     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction7 },
231     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction8 },
232     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction9 },
233     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction10 },
234     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction11 },
235     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction12 },
236 
237     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction1 },
238     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction2 },
239     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction3 },
240     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction4 },
241     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction5 },
242     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction6 },
243     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction7 },
244     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction8 },
245     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction9 },
246     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction10 },
247     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction11 },
248     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction12 },
249 };
250 
251 static const TEST_ENTRY s_group_01[] =
252 {
253     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction1 },
254     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction2 },
255     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction3 },
256     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction4 },
257     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction5 },
258     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction6 },
259     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction7 },
260     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction8 },
261     { __LINE__, WRITEDIR_0, "0100000", s_szTestFile0, L"", DoAction9 },
262     { __LINE__, WRITEDIR_0, "0010000", s_szTestFile0, L"", DoAction10 },
263     { __LINE__, WRITEDIR_0, "0001000", s_szTestDir0, L"", DoAction11 },
264     { __LINE__, WRITEDIR_0, "0000100", s_szTestDir0, L"", DoAction12 },
265 
266     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction1 },
267     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction2 },
268     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction3 },
269     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction4 },
270     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction5 },
271     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction6 },
272     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction7 },
273     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction8 },
274     { __LINE__, WRITEDIR_1, "0100000", s_szTestFile1, L"", DoAction9 },
275     { __LINE__, WRITEDIR_1, "0010000", s_szTestFile1, L"", DoAction10 },
276     { __LINE__, WRITEDIR_1, "0001000", s_szTestDir1, L"", DoAction11 },
277     { __LINE__, WRITEDIR_1, "0000100", s_szTestDir1, L"", DoAction12 },
278 };
279 
280 static const TEST_ENTRY s_group_02[] =
281 {
282     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction1 },
283     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction2 },
284     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction3 },
285     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction4 },
286     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction5 },
287     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction6 },
288     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction7 },
289     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction8 },
290     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction9 },
291     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction10 },
292     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction11 },
293     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction12 },
294 
295     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction1 },
296     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction2 },
297     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction3 },
298     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction4 },
299     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction5 },
300     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction6 },
301     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction7 },
302     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction8 },
303     { __LINE__, WRITEDIR_1, "0100000", s_szTestFile1, L"", DoAction9 },
304     { __LINE__, WRITEDIR_1, "0010000", s_szTestFile1, L"", DoAction10 },
305     { __LINE__, WRITEDIR_1, "0001000", s_szTestDir1, L"", DoAction11 },
306     { __LINE__, WRITEDIR_1, "0000100", s_szTestDir1, L"", DoAction12 },
307 };
308 
309 static const TEST_ENTRY s_group_03[] =
310 {
311     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction1 },
312     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction2 },
313     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction3 },
314     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction4 },
315     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction5 },
316     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction6 },
317     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction7 },
318     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction8 },
319     { __LINE__, WRITEDIR_0, "0100000", s_szTestFile0, L"", DoAction9 },
320     { __LINE__, WRITEDIR_0, "0010000", s_szTestFile0, L"", DoAction10 },
321     { __LINE__, WRITEDIR_0, "0001000", s_szTestDir0, L"", DoAction11 },
322     { __LINE__, WRITEDIR_0, "0000100", s_szTestDir0, L"", DoAction12 },
323 
324     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction1 },
325     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction2 },
326     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction3 },
327     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction4 },
328     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction5 },
329     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction6 },
330     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction7 },
331     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction8 },
332     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction9 },
333     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction10 },
334     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction11 },
335     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction12 },
336 };
337 
338 static const TEST_ENTRY s_group_04[] =
339 {
340     { __LINE__, WRITEDIR_0, "0100000", s_szTestFile0, L"", DoAction1 },
341     { __LINE__, WRITEDIR_0, "1000000", s_szTestFile0, s_szTestFile0Kai, DoAction2 },
342     { __LINE__, WRITEDIR_0, "1000000", s_szTestFile0Kai, s_szTestFile0, DoAction3 },
343     { __LINE__, WRITEDIR_0, "0010000", s_szTestFile0, L"", DoAction4 },
344     { __LINE__, WRITEDIR_0, "0001000", s_szTestDir0, L"", DoAction5 },
345     { __LINE__, WRITEDIR_0, "0000010", s_szTestDir0, s_szTestDir0Kai, DoAction6 },
346     { __LINE__, WRITEDIR_0, "0000010", s_szTestDir0Kai, s_szTestDir0, DoAction7 },
347     { __LINE__, WRITEDIR_0, "0000100", s_szTestDir0, L"", DoAction8 },
348     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction9 },
349     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction10 },
350     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction11 },
351     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction12 },
352 
353     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction1 },
354     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction2 },
355     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction3 },
356     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction4 },
357     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction5 },
358     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction6 },
359     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction7 },
360     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction8 },
361     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction9 },
362     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction10 },
363     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction11 },
364     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction12 },
365 };
366 
367 static const TEST_ENTRY s_group_05[] =
368 {
369     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction1 },
370     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction2 },
371     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction3 },
372     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction4 },
373     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction5 },
374     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction6 },
375     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction7 },
376     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction8 },
377     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction9 },
378     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction10 },
379     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction11 },
380     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction12 },
381 
382     { __LINE__, WRITEDIR_1, "0100000", s_szTestFile1, L"", DoAction1 },
383     { __LINE__, WRITEDIR_1, "1000000", s_szTestFile1, s_szTestFile1Kai, DoAction2 },
384     { __LINE__, WRITEDIR_1, "1000000", s_szTestFile1Kai, s_szTestFile1, DoAction3 },
385     { __LINE__, WRITEDIR_1, "0010000", s_szTestFile1, L"", DoAction4 },
386     { __LINE__, WRITEDIR_1, "0001000", s_szTestDir1, L"", DoAction5 },
387     { __LINE__, WRITEDIR_1, "0000010", s_szTestDir1, s_szTestDir1Kai, DoAction6 },
388     { __LINE__, WRITEDIR_1, "0000010", s_szTestDir1Kai, s_szTestDir1, DoAction7 },
389     { __LINE__, WRITEDIR_1, "0000100", s_szTestDir1, L"", DoAction8 },
390     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction9 },
391     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction10 },
392     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction11 },
393     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction12 },
394 };
395 
396 static const TEST_ENTRY s_group_06[] =
397 {
398     { __LINE__, WRITEDIR_0, "0100000", s_szTestFile0, L"", DoAction1 },
399     { __LINE__, WRITEDIR_0, "1000000", s_szTestFile0, s_szTestFile0Kai, DoAction2 },
400     { __LINE__, WRITEDIR_0, "1000000", s_szTestFile0Kai, s_szTestFile0, DoAction3 },
401     { __LINE__, WRITEDIR_0, "0010000", s_szTestFile0, L"", DoAction4 },
402     { __LINE__, WRITEDIR_0, "0001000", s_szTestDir0, L"", DoAction5 },
403     { __LINE__, WRITEDIR_0, "0000010", s_szTestDir0, s_szTestDir0Kai, DoAction6 },
404     { __LINE__, WRITEDIR_0, "0000010", s_szTestDir0Kai, s_szTestDir0, DoAction7 },
405     { __LINE__, WRITEDIR_0, "0000100", s_szTestDir0, L"", DoAction8 },
406     { __LINE__, WRITEDIR_0, "0100000", s_szTestFile0, L"", DoAction9 },
407     { __LINE__, WRITEDIR_0, "0010000", s_szTestFile0, L"", DoAction10 },
408     { __LINE__, WRITEDIR_0, "0001000", s_szTestDir0, L"", DoAction11 },
409     { __LINE__, WRITEDIR_0, "0000100", s_szTestDir0, L"", DoAction12 },
410 
411     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction1 },
412     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction2 },
413     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction3 },
414     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction4 },
415     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction5 },
416     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction6 },
417     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction7 },
418     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction8 },
419     { __LINE__, WRITEDIR_1, "0100000", s_szTestFile1, L"", DoAction9 },
420     { __LINE__, WRITEDIR_1, "0010000", s_szTestFile1, L"", DoAction10 },
421     { __LINE__, WRITEDIR_1, "0001000", s_szTestDir1, L"", DoAction11 },
422     { __LINE__, WRITEDIR_1, "0000100", s_szTestDir1, L"", DoAction12 },
423 };
424 
425 static const TEST_ENTRY s_group_07[] =
426 {
427     { __LINE__, WRITEDIR_0, "0100000", s_szTestFile0, L"", DoAction1 },
428     { __LINE__, WRITEDIR_0, "1000000", s_szTestFile0, s_szTestFile0Kai, DoAction2 },
429     { __LINE__, WRITEDIR_0, "1000000", s_szTestFile0Kai, s_szTestFile0, DoAction3 },
430     { __LINE__, WRITEDIR_0, "0010000", s_szTestFile0, L"", DoAction4 },
431     { __LINE__, WRITEDIR_0, "0001000", s_szTestDir0, L"", DoAction5 },
432     { __LINE__, WRITEDIR_0, "0000010", s_szTestDir0, s_szTestDir0Kai, DoAction6 },
433     { __LINE__, WRITEDIR_0, "0000010", s_szTestDir0Kai, s_szTestDir0, DoAction7 },
434     { __LINE__, WRITEDIR_0, "0000100", s_szTestDir0, L"", DoAction8 },
435     { __LINE__, WRITEDIR_0, "0100000", s_szTestFile0, L"", DoAction9 },
436     { __LINE__, WRITEDIR_0, "0010000", s_szTestFile0, L"", DoAction10 },
437     { __LINE__, WRITEDIR_0, "0001000", s_szTestDir0, L"", DoAction11 },
438     { __LINE__, WRITEDIR_0, "0000100", s_szTestDir0, L"", DoAction12 },
439 
440     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction1 },
441     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction2 },
442     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction3 },
443     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction4 },
444     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction5 },
445     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction6 },
446     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction7 },
447     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction8 },
448     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction9 },
449     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction10 },
450     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction11 },
451     { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction12 },
452 };
453 
454 static const TEST_ENTRY s_group_08[] =
455 {
456     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction1 },
457     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction2 },
458     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction3 },
459     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction4 },
460     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction5 },
461     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction6 },
462     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction7 },
463     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction8 },
464     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction9 },
465     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction10 },
466     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction11 },
467     { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction12 },
468 
469     { __LINE__, WRITEDIR_1, "0100000", s_szTestFile1, L"", DoAction1 },
470     { __LINE__, WRITEDIR_1, "1000000", s_szTestFile1, s_szTestFile1Kai, DoAction2 },
471     { __LINE__, WRITEDIR_1, "1000000", s_szTestFile1Kai, s_szTestFile1, DoAction3 },
472     { __LINE__, WRITEDIR_1, "0010000", s_szTestFile1, L"", DoAction4 },
473     { __LINE__, WRITEDIR_1, "0001000", s_szTestDir1, L"", DoAction5 },
474     { __LINE__, WRITEDIR_1, "0000010", s_szTestDir1, s_szTestDir1Kai, DoAction6 },
475     { __LINE__, WRITEDIR_1, "0000010", s_szTestDir1Kai, s_szTestDir1, DoAction7 },
476     { __LINE__, WRITEDIR_1, "0000100", s_szTestDir1, L"", DoAction8 },
477     { __LINE__, WRITEDIR_1, "0100000", s_szTestFile1, L"", DoAction9 },
478     { __LINE__, WRITEDIR_1, "0010000", s_szTestFile1, L"", DoAction10 },
479     { __LINE__, WRITEDIR_1, "0001000", s_szTestDir1, L"", DoAction11 },
480     { __LINE__, WRITEDIR_1, "0000100", s_szTestDir1, L"", DoAction12 },
481 };
482 
483 static LPCSTR PatternFromFlags(DWORD flags)
484 {
485     static CHAR s_buf[(TYPE_MAX + 1) + 1];
486     for (INT i = 0; i < (TYPE_MAX + 1); ++i)
487         s_buf[i] = (char)('0' + !!(flags & (1 << i)));
488 
489     s_buf[TYPE_MAX + 1] = 0;
490     return s_buf;
491 }
492 
493 static BOOL DoGetPaths(LPWSTR pszPath1, LPWSTR pszPath2)
494 {
495     pszPath1[0] = pszPath2[0] = 0;
496 
497     WCHAR szText[MAX_PATH * 2];
498     szText[0] = 0;
499     FILE *fp = _wfopen(TEMP_FILE, L"rb");
500     if (fp)
501     {
502         fread(szText, 1, sizeof(szText), fp);
503         fclose(fp);
504     }
505 
506     LPWSTR pch = wcschr(szText, L'|');
507     if (pch == NULL)
508         return FALSE;
509 
510     *pch = 0;
511     StringCchCopyW(pszPath1, MAX_PATH, szText);
512     StringCchCopyW(pszPath2, MAX_PATH, pch + 1);
513     return TRUE;
514 }
515 
516 static void DoTestEntry(INT iEntry, const TEST_ENTRY *entry, INT nSources)
517 {
518 #ifdef ENTRY_TICK
519     DWORD dwOldTick = GetTickCount();
520 #endif
521 
522     BOOL bInterrupting = FALSE;
523     if (entry->action)
524         bInterrupting = entry->action(entry);
525 
526     DWORD flags;
527     LPCSTR pattern;
528     if ((nSources & SHCNRF_InterruptLevel) && bInterrupting)
529     {
530         // The event won't work at here. Manually waiting...
531         UINT cTry = ((iEntry == 0) ? 100 : 60);
532         for (UINT iTry = 0; iTry < cTry; ++iTry)
533         {
534             flags = SendMessageW(s_hwnd, WM_GET_NOTIFY_FLAGS, 0, 0);
535             pattern = PatternFromFlags(flags);
536             if (strcmp(pattern, "0000000") != 0)
537                 break;
538 
539             Sleep(1);
540         }
541     }
542     else
543     {
544         if (WaitForSingleObject(s_hEvent, 100) == WAIT_OBJECT_0)
545             Sleep(1);
546 
547         flags = SendMessageW(s_hwnd, WM_GET_NOTIFY_FLAGS, 0, 0);
548         pattern = PatternFromFlags(flags);
549     }
550 
551     SendMessageW(s_hwnd, WM_SET_PATHS, 0, 0);
552 
553     WCHAR szPath1[MAX_PATH], szPath2[MAX_PATH];
554     szPath1[0] = szPath2[0] = 0;
555     BOOL bOK = DoGetPaths(szPath1, szPath2);
556 
557     static UINT s_cCalmDown = 0;
558 
559     if (pattern[TYPE_UPDATEDIR] == '1')
560     {
561         trace("Line %d: SHCNE_UPDATEDIR: Calm down (%u)...\n", entry->line, s_cCalmDown);
562 
563         if (++s_cCalmDown < 3)
564             Sleep(3000);
565 
566         if (entry->pattern)
567             ok(TRUE, "Line %d:\n", entry->line);
568         if (entry->path1)
569             ok(TRUE, "Line %d:\n", entry->line);
570         if (entry->path2)
571             ok(TRUE, "Line %d:\n", entry->line);
572     }
573     else
574     {
575         s_cCalmDown = 0;
576         if (entry->pattern)
577         {
578             ok(strcmp(pattern, entry->pattern) == 0,
579                "Line %d: pattern mismatch '%s', tick=0x%08lX\n",
580                entry->line, pattern, GetTickCount());
581         }
582         if (entry->path1)
583             ok(bOK && lstrcmpiW(entry->path1, szPath1) == 0,
584                "Line %d: path1 mismatch '%S' (%d)\n", entry->line, szPath1, bOK);
585         if (entry->path2)
586             ok(bOK && lstrcmpiW(entry->path2, szPath2) == 0,
587                "Line %d: path2 mismatch '%S' (%d)\n", entry->line, szPath2, bOK);
588     }
589 
590     SendMessageW(s_hwnd, WM_CLEAR_FLAGS, 0, 0);
591     ResetEvent(s_hEvent);
592 
593 #ifdef ENTRY_TICK
594     DWORD dwNewTick = GetTickCount();
595     DWORD dwTick = dwNewTick - dwOldTick;
596     trace("DoTestEntry: Line %d: tick=%lu.%lu sec\n", entry->line,
597           (dwTick / 1000), (dwTick / 100 % 10));
598 #endif
599 }
600 
601 static void DoQuitTest(BOOL bForce)
602 {
603     PostMessageW(s_hwnd, WM_COMMAND, IDOK, 0);
604 
605     DoWaitForWindow(CLASSNAME, CLASSNAME, TRUE, bForce);
606     s_hwnd = NULL;
607 
608     if (s_hEvent)
609     {
610         CloseHandle(s_hEvent);
611         s_hEvent = NULL;
612     }
613 
614     DoDeleteFilesAndDirs();
615 }
616 
617 static void DoAbortThread(void)
618 {
619     skip("Aborting the thread...\n");
620     if (s_hThread)
621     {
622         TerminateThread(s_hThread, -1);
623         s_hThread = NULL;
624     }
625 }
626 
627 static BOOL CALLBACK HandlerRoutine(DWORD dwCtrlType)
628 {
629     switch (dwCtrlType)
630     {
631         case CTRL_C_EVENT:
632         case CTRL_BREAK_EVENT:
633             DoAbortThread();
634             DoQuitTest(TRUE);
635             return TRUE;
636     }
637     return FALSE;
638 }
639 
640 static BOOL DoInitTest(void)
641 {
642     // DIRTYPE_DESKTOP
643     LPWSTR psz = DoGetDir(DIRTYPE_DESKTOP);
644     StringCchCopyW(s_szDesktop, _countof(s_szDesktop), psz);
645 
646     PathAppendW(psz, TEST_FILE);
647     StringCchCopyW(s_szTestFile0, _countof(s_szTestFile0), psz);
648 
649     PathRemoveFileSpecW(psz);
650     PathAppendW(psz, TEST_FILE_KAI);
651     StringCchCopyW(s_szTestFile0Kai, _countof(s_szTestFile0Kai), psz);
652 
653     PathRemoveFileSpecW(psz);
654     PathAppendW(psz, TEST_DIR);
655     StringCchCopyW(s_szTestDir0, _countof(s_szTestDir0), psz);
656 
657     PathRemoveFileSpecW(psz);
658     PathAppendW(psz, TEST_DIR_KAI);
659     StringCchCopyW(s_szTestDir0Kai, _countof(s_szTestDir0Kai), psz);
660 
661     // DIRTYPE_MYDOCUMENTS
662     psz = DoGetDir(DIRTYPE_MYDOCUMENTS);
663     StringCchCopyW(s_szDocuments, _countof(s_szDocuments), psz);
664 
665     PathAppendW(psz, TEST_FILE);
666     StringCchCopyW(s_szTestFile1, _countof(s_szTestFile1), psz);
667 
668     PathRemoveFileSpecW(psz);
669     PathAppendW(psz, TEST_FILE_KAI);
670     StringCchCopyW(s_szTestFile1Kai, _countof(s_szTestFile1Kai), psz);
671 
672     PathRemoveFileSpecW(psz);
673     PathAppendW(psz, TEST_DIR);
674     StringCchCopyW(s_szTestDir1, _countof(s_szTestDir1), psz);
675 
676     PathRemoveFileSpecW(psz);
677     PathAppendW(psz, TEST_DIR_KAI);
678     StringCchCopyW(s_szTestDir1Kai, _countof(s_szTestDir1Kai), psz);
679 
680     // prepare for files and dirs
681     DoDeleteFilesAndDirs();
682     DoCreateEmptyFile(TEMP_FILE);
683 
684     // Ctrl+C
685     SetConsoleCtrlHandler(HandlerRoutine, TRUE);
686 
687     // close Explorer windows
688     trace("Closing Explorer windows...\n");
689     DoWaitForWindow(L"CabinetWClass", NULL, TRUE, TRUE);
690 
691     // close the CLASSNAME windows
692     return DoWaitForWindow(CLASSNAME, CLASSNAME, TRUE, TRUE) == NULL;
693 }
694 
695 static BOOL
696 GetSubProgramPath(void)
697 {
698     GetModuleFileNameW(NULL, s_szSubProgram, _countof(s_szSubProgram));
699     PathRemoveFileSpecW(s_szSubProgram);
700     PathAppendW(s_szSubProgram, L"shell32_apitest_sub.exe");
701 
702     if (!PathFileExistsW(s_szSubProgram))
703     {
704         PathRemoveFileSpecW(s_szSubProgram);
705         PathAppendW(s_szSubProgram, L"testdata\\shell32_apitest_sub.exe");
706 
707         if (!PathFileExistsW(s_szSubProgram))
708             return FALSE;
709     }
710 
711     return TRUE;
712 }
713 
714 #define SRC_00 0
715 #define SRC_01 SHCNRF_ShellLevel
716 #define SRC_02 (SHCNRF_NewDelivery)
717 #define SRC_03 (SHCNRF_NewDelivery | SHCNRF_ShellLevel)
718 #define SRC_04 SHCNRF_InterruptLevel
719 #define SRC_05 (SHCNRF_InterruptLevel | SHCNRF_ShellLevel)
720 #define SRC_06 (SHCNRF_InterruptLevel | SHCNRF_NewDelivery)
721 #define SRC_07 (SHCNRF_InterruptLevel | SHCNRF_NewDelivery | SHCNRF_ShellLevel)
722 #define SRC_08 (SHCNRF_RecursiveInterrupt | SHCNRF_InterruptLevel)
723 #define SRC_09 (SHCNRF_RecursiveInterrupt | SHCNRF_InterruptLevel | SHCNRF_ShellLevel)
724 #define SRC_10 (SHCNRF_RecursiveInterrupt | SHCNRF_InterruptLevel | SHCNRF_NewDelivery)
725 #define SRC_11 (SHCNRF_RecursiveInterrupt | SHCNRF_InterruptLevel | SHCNRF_NewDelivery | SHCNRF_ShellLevel)
726 
727 #define WATCHDIR_0 DIRTYPE_NULL
728 #define WATCHDIR_1 DIRTYPE_DESKTOP
729 #define WATCHDIR_2 DIRTYPE_MYCOMPUTER
730 #define WATCHDIR_3 DIRTYPE_MYDOCUMENTS
731 
732 static void
733 DoTestGroup(INT line, UINT cEntries, const TEST_ENTRY *pEntries, BOOL fRecursive,
734             INT nSources, DIRTYPE iWatchDir)
735 {
736 #ifdef NO_INTERRUPT_LEVEL
737     if (nSources & SHCNRF_InterruptLevel)
738         return;
739 #endif
740 #ifdef NO_SHELL_LEVEL
741     if (nSources & SHCNRF_ShellLevel)
742         return;
743 #endif
744 #ifdef NEW_DELIVERY_ONLY
745     if (!(nSources & SHCNRF_NewDelivery))
746         return;
747 #endif
748 #ifdef GROUP_TICK
749     DWORD dwOldTick = GetTickCount();
750 #endif
751 #ifdef RANDOM_QUARTER
752     if ((rand() & 3) == 0)
753         return;
754 #elif defined(RANDOM_HALF)
755     if (rand() & 1)
756         return;
757 #endif
758 
759     trace("DoTestGroup: Line %d: fRecursive:%u, iWatchDir:%u, nSources:0x%X\n",
760           line, fRecursive, iWatchDir, nSources);
761 
762     if (s_hEvent)
763     {
764         CloseHandle(s_hEvent);
765         s_hEvent = NULL;
766     }
767     s_hEvent = CreateEventW(NULL, TRUE, FALSE, EVENT_NAME);
768 
769     WCHAR szParams[64];
770     StringCchPrintfW(szParams, _countof(szParams), L"%u,%u,%u", fRecursive, iWatchDir, nSources);
771 
772     HINSTANCE hinst = ShellExecuteW(NULL, NULL, s_szSubProgram, szParams, NULL, SW_SHOWNORMAL);
773     if ((INT_PTR)hinst <= 32)
774     {
775         skip("Unable to run shell32_apitest_sub.exe.\n");
776         return;
777     }
778 
779     s_hwnd = DoWaitForWindow(CLASSNAME, CLASSNAME, FALSE, FALSE);
780     if (!s_hwnd)
781     {
782         skip("Unable to find window.\n");
783         return;
784     }
785 
786     for (UINT i = 0; i < cEntries; ++i)
787     {
788         if (!IsWindow(s_hwnd))
789         {
790             DoAbortThread();
791             DoQuitTest(TRUE);
792             break;
793         }
794 
795         DoTestEntry(i, &pEntries[i], nSources);
796     }
797 
798     DoQuitTest(FALSE);
799 
800 #ifdef GROUP_TICK
801     DWORD dwNewTick = GetTickCount();
802     DWORD dwTick = dwNewTick - dwOldTick;
803     trace("DoTestGroup: Line %d: %lu.%lu sec\n", line, (dwTick / 1000), (dwTick / 100 % 10));
804 #endif
805 }
806 
807 static unsigned __stdcall TestThreadProc(void *)
808 {
809     srand(time(NULL));
810 #ifdef RANDOM_QUARTER
811     skip("RANDOM_QUARTER\n");
812 #elif defined(RANDOM_HALF)
813     skip("RANDOM_HALF\n");
814 #endif
815 
816     // fRecursive == FALSE.
817     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_00, WATCHDIR_0);
818     DoTestGroup(__LINE__, _countof(s_group_01), s_group_01, FALSE, SRC_01, WATCHDIR_0);
819     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_02, WATCHDIR_0);
820     DoTestGroup(__LINE__, _countof(s_group_01), s_group_01, FALSE, SRC_03, WATCHDIR_0);
821     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, FALSE, SRC_04, WATCHDIR_0);
822     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, FALSE, SRC_05, WATCHDIR_0);
823     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, FALSE, SRC_06, WATCHDIR_0);
824     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, FALSE, SRC_07, WATCHDIR_0);
825     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, FALSE, SRC_08, WATCHDIR_0);
826     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, FALSE, SRC_09, WATCHDIR_0);
827     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, FALSE, SRC_10, WATCHDIR_0);
828     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, FALSE, SRC_11, WATCHDIR_0);
829 
830     BOOL bTarget = IsWindowsXPOrGreater() && !IsWindowsVistaOrGreater();
831 
832 #define SWITCH(x, y) (bTarget ? (x) : (y))
833     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_00, WATCHDIR_1);
834     DoTestGroup(__LINE__, _countof(s_group_03), s_group_03, FALSE, SRC_01, WATCHDIR_1);
835     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_02, WATCHDIR_1);
836     DoTestGroup(__LINE__, _countof(s_group_03), s_group_03, FALSE, SRC_03, WATCHDIR_1);
837     DoTestGroup(__LINE__, SWITCH(_countof(s_group_00), _countof(s_group_04)), SWITCH(s_group_00, s_group_04), FALSE, SRC_04, WATCHDIR_1);
838     DoTestGroup(__LINE__, _countof(s_group_07), s_group_07, FALSE, SRC_05, WATCHDIR_1);
839     DoTestGroup(__LINE__, SWITCH(_countof(s_group_00), _countof(s_group_04)), SWITCH(s_group_00, s_group_04), FALSE, SRC_06, WATCHDIR_1);
840     DoTestGroup(__LINE__, _countof(s_group_07), s_group_07, FALSE, SRC_07, WATCHDIR_1);
841     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, FALSE, SRC_08, WATCHDIR_1);
842     DoTestGroup(__LINE__, _countof(s_group_07), s_group_07, FALSE, SRC_09, WATCHDIR_1);
843     DoTestGroup(__LINE__, SWITCH(_countof(s_group_00), _countof(s_group_04)), SWITCH(s_group_00, s_group_04), FALSE, SRC_06, WATCHDIR_1);
844     DoTestGroup(__LINE__, _countof(s_group_07), s_group_07, FALSE, SRC_11, WATCHDIR_1);
845 #undef SWITCH
846 
847 #ifndef NO_TRIVIAL
848     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_00, WATCHDIR_2);
849     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_01, WATCHDIR_2);
850     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_02, WATCHDIR_2);
851     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_03, WATCHDIR_2);
852     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_04, WATCHDIR_2);
853     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_05, WATCHDIR_2);
854     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_06, WATCHDIR_2);
855     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_07, WATCHDIR_2);
856     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_08, WATCHDIR_2);
857     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_09, WATCHDIR_2);
858     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_10, WATCHDIR_2);
859     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_11, WATCHDIR_2);
860 #endif
861 
862     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_00, WATCHDIR_3);
863     DoTestGroup(__LINE__, _countof(s_group_02), s_group_02, FALSE, SRC_01, WATCHDIR_3);
864     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_02, WATCHDIR_3);
865     DoTestGroup(__LINE__, _countof(s_group_02), s_group_02, FALSE, SRC_03, WATCHDIR_3);
866     DoTestGroup(__LINE__, _countof(s_group_05), s_group_05, FALSE, SRC_04, WATCHDIR_3);
867     DoTestGroup(__LINE__, _countof(s_group_08), s_group_08, FALSE, SRC_05, WATCHDIR_3);
868     DoTestGroup(__LINE__, _countof(s_group_05), s_group_05, FALSE, SRC_06, WATCHDIR_3);
869     DoTestGroup(__LINE__, _countof(s_group_08), s_group_08, FALSE, SRC_07, WATCHDIR_3);
870     DoTestGroup(__LINE__, _countof(s_group_05), s_group_05, FALSE, SRC_08, WATCHDIR_3);
871     DoTestGroup(__LINE__, _countof(s_group_08), s_group_08, FALSE, SRC_09, WATCHDIR_3);
872     DoTestGroup(__LINE__, _countof(s_group_05), s_group_05, FALSE, SRC_10, WATCHDIR_3);
873     DoTestGroup(__LINE__, _countof(s_group_08), s_group_08, FALSE, SRC_11, WATCHDIR_3);
874 
875     // fRecursive == TRUE.
876     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, TRUE, SRC_00, WATCHDIR_0);
877     DoTestGroup(__LINE__, _countof(s_group_01), s_group_01, TRUE, SRC_01, WATCHDIR_0);
878     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, TRUE, SRC_02, WATCHDIR_0);
879     DoTestGroup(__LINE__, _countof(s_group_01), s_group_01, TRUE, SRC_03, WATCHDIR_0);
880     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_04, WATCHDIR_0);
881     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_05, WATCHDIR_0);
882     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_06, WATCHDIR_0);
883     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_07, WATCHDIR_0);
884     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_08, WATCHDIR_0);
885     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_09, WATCHDIR_0);
886     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_10, WATCHDIR_0);
887     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_11, WATCHDIR_0);
888 
889     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, TRUE, SRC_00, WATCHDIR_1);
890     DoTestGroup(__LINE__, _countof(s_group_01), s_group_01, TRUE, SRC_01, WATCHDIR_1);
891     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, TRUE, SRC_02, WATCHDIR_1);
892     DoTestGroup(__LINE__, _countof(s_group_01), s_group_01, TRUE, SRC_03, WATCHDIR_1);
893     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_04, WATCHDIR_1);
894     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_05, WATCHDIR_1);
895     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_06, WATCHDIR_1);
896     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_07, WATCHDIR_1);
897     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_08, WATCHDIR_1);
898     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_09, WATCHDIR_1);
899     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_10, WATCHDIR_1);
900     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_11, WATCHDIR_1);
901 
902     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, TRUE, SRC_00, WATCHDIR_2);
903     DoTestGroup(__LINE__, _countof(s_group_01), s_group_01, TRUE, SRC_01, WATCHDIR_2);
904     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, TRUE, SRC_02, WATCHDIR_2);
905     DoTestGroup(__LINE__, _countof(s_group_01), s_group_01, TRUE, SRC_03, WATCHDIR_2);
906     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_04, WATCHDIR_2);
907     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_05, WATCHDIR_2);
908     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_06, WATCHDIR_2);
909     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_07, WATCHDIR_2);
910     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_08, WATCHDIR_2);
911     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_09, WATCHDIR_2);
912     DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_10, WATCHDIR_2);
913     DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_11, WATCHDIR_2);
914 
915     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, TRUE, SRC_00, WATCHDIR_3);
916     DoTestGroup(__LINE__, _countof(s_group_02), s_group_02, TRUE, SRC_01, WATCHDIR_3);
917     DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, TRUE, SRC_02, WATCHDIR_3);
918     DoTestGroup(__LINE__, _countof(s_group_02), s_group_02, TRUE, SRC_03, WATCHDIR_3);
919     DoTestGroup(__LINE__, _countof(s_group_05), s_group_05, TRUE, SRC_04, WATCHDIR_3);
920     DoTestGroup(__LINE__, _countof(s_group_08), s_group_08, TRUE, SRC_05, WATCHDIR_3);
921     DoTestGroup(__LINE__, _countof(s_group_05), s_group_05, TRUE, SRC_06, WATCHDIR_3);
922     DoTestGroup(__LINE__, _countof(s_group_08), s_group_08, TRUE, SRC_07, WATCHDIR_3);
923     DoTestGroup(__LINE__, _countof(s_group_05), s_group_05, TRUE, SRC_08, WATCHDIR_3);
924     DoTestGroup(__LINE__, _countof(s_group_08), s_group_08, TRUE, SRC_09, WATCHDIR_3);
925     DoTestGroup(__LINE__, _countof(s_group_05), s_group_05, TRUE, SRC_10, WATCHDIR_3);
926     DoTestGroup(__LINE__, _countof(s_group_08), s_group_08, TRUE, SRC_11, WATCHDIR_3);
927 
928     return 0;
929 }
930 
931 START_TEST(SHChangeNotify)
932 {
933 #ifdef DISABLE_THIS_TESTCASE
934     skip("This testcase is disabled by DISABLE_THIS_TESTCASE macro.\n");
935 #endif
936 #ifdef TOTAL_TICK
937     DWORD dwOldTick = GetTickCount();
938 #endif
939 
940     trace("Please don't operate your PC while testing...\n");
941 
942     if (!GetSubProgramPath())
943     {
944         skip("shell32_apitest_sub.exe not found\n");
945         return;
946     }
947 
948     if (!DoInitTest())
949     {
950         skip("Unable to initialize.\n");
951         DoQuitTest(TRUE);
952         return;
953     }
954 
955     s_hThread = (HANDLE)_beginthreadex(NULL, 0, TestThreadProc, NULL, 0, NULL);
956     WaitForSingleObject(s_hThread, INFINITE);
957     CloseHandle(s_hThread);
958 
959 #ifdef TOTAL_TICK
960     DWORD dwNewTick = GetTickCount();
961     DWORD dwTick = dwNewTick - dwOldTick;
962     trace("SHChangeNotify: Total %lu.%lu sec\n", (dwTick / 1000), (dwTick / 100 % 10));
963 #endif
964 }
965