Spelling fixes.
[wine.git] / dlls / kernel32 / tests / change.c
blob7c90ccd3b81c84341d967a953277712cd62923bd
1 /*
2 * Tests for file change notification functions
4 * Copyright (c) 2004 Hans Leidekker
5 * Copyright 2006 Mike McCormack for CodeWeavers
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.
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.
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
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
29 #include <stdarg.h>
30 #include <stdio.h>
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>
39 static DWORD CALLBACK NotificationThread(LPVOID arg)
41 HANDLE change = (HANDLE) arg;
42 BOOL notified = FALSE;
43 BOOL ret = FALSE;
44 DWORD status;
46 status = WaitForSingleObject(change, 100);
48 if (status == WAIT_OBJECT_0 ) {
49 notified = TRUE;
50 ret = FindNextChangeNotification(change);
53 ret = FindCloseChangeNotification(change);
54 ok( ret, "FindCloseChangeNotification error: %d\n",
55 GetLastError());
57 ExitThread((DWORD)notified);
60 static HANDLE StartNotificationThread(LPCSTR path, BOOL subtree, DWORD flags)
62 HANDLE change, thread;
63 DWORD threadId;
65 change = FindFirstChangeNotificationA(path, subtree, flags);
66 ok(change != INVALID_HANDLE_VALUE, "FindFirstChangeNotification error: %d\n", GetLastError());
68 thread = CreateThread(NULL, 0, NotificationThread, (LPVOID)change,
69 0, &threadId);
70 ok(thread != NULL, "CreateThread error: %d\n", GetLastError());
72 return thread;
75 static DWORD FinishNotificationThread(HANDLE thread)
77 DWORD status, exitcode;
79 status = WaitForSingleObject(thread, 5000);
80 ok(status == WAIT_OBJECT_0, "WaitForSingleObject status %d error %d\n", status, GetLastError());
82 ok(GetExitCodeThread(thread, &exitcode), "Could not retrieve thread exit code\n");
84 return exitcode;
87 static void test_FindFirstChangeNotification(void)
89 HANDLE change, file, thread;
90 DWORD attributes, count;
91 BOOL ret;
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];
98 /* pathetic checks */
100 change = FindFirstChangeNotificationA("not-a-file", FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
101 ok(change == INVALID_HANDLE_VALUE && GetLastError() == ERROR_FILE_NOT_FOUND,
102 "FindFirstChangeNotification error: %d\n", GetLastError());
104 if (0) /* This documents win2k behavior. It crashes on win98. */
106 change = FindFirstChangeNotificationA(NULL, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
107 ok(change == NULL && GetLastError() == ERROR_PATH_NOT_FOUND,
108 "FindFirstChangeNotification error: %d\n", GetLastError());
111 ret = FindNextChangeNotification(NULL);
112 ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindNextChangeNotification error: %d\n",
113 GetLastError());
115 ret = FindCloseChangeNotification(NULL);
116 ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindCloseChangeNotification error: %d\n",
117 GetLastError());
119 ret = GetTempPathA(MAX_PATH, workdir);
120 ok(ret, "GetTempPathA error: %d\n", GetLastError());
122 lstrcatA(workdir, "testFileChangeNotification");
124 ret = CreateDirectoryA(workdir, NULL);
125 ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
127 ret = GetTempFileNameA(workdir, prefix, 0, filename1);
128 ok(ret, "GetTempFileNameA error: %d\n", GetLastError());
130 file = CreateFileA(filename1, GENERIC_WRITE|GENERIC_READ, 0, NULL, CREATE_ALWAYS,
131 FILE_ATTRIBUTE_NORMAL, 0);
132 ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %d\n", GetLastError());
133 ret = CloseHandle(file);
134 ok( ret, "CloseHandle error: %d\n", GetLastError());
136 /* Try to register notification for a file. win98 and win2k behave differently here */
137 change = FindFirstChangeNotificationA(filename1, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
138 ok(change == INVALID_HANDLE_VALUE && (GetLastError() == ERROR_DIRECTORY ||
139 GetLastError() == ERROR_FILE_NOT_FOUND),
140 "FindFirstChangeNotification error: %d\n", GetLastError());
142 lstrcpyA(dirname1, filename1);
143 lstrcatA(dirname1, "dir");
145 lstrcpyA(dirname2, dirname1);
146 lstrcatA(dirname2, "new");
148 ret = CreateDirectoryA(dirname1, NULL);
149 ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
151 /* What if we move the directory we registered notification for? */
152 thread = StartNotificationThread(dirname1, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
153 ret = MoveFileA(dirname1, dirname2);
154 ok(ret, "MoveFileA error: %d\n", GetLastError());
155 ret = FinishNotificationThread(thread);
156 ok(!ret, "Unexpected notification\n");
158 /* What if we remove the directory we registered notification for? */
159 thread = StartNotificationThread(dirname2, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
160 ret = RemoveDirectoryA(dirname2);
161 ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
163 /* win98 and win2k behave differently here */
164 ret = FinishNotificationThread(thread);
165 ok(ret || !ret, "You'll never read this\n");
167 /* functional checks */
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");
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");
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");
187 lstrcpyA(filename2, filename1);
188 lstrcatA(filename2, "new");
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");
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");
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");
211 attributes = GetFileAttributesA(filename2);
212 ok(attributes != INVALID_FILE_ATTRIBUTES, "GetFileAttributesA error: %d\n", GetLastError());
213 attributes &= FILE_ATTRIBUTE_READONLY;
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");
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 ret = WriteFile(file, buffer, sizeof(buffer), &count, NULL);
227 ok(ret && count == sizeof(buffer), "WriteFile error: %d\n", GetLastError());
228 ret = CloseHandle(file);
229 ok( ret, "CloseHandle error: %d\n", GetLastError());
230 ok(FinishNotificationThread(thread), "Missed notification\n");
232 /* Change file size by truncating a file */
233 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_SIZE);
234 file = CreateFileA(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
235 FILE_ATTRIBUTE_NORMAL, 0);
236 ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %d\n", GetLastError());
237 ret = WriteFile(file, buffer, sizeof(buffer) / 2, &count, NULL);
238 ok(ret && count == sizeof(buffer) / 2, "WriteFileA error: %d\n", GetLastError());
239 ret = CloseHandle(file);
240 ok( ret, "CloseHandle error: %d\n", GetLastError());
241 ok(FinishNotificationThread(thread), "Missed notification\n");
243 /* clean up */
245 ret = DeleteFileA(filename2);
246 ok(ret, "DeleteFileA error: %d\n", GetLastError());
248 ret = RemoveDirectoryA(workdir);
249 ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
252 /* this test concentrates more on the wait behaviour of the handle */
253 static void test_ffcn(void)
255 DWORD filter;
256 HANDLE handle;
257 LONG r;
258 WCHAR path[MAX_PATH], subdir[MAX_PATH];
259 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
260 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
262 SetLastError(0xdeadbeef);
263 r = GetTempPathW( MAX_PATH, path );
264 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
266 skip("GetTempPathW is not implemented\n");
267 return;
269 ok( r != 0, "temp path failed\n");
270 if (!r)
271 return;
273 lstrcatW( path, szBoo );
274 lstrcpyW( subdir, path );
275 lstrcatW( subdir, szHoo );
277 RemoveDirectoryW( subdir );
278 RemoveDirectoryW( path );
280 r = CreateDirectoryW(path, NULL);
281 ok( r == TRUE, "failed to create directory\n");
283 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
284 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
286 handle = FindFirstChangeNotificationW( path, 1, filter);
287 ok( handle != INVALID_HANDLE_VALUE, "invalid handle\n");
289 r = WaitForSingleObject( handle, 0 );
290 ok( r == STATUS_TIMEOUT, "should time out\n");
292 r = CreateDirectoryW( subdir, NULL );
293 ok( r == TRUE, "failed to create subdir\n");
295 r = WaitForSingleObject( handle, 0 );
296 ok( r == WAIT_OBJECT_0, "should be ready\n");
298 r = WaitForSingleObject( handle, 0 );
299 ok( r == WAIT_OBJECT_0, "should be ready\n");
301 r = FindNextChangeNotification(handle);
302 ok( r == TRUE, "find next failed\n");
304 r = WaitForSingleObject( handle, 0 );
305 ok( r == STATUS_TIMEOUT, "should time out\n");
307 r = RemoveDirectoryW( subdir );
308 ok( r == TRUE, "failed to remove subdir\n");
310 r = WaitForSingleObject( handle, 0 );
311 ok( r == WAIT_OBJECT_0, "should be ready\n");
313 r = WaitForSingleObject( handle, 0 );
314 ok( r == WAIT_OBJECT_0, "should be ready\n");
316 r = FindNextChangeNotification(handle);
317 ok( r == TRUE, "find next failed\n");
319 r = FindNextChangeNotification(handle);
320 ok( r == TRUE, "find next failed\n");
322 r = FindCloseChangeNotification(handle);
323 ok( r == TRUE, "should succeed\n");
325 r = RemoveDirectoryW( path );
326 ok( r == TRUE, "failed to remove dir\n");
329 /* this test concentrates on the wait behavior when multiple threads are
330 * waiting on a change notification handle. */
331 static void test_ffcnMultipleThreads(void)
333 LONG r;
334 DWORD filter, threadId, status, exitcode;
335 HANDLE handles[2];
336 char path[MAX_PATH];
338 r = GetTempPathA(MAX_PATH, path);
339 ok(r, "GetTempPathA error: %d\n", GetLastError());
341 lstrcatA(path, "ffcnTestMultipleThreads");
343 RemoveDirectoryA(path);
345 r = CreateDirectoryA(path, NULL);
346 ok(r, "CreateDirectoryA error: %d\n", GetLastError());
348 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
349 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
351 handles[0] = FindFirstChangeNotificationA(path, FALSE, filter);
352 ok(handles[0] != INVALID_HANDLE_VALUE, "FindFirstChangeNotification error: %d\n", GetLastError());
354 /* Test behavior if a waiting thread holds the last reference to a change
355 * directory object with an empty wine user APC queue for this thread (bug #7286) */
357 /* Create our notification thread */
358 handles[1] = CreateThread(NULL, 0, NotificationThread, (LPVOID)handles[0],
359 0, &threadId);
360 ok(handles[1] != NULL, "CreateThread error: %d\n", GetLastError());
362 status = WaitForMultipleObjects(2, handles, FALSE, 5000);
363 ok(status == WAIT_OBJECT_0 || status == WAIT_OBJECT_0+1, "WaitForMultipleObjects status %d error %d\n", status, GetLastError());
364 ok(GetExitCodeThread(handles[1], &exitcode), "Could not retrieve thread exit code\n");
366 /* Clean up */
367 r = RemoveDirectoryA( path );
368 ok( r == TRUE, "failed to remove dir\n");
371 typedef BOOL (WINAPI *fnReadDirectoryChangesW)(HANDLE,LPVOID,DWORD,BOOL,DWORD,
372 LPDWORD,LPOVERLAPPED,LPOVERLAPPED_COMPLETION_ROUTINE);
373 fnReadDirectoryChangesW pReadDirectoryChangesW;
375 static void test_readdirectorychanges(void)
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;
387 if (!pReadDirectoryChangesW)
389 skip("ReadDirectoryChangesW is not available\n");
390 return;
393 SetLastError(0xdeadbeef);
394 r = GetTempPathW( MAX_PATH, path );
395 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
397 skip("GetTempPathW is not implemented\n");
398 return;
400 ok( r != 0, "temp path failed\n");
401 if (!r)
402 return;
404 lstrcatW( path, szBoo );
405 lstrcpyW( subdir, path );
406 lstrcatW( subdir, szHoo );
408 lstrcpyW( subsubdir, path );
409 lstrcatW( subsubdir, szGa );
411 RemoveDirectoryW( subsubdir );
412 RemoveDirectoryW( subdir );
413 RemoveDirectoryW( path );
415 r = CreateDirectoryW(path, NULL);
416 ok( r == TRUE, "failed to create directory\n");
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");
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");
429 ov.hEvent = CreateEvent( NULL, 1, 0, NULL );
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");
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");
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;
450 SetLastError(0xd0b00b00);
451 ov.Internal = 0;
452 ov.InternalHigh = 0;
453 memset( buffer, 0, sizeof buffer );
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");
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");
463 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
464 ok(r==TRUE, "should return true\n");
466 r = WaitForSingleObject( ov.hEvent, 10 );
467 ok( r == STATUS_TIMEOUT, "should timeout\n" );
469 r = CreateDirectoryW( subdir, NULL );
470 ok( r == TRUE, "failed to create directory\n");
472 r = WaitForSingleObject( ov.hEvent, 1000 );
473 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
475 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
476 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
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" );
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");
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");
494 filter = FILE_NOTIFY_CHANGE_SIZE;
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");
505 ok( ov.Internal == STATUS_PENDING, "ov.Internal wrong\n");
506 ok( ov.InternalHigh == 1, "ov.InternalHigh wrong\n");
508 r = WaitForSingleObject( ov.hEvent, 0 );
509 ok( r == STATUS_TIMEOUT, "should timeout\n" );
511 r = RemoveDirectoryW( subdir );
512 ok( r == TRUE, "failed to remove directory\n");
514 r = WaitForSingleObject( ov.hEvent, 1000 );
515 ok( r == WAIT_OBJECT_0, "should be ready\n" );
517 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
518 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
520 if (ov.Internal == STATUS_SUCCESS)
522 r = GetOverlappedResult( hdir, &ov, &dwCount, TRUE );
523 ok( r == TRUE, "getoverlappedresult failed\n");
524 ok( dwCount == 0x12, "count wrong\n");
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" );
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");
537 r = CreateDirectoryW( subdir, NULL );
538 ok( r == TRUE, "failed to create directory\n");
540 r = WaitForSingleObject( ov.hEvent, 1000 );
541 ok( r == WAIT_OBJECT_0, "should be ready\n" );
543 ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
544 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
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");
550 r = CreateDirectoryW( subsubdir, NULL );
551 ok( r == TRUE, "failed to create directory\n");
553 r = WaitForSingleObject( ov.hEvent, 1000 );
554 ok( r == WAIT_OBJECT_0, "should be ready\n" );
556 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
557 ok( ov.InternalHigh == 0x18, "ov.InternalHigh wrong\n");
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 == 0x0c, "len wrong\n" );
563 ok( !memcmp(pfni->FileName,&szGa[1],6), "name wrong\n" );
565 r = RemoveDirectoryW( subsubdir );
566 ok( r == TRUE, "failed to remove directory\n");
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");
573 r = RemoveDirectoryW( subdir );
574 ok( r == TRUE, "failed to remove directory\n");
576 r = WaitForSingleObject( ov.hEvent, 1000 );
577 ok( r == WAIT_OBJECT_0, "should be ready\n" );
579 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
580 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
581 ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong\n" );
582 ok( pfni->FileNameLength == 0x0c, "len wrong\n" );
583 ok( !memcmp(pfni->FileName,&szGa[1],6), "name wrong\n" );
585 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
586 ok( ov.InternalHigh == 0x18, "ov.InternalHigh wrong\n");
588 CloseHandle(hdir);
590 r = RemoveDirectoryW( path );
591 ok( r == TRUE, "failed to remove directory\n");
594 /* show the behaviour when a null buffer is passed */
595 static void test_readdirectorychanges_null(void)
597 NTSTATUS r;
598 HANDLE hdir;
599 char buffer[0x1000];
600 DWORD fflags, filter = 0;
601 OVERLAPPED ov;
602 WCHAR path[MAX_PATH], subdir[MAX_PATH];
603 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
604 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
605 PFILE_NOTIFY_INFORMATION pfni;
607 if (!pReadDirectoryChangesW)
609 skip("ReadDirectoryChangesW is not available\n");
610 return;
612 SetLastError(0xdeadbeef);
613 r = GetTempPathW( MAX_PATH, path );
614 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
616 skip("GetTempPathW is not implemented\n");
617 return;
619 ok( r != 0, "temp path failed\n");
620 if (!r)
621 return;
623 lstrcatW( path, szBoo );
624 lstrcpyW( subdir, path );
625 lstrcatW( subdir, szHoo );
627 RemoveDirectoryW( subdir );
628 RemoveDirectoryW( path );
630 r = CreateDirectoryW(path, NULL);
631 ok( r == TRUE, "failed to create directory\n");
633 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
634 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
635 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
636 OPEN_EXISTING, fflags, NULL);
637 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
639 ov.hEvent = CreateEvent( NULL, 1, 0, NULL );
641 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
642 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
644 SetLastError(0xd0b00b00);
645 ov.Internal = 0;
646 ov.InternalHigh = 0;
647 memset( buffer, 0, sizeof buffer );
649 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,filter,NULL,&ov,NULL);
650 ok(r==TRUE, "should return true\n");
652 r = WaitForSingleObject( ov.hEvent, 0 );
653 ok( r == STATUS_TIMEOUT, "should timeout\n" );
655 r = CreateDirectoryW( subdir, NULL );
656 ok( r == TRUE, "failed to create directory\n");
658 r = WaitForSingleObject( ov.hEvent, 0 );
659 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
661 ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
662 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
664 ov.Internal = 0;
665 ov.InternalHigh = 0;
666 S(U(ov)).Offset = 0;
667 S(U(ov)).OffsetHigh = 0;
668 memset( buffer, 0, sizeof buffer );
670 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
671 ok(r==TRUE, "should return true\n");
673 r = WaitForSingleObject( ov.hEvent, 0 );
674 ok( r == STATUS_TIMEOUT, "should timeout\n" );
676 r = RemoveDirectoryW( subdir );
677 ok( r == TRUE, "failed to remove directory\n");
679 r = WaitForSingleObject( ov.hEvent, 1000 );
680 ok( r == WAIT_OBJECT_0, "should be ready\n" );
682 ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
683 ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
685 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
686 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
688 CloseHandle(hdir);
690 r = RemoveDirectoryW( path );
691 ok( r == TRUE, "failed to remove directory\n");
694 static void test_readdirectorychanges_filedir(void)
696 NTSTATUS r;
697 HANDLE hdir, hfile;
698 char buffer[0x1000];
699 DWORD fflags, filter = 0;
700 OVERLAPPED ov;
701 WCHAR path[MAX_PATH], subdir[MAX_PATH], file[MAX_PATH];
702 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
703 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
704 static const WCHAR szFoo[] = { '\\','f','o','o',0 };
705 PFILE_NOTIFY_INFORMATION pfni;
707 SetLastError(0xdeadbeef);
708 r = GetTempPathW( MAX_PATH, path );
709 if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
711 skip("GetTempPathW is not implemented\n");
712 return;
714 ok( r != 0, "temp path failed\n");
715 if (!r)
716 return;
718 lstrcatW( path, szBoo );
719 lstrcpyW( subdir, path );
720 lstrcatW( subdir, szHoo );
722 lstrcpyW( file, path );
723 lstrcatW( file, szFoo );
725 DeleteFileW( file );
726 RemoveDirectoryW( subdir );
727 RemoveDirectoryW( path );
729 r = CreateDirectoryW(path, NULL);
730 ok( r == TRUE, "failed to create directory\n");
732 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
733 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
734 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
735 OPEN_EXISTING, fflags, NULL);
736 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
738 ov.hEvent = CreateEvent( NULL, 0, 0, NULL );
740 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
742 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
743 ok(r==TRUE, "should return true\n");
745 r = WaitForSingleObject( ov.hEvent, 10 );
746 ok( r == WAIT_TIMEOUT, "should timeout\n" );
748 r = CreateDirectoryW( subdir, NULL );
749 ok( r == TRUE, "failed to create directory\n");
751 hfile = CreateFileW( file, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL );
752 ok( hfile != INVALID_HANDLE_VALUE, "failed to create file\n");
753 ok( CloseHandle(hfile), "failed toc lose file\n");
755 r = WaitForSingleObject( ov.hEvent, 1000 );
756 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
758 ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
759 ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
761 pfni = (PFILE_NOTIFY_INFORMATION) buffer;
762 ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
763 ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
764 ok( pfni->FileNameLength == 6, "len wrong\n" );
765 ok( !memcmp(pfni->FileName,&szFoo[1],6), "name wrong\n" );
767 r = DeleteFileW( file );
768 ok( r == TRUE, "failed to delete file\n");
770 r = RemoveDirectoryW( subdir );
771 ok( r == TRUE, "failed to remove directory\n");
773 CloseHandle(hdir);
775 r = RemoveDirectoryW( path );
776 ok( r == TRUE, "failed to remove directory\n");
779 static void test_ffcn_directory_overlap(void)
781 HANDLE parent_watch, child_watch, parent_thread, child_thread;
782 char workdir[MAX_PATH], parentdir[MAX_PATH], childdir[MAX_PATH];
783 char tempfile[MAX_PATH];
784 DWORD threadId;
785 BOOL ret;
787 /* Setup directory hierarchy */
788 ret = GetTempPathA(MAX_PATH, workdir);
789 ok((ret > 0) && (ret <= MAX_PATH),
790 "GetTempPathA error: %d\n", GetLastError());
792 ret = GetTempFileNameA(workdir, "fcn", 0, tempfile);
793 ok(ret, "GetTempFileNameA error: %d\n", GetLastError());
794 ret = DeleteFileA(tempfile);
795 ok(ret, "DeleteFileA error: %d\n", GetLastError());
797 lstrcpyA(parentdir, tempfile);
798 ret = CreateDirectoryA(parentdir, NULL);
799 ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
801 lstrcpyA(childdir, parentdir);
802 lstrcatA(childdir, "\\c");
803 ret = CreateDirectoryA(childdir, NULL);
804 ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
807 /* When recursively watching overlapping directories, changes in child
808 * should trigger notifications for both child and parent */
809 parent_thread = StartNotificationThread(parentdir, TRUE,
810 FILE_NOTIFY_CHANGE_FILE_NAME);
811 child_thread = StartNotificationThread(childdir, TRUE,
812 FILE_NOTIFY_CHANGE_FILE_NAME);
814 /* Create a file in child */
815 ret = GetTempFileNameA(childdir, "fcn", 0, tempfile);
816 ok(ret, "GetTempFileNameA error: %d\n", GetLastError());
818 /* Both watches should trigger */
819 ret = FinishNotificationThread(parent_thread);
820 ok(ret, "Missed parent notification\n");
821 ret = FinishNotificationThread(child_thread);
822 ok(ret, "Missed child notification\n");
824 ret = DeleteFileA(tempfile);
825 ok(ret, "DeleteFileA error: %d\n", GetLastError());
828 /* Removing a recursive parent watch should not affect child watches. Doing
829 * so used to crash wineserver. */
830 parent_watch = FindFirstChangeNotificationA(parentdir, TRUE,
831 FILE_NOTIFY_CHANGE_FILE_NAME);
832 ok(parent_watch != INVALID_HANDLE_VALUE,
833 "FindFirstChangeNotification error: %d\n", GetLastError());
834 child_watch = FindFirstChangeNotificationA(childdir, TRUE,
835 FILE_NOTIFY_CHANGE_FILE_NAME);
836 ok(child_watch != INVALID_HANDLE_VALUE,
837 "FindFirstChangeNotification error: %d\n", GetLastError());
839 ret = FindCloseChangeNotification(parent_watch);
840 ok(ret, "FindCloseChangeNotification error: %d\n", GetLastError());
842 child_thread = CreateThread(NULL, 0, NotificationThread,
843 (LPVOID)child_watch, 0, &threadId);
844 ok(child_thread != NULL, "CreateThread error: %d\n", GetLastError());
846 /* Create a file in child */
847 ret = GetTempFileNameA(childdir, "fcn", 0, tempfile);
848 ok(ret, "GetTempFileNameA error: %d\n", GetLastError());
850 /* Child watch should trigger */
851 ret = FinishNotificationThread(child_thread);
852 ok(ret, "Missed child notification\n");
854 /* clean up */
855 ret = DeleteFileA(tempfile);
856 ok(ret, "DeleteFileA error: %d\n", GetLastError());
858 ret = RemoveDirectoryA(childdir);
859 ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
861 ret = RemoveDirectoryA(parentdir);
862 ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
865 START_TEST(change)
867 HMODULE hkernel32 = GetModuleHandle("kernel32");
868 pReadDirectoryChangesW = (fnReadDirectoryChangesW)
869 GetProcAddress(hkernel32, "ReadDirectoryChangesW");
871 test_ffcnMultipleThreads();
872 /* The above function runs a test that must occur before FindCloseChangeNotification is run in the
873 current thread to preserve the emptiness of the wine user APC queue. To ensure this it should be
874 placed first. */
875 test_FindFirstChangeNotification();
876 test_ffcn();
877 test_readdirectorychanges();
878 test_readdirectorychanges_null();
879 test_readdirectorychanges_filedir();
880 test_ffcn_directory_overlap();