[SHELL32_APITEST]
authorThomas Faber <thomas.faber@reactos.org>
Mon, 28 Mar 2016 15:35:21 +0000 (15:35 +0000)
committerThomas Faber <thomas.faber@reactos.org>
Mon, 28 Mar 2016 15:35:21 +0000 (15:35 +0000)
- Add tests for IShellFolder::CompareIDs. Patch by Mark Jansen, with additional test cases by Sylvain Deverre.
CORE-10747

svn path=/trunk/; revision=71064

rostests/apitests/shell32/CMakeLists.txt
rostests/apitests/shell32/CShellDesktop.cpp
rostests/apitests/shell32/shelltest.cpp [new file with mode: 0644]
rostests/apitests/shell32/shelltest.h

index 0d7dbf0..0855e5f 100644 (file)
@@ -7,8 +7,9 @@ add_executable(shell32_apitest
     CMyComputer.cpp
     CShellDesktop.cpp
     menu.cpp
+    shelltest.cpp
     testlist.c)
-target_link_libraries(shell32_apitest wine uuid)
+target_link_libraries(shell32_apitest wine uuid ${PSEH_LIB})
 set_module_type(shell32_apitest win32cui)
-add_importlibs(shell32_apitest user32 gdi32 shell32 ole32 oleaut32 shlwapi msvcrt kernel32)
+add_importlibs(shell32_apitest user32 gdi32 shell32 ole32 oleaut32 shlwapi msvcrt kernel32 ntdll)
 add_cd_file(TARGET shell32_apitest DESTINATION reactos/bin FOR all)
index c11348b..5a3a0d8 100644 (file)
  * LICENSE:         LGPLv2.1+ - See COPYING.LIB in the top level directory
  * PURPOSE:         Test for CShellDesktop
  * PROGRAMMER:      Thomas Faber <thomas.faber@reactos.org>
+ *                  Mark Jansen
  */
 
 #include "shelltest.h"
 #include <atlbase.h>
 #include <atlcom.h>
 #include <strsafe.h>
+#include <ndk/rtlfuncs.h>
 
 #define NDEBUG
 #include <debug.h>
 #include <shellutils.h>
 
+
+// We would normally use S_LESSTHAN and S_GREATERTHAN, but w2k3 returns numbers like 3 and -3...
+// So instead we check on the sign bit (compare result is the low word of the hresult).
+#define SHORT_SIGN_BIT  0x8000
+
+static
+VOID
+compare_imp(IShellFolder* psf, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, HRESULT expected)
+{
+    HRESULT hr;
+    _SEH2_TRY
+    {
+        hr = psf->CompareIDs(0, pidl1, pidl2);
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        winetest_ok(0, "Exception %lx!\n", _SEH2_GetExceptionCode());
+        hr = HRESULT_FROM_WIN32(RtlNtStatusToDosError(_SEH2_GetExceptionCode()));
+    }
+    _SEH2_END;
+    if (expected == S_LESSTHAN)
+        winetest_ok(SUCCEEDED(hr) && (hr & SHORT_SIGN_BIT), "hr = %lx\n", hr);
+    else if (expected == S_EQUAL)
+        winetest_ok(hr == S_EQUAL, "hr = %lx\n", hr);
+    else if (expected == S_GREATERTHAN)
+        winetest_ok(SUCCEEDED(hr) && !(hr & SHORT_SIGN_BIT), "hr = %lx\n", hr);
+    else
+        winetest_ok(hr == expected, "hr = %lx\n", hr);
+}
+
+// make the winetest_ok look like it came from the line where the compare function was called, and not from inside the compare_imp function :)
+#define compare         (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : compare_imp
+
+static
+VOID
+TestCompareIDList(IShellFolder* psf)
+{
+    compare(psf, NULL, NULL, E_INVALIDARG);
+
+    CComHeapPtr<ITEMIDLIST> desktop;
+    HRESULT hr = SHGetFolderLocation(NULL, CSIDL_DESKTOP, NULL, NULL, &desktop);
+    ok(hr == S_OK, "hr = %lx\n", hr);
+    compare(psf, desktop, NULL, E_INVALIDARG);
+    compare(psf, NULL, desktop, E_INVALIDARG);
+    compare(psf, desktop, desktop, S_EQUAL);
+
+    // First check the ordering of some special folders against eachother
+    CComHeapPtr<ITEMIDLIST> internet;
+    hr = SHGetFolderLocation(NULL, CSIDL_INTERNET, NULL, NULL, &internet);
+    ok(hr == S_OK, "hr = %lx\n", hr);
+    compare(psf, internet, desktop, S_LESSTHAN);
+    compare(psf, desktop, internet, S_GREATERTHAN);
+
+    CComHeapPtr<ITEMIDLIST> programs;
+    hr = SHGetFolderLocation(NULL, CSIDL_PROGRAMS, NULL, NULL, &programs);
+    ok(hr == S_OK, "hr = %lx\n", hr);
+    compare(psf, programs, desktop, S_LESSTHAN);
+    compare(psf, desktop, programs, S_GREATERTHAN);
+    compare(psf, internet, programs, S_GREATERTHAN);
+    compare(psf, programs, internet, S_LESSTHAN);
+
+    // Verify that an idlist retrieved from GetCurFolder is equal to the original one.
+    CComPtr<IPersistFolder2> persist;
+    hr = psf->QueryInterface(IID_PPV_ARG(IPersistFolder2, &persist));
+    ok(hr == S_OK, "hr = %lx\n", hr);
+    if (hr == S_OK)
+    {
+        CComHeapPtr<ITEMIDLIST> cur;
+        hr = persist->GetCurFolder(&cur);
+        ok(hr == S_OK, "hr = %lx\n", hr);
+        compare(psf, cur, desktop, S_EQUAL);
+        compare(psf, desktop, cur, S_EQUAL);
+    }
+
+    // Compare special folders against full paths
+    CComHeapPtr<ITEMIDLIST> dir1, dir2;
+    PathToIDList(L"A:\\AAA.AAA", &dir1);
+    PathToIDList(L"A:\\ZZZ.ZZZ", &dir2);
+
+    compare(psf, dir1, desktop, S_LESSTHAN);
+    compare(psf, desktop, dir1, S_GREATERTHAN);
+    compare(psf, dir1, programs, S_LESSTHAN);
+    compare(psf, programs, dir1, S_GREATERTHAN);
+    compare(psf, dir1, dir1, S_EQUAL);
+
+    compare(psf, dir2, desktop, S_LESSTHAN);
+    compare(psf, desktop, dir2, S_GREATERTHAN);
+    compare(psf, dir2, programs, S_LESSTHAN);
+    compare(psf, programs, dir2, S_GREATERTHAN);
+    compare(psf, dir2, dir2, S_EQUAL);
+
+    CComHeapPtr<ITEMIDLIST> dir3, dir4;
+    PathToIDList(L"Z:\\AAA.AAA", &dir3);
+    PathToIDList(L"Z:\\ZZZ.ZZZ", &dir4);
+
+    compare(psf, dir3, desktop, S_LESSTHAN);
+    compare(psf, desktop, dir3, S_GREATERTHAN);
+    compare(psf, dir3, programs, S_GREATERTHAN);
+    compare(psf, programs, dir3, S_LESSTHAN);
+    compare(psf, dir3, dir3, S_EQUAL);
+
+    compare(psf, dir4, desktop, S_LESSTHAN);
+    compare(psf, desktop, dir4, S_GREATERTHAN);
+    compare(psf, dir4, programs, S_GREATERTHAN);
+    compare(psf, programs, dir4, S_LESSTHAN);
+    compare(psf, dir4, dir4, S_EQUAL);
+
+    // Now compare the paths against eachother.
+    compare(psf, dir1, dir2, S_LESSTHAN);
+    compare(psf, dir2, dir1, S_GREATERTHAN);
+
+    compare(psf, dir2, dir3, S_LESSTHAN);
+    compare(psf, dir3, dir2, S_GREATERTHAN);
+
+    compare(psf, dir3, dir4, S_LESSTHAN);
+    compare(psf, dir4, dir3, S_GREATERTHAN);
+
+    // Check that comparing desktop pidl with another one with another IShellFolder fails
+    CComPtr<IShellFolder> psf2;
+    hr = psf->BindToObject(programs, NULL, IID_IShellFolder, reinterpret_cast<void**>(&psf2));
+    ok(hr == S_OK, "Impossible to bind to Programs pidl");
+    if (hr == S_OK)
+    {
+        // Compare desktop pidl in programs scope should fail since it's relative pidl
+        compare(psf2, desktop, programs, E_INVALIDARG);
+        compare(psf2, programs, desktop, E_INVALIDARG);
+        // For the same reasons, filesystem paths can't be compared with special shell
+        // folders that don't have CFSFolder in children
+        compare(psf2, dir1, dir2, E_INVALIDARG);
+        compare(psf2, dir2, dir1, E_INVALIDARG);
+    }
+}
+
 static
 VOID
 TestShellFolder(
@@ -83,4 +218,5 @@ START_TEST(CShellDesktop)
     ok(psf == static_cast<IShellFolder *>(psf2), "Expected %p == %p\n", static_cast<PVOID>(psf), static_cast<PVOID>(psf2));
 
     TestShellFolder(psf2);
+    TestCompareIDList(psf);
 }
diff --git a/rostests/apitests/shell32/shelltest.cpp b/rostests/apitests/shell32/shelltest.cpp
new file mode 100644 (file)
index 0000000..8f362c3
--- /dev/null
@@ -0,0 +1,98 @@
+#include "shelltest.h"
+#include <atlbase.h>
+#include <atlcom.h>
+
+
+// + Adapted from https://blogs.msdn.microsoft.com/oldnewthing/20130503-00/?p=4463/
+// In short: We want to create an IDLIST from an item that does not exist,
+// so we have to provide WIN32_FIND_DATAW in a bind context.
+// If we don't, the FS will be queried, and we do not get a valid IDLIST for a non-existing path.
+
+CComModule gModule;
+
+class CFileSysBindData :
+    public CComCoClass<CFileSysBindData>,
+    public CComObjectRootEx<CComMultiThreadModelNoCS>,
+    public IFileSystemBindData
+{
+public:
+    virtual HRESULT STDMETHODCALLTYPE SetFindData(const WIN32_FIND_DATAW *pfd)
+    {
+        m_Data = *pfd;
+        return S_OK;
+    }
+
+    virtual HRESULT STDMETHODCALLTYPE GetFindData(WIN32_FIND_DATAW *pfd)
+    {
+        *pfd = m_Data;
+        return S_OK;
+    }
+
+    DECLARE_NOT_AGGREGATABLE(CFileSysBindData)
+    DECLARE_PROTECT_FINAL_CONSTRUCT()
+    BEGIN_COM_MAP(CFileSysBindData)
+        COM_INTERFACE_ENTRY_IID(IID_IFileSystemBindData, IFileSystemBindData)
+    END_COM_MAP()
+private:
+    WIN32_FIND_DATAW m_Data;
+};
+
+static
+HRESULT
+AddFileSysBindCtx(_In_ IBindCtx *pbc)
+{
+    CComPtr<IFileSystemBindData> spfsbc(new CComObject<CFileSysBindData>());
+    WIN32_FIND_DATAW wfd = { 0 };
+    wfd.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
+    spfsbc->SetFindData(&wfd);
+    HRESULT hr = pbc->RegisterObjectParam((LPOLESTR)STR_FILE_SYS_BIND_DATA, spfsbc);
+    ok(hr == S_OK, "hr = %lx\n", hr);
+    return hr;
+}
+
+static
+HRESULT
+CreateBindCtxWithOpts(_In_ BIND_OPTS *pbo, _Outptr_ IBindCtx **ppbc)
+{
+    CComPtr<IBindCtx> spbc;
+    HRESULT hr = CreateBindCtx(0, &spbc);
+    ok(hr == S_OK, "hr = %lx\n", hr);
+    if (SUCCEEDED(hr))
+    {
+        hr = spbc->SetBindOptions(pbo);
+        ok(hr == S_OK, "hr = %lx\n", hr);
+    }
+    *ppbc = SUCCEEDED(hr) ? spbc.Detach() : NULL;
+    return hr;
+}
+
+static HRESULT
+CreateFileSysBindCtx(_Outptr_ IBindCtx **ppbc)
+{
+    CComPtr<IBindCtx> spbc;
+    BIND_OPTS bo = { sizeof(bo), 0, STGM_CREATE, 0 };
+    HRESULT hr = CreateBindCtxWithOpts(&bo, &spbc);
+    ok(hr == S_OK, "hr = %lx\n", hr);
+    if (SUCCEEDED(hr))
+    {
+        hr = AddFileSysBindCtx(spbc);
+        ok(hr == S_OK, "hr = %lx\n", hr);
+    }
+    *ppbc = SUCCEEDED(hr) ? spbc.Detach() : NULL;
+    return hr;
+}
+
+VOID
+PathToIDList(LPCWSTR pszPath, ITEMIDLIST** ppidl)
+{
+    CComPtr<IBindCtx> spbc;
+    HRESULT hr = CreateFileSysBindCtx(&spbc);
+    ok(hr == S_OK, "hr = %lx\n", hr);
+    if (SUCCEEDED(hr))
+    {
+        hr = SHParseDisplayName(pszPath, spbc, ppidl, 0, NULL);
+        ok(hr == S_OK, "hr = %lx\n", hr);
+    }
+}
+
+// - Adapted from https://blogs.msdn.microsoft.com/oldnewthing/20130503-00/?p=4463/
index 500fa90..f24e54f 100644 (file)
@@ -28,3 +28,5 @@
 DEFINE_GUID(CLSID_MenuBandSite, 0xE13EF4E4, 0xD2F2, 0x11D0, 0x98, 0x16, 0x00, 0xC0, 0x4F, 0xD9, 0x19, 0x72);
 
 #include "unknownbase.h"
+
+VOID PathToIDList(LPCWSTR pszPath, ITEMIDLIST** ppidl);