[PATCH 2/2] oleacc: Add tests for AccessibleObjectFromEvent.

Connor McAdams cmcadams at codeweavers.com
Mon May 24 21:11:51 CDT 2021


Signed-off-by: Connor McAdams <cmcadams at codeweavers.com>
---
-This may need more tests, I'm struggling to think up more.
---
 dlls/oleacc/tests/main.c | 620 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 620 insertions(+)

diff --git a/dlls/oleacc/tests/main.c b/dlls/oleacc/tests/main.c
index ce44e06abe4..7f8211cd004 100644
--- a/dlls/oleacc/tests/main.c
+++ b/dlls/oleacc/tests/main.c
@@ -304,6 +304,567 @@ static IAccessibleVtbl AccessibleVtbl = {
 
 static IAccessible Accessible = {&AccessibleVtbl};
 
+/*
+ * Create a tree of objects that looks like this:
+ * --ACC_OBJ_ID_MAIN,
+ * ----ACC_OBJ_ID_CHILD_0,
+ * ----ACC_OBJ_ID_CHILD_1,
+ * ------ACC_OBJ_ID_CHILD_1_0,
+ * ------ACC_OBJ_ID_CHILD_1_1,
+ * --------ACC_OBJ_ID_CHILD_1_1_0
+ * ------ACC_OBJ_ID_CHILD_1_2,
+ * ----ACC_OBJ_ID_CHILD_2,
+ * ------ACC_OBJ_ID_CHILD_2_0,
+ */
+enum {
+    ACC_OBJ_ID_NONE = -1,
+    ACC_OBJ_ID_MAIN = 1,
+    ACC_OBJ_ID_CHILD_0,
+    ACC_OBJ_ID_CHILD_1,
+    ACC_OBJ_ID_CHILD_1_0,
+    ACC_OBJ_ID_CHILD_1_1,
+    ACC_OBJ_ID_CHILD_1_1_0,
+    ACC_OBJ_ID_CHILD_1_2,
+    ACC_OBJ_ID_CHILD_2,
+    ACC_OBJ_ID_CHILD_2_0,
+};
+
+/*
+ * Custom accesibility tree object info structure.
+ */
+typedef struct {
+    const WCHAR *obj_name;
+
+    int parent_id, child_id, child_pos;
+    unsigned int child_count, role;
+} acc_object_info;
+
+typedef struct {
+    IAccessible IAccessible_iface;
+    IEnumVARIANT IEnumVARIANT_iface;
+
+    LONG ref;
+
+    BSTR name;
+    HWND hwnd;
+    UINT child_id, role, enum_pos;
+
+    IAccessible *parent;
+
+    UINT child_count;
+    IAccessible **children;
+} Server;
+
+Server *server_obj_tree = NULL;
+
+const acc_object_info obj_tree_info[] = {
+    { .obj_name = L"acc_main",
+      .parent_id = ACC_OBJ_ID_NONE,
+      .child_id = ACC_OBJ_ID_MAIN,
+      .child_pos = 0,
+      .child_count = 3,
+      .role = ROLE_SYSTEM_DIALOG,
+    },
+    { .obj_name = L"acc_child_0",
+      .parent_id = ACC_OBJ_ID_MAIN,
+      .child_id = ACC_OBJ_ID_CHILD_0,
+      .child_pos = 0,
+      .child_count = 0,
+      .role = ROLE_SYSTEM_CHECKBUTTON,
+    },
+    { .obj_name = L"acc_child_1",
+      .parent_id = ACC_OBJ_ID_MAIN,
+      .child_id = ACC_OBJ_ID_CHILD_1,
+      .child_pos = 1,
+      .child_count = 3,
+      .role = ROLE_SYSTEM_CHECKBUTTON,
+    },
+    { .obj_name = L"acc_child_1_0",
+      .parent_id = ACC_OBJ_ID_CHILD_1,
+      .child_id = ACC_OBJ_ID_CHILD_1_0,
+      .child_pos = 0,
+      .child_count = 0,
+      .role = ROLE_SYSTEM_CHECKBUTTON,
+    },
+    { .obj_name = L"acc_child_1_1",
+      .parent_id = ACC_OBJ_ID_CHILD_1,
+      .child_id = ACC_OBJ_ID_CHILD_1_1,
+      .child_pos = 1,
+      .child_count = 1,
+      .role = ROLE_SYSTEM_CHECKBUTTON,
+    },
+    { .obj_name = L"acc_child_1_1_0",
+      .parent_id = ACC_OBJ_ID_CHILD_1_1,
+      .child_id = ACC_OBJ_ID_CHILD_1_1_0,
+      .child_pos = 0,
+      .child_count = 0,
+      .role = ROLE_SYSTEM_CHECKBUTTON,
+    },
+    { .obj_name = L"acc_child_1_2",
+      .parent_id = ACC_OBJ_ID_CHILD_1,
+      .child_id = ACC_OBJ_ID_CHILD_1_2,
+      .child_pos = 2,
+      .child_count = 0,
+      .role = ROLE_SYSTEM_CHECKBUTTON,
+    },
+    { .obj_name = L"acc_child_2",
+      .parent_id = ACC_OBJ_ID_MAIN,
+      .child_id = ACC_OBJ_ID_CHILD_2,
+      .child_pos = 2,
+      .child_count = 1,
+      .role = ROLE_SYSTEM_CHECKBUTTON,
+    },
+    { .obj_name = L"acc_child_2_0",
+      .parent_id = ACC_OBJ_ID_CHILD_2,
+      .child_id = ACC_OBJ_ID_CHILD_2_0,
+      .child_pos = 0,
+      .child_count = 0,
+      .role = ROLE_SYSTEM_CHECKBUTTON,
+    },
+};
+
+/*
+ * Custom server IAccessible.
+ */
+static inline Server* impl_from_Server(IAccessible *iface)
+{
+    return CONTAINING_RECORD(iface, Server, IAccessible_iface);
+}
+
+static void find_accessible(IAccessible *iface, IAccessible **found, UINT child_id)
+{
+    Server *data = impl_from_Server(iface);
+    LONG i;
+
+    for (i = 0; i < data->child_count; i++)
+    {
+        Server *child = impl_from_Server(data->children[i]);
+
+        if (child->child_id == child_id)
+        {
+            *found = &child->IAccessible_iface;
+            return;
+        }
+
+        if (child->child_count)
+            find_accessible(&child->IAccessible_iface, found, child_id);
+
+        if (*found)
+            return;
+    }
+}
+
+HRESULT WINAPI Server_QueryInterface(IAccessible *iface, REFIID riid, void **ppv)
+{
+    Server *This = impl_from_Server(iface);
+
+    if(IsEqualIID(riid, &IID_IAccessible) ||
+            IsEqualIID(riid, &IID_IDispatch) ||
+            IsEqualIID(riid, &IID_IUnknown)) {
+        *ppv = iface;
+    }else if(IsEqualIID(riid, &IID_IEnumVARIANT)) {
+        *ppv = &This->IEnumVARIANT_iface;
+    }else {
+        *ppv = NULL;
+        return E_NOINTERFACE;
+    }
+
+    IAccessible_AddRef(iface);
+    return S_OK;
+}
+
+ULONG WINAPI Server_AddRef(IAccessible *iface)
+{
+    Server *This = impl_from_Server(iface);
+    return InterlockedIncrement(&This->ref);
+}
+
+ULONG WINAPI Server_Release(IAccessible *iface)
+{
+    Server *This = impl_from_Server(iface);
+    wprintf(L"%S: iface %p, This->ref %d\n", __func__, iface, This->ref);
+    return InterlockedDecrement(&This->ref);
+}
+
+HRESULT WINAPI Server_GetTypeInfoCount(
+        IAccessible *iface, UINT *pctinfo)
+{
+    *pctinfo = 0;
+
+    return S_OK;
+}
+
+HRESULT WINAPI Server_GetTypeInfo(IAccessible *iface,
+        UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
+{
+    *ppTInfo = NULL;
+
+    return S_OK;
+}
+
+HRESULT WINAPI Server_GetIDsOfNames(IAccessible *iface, REFIID riid,
+        LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
+{
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI Server_Invoke(IAccessible *iface, DISPID dispIdMember,
+        REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
+        VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
+{
+    return S_OK;
+}
+
+HRESULT WINAPI Server_get_accParent(
+        IAccessible *iface, IDispatch **ppdispParent)
+{
+    Server *This = impl_from_Server(iface);
+
+    if (This->parent)
+    {
+        Server_QueryInterface(This->parent, &IID_IDispatch, (void **)ppdispParent);
+	return S_OK;
+    }
+
+    *ppdispParent = NULL;
+
+    return S_FALSE;
+}
+
+HRESULT WINAPI Server_get_accChildCount(
+        IAccessible *iface, LONG *pcountChildren)
+{
+    Server *This = impl_from_Server(iface);
+
+    *pcountChildren = This->child_count;
+
+    return S_OK;
+}
+
+HRESULT WINAPI Server_get_accChild(IAccessible *iface,
+        VARIANT varChildID, IDispatch **ppdispChild)
+{
+    IAccessible *child = NULL;
+
+    if (V_VT(&varChildID) != VT_I4)
+        return E_FAIL;
+
+    if (V_I4(&varChildID) == CHILDID_SELF)
+        child = iface;
+    else
+        find_accessible(iface, &child, V_I4(&varChildID));
+
+    if (child)
+    {
+        Server_QueryInterface(child, &IID_IDispatch, (void **)ppdispChild);
+        return S_OK;
+    }
+
+    return E_FAIL;
+}
+
+HRESULT WINAPI Server_get_accName(IAccessible *iface,
+        VARIANT varID, BSTR *pszName)
+{
+    Server *This = impl_from_Server(iface);
+
+    *pszName = This->name;
+
+    return S_OK;
+}
+
+HRESULT WINAPI Server_get_accValue(IAccessible *iface,
+        VARIANT varID, BSTR *pszValue)
+{
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI Server_get_accDescription(IAccessible *iface,
+        VARIANT varID, BSTR *pszDescription)
+{
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI Server_get_accRole(IAccessible *iface,
+        VARIANT varID, VARIANT *pvarRole)
+{
+    Server *This = impl_from_Server(iface);
+
+    if ((V_VT(&varID) == VT_I4) && (V_I4(&varID) == CHILDID_SELF))
+    {
+        V_VT(pvarRole) = VT_I4;
+        V_I4(pvarRole) = This->role;
+
+        return S_OK;
+    }
+
+    return E_INVALIDARG;
+}
+
+HRESULT WINAPI Server_get_accState(IAccessible *iface,
+        VARIANT varID, VARIANT *pvarState)
+{
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI Server_get_accHelp(IAccessible *iface,
+        VARIANT varID, BSTR *pszHelp)
+{
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI Server_get_accHelpTopic(IAccessible *iface,
+        BSTR *pszHelpFile, VARIANT varID, LONG *pidTopic)
+{
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI Server_get_accKeyboardShortcut(IAccessible *iface,
+        VARIANT varID, BSTR *pszKeyboardShortcut)
+{
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI Server_get_accFocus(IAccessible *iface, VARIANT *pvarID)
+{
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI Server_get_accSelection(
+        IAccessible *iface, VARIANT *pvarID)
+{
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI Server_get_accDefaultAction(IAccessible *iface,
+        VARIANT varID, BSTR *pszDefaultAction)
+{
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI Server_accSelect(IAccessible *iface,
+        LONG flagsSelect, VARIANT varID)
+{
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI Server_accLocation(IAccessible *iface, LONG *pxLeft,
+        LONG *pyTop, LONG *pcxWidth, LONG *pcyHeight, VARIANT varID)
+{
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI Server_accNavigate(IAccessible *iface,
+        LONG navDir, VARIANT varStart, VARIANT *pvarEnd)
+{
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI Server_accHitTest(IAccessible *iface,
+        LONG xLeft, LONG yTop, VARIANT *pvarID)
+{
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI Server_accDoDefaultAction(
+        IAccessible *iface, VARIANT varID)
+{
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI Server_put_accName(IAccessible *iface,
+        VARIANT varID, BSTR pszName)
+{
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI Server_put_accValue(IAccessible *iface,
+        VARIANT varID, BSTR pszValue)
+{
+    return E_NOTIMPL;
+}
+
+IAccessibleVtbl ServerAccessibleVtbl = {
+    Server_QueryInterface,
+    Server_AddRef,
+    Server_Release,
+    Server_GetTypeInfoCount,
+    Server_GetTypeInfo,
+    Server_GetIDsOfNames,
+    Server_Invoke,
+    Server_get_accParent,
+    Server_get_accChildCount,
+    Server_get_accChild,
+    Server_get_accName,
+    Server_get_accValue,
+    Server_get_accDescription,
+    Server_get_accRole,
+    Server_get_accState,
+    Server_get_accHelp,
+    Server_get_accHelpTopic,
+    Server_get_accKeyboardShortcut,
+    Server_get_accFocus,
+    Server_get_accSelection,
+    Server_get_accDefaultAction,
+    Server_accSelect,
+    Server_accLocation,
+    Server_accNavigate,
+    Server_accHitTest,
+    Server_accDoDefaultAction,
+    Server_put_accName,
+    Server_put_accValue
+};
+
+/*
+ * Server enumVARIANT Vtbl.
+ */
+inline Server* impl_from_Server_EnumVARIANT(IEnumVARIANT *iface)
+{
+    return CONTAINING_RECORD(iface, Server, IEnumVARIANT_iface);
+}
+
+HRESULT WINAPI Server_EnumVARIANT_QueryInterface(IEnumVARIANT *iface, REFIID riid, void **ppv)
+{
+    Server *This = impl_from_Server_EnumVARIANT(iface);
+    return IAccessible_QueryInterface(&This->IAccessible_iface, riid, ppv);
+}
+
+ULONG WINAPI Server_EnumVARIANT_AddRef(IEnumVARIANT *iface)
+{
+    Server *This = impl_from_Server_EnumVARIANT(iface);
+    return IAccessible_AddRef(&This->IAccessible_iface);
+}
+
+ULONG WINAPI Server_EnumVARIANT_Release(IEnumVARIANT *iface)
+{
+    Server *This = impl_from_Server_EnumVARIANT(iface);
+    return IAccessible_Release(&This->IAccessible_iface);
+}
+
+HRESULT WINAPI Server_EnumVARIANT_Next(IEnumVARIANT *iface,
+        ULONG celt, VARIANT *rgVar, ULONG *pCeltFetched)
+{
+    Server *This = impl_from_Server_EnumVARIANT(iface);
+    ULONG fetched = 0;
+    UINT i;
+
+    for (i = This->enum_pos; i < This->child_count; i++)
+    {
+        Server *child = impl_from_Server(This->children[i]);
+
+	V_VT(&rgVar[i]) = VT_I4;
+	V_I4(&rgVar[i]) = child->child_id;
+	fetched++;
+    }
+
+    *pCeltFetched = fetched;
+
+    return celt == fetched ? S_OK : S_FALSE;
+}
+
+HRESULT WINAPI Server_EnumVARIANT_Skip(IEnumVARIANT *iface, ULONG celt)
+{
+    Server *This = impl_from_Server_EnumVARIANT(iface);
+
+    if ((celt + This->enum_pos) < This->child_count)
+    {
+	This->enum_pos += celt;
+	return S_OK;
+    }
+
+    return S_FALSE;
+}
+
+HRESULT WINAPI Server_EnumVARIANT_Reset(IEnumVARIANT *iface)
+{
+    Server *This = impl_from_Server_EnumVARIANT(iface);
+
+    This->enum_pos = 0;
+
+    return S_OK;
+}
+
+HRESULT WINAPI Server_EnumVARIANT_Clone(IEnumVARIANT *iface, IEnumVARIANT **ppEnum)
+{
+    return E_NOTIMPL;
+}
+
+const IEnumVARIANTVtbl ServerEnumVARIANTVtbl = {
+    Server_EnumVARIANT_QueryInterface,
+    Server_EnumVARIANT_AddRef,
+    Server_EnumVARIANT_Release,
+    Server_EnumVARIANT_Next,
+    Server_EnumVARIANT_Skip,
+    Server_EnumVARIANT_Reset,
+    Server_EnumVARIANT_Clone
+};
+
+static HRESULT create_server_obj_tree(Server **tree, HWND hwnd)
+{
+    Server *acc_objs;
+    unsigned int i;
+
+    if (!IsWindow(hwnd))
+        return E_FAIL;
+
+    acc_objs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
+            sizeof(*acc_objs) * ARRAY_SIZE(obj_tree_info));
+    if (!acc_objs)
+        return E_OUTOFMEMORY;
+
+    for (i = 0; i < ARRAY_SIZE(obj_tree_info); i++)
+    {
+        const acc_object_info *obj_info = &obj_tree_info[i];
+        Server *obj = &acc_objs[i];
+
+        obj->IAccessible_iface.lpVtbl = &ServerAccessibleVtbl;
+        obj->IEnumVARIANT_iface.lpVtbl = &ServerEnumVARIANTVtbl;
+        obj->ref = 1;
+        obj->hwnd = hwnd;
+        obj->name = SysAllocString(obj_info->obj_name);
+        if (!obj->name && obj_info->obj_name)
+            return E_OUTOFMEMORY;
+        obj->child_id = obj_info->child_id;
+        obj->role = obj_info->role;
+        obj->child_count = obj_info->child_count;
+        if (obj->child_count)
+        {
+            obj->children = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
+                    sizeof(*obj->children) * obj->child_count);
+            if (!obj->children)
+                return E_OUTOFMEMORY;
+        }
+
+        if (obj_info->parent_id >= 0)
+        {
+            obj->parent = &acc_objs[obj_info->parent_id - 1].IAccessible_iface;
+            acc_objs[obj_info->parent_id - 1].children[obj_info->child_pos] = &obj->IAccessible_iface;
+        }
+    }
+
+    *tree = acc_objs;
+
+    return S_OK;
+}
+
+static void free_server_obj_tree(Server *tree)
+{
+    unsigned int i;
+
+    for (i = 0; i < ARRAY_SIZE(obj_tree_info); i++)
+    {
+        Server *obj = &tree[i];
+
+        if (obj->name)
+            SysFreeString(obj->name);
+        if (obj->children)
+            HeapFree(GetProcessHeap(), 0, obj->children);
+        IAccessible_Release(&obj->IAccessible_iface);
+    }
+
+    HeapFree(GetProcessHeap(), 0, tree);
+}
+
 static void test_getroletext(void)
 {
     INT ret, role;
@@ -564,7 +1125,13 @@ static LRESULT WINAPI test_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARA
         if(lparam == (DWORD)OBJID_CURSOR)
             return E_UNEXPECTED;
         if(lparam == (DWORD)OBJID_CLIENT)
+        {
+            if (server_obj_tree)
+                return LresultFromObject(&IID_IAccessible, wparam,
+                        (IUnknown *)&server_obj_tree->IAccessible_iface);
+
             return LresultFromObject(&IID_IUnknown, wparam, &Object);
+        }
         if(lparam == (DWORD)OBJID_WINDOW)
             return 0;
 
@@ -621,6 +1188,58 @@ static void test_AccessibleObjectFromWindow(void)
     DestroyWindow(hwnd);
 }
 
+static void test_AccessibleObjectFromEvent(void)
+{
+    IAccessible *acc = NULL;
+    BSTR name = NULL;
+    VARIANT var;
+    HRESULT hr;
+    HWND hwnd;
+
+    hwnd = CreateWindowA("oleacc_test", "test", WS_OVERLAPPEDWINDOW,
+            0, 0, 0, 0, NULL, NULL, NULL, NULL);
+    ok(hwnd != NULL, "CreateWindow failed\n");
+
+    hr = create_server_obj_tree(&server_obj_tree, hwnd);
+    ok(SUCCEEDED(hr), "got %#x\n", hr);
+
+    hr = AccessibleObjectFromEvent(hwnd, OBJID_CLIENT, CHILDID_SELF, &acc, &var);
+    ok(SUCCEEDED(hr), "got %#x\n", hr);
+
+    hr = IAccessible_get_accName(acc, var, &name);
+    ok(SUCCEEDED(hr), "got %#x\n", hr);
+    ok(!lstrcmpW(name, L"acc_main"), "name = %s\n", wine_dbgstr_w(name));
+    IAccessible_Release(acc);
+
+    acc = NULL;
+    hr = AccessibleObjectFromEvent(hwnd, OBJID_CLIENT, ACC_OBJ_ID_CHILD_1_1_0, &acc, &var);
+    ok(SUCCEEDED(hr), "got %#x\n", hr);
+    ok(V_VT(&var) == VT_I4, "got %#x, expected %#x\n", V_VT(&var), VT_I4);
+    ok(V_I4(&var) == CHILDID_SELF, "got %#x, expected %#x\n", V_I4(&var), CHILDID_SELF);
+
+    hr = IAccessible_get_accName(acc, var, &name);
+    ok(SUCCEEDED(hr), "got %#x\n", hr);
+    ok(!lstrcmpW(name, L"acc_child_1_1_0"), "name = %s\n", wine_dbgstr_w(name));
+    IAccessible_Release(acc);
+
+    /*
+     * Invalid Child ID, in this case we just get the same result as
+     * AccessibleObjectFromWindow.
+     */
+    acc = NULL;
+    hr = AccessibleObjectFromEvent(hwnd, OBJID_CLIENT, ACC_OBJ_ID_CHILD_2_0 + 1, &acc, &var);
+    ok(SUCCEEDED(hr), "got %#x\n", hr);
+
+    hr = IAccessible_get_accName(acc, var, &name);
+    ok(SUCCEEDED(hr), "got %#x\n", hr);
+    ok(!lstrcmpW(name, L"acc_main"), "name = %s\n", wine_dbgstr_w(name));
+    IAccessible_Release(acc);
+
+    DestroyWindow(hwnd);
+    free_server_obj_tree(server_obj_tree);
+    server_obj_tree = NULL;
+}
+
 static void test_GetProcessHandleFromHwnd(void)
 {
     HANDLE proc;
@@ -1061,6 +1680,7 @@ START_TEST(main)
     test_GetProcessHandleFromHwnd();
     test_default_client_accessible_object();
     test_AccessibleChildren(&Accessible);
+    test_AccessibleObjectFromEvent();
 
     unregister_window_class();
     CoUninitialize();
-- 
2.25.1




More information about the wine-devel mailing list