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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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
32 #include "wine/test.h"
36 static DWORD CALLBACK NotificationThread(LPVOID arg)
38 HANDLE change = (HANDLE) arg;
42 status = WaitForSingleObject(change, 100);
44 if (status == WAIT_OBJECT_0 ) {
45 ret = FindNextChangeNotification(change);
48 ret = FindCloseChangeNotification(change);
49 ok( ret, "FindCloseChangeNotification error: %ld\n",
52 ExitThread((DWORD)ret);
55 static HANDLE StartNotificationThread(LPCSTR path, BOOL subtree, DWORD flags)
57 HANDLE change, thread;
60 change = FindFirstChangeNotificationA(path, subtree, flags);
61 ok(change != INVALID_HANDLE_VALUE, "FindFirstChangeNotification error: %ld\n", GetLastError());
63 thread = CreateThread(NULL, 0, NotificationThread, (LPVOID)change,
65 ok(thread != INVALID_HANDLE_VALUE, "CreateThread error: %ld\n", GetLastError());
70 static DWORD FinishNotificationThread(HANDLE thread)
72 DWORD status, exitcode;
74 status = WaitForSingleObject(thread, 5000);
75 ok(status == WAIT_OBJECT_0, "WaitForSingleObject status %ld error %ld\n", status, GetLastError());
77 ok(GetExitCodeThread(thread, &exitcode), "Could not retrieve thread exit code\n");
82 static void test_FindFirstChangeNotification(void)
84 HANDLE change, file, thread;
85 DWORD attributes, count;
88 char workdir[MAX_PATH], dirname1[MAX_PATH], dirname2[MAX_PATH];
89 char filename1[MAX_PATH], filename2[MAX_PATH];
90 static const char prefix[] = "FCN";
95 change = FindFirstChangeNotificationA("not-a-file", FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
96 ok(change == INVALID_HANDLE_VALUE && GetLastError() == ERROR_FILE_NOT_FOUND,
97 "FindFirstChangeNotification error: %ld\n", GetLastError());
99 if (0) /* This documents win2k behavior. It crashes on win98. */
101 change = FindFirstChangeNotificationA(NULL, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
102 ok(change == NULL && GetLastError() == ERROR_PATH_NOT_FOUND,
103 "FindFirstChangeNotification error: %ld\n", GetLastError());
106 ret = FindNextChangeNotification(NULL);
107 ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindNextChangeNotification error: %ld\n",
110 ret = FindCloseChangeNotification(NULL);
111 ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindCloseChangeNotification error: %ld\n",
114 ret = GetTempPathA(MAX_PATH, workdir);
115 ok(ret, "GetTempPathA error: %ld\n", GetLastError());
117 lstrcatA(workdir, "testFileChangeNotification");
119 ret = CreateDirectoryA(workdir, NULL);
120 ok(ret, "CreateDirectoryA error: %ld\n", GetLastError());
122 ret = GetTempFileNameA(workdir, prefix, 0, filename1);
123 ok(ret, "GetTempFileNameA error: %ld\n", GetLastError());
125 file = CreateFileA(filename1, GENERIC_WRITE|GENERIC_READ, 0, NULL, CREATE_ALWAYS,
126 FILE_ATTRIBUTE_NORMAL, 0);
127 ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %ld\n", GetLastError());
128 ret = CloseHandle(file);
129 ok( ret, "CloseHandle error: %ld\n", GetLastError());
131 /* Try to register notification for a file. win98 and win2k behave differently here */
132 change = FindFirstChangeNotificationA(filename1, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
133 ok(change == INVALID_HANDLE_VALUE && (GetLastError() == ERROR_DIRECTORY ||
134 GetLastError() == ERROR_FILE_NOT_FOUND),
135 "FindFirstChangeNotification error: %ld\n", GetLastError());
137 lstrcpyA(dirname1, filename1);
138 lstrcatA(dirname1, "dir");
140 lstrcpyA(dirname2, dirname1);
141 lstrcatA(dirname2, "new");
143 ret = CreateDirectoryA(dirname1, NULL);
144 ok(ret, "CreateDirectoryA error: %ld\n", GetLastError());
146 /* What if we move the directory we registered notification for? */
147 thread = StartNotificationThread(dirname1, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
148 ret = MoveFileA(dirname1, dirname2);
149 ok(ret, "MoveFileA error: %ld\n", GetLastError());
150 ok(FinishNotificationThread(thread), "Missed notification\n");
152 /* What if we remove the directory we registered notification for? */
153 thread = StartNotificationThread(dirname2, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
154 ret = RemoveDirectoryA(dirname2);
155 ok(ret, "RemoveDirectoryA error: %ld\n", GetLastError());
157 /* win98 and win2k behave differently here */
158 ret = FinishNotificationThread(thread);
159 ok(ret || !ret, "You'll never read this\n");
161 /* functional checks */
163 /* Create a directory */
164 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
165 ret = CreateDirectoryA(dirname1, NULL);
166 ok(ret, "CreateDirectoryA error: %ld\n", GetLastError());
167 ok(FinishNotificationThread(thread), "Missed notification\n");
169 /* Rename a directory */
170 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
171 ret = MoveFileA(dirname1, dirname2);
172 ok(ret, "MoveFileA error: %ld\n", GetLastError());
173 ok(FinishNotificationThread(thread), "Missed notification\n");
175 /* Delete a directory */
176 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
177 ret = RemoveDirectoryA(dirname2);
178 ok(ret, "RemoveDirectoryA error: %ld\n", GetLastError());
179 ok(FinishNotificationThread(thread), "Missed notification\n");
181 lstrcpyA(filename2, filename1);
182 lstrcatA(filename2, "new");
185 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
186 ret = MoveFileA(filename1, filename2);
187 ok(ret, "MoveFileA error: %ld\n", GetLastError());
188 ok(FinishNotificationThread(thread), "Missed notification\n");
191 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
192 ret = DeleteFileA(filename2);
193 ok(ret, "DeleteFileA error: %ld\n", GetLastError());
194 ok(FinishNotificationThread(thread), "Missed notification\n");
197 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
198 file = CreateFileA(filename2, GENERIC_WRITE|GENERIC_READ, 0, NULL, CREATE_ALWAYS,
199 FILE_ATTRIBUTE_NORMAL, 0);
200 ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %ld\n", GetLastError());
201 ret = CloseHandle(file);
202 ok( ret, "CloseHandle error: %ld\n", GetLastError());
203 ok(FinishNotificationThread(thread), "Missed notification\n");
205 attributes = GetFileAttributesA(filename2);
206 ok(attributes != INVALID_FILE_ATTRIBUTES, "GetFileAttributesA error: %ld\n", GetLastError());
207 attributes &= FILE_ATTRIBUTE_READONLY;
209 /* Change file attributes */
210 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_ATTRIBUTES);
211 ret = SetFileAttributesA(filename2, attributes);
212 ok(ret, "SetFileAttributesA error: %ld\n", GetLastError());
213 ok(FinishNotificationThread(thread), "Missed notification\n");
215 /* Change last write time by writing to a file */
216 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE);
217 file = CreateFileA(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
218 FILE_ATTRIBUTE_NORMAL, 0);
219 ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %ld\n", GetLastError());
220 ret = WriteFile(file, buffer, sizeof(buffer), &count, NULL);
221 ok(ret && count == sizeof(buffer), "WriteFile error: %ld\n", GetLastError());
222 ret = CloseHandle(file);
223 ok( ret, "CloseHandle error: %ld\n", GetLastError());
224 ok(FinishNotificationThread(thread), "Missed notification\n");
226 /* Change file size by truncating a file */
227 thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_SIZE);
228 file = CreateFileA(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
229 FILE_ATTRIBUTE_NORMAL, 0);
230 ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %ld\n", GetLastError());
231 ret = WriteFile(file, buffer, sizeof(buffer) / 2, &count, NULL);
232 ok(ret && count == sizeof(buffer) / 2, "WriteFileA error: %ld\n", GetLastError());
233 ret = CloseHandle(file);
234 ok( ret, "CloseHandle error: %ld\n", GetLastError());
235 ok(FinishNotificationThread(thread), "Missed notification\n");
239 ret = DeleteFileA(filename2);
240 ok(ret, "DeleteFileA error: %ld\n", GetLastError());
242 ret = RemoveDirectoryA(workdir);
243 ok(ret, "RemoveDirectoryA error: %ld\n", GetLastError());
246 /* this test concentrates more on the wait behaviour of the handle */
247 static void test_ffcn(void)
252 WCHAR path[MAX_PATH], subdir[MAX_PATH];
253 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
254 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
256 r = GetTempPathW( MAX_PATH, path );
257 ok( r != 0, "temp path failed\n");
261 lstrcatW( path, szBoo );
262 lstrcpyW( subdir, path );
263 lstrcatW( subdir, szHoo );
265 RemoveDirectoryW( subdir );
266 RemoveDirectoryW( path );
268 r = CreateDirectoryW(path, NULL);
269 ok( r == TRUE, "failed to create directory\n");
271 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
272 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
274 handle = FindFirstChangeNotificationW( path, 1, filter);
275 ok( handle != INVALID_HANDLE_VALUE, "invalid handle\n");
277 r = WaitForSingleObject( handle, 0 );
278 ok( r == STATUS_TIMEOUT, "should time out\n");
280 r = CreateDirectoryW( subdir, NULL );
281 ok( r == TRUE, "failed to create subdir\n");
283 r = WaitForSingleObject( handle, 0 );
284 ok( r == WAIT_OBJECT_0, "should be ready\n");
286 r = WaitForSingleObject( handle, 0 );
287 ok( r == WAIT_OBJECT_0, "should be ready\n");
289 r = FindNextChangeNotification(handle);
290 ok( r == TRUE, "find next failed\n");
292 r = WaitForSingleObject( handle, 0 );
293 ok( r == STATUS_TIMEOUT, "should time out\n");
295 r = RemoveDirectoryW( subdir );
296 ok( r == TRUE, "failed to remove subdir\n");
298 r = WaitForSingleObject( handle, 0 );
299 ok( r == WAIT_OBJECT_0, "should be ready\n");
301 r = WaitForSingleObject( handle, 0 );
302 ok( r == WAIT_OBJECT_0, "should be ready\n");
304 r = FindNextChangeNotification(handle);
305 ok( r == TRUE, "find next failed\n");
307 r = FindNextChangeNotification(handle);
308 ok( r == TRUE, "find next failed\n");
310 r = FindCloseChangeNotification(handle);
311 ok( r == TRUE, "should succeed\n");
313 r = RemoveDirectoryW( path );
314 ok( r == TRUE, "failed to remove dir\n");
317 typedef BOOL (WINAPI *fnReadDirectoryChangesW)(HANDLE,LPVOID,DWORD,BOOL,DWORD,
318 LPDWORD,LPOVERLAPPED,LPOVERLAPPED_COMPLETION_ROUTINE);
320 static void test_readdirectorychanges(void)
324 DWORD fflags, filter = 0, r;
326 WCHAR path[MAX_PATH], subdir[MAX_PATH];
327 static const WCHAR szBoo[] = { '\\','b','o','o',0 };
328 static const WCHAR szHoo[] = { '\\','h','o','o',0 };
329 fnReadDirectoryChangesW pReadDirectoryChangesW;
332 hkernel32 = GetModuleHandle("kernel32");
333 pReadDirectoryChangesW = (fnReadDirectoryChangesW)
334 GetProcAddress(hkernel32, "ReadDirectoryChangesW");
335 if (!pReadDirectoryChangesW)
338 r = GetTempPathW( MAX_PATH, path );
339 ok( r != 0, "temp path failed\n");
343 lstrcatW( path, szBoo );
344 lstrcpyW( subdir, path );
345 lstrcatW( subdir, szHoo );
347 RemoveDirectoryW( subdir );
348 RemoveDirectoryW( path );
350 r = CreateDirectoryW(path, NULL);
351 ok( r == TRUE, "failed to create directory\n");
353 SetLastError(0xd0b00b00);
354 r = pReadDirectoryChangesW(NULL,NULL,0,FALSE,0,NULL,NULL,NULL);
355 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
356 ok(r==FALSE, "should return false\n");
358 fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
359 hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE, FILE_SHARE_READ, NULL,
360 OPEN_EXISTING, fflags, NULL);
361 ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
363 ov.hEvent = CreateEvent( NULL, 0, 0, NULL );
365 SetLastError(0xd0b00b00);
366 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,NULL,NULL);
367 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
368 ok(r==FALSE, "should return false\n");
370 SetLastError(0xd0b00b00);
371 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,&ov,NULL);
372 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
373 ok(r==FALSE, "should return false\n");
375 filter = FILE_NOTIFY_CHANGE_FILE_NAME;
376 filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
377 filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
378 filter |= FILE_NOTIFY_CHANGE_SIZE;
379 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
380 filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
381 filter |= FILE_NOTIFY_CHANGE_CREATION;
382 filter |= FILE_NOTIFY_CHANGE_SECURITY;
384 SetLastError(0xd0b00b00);
385 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,-1,NULL,&ov,NULL);
386 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
387 ok(r==FALSE, "should return false\n");
389 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,filter,NULL,&ov,NULL);
390 ok(r==TRUE, "should return true\n");
392 r = WaitForSingleObject( ov.hEvent, 0 );
393 ok( r == STATUS_TIMEOUT, "should timeout\n" );
395 r = WaitForSingleObject( hdir, 0 );
396 ok( r == STATUS_TIMEOUT, "should timeout\n" );
398 r = CreateDirectoryW( subdir, NULL );
399 ok( r == TRUE, "failed to create directory\n");
401 r = WaitForSingleObject( hdir, 0 );
402 ok( r == STATUS_TIMEOUT, "should timeout\n" );
404 r = WaitForSingleObject( ov.hEvent, 0 );
405 ok( r == WAIT_OBJECT_0, "event should be ready\n" );
407 SetLastError(0xd0b00b00);
408 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,NULL,NULL);
409 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
410 ok(r==FALSE, "should return false\n");
412 r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL);
413 ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
414 ok(r==FALSE, "should return false\n");
416 filter = FILE_NOTIFY_CHANGE_SIZE;
418 CloseHandle( ov.hEvent );
420 r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,filter,NULL,&ov,NULL);
421 ok(r==TRUE, "should return true\n");
423 r = WaitForSingleObject( hdir, 0 );
424 ok( r == STATUS_TIMEOUT, "should timeout\n" );
426 r = RemoveDirectoryW( subdir );
427 ok( r == TRUE, "failed to remove directory\n");
429 r = WaitForSingleObject( hdir, 0 );
430 ok( r == WAIT_OBJECT_0, "should be ready\n" );
432 r = WaitForSingleObject( hdir, 0 );
433 ok( r == WAIT_OBJECT_0, "should be ready\n" );
437 r = RemoveDirectoryW( path );
438 ok( r == TRUE, "failed to remove directory\n");
444 test_FindFirstChangeNotification();
446 test_readdirectorychanges();