From 25e2f5f277b32450943e741398c2d579c1f10d05 Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Wed, 24 Apr 2024 09:10:29 +0900 Subject: [PATCH] [SHELL32_APITEST] Make SHChangeNotify testcase more stable (#6796) Make the testcase more stable and faster. Reduce code. JIRA issue: CORE-13950 - Use WM_COPYDATA message to communicate with shell32_apitest_sub.exe. - Use concept of "stage" and "step". - Do multiple checks in one step. - Flush notification by using (SHCNF_FLUSH | SHCNF_FLUSHNOWAIT) flags. --- .../apitests/shell32/SHChangeNotify.cpp | 1394 +++++++---------- .../apitests/shell32/ShellExecCmdLine.cpp | 2 +- .../apitests/shell32/shell32_apitest_sub.cpp | 417 ++--- .../apitests/shell32/shell32_apitest_sub.h | 66 +- 4 files changed, 764 insertions(+), 1115 deletions(-) diff --git a/modules/rostests/apitests/shell32/SHChangeNotify.cpp b/modules/rostests/apitests/shell32/SHChangeNotify.cpp index 7a95fc470f6..498c3ad939d 100644 --- a/modules/rostests/apitests/shell32/SHChangeNotify.cpp +++ b/modules/rostests/apitests/shell32/SHChangeNotify.cpp @@ -2,673 +2,94 @@ * PROJECT: ReactOS api tests * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) * PURPOSE: Test for SHChangeNotify - * COPYRIGHT: Copyright 2020-2021 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) + * COPYRIGHT: Copyright 2020-2024 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) */ -// NOTE: This test program closes the Explorer windows before tests. +// NOTE: This testcase requires shell32_apitest_sub.exe. #include "shelltest.h" #include "shell32_apitest_sub.h" -#include -#include -#include +#include -// --- The selection of tests --- -#define DISABLE_THIS_TESTCASE -#define NO_TRIVIAL -//#define NO_INTERRUPT_LEVEL -//#define NO_SHELL_LEVEL -#define NEW_DELIVERY_ONLY -//#define RANDOM_HALF -#define RANDOM_QUARTER +#define NUM_STAGE 4 +#define NUM_STEP 8 +#define NUM_CHECKS 12 +#define INTERVAL 600 +#define MAX_EVENT_TYPE 6 -// --- Show the elapsed time by GetTickCount() --- -//#define ENTRY_TICK -#define GROUP_TICK -#define TOTAL_TICK - -static HWND s_hwnd = NULL; -static WCHAR s_szSubProgram[MAX_PATH]; +static HWND s_hMainWnd = NULL, s_hSubWnd = NULL; +static WCHAR s_szSubProgram[MAX_PATH]; // shell32_apitest_sub.exe static HANDLE s_hThread = NULL; -static HANDLE s_hEvent = NULL; +static INT s_iStage = -1, s_iStep = -1; +static BYTE s_abChecks[NUM_CHECKS] = { 0 }; // Flags for testing +static BOOL s_bGotUpdateDir = FALSE; // Got SHCNE_UPDATEDIR? -static BOOL DoCreateEmptyFile(LPCWSTR pszFileName) +static BOOL DoCreateFile(LPCWSTR pszFileName) { - FILE *fp = _wfopen(pszFileName, L"wb"); - if (fp) - fclose(fp); - return fp != NULL; + HANDLE hFile = ::CreateFileW(pszFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + ::CloseHandle(hFile); + return hFile != INVALID_HANDLE_VALUE; } -struct TEST_ENTRY; - -typedef BOOL (*ACTION)(const struct TEST_ENTRY *entry); - -typedef struct TEST_ENTRY +static void DoDeleteDirectory(LPCWSTR pszDir) { - INT line; - DIRTYPE iWriteDir; - LPCSTR pattern; - LPCWSTR path1; - LPCWSTR path2; - ACTION action; -} TEST_ENTRY; - -#define TEST_FILE L"_TEST_.txt" -#define TEST_FILE_KAI L"_TEST_KAI_.txt" -#define TEST_DIR L"_TESTDIR_" -#define TEST_DIR_KAI L"_TESTDIR_KAI_" -#define MOVE_FILE(from, to) MoveFileW((from), (to)) - -static BOOL DoAction1(const TEST_ENTRY *entry) -{ - LPWSTR pszPath = DoGetDir(entry->iWriteDir); - PathAppendW(pszPath, TEST_FILE); - ok(DoCreateEmptyFile(pszPath), "Line %d: DoCreateEmptyFile failed\n", entry->line); - return TRUE; + WCHAR szPath[MAX_PATH]; + ZeroMemory(szPath, sizeof(szPath)); + StringCchCopyW(szPath, _countof(szPath), pszDir); // Double-NULL terminated + SHFILEOPSTRUCTW FileOp = { NULL, FO_DELETE, szPath, NULL, FOF_NOCONFIRMATION | FOF_SILENT }; + SHFileOperation(&FileOp); } -static BOOL DoAction2(const TEST_ENTRY *entry) +static INT GetEventType(LONG lEvent) { - LPWSTR pszPath1 = DoGetDir(entry->iWriteDir), pszPath2 = DoGetDir(entry->iWriteDir); - PathAppendW(pszPath1, TEST_FILE); - PathAppendW(pszPath2, TEST_FILE_KAI); - ok(MOVE_FILE(pszPath1, pszPath2), "Line %d: MOVE_FILE(%ls, %ls) failed (%ld)\n", - entry->line, pszPath1, pszPath2, GetLastError()); - return TRUE; + switch (lEvent) + { + case SHCNE_CREATE: return 0; + case SHCNE_DELETE: return 1; + case SHCNE_RENAMEITEM: return 2; + case SHCNE_MKDIR: return 3; + case SHCNE_RMDIR: return 4; + case SHCNE_RENAMEFOLDER: return 5; + C_ASSERT(5 + 1 == MAX_EVENT_TYPE); + default: return -1; + } } -static BOOL DoAction3(const TEST_ENTRY *entry) -{ - LPWSTR pszPath1 = DoGetDir(entry->iWriteDir), pszPath2 = DoGetDir(entry->iWriteDir); - PathAppendW(pszPath1, TEST_FILE_KAI); - PathAppendW(pszPath2, TEST_FILE); - ok(MOVE_FILE(pszPath1, pszPath2), "Line %d: MOVE_FILE(%ls, %ls) failed (%ld)\n", - entry->line, pszPath1, pszPath2, GetLastError()); - return TRUE; -} +#define FILE_1 L"_TESTFILE_1_.txt" +#define FILE_2 L"_TESTFILE_2_.txt" +#define DIR_1 L"_TESTDIR_1_" +#define DIR_2 L"_TESTDIR_2_" -static BOOL DoAction4(const TEST_ENTRY *entry) -{ - LPWSTR pszPath = DoGetDir(entry->iWriteDir); - PathAppendW(pszPath, TEST_FILE); - ok(DeleteFileW(pszPath), "Line %d: DeleteFileW(%ls) failed (%ld)\n", - entry->line, pszPath, GetLastError()); - return TRUE; -} - -static BOOL DoAction5(const TEST_ENTRY *entry) -{ - LPWSTR pszPath = DoGetDir(entry->iWriteDir); - PathAppendW(pszPath, TEST_DIR); - ok(CreateDirectoryW(pszPath, NULL), "Line %d: CreateDirectoryW(%ls) failed (%ld)\n", - entry->line, pszPath, GetLastError()); - return TRUE; -} - -static BOOL DoAction6(const TEST_ENTRY *entry) -{ - LPWSTR pszPath1 = DoGetDir(entry->iWriteDir), pszPath2 = DoGetDir(entry->iWriteDir); - PathAppendW(pszPath1, TEST_DIR); - PathAppendW(pszPath2, TEST_DIR_KAI); - ok(MOVE_FILE(pszPath1, pszPath2), "Line %d: MOVE_FILE(%ls, %ls) failed (%ld)\n", - entry->line, pszPath1, pszPath2, GetLastError()); - return TRUE; -} - -static BOOL DoAction7(const TEST_ENTRY *entry) -{ - LPWSTR pszPath1 = DoGetDir(entry->iWriteDir), pszPath2 = DoGetDir(entry->iWriteDir); - PathAppendW(pszPath1, TEST_DIR_KAI); - PathAppendW(pszPath2, TEST_DIR); - ok(MOVE_FILE(pszPath1, pszPath2), "Line %d: MOVE_FILE(%ls, %ls) failed (%ld)\n", - entry->line, pszPath1, pszPath2, GetLastError()); - return TRUE; -} - -static BOOL DoAction8(const TEST_ENTRY *entry) -{ - LPWSTR pszPath = DoGetDir(entry->iWriteDir); - PathAppendW(pszPath, TEST_DIR); - ok(RemoveDirectoryW(pszPath), "Line %d: RemoveDirectoryW(%ls) failed (%ld)\n", - entry->line, pszPath, GetLastError()); - return TRUE; -} - -static BOOL DoAction9(const TEST_ENTRY *entry) -{ - LPWSTR pszPath = DoGetDir(entry->iWriteDir); - PathAppendW(pszPath, TEST_FILE); - SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW | SHCNF_FLUSH, pszPath, NULL); - return FALSE; -} - -static BOOL DoAction10(const TEST_ENTRY *entry) -{ - LPWSTR pszPath = DoGetDir(entry->iWriteDir); - PathAppendW(pszPath, TEST_FILE); - SHChangeNotify(SHCNE_DELETE, SHCNF_PATHW | SHCNF_FLUSH, pszPath, NULL); - return FALSE; -} - -static BOOL DoAction11(const TEST_ENTRY *entry) -{ - LPWSTR pszPath = DoGetDir(entry->iWriteDir); - PathAppendW(pszPath, TEST_DIR); - SHChangeNotify(SHCNE_MKDIR, SHCNF_PATHW | SHCNF_FLUSH, pszPath, NULL); - return FALSE; -} - -static BOOL DoAction12(const TEST_ENTRY *entry) -{ - LPWSTR pszPath = DoGetDir(entry->iWriteDir); - PathAppendW(pszPath, TEST_DIR); - SHChangeNotify(SHCNE_RMDIR, SHCNF_PATHW | SHCNF_FLUSH, pszPath, NULL); - return FALSE; -} - -#define WRITEDIR_0 DIRTYPE_DESKTOP -static WCHAR s_szDesktop[MAX_PATH]; -static WCHAR s_szTestFile0[MAX_PATH]; -static WCHAR s_szTestFile0Kai[MAX_PATH]; -static WCHAR s_szTestDir0[MAX_PATH]; -static WCHAR s_szTestDir0Kai[MAX_PATH]; - -#define WRITEDIR_1 DIRTYPE_MYDOCUMENTS -static WCHAR s_szDocuments[MAX_PATH]; -static WCHAR s_szTestFile1[MAX_PATH]; -static WCHAR s_szTestFile1Kai[MAX_PATH]; -static WCHAR s_szTestDir1[MAX_PATH]; -static WCHAR s_szTestDir1Kai[MAX_PATH]; +static WCHAR s_szDir1[MAX_PATH]; +static WCHAR s_szDir1InDir1[MAX_PATH]; +static WCHAR s_szDir2InDir1[MAX_PATH]; +static WCHAR s_szFile1InDir1InDir1[MAX_PATH]; +static WCHAR s_szFile1InDir1[MAX_PATH]; +static WCHAR s_szFile2InDir1[MAX_PATH]; static void DoDeleteFilesAndDirs(void) { - DeleteFileW(TEMP_FILE); - DeleteFileW(s_szTestFile0); - DeleteFileW(s_szTestFile0Kai); - DeleteFileW(s_szTestFile1); - DeleteFileW(s_szTestFile1Kai); - RemoveDirectoryW(s_szTestDir0); - RemoveDirectoryW(s_szTestDir0Kai); - RemoveDirectoryW(s_szTestDir1); - RemoveDirectoryW(s_szTestDir1Kai); + ::DeleteFileW(s_szFile1InDir1); + ::DeleteFileW(s_szFile2InDir1); + ::DeleteFileW(s_szFile1InDir1InDir1); + DoDeleteDirectory(s_szDir1InDir1); + DoDeleteDirectory(s_szDir2InDir1); + DoDeleteDirectory(s_szDir1); } -static const TEST_ENTRY s_group_00[] = +static void TEST_Quit(void) { - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction1 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction2 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction3 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction4 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction5 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction6 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction7 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction8 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction9 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction10 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction11 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction12 }, + CloseHandle(s_hThread); + s_hThread = NULL; - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction1 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction2 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction3 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction4 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction5 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction6 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction7 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction8 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction9 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction10 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction11 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction12 }, -}; - -static const TEST_ENTRY s_group_01[] = -{ - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction1 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction2 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction3 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction4 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction5 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction6 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction7 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction8 }, - { __LINE__, WRITEDIR_0, "0100000", s_szTestFile0, L"", DoAction9 }, - { __LINE__, WRITEDIR_0, "0010000", s_szTestFile0, L"", DoAction10 }, - { __LINE__, WRITEDIR_0, "0001000", s_szTestDir0, L"", DoAction11 }, - { __LINE__, WRITEDIR_0, "0000100", s_szTestDir0, L"", DoAction12 }, - - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction1 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction2 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction3 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction4 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction5 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction6 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction7 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction8 }, - { __LINE__, WRITEDIR_1, "0100000", s_szTestFile1, L"", DoAction9 }, - { __LINE__, WRITEDIR_1, "0010000", s_szTestFile1, L"", DoAction10 }, - { __LINE__, WRITEDIR_1, "0001000", s_szTestDir1, L"", DoAction11 }, - { __LINE__, WRITEDIR_1, "0000100", s_szTestDir1, L"", DoAction12 }, -}; - -static const TEST_ENTRY s_group_02[] = -{ - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction1 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction2 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction3 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction4 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction5 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction6 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction7 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction8 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction9 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction10 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction11 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction12 }, - - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction1 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction2 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction3 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction4 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction5 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction6 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction7 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction8 }, - { __LINE__, WRITEDIR_1, "0100000", s_szTestFile1, L"", DoAction9 }, - { __LINE__, WRITEDIR_1, "0010000", s_szTestFile1, L"", DoAction10 }, - { __LINE__, WRITEDIR_1, "0001000", s_szTestDir1, L"", DoAction11 }, - { __LINE__, WRITEDIR_1, "0000100", s_szTestDir1, L"", DoAction12 }, -}; - -static const TEST_ENTRY s_group_03[] = -{ - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction1 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction2 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction3 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction4 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction5 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction6 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction7 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction8 }, - { __LINE__, WRITEDIR_0, "0100000", s_szTestFile0, L"", DoAction9 }, - { __LINE__, WRITEDIR_0, "0010000", s_szTestFile0, L"", DoAction10 }, - { __LINE__, WRITEDIR_0, "0001000", s_szTestDir0, L"", DoAction11 }, - { __LINE__, WRITEDIR_0, "0000100", s_szTestDir0, L"", DoAction12 }, - - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction1 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction2 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction3 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction4 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction5 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction6 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction7 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction8 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction9 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction10 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction11 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction12 }, -}; - -static const TEST_ENTRY s_group_04[] = -{ - { __LINE__, WRITEDIR_0, "0100000", s_szTestFile0, L"", DoAction1 }, - { __LINE__, WRITEDIR_0, "1000000", s_szTestFile0, s_szTestFile0Kai, DoAction2 }, - { __LINE__, WRITEDIR_0, "1000000", s_szTestFile0Kai, s_szTestFile0, DoAction3 }, - { __LINE__, WRITEDIR_0, "0010000", s_szTestFile0, L"", DoAction4 }, - { __LINE__, WRITEDIR_0, "0001000", s_szTestDir0, L"", DoAction5 }, - { __LINE__, WRITEDIR_0, "0000010", s_szTestDir0, s_szTestDir0Kai, DoAction6 }, - { __LINE__, WRITEDIR_0, "0000010", s_szTestDir0Kai, s_szTestDir0, DoAction7 }, - { __LINE__, WRITEDIR_0, "0000100", s_szTestDir0, L"", DoAction8 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction9 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction10 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction11 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction12 }, - - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction1 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction2 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction3 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction4 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction5 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction6 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction7 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction8 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction9 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction10 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction11 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction12 }, -}; - -static const TEST_ENTRY s_group_05[] = -{ - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction1 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction2 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction3 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction4 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction5 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction6 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction7 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction8 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction9 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction10 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction11 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction12 }, - - { __LINE__, WRITEDIR_1, "0100000", s_szTestFile1, L"", DoAction1 }, - { __LINE__, WRITEDIR_1, "1000000", s_szTestFile1, s_szTestFile1Kai, DoAction2 }, - { __LINE__, WRITEDIR_1, "1000000", s_szTestFile1Kai, s_szTestFile1, DoAction3 }, - { __LINE__, WRITEDIR_1, "0010000", s_szTestFile1, L"", DoAction4 }, - { __LINE__, WRITEDIR_1, "0001000", s_szTestDir1, L"", DoAction5 }, - { __LINE__, WRITEDIR_1, "0000010", s_szTestDir1, s_szTestDir1Kai, DoAction6 }, - { __LINE__, WRITEDIR_1, "0000010", s_szTestDir1Kai, s_szTestDir1, DoAction7 }, - { __LINE__, WRITEDIR_1, "0000100", s_szTestDir1, L"", DoAction8 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction9 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction10 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction11 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction12 }, -}; - -static const TEST_ENTRY s_group_06[] = -{ - { __LINE__, WRITEDIR_0, "0100000", s_szTestFile0, L"", DoAction1 }, - { __LINE__, WRITEDIR_0, "1000000", s_szTestFile0, s_szTestFile0Kai, DoAction2 }, - { __LINE__, WRITEDIR_0, "1000000", s_szTestFile0Kai, s_szTestFile0, DoAction3 }, - { __LINE__, WRITEDIR_0, "0010000", s_szTestFile0, L"", DoAction4 }, - { __LINE__, WRITEDIR_0, "0001000", s_szTestDir0, L"", DoAction5 }, - { __LINE__, WRITEDIR_0, "0000010", s_szTestDir0, s_szTestDir0Kai, DoAction6 }, - { __LINE__, WRITEDIR_0, "0000010", s_szTestDir0Kai, s_szTestDir0, DoAction7 }, - { __LINE__, WRITEDIR_0, "0000100", s_szTestDir0, L"", DoAction8 }, - { __LINE__, WRITEDIR_0, "0100000", s_szTestFile0, L"", DoAction9 }, - { __LINE__, WRITEDIR_0, "0010000", s_szTestFile0, L"", DoAction10 }, - { __LINE__, WRITEDIR_0, "0001000", s_szTestDir0, L"", DoAction11 }, - { __LINE__, WRITEDIR_0, "0000100", s_szTestDir0, L"", DoAction12 }, - - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction1 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction2 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction3 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction4 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction5 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction6 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction7 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction8 }, - { __LINE__, WRITEDIR_1, "0100000", s_szTestFile1, L"", DoAction9 }, - { __LINE__, WRITEDIR_1, "0010000", s_szTestFile1, L"", DoAction10 }, - { __LINE__, WRITEDIR_1, "0001000", s_szTestDir1, L"", DoAction11 }, - { __LINE__, WRITEDIR_1, "0000100", s_szTestDir1, L"", DoAction12 }, -}; - -static const TEST_ENTRY s_group_07[] = -{ - { __LINE__, WRITEDIR_0, "0100000", s_szTestFile0, L"", DoAction1 }, - { __LINE__, WRITEDIR_0, "1000000", s_szTestFile0, s_szTestFile0Kai, DoAction2 }, - { __LINE__, WRITEDIR_0, "1000000", s_szTestFile0Kai, s_szTestFile0, DoAction3 }, - { __LINE__, WRITEDIR_0, "0010000", s_szTestFile0, L"", DoAction4 }, - { __LINE__, WRITEDIR_0, "0001000", s_szTestDir0, L"", DoAction5 }, - { __LINE__, WRITEDIR_0, "0000010", s_szTestDir0, s_szTestDir0Kai, DoAction6 }, - { __LINE__, WRITEDIR_0, "0000010", s_szTestDir0Kai, s_szTestDir0, DoAction7 }, - { __LINE__, WRITEDIR_0, "0000100", s_szTestDir0, L"", DoAction8 }, - { __LINE__, WRITEDIR_0, "0100000", s_szTestFile0, L"", DoAction9 }, - { __LINE__, WRITEDIR_0, "0010000", s_szTestFile0, L"", DoAction10 }, - { __LINE__, WRITEDIR_0, "0001000", s_szTestDir0, L"", DoAction11 }, - { __LINE__, WRITEDIR_0, "0000100", s_szTestDir0, L"", DoAction12 }, - - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction1 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction2 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction3 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction4 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction5 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction6 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction7 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction8 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction9 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction10 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction11 }, - { __LINE__, WRITEDIR_1, "0000000", L"", L"", DoAction12 }, -}; - -static const TEST_ENTRY s_group_08[] = -{ - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction1 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction2 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction3 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction4 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction5 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction6 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction7 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction8 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction9 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction10 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction11 }, - { __LINE__, WRITEDIR_0, "0000000", L"", L"", DoAction12 }, - - { __LINE__, WRITEDIR_1, "0100000", s_szTestFile1, L"", DoAction1 }, - { __LINE__, WRITEDIR_1, "1000000", s_szTestFile1, s_szTestFile1Kai, DoAction2 }, - { __LINE__, WRITEDIR_1, "1000000", s_szTestFile1Kai, s_szTestFile1, DoAction3 }, - { __LINE__, WRITEDIR_1, "0010000", s_szTestFile1, L"", DoAction4 }, - { __LINE__, WRITEDIR_1, "0001000", s_szTestDir1, L"", DoAction5 }, - { __LINE__, WRITEDIR_1, "0000010", s_szTestDir1, s_szTestDir1Kai, DoAction6 }, - { __LINE__, WRITEDIR_1, "0000010", s_szTestDir1Kai, s_szTestDir1, DoAction7 }, - { __LINE__, WRITEDIR_1, "0000100", s_szTestDir1, L"", DoAction8 }, - { __LINE__, WRITEDIR_1, "0100000", s_szTestFile1, L"", DoAction9 }, - { __LINE__, WRITEDIR_1, "0010000", s_szTestFile1, L"", DoAction10 }, - { __LINE__, WRITEDIR_1, "0001000", s_szTestDir1, L"", DoAction11 }, - { __LINE__, WRITEDIR_1, "0000100", s_szTestDir1, L"", DoAction12 }, -}; - -static LPCSTR PatternFromFlags(DWORD flags) -{ - static CHAR s_buf[(TYPE_MAX + 1) + 1]; - for (INT i = 0; i < (TYPE_MAX + 1); ++i) - s_buf[i] = (char)('0' + !!(flags & (1 << i))); - - s_buf[TYPE_MAX + 1] = 0; - return s_buf; -} - -static BOOL DoGetPaths(LPWSTR pszPath1, LPWSTR pszPath2) -{ - pszPath1[0] = pszPath2[0] = 0; - - WCHAR szText[MAX_PATH * 2]; - szText[0] = 0; - FILE *fp = _wfopen(TEMP_FILE, L"rb"); - if (fp) - { - fread(szText, 1, sizeof(szText), fp); - fclose(fp); - } - - LPWSTR pch = wcschr(szText, L'|'); - if (pch == NULL) - return FALSE; - - *pch = 0; - StringCchCopyW(pszPath1, MAX_PATH, szText); - StringCchCopyW(pszPath2, MAX_PATH, pch + 1); - return TRUE; -} - -static void DoTestEntry(INT iEntry, const TEST_ENTRY *entry, INT nSources) -{ -#ifdef ENTRY_TICK - DWORD dwOldTick = GetTickCount(); -#endif - - BOOL bInterrupting = FALSE; - if (entry->action) - bInterrupting = entry->action(entry); - - DWORD flags; - LPCSTR pattern; - if ((nSources & SHCNRF_InterruptLevel) && bInterrupting) - { - // The event won't work at here. Manually waiting... - UINT cTry = ((iEntry == 0) ? 100 : 60); - for (UINT iTry = 0; iTry < cTry; ++iTry) - { - flags = SendMessageW(s_hwnd, WM_GET_NOTIFY_FLAGS, 0, 0); - pattern = PatternFromFlags(flags); - if (strcmp(pattern, "0000000") != 0) - break; - - Sleep(1); - } - } - else - { - if (WaitForSingleObject(s_hEvent, 100) == WAIT_OBJECT_0) - Sleep(1); - - flags = SendMessageW(s_hwnd, WM_GET_NOTIFY_FLAGS, 0, 0); - pattern = PatternFromFlags(flags); - } - - SendMessageW(s_hwnd, WM_SET_PATHS, 0, 0); - - WCHAR szPath1[MAX_PATH], szPath2[MAX_PATH]; - szPath1[0] = szPath2[0] = 0; - BOOL bOK = DoGetPaths(szPath1, szPath2); - - static UINT s_cCalmDown = 0; - - if (pattern[TYPE_UPDATEDIR] == '1') - { - trace("Line %d: SHCNE_UPDATEDIR: Calm down (%u)...\n", entry->line, s_cCalmDown); - - if (++s_cCalmDown < 3) - Sleep(3000); - - if (entry->pattern) - ok(TRUE, "Line %d:\n", entry->line); - if (entry->path1) - ok(TRUE, "Line %d:\n", entry->line); - if (entry->path2) - ok(TRUE, "Line %d:\n", entry->line); - } - else - { - s_cCalmDown = 0; - if (entry->pattern) - { - ok(strcmp(pattern, entry->pattern) == 0, - "Line %d: pattern mismatch '%s', tick=0x%08lX\n", - entry->line, pattern, GetTickCount()); - } - if (entry->path1) - ok(bOK && lstrcmpiW(entry->path1, szPath1) == 0, - "Line %d: path1 mismatch '%S' (%d)\n", entry->line, szPath1, bOK); - if (entry->path2) - ok(bOK && lstrcmpiW(entry->path2, szPath2) == 0, - "Line %d: path2 mismatch '%S' (%d)\n", entry->line, szPath2, bOK); - } - - SendMessageW(s_hwnd, WM_CLEAR_FLAGS, 0, 0); - ResetEvent(s_hEvent); - -#ifdef ENTRY_TICK - DWORD dwNewTick = GetTickCount(); - DWORD dwTick = dwNewTick - dwOldTick; - trace("DoTestEntry: Line %d: tick=%lu.%lu sec\n", entry->line, - (dwTick / 1000), (dwTick / 100 % 10)); -#endif -} - -static void DoQuitTest(BOOL bForce) -{ - PostMessageW(s_hwnd, WM_COMMAND, IDOK, 0); - - DoWaitForWindow(CLASSNAME, CLASSNAME, TRUE, bForce); - s_hwnd = NULL; - - if (s_hEvent) - { - CloseHandle(s_hEvent); - s_hEvent = NULL; - } + PostMessageW(s_hSubWnd, WM_COMMAND, IDNO, 0); // Finish + DoWaitForWindow(SUB_CLASSNAME, SUB_CLASSNAME, TRUE, TRUE); // Close sub-windows DoDeleteFilesAndDirs(); } -static void DoAbortThread(void) -{ - skip("Aborting the thread...\n"); - if (s_hThread) - { - TerminateThread(s_hThread, -1); - s_hThread = NULL; - } -} - -static BOOL CALLBACK HandlerRoutine(DWORD dwCtrlType) -{ - switch (dwCtrlType) - { - case CTRL_C_EVENT: - case CTRL_BREAK_EVENT: - DoAbortThread(); - DoQuitTest(TRUE); - return TRUE; - } - return FALSE; -} - -static BOOL DoInitTest(void) -{ - // DIRTYPE_DESKTOP - LPWSTR psz = DoGetDir(DIRTYPE_DESKTOP); - StringCchCopyW(s_szDesktop, _countof(s_szDesktop), psz); - - PathAppendW(psz, TEST_FILE); - StringCchCopyW(s_szTestFile0, _countof(s_szTestFile0), psz); - - PathRemoveFileSpecW(psz); - PathAppendW(psz, TEST_FILE_KAI); - StringCchCopyW(s_szTestFile0Kai, _countof(s_szTestFile0Kai), psz); - - PathRemoveFileSpecW(psz); - PathAppendW(psz, TEST_DIR); - StringCchCopyW(s_szTestDir0, _countof(s_szTestDir0), psz); - - PathRemoveFileSpecW(psz); - PathAppendW(psz, TEST_DIR_KAI); - StringCchCopyW(s_szTestDir0Kai, _countof(s_szTestDir0Kai), psz); - - // DIRTYPE_MYDOCUMENTS - psz = DoGetDir(DIRTYPE_MYDOCUMENTS); - StringCchCopyW(s_szDocuments, _countof(s_szDocuments), psz); - - PathAppendW(psz, TEST_FILE); - StringCchCopyW(s_szTestFile1, _countof(s_szTestFile1), psz); - - PathRemoveFileSpecW(psz); - PathAppendW(psz, TEST_FILE_KAI); - StringCchCopyW(s_szTestFile1Kai, _countof(s_szTestFile1Kai), psz); - - PathRemoveFileSpecW(psz); - PathAppendW(psz, TEST_DIR); - StringCchCopyW(s_szTestDir1, _countof(s_szTestDir1), psz); - - PathRemoveFileSpecW(psz); - PathAppendW(psz, TEST_DIR_KAI); - StringCchCopyW(s_szTestDir1Kai, _countof(s_szTestDir1Kai), psz); - - // prepare for files and dirs - DoDeleteFilesAndDirs(); - DoCreateEmptyFile(TEMP_FILE); - - // Ctrl+C - SetConsoleCtrlHandler(HandlerRoutine, TRUE); - - // close Explorer windows - trace("Closing Explorer windows...\n"); - DoWaitForWindow(L"CabinetWClass", NULL, TRUE, TRUE); - - // close the CLASSNAME windows - return DoWaitForWindow(CLASSNAME, CLASSNAME, TRUE, TRUE) == NULL; -} - -static BOOL -GetSubProgramPath(void) +static BOOL FindSubProgram(void) { GetModuleFileNameW(NULL, s_szSubProgram, _countof(s_szSubProgram)); PathRemoveFileSpecW(s_szSubProgram); @@ -686,258 +107,531 @@ GetSubProgramPath(void) return TRUE; } -#define SRC_00 0 -#define SRC_01 SHCNRF_ShellLevel -#define SRC_02 (SHCNRF_NewDelivery) -#define SRC_03 (SHCNRF_NewDelivery | SHCNRF_ShellLevel) -#define SRC_04 SHCNRF_InterruptLevel -#define SRC_05 (SHCNRF_InterruptLevel | SHCNRF_ShellLevel) -#define SRC_06 (SHCNRF_InterruptLevel | SHCNRF_NewDelivery) -#define SRC_07 (SHCNRF_InterruptLevel | SHCNRF_NewDelivery | SHCNRF_ShellLevel) -#define SRC_08 (SHCNRF_RecursiveInterrupt | SHCNRF_InterruptLevel) -#define SRC_09 (SHCNRF_RecursiveInterrupt | SHCNRF_InterruptLevel | SHCNRF_ShellLevel) -#define SRC_10 (SHCNRF_RecursiveInterrupt | SHCNRF_InterruptLevel | SHCNRF_NewDelivery) -#define SRC_11 (SHCNRF_RecursiveInterrupt | SHCNRF_InterruptLevel | SHCNRF_NewDelivery | SHCNRF_ShellLevel) - -#define WATCHDIR_0 DIRTYPE_NULL -#define WATCHDIR_1 DIRTYPE_DESKTOP -#define WATCHDIR_2 DIRTYPE_MYCOMPUTER -#define WATCHDIR_3 DIRTYPE_MYDOCUMENTS - -static void -DoTestGroup(INT line, UINT cEntries, const TEST_ENTRY *pEntries, BOOL fRecursive, - INT nSources, DIRTYPE iWatchDir) +static void DoBuildFilesAndDirs(void) { -#ifdef NO_INTERRUPT_LEVEL - if (nSources & SHCNRF_InterruptLevel) - return; -#endif -#ifdef NO_SHELL_LEVEL - if (nSources & SHCNRF_ShellLevel) - return; -#endif -#ifdef NEW_DELIVERY_ONLY - if (!(nSources & SHCNRF_NewDelivery)) - return; -#endif -#ifdef GROUP_TICK - DWORD dwOldTick = GetTickCount(); -#endif -#ifdef RANDOM_QUARTER - if ((rand() & 3) == 0) - return; -#elif defined(RANDOM_HALF) - if (rand() & 1) - return; -#endif + WCHAR szPath1[MAX_PATH]; + SHGetSpecialFolderPathW(NULL, szPath1, CSIDL_PERSONAL, FALSE); // My Documents + PathAppendW(szPath1, DIR_1); + StringCchCopyW(s_szDir1, _countof(s_szDir1), szPath1); - trace("DoTestGroup: Line %d: fRecursive:%u, iWatchDir:%u, nSources:0x%X\n", - line, fRecursive, iWatchDir, nSources); + PathAppendW(szPath1, DIR_1); + StringCchCopyW(s_szDir1InDir1, _countof(s_szDir1InDir1), szPath1); + PathRemoveFileSpecW(szPath1); - if (s_hEvent) - { - CloseHandle(s_hEvent); - s_hEvent = NULL; - } - s_hEvent = CreateEventW(NULL, TRUE, FALSE, EVENT_NAME); + PathAppendW(szPath1, DIR_2); + StringCchCopyW(s_szDir2InDir1, _countof(s_szDir2InDir1), szPath1); + PathRemoveFileSpecW(szPath1); - WCHAR szParams[64]; - StringCchPrintfW(szParams, _countof(szParams), L"%u,%u,%u", fRecursive, iWatchDir, nSources); + PathAppendW(szPath1, DIR_1); + PathAppendW(szPath1, FILE_1); + StringCchCopyW(s_szFile1InDir1InDir1, _countof(s_szFile1InDir1InDir1), szPath1); + PathRemoveFileSpecW(szPath1); + PathRemoveFileSpecW(szPath1); - HINSTANCE hinst = ShellExecuteW(NULL, NULL, s_szSubProgram, szParams, NULL, SW_SHOWNORMAL); - if ((INT_PTR)hinst <= 32) - { - skip("Unable to run shell32_apitest_sub.exe.\n"); - return; - } + PathAppendW(szPath1, FILE_1); + StringCchCopyW(s_szFile1InDir1, _countof(s_szFile1InDir1), szPath1); + PathRemoveFileSpecW(szPath1); - s_hwnd = DoWaitForWindow(CLASSNAME, CLASSNAME, FALSE, FALSE); - if (!s_hwnd) - { - skip("Unable to find window.\n"); - return; - } + PathAppendW(szPath1, FILE_2); + StringCchCopyW(s_szFile2InDir1, _countof(s_szFile2InDir1), szPath1); + PathRemoveFileSpecW(szPath1); - for (UINT i = 0; i < cEntries; ++i) - { - if (!IsWindow(s_hwnd)) - { - DoAbortThread(); - DoQuitTest(TRUE); - break; - } +#define TRACE_PATH(path) trace(#path ": %ls\n", path) + TRACE_PATH(s_szDir1); + TRACE_PATH(s_szDir1InDir1); + TRACE_PATH(s_szFile1InDir1); + TRACE_PATH(s_szFile1InDir1InDir1); + TRACE_PATH(s_szFile2InDir1); +#undef TRACE_PATH - DoTestEntry(i, &pEntries[i], nSources); - } + DoDeleteFilesAndDirs(); - DoQuitTest(FALSE); + ::CreateDirectoryW(s_szDir1, NULL); + ok_int(!!PathIsDirectoryW(s_szDir1), TRUE); -#ifdef GROUP_TICK - DWORD dwNewTick = GetTickCount(); - DWORD dwTick = dwNewTick - dwOldTick; - trace("DoTestGroup: Line %d: %lu.%lu sec\n", line, (dwTick / 1000), (dwTick / 100 % 10)); -#endif + DoDeleteDirectory(s_szDir1InDir1); + ok_int(!PathIsDirectoryW(s_szDir1InDir1), TRUE); } -static unsigned __stdcall TestThreadProc(void *) +static void DoTestEntry(LONG lEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { - srand(time(NULL)); -#ifdef RANDOM_QUARTER - skip("RANDOM_QUARTER\n"); -#elif defined(RANDOM_HALF) - skip("RANDOM_HALF\n"); -#endif + WCHAR szPath1[MAX_PATH], szPath2[MAX_PATH]; - // fRecursive == FALSE. - DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_00, WATCHDIR_0); - DoTestGroup(__LINE__, _countof(s_group_01), s_group_01, FALSE, SRC_01, WATCHDIR_0); - DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_02, WATCHDIR_0); - DoTestGroup(__LINE__, _countof(s_group_01), s_group_01, FALSE, SRC_03, WATCHDIR_0); - DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, FALSE, SRC_04, WATCHDIR_0); - DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, FALSE, SRC_05, WATCHDIR_0); - DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, FALSE, SRC_06, WATCHDIR_0); - DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, FALSE, SRC_07, WATCHDIR_0); - DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, FALSE, SRC_08, WATCHDIR_0); - DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, FALSE, SRC_09, WATCHDIR_0); - DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, FALSE, SRC_10, WATCHDIR_0); - DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, FALSE, SRC_11, WATCHDIR_0); + szPath1[0] = szPath2[0] = 0; + SHGetPathFromIDListW(pidl1, szPath1); + SHGetPathFromIDListW(pidl2, szPath2); - BOOL bTarget = IsWindowsXPOrGreater() && !IsWindowsVistaOrGreater(); + trace("(0x%lX, '%ls', '%ls')\n", lEvent, szPath1, szPath2); -#define SWITCH(x, y) (bTarget ? (x) : (y)) - DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_00, WATCHDIR_1); - DoTestGroup(__LINE__, _countof(s_group_03), s_group_03, FALSE, SRC_01, WATCHDIR_1); - DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_02, WATCHDIR_1); - DoTestGroup(__LINE__, _countof(s_group_03), s_group_03, FALSE, SRC_03, WATCHDIR_1); - DoTestGroup(__LINE__, SWITCH(_countof(s_group_00), _countof(s_group_04)), SWITCH(s_group_00, s_group_04), FALSE, SRC_04, WATCHDIR_1); - DoTestGroup(__LINE__, _countof(s_group_07), s_group_07, FALSE, SRC_05, WATCHDIR_1); - DoTestGroup(__LINE__, SWITCH(_countof(s_group_00), _countof(s_group_04)), SWITCH(s_group_00, s_group_04), FALSE, SRC_06, WATCHDIR_1); - DoTestGroup(__LINE__, _countof(s_group_07), s_group_07, FALSE, SRC_07, WATCHDIR_1); - DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, FALSE, SRC_08, WATCHDIR_1); - DoTestGroup(__LINE__, _countof(s_group_07), s_group_07, FALSE, SRC_09, WATCHDIR_1); - DoTestGroup(__LINE__, SWITCH(_countof(s_group_00), _countof(s_group_04)), SWITCH(s_group_00, s_group_04), FALSE, SRC_06, WATCHDIR_1); - DoTestGroup(__LINE__, _countof(s_group_07), s_group_07, FALSE, SRC_11, WATCHDIR_1); -#undef SWITCH + if (lEvent == SHCNE_UPDATEDIR) + { + trace("Got SHCNE_UPDATEDIR\n"); + s_bGotUpdateDir = TRUE; + return; + } -#ifndef NO_TRIVIAL - DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_00, WATCHDIR_2); - DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_01, WATCHDIR_2); - DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_02, WATCHDIR_2); - DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_03, WATCHDIR_2); - DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_04, WATCHDIR_2); - DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_05, WATCHDIR_2); - DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_06, WATCHDIR_2); - DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_07, WATCHDIR_2); - DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_08, WATCHDIR_2); - DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_09, WATCHDIR_2); - DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_10, WATCHDIR_2); - DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_11, WATCHDIR_2); -#endif + INT iEventType = GetEventType(lEvent); + if (iEventType < 0) + return; - DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_00, WATCHDIR_3); - DoTestGroup(__LINE__, _countof(s_group_02), s_group_02, FALSE, SRC_01, WATCHDIR_3); - DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, FALSE, SRC_02, WATCHDIR_3); - DoTestGroup(__LINE__, _countof(s_group_02), s_group_02, FALSE, SRC_03, WATCHDIR_3); - DoTestGroup(__LINE__, _countof(s_group_05), s_group_05, FALSE, SRC_04, WATCHDIR_3); - DoTestGroup(__LINE__, _countof(s_group_08), s_group_08, FALSE, SRC_05, WATCHDIR_3); - DoTestGroup(__LINE__, _countof(s_group_05), s_group_05, FALSE, SRC_06, WATCHDIR_3); - DoTestGroup(__LINE__, _countof(s_group_08), s_group_08, FALSE, SRC_07, WATCHDIR_3); - DoTestGroup(__LINE__, _countof(s_group_05), s_group_05, FALSE, SRC_08, WATCHDIR_3); - DoTestGroup(__LINE__, _countof(s_group_08), s_group_08, FALSE, SRC_09, WATCHDIR_3); - DoTestGroup(__LINE__, _countof(s_group_05), s_group_05, FALSE, SRC_10, WATCHDIR_3); - DoTestGroup(__LINE__, _countof(s_group_08), s_group_08, FALSE, SRC_11, WATCHDIR_3); + assert(iEventType < MAX_EVENT_TYPE); - // fRecursive == TRUE. - DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, TRUE, SRC_00, WATCHDIR_0); - DoTestGroup(__LINE__, _countof(s_group_01), s_group_01, TRUE, SRC_01, WATCHDIR_0); - DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, TRUE, SRC_02, WATCHDIR_0); - DoTestGroup(__LINE__, _countof(s_group_01), s_group_01, TRUE, SRC_03, WATCHDIR_0); - DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_04, WATCHDIR_0); - DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_05, WATCHDIR_0); - DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_06, WATCHDIR_0); - DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_07, WATCHDIR_0); - DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_08, WATCHDIR_0); - DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_09, WATCHDIR_0); - DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_10, WATCHDIR_0); - DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_11, WATCHDIR_0); + INT i = 0; + s_abChecks[i++] |= (lstrcmpiW(szPath1, L"") == 0) << iEventType; + s_abChecks[i++] |= (lstrcmpiW(szPath1, s_szDir1) == 0) << iEventType; + s_abChecks[i++] |= (lstrcmpiW(szPath1, s_szDir2InDir1) == 0) << iEventType; + s_abChecks[i++] |= (lstrcmpiW(szPath1, s_szFile1InDir1) == 0) << iEventType; + s_abChecks[i++] |= (lstrcmpiW(szPath1, s_szFile2InDir1) == 0) << iEventType; + s_abChecks[i++] |= (lstrcmpiW(szPath1, s_szFile1InDir1InDir1) == 0) << iEventType; + s_abChecks[i++] |= (lstrcmpiW(szPath2, L"") == 0) << iEventType; + s_abChecks[i++] |= (lstrcmpiW(szPath2, s_szDir1) == 0) << iEventType; + s_abChecks[i++] |= (lstrcmpiW(szPath2, s_szDir2InDir1) == 0) << iEventType; + s_abChecks[i++] |= (lstrcmpiW(szPath2, s_szFile1InDir1) == 0) << iEventType; + s_abChecks[i++] |= (lstrcmpiW(szPath2, s_szFile2InDir1) == 0) << iEventType; + s_abChecks[i++] |= (lstrcmpiW(szPath2, s_szFile1InDir1InDir1) == 0) << iEventType; + assert(i == NUM_CHECKS); +} - DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, TRUE, SRC_00, WATCHDIR_1); - DoTestGroup(__LINE__, _countof(s_group_01), s_group_01, TRUE, SRC_01, WATCHDIR_1); - DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, TRUE, SRC_02, WATCHDIR_1); - DoTestGroup(__LINE__, _countof(s_group_01), s_group_01, TRUE, SRC_03, WATCHDIR_1); - DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_04, WATCHDIR_1); - DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_05, WATCHDIR_1); - DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_06, WATCHDIR_1); - DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_07, WATCHDIR_1); - DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_08, WATCHDIR_1); - DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_09, WATCHDIR_1); - DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_10, WATCHDIR_1); - DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_11, WATCHDIR_1); +static LPCSTR StringFromChecks(void) +{ + static char s_sz[2 * NUM_CHECKS + 1]; - DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, TRUE, SRC_00, WATCHDIR_2); - DoTestGroup(__LINE__, _countof(s_group_01), s_group_01, TRUE, SRC_01, WATCHDIR_2); - DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, TRUE, SRC_02, WATCHDIR_2); - DoTestGroup(__LINE__, _countof(s_group_01), s_group_01, TRUE, SRC_03, WATCHDIR_2); - DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_04, WATCHDIR_2); - DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_05, WATCHDIR_2); - DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_06, WATCHDIR_2); - DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_07, WATCHDIR_2); - DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_08, WATCHDIR_2); - DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_09, WATCHDIR_2); - DoTestGroup(__LINE__, _countof(s_group_04), s_group_04, TRUE, SRC_10, WATCHDIR_2); - DoTestGroup(__LINE__, _countof(s_group_06), s_group_06, TRUE, SRC_11, WATCHDIR_2); + char *pch = s_sz; + for (INT i = 0; i < NUM_CHECKS; ++i) + { + WCHAR sz[3]; + StringCchPrintfW(sz, _countof(sz), L"%02X", s_abChecks[i]); + *pch++ = sz[0]; + *pch++ = sz[1]; + } - DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, TRUE, SRC_00, WATCHDIR_3); - DoTestGroup(__LINE__, _countof(s_group_02), s_group_02, TRUE, SRC_01, WATCHDIR_3); - DoTestGroup(__LINE__, _countof(s_group_00), s_group_00, TRUE, SRC_02, WATCHDIR_3); - DoTestGroup(__LINE__, _countof(s_group_02), s_group_02, TRUE, SRC_03, WATCHDIR_3); - DoTestGroup(__LINE__, _countof(s_group_05), s_group_05, TRUE, SRC_04, WATCHDIR_3); - DoTestGroup(__LINE__, _countof(s_group_08), s_group_08, TRUE, SRC_05, WATCHDIR_3); - DoTestGroup(__LINE__, _countof(s_group_05), s_group_05, TRUE, SRC_06, WATCHDIR_3); - DoTestGroup(__LINE__, _countof(s_group_08), s_group_08, TRUE, SRC_07, WATCHDIR_3); - DoTestGroup(__LINE__, _countof(s_group_05), s_group_05, TRUE, SRC_08, WATCHDIR_3); - DoTestGroup(__LINE__, _countof(s_group_08), s_group_08, TRUE, SRC_09, WATCHDIR_3); - DoTestGroup(__LINE__, _countof(s_group_05), s_group_05, TRUE, SRC_10, WATCHDIR_3); - DoTestGroup(__LINE__, _countof(s_group_08), s_group_08, TRUE, SRC_11, WATCHDIR_3); + assert((pch - s_sz) + 1 == sizeof(s_sz)); + + *pch = 0; + return s_sz; +} + +struct TEST_ANSWER +{ + INT lineno; + LPCSTR answer; +}; + +static void DoStepCheck(INT iStage, INT iStep, LPCSTR checks) +{ + assert(0 <= iStep); + assert(iStep < NUM_STEP); + + assert(0 <= iStage); + assert(iStage < NUM_STAGE); + + if (s_bGotUpdateDir) + { + ok_int(TRUE, TRUE); + return; + } + + LPCSTR answer = NULL; + INT lineno; + switch (iStage) + { + case 0: + { + static const TEST_ANSWER c_answers[] = + { + { __LINE__, "000000010000010000000000" }, // 0 + { __LINE__, "000000040000000000000400" }, // 1 + { __LINE__, "000000000200020000000000" }, // 2 + { __LINE__, "000000000000080000000000" }, // 3 + { __LINE__, "000000000001010000000000" }, // 4 + { __LINE__, "000000000002020000000000" }, // 5 + { __LINE__, "000000000000000020000000" }, // 6 + { __LINE__, "000010000000100000000000" }, // 7 + }; + C_ASSERT(_countof(c_answers) == NUM_STEP); + lineno = c_answers[iStep].lineno; + answer = c_answers[iStep].answer; + break; + } + case 1: + { + static const TEST_ANSWER c_answers[] = + { + { __LINE__, "000000010000010000000000" }, // 0 + { __LINE__, "000000040000000000000400" }, // 1 + { __LINE__, "000000000200020000000000" }, // 2 + { __LINE__, "000000000000080000000000" }, // 3 + { __LINE__, "000000000001010000000000" }, // 4 + { __LINE__, "000000000002020000000000" }, // 5 + { __LINE__, "000000000000000020000000" }, // 6 + { __LINE__, "000010000000100000000000" }, // 7 + }; + C_ASSERT(_countof(c_answers) == NUM_STEP); + lineno = c_answers[iStep].lineno; + answer = c_answers[iStep].answer; + break; + } + case 2: + { + static const TEST_ANSWER c_answers[] = + { + { __LINE__, "000000010000010000000000" }, // 0 + { __LINE__, "000000040000000000000400" }, // 1 + { __LINE__, "000000000200020000000000" }, // 2 + { __LINE__, "000000000000080000000000" }, // 3 + { __LINE__, "000000000001010000000000" }, // 4 // Recursive + { __LINE__, "000000000002020000000000" }, // 5 // Recursive + { __LINE__, "000000000000000020000000" }, // 6 + { __LINE__, "000010000000100000000000" }, // 7 + }; + C_ASSERT(_countof(c_answers) == NUM_STEP); + lineno = c_answers[iStep].lineno; + answer = c_answers[iStep].answer; + if (iStep == 4 || iStep == 5) // Recursive cases + { + if (lstrcmpA(checks, "000000000000000000000000") == 0) + answer = "000000000000000000000000"; + else + trace("Warning\n"); + } + break; + } + case 3: + { + static const TEST_ANSWER c_answers[] = + { + { __LINE__, "000000010000010000000000" }, // 0 + { __LINE__, "000000040000000000000400" }, // 1 + { __LINE__, "000000000200020000000000" }, // 2 + { __LINE__, "000000000000080000000000" }, // 3 + { __LINE__, "000000000001010000000000" }, // 4 // Recursive + { __LINE__, "000000000002020000000000" }, // 5 // Recursive + { __LINE__, "000000000000000020000000" }, // 6 + { __LINE__, "000010000000100000000000" }, // 7 + }; + C_ASSERT(_countof(c_answers) == NUM_STEP); + lineno = c_answers[iStep].lineno; + answer = c_answers[iStep].answer; + break; + } + default: + { + assert(0); + break; + } + } + + ok(lstrcmpA(checks, answer) == 0, + "Line %d: '%s' vs '%s' at Stage %d, Step %d\n", lineno, checks, answer, iStage, iStep); +} + +static DWORD WINAPI StageThreadFunc(LPVOID arg) +{ + BOOL ret; + + trace("Stage %d\n", s_iStage); + + // 0: Create file1 in dir1 + s_iStep = 0; + trace("Step %d\n", s_iStep); + ::Sleep(1000); // Extra wait + ZeroMemory(s_abChecks, sizeof(s_abChecks)); + ret = DoCreateFile(s_szFile1InDir1); + ok_int(ret, TRUE); + SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW | SHCNF_FLUSH | SHCNF_FLUSHNOWAIT, s_szFile1InDir1, 0); + ::Sleep(INTERVAL); + DoStepCheck(s_iStage, s_iStep, StringFromChecks()); + + // 1: Rename file1 as file2 in dir1 + ++s_iStep; + trace("Step %d\n", s_iStep); + ZeroMemory(s_abChecks, sizeof(s_abChecks)); + ret = MoveFileW(s_szFile1InDir1, s_szFile2InDir1); + ok_int(ret, TRUE); + SHChangeNotify(SHCNE_RENAMEITEM, SHCNF_PATHW | SHCNF_FLUSH | SHCNF_FLUSHNOWAIT, s_szFile1InDir1, s_szFile2InDir1); + ::Sleep(INTERVAL); + DoStepCheck(s_iStage, s_iStep, StringFromChecks()); + + // 2: Delete file2 in dir1 + ++s_iStep; + trace("Step %d\n", s_iStep); + ZeroMemory(s_abChecks, sizeof(s_abChecks)); + ret = DeleteFileW(s_szFile2InDir1); + ok_int(ret, TRUE); + SHChangeNotify(SHCNE_DELETE, SHCNF_PATHW | SHCNF_FLUSH | SHCNF_FLUSHNOWAIT, s_szFile2InDir1, NULL); + ::Sleep(INTERVAL); + DoStepCheck(s_iStage, s_iStep, StringFromChecks()); + + // 3: Create dir1 in dir1 + ++s_iStep; + trace("Step %d\n", s_iStep); + ZeroMemory(s_abChecks, sizeof(s_abChecks)); + ret = CreateDirectoryExW(s_szDir1, s_szDir1InDir1, NULL); + ok_int(ret, TRUE); + SHChangeNotify(SHCNE_MKDIR, SHCNF_PATHW | SHCNF_FLUSH | SHCNF_FLUSHNOWAIT, s_szDir1InDir1, NULL); + ::Sleep(INTERVAL); + DoStepCheck(s_iStage, s_iStep, StringFromChecks()); + + // 4: Create file1 in dir1 in dir1 + ++s_iStep; + trace("Step %d\n", s_iStep); + ZeroMemory(s_abChecks, sizeof(s_abChecks)); + ret = DoCreateFile(s_szFile1InDir1InDir1); + ok_int(ret, TRUE); + SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW | SHCNF_FLUSH | SHCNF_FLUSHNOWAIT, s_szFile1InDir1InDir1, NULL); + ::Sleep(INTERVAL); + DoStepCheck(s_iStage, s_iStep, StringFromChecks()); + + // 5: Delete file1 in dir1 in dir1 + ++s_iStep; + trace("Step %d\n", s_iStep); + ZeroMemory(s_abChecks, sizeof(s_abChecks)); + ret = DeleteFileW(s_szFile1InDir1InDir1); + ok_int(ret, TRUE); + SHChangeNotify(SHCNE_DELETE, SHCNF_PATHW | SHCNF_FLUSH | SHCNF_FLUSHNOWAIT, s_szFile1InDir1InDir1, NULL); + ::Sleep(INTERVAL); + DoStepCheck(s_iStage, s_iStep, StringFromChecks()); + + // 6: Rename dir1 as dir2 in dir1 + ++s_iStep; + trace("Step %d\n", s_iStep); + ZeroMemory(s_abChecks, sizeof(s_abChecks)); + ret = ::MoveFileW(s_szDir1InDir1, s_szDir2InDir1); + ok_int(ret, TRUE); + SHChangeNotify(SHCNE_RENAMEFOLDER, SHCNF_PATHW | SHCNF_FLUSH | SHCNF_FLUSHNOWAIT, s_szDir1InDir1, s_szDir2InDir1); + ::Sleep(INTERVAL); + DoStepCheck(s_iStage, s_iStep, StringFromChecks()); + + // 7: Remove dir2 in dir1 + ++s_iStep; + trace("Step %d\n", s_iStep); + ZeroMemory(s_abChecks, sizeof(s_abChecks)); + ret = RemoveDirectoryW(s_szDir2InDir1); + ok_int(ret, TRUE); + SHChangeNotify(SHCNE_RMDIR, SHCNF_PATHW | SHCNF_FLUSH | SHCNF_FLUSHNOWAIT, s_szDir2InDir1, NULL); + ::Sleep(INTERVAL); + DoStepCheck(s_iStage, s_iStep, StringFromChecks()); + + // 8: Finish + ++s_iStep; + assert(s_iStep == NUM_STEP); + C_ASSERT(NUM_STEP == 8); + if (s_iStage + 1 < NUM_STAGE) + { + ::PostMessage(s_hSubWnd, WM_COMMAND, IDRETRY, 0); // Next stage + } + else + { + // Finish + ::PostMessage(s_hSubWnd, WM_COMMAND, IDNO, 0); + ::PostMessage(s_hMainWnd, WM_COMMAND, IDNO, 0); + } + + s_iStep = -1; return 0; } -START_TEST(SHChangeNotify) +// WM_COPYDATA +static BOOL OnCopyData(HWND hwnd, HWND hwndSender, COPYDATASTRUCT *pCopyData) { -#ifdef TOTAL_TICK - DWORD dwOldTick = GetTickCount(); -#endif -#ifdef DISABLE_THIS_TESTCASE - skip("This testcase is disabled by DISABLE_THIS_TESTCASE macro.\n"); - return; -#endif + if (pCopyData->dwData != 0xBEEFCAFE) + return FALSE; - trace("Please don't operate your PC while testing...\n"); + LPBYTE pbData = (LPBYTE)pCopyData->lpData; + LPBYTE pb = pbData; - if (!GetSubProgramPath()) + LONG cbTotal = pCopyData->cbData; + assert(cbTotal >= LONG(sizeof(LONG) + sizeof(DWORD) + sizeof(DWORD))); + + LONG lEvent = *(LONG*)pb; + pb += sizeof(lEvent); + + DWORD cbPidl1 = *(DWORD*)pb; + pb += sizeof(cbPidl1); + + DWORD cbPidl2 = *(DWORD*)pb; + pb += sizeof(cbPidl2); + + LPITEMIDLIST pidl1 = NULL; + if (cbPidl1) + { + pidl1 = (LPITEMIDLIST)CoTaskMemAlloc(cbPidl1); + CopyMemory(pidl1, pb, cbPidl1); + pb += cbPidl1; + } + + LPITEMIDLIST pidl2 = NULL; + if (cbPidl2) + { + pidl2 = (LPITEMIDLIST)CoTaskMemAlloc(cbPidl2); + CopyMemory(pidl2, pb, cbPidl2); + pb += cbPidl2; + } + + assert((pb - pbData) == cbTotal); + + DoTestEntry(lEvent, pidl1, pidl2); + + CoTaskMemFree(pidl1); + CoTaskMemFree(pidl2); + + return TRUE; +} + +static LRESULT CALLBACK +MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_CREATE: + s_hMainWnd = hwnd; + return 0; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDYES: // Start testing + { + s_iStage = 0; + s_bGotUpdateDir = FALSE; + s_hThread = ::CreateThread(NULL, 0, StageThreadFunc, hwnd, 0, NULL); + if (!s_hThread) + { + skip("!s_hThread\n"); + DestroyWindow(hwnd); + } + break; + } + case IDRETRY: // New stage + { + ::CloseHandle(s_hThread); + ++s_iStage; + s_bGotUpdateDir = FALSE; + s_hThread = ::CreateThread(NULL, 0, StageThreadFunc, hwnd, 0, NULL); + if (!s_hThread) + { + skip("!s_hThread\n"); + DestroyWindow(hwnd); + } + break; + } + case IDNO: // Quit + { + s_iStage = -1; + DestroyWindow(hwnd); + break; + } + } + break; + + case WM_COPYDATA: + if (s_iStage < 0 || s_iStep < 0) + break; + + OnCopyData(hwnd, (HWND)wParam, (COPYDATASTRUCT*)lParam); + break; + + case WM_DESTROY: + ::PostQuitMessage(0); + break; + + default: + return ::DefWindowProcW(hwnd, uMsg, wParam, lParam); + } + return 0; +} + +static BOOL TEST_Init(void) +{ + if (!FindSubProgram()) { skip("shell32_apitest_sub.exe not found\n"); - return; + return FALSE; } - if (!DoInitTest()) + // close the SUB_CLASSNAME windows + DoWaitForWindow(SUB_CLASSNAME, SUB_CLASSNAME, TRUE, TRUE); + + // Execute sub program + HINSTANCE hinst = ShellExecuteW(NULL, NULL, s_szSubProgram, L"---", NULL, SW_SHOWNORMAL); + if ((INT_PTR)hinst <= 32) { - skip("Unable to initialize.\n"); - DoQuitTest(TRUE); + skip("Unable to run shell32_apitest_sub.exe.\n"); + return FALSE; + } + + // prepare for files and dirs + DoBuildFilesAndDirs(); + + // Register main window + WNDCLASSW wc = { 0, MainWndProc }; + wc.hInstance = GetModuleHandleW(NULL); + wc.hIcon = LoadIconW(NULL, IDI_APPLICATION); + wc.hCursor = LoadCursorW(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); + wc.lpszClassName = MAIN_CLASSNAME; + if (!RegisterClassW(&wc)) + { + skip("RegisterClassW failed\n"); + return FALSE; + } + + // Create main window + HWND hwnd = CreateWindowW(MAIN_CLASSNAME, MAIN_CLASSNAME, WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, 400, 100, + NULL, NULL, GetModuleHandleW(NULL), NULL); + if (!hwnd) + { + skip("CreateWindowW failed\n"); + return FALSE; + } + ::ShowWindow(hwnd, SW_SHOWNORMAL); + ::UpdateWindow(hwnd); + + // Find sub-window + s_hSubWnd = DoWaitForWindow(SUB_CLASSNAME, SUB_CLASSNAME, FALSE, FALSE); + if (!s_hSubWnd) + { + skip("Unable to find sub-program window.\n"); + return FALSE; + } + + // Start testing + SendMessageW(s_hSubWnd, WM_COMMAND, IDYES, 0); + + return TRUE; +} + +static void TEST_Main(void) +{ + if (!TEST_Init()) + { + skip("Unable to start testing.\n"); + TEST_Quit(); return; } - s_hThread = (HANDLE)_beginthreadex(NULL, 0, TestThreadProc, NULL, 0, NULL); - WaitForSingleObject(s_hThread, INFINITE); - CloseHandle(s_hThread); + // Message loop + MSG msg; + while (GetMessageW(&msg, NULL, 0, 0)) + { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } -#ifdef TOTAL_TICK + TEST_Quit(); +} + +START_TEST(SHChangeNotify) +{ + trace("Please close all Explorer windows before testing.\n"); + trace("Please don't operate your PC while testing.\n"); + + DWORD dwOldTick = GetTickCount(); + TEST_Main(); DWORD dwNewTick = GetTickCount(); + DWORD dwTick = dwNewTick - dwOldTick; trace("SHChangeNotify: Total %lu.%lu sec\n", (dwTick / 1000), (dwTick / 100 % 10)); -#endif - - DoWaitForWindow(CLASSNAME, CLASSNAME, TRUE, TRUE); - Sleep(100); } diff --git a/modules/rostests/apitests/shell32/ShellExecCmdLine.cpp b/modules/rostests/apitests/shell32/ShellExecCmdLine.cpp index 218ff08ed88..c0a2104a25b 100644 --- a/modules/rostests/apitests/shell32/ShellExecCmdLine.cpp +++ b/modules/rostests/apitests/shell32/ShellExecCmdLine.cpp @@ -747,6 +747,6 @@ START_TEST(ShellExecCmdLine) ok(DeleteFileA("Test File 2.bat"), "failed to delete the test file\n"); free(s_wi0.phwnd); - DoWaitForWindow(CLASSNAME, CLASSNAME, TRUE, TRUE); + DoWaitForWindow(SUB_CLASSNAME, SUB_CLASSNAME, TRUE, TRUE); Sleep(100); } diff --git a/modules/rostests/apitests/shell32/shell32_apitest_sub.cpp b/modules/rostests/apitests/shell32/shell32_apitest_sub.cpp index 5a56b08aefb..db591a9f0d6 100644 --- a/modules/rostests/apitests/shell32/shell32_apitest_sub.cpp +++ b/modules/rostests/apitests/shell32/shell32_apitest_sub.cpp @@ -2,150 +2,239 @@ * PROJECT: ReactOS api tests * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) * PURPOSE: Test for SHChangeNotify - * COPYRIGHT: Copyright 2020 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) + * COPYRIGHT: Copyright 2020-2024 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) */ +// This program is used in SHChangeNotify and ShellExecCmdLine testcases. + #include "shelltest.h" #include "shell32_apitest_sub.h" +#include -static HWND s_hwnd = NULL; +typedef enum DIRTYPE +{ + DIRTYPE_DESKTOP = 0, + DIRTYPE_DRIVES, + DIRTYPE_DIR1, + DIRTYPE_MAX +} DIRTYPE; + +static HWND s_hMainWnd = NULL, s_hSubWnd = NULL; +static LPITEMIDLIST s_pidl[DIRTYPE_MAX]; static UINT s_uRegID = 0; -static BOOL s_fRecursive = FALSE; -static DIRTYPE s_iWatchDir = DIRTYPE_NULL; -static INT s_nSources = 0; -static LPITEMIDLIST s_pidl = NULL; -static WCHAR s_path1[MAX_PATH], s_path2[MAX_PATH]; -static BYTE s_counters[TYPE_MAX + 1]; -static HANDLE s_hEvent = NULL; +static INT s_iStage = -1; -static BOOL -OnCreate(HWND hwnd) +#define NUM_STAGE 4 + +#define EVENTS (SHCNE_CREATE | SHCNE_DELETE | SHCNE_MKDIR | SHCNE_RMDIR | \ + SHCNE_RENAMEFOLDER | SHCNE_RENAMEITEM | SHCNE_UPDATEDIR | SHCNE_UPDATEITEM) + +inline LPITEMIDLIST DoGetPidl(INT iDir) { - s_hwnd = hwnd; - s_pidl = DoGetPidl(s_iWatchDir); + LPITEMIDLIST ret = NULL; - SHChangeNotifyEntry entry; - entry.pidl = s_pidl; - entry.fRecursive = s_fRecursive; - s_uRegID = SHChangeNotifyRegister(hwnd, s_nSources, SHCNE_ALLEVENTS, WM_SHELL_NOTIFY, 1, &entry); - return s_uRegID != 0; -} - -static void -OnCommand(HWND hwnd, UINT id) -{ - switch (id) + switch (iDir) { - case IDOK: - case IDCANCEL: - DestroyWindow(hwnd); + case DIRTYPE_DESKTOP: + { + SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &ret); break; - } -} - -static void -OnDestroy(HWND hwnd) -{ - SHChangeNotifyDeregister(s_uRegID); - s_uRegID = 0; - - CoTaskMemFree(s_pidl); - s_pidl = NULL; - - PostQuitMessage(0); - s_hwnd = NULL; -} - -static BOOL DoPathes(PIDLIST_ABSOLUTE pidl1, PIDLIST_ABSOLUTE pidl2) -{ - WCHAR path[MAX_PATH]; - if (!SHGetPathFromIDListW(pidl1, path)) - { - s_path1[0] = s_path2[0] = 0; - return FALSE; + } + case DIRTYPE_DRIVES: + { + SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES, &ret); + break; + } + case DIRTYPE_DIR1: + { + WCHAR szPath1[MAX_PATH]; + SHGetSpecialFolderPathW(NULL, szPath1, CSIDL_PERSONAL, FALSE); // My Documents + PathAppendW(szPath1, L"_TESTDIR_1_"); + ret = ILCreateFromPathW(szPath1); + break; + } + default: + { + assert(0); + break; + } } - if (wcsstr(path, L"Recent") != NULL) - return FALSE; + return ret; +} - StringCchCopyW(s_path1, _countof(s_path1), path); +static BOOL OnCreate(HWND hwnd) +{ + s_hSubWnd = hwnd; - if (!SHGetPathFromIDListW(pidl2, s_path2)) - s_path2[0] = 0; + for (INT i = 0; i < DIRTYPE_MAX; ++i) + s_pidl[i] = DoGetPidl(i); return TRUE; } -static VOID DoPathesAndFlags(UINT type, PIDLIST_ABSOLUTE pidl1, PIDLIST_ABSOLUTE pidl2) +static BOOL InitSHCN(HWND hwnd) { - if (DoPathes(pidl1, pidl2)) - { - s_counters[type] = 1; - SetEvent(s_hEvent); - } -} + assert(0 <= s_iStage); + assert(s_iStage < NUM_STAGE); -static void -DoShellNotify(HWND hwnd, PIDLIST_ABSOLUTE pidl1, PIDLIST_ABSOLUTE pidl2, LONG lEvent) -{ - switch (lEvent) + SHChangeNotifyEntry entry; + INT sources; + LONG events; + switch (s_iStage) { - case SHCNE_RENAMEITEM: - DoPathesAndFlags(TYPE_RENAMEITEM, pidl1, pidl2); + case 0: + { + entry.fRecursive = TRUE; + entry.pidl = s_pidl[DIRTYPE_DESKTOP]; + sources = SHCNRF_NewDelivery | SHCNRF_ShellLevel; + events = EVENTS; break; - case SHCNE_CREATE: - DoPathesAndFlags(TYPE_CREATE, pidl1, pidl2); + } + case 1: + { + entry.fRecursive = TRUE; + entry.pidl = s_pidl[DIRTYPE_DRIVES]; + sources = SHCNRF_NewDelivery | SHCNRF_ShellLevel; + events = EVENTS; break; - case SHCNE_DELETE: - DoPathesAndFlags(TYPE_DELETE, pidl1, pidl2); + } + case 2: + { + entry.fRecursive = FALSE; + entry.pidl = s_pidl[DIRTYPE_DIR1]; + sources = SHCNRF_NewDelivery | SHCNRF_ShellLevel; + events = EVENTS; break; - case SHCNE_MKDIR: - DoPathesAndFlags(TYPE_MKDIR, pidl1, pidl2); - break; - case SHCNE_RMDIR: - DoPathesAndFlags(TYPE_RMDIR, pidl1, pidl2); - break; - case SHCNE_MEDIAINSERTED: - break; - case SHCNE_MEDIAREMOVED: - break; - case SHCNE_DRIVEREMOVED: - break; - case SHCNE_DRIVEADD: - break; - case SHCNE_NETSHARE: - break; - case SHCNE_NETUNSHARE: - break; - case SHCNE_ATTRIBUTES: - break; - case SHCNE_UPDATEDIR: - DoPathesAndFlags(TYPE_UPDATEDIR, pidl1, pidl2); - break; - case SHCNE_UPDATEITEM: - break; - case SHCNE_SERVERDISCONNECT: - break; - case SHCNE_UPDATEIMAGE: - break; - case SHCNE_DRIVEADDGUI: - break; - case SHCNE_RENAMEFOLDER: - DoPathesAndFlags(TYPE_RENAMEFOLDER, pidl1, pidl2); - break; - case SHCNE_FREESPACE: - break; - case SHCNE_EXTENDED_EVENT: - break; - case SHCNE_ASSOCCHANGED: + } + case 3: + { + entry.fRecursive = TRUE; + entry.pidl = s_pidl[DIRTYPE_DIR1]; + sources = SHCNRF_NewDelivery | SHCNRF_ShellLevel | SHCNRF_InterruptLevel | + SHCNRF_RecursiveInterrupt; + events = EVENTS; break; + } default: + { + assert(0); break; + } + } + + s_uRegID = SHChangeNotifyRegister(hwnd, sources, events, WM_SHELL_NOTIFY, 1, &entry); + if (s_uRegID == 0) + return FALSE; + + return TRUE; +} + +static void UnInitSHCN(HWND hwnd) +{ + if (s_uRegID) + { + SHChangeNotifyDeregister(s_uRegID); + s_uRegID = 0; } } -static INT_PTR -OnShellNotify(HWND hwnd, WPARAM wParam, LPARAM lParam) +static void OnCommand(HWND hwnd, UINT id) +{ + switch (id) + { + case IDYES: // Start testing + { + s_hMainWnd = ::FindWindow(MAIN_CLASSNAME, MAIN_CLASSNAME); + if (!s_hMainWnd) + { + ::DestroyWindow(hwnd); + break; + } + s_iStage = 0; + InitSHCN(hwnd); + ::PostMessageW(s_hMainWnd, WM_COMMAND, IDYES, 0); + break; + } + case IDRETRY: // New stage + { + UnInitSHCN(hwnd); + ++s_iStage; + InitSHCN(hwnd); + ::PostMessageW(s_hMainWnd, WM_COMMAND, IDRETRY, 0); + break; + } + case IDNO: // Quit + { + s_iStage = -1; + UnInitSHCN(hwnd); + ::DestroyWindow(hwnd); + break; + } + } +} + +static void OnDestroy(HWND hwnd) +{ + UnInitSHCN(hwnd); + + for (auto& pidl : s_pidl) + { + CoTaskMemFree(pidl); + pidl = NULL; + } + + ::PostMessageW(s_hMainWnd, WM_COMMAND, IDNO, 0); + + PostQuitMessage(0); +} + +static BOOL DoSendData(LONG lEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) +{ + DWORD cbPidl1 = ILGetSize(pidl1), cbPidl2 = ILGetSize(pidl2); + DWORD cbTotal = sizeof(lEvent) + sizeof(cbPidl1) + sizeof(cbPidl2) + cbPidl1 + cbPidl2; + LPBYTE pbData = (LPBYTE)::LocalAlloc(LPTR, cbTotal); + if (!pbData) + return FALSE; + + LPBYTE pb = pbData; + + *(LONG*)pb = lEvent; + pb += sizeof(lEvent); + + *(DWORD*)pb = cbPidl1; + pb += sizeof(cbPidl1); + + *(DWORD*)pb = cbPidl2; + pb += sizeof(cbPidl2); + + CopyMemory(pb, pidl1, cbPidl1); + pb += cbPidl1; + + CopyMemory(pb, pidl2, cbPidl2); + pb += cbPidl2; + + assert(INT(pb - pbData) == INT(cbTotal)); + + COPYDATASTRUCT CopyData; + CopyData.dwData = 0xBEEFCAFE; + CopyData.cbData = cbTotal; + CopyData.lpData = pbData; + BOOL ret = (BOOL)::SendMessageW(s_hMainWnd, WM_COPYDATA, (WPARAM)s_hSubWnd, (LPARAM)&CopyData); + + ::LocalFree(pbData); + return ret; +} + +static void DoShellNotify(HWND hwnd, PIDLIST_ABSOLUTE pidl1, PIDLIST_ABSOLUTE pidl2, LONG lEvent) +{ + if (s_iStage < 0) + return; + + DoSendData(lEvent, pidl1, pidl2); +} + +static INT_PTR OnShellNotify(HWND hwnd, WPARAM wParam, LPARAM lParam) { LONG lEvent; PIDLIST_ABSOLUTE *pidlAbsolute; @@ -163,40 +252,7 @@ OnShellNotify(HWND hwnd, WPARAM wParam, LPARAM lParam) return TRUE; } -static LRESULT -OnGetNotifyFlags(HWND hwnd) -{ - if (s_uRegID == 0) - return 0xFFFFFFFF; - - DWORD dwFlags = 0; - for (size_t i = 0; i < _countof(s_counters); ++i) - { - if (s_counters[i]) - dwFlags |= (1 << i); - } - return dwFlags; -} - -static void -DoSetPaths(HWND hwnd) -{ - WCHAR szText[MAX_PATH * 2]; - StringCchCopyW(szText, _countof(szText), s_path1); - StringCchCatW(szText, _countof(szText), L"|"); - StringCchCatW(szText, _countof(szText), s_path2); - - FILE *fp = _wfopen(TEMP_FILE, L"wb"); - if (fp) - { - fwrite(szText, (wcslen(szText) + 1) * sizeof(WCHAR), 1, fp); - fflush(fp); - fclose(fp); - } -} - -static LRESULT CALLBACK -WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +static LRESULT CALLBACK SubWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { @@ -214,73 +270,36 @@ WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) OnDestroy(hwnd); break; - case WM_GET_NOTIFY_FLAGS: - return OnGetNotifyFlags(hwnd); - - case WM_CLEAR_FLAGS: - ZeroMemory(&s_counters, sizeof(s_counters)); - s_path1[0] = s_path2[0] = 0; - break; - - case WM_SET_PATHS: - DoSetPaths(hwnd); - break; - default: - return DefWindowProcW(hwnd, uMsg, wParam, lParam); + return ::DefWindowProcW(hwnd, uMsg, wParam, lParam); } return 0; } -static BOOL ParseCommandLine(LPWSTR lpCmdLine) -{ - LPWSTR pch = lpCmdLine; // fRecursive,iWatchDir,nSources - s_fRecursive = !!wcstoul(pch, NULL, 0); - pch = wcschr(pch, L','); - if (!pch) - return FALSE; - ++pch; - - s_iWatchDir = (DIRTYPE)wcstoul(pch, NULL, 0); - pch = wcschr(pch, L','); - if (!pch) - return FALSE; - ++pch; - - s_nSources = wcstoul(pch, NULL, 0); - return TRUE; -} - INT APIENTRY -wWinMain(HINSTANCE hInstance, - HINSTANCE hPrevInstance, - LPWSTR lpCmdLine, - INT nCmdShow) +wWinMain( + HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPWSTR lpCmdLine, + INT nCmdShow) { if (lstrcmpiW(lpCmdLine, L"") == 0 || lstrcmpiW(lpCmdLine, L"TEST") == 0) return 0; - if (!ParseCommandLine(lpCmdLine)) - return -1; - - s_hEvent = OpenEventW(EVENT_ALL_ACCESS, TRUE, EVENT_NAME); - - WNDCLASSW wc; - ZeroMemory(&wc, sizeof(wc)); - wc.lpfnWndProc = WindowProc; - wc.hInstance = GetModuleHandleW(NULL); - wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); - wc.hCursor = LoadCursor(NULL, IDC_ARROW); + WNDCLASSW wc = { 0, SubWindowProc }; + wc.hInstance = hInstance; + wc.hIcon = LoadIconW(NULL, IDI_APPLICATION); + wc.hCursor = LoadCursorW(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); - wc.lpszClassName = CLASSNAME; + wc.lpszClassName = SUB_CLASSNAME; if (!RegisterClassW(&wc)) return -1; - HWND hwnd = CreateWindowW(CLASSNAME, CLASSNAME, WS_OVERLAPPEDWINDOW, + HWND hwnd = CreateWindowW(SUB_CLASSNAME, SUB_CLASSNAME, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 100, - NULL, NULL, GetModuleHandleW(NULL), NULL); + NULL, NULL, hInstance, NULL); if (!hwnd) - return -1; + return -2; ShowWindow(hwnd, SW_SHOWNORMAL); UpdateWindow(hwnd); @@ -292,7 +311,5 @@ wWinMain(HINSTANCE hInstance, DispatchMessageW(&msg); } - CloseHandle(s_hEvent); - return 0; } diff --git a/modules/rostests/apitests/shell32/shell32_apitest_sub.h b/modules/rostests/apitests/shell32/shell32_apitest_sub.h index bff2e174683..5212ac6710e 100644 --- a/modules/rostests/apitests/shell32/shell32_apitest_sub.h +++ b/modules/rostests/apitests/shell32/shell32_apitest_sub.h @@ -5,72 +5,10 @@ #include #include -#define TEMP_FILE L"shell-notify-temporary.txt" -#define CLASSNAME L"SHChangeNotify testcase window" -#define EVENT_NAME L"SHChangeNotify testcase event" +#define MAIN_CLASSNAME L"SHChangeNotify main window" +#define SUB_CLASSNAME L"SHChangeNotify sub-window" #define WM_SHELL_NOTIFY (WM_USER + 100) -#define WM_GET_NOTIFY_FLAGS (WM_USER + 101) -#define WM_CLEAR_FLAGS (WM_USER + 102) -#define WM_SET_PATHS (WM_USER + 103) - -typedef enum TYPE -{ - TYPE_RENAMEITEM, - TYPE_CREATE, - TYPE_DELETE, - TYPE_MKDIR, - TYPE_RMDIR, - TYPE_RENAMEFOLDER, - TYPE_UPDATEDIR, - TYPE_MAX = TYPE_UPDATEDIR -} TYPE; - -typedef enum DIRTYPE -{ - DIRTYPE_NULL = 0, - DIRTYPE_DESKTOP, - DIRTYPE_MYCOMPUTER, - DIRTYPE_MYDOCUMENTS -} DIRTYPE; - -inline LPITEMIDLIST DoGetPidl(DIRTYPE iDir) -{ - LPITEMIDLIST ret = NULL; - - switch (iDir) - { - case DIRTYPE_NULL: - break; - - case DIRTYPE_DESKTOP: - SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &ret); - break; - - case DIRTYPE_MYCOMPUTER: - SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES, &ret); - break; - - case DIRTYPE_MYDOCUMENTS: - SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &ret); - break; - } - - return ret; -} - -static inline LPWSTR DoGetDir(DIRTYPE iDir) -{ - static size_t s_index = 0; - static WCHAR s_pathes[3][MAX_PATH]; - LPWSTR psz = s_pathes[s_index]; - LPITEMIDLIST pidl = DoGetPidl(iDir); - psz[0] = 0; - SHGetPathFromIDListW(pidl, psz); - CoTaskMemFree(pidl); - s_index = (s_index + 1) % _countof(s_pathes); - return psz; -} static inline HWND DoWaitForWindow(LPCWSTR clsname, LPCWSTR text, BOOL bClosing, BOOL bForce) {