1 /* 2 * PROJECT: ReactOS api tests 3 * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) 4 * PURPOSE: Test for SHChangeNotify 5 * COPYRIGHT: Copyright 2020 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) 6 */ 7 8 #include "shelltest.h" 9 #include <shlwapi.h> 10 #include <stdio.h> 11 12 #define WM_SHELL_NOTIFY (WM_USER + 100) 13 #define WM_GET_NOTIFY_FLAGS (WM_USER + 101) 14 #define WM_CLEAR_FLAGS (WM_USER + 102) 15 16 static WCHAR s_dir1[MAX_PATH]; // "%TEMP%\\WatchDir1" 17 static WCHAR s_dir2[MAX_PATH]; // "%TEMP%\\WatchDir1\\Dir2" 18 static WCHAR s_dir3[MAX_PATH]; // "%TEMP%\\WatchDir1\\Dir3" 19 static WCHAR s_file1[MAX_PATH]; // "%TEMP%\\WatchDir1\\File1.txt" 20 static WCHAR s_file2[MAX_PATH]; // "%TEMP%\\WatchDir1\\File2.txt" 21 22 static HWND s_hwnd = NULL; 23 static const WCHAR s_szName[] = L"SHChangeNotify testcase"; 24 25 typedef enum TYPE 26 { 27 TYPE_RENAMEITEM, 28 TYPE_CREATE, 29 TYPE_DELETE, 30 TYPE_MKDIR, 31 TYPE_RMDIR, 32 TYPE_UPDATEDIR, 33 TYPE_UPDATEITEM, 34 TYPE_RENAMEFOLDER, 35 TYPE_FREESPACE 36 } TYPE; 37 38 typedef void (*ACTION)(void); 39 40 typedef struct TEST_ENTRY 41 { 42 INT line; 43 LONG event; 44 LPCVOID item1; 45 LPCVOID item2; 46 LPCSTR pattern; 47 ACTION action; 48 } TEST_ENTRY; 49 50 static BOOL 51 DoCreateEmptyFile(LPCWSTR pszFileName) 52 { 53 FILE *fp = _wfopen(pszFileName, L"wb"); 54 fclose(fp); 55 return fp != NULL; 56 } 57 58 static void 59 DoAction1(void) 60 { 61 ok_int(CreateDirectoryW(s_dir2, NULL), TRUE); 62 } 63 64 static void 65 DoAction2(void) 66 { 67 ok_int(RemoveDirectoryW(s_dir2), TRUE); 68 } 69 70 static void 71 DoAction3(void) 72 { 73 ok_int(MoveFileExW(s_dir2, s_dir3, MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING), TRUE); 74 } 75 76 static void 77 DoAction4(void) 78 { 79 ok_int(DoCreateEmptyFile(s_file1), TRUE); 80 } 81 82 static void 83 DoAction5(void) 84 { 85 ok_int(MoveFileExW(s_file1, s_file2, MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING), TRUE); 86 } 87 88 static void 89 DoAction6(void) 90 { 91 ok_int(DeleteFileW(s_file2), TRUE); 92 } 93 94 static void 95 DoAction7(void) 96 { 97 DeleteFileW(s_file1); 98 DeleteFileW(s_file2); 99 ok_int(RemoveDirectoryW(s_dir3), TRUE); 100 } 101 102 static void 103 DoAction8(void) 104 { 105 ok_int(RemoveDirectoryW(s_dir1), TRUE); 106 } 107 108 static const TEST_ENTRY s_TestEntries[] = { 109 {__LINE__, SHCNE_MKDIR, s_dir1, NULL, "000100000", NULL}, 110 {__LINE__, SHCNE_MKDIR, s_dir2, NULL, "000100000", NULL}, 111 {__LINE__, SHCNE_RMDIR, s_dir2, NULL, "000010000", NULL}, 112 {__LINE__, SHCNE_MKDIR, s_dir2, NULL, "000100000", DoAction1}, 113 {__LINE__, SHCNE_RMDIR, s_dir2, NULL, "000010000", NULL}, 114 {__LINE__, SHCNE_RMDIR, s_dir2, NULL, "000010000", DoAction2}, 115 {__LINE__, SHCNE_MKDIR, s_dir2, NULL, "000100000", DoAction1}, 116 {__LINE__, SHCNE_RENAMEFOLDER, s_dir2, s_dir3, "000000010", NULL}, 117 {__LINE__, SHCNE_RENAMEFOLDER, s_dir2, s_dir3, "000000010", DoAction3}, 118 {__LINE__, SHCNE_CREATE, s_file1, NULL, "010000000", NULL}, 119 {__LINE__, SHCNE_CREATE, s_file1, s_file2, "010000000", NULL}, 120 {__LINE__, SHCNE_CREATE, s_file1, NULL, "010000000", DoAction4}, 121 {__LINE__, SHCNE_RENAMEITEM, s_file1, s_file2, "100000000", NULL}, 122 {__LINE__, SHCNE_RENAMEITEM, s_file1, s_file2, "100000000", DoAction5}, 123 {__LINE__, SHCNE_RENAMEITEM, s_file1, s_file2, "100000000", NULL}, 124 {__LINE__, SHCNE_UPDATEITEM, s_file1, NULL, "000000100", NULL}, 125 {__LINE__, SHCNE_UPDATEITEM, s_file2, NULL, "000000100", NULL}, 126 {__LINE__, SHCNE_UPDATEITEM, s_file1, s_file2, "000000100", NULL}, 127 {__LINE__, SHCNE_UPDATEITEM, s_file2, s_file1, "000000100", NULL}, 128 {__LINE__, SHCNE_DELETE, s_file1, NULL, "001000000", NULL}, 129 {__LINE__, SHCNE_DELETE, s_file2, NULL, "001000000", NULL}, 130 {__LINE__, SHCNE_DELETE, s_file2, s_file1, "001000000", NULL}, 131 {__LINE__, SHCNE_DELETE, s_file1, s_file2, "001000000", NULL}, 132 {__LINE__, SHCNE_DELETE, s_file2, NULL, "001000000", DoAction6}, 133 {__LINE__, SHCNE_DELETE, s_file2, NULL, "001000000", NULL}, 134 {__LINE__, SHCNE_DELETE, s_file1, NULL, "001000000", NULL}, 135 {__LINE__, SHCNE_UPDATEDIR, s_file1, NULL, "000001000", NULL}, 136 {__LINE__, SHCNE_UPDATEDIR, s_file2, NULL, "000001000", NULL}, 137 {__LINE__, SHCNE_UPDATEDIR, s_file1, s_file2, "000001000", NULL}, 138 {__LINE__, SHCNE_UPDATEDIR, s_file2, s_file1, "000001000", NULL}, 139 {__LINE__, SHCNE_UPDATEDIR, s_dir1, NULL, "000001000", NULL}, 140 {__LINE__, SHCNE_UPDATEDIR, s_dir2, NULL, "000001000", NULL}, 141 {__LINE__, SHCNE_UPDATEDIR, s_dir1, s_dir2, "000001000", NULL}, 142 {__LINE__, SHCNE_UPDATEDIR, s_dir2, s_dir1, "000001000", NULL}, 143 {__LINE__, SHCNE_RMDIR, s_dir1, NULL, "000010000", NULL}, 144 {__LINE__, SHCNE_RMDIR, s_dir2, NULL, "000010000", NULL}, 145 {__LINE__, SHCNE_RMDIR, s_dir3, NULL, "000010000", NULL}, 146 {__LINE__, SHCNE_RMDIR, s_dir1, s_dir2, "000010000", NULL}, 147 {__LINE__, SHCNE_RMDIR, s_dir1, s_dir3, "000010000", NULL}, 148 {__LINE__, SHCNE_RMDIR, s_dir2, s_dir1, "000010000", NULL}, 149 {__LINE__, SHCNE_RMDIR, s_dir2, s_dir3, "000010000", NULL}, 150 {__LINE__, SHCNE_RMDIR, s_dir3, NULL, "000010000", NULL}, 151 {__LINE__, SHCNE_RMDIR, s_dir3, NULL, "000010000", DoAction7}, 152 {__LINE__, SHCNE_RMDIR, s_dir1, NULL, "000010000", NULL}, 153 {__LINE__, SHCNE_RMDIR, s_dir1, NULL, "000010000", DoAction8}, 154 }; 155 156 LPCSTR PatternFromFlags(DWORD flags) 157 { 158 static char s_buf[TYPE_FREESPACE + 1 + 1]; 159 DWORD i; 160 for (i = 0; i <= TYPE_FREESPACE; ++i) 161 { 162 s_buf[i] = (char)('0' + !!(flags & (1 << i))); 163 } 164 s_buf[i] = 0; 165 return s_buf; 166 } 167 168 static void 169 DoTestEntry(const TEST_ENTRY *entry) 170 { 171 if (entry->action) 172 { 173 (*entry->action)(); 174 } 175 176 SHChangeNotify(entry->event, SHCNF_PATHW | SHCNF_FLUSH, entry->item1, entry->item2); 177 178 DWORD flags = SendMessageW(s_hwnd, WM_GET_NOTIFY_FLAGS, 0, 0); 179 LPCSTR pattern = PatternFromFlags(flags); 180 181 ok(lstrcmpA(pattern, entry->pattern) == 0, "Line %d: pattern mismatch '%s'\n", entry->line, pattern); 182 183 SendMessageW(s_hwnd, WM_CLEAR_FLAGS, 0, 0); 184 } 185 186 static BOOL 187 DoInit(HWND hwnd) 188 { 189 WCHAR szTemp[MAX_PATH], szPath[MAX_PATH]; 190 191 GetTempPathW(_countof(szTemp), szTemp); 192 GetLongPathNameW(szTemp, szPath, _countof(szPath)); 193 194 lstrcpyW(s_dir1, szPath); 195 PathAppendW(s_dir1, L"WatchDir1"); 196 CreateDirectoryW(s_dir1, NULL); 197 198 lstrcpyW(s_dir2, s_dir1); 199 PathAppendW(s_dir2, L"Dir2"); 200 201 lstrcpyW(s_dir3, s_dir1); 202 PathAppendW(s_dir3, L"Dir3"); 203 204 lstrcpyW(s_file1, s_dir1); 205 PathAppendW(s_file1, L"File1.txt"); 206 207 lstrcpyW(s_file2, s_dir1); 208 PathAppendW(s_file2, L"File2.txt"); 209 210 return TRUE; 211 } 212 213 static void 214 DoEnd(HWND hwnd) 215 { 216 DeleteFileW(s_file1); 217 DeleteFileW(s_file2); 218 RemoveDirectoryW(s_dir3); 219 RemoveDirectoryW(s_dir2); 220 RemoveDirectoryW(s_dir1); 221 222 SendMessageW(s_hwnd, WM_COMMAND, IDOK, 0); 223 } 224 225 static BOOL CALLBACK 226 PropEnumProcEx(HWND hwnd, LPWSTR lpszString, HANDLE hData, ULONG_PTR dwData) 227 { 228 if (HIWORD(lpszString)) 229 trace("Prop: '%S' --> %p\n", lpszString, hData); 230 else 231 trace("Prop: '%u' --> %p\n", LOWORD(lpszString), hData); 232 return TRUE; 233 } 234 235 static void 236 DoPropTest(HWND hwnd) 237 { 238 EnumPropsExW(hwnd, PropEnumProcEx, 0); 239 } 240 241 START_TEST(SHChangeNotify) 242 { 243 WCHAR szPath[MAX_PATH]; 244 GetModuleFileNameW(NULL, szPath, _countof(szPath)); 245 PathRemoveFileSpecW(szPath); 246 PathAppendW(szPath, L"shell-notify.exe"); 247 248 if (!PathFileExistsW(szPath)) 249 { 250 trace("szPath: %S\n", szPath); 251 PathRemoveFileSpecW(szPath); 252 PathAppendW(szPath, L"testdata\\shell-notify.exe"); 253 254 if (!PathFileExistsW(szPath)) 255 { 256 trace("szPath: %S\n", szPath); 257 skip("shell-notify.exe not found\n"); 258 return; 259 } 260 } 261 262 HINSTANCE hinst = ShellExecuteW(NULL, NULL, szPath, NULL, NULL, SW_SHOWNORMAL); 263 if ((INT_PTR)hinst <= 32) 264 { 265 skip("Unable to run shell-notify.exe.\n"); 266 return; 267 } 268 269 for (int i = 0; i < 10; ++i) 270 { 271 s_hwnd = FindWindowW(s_szName, s_szName); 272 if (s_hwnd) 273 break; 274 275 Sleep(100); 276 } 277 278 if (!s_hwnd) 279 { 280 skip("Unable to find window.\n"); 281 return; 282 } 283 284 if (!DoInit(s_hwnd)) 285 { 286 skip("Unable to initialize.\n"); 287 return; 288 } 289 290 DoPropTest(s_hwnd); 291 292 for (size_t i = 0; i < _countof(s_TestEntries); ++i) 293 { 294 DoTestEntry(&s_TestEntries[i]); 295 } 296 297 DoEnd(s_hwnd); 298 } 299