From 6e42e63c4623bb929a968309b0ea4f9d82483c1f Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Tue, 12 Nov 2019 16:49:52 +0900 Subject: [PATCH] [SHELL32_APITEST] Add DragDrop testcase (#2023) Add DragDrop testcase to shell32_apitest for testing Drag & Drop feature of the Shell. This PR tests IDropTarget::DragEnter and IDropTarget::Drop. CORE-11238 --- .../rostests/apitests/shell32/CMakeLists.txt | 1 + .../rostests/apitests/shell32/DragDrop.cpp | 381 ++++++++++++++++++ modules/rostests/apitests/shell32/testlist.c | 2 + 3 files changed, 384 insertions(+) create mode 100644 modules/rostests/apitests/shell32/DragDrop.cpp diff --git a/modules/rostests/apitests/shell32/CMakeLists.txt b/modules/rostests/apitests/shell32/CMakeLists.txt index 02b28625d63..f8d8c84e8d8 100644 --- a/modules/rostests/apitests/shell32/CMakeLists.txt +++ b/modules/rostests/apitests/shell32/CMakeLists.txt @@ -17,6 +17,7 @@ list(APPEND SOURCE CShellLink.cpp CUserNotification.cpp Control_RunDLLW.cpp + DragDrop.cpp IShellFolderViewCB.cpp OpenAs_RunDLL.cpp PathResolve.cpp diff --git a/modules/rostests/apitests/shell32/DragDrop.cpp b/modules/rostests/apitests/shell32/DragDrop.cpp new file mode 100644 index 00000000000..43e06f5a0b2 --- /dev/null +++ b/modules/rostests/apitests/shell32/DragDrop.cpp @@ -0,0 +1,381 @@ +/* + * PROJECT: ReactOS api tests + * LICENSE: LGPLv2.1+ - See COPYING.LIB in the top level directory + * PURPOSE: Test for Drag & Drop + * PROGRAMMER: Katayama Hirofumi MZ + */ + +#include "shelltest.h" +#include + +#define NDEBUG +#include +#include + +#define TESTFILENAME L"DragDropTest.txt" +#define DROPPED_ON_FILE L"DragDroppedOn.lnk" + +static CComPtr s_pDesktop; + +static WCHAR s_szSrcTestFile[MAX_PATH]; +static WCHAR s_szDestFolder[MAX_PATH]; +static WCHAR s_szDestTestFile[MAX_PATH]; +static WCHAR s_szDestLinkSpec[MAX_PATH]; +static WCHAR s_szDroppedToItem[MAX_PATH]; + +enum OP +{ + OP_NONE, + OP_COPY, + OP_MOVE, + OP_LINK, + OP_NONE_OR_COPY, + OP_NONE_OR_MOVE, + OP_NONE_OR_LINK +}; + +#define D_NONE DROPEFFECT_NONE +#define D_COPY DROPEFFECT_COPY +#define D_MOVE DROPEFFECT_MOVE +#define D_LINK DROPEFFECT_LINK +#define D_NONE_OR_COPY 0xAABBCCDD +#define D_NONE_OR_MOVE 0x11223344 +#define D_NONE_OR_LINK 0x55667788 + +struct TEST_ENTRY +{ + int line; + OP op; + HRESULT hr1; + HRESULT hr2; + DWORD dwKeyState; + DWORD dwEffects1; + DWORD dwEffects2; + DWORD dwEffects3; +}; + +static const TEST_ENTRY s_TestEntries[] = +{ + // MK_LBUTTON + { __LINE__, OP_NONE, S_OK, S_OK, MK_LBUTTON, D_NONE, D_NONE, D_NONE }, + { __LINE__, OP_COPY, S_OK, S_OK, MK_LBUTTON, D_COPY, D_COPY, D_COPY }, + { __LINE__, OP_MOVE, S_OK, S_OK, MK_LBUTTON, D_COPY | D_MOVE, D_MOVE, D_NONE }, + { __LINE__, OP_MOVE, S_OK, S_OK, MK_LBUTTON, D_MOVE, D_MOVE, D_NONE }, + { __LINE__, OP_LINK, S_OK, S_OK, MK_LBUTTON, D_LINK, D_LINK, D_LINK }, + + // MK_LBUTTON | MK_SHIFT + { __LINE__, OP_NONE, S_OK, S_OK, MK_LBUTTON | MK_SHIFT, D_NONE, D_NONE, D_NONE }, + { __LINE__, OP_NONE_OR_COPY, S_OK, S_OK, MK_LBUTTON | MK_SHIFT, D_COPY, D_NONE_OR_COPY, D_NONE_OR_COPY }, + { __LINE__, OP_MOVE, S_OK, S_OK, MK_LBUTTON | MK_SHIFT, D_COPY | D_MOVE, D_MOVE, D_NONE }, + { __LINE__, OP_MOVE, S_OK, S_OK, MK_LBUTTON | MK_SHIFT, D_MOVE, D_MOVE, D_NONE }, + { __LINE__, OP_NONE_OR_LINK, S_OK, S_OK, MK_LBUTTON | MK_SHIFT, D_LINK, D_NONE_OR_LINK, D_NONE_OR_LINK }, + + // MK_LBUTTON | MK_SHIFT | MK_CONTROL +#define MK_LBUTTON_SHIFT_CTRL (MK_LBUTTON | MK_SHIFT | MK_CONTROL) + { __LINE__, OP_NONE, S_OK, S_OK, MK_LBUTTON_SHIFT_CTRL, D_NONE, D_NONE, D_NONE }, + { __LINE__, OP_NONE_OR_COPY, S_OK, S_OK, MK_LBUTTON_SHIFT_CTRL, D_COPY, D_NONE_OR_COPY, D_NONE_OR_COPY }, + { __LINE__, OP_NONE_OR_COPY, S_OK, S_OK, MK_LBUTTON_SHIFT_CTRL, D_COPY | D_MOVE, D_NONE_OR_COPY, D_NONE_OR_COPY }, + { __LINE__, OP_NONE_OR_MOVE, S_OK, S_OK, MK_LBUTTON_SHIFT_CTRL, D_MOVE, D_NONE_OR_MOVE, D_NONE_OR_MOVE }, + { __LINE__, OP_LINK, S_OK, S_OK, MK_LBUTTON_SHIFT_CTRL, D_LINK, D_LINK, D_LINK }, +#undef MK_LBUTTON_SHIFT_CTRL + + // MK_LBUTTON | MK_CONTROL + { __LINE__, OP_NONE, S_OK, S_OK, MK_LBUTTON | MK_CONTROL, D_NONE, D_NONE, D_NONE }, + { __LINE__, OP_COPY, S_OK, S_OK, MK_LBUTTON | MK_CONTROL, D_COPY, D_COPY, D_COPY }, + { __LINE__, OP_COPY, S_OK, S_OK, MK_LBUTTON | MK_CONTROL, D_COPY | D_MOVE, D_COPY, D_COPY }, + { __LINE__, OP_NONE_OR_MOVE, S_OK, S_OK, MK_LBUTTON | MK_CONTROL, D_MOVE, D_NONE_OR_MOVE, D_NONE_OR_MOVE }, + { __LINE__, OP_NONE_OR_LINK, S_OK, S_OK, MK_LBUTTON | MK_CONTROL, D_LINK, D_NONE_OR_LINK, D_NONE_OR_LINK }, +}; + +static void DoCreateTestFile(LPCWSTR pszFileName) +{ + FILE *fp = _wfopen(pszFileName, L"wb"); + ok(fp != NULL, "fp is NULL for '%S'\n", pszFileName); + fclose(fp); +} + +HRESULT DoCreateShortcut( + LPCWSTR pszLnkFileName, + LPCWSTR pszTargetPathName) +{ + CComPtr ppf; + CComPtr psl; + HRESULT hr; + + hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, + IID_IShellLinkW, (LPVOID *)&psl); + if (SUCCEEDED(hr)) + { + psl->SetPath(pszTargetPathName); + + hr = psl->QueryInterface(IID_IPersistFile, (LPVOID *)&ppf); + if (SUCCEEDED(hr)) + { + hr = ppf->Save(pszLnkFileName, TRUE); + } + } + + return hr; +} + +static HRESULT +GetUIObjectOfAbsPidl(PIDLIST_ABSOLUTE pidl, REFIID riid, LPVOID *ppvOut) +{ + *ppvOut = NULL; + + LPCITEMIDLIST pidlLast; + CComPtr psf; + HRESULT hr = SHBindToParent(pidl, IID_IShellFolder, (LPVOID *)&psf, + &pidlLast); + if (FAILED(hr)) + return hr; + + hr = psf->GetUIObjectOf(NULL, 1, &pidlLast, riid, NULL, ppvOut); + return hr; +} + +static HRESULT +GetUIObjectOfPath(LPCWSTR pszPath, REFIID riid, LPVOID *ppvOut) +{ + *ppvOut = NULL; + + PIDLIST_ABSOLUTE pidl = ILCreateFromPathW(pszPath); + if (!pidl) + return E_FAIL; + + HRESULT hr = GetUIObjectOfAbsPidl(pidl, riid, ppvOut); + + CoTaskMemFree(pidl); + + return hr; +} + +BOOL DoSpecExistsW(LPCWSTR pszSpec) +{ + WIN32_FIND_DATAW find; + HANDLE hFind = FindFirstFileW(pszSpec, &find); + if (hFind != INVALID_HANDLE_VALUE) + { + FindClose(hFind); + return TRUE; + } + return FALSE; +} + +void DoDeleteSpecW(LPCWSTR pszSpec) +{ + WCHAR szPath[MAX_PATH], szFile[MAX_PATH]; + lstrcpyW(szPath, pszSpec); + PathRemoveFileSpecW(szPath); + + WIN32_FIND_DATAW find; + HANDLE hFind = FindFirstFileW(pszSpec, &find); + if (hFind != INVALID_HANDLE_VALUE) + { + do + { + lstrcpyW(szFile, szPath); + PathAppendW(szFile, find.cFileName); + DeleteFileW(szFile); + } while (FindNextFileW(hFind, &find)); + + FindClose(hFind); + } +} + +static void DoTestEntry(const TEST_ENTRY *pEntry) +{ + int line = pEntry->line; + HRESULT hr; + PIDLIST_ABSOLUTE pidlDesktop = NULL; + CComPtr pDropTarget; + CComPtr pDataObject; + + // get the desktop PIDL + SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidlDesktop); + ok(!!pidlDesktop, "pidlDesktop is NULL\n"); + + // build paths + // + SHGetPathFromIDListW(pidlDesktop, s_szDroppedToItem); + PathAppendW(s_szDroppedToItem, DROPPED_ON_FILE); + + GetModuleFileNameW(NULL, s_szSrcTestFile, _countof(s_szSrcTestFile)); + PathRemoveFileSpecW(s_szSrcTestFile); + PathAppendW(s_szSrcTestFile, TESTFILENAME); + + lstrcpyW(s_szDestTestFile, s_szDestFolder); + PathAppendW(s_szDestTestFile, TESTFILENAME); + + lstrcpyW(s_szDestLinkSpec, s_szDestFolder); + PathAppendW(s_szDestLinkSpec, L"*DragDropTest*.lnk"); + + //trace("s_szSrcTestFile: '%S'\n", s_szSrcTestFile); + //trace("s_szDestTestFile: '%S'\n", s_szDestTestFile); + //trace("s_szDestLinkSpec: '%S'\n", s_szDestLinkSpec); + //trace("s_szDroppedToItem: '%S'\n", s_szDroppedToItem); + + // create or delete files + // + DoCreateTestFile(s_szSrcTestFile); + DeleteFileW(s_szDestTestFile); + DoDeleteSpecW(s_szDestLinkSpec); + DeleteFileW(s_szDroppedToItem); + DoCreateShortcut(s_szDroppedToItem, s_szDestFolder); + + // check file existence + // + ok(PathIsDirectoryW(s_szDestFolder), "s_szDestFolder is not directory\n"); + ok(PathFileExistsW(s_szSrcTestFile), "s_szSrcTestFile doesn't exist\n"); + ok(!DoSpecExistsW(s_szDestLinkSpec), "s_szDestLinkSpec doesn't exist\n"); + ok(!PathFileExistsW(s_szDestTestFile), "s_szDestTestFile exists\n"); + + // get an IDataObject + pDataObject = NULL; + hr = GetUIObjectOfPath(s_szSrcTestFile, IID_IDataObject, (LPVOID *)&pDataObject); + ok_long(hr, S_OK); + + // get an IDropTarget + CComPtr pEnumIDList; + PIDLIST_ABSOLUTE pidl = NULL; + hr = s_pDesktop->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, + &pEnumIDList); + ok_long(hr, S_OK); + while (pEnumIDList->Next(1, &pidl, NULL) == S_OK) + { + WCHAR szText[MAX_PATH]; + SHGetPathFromIDListW(pidl, szText); + if (wcsstr(szText, DROPPED_ON_FILE) != NULL) + { + break; + } + CoTaskMemFree(pidl); + pidl = NULL; + } + ok(pidl != NULL, "pidl is NULL\n"); + pDropTarget = NULL; + PITEMID_CHILD pidlLast = ILFindLastID(pidl); + hr = s_pDesktop->GetUIObjectOf(NULL, 1, &pidlLast, IID_IDropTarget, + NULL, (LPVOID *)&pDropTarget); + CoTaskMemFree(pidl); + ok_long(hr, S_OK); + + // DragEnter + POINTL ptl = { 0, 0 }; + DWORD dwKeyState = pEntry->dwKeyState; + DWORD dwEffects = pEntry->dwEffects1; + hr = pDropTarget->DragEnter(pDataObject, dwKeyState, ptl, &dwEffects); + + ok(hr == pEntry->hr1, "Line %d: hr1 was %08lX\n", line, hr); + + switch (pEntry->dwEffects2) + { + case D_NONE_OR_COPY: + ok((dwEffects == D_NONE || dwEffects == D_COPY), + "Line %d: dwEffects2 was %08lX\n", line, dwEffects); + break; + case D_NONE_OR_MOVE: + ok((dwEffects == D_NONE || dwEffects == D_MOVE), + "Line %d: dwEffects2 was %08lX\n", line, dwEffects); + break; + case D_NONE_OR_LINK: + ok((dwEffects == D_NONE || dwEffects == D_LINK), + "Line %d: dwEffects2 was %08lX\n", line, dwEffects); + break; + default: + ok(dwEffects == pEntry->dwEffects2, + "Line %d: dwEffects2 was %08lX\n", line, dwEffects); + break; + } + + // Drop + hr = pDropTarget->Drop(pDataObject, dwKeyState, ptl, &dwEffects); + ok(hr == pEntry->hr2, "Line %d: hr2 was %08lX\n", line, hr); + + switch (pEntry->dwEffects3) + { + case D_NONE_OR_COPY: + ok((dwEffects == D_NONE || dwEffects == D_COPY), + "Line %d: dwEffects3 was %08lX\n", line, dwEffects); + break; + case D_NONE_OR_MOVE: + ok((dwEffects == D_NONE || dwEffects == D_MOVE), + "Line %d: dwEffects3 was %08lX\n", line, dwEffects); + break; + case D_NONE_OR_LINK: + ok((dwEffects == D_NONE || dwEffects == D_LINK), + "Line %d: dwEffects3 was %08lX\n", line, dwEffects); + break; + default: + ok(dwEffects == pEntry->dwEffects3, + "Line %d: dwEffects3 was %08lX\n", line, dwEffects); + break; + } + + // check file existence by pEntry->op + switch (pEntry->op) + { + case OP_NONE: + ok(PathFileExistsW(s_szSrcTestFile), "Line %d: src not exists\n", line); + ok(!PathFileExistsW(s_szDestTestFile), "Line %d: dest exists\n", line); + ok(!DoSpecExistsW(s_szDestLinkSpec), "Line %d: link exists\n", line); + break; + case OP_COPY: + ok(PathFileExistsW(s_szSrcTestFile), "Line %d: src not exists\n", line); + ok(PathFileExistsW(s_szDestTestFile), "Line %d: dest not exists\n", line); + ok(!DoSpecExistsW(s_szDestLinkSpec), "Line %d: link exists\n", line); + break; + case OP_MOVE: + ok(!PathFileExistsW(s_szSrcTestFile), "Line %d: src exists\n", line); + ok(PathFileExistsW(s_szDestTestFile), "Line %d: dest not exists\n", line); + ok(!DoSpecExistsW(s_szDestLinkSpec), "Line %d: link exists\n", line); + break; + case OP_LINK: + ok(PathFileExistsW(s_szSrcTestFile), "Line %d: src not exists\n", line); + ok(!PathFileExistsW(s_szDestTestFile), "Line %d: dest not exists\n", line); + ok(DoSpecExistsW(s_szDestLinkSpec), "Line %d: link not exists\n", line); + break; + case OP_NONE_OR_COPY: + ok(PathFileExistsW(s_szSrcTestFile), "Line %d: src not exists\n", line); + ok(!DoSpecExistsW(s_szDestLinkSpec), "Line %d: link exists\n", line); + break; + case OP_NONE_OR_MOVE: + ok(PathFileExistsW(s_szSrcTestFile) != PathFileExistsW(s_szDestTestFile), + "Line %d: It must be either None or Move\n", line); + break; + case OP_NONE_OR_LINK: + ok(PathFileExistsW(s_szSrcTestFile), "Line %d: src not exists\n", line); + ok(!PathFileExistsW(s_szDestTestFile), "Line %d: dest not exists\n", line); + break; + } + + // clean up + DeleteFileW(s_szSrcTestFile); + DeleteFileW(s_szDestTestFile); + DoDeleteSpecW(s_szDestLinkSpec); + ILFree(pidlDesktop); +} + +START_TEST(DragDrop) +{ + HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + ok_int(SUCCEEDED(hr), TRUE); + + SHGetDesktopFolder(&s_pDesktop); + ok(!!s_pDesktop, "s_pDesktop is NULL\n"); + + BOOL ret = SHGetSpecialFolderPathW(NULL, s_szDestFolder, CSIDL_DESKTOP, FALSE); + ok_int(ret, TRUE); + + for (size_t i = 0; i < _countof(s_TestEntries); ++i) + { + DoTestEntry(&s_TestEntries[i]); + } + + DeleteFileW(s_szSrcTestFile); + DeleteFileW(s_szDestTestFile); + DoDeleteSpecW(s_szDestLinkSpec); + DeleteFileW(s_szDroppedToItem); + + CoUninitialize(); +} diff --git a/modules/rostests/apitests/shell32/testlist.c b/modules/rostests/apitests/shell32/testlist.c index 7b603dff147..b16ace6cc23 100644 --- a/modules/rostests/apitests/shell32/testlist.c +++ b/modules/rostests/apitests/shell32/testlist.c @@ -11,6 +11,7 @@ extern void func_CMyComputer(void); extern void func_CShellDesktop(void); extern void func_CShellLink(void); extern void func_CUserNotification(void); +extern void func_DragDrop(void); extern void func_IShellFolderViewCB(void); extern void func_menu(void); extern void func_OpenAs_RunDLL(void); @@ -32,6 +33,7 @@ const struct test winetest_testlist[] = { "CShellDesktop", func_CShellDesktop }, { "CShellLink", func_CShellLink }, { "CUserNotification", func_CUserNotification }, + { "DragDrop", func_DragDrop }, { "IShellFolderViewCB", func_IShellFolderViewCB }, { "menu", func_menu }, { "OpenAs_RunDLL", func_OpenAs_RunDLL },