Correct the calculation of the year for the 31'st of December of 2000
[wine] / dlls / kernel / tests / change.c
1 /*
2  * Tests for file change notification functions
3  *
4  * Copyright (c) 2004 Hans Leidekker
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 /* TODO: - security attribute changes
22  *       - compound filter and multiple notifications
23  *       - subtree notifications
24  *       - non-documented flags FILE_NOTIFY_CHANGE_LAST_ACCESS and
25  *         FILE_NOTIFY_CHANGE_CREATION
26  */
27
28 #include <stdarg.h>
29 #include <stdio.h>
30
31 #include "wine/test.h"
32 #include <windef.h>
33 #include <winbase.h>
34
35 static DWORD CALLBACK NotificationThread(LPVOID arg)
36 {
37     HANDLE change = (HANDLE) arg;
38     BOOL ret = FALSE;
39     DWORD status;
40
41     status = WaitForSingleObject(change, 100);
42
43     if (status == WAIT_OBJECT_0 ) {
44         ret = FindNextChangeNotification(change);
45     }
46
47     ok(FindCloseChangeNotification(change), "FindCloseChangeNotification error: %ld\n",
48        GetLastError());
49
50     ExitThread((DWORD)ret);
51 }
52
53 static HANDLE StartNotificationThread(LPCSTR path, BOOL subtree, DWORD flags)
54 {
55     HANDLE change, thread;
56     DWORD threadId;
57
58     change = FindFirstChangeNotificationA(path, subtree, flags);
59     ok(change != INVALID_HANDLE_VALUE, "FindFirstChangeNotification error: %ld\n", GetLastError());
60
61     thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)NotificationThread, (LPVOID)change,
62                           0, &threadId);
63     ok(thread != INVALID_HANDLE_VALUE, "CreateThread error: %ld\n", GetLastError());
64
65     return thread;
66 }
67
68 static DWORD FinishNotificationThread(HANDLE thread)
69 {
70     DWORD status, exitcode;
71
72     status = WaitForSingleObject(thread, 5000);
73     ok(status == WAIT_OBJECT_0, "WaitForSingleObject status %ld error %ld\n", status, GetLastError());
74
75     ok(GetExitCodeThread(thread, &exitcode), "Could not retrieve thread exit code\n");
76
77     return exitcode;
78 }
79
80 static void test_FindFirstChangeNotification(void)
81 {
82     HANDLE change, file, thread;
83     DWORD attributes, count;
84     BOOL ret;
85
86     char workdir[MAX_PATH], dirname1[MAX_PATH], dirname2[MAX_PATH];
87     char filename1[MAX_PATH], filename2[MAX_PATH];
88     static const char prefix[] = "FCN";
89     char buffer[2048];
90
91     /* pathetic checks */
92
93     change = FindFirstChangeNotificationA("not-a-file", FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
94     ok(change == INVALID_HANDLE_VALUE && GetLastError() == ERROR_FILE_NOT_FOUND,
95        "FindFirstChangeNotification error: %ld\n", GetLastError());
96
97     if (0) /* This documents win2k behavior. It crashes on win98. */
98     { 
99         change = FindFirstChangeNotificationA(NULL, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
100         ok(change == NULL && GetLastError() == ERROR_PATH_NOT_FOUND,
101         "FindFirstChangeNotification error: %ld\n", GetLastError());
102     }
103
104     ret = FindNextChangeNotification(NULL);
105     ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindNextChangeNotification error: %ld\n",
106        GetLastError());
107
108     ret = FindCloseChangeNotification(NULL);
109     ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindCloseChangeNotification error: %ld\n",
110        GetLastError());
111
112     ret = GetTempPathA(MAX_PATH, workdir);
113     ok(ret, "GetTempPathA error: %ld\n", GetLastError());
114
115     lstrcatA(workdir, "testFileChangeNotification");
116
117     ret = CreateDirectoryA(workdir, NULL);
118     ok(ret, "CreateDirectoryA error: %ld\n", GetLastError());
119
120     ret = GetTempFileNameA(workdir, prefix, 0, filename1);
121     ok(ret, "GetTempFileNameA error: %ld\n", GetLastError());
122
123     file = CreateFileA(filename1, GENERIC_WRITE|GENERIC_READ, 0, NULL, CREATE_ALWAYS,
124                        FILE_ATTRIBUTE_NORMAL, 0);
125     ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %ld\n", GetLastError());
126     ok(CloseHandle(file), "CloseHandle error: %ld\n", GetLastError());
127
128     /* Try to register notification for a file. win98 and win2k behave differently here */
129     change = FindFirstChangeNotificationA(filename1, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
130     ok(change == INVALID_HANDLE_VALUE && (GetLastError() == ERROR_DIRECTORY ||
131                                           GetLastError() == ERROR_FILE_NOT_FOUND),
132        "FindFirstChangeNotification error: %ld\n", GetLastError());
133
134     lstrcpyA(dirname1, filename1);
135     lstrcatA(dirname1, "dir");
136
137     ret = CreateDirectoryA(dirname1, NULL);
138     ok(ret, "CreateDirectoryA error: %ld\n", GetLastError());
139
140     /* What if we remove the directory we registered notification for? */
141     thread = StartNotificationThread(dirname1, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
142     ret = RemoveDirectoryA(dirname1);
143     ok(ret, "RemoveDirectoryA error: %ld\n", GetLastError());
144
145     /* win98 and win2k behave differently here */
146     ret = FinishNotificationThread(thread);
147     ok(ret || !ret, "You'll never read this\n");
148
149     /* functional checks */
150
151     /* Create a directory */
152     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
153     ret = CreateDirectoryA(dirname1, NULL);
154     ok(ret, "CreateDirectoryA error: %ld\n", GetLastError());
155     ok(FinishNotificationThread(thread), "Missed notification\n");
156
157     lstrcpyA(dirname2, dirname1);
158     lstrcatA(dirname2, "new");
159
160     /* Rename a directory */
161     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
162     ret = MoveFileA(dirname1, dirname2);
163     ok(ret, "MoveFileA error: %ld\n", GetLastError());
164     ok(FinishNotificationThread(thread), "Missed notification\n");
165
166     /* Delete a directory */
167     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
168     ret = RemoveDirectoryA(dirname2);
169     ok(ret, "RemoveDirectoryA error: %ld\n", GetLastError());
170     ok(FinishNotificationThread(thread), "Missed notification\n");
171
172     lstrcpyA(filename2, filename1);
173     lstrcatA(filename2, "new");
174
175     /* Rename a file */
176     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
177     ret = MoveFileA(filename1, filename2);
178     ok(ret, "MoveFileA error: %ld\n", GetLastError());
179     ok(FinishNotificationThread(thread), "Missed notification\n");
180
181     /* Delete a file */
182     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
183     ret = DeleteFileA(filename2);
184     ok(ret, "DeleteFileA error: %ld\n", GetLastError());
185     ok(FinishNotificationThread(thread), "Missed notification\n");
186
187     /* Create a file */
188     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
189     file = CreateFileA(filename2, GENERIC_WRITE|GENERIC_READ, 0, NULL, CREATE_ALWAYS, 
190                        FILE_ATTRIBUTE_NORMAL, 0);
191     ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %ld\n", GetLastError());
192     ok(CloseHandle(file), "CloseHandle error: %ld\n", GetLastError());
193     ok(FinishNotificationThread(thread), "Missed notification\n");
194
195     attributes = GetFileAttributesA(filename2);
196     ok(attributes != INVALID_FILE_ATTRIBUTES, "GetFileAttributesA error: %ld\n", GetLastError());
197     attributes &= FILE_ATTRIBUTE_READONLY;
198
199     /* Change file attributes */
200     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_ATTRIBUTES);
201     ret = SetFileAttributesA(filename2, attributes);
202     ok(ret, "SetFileAttributesA error: %ld\n", GetLastError());
203     ok(FinishNotificationThread(thread), "Missed notification\n");
204
205     /* Change last write time by writing to a file */
206     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE);
207     file = CreateFileA(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 
208                        FILE_ATTRIBUTE_NORMAL, 0);
209     ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %ld\n", GetLastError());
210     ret = WriteFile(file, buffer, sizeof(buffer), &count, NULL);
211     ok(ret && count == sizeof(buffer), "WriteFile error: %ld\n", GetLastError());
212     ok(CloseHandle(file), "CloseHandle error: %ld\n", GetLastError());
213     ok(FinishNotificationThread(thread), "Missed notification\n");
214
215     /* Change file size by truncating a file */
216     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_SIZE);
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) / 2, &count, NULL);
221     ok(ret && count == sizeof(buffer) / 2, "WriteFileA error: %ld\n", GetLastError());
222     ok(CloseHandle(file), "CloseHandle error: %ld\n", GetLastError());
223     ok(FinishNotificationThread(thread), "Missed notification\n");
224
225     /* clean up */
226     
227     ret = DeleteFileA(filename2);
228     ok(ret, "DeleteFileA error: %ld\n", GetLastError());
229
230     ret = RemoveDirectoryA(workdir);
231     ok(ret, "RemoveDirectoryA error: %ld\n", GetLastError());
232 }
233
234 START_TEST(change)
235 {
236     test_FindFirstChangeNotification();
237 }