[PATCH v2 2/2] oleacc: Add tests for AccessibleObjectFromEvent.
Connor McAdams
cmcadams at codeweavers.com
Mon May 24 21:15:24 CDT 2021
Signed-off-by: Connor McAdams <cmcadams at codeweavers.com>
---
-This may need more tests, I'm struggling to think up more.
-V2: Remove wprintf debugging function left in by accident.
---
dlls/oleacc/tests/main.c | 619 +++++++++++++++++++++++++++++++++++++++
1 file changed, 619 insertions(+)
diff --git a/dlls/oleacc/tests/main.c b/dlls/oleacc/tests/main.c
index ce44e06abe4..c8cd1d991bd 100644
--- a/dlls/oleacc/tests/main.c
+++ b/dlls/oleacc/tests/main.c
@@ -304,6 +304,566 @@ 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);
+ 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 +1124,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 +1187,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 +1679,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