kernel32/tests: Fix a test for real hardware.
[wine] / dlls / kernel32 / tests / change.c
1 /*
2  * Tests for file change notification functions
3  *
4  * Copyright (c) 2004 Hans Leidekker
5  * Copyright 2006 Mike McCormack for CodeWeavers
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 /* TODO: - security attribute changes
23  *       - compound filter and multiple notifications
24  *       - subtree notifications
25  *       - non-documented flags FILE_NOTIFY_CHANGE_LAST_ACCESS and
26  *         FILE_NOTIFY_CHANGE_CREATION
27  */
28
29 #include <stdarg.h>
30 #include <stdio.h>
31
32 #include "ntstatus.h"
33 #define WIN32_NO_STATUS
34 #include "wine/test.h"
35 #include <windef.h>
36 #include <winbase.h>
37 #include <winternl.h>
38
39 static DWORD CALLBACK NotificationThread(LPVOID arg)
40 {
41     HANDLE change = arg;
42     BOOL notified = FALSE;
43     BOOL ret = FALSE;
44     DWORD status;
45
46     status = WaitForSingleObject(change, 100);
47
48     if (status == WAIT_OBJECT_0 ) {
49         notified = TRUE;
50         FindNextChangeNotification(change);
51     }
52
53     ret = FindCloseChangeNotification(change);
54     ok( ret, "FindCloseChangeNotification error: %d\n",
55        GetLastError());
56
57     ExitThread((DWORD)notified);
58 }
59
60 static HANDLE StartNotificationThread(LPCSTR path, BOOL subtree, DWORD flags)
61 {
62     HANDLE change, thread;
63     DWORD threadId;
64
65     change = FindFirstChangeNotificationA(path, subtree, flags);
66     ok(change != INVALID_HANDLE_VALUE, "FindFirstChangeNotification error: %d\n", GetLastError());
67
68     thread = CreateThread(NULL, 0, NotificationThread, change, 0, &threadId);
69     ok(thread != NULL, "CreateThread error: %d\n", GetLastError());
70
71     return thread;
72 }
73
74 static DWORD FinishNotificationThread(HANDLE thread)
75 {
76     DWORD status, exitcode;
77
78     status = WaitForSingleObject(thread, 5000);
79     ok(status == WAIT_OBJECT_0, "WaitForSingleObject status %d error %d\n", status, GetLastError());
80
81     ok(GetExitCodeThread(thread, &exitcode), "Could not retrieve thread exit code\n");
82     CloseHandle(thread);
83
84     return exitcode;
85 }
86
87 static void test_FindFirstChangeNotification(void)
88 {
89     HANDLE change, file, thread;
90     DWORD attributes, count;
91     BOOL ret;
92
93     char workdir[MAX_PATH], dirname1[MAX_PATH], dirname2[MAX_PATH];
94     char filename1[MAX_PATH], filename2[MAX_PATH];
95     static const char prefix[] = "FCN";
96     char buffer[2048];
97
98     /* pathetic checks */
99
100     change = FindFirstChangeNotificationA("not-a-file", FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
101     ok(change == INVALID_HANDLE_VALUE, "Expected INVALID_HANDLE_VALUE, got %p\n", change);
102     ok(GetLastError() == ERROR_FILE_NOT_FOUND ||
103        GetLastError() == ERROR_NO_MORE_FILES, /* win95 */
104        "FindFirstChangeNotification error: %d\n", GetLastError());
105
106     if (0) /* This documents win2k behavior. It crashes on win98. */
107     { 
108         change = FindFirstChangeNotificationA(NULL, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
109         ok(change == NULL && GetLastError() == ERROR_PATH_NOT_FOUND,
110         "FindFirstChangeNotification error: %d\n", GetLastError());
111     }
112
113     ret = FindNextChangeNotification(NULL);
114     ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindNextChangeNotification error: %d\n",
115        GetLastError());
116
117     ret = FindCloseChangeNotification(NULL);
118     ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindCloseChangeNotification error: %d\n",
119        GetLastError());
120
121     ret = GetTempPathA(MAX_PATH, workdir);
122     ok(ret, "GetTempPathA error: %d\n", GetLastError());
123
124     lstrcatA(workdir, "testFileChangeNotification");
125
126     ret = CreateDirectoryA(workdir, NULL);
127     ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
128
129     ret = GetTempFileNameA(workdir, prefix, 0, filename1);
130     ok(ret, "GetTempFileNameA error: %d\n", GetLastError());
131
132     file = CreateFileA(filename1, GENERIC_WRITE|GENERIC_READ, 0, NULL, CREATE_ALWAYS,
133                        FILE_ATTRIBUTE_NORMAL, 0);
134     ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %d\n", GetLastError());
135     ret = CloseHandle(file);
136     ok( ret, "CloseHandle error: %d\n", GetLastError());
137
138     /* Try to register notification for a file. win98 and win2k behave differently here */
139     change = FindFirstChangeNotificationA(filename1, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
140     ok(change == INVALID_HANDLE_VALUE && (GetLastError() == ERROR_DIRECTORY ||
141                                           GetLastError() == ERROR_FILE_NOT_FOUND),
142        "FindFirstChangeNotification error: %d\n", GetLastError());
143
144     lstrcpyA(dirname1, filename1);
145     lstrcatA(dirname1, "dir");
146
147     lstrcpyA(dirname2, dirname1);
148     lstrcatA(dirname2, "new");
149
150     ret = CreateDirectoryA(dirname1, NULL);
151     ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
152
153     /* What if we move the directory we registered notification for? */
154     thread = StartNotificationThread(dirname1, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
155     ret = MoveFileA(dirname1, dirname2);
156     ok(ret, "MoveFileA error: %d\n", GetLastError());
157     /* win9x and win2k behave differently here, don't check result */
158     FinishNotificationThread(thread);
159
160     /* What if we remove the directory we registered notification for? */
161     thread = StartNotificationThread(dirname2, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
162     ret = RemoveDirectoryA(dirname2);
163     ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
164     /* win9x and win2k behave differently here, don't check result */
165     FinishNotificationThread(thread);
166
167     /* functional checks */
168
169     /* Create a directory */
170     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
171     ret = CreateDirectoryA(dirname1, NULL);
172     ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
173     ok(FinishNotificationThread(thread), "Missed notification\n");
174
175     /* Rename a directory */
176     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
177     ret = MoveFileA(dirname1, dirname2);
178     ok(ret, "MoveFileA error: %d\n", GetLastError());
179     ok(FinishNotificationThread(thread), "Missed notification\n");
180
181     /* Delete a directory */
182     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
183     ret = RemoveDirectoryA(dirname2);
184     ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
185     ok(FinishNotificationThread(thread), "Missed notification\n");
186
187     lstrcpyA(filename2, filename1);
188     lstrcatA(filename2, "new");
189
190     /* Rename a file */
191     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
192     ret = MoveFileA(filename1, filename2);
193     ok(ret, "MoveFileA error: %d\n", GetLastError());
194     ok(FinishNotificationThread(thread), "Missed notification\n");
195
196     /* Delete a file */
197     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
198     ret = DeleteFileA(filename2);
199     ok(ret, "DeleteFileA error: %d\n", GetLastError());
200     ok(FinishNotificationThread(thread), "Missed notification\n");
201
202     /* Create a file */
203     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
204     file = CreateFileA(filename2, GENERIC_WRITE|GENERIC_READ, 0, NULL, CREATE_ALWAYS, 
205                        FILE_ATTRIBUTE_NORMAL, 0);
206     ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %d\n", GetLastError());
207     ret = CloseHandle(file);
208     ok( ret, "CloseHandle error: %d\n", GetLastError());
209     ok(FinishNotificationThread(thread), "Missed notification\n");
210
211     attributes = GetFileAttributesA(filename2);
212     ok(attributes != INVALID_FILE_ATTRIBUTES, "GetFileAttributesA error: %d\n", GetLastError());
213     attributes &= FILE_ATTRIBUTE_READONLY;
214
215     /* Change file attributes */
216     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_ATTRIBUTES);
217     ret = SetFileAttributesA(filename2, attributes);
218     ok(ret, "SetFileAttributesA error: %d\n", GetLastError());
219     ok(FinishNotificationThread(thread), "Missed notification\n");
220
221     /* Change last write time by writing to a file */
222     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE);
223     file = CreateFileA(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 
224                        FILE_ATTRIBUTE_NORMAL, 0);
225     ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %d\n", GetLastError());
226     memset(buffer, 0, sizeof(buffer));
227     ret = WriteFile(file, buffer, sizeof(buffer), &count, NULL);
228     ok(ret && count == sizeof(buffer), "WriteFile error: %d\n", GetLastError());
229     ret = CloseHandle(file);
230     ok( ret, "CloseHandle error: %d\n", GetLastError());
231     ok(FinishNotificationThread(thread), "Missed notification\n");
232
233     /* Change file size by truncating a file */
234     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_SIZE);
235     file = CreateFileA(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 
236                        FILE_ATTRIBUTE_NORMAL, 0);
237     ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %d\n", GetLastError());
238     ret = WriteFile(file, buffer, sizeof(buffer) / 2, &count, NULL);
239     ok(ret && count == sizeof(buffer) / 2, "WriteFileA error: %d\n", GetLastError());
240     ret = CloseHandle(file);
241     ok( ret, "CloseHandle error: %d\n", GetLastError());
242     ok(FinishNotificationThread(thread), "Missed notification\n");
243
244     /* clean up */
245     
246     ret = DeleteFileA(filename2);
247     ok(ret, "DeleteFileA error: %d\n", GetLastError());
248
249     ret = RemoveDirectoryA(workdir);
250     ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
251 }
252
253 /* this test concentrates more on the wait behaviour of the handle */
254 static void test_ffcn(void)
255 {
256     DWORD filter;
257     HANDLE handle;
258     LONG r;
259     WCHAR path[MAX_PATH], subdir[MAX_PATH];
260     static const WCHAR szBoo[] = { '\\','b','o','o',0 };
261     static const WCHAR szHoo[] = { '\\','h','o','o',0 };
262
263     SetLastError(0xdeadbeef);
264     r = GetTempPathW( MAX_PATH, path );
265     if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
266     {
267         win_skip("GetTempPathW is not implemented\n");
268         return;
269     }
270     ok( r != 0, "temp path failed\n");
271     if (!r)
272         return;
273
274     lstrcatW( path, szBoo );
275     lstrcpyW( subdir, path );
276     lstrcatW( subdir, szHoo );
277
278     RemoveDirectoryW( subdir );
279     RemoveDirectoryW( path );
280     
281     r = CreateDirectoryW(path, NULL);
282     ok( r == TRUE, "failed to create directory\n");
283
284     filter = FILE_NOTIFY_CHANGE_FILE_NAME;
285     filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
286
287     handle = FindFirstChangeNotificationW( path, 1, filter);
288     ok( handle != INVALID_HANDLE_VALUE, "invalid handle\n");
289
290     r = WaitForSingleObject( handle, 0 );
291     ok( r == STATUS_TIMEOUT, "should time out\n");
292
293     r = CreateDirectoryW( subdir, NULL );
294     ok( r == TRUE, "failed to create subdir\n");
295
296     r = WaitForSingleObject( handle, 0 );
297     ok( r == WAIT_OBJECT_0, "should be ready\n");
298
299     r = WaitForSingleObject( handle, 0 );
300     ok( r == WAIT_OBJECT_0, "should be ready\n");
301
302     r = FindNextChangeNotification(handle);
303     ok( r == TRUE, "find next failed\n");
304
305     r = WaitForSingleObject( handle, 0 );
306     ok( r == STATUS_TIMEOUT, "should time out\n");
307
308     r = RemoveDirectoryW( subdir );
309     ok( r == TRUE, "failed to remove subdir\n");
310
311     r = WaitForSingleObject( handle, 0 );
312     ok( r == WAIT_OBJECT_0, "should be ready\n");
313
314     r = WaitForSingleObject( handle, 0 );
315     ok( r == WAIT_OBJECT_0, "should be ready\n");
316
317     r = FindNextChangeNotification(handle);
318     ok( r == TRUE, "find next failed\n");
319
320     r = FindNextChangeNotification(handle);
321     ok( r == TRUE, "find next failed\n");
322
323     r = FindCloseChangeNotification(handle);
324     ok( r == TRUE, "should succeed\n");
325
326     r = RemoveDirectoryW( path );
327     ok( r == TRUE, "failed to remove dir\n");
328 }
329
330 /* this test concentrates on the wait behavior when multiple threads are
331  * waiting on a change notification handle. */
332 static void test_ffcnMultipleThreads(void)
333 {
334     LONG r;
335     DWORD filter, threadId, status, exitcode;
336     HANDLE handles[2];
337     char path[MAX_PATH];
338
339     r = GetTempPathA(MAX_PATH, path);
340     ok(r, "GetTempPathA error: %d\n", GetLastError());
341
342     lstrcatA(path, "ffcnTestMultipleThreads");
343
344     RemoveDirectoryA(path);
345
346     r = CreateDirectoryA(path, NULL);
347     ok(r, "CreateDirectoryA error: %d\n", GetLastError());
348
349     filter = FILE_NOTIFY_CHANGE_FILE_NAME;
350     filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
351
352     handles[0] = FindFirstChangeNotificationA(path, FALSE, filter);
353     ok(handles[0] != INVALID_HANDLE_VALUE, "FindFirstChangeNotification error: %d\n", GetLastError());
354
355     /* Test behavior if a waiting thread holds the last reference to a change
356      * directory object with an empty wine user APC queue for this thread (bug #7286) */
357
358     /* Create our notification thread */
359     handles[1] = CreateThread(NULL, 0, NotificationThread, handles[0], 0,
360                               &threadId);
361     ok(handles[1] != NULL, "CreateThread error: %d\n", GetLastError());
362
363     status = WaitForMultipleObjects(2, handles, FALSE, 5000);
364     ok(status == WAIT_OBJECT_0 || status == WAIT_OBJECT_0+1, "WaitForMultipleObjects status %d error %d\n", status, GetLastError());
365     ok(GetExitCodeThread(handles[1], &exitcode), "Could not retrieve thread exit code\n");
366
367     /* Clean up */
368     r = RemoveDirectoryA( path );
369     ok( r == TRUE, "failed to remove dir\n");
370 }
371
372 static BOOL (WINAPI *pReadDirectoryChangesW)(HANDLE,LPVOID,DWORD,BOOL,DWORD,
373                          LPDWORD,LPOVERLAPPED,LPOVERLAPPED_COMPLETION_ROUTINE);
374
375 static void test_readdirectorychanges(void)
376 {
377     HANDLE hdir;
378     char buffer[0x1000];
379     DWORD fflags, filter = 0, r, dwCount;
380     OVERLAPPED ov;
381     WCHAR path[MAX_PATH], subdir[MAX_PATH], subsubdir[MAX_PATH];
382     static const WCHAR szBoo[] = { '\\','b','o','o',0 };
383     static const WCHAR szHoo[] = { '\\','h','o','o',0 };
384     static const WCHAR szGa[] = { '\\','h','o','o','\\','g','a',0 };
385     PFILE_NOTIFY_INFORMATION pfni;
386
387     if (!pReadDirectoryChangesW)
388     {
389         win_skip("ReadDirectoryChangesW is not available\n");
390         return;
391     }
392
393     SetLastError(0xdeadbeef);
394     r = GetTempPathW( MAX_PATH, path );
395     if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
396     {
397         win_skip("GetTempPathW is not implemented\n");
398         return;
399     }
400     ok( r != 0, "temp path failed\n");
401     if (!r)
402         return;
403
404     lstrcatW( path, szBoo );
405     lstrcpyW( subdir, path );
406     lstrcatW( subdir, szHoo );
407
408     lstrcpyW( subsubdir, path );
409     lstrcatW( subsubdir, szGa );
410
411     RemoveDirectoryW( subsubdir );
412     RemoveDirectoryW( subdir );
413     RemoveDirectoryW( path );
414     
415     r = CreateDirectoryW(path, NULL);
416     ok( r == TRUE, "failed to create directory\n");
417
418     SetLastError(0xd0b00b00);
419     r = pReadDirectoryChangesW(NULL,NULL,0,FALSE,0,NULL,NULL,NULL);
420     ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
421     ok(r==FALSE, "should return false\n");
422
423     fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
424     hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY, 
425                         FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, 
426                         OPEN_EXISTING, fflags, NULL);
427     ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
428
429     ov.hEvent = CreateEvent( NULL, 1, 0, NULL );
430
431     SetLastError(0xd0b00b00);
432     r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,NULL,NULL);
433     ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
434     ok(r==FALSE, "should return false\n");
435
436     SetLastError(0xd0b00b00);
437     r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,&ov,NULL);
438     ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
439     ok(r==FALSE, "should return false\n");
440
441     filter = FILE_NOTIFY_CHANGE_FILE_NAME;
442     filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
443     filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
444     filter |= FILE_NOTIFY_CHANGE_SIZE;
445     filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
446     filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
447     filter |= FILE_NOTIFY_CHANGE_CREATION;
448     filter |= FILE_NOTIFY_CHANGE_SECURITY;
449
450     SetLastError(0xd0b00b00);
451     ov.Internal = 0;
452     ov.InternalHigh = 0;
453     memset( buffer, 0, sizeof buffer );
454
455     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,-1,NULL,&ov,NULL);
456     ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
457     ok(r==FALSE, "should return false\n");
458
459     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL);
460     ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
461     ok(r==FALSE, "should return false\n");
462
463     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
464     ok(r==TRUE, "should return true\n");
465
466     r = WaitForSingleObject( ov.hEvent, 10 );
467     ok( r == STATUS_TIMEOUT, "should timeout\n" );
468
469     r = CreateDirectoryW( subdir, NULL );
470     ok( r == TRUE, "failed to create directory\n");
471
472     r = WaitForSingleObject( ov.hEvent, 1000 );
473     ok( r == WAIT_OBJECT_0, "event should be ready\n" );
474
475     ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
476     ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
477
478     pfni = (PFILE_NOTIFY_INFORMATION) buffer;
479     ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
480     ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
481     ok( pfni->FileNameLength == 6, "len wrong\n" );
482     ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
483
484     ResetEvent(ov.hEvent);
485     SetLastError(0xd0b00b00);
486     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,NULL,NULL);
487     ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
488     ok(r==FALSE, "should return false\n");
489
490     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL);
491     ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
492     ok(r==FALSE, "should return false\n");
493
494     filter = FILE_NOTIFY_CHANGE_SIZE;
495
496     SetEvent(ov.hEvent);
497     ov.Internal = 1;
498     ov.InternalHigh = 1;
499     S(U(ov)).Offset = 0;
500     S(U(ov)).OffsetHigh = 0;
501     memset( buffer, 0, sizeof buffer );
502     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
503     ok(r==TRUE, "should return true\n");
504
505     ok( ov.Internal == STATUS_PENDING, "ov.Internal wrong\n");
506     ok( ov.InternalHigh == 1, "ov.InternalHigh wrong\n");
507
508     r = WaitForSingleObject( ov.hEvent, 0 );
509     ok( r == STATUS_TIMEOUT, "should timeout\n" );
510
511     r = RemoveDirectoryW( subdir );
512     ok( r == TRUE, "failed to remove directory\n");
513
514     r = WaitForSingleObject( ov.hEvent, 1000 );
515     ok( r == WAIT_OBJECT_0, "should be ready\n" );
516
517     ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
518     ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
519
520     if (ov.Internal == STATUS_SUCCESS)
521     {
522         r = GetOverlappedResult( hdir, &ov, &dwCount, TRUE );
523         ok( r == TRUE, "getoverlappedresult failed\n");
524         ok( dwCount == 0x12, "count wrong\n");
525     }
526
527     pfni = (PFILE_NOTIFY_INFORMATION) buffer;
528     ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
529     ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong\n" );
530     ok( pfni->FileNameLength == 6, "len wrong\n" );
531     ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
532
533     /* what happens if the buffer is too small? */
534     r = pReadDirectoryChangesW(hdir,buffer,0x10,FALSE,filter,NULL,&ov,NULL);
535     ok(r==TRUE, "should return true\n");
536
537     r = CreateDirectoryW( subdir, NULL );
538     ok( r == TRUE, "failed to create directory\n");
539
540     r = WaitForSingleObject( ov.hEvent, 1000 );
541     ok( r == WAIT_OBJECT_0, "should be ready\n" );
542
543     ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
544     ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
545
546     /* test the recursive watch */
547     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
548     ok(r==TRUE, "should return true\n");
549
550     r = CreateDirectoryW( subsubdir, NULL );
551     ok( r == TRUE, "failed to create directory\n");
552
553     r = WaitForSingleObject( ov.hEvent, 1000 );
554     ok( r == WAIT_OBJECT_0, "should be ready\n" );
555
556     ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
557     ok( ov.InternalHigh == 0x18, "ov.InternalHigh wrong\n");
558
559     pfni = (PFILE_NOTIFY_INFORMATION) buffer;
560     ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
561     ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
562     ok( pfni->FileNameLength == 6*sizeof(WCHAR), "len wrong\n" );
563     ok( !memcmp(pfni->FileName,&szGa[1],6*sizeof(WCHAR)), "name wrong\n" );
564
565     r = RemoveDirectoryW( subsubdir );
566     ok( r == TRUE, "failed to remove directory\n");
567
568     ov.Internal = 1;
569     ov.InternalHigh = 1;
570     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
571     ok(r==TRUE, "should return true\n");
572
573     r = RemoveDirectoryW( subdir );
574     ok( r == TRUE, "failed to remove directory\n");
575
576     r = WaitForSingleObject( ov.hEvent, 1000 );
577     ok( r == WAIT_OBJECT_0, "should be ready\n" );
578
579     pfni = (PFILE_NOTIFY_INFORMATION) buffer;
580     /* we may get a notification for the parent dir too */
581     if (pfni->Action == FILE_ACTION_MODIFIED && pfni->NextEntryOffset)
582     {
583         ok( pfni->FileNameLength == 3*sizeof(WCHAR), "len wrong %u\n", pfni->FileNameLength );
584         ok( !memcmp(pfni->FileName,&szGa[1],3*sizeof(WCHAR)), "name wrong\n" );
585         pfni = (PFILE_NOTIFY_INFORMATION)((char *)pfni + pfni->NextEntryOffset);
586     }
587     ok( pfni->NextEntryOffset == 0, "offset wrong %u\n", pfni->NextEntryOffset );
588     ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong %u\n", pfni->Action );
589     ok( pfni->FileNameLength == 6*sizeof(WCHAR), "len wrong %u\n", pfni->FileNameLength );
590     ok( !memcmp(pfni->FileName,&szGa[1],6*sizeof(WCHAR)), "name wrong\n" );
591
592     ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
593     dwCount = (char *)&pfni->FileName[pfni->FileNameLength/sizeof(WCHAR)] - buffer;
594     ok( ov.InternalHigh == dwCount, "ov.InternalHigh wrong %lu/%u\n",ov.InternalHigh, dwCount );
595
596     CloseHandle(hdir);
597
598     r = RemoveDirectoryW( path );
599     ok( r == TRUE, "failed to remove directory\n");
600 }
601
602 /* show the behaviour when a null buffer is passed */
603 static void test_readdirectorychanges_null(void)
604 {
605     NTSTATUS r;
606     HANDLE hdir;
607     char buffer[0x1000];
608     DWORD fflags, filter = 0;
609     OVERLAPPED ov;
610     WCHAR path[MAX_PATH], subdir[MAX_PATH];
611     static const WCHAR szBoo[] = { '\\','b','o','o',0 };
612     static const WCHAR szHoo[] = { '\\','h','o','o',0 };
613     PFILE_NOTIFY_INFORMATION pfni;
614
615     if (!pReadDirectoryChangesW)
616     {
617         win_skip("ReadDirectoryChangesW is not available\n");
618         return;
619     }
620     SetLastError(0xdeadbeef);
621     r = GetTempPathW( MAX_PATH, path );
622     if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
623     {
624         win_skip("GetTempPathW is not implemented\n");
625         return;
626     }
627     ok( r != 0, "temp path failed\n");
628     if (!r)
629         return;
630
631     lstrcatW( path, szBoo );
632     lstrcpyW( subdir, path );
633     lstrcatW( subdir, szHoo );
634
635     RemoveDirectoryW( subdir );
636     RemoveDirectoryW( path );
637     
638     r = CreateDirectoryW(path, NULL);
639     ok( r == TRUE, "failed to create directory\n");
640
641     fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
642     hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY, 
643                         FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, 
644                         OPEN_EXISTING, fflags, NULL);
645     ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
646
647     ov.hEvent = CreateEvent( NULL, 1, 0, NULL );
648
649     filter = FILE_NOTIFY_CHANGE_FILE_NAME;
650     filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
651
652     SetLastError(0xd0b00b00);
653     ov.Internal = 0;
654     ov.InternalHigh = 0;
655     memset( buffer, 0, sizeof buffer );
656
657     r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,filter,NULL,&ov,NULL);
658     ok(r==TRUE, "should return true\n");
659
660     r = WaitForSingleObject( ov.hEvent, 0 );
661     ok( r == STATUS_TIMEOUT, "should timeout\n" );
662
663     r = CreateDirectoryW( subdir, NULL );
664     ok( r == TRUE, "failed to create directory\n");
665
666     r = WaitForSingleObject( ov.hEvent, 0 );
667     ok( r == WAIT_OBJECT_0, "event should be ready\n" );
668
669     ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
670     ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
671
672     ov.Internal = 0;
673     ov.InternalHigh = 0;
674     S(U(ov)).Offset = 0;
675     S(U(ov)).OffsetHigh = 0;
676     memset( buffer, 0, sizeof buffer );
677
678     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
679     ok(r==TRUE, "should return true\n");
680
681     r = WaitForSingleObject( ov.hEvent, 0 );
682     ok( r == STATUS_TIMEOUT, "should timeout\n" );
683
684     r = RemoveDirectoryW( subdir );
685     ok( r == TRUE, "failed to remove directory\n");
686
687     r = WaitForSingleObject( ov.hEvent, 1000 );
688     ok( r == WAIT_OBJECT_0, "should be ready\n" );
689
690     ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
691     ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
692
693     pfni = (PFILE_NOTIFY_INFORMATION) buffer;
694     ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
695
696     CloseHandle(hdir);
697
698     r = RemoveDirectoryW( path );
699     ok( r == TRUE, "failed to remove directory\n");
700 }
701
702 static void test_readdirectorychanges_filedir(void)
703 {
704     NTSTATUS r;
705     HANDLE hdir, hfile;
706     char buffer[0x1000];
707     DWORD fflags, filter = 0;
708     OVERLAPPED ov;
709     WCHAR path[MAX_PATH], subdir[MAX_PATH], file[MAX_PATH];
710     static const WCHAR szBoo[] = { '\\','b','o','o',0 };
711     static const WCHAR szHoo[] = { '\\','h','o','o',0 };
712     static const WCHAR szFoo[] = { '\\','f','o','o',0 };
713     PFILE_NOTIFY_INFORMATION pfni;
714
715     SetLastError(0xdeadbeef);
716     r = GetTempPathW( MAX_PATH, path );
717     if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
718     {
719         win_skip("GetTempPathW is not implemented\n");
720         return;
721     }
722     ok( r != 0, "temp path failed\n");
723     if (!r)
724         return;
725
726     lstrcatW( path, szBoo );
727     lstrcpyW( subdir, path );
728     lstrcatW( subdir, szHoo );
729
730     lstrcpyW( file, path );
731     lstrcatW( file, szFoo );
732
733     DeleteFileW( file );
734     RemoveDirectoryW( subdir );
735     RemoveDirectoryW( path );
736     
737     r = CreateDirectoryW(path, NULL);
738     ok( r == TRUE, "failed to create directory\n");
739
740     fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
741     hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY, 
742                         FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, 
743                         OPEN_EXISTING, fflags, NULL);
744     ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
745
746     ov.hEvent = CreateEvent( NULL, 0, 0, NULL );
747
748     filter = FILE_NOTIFY_CHANGE_FILE_NAME;
749
750     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
751     ok(r==TRUE, "should return true\n");
752
753     r = WaitForSingleObject( ov.hEvent, 10 );
754     ok( r == WAIT_TIMEOUT, "should timeout\n" );
755
756     r = CreateDirectoryW( subdir, NULL );
757     ok( r == TRUE, "failed to create directory\n");
758
759     hfile = CreateFileW( file, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL );
760     ok( hfile != INVALID_HANDLE_VALUE, "failed to create file\n");
761     ok( CloseHandle(hfile), "failed toc lose file\n");
762
763     r = WaitForSingleObject( ov.hEvent, 1000 );
764     ok( r == WAIT_OBJECT_0, "event should be ready\n" );
765
766     ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
767     ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
768
769     pfni = (PFILE_NOTIFY_INFORMATION) buffer;
770     ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
771     ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
772     ok( pfni->FileNameLength == 6, "len wrong\n" );
773     ok( !memcmp(pfni->FileName,&szFoo[1],6), "name wrong\n" );
774
775     r = DeleteFileW( file );
776     ok( r == TRUE, "failed to delete file\n");
777
778     r = RemoveDirectoryW( subdir );
779     ok( r == TRUE, "failed to remove directory\n");
780
781     CloseHandle(hdir);
782
783     r = RemoveDirectoryW( path );
784     ok( r == TRUE, "failed to remove directory\n");
785 }
786
787 static void CALLBACK readdirectorychanges_cr(DWORD error, DWORD len, LPOVERLAPPED ov)
788 {
789     ok(error == 0, "ReadDirectoryChangesW error %d\n", error);
790     ok(ov->hEvent == (void*)0xdeadbeef, "hEvent should not have changed\n");
791 }
792
793 static void test_readdirectorychanges_cr(void)
794 {
795     static const WCHAR szBoo[] = { '\\','b','o','o','\\',0 };
796     static const WCHAR szDir[] = { 'd','i','r',0 };
797     static const WCHAR szFile[] = { 'f','i','l','e',0 };
798     static const WCHAR szBackslash[] = { '\\',0 };
799
800     WCHAR path[MAX_PATH], file[MAX_PATH], dir[MAX_PATH], sub_file[MAX_PATH];
801     FILE_NOTIFY_INFORMATION fni[1024], *fni_next;
802     OVERLAPPED ov;
803     HANDLE hdir, hfile;
804     NTSTATUS r;
805
806     if (!pReadDirectoryChangesW)
807     {
808         win_skip("ReadDirectoryChangesW is not available\n");
809         return;
810     }
811
812     SetLastError(0xdeadbeef);
813     r = GetTempPathW(MAX_PATH, path);
814     if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
815     {
816         win_skip("GetTempPathW is not implemented\n");
817         return;
818     }
819     ok(r != 0, "temp path failed\n");
820     if (!r)
821         return;
822
823     lstrcatW(path, szBoo);
824     lstrcpyW(dir, path);
825     lstrcatW(dir, szDir);
826     lstrcpyW(file, path);
827     lstrcatW(file, szFile);
828     lstrcpyW(sub_file, dir);
829     lstrcatW(sub_file, szBackslash);
830     lstrcatW(sub_file, szFile);
831
832     DeleteFileW(file);
833     RemoveDirectoryW(dir);
834     RemoveDirectoryW(path);
835
836     r = CreateDirectoryW(path, NULL);
837     ok(r == TRUE, "failed to create directory\n");
838
839     hdir = CreateFileW(path, GENERIC_READ,
840             FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
841             FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
842     ok(hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
843
844     memset(&ov, 0, sizeof(ov));
845     ov.hEvent = (void*)0xdeadbeef;
846     r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
847             FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr);
848     ok(r == TRUE, "pReadDirectoryChangesW failed\n");
849
850     hfile = CreateFileW(file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
851     ok(hfile != INVALID_HANDLE_VALUE, "failed to create file\n");
852     CloseHandle(hfile);
853
854     r = SleepEx(1000, TRUE);
855     ok(r != 0, "failed to receive file creation event\n");
856     ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n");
857     ok(fni->Action == FILE_ACTION_ADDED, "Action = %d\n", fni->Action);
858     ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
859             "FileNameLength = %d\n", fni->FileNameLength);
860     ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
861             "FileName = %s\n", wine_dbgstr_w(fni->FileName));
862
863     r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
864             FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr);
865     ok(r == TRUE, "pReadDirectoryChangesW failed\n");
866
867     /* This event will not be reported */
868     r = CreateDirectoryW(dir, NULL);
869     ok(r == TRUE, "failed to create directory\n");
870
871     r = MoveFileW(file, sub_file);
872     ok(r == TRUE, "failed to move file\n");
873
874     r = SleepEx(1000, TRUE);
875     ok(r != 0, "failed to receive file move event\n");
876     ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n");
877     ok(fni->Action == FILE_ACTION_REMOVED, "Action = %d\n", fni->Action);
878     ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
879             "FileNameLength = %d\n", fni->FileNameLength);
880     ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
881             "FileName = %s\n", wine_dbgstr_w(fni->FileName));
882
883     r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
884             FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr);
885     ok(r == TRUE, "pReadDirectoryChangesW failed\n");
886
887     r = MoveFileW(sub_file, file);
888     ok(r == TRUE, "failed to move file\n");
889
890     r = SleepEx(1000, TRUE);
891     ok(r != 0, "failed to receive file move event\n");
892     ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n");
893     ok(fni->Action == FILE_ACTION_ADDED, "Action = %d\n", fni->Action);
894     ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
895             "FileNameLength = %d\n", fni->FileNameLength);
896     ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
897             "FileName = %s\n", wine_dbgstr_w(fni->FileName));
898
899     r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
900             FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr);
901     ok(r == TRUE, "pReadDirectoryChangesW failed\n");
902
903     r = DeleteFileW(file);
904     ok(r == TRUE, "failed to delete file\n");
905
906     r = SleepEx(1000, TRUE);
907     ok(r != 0, "failed to receive file removal event\n");
908     ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n");
909     ok(fni->Action == FILE_ACTION_REMOVED, "Action = %d\n", fni->Action);
910     ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
911             "FileNameLength = %d\n", fni->FileNameLength);
912     ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
913             "FileName = %s\n", wine_dbgstr_w(fni->FileName));
914
915     CloseHandle(hdir);
916
917     hdir = CreateFileW(path, GENERIC_READ,
918             FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
919             FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
920     ok(hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
921
922     r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
923             FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr);
924     ok(r == TRUE, "pReadDirectoryChangesW failed\n");
925
926     r = MoveFileW(dir, file);
927     ok(r == TRUE, "failed to move directory\n");
928
929     r = SleepEx(1000, TRUE);
930     ok(r != 0, "failed to receive directory move event\n");
931     if (fni->Action == FILE_ACTION_RENAMED_OLD_NAME)
932     {
933         ok(fni->Action == FILE_ACTION_RENAMED_OLD_NAME, "Action = %d\n", fni->Action);
934         ok(fni->FileNameLength == lstrlenW(szDir)*sizeof(WCHAR),
935                 "FileNameLength = %d\n", fni->FileNameLength);
936         ok(!memcmp(fni->FileName, szDir, lstrlenW(szDir)*sizeof(WCHAR)),
937                 "FileName = %s\n", wine_dbgstr_w(fni->FileName));
938         ok(fni->NextEntryOffset != 0, "no next entry in movement event\n");
939         fni_next = (FILE_NOTIFY_INFORMATION*)((char*)fni+fni->NextEntryOffset);
940         ok(fni_next->NextEntryOffset == 0, "there should be no more events in buffer\n");
941         ok(fni_next->Action == FILE_ACTION_RENAMED_NEW_NAME, "Action = %d\n", fni_next->Action);
942         ok(fni_next->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
943                 "FileNameLength = %d\n", fni_next->FileNameLength);
944         ok(!memcmp(fni_next->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
945                 "FileName = %s\n", wine_dbgstr_w(fni_next->FileName));
946     }
947     else
948     {
949         todo_wine ok(0, "Expected rename event\n");
950
951         if (fni->NextEntryOffset == 0)
952         {
953             r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
954                     FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr);
955             ok(r == TRUE, "pReadDirectoryChangesW failed\n");
956
957             r = SleepEx(1000, TRUE);
958             ok(r != 0, "failed to receive directory move event\n");
959         }
960     }
961
962     r = CreateDirectoryW(dir, NULL);
963     ok(r == TRUE, "failed to create directory\n");
964
965     r = RemoveDirectoryW(dir);
966     ok(r == TRUE, "failed to remove directory\n");
967
968     r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
969             FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr);
970     ok(r == TRUE, "pReadDirectoryChangesW failed\n");
971
972     r = SleepEx(1000, TRUE);
973     ok(r != 0, "failed to receive directory creation event\n");
974     ok(fni->Action == FILE_ACTION_ADDED, "Action = %d\n", fni->Action);
975     ok(fni->FileNameLength == lstrlenW(szDir)*sizeof(WCHAR),
976             "FileNameLength = %d\n", fni->FileNameLength);
977     ok(!memcmp(fni->FileName, szDir, lstrlenW(szDir)*sizeof(WCHAR)),
978             "FileName = %s\n", wine_dbgstr_w(fni->FileName));
979     if (fni->NextEntryOffset)
980         fni_next = (FILE_NOTIFY_INFORMATION*)((char*)fni+fni->NextEntryOffset);
981     else
982     {
983         r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
984                 FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr);
985         ok(r == TRUE, "pReadDirectoryChangesW failed\n");
986
987         r = SleepEx(1000, TRUE);
988         ok(r != 0, "failed to receive directory removal event\n");
989         fni_next = fni;
990     }
991     ok(fni_next->NextEntryOffset == 0, "there should be no more events in buffer\n");
992     ok(fni_next->Action == FILE_ACTION_REMOVED, "Action = %d\n", fni_next->Action);
993     ok(fni_next->FileNameLength == lstrlenW(szDir)*sizeof(WCHAR),
994             "FileNameLength = %d\n", fni_next->FileNameLength);
995     ok(!memcmp(fni_next->FileName, szDir, lstrlenW(szDir)*sizeof(WCHAR)),
996             "FileName = %s\n", wine_dbgstr_w(fni_next->FileName));
997
998     CloseHandle(hdir);
999     RemoveDirectoryW(file);
1000     RemoveDirectoryW(path);
1001 }
1002
1003 static void test_ffcn_directory_overlap(void)
1004 {
1005     HANDLE parent_watch, child_watch, parent_thread, child_thread;
1006     char workdir[MAX_PATH], parentdir[MAX_PATH], childdir[MAX_PATH];
1007     char tempfile[MAX_PATH];
1008     DWORD threadId;
1009     BOOL ret;
1010
1011     /* Setup directory hierarchy */
1012     ret = GetTempPathA(MAX_PATH, workdir);
1013     ok((ret > 0) && (ret <= MAX_PATH),
1014        "GetTempPathA error: %d\n", GetLastError());
1015
1016     ret = GetTempFileNameA(workdir, "fcn", 0, tempfile);
1017     ok(ret, "GetTempFileNameA error: %d\n", GetLastError());
1018     ret = DeleteFileA(tempfile);
1019     ok(ret, "DeleteFileA error: %d\n", GetLastError());
1020
1021     lstrcpyA(parentdir, tempfile);
1022     ret = CreateDirectoryA(parentdir, NULL);
1023     ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
1024
1025     lstrcpyA(childdir, parentdir);
1026     lstrcatA(childdir, "\\c");
1027     ret = CreateDirectoryA(childdir, NULL);
1028     ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
1029
1030
1031     /* When recursively watching overlapping directories, changes in child
1032      * should trigger notifications for both child and parent */
1033     parent_thread = StartNotificationThread(parentdir, TRUE,
1034                                             FILE_NOTIFY_CHANGE_FILE_NAME);
1035     child_thread = StartNotificationThread(childdir, TRUE,
1036                                             FILE_NOTIFY_CHANGE_FILE_NAME);
1037
1038     /* Create a file in child */
1039     ret = GetTempFileNameA(childdir, "fcn", 0, tempfile);
1040     ok(ret, "GetTempFileNameA error: %d\n", GetLastError());
1041
1042     /* Both watches should trigger */
1043     ret = FinishNotificationThread(parent_thread);
1044     ok(ret, "Missed parent notification\n");
1045     ret = FinishNotificationThread(child_thread);
1046     ok(ret, "Missed child notification\n");
1047
1048     ret = DeleteFileA(tempfile);
1049     ok(ret, "DeleteFileA error: %d\n", GetLastError());
1050
1051
1052     /* Removing a recursive parent watch should not affect child watches. Doing
1053      * so used to crash wineserver. */
1054     parent_watch = FindFirstChangeNotificationA(parentdir, TRUE,
1055                                                 FILE_NOTIFY_CHANGE_FILE_NAME);
1056     ok(parent_watch != INVALID_HANDLE_VALUE,
1057        "FindFirstChangeNotification error: %d\n", GetLastError());
1058     child_watch = FindFirstChangeNotificationA(childdir, TRUE,
1059                                                FILE_NOTIFY_CHANGE_FILE_NAME);
1060     ok(child_watch != INVALID_HANDLE_VALUE,
1061        "FindFirstChangeNotification error: %d\n", GetLastError());
1062
1063     ret = FindCloseChangeNotification(parent_watch);
1064     ok(ret, "FindCloseChangeNotification error: %d\n", GetLastError());
1065
1066     child_thread = CreateThread(NULL, 0, NotificationThread, child_watch, 0,
1067                                 &threadId);
1068     ok(child_thread != NULL, "CreateThread error: %d\n", GetLastError());
1069
1070     /* Create a file in child */
1071     ret = GetTempFileNameA(childdir, "fcn", 0, tempfile);
1072     ok(ret, "GetTempFileNameA error: %d\n", GetLastError());
1073
1074     /* Child watch should trigger */
1075     ret = FinishNotificationThread(child_thread);
1076     ok(ret, "Missed child notification\n");
1077
1078     /* clean up */
1079     ret = DeleteFileA(tempfile);
1080     ok(ret, "DeleteFileA error: %d\n", GetLastError());
1081
1082     ret = RemoveDirectoryA(childdir);
1083     ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
1084
1085     ret = RemoveDirectoryA(parentdir);
1086     ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
1087 }
1088
1089 START_TEST(change)
1090 {
1091     HMODULE hkernel32 = GetModuleHandle("kernel32");
1092     pReadDirectoryChangesW = (void *)GetProcAddress(hkernel32, "ReadDirectoryChangesW");
1093
1094     test_ffcnMultipleThreads();
1095     /* The above function runs a test that must occur before FindCloseChangeNotification is run in the
1096        current thread to preserve the emptiness of the wine user APC queue. To ensure this it should be
1097        placed first. */
1098     test_FindFirstChangeNotification();
1099     test_ffcn();
1100     test_readdirectorychanges();
1101     test_readdirectorychanges_null();
1102     test_readdirectorychanges_filedir();
1103     test_readdirectorychanges_cr();
1104     test_ffcn_directory_overlap();
1105 }