diff options
Diffstat (limited to 'toolkit/qa/cppunit/a11y')
11 files changed, 1782 insertions, 0 deletions
diff --git a/toolkit/qa/cppunit/a11y/AccessibilityTools.cxx b/toolkit/qa/cppunit/a11y/AccessibilityTools.cxx new file mode 100644 index 000000000..572f82e62 --- /dev/null +++ b/toolkit/qa/cppunit/a11y/AccessibilityTools.cxx @@ -0,0 +1,515 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "AccessibilityTools.hxx" + +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/XAccessible.hpp> +#include <com/sun/star/accessibility/XAccessibleContext.hpp> + +#include <sal/log.hxx> +#include <vcl/scheduler.hxx> +#include <vcl/timer.hxx> + +using namespace css; + +/* FIXME: mostly copied from sw/qa/extras/accessibility/accessible_relation_set.cxx */ +css::uno::Reference<css::accessibility::XAccessibleContext> +AccessibilityTools::getAccessibleObjectForRole( + const css::uno::Reference<css::accessibility::XAccessible>& xacc, sal_Int16 role) +{ + css::uno::Reference<css::accessibility::XAccessibleContext> ac = xacc->getAccessibleContext(); + bool isShowing + = ac->getAccessibleStateSet()->contains(accessibility::AccessibleStateType::SHOWING); + + if ((ac->getAccessibleRole() == role) && isShowing) + { + return ac; + } + else + { + int count = ac->getAccessibleChildCount(); + + for (int i = 0; i < count && i < AccessibilityTools::MAX_CHILDREN; i++) + { + css::uno::Reference<css::accessibility::XAccessibleContext> ac2 + = AccessibilityTools::getAccessibleObjectForRole(ac->getAccessibleChild(i), role); + if (ac2.is()) + return ac2; + } + } + return nullptr; +} +/* FIXME: end copy */ + +bool AccessibilityTools::equals(const uno::Reference<accessibility::XAccessible>& xacc1, + const uno::Reference<accessibility::XAccessible>& xacc2) +{ + if (!xacc1.is() || !xacc2.is()) + return xacc1.is() == xacc2.is(); + return equals(xacc1->getAccessibleContext(), xacc2->getAccessibleContext()); +} + +bool AccessibilityTools::equals(const uno::Reference<accessibility::XAccessibleContext>& xctx1, + const uno::Reference<accessibility::XAccessibleContext>& xctx2) +{ + if (!xctx1.is() || !xctx2.is()) + return xctx1.is() == xctx2.is(); + + if (xctx1->getAccessibleRole() != xctx2->getAccessibleRole()) + return false; + + if (xctx1->getAccessibleName() != xctx2->getAccessibleName()) + return false; + + if (xctx1->getAccessibleDescription() != xctx2->getAccessibleDescription()) + return false; + + if (xctx1->getAccessibleChildCount() != xctx2->getAccessibleChildCount()) + return false; + + /* this one was not in the Java version */ + if (xctx1->getAccessibleIndexInParent() != xctx2->getAccessibleIndexInParent()) + return false; + + return equals(xctx1->getAccessibleParent(), xctx2->getAccessibleParent()); +} + +bool AccessibilityTools::equals(const uno::Reference<accessibility::XAccessibleStateSet>& xsts1, + const uno::Reference<accessibility::XAccessibleStateSet>& xsts2) +{ + if (!xsts1.is() || !xsts2.is()) + return xsts1.is() == xsts2.is(); + return xsts1->getStates() == xsts2->getStates(); +} + +OUString AccessibilityTools::getRoleName(const sal_Int16 role) +{ + switch (role) + { + case accessibility::AccessibleRole::UNKNOWN: + return "UNKNOWN"; + case accessibility::AccessibleRole::ALERT: + return "ALERT"; + case accessibility::AccessibleRole::BUTTON_DROPDOWN: + return "BUTTON_DROPDOWN"; + case accessibility::AccessibleRole::BUTTON_MENU: + return "BUTTON_MENU"; + case accessibility::AccessibleRole::CANVAS: + return "CANVAS"; + case accessibility::AccessibleRole::CAPTION: + return "CAPTION"; + case accessibility::AccessibleRole::CHART: + return "CHART"; + case accessibility::AccessibleRole::CHECK_BOX: + return "CHECK_BOX"; + case accessibility::AccessibleRole::CHECK_MENU_ITEM: + return "CHECK_MENU_ITEM"; + case accessibility::AccessibleRole::COLOR_CHOOSER: + return "COLOR_CHOOSER"; + case accessibility::AccessibleRole::COLUMN_HEADER: + return "COLUMN_HEADER"; + case accessibility::AccessibleRole::COMBO_BOX: + return "COMBO_BOX"; + case accessibility::AccessibleRole::COMMENT: + return "COMMENT"; + case accessibility::AccessibleRole::COMMENT_END: + return "COMMENT_END"; + case accessibility::AccessibleRole::DATE_EDITOR: + return "DATE_EDITOR"; + case accessibility::AccessibleRole::DESKTOP_ICON: + return "DESKTOP_ICON"; + case accessibility::AccessibleRole::DESKTOP_PANE: + return "DESKTOP_PANE"; + case accessibility::AccessibleRole::DIALOG: + return "DIALOG"; + case accessibility::AccessibleRole::DIRECTORY_PANE: + return "DIRECTORY_PANE"; + case accessibility::AccessibleRole::DOCUMENT: + return "DOCUMENT"; + case accessibility::AccessibleRole::DOCUMENT_PRESENTATION: + return "DOCUMENT_PRESENTATION"; + case accessibility::AccessibleRole::DOCUMENT_SPREADSHEET: + return "DOCUMENT_SPREADSHEET"; + case accessibility::AccessibleRole::DOCUMENT_TEXT: + return "DOCUMENT_TEXT"; + case accessibility::AccessibleRole::EDIT_BAR: + return "EDIT_BAR"; + case accessibility::AccessibleRole::EMBEDDED_OBJECT: + return "EMBEDDED_OBJECT"; + case accessibility::AccessibleRole::END_NOTE: + return "END_NOTE"; + case accessibility::AccessibleRole::FILE_CHOOSER: + return "FILE_CHOOSER"; + case accessibility::AccessibleRole::FILLER: + return "FILLER"; + case accessibility::AccessibleRole::FONT_CHOOSER: + return "FONT_CHOOSER"; + case accessibility::AccessibleRole::FOOTER: + return "FOOTER"; + case accessibility::AccessibleRole::FOOTNOTE: + return "FOOTNOTE"; + case accessibility::AccessibleRole::FORM: + return "FORM"; + case accessibility::AccessibleRole::FRAME: + return "FRAME"; + case accessibility::AccessibleRole::GLASS_PANE: + return "GLASS_PANE"; + case accessibility::AccessibleRole::GRAPHIC: + return "GRAPHIC"; + case accessibility::AccessibleRole::GROUP_BOX: + return "GROUP_BOX"; + case accessibility::AccessibleRole::HEADER: + return "HEADER"; + case accessibility::AccessibleRole::HEADING: + return "HEADING"; + case accessibility::AccessibleRole::HYPER_LINK: + return "HYPER_LINK"; + case accessibility::AccessibleRole::ICON: + return "ICON"; + case accessibility::AccessibleRole::IMAGE_MAP: + return "IMAGE_MAP"; + case accessibility::AccessibleRole::INTERNAL_FRAME: + return "INTERNAL_FRAME"; + case accessibility::AccessibleRole::LABEL: + return "LABEL"; + case accessibility::AccessibleRole::LAYERED_PANE: + return "LAYERED_PANE"; + case accessibility::AccessibleRole::LIST: + return "LIST"; + case accessibility::AccessibleRole::LIST_ITEM: + return "LIST_ITEM"; + case accessibility::AccessibleRole::MENU: + return "MENU"; + case accessibility::AccessibleRole::MENU_BAR: + return "MENU_BAR"; + case accessibility::AccessibleRole::MENU_ITEM: + return "MENU_ITEM"; + case accessibility::AccessibleRole::NOTE: + return "NOTE"; + case accessibility::AccessibleRole::OPTION_PANE: + return "OPTION_PANE"; + case accessibility::AccessibleRole::PAGE: + return "PAGE"; + case accessibility::AccessibleRole::PAGE_TAB: + return "PAGE_TAB"; + case accessibility::AccessibleRole::PAGE_TAB_LIST: + return "PAGE_TAB_LIST"; + case accessibility::AccessibleRole::PANEL: + return "PANEL"; + case accessibility::AccessibleRole::PARAGRAPH: + return "PARAGRAPH"; + case accessibility::AccessibleRole::PASSWORD_TEXT: + return "PASSWORD_TEXT"; + case accessibility::AccessibleRole::POPUP_MENU: + return "POPUP_MENU"; + case accessibility::AccessibleRole::PROGRESS_BAR: + return "PROGRESS_BAR"; + case accessibility::AccessibleRole::PUSH_BUTTON: + return "PUSH_BUTTON"; + case accessibility::AccessibleRole::RADIO_BUTTON: + return "RADIO_BUTTON"; + case accessibility::AccessibleRole::RADIO_MENU_ITEM: + return "RADIO_MENU_ITEM"; + case accessibility::AccessibleRole::ROOT_PANE: + return "ROOT_PANE"; + case accessibility::AccessibleRole::ROW_HEADER: + return "ROW_HEADER"; + case accessibility::AccessibleRole::RULER: + return "RULER"; + case accessibility::AccessibleRole::SCROLL_BAR: + return "SCROLL_BAR"; + case accessibility::AccessibleRole::SCROLL_PANE: + return "SCROLL_PANE"; + case accessibility::AccessibleRole::SECTION: + return "SECTION"; + case accessibility::AccessibleRole::SEPARATOR: + return "SEPARATOR"; + case accessibility::AccessibleRole::SHAPE: + return "SHAPE"; + case accessibility::AccessibleRole::SLIDER: + return "SLIDER"; + case accessibility::AccessibleRole::SPIN_BOX: + return "SPIN_BOX"; + case accessibility::AccessibleRole::SPLIT_PANE: + return "SPLIT_PANE"; + case accessibility::AccessibleRole::STATIC: + return "STATIC"; + case accessibility::AccessibleRole::STATUS_BAR: + return "STATUS_BAR"; + case accessibility::AccessibleRole::TABLE: + return "TABLE"; + case accessibility::AccessibleRole::TABLE_CELL: + return "TABLE_CELL"; + case accessibility::AccessibleRole::TEXT: + return "TEXT"; + case accessibility::AccessibleRole::TEXT_FRAME: + return "TEXT_FRAME"; + case accessibility::AccessibleRole::TOGGLE_BUTTON: + return "TOGGLE_BUTTON"; + case accessibility::AccessibleRole::TOOL_BAR: + return "TOOL_BAR"; + case accessibility::AccessibleRole::TOOL_TIP: + return "TOOL_TIP"; + case accessibility::AccessibleRole::TREE: + return "TREE"; + case accessibility::AccessibleRole::TREE_ITEM: + return "TREE_ITEM"; + case accessibility::AccessibleRole::TREE_TABLE: + return "TREE_TABLE"; + case accessibility::AccessibleRole::VIEW_PORT: + return "VIEW_PORT"; + case accessibility::AccessibleRole::WINDOW: + return "WINDOW"; + }; + return "unknown"; +} + +OUString AccessibilityTools::getStateName(const sal_Int16 state) +{ + switch (state) + { + case accessibility::AccessibleStateType::ACTIVE: + return "ACTIVE"; + case accessibility::AccessibleStateType::ARMED: + return "ARMED"; + case accessibility::AccessibleStateType::BUSY: + return "BUSY"; + case accessibility::AccessibleStateType::CHECKED: + return "CHECKED"; + case accessibility::AccessibleStateType::COLLAPSE: + return "COLLAPSE"; + case accessibility::AccessibleStateType::DEFAULT: + return "DEFAULT"; + case accessibility::AccessibleStateType::DEFUNC: + return "DEFUNC"; + case accessibility::AccessibleStateType::EDITABLE: + return "EDITABLE"; + case accessibility::AccessibleStateType::ENABLED: + return "ENABLED"; + case accessibility::AccessibleStateType::EXPANDABLE: + return "EXPANDABLE"; + case accessibility::AccessibleStateType::EXPANDED: + return "EXPANDED"; + case accessibility::AccessibleStateType::FOCUSABLE: + return "FOCUSABLE"; + case accessibility::AccessibleStateType::FOCUSED: + return "FOCUSED"; + case accessibility::AccessibleStateType::HORIZONTAL: + return "HORIZONTAL"; + case accessibility::AccessibleStateType::ICONIFIED: + return "ICONIFIED"; + case accessibility::AccessibleStateType::INDETERMINATE: + return "INDETERMINATE"; + case accessibility::AccessibleStateType::INVALID: + return "INVALID"; + case accessibility::AccessibleStateType::MANAGES_DESCENDANTS: + return "MANAGES_DESCENDANTS"; + case accessibility::AccessibleStateType::MODAL: + return "MODAL"; + case accessibility::AccessibleStateType::MOVEABLE: + return "MOVEABLE"; + case accessibility::AccessibleStateType::MULTI_LINE: + return "MULTI_LINE"; + case accessibility::AccessibleStateType::MULTI_SELECTABLE: + return "MULTI_SELECTABLE"; + case accessibility::AccessibleStateType::OFFSCREEN: + return "OFFSCREEN"; + case accessibility::AccessibleStateType::OPAQUE: + return "OPAQUE"; + case accessibility::AccessibleStateType::PRESSED: + return "PRESSED"; + case accessibility::AccessibleStateType::RESIZABLE: + return "RESIZABLE"; + case accessibility::AccessibleStateType::SELECTABLE: + return "SELECTABLE"; + case accessibility::AccessibleStateType::SELECTED: + return "SELECTED"; + case accessibility::AccessibleStateType::SENSITIVE: + return "SENSITIVE"; + case accessibility::AccessibleStateType::SHOWING: + return "SHOWING"; + case accessibility::AccessibleStateType::SINGLE_LINE: + return "SINGLE_LINE"; + case accessibility::AccessibleStateType::STALE: + return "STALE"; + case accessibility::AccessibleStateType::TRANSIENT: + return "TRANSIENT"; + case accessibility::AccessibleStateType::VERTICAL: + return "VERTICAL"; + case accessibility::AccessibleStateType::VISIBLE: + return "VISIBLE"; + } + return "unknown"; +} + +OUString AccessibilityTools::getEventIdName(const sal_Int16 event_id) +{ + switch (event_id) + { + case accessibility::AccessibleEventId::ACTION_CHANGED: + return "ACTION_CHANGED"; + case accessibility::AccessibleEventId::ACTIVE_DESCENDANT_CHANGED: + return "ACTIVE_DESCENDANT_CHANGED"; + case accessibility::AccessibleEventId::ACTIVE_DESCENDANT_CHANGED_NOFOCUS: + return "ACTIVE_DESCENDANT_CHANGED_NOFOCUS"; + case accessibility::AccessibleEventId::BOUNDRECT_CHANGED: + return "BOUNDRECT_CHANGED"; + case accessibility::AccessibleEventId::CARET_CHANGED: + return "CARET_CHANGED"; + case accessibility::AccessibleEventId::CHILD: + return "CHILD"; + case accessibility::AccessibleEventId::COLUMN_CHANGED: + return "COLUMN_CHANGED"; + case accessibility::AccessibleEventId::CONTENT_FLOWS_FROM_RELATION_CHANGED: + return "CONTENT_FLOWS_FROM_RELATION_CHANGED"; + case accessibility::AccessibleEventId::CONTENT_FLOWS_TO_RELATION_CHANGED: + return "CONTENT_FLOWS_TO_RELATION_CHANGED"; + case accessibility::AccessibleEventId::CONTROLLED_BY_RELATION_CHANGED: + return "CONTROLLED_BY_RELATION_CHANGED"; + case accessibility::AccessibleEventId::CONTROLLER_FOR_RELATION_CHANGED: + return "CONTROLLER_FOR_RELATION_CHANGED"; + case accessibility::AccessibleEventId::DESCRIPTION_CHANGED: + return "DESCRIPTION_CHANGED"; + case accessibility::AccessibleEventId::HYPERTEXT_CHANGED: + return "HYPERTEXT_CHANGED"; + case accessibility::AccessibleEventId::INVALIDATE_ALL_CHILDREN: + return "INVALIDATE_ALL_CHILDREN"; + case accessibility::AccessibleEventId::LABELED_BY_RELATION_CHANGED: + return "LABELED_BY_RELATION_CHANGED"; + case accessibility::AccessibleEventId::LABEL_FOR_RELATION_CHANGED: + return "LABEL_FOR_RELATION_CHANGED"; + case accessibility::AccessibleEventId::LISTBOX_ENTRY_COLLAPSED: + return "LISTBOX_ENTRY_COLLAPSED"; + case accessibility::AccessibleEventId::LISTBOX_ENTRY_EXPANDED: + return "LISTBOX_ENTRY_EXPANDED"; + case accessibility::AccessibleEventId::MEMBER_OF_RELATION_CHANGED: + return "MEMBER_OF_RELATION_CHANGED"; + case accessibility::AccessibleEventId::NAME_CHANGED: + return "NAME_CHANGED"; + case accessibility::AccessibleEventId::PAGE_CHANGED: + return "PAGE_CHANGED"; + case accessibility::AccessibleEventId::ROLE_CHANGED: + return "ROLE_CHANGED"; + case accessibility::AccessibleEventId::SECTION_CHANGED: + return "SECTION_CHANGED"; + case accessibility::AccessibleEventId::SELECTION_CHANGED: + return "SELECTION_CHANGED"; + case accessibility::AccessibleEventId::SELECTION_CHANGED_ADD: + return "SELECTION_CHANGED_ADD"; + case accessibility::AccessibleEventId::SELECTION_CHANGED_REMOVE: + return "SELECTION_CHANGED_REMOVE"; + case accessibility::AccessibleEventId::SELECTION_CHANGED_WITHIN: + return "SELECTION_CHANGED_WITHIN"; + case accessibility::AccessibleEventId::STATE_CHANGED: + return "STATE_CHANGED"; + case accessibility::AccessibleEventId::SUB_WINDOW_OF_RELATION_CHANGED: + return "SUB_WINDOW_OF_RELATION_CHANGED"; + case accessibility::AccessibleEventId::TABLE_CAPTION_CHANGED: + return "TABLE_CAPTION_CHANGED"; + case accessibility::AccessibleEventId::TABLE_COLUMN_DESCRIPTION_CHANGED: + return "TABLE_COLUMN_DESCRIPTION_CHANGED"; + case accessibility::AccessibleEventId::TABLE_COLUMN_HEADER_CHANGED: + return "TABLE_COLUMN_HEADER_CHANGED"; + case accessibility::AccessibleEventId::TABLE_MODEL_CHANGED: + return "TABLE_MODEL_CHANGED"; + case accessibility::AccessibleEventId::TABLE_ROW_DESCRIPTION_CHANGED: + return "TABLE_ROW_DESCRIPTION_CHANGED"; + case accessibility::AccessibleEventId::TABLE_ROW_HEADER_CHANGED: + return "TABLE_ROW_HEADER_CHANGED"; + case accessibility::AccessibleEventId::TABLE_SUMMARY_CHANGED: + return "TABLE_SUMMARY_CHANGED"; + case accessibility::AccessibleEventId::TEXT_ATTRIBUTE_CHANGED: + return "TEXT_ATTRIBUTE_CHANGED"; + case accessibility::AccessibleEventId::TEXT_CHANGED: + return "TEXT_CHANGED"; + case accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED: + return "TEXT_SELECTION_CHANGED"; + case accessibility::AccessibleEventId::VALUE_CHANGED: + return "VALUE_CHANGED"; + case accessibility::AccessibleEventId::VISIBLE_DATA_CHANGED: + return "VISIBLE_DATA_CHANGED"; + } + return "unknown"; +} + +OUString AccessibilityTools::debugName(accessibility::XAccessibleContext* ctx) +{ + return "role=" + AccessibilityTools::getRoleName(ctx->getAccessibleRole()) + " name=\"" + + ctx->getAccessibleName() + "\" description=\"" + ctx->getAccessibleDescription() + + "\""; +} + +OUString AccessibilityTools::debugName(accessibility::XAccessible* acc) +{ + return debugName(acc->getAccessibleContext().get()); +} + +OUString AccessibilityTools::debugName(accessibility::XAccessibleStateSet* xsts) +{ + OUString name; + + for (auto state : xsts->getStates()) + { + if (name.getLength()) + name += " | "; + name += AccessibilityTools::getStateName(state); + } + + return name; +} + +OUString AccessibilityTools::debugName(const accessibility::AccessibleEventObject* evobj) +{ + return "(AccessibleEventObject) { id=" + getEventIdName(evobj->EventId) + + " old=" + evobj->OldValue.getValueTypeName() + + " new=" + evobj->NewValue.getValueTypeName() + " }"; +} + +bool AccessibilityTools::Await(const std::function<bool()>& cUntilCallback, sal_uInt64 nTimeoutMs) +{ + bool success = false; + Timer aTimer("wait for event"); + aTimer.SetTimeout(nTimeoutMs); + aTimer.Start(); + do + { + Scheduler::ProcessEventsToIdle(); + success = cUntilCallback(); + } while (!success && aTimer.IsActive()); + SAL_WARN_IF(!success, "test", "timeout reached"); + return success; +} + +void AccessibilityTools::Wait(sal_uInt64 nTimeoutMs) +{ + Timer aTimer("wait for event"); + aTimer.SetTimeout(nTimeoutMs); + aTimer.Start(); + std::cout << "waiting for " << nTimeoutMs << "ms... "; + do + { + Scheduler::ProcessEventsToIdle(); + } while (aTimer.IsActive()); + std::cout << "ok." << std::endl; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/toolkit/qa/cppunit/a11y/AccessibilityTools.hxx b/toolkit/qa/cppunit/a11y/AccessibilityTools.hxx new file mode 100644 index 000000000..a16338a85 --- /dev/null +++ b/toolkit/qa/cppunit/a11y/AccessibilityTools.hxx @@ -0,0 +1,158 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <functional> +#include <string> + +#include <cppunit/TestAssert.h> + +#include <com/sun/star/accessibility/AccessibleEventObject.hpp> +#include <com/sun/star/accessibility/XAccessible.hpp> +#include <com/sun/star/accessibility/XAccessibleContext.hpp> +#include <com/sun/star/accessibility/XAccessibleStateSet.hpp> + +class AccessibilityTools +{ +public: + /** Maximum number of children to work on. This is especially useful for + * Calc which has a million elements, if not more. */ + static const sal_Int32 MAX_CHILDREN = 500; + + static css::uno::Reference<css::accessibility::XAccessibleContext> + getAccessibleObjectForRole(const css::uno::Reference<css::accessibility::XAccessible>& xacc, + sal_Int16 role); + + static bool equals(const css::uno::Reference<css::accessibility::XAccessible>& xacc1, + const css::uno::Reference<css::accessibility::XAccessible>& xacc2); + static bool equals(const css::uno::Reference<css::accessibility::XAccessibleContext>& xctx1, + const css::uno::Reference<css::accessibility::XAccessibleContext>& xctx2); + static bool equals(const css::uno::Reference<css::accessibility::XAccessibleStateSet>& xsts1, + const css::uno::Reference<css::accessibility::XAccessibleStateSet>& xsts2); + + static OUString getRoleName(const sal_Int16 role); + static OUString getStateName(const sal_Int16 state); + static OUString getEventIdName(const sal_Int16 event_id); + + template <typename T> static std::string debugString(const css::uno::Reference<T>& x) + { + return debugString(x.get()); + } + + template <typename T> static std::string debugString(const T& x) { return debugString(&x); } + + template <typename T> static std::string debugString(const T* p) + { + /* only the forwarding to debugName() might actually dereference @c p, + * and we rely on specializations to be as constant as possible and not + * violate the cast here. In practice it'll be the case for all types + * handle if we carefully write the specializations. In most case the + * specialization could take a const itself if the methods were + * properly marked const, but well. */ + return debugString(const_cast<T*>(p)); + } + + template <typename T> static std::string debugString(T* p) + { + CPPUNIT_NS::OStringStream ost; + + ost << "(" << static_cast<const void*>(p) << ")"; + if (p != nullptr) + ost << " " << debugName(p); + + return ost.str(); + } + + /** + * @brief Process events until a condition or a timeout + * @param cUntilCallback Callback condition + * @param nTimeoutMs Maximum time in ms to wait for condition + * @returns @c true if the condition was met, or @c false if the timeout + * has been reached. + * + * Processes events until idle, and either until the given condition + * becomes @c true or a timeout is reached. + * + * This is similar to Scheduler::ProcessEventsToIdle() but awaits a + * condition up to a timeout. This is useful if the waited-on condition + * might happen after the first idle time. The timeout helps in case the + * condition is not satisfied in reasonable time. + * + * @p cUntilCallback is called each time the scheduler reaches idle to check + * whether the condition is met. + * + * Example: + * @code + * ProcessEvents([&]() { return taskHasRun; }); + * @endcode + * + * @see Scheduler::ProcessEventsToIdle() + */ + static bool Await(const std::function<bool()>& cUntilCallback, sal_uInt64 nTimeoutMs = 3000); + + /** + * @brief Process events for a given time + * @param nTimeoutMs Time to dispatch events for + * + * Process events for a given time. This can be useful if waiting is in + * order but there is no actual condition to wait on (e.g. expect + * something *not* to happen). This similar in spirit to + * @c sleep(nTimeoutMs), but dispatches events during the wait. + * + * This function should be used sparsely because waiting a given time is + * rarely a good solution for a problem, but in some specific situations + * there is no better alternative (like, again, waiting for something not + * to happen). + */ + static void Wait(sal_uInt64 nTimeoutMs); + +private: + static OUString debugName(css::accessibility::XAccessibleContext* xctx); + static OUString debugName(css::accessibility::XAccessible* xacc); + static OUString debugName(css::accessibility::XAccessibleStateSet* xsts); + static OUString debugName(const css::accessibility::AccessibleEventObject* evobj); +}; + +CPPUNIT_NS_BEGIN +/* How to generate those automatically? We don't want to match all types + * not to mess up cppunit for types we don't support */ +#define AT_ASSERTION_TRAITS(T) \ + template <> struct assertion_traits<css::uno::Reference<T>> \ + { \ + static bool equal(const css::uno::Reference<T>& x, const css::uno::Reference<T>& y) \ + { \ + return AccessibilityTools::equals(x, y); \ + } \ + \ + static std::string toString(const css::uno::Reference<T>& x) \ + { \ + return AccessibilityTools::debugString(x); \ + } \ + } + +AT_ASSERTION_TRAITS(css::accessibility::XAccessible); +AT_ASSERTION_TRAITS(css::accessibility::XAccessibleContext); +AT_ASSERTION_TRAITS(css::accessibility::XAccessibleStateSet); + +#undef AT_ASSERTION_TRAITS + +CPPUNIT_NS_END + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/toolkit/qa/cppunit/a11y/AccessibleStatusBarTest.cxx b/toolkit/qa/cppunit/a11y/AccessibleStatusBarTest.cxx new file mode 100644 index 000000000..822223a74 --- /dev/null +++ b/toolkit/qa/cppunit/a11y/AccessibleStatusBarTest.cxx @@ -0,0 +1,184 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <string> + +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/XAccessible.hpp> +#include <com/sun/star/accessibility/XAccessibleComponent.hpp> +#include <com/sun/star/accessibility/XAccessibleContext.hpp> +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/awt/XTopWindow.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/util/XCloseable.hpp> + +#include <comphelper/sequence.hxx> +#include <rtl/ustrbuf.hxx> +#include <test/bootstrapfixture.hxx> +#include <vcl/scheduler.hxx> + +#include "AccessibilityTools.hxx" +#include "XAccessibleComponentTester.hxx" +#include "XAccessibleContextTester.hxx" +#include "XAccessibleExtendedComponentTester.hxx" +#include "XAccessibleEventBroadcasterTester.hxx" + +using namespace css; + +namespace +{ +class AccessibleStatusBarTest : public test::BootstrapFixture +{ +private: + uno::Reference<frame::XDesktop2> mxDesktop; + + uno::Reference<accessibility::XAccessibleContext> + getTestObject(const uno::Reference<awt::XWindow>& xWindow); + void runAllTests(const uno::Reference<awt::XWindow>& xWindow); + uno::Reference<lang::XComponent> openDocument(std::string_view sKind); + void testDocument(std::string_view sKind); + + void testWriterDoc() { testDocument("swriter"); } + void testMathDoc() { testDocument("smath"); } + void testDrawDoc() { testDocument("sdraw"); } + void testImpressDoc() { testDocument("simpress"); } + void testCalcDoc() { testDocument("scalc"); } + +public: + virtual void setUp() override; + + CPPUNIT_TEST_SUITE(AccessibleStatusBarTest); + CPPUNIT_TEST(testWriterDoc); + CPPUNIT_TEST(testMathDoc); + CPPUNIT_TEST(testDrawDoc); + CPPUNIT_TEST(testImpressDoc); + CPPUNIT_TEST(testCalcDoc); + CPPUNIT_TEST_SUITE_END(); +}; + +void AccessibleStatusBarTest::setUp() +{ + test::BootstrapFixture::setUp(); + + mxDesktop = frame::Desktop::create(mxComponentContext); +} + +uno::Reference<accessibility::XAccessibleContext> +AccessibleStatusBarTest::getTestObject(const uno::Reference<awt::XWindow>& xWindow) +{ + uno::Reference<accessibility::XAccessible> xAccessible(xWindow, uno::UNO_QUERY_THROW); + std::cout << "got accessible: " << xAccessible << std::endl; + std::cout << "accessible name: " << AccessibilityTools::debugString(xAccessible) << std::endl; + + auto xContext = AccessibilityTools::getAccessibleObjectForRole( + xAccessible, accessibility::AccessibleRole::STATUS_BAR); + std::cout << "got context: " << xContext << std::endl; + std::cout << "context name: " << AccessibilityTools::debugString(xContext) << std::endl; + + Scheduler::ProcessEventsToIdle(); // not sure why? + + uno::Reference<lang::XServiceInfo> xSI(xContext, uno::UNO_QUERY_THROW); + std::cout << "implementation name: " << xSI->getImplementationName() << std::endl; + auto serviceNames = xSI->getSupportedServiceNames(); + std::cout << "has " << serviceNames.size() << " services:" << std::endl; + for (auto& service : serviceNames) + std::cout << " * service: " << service << std::endl; + + return xContext; +} + +void AccessibleStatusBarTest::runAllTests(const uno::Reference<awt::XWindow>& xWindow) +{ + auto xContext = getTestObject(xWindow); + + uno::Reference<accessibility::XAccessibleComponent> xAccessibleComponent(xContext, + uno::UNO_QUERY_THROW); + XAccessibleComponentTester componentTester(xAccessibleComponent); + componentTester.testAll(); + + XAccessibleContextTester contextTester(xContext); + contextTester.testAll(); + + uno::Reference<accessibility::XAccessibleExtendedComponent> xAccessibleExtendedComponent( + xContext, uno::UNO_QUERY_THROW); + XAccessibleExtendedComponentTester extendedComponentTester(xAccessibleExtendedComponent); + extendedComponentTester.testAll(); + + uno::Reference<accessibility::XAccessibleEventBroadcaster> xAccessibleEventBroadcaster( + xContext, uno::UNO_QUERY_THROW); + XAccessibleEventBroadcasterTester eventBroadcasterTester(xAccessibleEventBroadcaster, xWindow); + eventBroadcasterTester.testAll(); +} + +uno::Reference<lang::XComponent> AccessibleStatusBarTest::openDocument(std::string_view sKind) +{ + /* not sure what's that about, but from SOfficeFactory.java:openDoc() */ + // that noargs thing for load attributes + std::vector<beans::PropertyValue> aArgs; + if (sKind == "simpress") + { + beans::PropertyValue aValue; + aValue.Name = "OpenFlags"; + aValue.Handle = -1; + aValue.Value <<= OUString("S"); + aValue.State = beans::PropertyState_DIRECT_VALUE; + aArgs.push_back(aValue); + } + + rtl::OUStringBuffer sURL("private:factory/"); + sURL.appendAscii(sKind.data(), sKind.length()); + + return mxDesktop->loadComponentFromURL(sURL.makeStringAndClear(), "_blank", 40, + comphelper::containerToSequence(aArgs)); +} + +void AccessibleStatusBarTest::testDocument(std::string_view sKind) +{ + auto xDoc = openDocument(sKind); + std::cout << "got document: " << xDoc << std::endl; + CPPUNIT_ASSERT(xDoc.is()); + uno::Reference<frame::XModel> xModel(xDoc, uno::UNO_QUERY_THROW); + std::cout << "got model: " << xModel << std::endl; + uno::Reference<awt::XWindow> xWindow( + xModel->getCurrentController()->getFrame()->getContainerWindow()); + std::cout << "got window: " << xWindow << std::endl; + uno::Reference<awt::XTopWindow> xTopWindow(xWindow, uno::UNO_QUERY_THROW); + std::cout << "got top window: " << xTopWindow << std::endl; + xTopWindow->toFront(); + + Scheduler::ProcessEventsToIdle(); + + runAllTests(xWindow); + + // close document + uno::Reference<css::util::XCloseable> xCloseable(xDoc, uno::UNO_QUERY_THROW); + xCloseable->close(false); +} + +CPPUNIT_TEST_SUITE_REGISTRATION(AccessibleStatusBarTest); +} // namespace + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/toolkit/qa/cppunit/a11y/XAccessibleComponentTester.cxx b/toolkit/qa/cppunit/a11y/XAccessibleComponentTester.cxx new file mode 100644 index 000000000..ea46ff778 --- /dev/null +++ b/toolkit/qa/cppunit/a11y/XAccessibleComponentTester.cxx @@ -0,0 +1,290 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "XAccessibleComponentTester.hxx" + +#include <cppunit/TestAssert.h> + +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/XAccessible.hpp> +#include <com/sun/star/accessibility/XAccessibleComponent.hpp> +#include <com/sun/star/accessibility/XAccessibleContext.hpp> +#include <com/sun/star/awt/Point.hpp> +#include <com/sun/star/uno/Reference.hxx> + +#include <tools/color.hxx> + +#include "AccessibilityTools.hxx" + +using namespace css; + +/** + * @brief Checks the component's bounds + * + * Checks the X and Y coordinates are non-negative, and that the width and + * height are greater than 0. + * + * Coherence with @c getLocation() is tested in the test for the + * latter. + */ +void XAccessibleComponentTester::testBounds() +{ + auto bounds = mxComponent->getBounds(); + std::cout << "bounds: " << bounds.Width << "x" << bounds.Height << std::showpos << bounds.X + << bounds.Y << std::noshowpos << std::endl; + CPPUNIT_ASSERT_GREATEREQUAL(static_cast<sal_Int32>(0), bounds.X); + CPPUNIT_ASSERT_GREATEREQUAL(static_cast<sal_Int32>(0), bounds.Y); + CPPUNIT_ASSERT_GREATER(static_cast<sal_Int32>(0), bounds.Width); + CPPUNIT_ASSERT_GREATER(static_cast<sal_Int32>(0), bounds.Height); +} + +/** + * @brief Tests results of XAccessibleComponent::getSize() + * + * Succeeds if the size is the same as in bounds. + */ +void XAccessibleComponentTester::testSize() +{ + auto bounds = mxComponent->getBounds(); + auto size = mxComponent->getSize(); + CPPUNIT_ASSERT_EQUAL(bounds.Width, size.Width); + CPPUNIT_ASSERT_EQUAL(bounds.Height, size.Height); +} + +/** + * @brief Tests results of XAccessibleComponent::getBounds() + * + * First checks 4 inner bounds (upper, lower, left and right) of component + * bounding box to contain at least one point of the component. Second 4 outer + * bounds are checked to not contain any component points. + * + * Succeeds if inner bounds contain component points and outer bounds don't + * contain any component points. + */ +void XAccessibleComponentTester::testContainsPoint() +{ + auto bounds = mxComponent->getBounds(); + + /* upper end */ + int curX = 0; + while (!mxComponent->containsPoint(awt::Point(curX, 0)) && curX < bounds.Width) + curX++; + CPPUNIT_ASSERT_MESSAGE("Upper bound of box contains no component points", curX < bounds.Width); + std::cout << "Upper bound of box contains point (" << curX << ",0)" << std::endl; + /* lower end */ + curX = 0; + while (!mxComponent->containsPoint(awt::Point(curX, bounds.Height - 1)) && curX < bounds.Width) + curX++; + CPPUNIT_ASSERT_MESSAGE("Lower bound of box contains no component points", curX < bounds.Width); + std::cout << "Lower bound of box contains point (" << curX << "," << (bounds.Height - 1) << ")" + << std::endl; + /* left end */ + int curY = 0; + while (!mxComponent->containsPoint(awt::Point(0, curY)) && curY < bounds.Height) + curY++; + CPPUNIT_ASSERT_MESSAGE("Left bound of box contains no component points", curY < bounds.Height); + std::cout << "Left bound of box contains point (0," << curY << ")" << std::endl; + /* right end */ + curY = 0; + while (!mxComponent->containsPoint(awt::Point(bounds.Width - 1, curY)) && curY < bounds.Height) + curY++; + CPPUNIT_ASSERT_MESSAGE("Right bound of box contains no component points", curY < bounds.Height); + std::cout << "Right bound of box contains point (" << (bounds.Width - 1) << "," << curY << ")" + << std::endl; + /* no match outside the bounds */ + for (int x = -1; x <= bounds.Width; x++) + { + CPPUNIT_ASSERT_MESSAGE("Outer upper bound CONTAINS a component point", + !mxComponent->containsPoint(awt::Point(x, -1))); + CPPUNIT_ASSERT_MESSAGE("Outer lower bound CONTAINS a component point", + !mxComponent->containsPoint(awt::Point(x, bounds.Height))); + } + for (int y = -1; y <= bounds.Height; y++) + { + CPPUNIT_ASSERT_MESSAGE("Outer left bound CONTAINS a component point", + !mxComponent->containsPoint(awt::Point(-1, y))); + CPPUNIT_ASSERT_MESSAGE("Outer right bound CONTAINS a component point", + !mxComponent->containsPoint(awt::Point(bounds.Width, y))); + } +} + +/** + * @brief Tests results of XAccessibleComponent::getAccessibleAtPoint() + * + * Iterates through all children which implement + * <code>XAccessibleComponent</code> (if they exist) determines their + * boundaries and tries to get each child by <code>getAccessibleAtPoint</code> + * passing point which belongs to the child. + * Also the point is checked which doesn't belong to child boundary box. + * + * Succeeds if in the first cases the right children are returned, and in the + * second <code>null</code> or another child is returned. + */ +void XAccessibleComponentTester::testAccessibleAtPoint() +{ + int count = mxContext->getAccessibleChildCount(); + std::cout << "Found " << count << " children" << std::endl; + for (int i = 0; i < count && i < AccessibilityTools::MAX_CHILDREN; i++) + { + auto child = mxContext->getAccessibleChild(i); + uno::Reference<accessibility::XAccessibleContext> childContext( + child->getAccessibleContext(), uno::UNO_SET_THROW); + std::cout << "* Found child: " << AccessibilityTools::debugString(child) << std::endl; + std::cout << " states: " + << AccessibilityTools::debugString(childContext->getAccessibleStateSet()) + << std::endl; + uno::Reference<accessibility::XAccessibleComponent> xChildComponent(childContext, + uno::UNO_QUERY); + std::cout << " component: " << xChildComponent << std::endl; + if (!xChildComponent) + continue; + + auto childBounds = xChildComponent->getBounds(); + if (childBounds.X == -1) + continue; + std::cout << " bounds: " << childBounds.Width << "x" << childBounds.Height << std::showpos + << childBounds.X << childBounds.Y << std::noshowpos << std::endl; + + std::cout << "finding the point which lies on the component" << std::endl; + int curX = 0; + int curY = 0; + while (!xChildComponent->containsPoint(awt::Point(curX, curY)) && curX < childBounds.Width) + { + curX++; + curY++; + } + if (curX >= childBounds.Width) + { + std::cout << "Couldn't find a point with contains" << std::endl; + continue; + } + else + { + std::cout << "Child found at point +" << childBounds.X + curX << "+" + << childBounds.Y + curY << std::endl; + } + + // trying the point laying on child + auto xAccAtPoint + = mxComponent->getAccessibleAtPoint(awt::Point(childBounds.X, childBounds.Y)); + CPPUNIT_ASSERT_MESSAGE("Child not found at point", xAccAtPoint.is()); + if (!AccessibilityTools::equals(child, xAccAtPoint)) + { + auto idxExpected = childContext->getAccessibleIndexInParent(); + auto idxResult = xAccAtPoint->getAccessibleContext()->getAccessibleIndexInParent(); + std::cout << "The child found (" << AccessibilityTools::debugString(xAccAtPoint) + << ") is not the expected one (" << AccessibilityTools::debugString(child) + << ")" << std::endl; + if (idxExpected < idxResult) + { + std::cout << "-- it probably is hidden behind? Skipping." << std::endl; + } + else + { + CPPUNIT_ASSERT_EQUAL_MESSAGE("The child found is NOT the expected one", child, + xAccAtPoint); + } + } + + // trying the point NOT laying on child + xAccAtPoint + = mxComponent->getAccessibleAtPoint(awt::Point(childBounds.X - 1, childBounds.Y - 1)); + if (xAccAtPoint.is()) + { + CPPUNIT_ASSERT_MESSAGE("Child found OUTSIDE its bounds", + !AccessibilityTools::equals(child, xAccAtPoint)); + } + } +} + +/** + * @brief Tests results of XAccessibleComponent::getLocation() + * + * Succeeds if the location is the same as location of boundary obtained by + * the <code>getBounds()</code> method. + */ +void XAccessibleComponentTester::testLocation() +{ + auto bounds = mxComponent->getBounds(); + auto location = mxComponent->getLocation(); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Invalid X location", bounds.X, location.X); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Invalid Y location", bounds.Y, location.Y); +} + +/** + * @brief Tests results of XAccessibleComponent::getLocationOnScreen() + * @returns @c true on success. + * + * Get the screen location of the component and its parent + * (if it exists and supports <code>XAccessibleComponent</code>). + * + * Succeeds component screen location equals to screen location of its parent + * plus location of the component relative to the parent. + */ +void XAccessibleComponentTester::testLocationOnScreen() +{ + auto location = mxComponent->getLocationOnScreen(); + std::cout << "location on screen: +" << location.X << "+" << location.Y << std::endl; + + auto xParent = mxContext->getAccessibleParent(); + if (!xParent.is()) + std::cout << "No parent" << std::endl; + else + { + std::cout << "Found parent: " << AccessibilityTools::debugString(xParent) << std::endl; + uno::Reference<accessibility::XAccessibleComponent> xParentComponent( + xParent->getAccessibleContext(), uno::UNO_QUERY); + if (!xParentComponent.is()) + std::cout << "Parent is not a Component" << std::endl; + else + { + auto bounds = mxComponent->getBounds(); + auto parentLocation = xParentComponent->getLocationOnScreen(); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Invalid X screen location", parentLocation.X + bounds.X, + location.X); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Invalid Y screen location", parentLocation.Y + bounds.Y, + location.Y); + } + } +} + +/** + * Just calls the method. + */ +void XAccessibleComponentTester::testGrabFocus() { mxComponent->grabFocus(); } + +/** + * Just calls the method. + */ +void XAccessibleComponentTester::testGetForeground() +{ + auto color = mxComponent->getForeground(); + std::cout << "foreground color: " << Color(ColorAlpha, color) << std::endl; +} + +/** + * Just calls the method. + */ +void XAccessibleComponentTester::testGetBackground() +{ + auto color = mxComponent->getBackground(); + std::cout << "background color: " << Color(ColorAlpha, color) << std::endl; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/toolkit/qa/cppunit/a11y/XAccessibleComponentTester.hxx b/toolkit/qa/cppunit/a11y/XAccessibleComponentTester.hxx new file mode 100644 index 000000000..ba84fbbac --- /dev/null +++ b/toolkit/qa/cppunit/a11y/XAccessibleComponentTester.hxx @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/accessibility/XAccessibleComponent.hpp> +#include <com/sun/star/accessibility/XAccessibleContext.hpp> + +#include "AccessibilityTools.hxx" + +class XAccessibleComponentTester +{ +private: + const css::uno::Reference<css::accessibility::XAccessibleComponent> mxComponent; + const css::uno::Reference<css::accessibility::XAccessibleContext> mxContext; + +public: + XAccessibleComponentTester( + const css::uno::Reference<css::accessibility::XAccessibleComponent>& component) + : mxComponent(component) + , mxContext(component, css::uno::UNO_QUERY_THROW) + { + } + + void testBounds(); + void testSize(); + void testContainsPoint(); + void testAccessibleAtPoint(); + void testLocation(); + void testLocationOnScreen(); + void testGrabFocus(); + void testGetForeground(); + void testGetBackground(); + + void testAll() + { + testBounds(); + testSize(); + testContainsPoint(); + testAccessibleAtPoint(); + testLocation(); + testLocationOnScreen(); + testGrabFocus(); + testGetForeground(); + testGetBackground(); + } +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/toolkit/qa/cppunit/a11y/XAccessibleContextTester.cxx b/toolkit/qa/cppunit/a11y/XAccessibleContextTester.cxx new file mode 100644 index 000000000..b8c7898f9 --- /dev/null +++ b/toolkit/qa/cppunit/a11y/XAccessibleContextTester.cxx @@ -0,0 +1,164 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "XAccessibleContextTester.hxx" + +#include <cppunit/TestAssert.h> + +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/accessibility/XAccessibleContext.hpp> + +#include "AccessibilityTools.hxx" + +/** + * @brief Tries to get every child and checks its parent. + * + * Checks that the parent of every child and the tested component are the same object. + */ +void XAccessibleContextTester::testGetAccessibleChild() +{ + int count = mxContext->getAccessibleChildCount(); + for (int i = 0; i < count && i < AccessibilityTools::MAX_CHILDREN; i++) + { + auto child = mxContext->getAccessibleChild(i); + auto childCtx = child->getAccessibleContext(); + + std::cout << " Child " << i << ": " << AccessibilityTools::debugString(childCtx) + << std::endl; + + CPPUNIT_ASSERT_EQUAL_MESSAGE("child's parent context is not parent's context!", + childCtx->getAccessibleParent()->getAccessibleContext(), + mxContext); + } +} + +/** + * @brief Calls the method. + * + * Checks that the child count is non-negative. + */ +void XAccessibleContextTester::testGetAccessibleChildCount() +{ + auto childCount = mxContext->getAccessibleChildCount(); + std::cout << childCount << " children found." << std::endl; + CPPUNIT_ASSERT_GREATEREQUAL(static_cast<sal_Int32>(0), childCount); +} + +/** + * @brief Get the accessible description of the component. + */ +void XAccessibleContextTester::testGetAccessibleDescription() +{ + auto desc = mxContext->getAccessibleDescription(); + std::cout << "The description is '" << desc << "'" << std::endl; +} + +/** + * @brief Checks the index in parent + * + * Checks that the parent's child and the tested component are the same objects. + * + * Retrieves the index of tested component in its parent. + * Then gets the parent's child by this index and compares + * it with tested component. + */ +void XAccessibleContextTester::testGetAccessibleIndexInParent() +{ + int idx = mxContext->getAccessibleIndexInParent(); + std::cout << "The index in parent is " << idx << std::endl; + + auto parent = mxContext->getAccessibleParent(); + CPPUNIT_ASSERT(parent.is()); + auto parentCtx = parent->getAccessibleContext(); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Parent's child context at our index is not us!", mxContext, + parentCtx->getAccessibleChild(idx)->getAccessibleContext()); +} + +/** + * @brief Get the accessible name of the component. + */ +void XAccessibleContextTester::testGetAccessibleName() +{ + auto name = mxContext->getAccessibleName(); + std::cout << "The name is '" << name << "'" << std::endl; +} + +/** + * @brief Just gets the parent. + * + * Checks that the parent is not null. + */ +void XAccessibleContextTester::testGetAccessibleParent() +{ + // assume that the component is not ROOT + auto parent = mxContext->getAccessibleParent(); + std::cout << "The parent is " << AccessibilityTools::debugString(parent) << std::endl; + CPPUNIT_ASSERT_MESSAGE("parent is not set", parent.is()); +} + +/** + * @brief Just gets the relation set. + * + * Checks that the relation set is not null. + */ +void XAccessibleContextTester::testGetAccessibleRelationSet() +{ + auto relSet = mxContext->getAccessibleRelationSet(); + CPPUNIT_ASSERT_MESSAGE("relation set is not set", relSet.is()); +} + +/** + * @brief Get the accessible role of component. + * + * Checks that the role is a non-negative number. + */ +void XAccessibleContextTester::testGetAccessibleRole() +{ + sal_Int32 role = mxContext->getAccessibleRole(); + std::cout << "The role is " << role << " (" << AccessibilityTools::getRoleName(role) << ")" + << std::endl; + CPPUNIT_ASSERT_GREATEREQUAL(static_cast<sal_Int32>(0), role); +} + +/** + * @brief Just gets the state set. + * + * Checks that the state set is not null. + */ +void XAccessibleContextTester::testGetAccessibleStateSet() +{ + auto stateSet = mxContext->getAccessibleStateSet(); + std::cout << "The state set is: " << AccessibilityTools::debugString(stateSet) << std::endl; + CPPUNIT_ASSERT_MESSAGE("state set is not set", stateSet.is()); +} + +/** + * @brief Gets the locale. + * + * Checks that @c Country and @c Language fields of locale structure are not empty. + */ +void XAccessibleContextTester::testGetLocale() +{ + auto loc = mxContext->getLocale(); + std::cout << "The locale is " << loc.Language << "," << loc.Country << std::endl; + CPPUNIT_ASSERT_GREATER(static_cast<sal_Int32>(0), loc.Language.getLength()); + CPPUNIT_ASSERT_GREATER(static_cast<sal_Int32>(0), loc.Country.getLength()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/toolkit/qa/cppunit/a11y/XAccessibleContextTester.hxx b/toolkit/qa/cppunit/a11y/XAccessibleContextTester.hxx new file mode 100644 index 000000000..b399028cf --- /dev/null +++ b/toolkit/qa/cppunit/a11y/XAccessibleContextTester.hxx @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/accessibility/XAccessibleContext.hpp> + +class XAccessibleContextTester +{ +protected: + const css::uno::Reference<css::accessibility::XAccessibleContext> mxContext; + +public: + XAccessibleContextTester(const css::uno::Reference<css::accessibility::XAccessibleContext>& ctx) + : mxContext(ctx) + { + } + + void testGetAccessibleChild(); + void testGetAccessibleChildCount(); + void testGetAccessibleDescription(); + void testGetAccessibleIndexInParent(); + void testGetAccessibleName(); + void testGetAccessibleParent(); + void testGetAccessibleRelationSet(); + void testGetAccessibleRole(); + void testGetAccessibleStateSet(); + void testGetLocale(); + + void testAll() + { + testGetAccessibleChild(); + testGetAccessibleChildCount(); + testGetAccessibleDescription(); + testGetAccessibleIndexInParent(); + testGetAccessibleName(); + testGetAccessibleParent(); + testGetAccessibleRelationSet(); + testGetAccessibleRole(); + testGetAccessibleStateSet(); + testGetLocale(); + } +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/toolkit/qa/cppunit/a11y/XAccessibleEventBroadcasterTester.cxx b/toolkit/qa/cppunit/a11y/XAccessibleEventBroadcasterTester.cxx new file mode 100644 index 000000000..80aa2c079 --- /dev/null +++ b/toolkit/qa/cppunit/a11y/XAccessibleEventBroadcasterTester.cxx @@ -0,0 +1,186 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "XAccessibleEventBroadcasterTester.hxx" + +#include <iostream> + +#include <cppunit/TestAssert.h> + +#include <com/sun/star/accessibility/AccessibleEventObject.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/XAccessible.hpp> +#include <com/sun/star/accessibility/XAccessibleContext.hpp> +#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp> +#include <com/sun/star/accessibility/XAccessibleEventListener.hpp> +#include <com/sun/star/awt/PosSize.hpp> +#include <com/sun/star/awt/Rectangle.hpp> +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/lang/EventObject.hpp> + +#include <cppuhelper/implbase.hxx> +#include <rtl/ref.hxx> +#include <sal/log.hxx> + +#include "AccessibilityTools.hxx" + +using namespace css; + +namespace +{ +class EvListener : public cppu::WeakImplHelper<accessibility::XAccessibleEventListener> +{ +public: + bool mbGotEvent; + + EvListener() + : mbGotEvent(false) + { + } + + // XEventListener + virtual void SAL_CALL disposing(const lang::EventObject&) override {} + + // XAccessibleEventListener + virtual void SAL_CALL notifyEvent(const accessibility::AccessibleEventObject& aEvent) override + { + std::cout << "Listener got event: " << AccessibilityTools::debugString(aEvent) << std::endl; + uno::Reference<accessibility::XAccessible> xOld(aEvent.OldValue, uno::UNO_QUERY); + if (xOld.is()) + std::cout << "Old: " << AccessibilityTools::debugString(xOld) << std::endl; + + uno::Reference<accessibility::XAccessible> xNew(aEvent.NewValue, uno::UNO_QUERY); + if (xNew.is()) + std::cout << "New: " << AccessibilityTools::debugString(xNew) << std::endl; + + mbGotEvent = true; + } +}; +} + +XAccessibleEventBroadcasterTester::XAccessibleEventBroadcasterTester( + const uno::Reference<accessibility::XAccessibleEventBroadcaster>& xBroadcaster, + const uno::Reference<awt::XWindow>& xWindow) + : mxBroadcaster(xBroadcaster) + , mxWindow(xWindow) +{ +} + +bool XAccessibleEventBroadcasterTester::isTransient( + const uno::Reference<accessibility::XAccessibleEventBroadcaster> xBroadcaster) +{ + uno::Reference<accessibility::XAccessibleContext> xCtx(xBroadcaster, uno::UNO_QUERY_THROW); + return isTransient(xCtx); +} + +bool XAccessibleEventBroadcasterTester::isTransient( + const uno::Reference<accessibility::XAccessibleContext> xCtx) +{ + return ( + xCtx->getAccessibleStateSet()->contains(accessibility::AccessibleStateType::TRANSIENT) + && xCtx->getAccessibleParent()->getAccessibleContext()->getAccessibleStateSet()->contains( + accessibility::AccessibleStateType::MANAGES_DESCENDANTS)); +} + +/** + * @brief Generates an event on @c mxBroadcaster. + * + * This method indirectly alters the state of @c mxBroadcaster so that an + * accessible event will be fired for it. It can be called as many times as + * needed and triggers an event each time. + */ +void XAccessibleEventBroadcasterTester::fireEvent() +{ + awt::Rectangle newPosSize = mxWindow->getPosSize(); + newPosSize.Width = newPosSize.Width - 20; + newPosSize.Height = newPosSize.Height - 20; + newPosSize.X = newPosSize.X + 20; + newPosSize.Y = newPosSize.Y + 20; + mxWindow->setPosSize(newPosSize.X, newPosSize.Y, newPosSize.Width, newPosSize.Height, + awt::PosSize::POSSIZE); +} + +/** + * @brief Adds a listener and fires events by mean of object relation. + * + * Asserts that the listener was properly called. + */ +void XAccessibleEventBroadcasterTester::testAddEventListener() +{ + rtl::Reference<EvListener> xListener(new EvListener); + mxBroadcaster->addAccessibleEventListener(xListener); + bool transient = isTransient(mxBroadcaster); + + std::cout << "firing event" << std::endl; + fireEvent(); + + AccessibilityTools::Await([&xListener]() { return xListener->mbGotEvent; }); + + if (!transient) + CPPUNIT_ASSERT_MESSAGE("listener wasn't called", xListener->mbGotEvent); + else + CPPUNIT_ASSERT_MESSAGE("Object is Transient, listener isn't expected to be called", + !xListener->mbGotEvent); + + mxBroadcaster->removeAccessibleEventListener(xListener); +} + +/** + * @brief Similar to @c testAddEventListener() but also removes the listener + * + * Adds an event listener just like @c testAddEventListener(), and then removes it and verifies an + * event doesn't trigger the supposedly removed listener. + * + * @see testAddEventListener() + */ +void XAccessibleEventBroadcasterTester::testRemoveEventListener() +{ + /* there is nothing we can really test for transient objects */ + if (isTransient(mxBroadcaster)) + { + std::cerr << "could not test removing listener on transient object " << mxBroadcaster + << std::endl; + return; + } + + rtl::Reference<EvListener> xListener(new EvListener); + mxBroadcaster->addAccessibleEventListener(xListener); + + std::cout << "firing event (with listener)" << std::endl; + fireEvent(); + + AccessibilityTools::Await([&xListener]() { return xListener->mbGotEvent; }); + + CPPUNIT_ASSERT_MESSAGE("listener wasn't called", xListener->mbGotEvent); + + /* reset listener, remove it and try again */ + xListener->mbGotEvent = false; + + std::cout << "removing listener" << std::endl; + mxBroadcaster->removeAccessibleEventListener(xListener); + + std::cout << "firing event (without listener)" << std::endl; + fireEvent(); + + AccessibilityTools::Wait(500); + + CPPUNIT_ASSERT_MESSAGE("removed listener was called", !xListener->mbGotEvent); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/toolkit/qa/cppunit/a11y/XAccessibleEventBroadcasterTester.hxx b/toolkit/qa/cppunit/a11y/XAccessibleEventBroadcasterTester.hxx new file mode 100644 index 000000000..0fc7c23bd --- /dev/null +++ b/toolkit/qa/cppunit/a11y/XAccessibleEventBroadcasterTester.hxx @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/accessibility/XAccessibleContext.hpp> +#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp> +#include <com/sun/star/awt/XWindow.hpp> + +class XAccessibleEventBroadcasterTester +{ +private: + const css::uno::Reference<css::accessibility::XAccessibleEventBroadcaster> mxBroadcaster; + const css::uno::Reference<css::awt::XWindow> mxWindow; + + static bool isTransient( + const css::uno::Reference<css::accessibility::XAccessibleEventBroadcaster> xBroadcaster); + static bool isTransient(const css::uno::Reference<css::accessibility::XAccessibleContext> xCtx); + + void fireEvent(); + +public: + XAccessibleEventBroadcasterTester( + const css::uno::Reference<css::accessibility::XAccessibleEventBroadcaster>& xBroadcaster, + const css::uno::Reference<css::awt::XWindow>& xWindow); + + void testAddEventListener(); + void testRemoveEventListener(); + + void testAll() + { + testAddEventListener(); + testRemoveEventListener(); + } +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/toolkit/qa/cppunit/a11y/XAccessibleExtendedComponentTester.cxx b/toolkit/qa/cppunit/a11y/XAccessibleExtendedComponentTester.cxx new file mode 100644 index 000000000..a7137c4ba --- /dev/null +++ b/toolkit/qa/cppunit/a11y/XAccessibleExtendedComponentTester.cxx @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "XAccessibleExtendedComponentTester.hxx" + +#include <iostream> + +#include <com/sun/star/accessibility/XAccessibleExtendedComponent.hpp> + +/** + * @brief Just calls the method. + */ +void XAccessibleExtendedComponentTester::testGetFont() +{ + auto font = mxExtendedComponent->getFont(); + std::cout << "font: " << font << std::endl; +} + +/** + * @brief Just calls the method. + */ +void XAccessibleExtendedComponentTester::testGetTitledBorderText() +{ + auto titleBorderText = mxExtendedComponent->getTitledBorderText(); + std::cout << "getTitledBorderText(): '" << titleBorderText << "'" << std::endl; +} + +/** + * @brief Just calls the method. + */ +void XAccessibleExtendedComponentTester::testGetToolTipText() +{ + auto toolTipText = mxExtendedComponent->getToolTipText(); + std::cout << "getToolTipText(): '" << toolTipText << "'" << std::endl; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/toolkit/qa/cppunit/a11y/XAccessibleExtendedComponentTester.hxx b/toolkit/qa/cppunit/a11y/XAccessibleExtendedComponentTester.hxx new file mode 100644 index 000000000..129705282 --- /dev/null +++ b/toolkit/qa/cppunit/a11y/XAccessibleExtendedComponentTester.hxx @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/accessibility/XAccessibleExtendedComponent.hpp> + +class XAccessibleExtendedComponentTester +{ +protected: + const css::uno::Reference<css::accessibility::XAccessibleExtendedComponent> mxExtendedComponent; + +public: + XAccessibleExtendedComponentTester( + const css::uno::Reference<css::accessibility::XAccessibleExtendedComponent>& + extendedComponent) + : mxExtendedComponent(extendedComponent) + { + } + + void testGetFont(); + void testGetTitledBorderText(); + void testGetToolTipText(); + + void testAll() + { + testGetFont(); + testGetTitledBorderText(); + testGetToolTipText(); + } +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ |