diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
commit | 940b4d1848e8c70ab7642901a68594e8016caffc (patch) | |
tree | eb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /vcl/source/uitest | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | vcl/source/uitest/logger.cxx | 553 | ||||
-rw-r--r-- | vcl/source/uitest/uiobject.cxx | 1506 | ||||
-rw-r--r-- | vcl/source/uitest/uitest.cxx | 80 | ||||
-rw-r--r-- | vcl/source/uitest/uno/uiobject_uno.cxx | 210 | ||||
-rw-r--r-- | vcl/source/uitest/uno/uiobject_uno.hxx | 73 | ||||
-rw-r--r-- | vcl/source/uitest/uno/uitest_uno.cxx | 120 |
6 files changed, 2542 insertions, 0 deletions
diff --git a/vcl/source/uitest/logger.cxx b/vcl/source/uitest/logger.cxx new file mode 100644 index 000000000..13cbf5a7a --- /dev/null +++ b/vcl/source/uitest/logger.cxx @@ -0,0 +1,553 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + */ + +#include <config_folders.h> + +#include <vcl/uitest/logger.hxx> + +#include <rtl/bootstrap.hxx> +#include <rtl/ustrbuf.hxx> +#include <osl/file.hxx> +#include <vcl/ctrl.hxx> +#include <vcl/event.hxx> +#include <vcl/uitest/uiobject.hxx> +#include <vcl/uitest/eventdescription.hxx> +#include <svdata.hxx> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <memory> + +namespace +{ +bool isDialogWindow(vcl::Window const* pWindow) +{ + WindowType nType = pWindow->GetType(); + // DIALOG to MODALDIALOG + if (nType >= WindowType::DIALOG && nType <= WindowType::MODALDIALOG) + return true; + + // MESSBOX, INFOBOX, WARNINGBOX, ERRORBOX, QUERYBOX + if (nType >= WindowType::MESSBOX && nType <= WindowType::QUERYBOX) + return true; + + if (nType == WindowType::TABDIALOG) + return true; + + return false; +} + +bool isTopWindow(vcl::Window const* pWindow) +{ + WindowType eType = pWindow->GetType(); + if (eType == WindowType::FLOATINGWINDOW) + { + return pWindow->GetStyle() & WB_SYSTEMFLOATWIN; + } + return false; +} + +vcl::Window* get_top_parent(vcl::Window* pWindow) +{ + if (isDialogWindow(pWindow) || isTopWindow(pWindow)) + return pWindow; + + vcl::Window* pParent = pWindow->GetParent(); + if (!pParent) + return pWindow; + + return get_top_parent(pParent); +} +} +UITestLogger::UITestLogger() + : maStream() + , mbValid(false) +{ + static const char* pFile = std::getenv("LO_COLLECT_UIINFO"); + if (pFile) + { + OUString aDirPath("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER + "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/uitest/"); + rtl::Bootstrap::expandMacros(aDirPath); + osl::Directory::createPath(aDirPath); + OUString aFilePath = aDirPath + OUString::fromUtf8(pFile); + + maStream.Open(aFilePath, StreamMode::READWRITE | StreamMode::TRUNC); + mbValid = true; + } +} + +void UITestLogger::logCommand(const OUString& rAction, + const css::uno::Sequence<css::beans::PropertyValue>& rArgs) +{ + if (!mbValid) + return; + + OUStringBuffer aBuffer(rAction); + + if (rArgs.hasElements()) + { + aBuffer.append(" {"); + for (const css::beans::PropertyValue& rProp : rArgs) + { + OUString aTypeName = rProp.Value.getValueTypeName(); + + if (aTypeName == "long" || aTypeName == "short") + { + sal_Int32 nValue = 0; + rProp.Value >>= nValue; + aBuffer.append("\"").append(rProp.Name).append("\": "); + aBuffer.append(OUString::number(nValue)).append(", "); + } + else if (aTypeName == "unsigned long") + { + sal_uInt32 nValue = 0; + rProp.Value >>= nValue; + aBuffer.append("\"").append(rProp.Name).append("\": "); + aBuffer.append(OUString::number(nValue)).append(", "); + } + else if (aTypeName == "boolean") + { + bool bValue = false; + rProp.Value >>= bValue; + aBuffer.append("\"").append(rProp.Name).append("\": "); + if (bValue) + aBuffer.append("True, "); + else + aBuffer.append("False, "); + } + } + aBuffer.append("}"); + } + + OUString aCommand(aBuffer.makeStringAndClear()); + maStream.WriteLine(OUStringToOString(aCommand, RTL_TEXTENCODING_UTF8)); +} + +namespace +{ +// most likely this should be recursive +bool child_windows_have_focus(VclPtr<vcl::Window> const& xUIElement) +{ + sal_Int32 nCount = xUIElement->GetChildCount(); + for (sal_Int32 i = 0; i < nCount; ++i) + { + vcl::Window* pChild = xUIElement->GetChild(i); + if (pChild->HasFocus()) + { + return true; + } + if (child_windows_have_focus(VclPtr<vcl::Window>(pChild))) + return true; + } + return false; +} +} + +void UITestLogger::logAction(VclPtr<Control> const& xUIElement, VclEventId nEvent) +{ + if (!mbValid) + return; + + if (xUIElement->get_id().isEmpty()) + return; + + std::unique_ptr<UIObject> pUIObject = xUIElement->GetUITestFactory()(xUIElement.get()); + OUString aAction = pUIObject->get_action(nEvent); + if (!xUIElement->HasFocus() && !child_windows_have_focus(xUIElement)) + { + return; + } + + if (!aAction.isEmpty()) + maStream.WriteLine(OUStringToOString(aAction, RTL_TEXTENCODING_UTF8)); +} + +void UITestLogger::log(const OUString& rString) +{ + if (!mbValid) + return; + + if (rString.isEmpty()) + return; + + maStream.WriteLine(OUStringToOString(rString, RTL_TEXTENCODING_UTF8)); +} + +void UITestLogger::logKeyInput(VclPtr<vcl::Window> const& xUIElement, const KeyEvent& rEvent) +{ + if (!mbValid) + return; + + //We need to check for Parent's ID in case the UI Element is SubEdit of Combobox/SpinField + const OUString& rID + = xUIElement->get_id().isEmpty() ? xUIElement->GetParent()->get_id() : xUIElement->get_id(); + if (rID.isEmpty()) + return; + + sal_Unicode nChar = rEvent.GetCharCode(); + sal_uInt16 nKeyCode = rEvent.GetKeyCode().GetCode(); + bool bShift = rEvent.GetKeyCode().IsShift(); + bool bMod1 = rEvent.GetKeyCode().IsMod1(); + bool bMod2 = rEvent.GetKeyCode().IsMod2(); + bool bMod3 = rEvent.GetKeyCode().IsMod3(); + + std::map<OUString, sal_uInt16> aKeyMap + = { { "ESC", KEY_ESCAPE }, { "TAB", KEY_TAB }, { "DOWN", KEY_DOWN }, + { "UP", KEY_UP }, { "LEFT", KEY_LEFT }, { "RIGHT", KEY_RIGHT }, + { "DELETE", KEY_DELETE }, { "INSERT", KEY_INSERT }, { "BACKSPACE", KEY_BACKSPACE }, + { "RETURN", KEY_RETURN }, { "HOME", KEY_HOME }, { "END", KEY_END }, + { "PAGEUP", KEY_PAGEUP }, { "PAGEDOWN", KEY_PAGEDOWN } }; + + OUString aFound; + for (const auto& itr : aKeyMap) + { + if (itr.second == nKeyCode) + { + aFound = itr.first; + break; + } + } + + OUString aKeyCode; + if (!aFound.isEmpty() || bShift || bMod1 || bMod2 || bMod3) + { + aKeyCode = "{\"KEYCODE\": \""; + if (bShift) + aKeyCode += "SHIFT+"; + + if (bMod1) + aKeyCode += "CTRL+"; + + if (bMod2) + aKeyCode += "ALT+"; + + if (aFound.isEmpty()) + aKeyCode += OUStringChar(nChar) + "\"}"; + else + aKeyCode += aFound + "\"}"; + } + else + { + aKeyCode = "{\"TEXT\": \"" + OUStringChar(nChar) + "\"}"; + } + + std::unique_ptr<UIObject> pUIObject = xUIElement->GetUITestFactory()(xUIElement.get()); + + VclPtr<vcl::Window> pParent = xUIElement->GetParent(); + + while (!pParent->IsTopWindow()) + { + pParent = pParent->GetParent(); + } + + OUString aParentID = pParent->get_id(); + + OUString aContent; + + if (pUIObject->get_type() == "EditUIObject") + { + if (aParentID.isEmpty()) + { + VclPtr<vcl::Window> pParent_top = get_top_parent(xUIElement); + aParentID = pParent_top->get_id(); + } + if (aParentID.isEmpty()) + { + aContent += "Type on '" + rID + "' " + aKeyCode; + } + else + { + aContent += "Type on '" + rID + "' " + aKeyCode + " from " + aParentID; + } + } + else if (pUIObject->get_type() == "SwEditWinUIObject" && rID == "writer_edit") + { + aContent = "Type on writer " + aKeyCode; + } + else if (pUIObject->get_type() == "ScGridWinUIObject" && rID == "grid_window") + { + aContent = "Type on current cell " + aKeyCode; + } + else if (pUIObject->get_type() == "ImpressWindowUIObject" && rID == "impress_win") + { + aContent = "Type on impress " + aKeyCode; + } + else if (pUIObject->get_type() == "WindowUIObject" && rID == "math_edit") + { + aContent = "Type on math " + aKeyCode; + } + else if (rID == "draw_win") + { + aContent = "Type on draw " + aKeyCode; + } + else + { + if (aParentID.isEmpty()) + { + VclPtr<vcl::Window> pParent_top = get_top_parent(xUIElement); + aParentID = pParent_top->get_id(); + } + if (aParentID.isEmpty()) + { + aContent = "Type on '" + rID + "' " + aKeyCode; + } + else + { + aContent = "Type on '" + rID + "' " + aKeyCode + " from " + aParentID; + } + } + maStream.WriteLine(OUStringToOString(aContent, RTL_TEXTENCODING_UTF8)); +} + +namespace +{ +OUString StringMapToOUString(const std::map<OUString, OUString>& rParameters) +{ + if (rParameters.empty()) + return ""; + + OUStringBuffer aParameterString(static_cast<int>(rParameters.size()*32)); + aParameterString.append(" {"); + + for (std::map<OUString, OUString>::const_iterator itr = rParameters.begin(); + itr != rParameters.end(); ++itr) + { + if (itr != rParameters.begin()) + aParameterString.append(", "); + aParameterString.append("\"") + .append(itr->first) + .append("\": \"") + .append(itr->second) + .append("\""); + } + + aParameterString.append("}"); + + return aParameterString.makeStringAndClear(); +} + +OUString GetValueInMapWithIndex(const std::map<OUString, OUString>& rParameters, sal_Int32 index) +{ + sal_Int32 j = 0; + + std::map<OUString, OUString>::const_iterator itr = rParameters.begin(); + + for (; itr != rParameters.end() && j < index; ++itr, ++j) + ; + + assert(itr != rParameters.end()); + + return itr->second; +} + +OUString GetKeyInMapWithIndex(const std::map<OUString, OUString>& rParameters, sal_Int32 index) +{ + sal_Int32 j = 0; + + std::map<OUString, OUString>::const_iterator itr = rParameters.begin(); + + for (; itr != rParameters.end() && j < index; ++itr, ++j) + ; + + assert(itr != rParameters.end()); + + return itr->first; +} +} + +void UITestLogger::logEvent(const EventDescription& rDescription) +{ + OUString aParameterString = StringMapToOUString(rDescription.aParameters); + + //here we will customize our statements depending on the caller of this function + OUString aLogLine; + //first check on general commands + if (rDescription.aAction == "SET") + { + aLogLine = "Set Zoom to " + GetValueInMapWithIndex(rDescription.aParameters, 0); + } + else if (rDescription.aAction == "SIDEBAR") + { + aLogLine = "From SIDEBAR Choose " + aParameterString; + } + else if (rDescription.aAction == "SELECT" && rDescription.aID.isEmpty()) + { + aLogLine = "Select " + aParameterString; + } + else if (rDescription.aID == "writer_edit") + { + if (rDescription.aAction == "GOTO") + { + aLogLine = "GOTO page number " + GetValueInMapWithIndex(rDescription.aParameters, 0); + } + else if (rDescription.aAction == "SELECT") + { + OUString to = GetValueInMapWithIndex(rDescription.aParameters, 0); + OUString from = GetValueInMapWithIndex(rDescription.aParameters, 1); + aLogLine = "Select from Pos " + from + " to Pos " + to; + } + else if (rDescription.aAction == "CREATE_TABLE") + { + OUString size = GetValueInMapWithIndex(rDescription.aParameters, 0); + aLogLine = "Create Table with " + size; + ; + } + else if (rDescription.aAction == "COPY") + { + aLogLine = "Copy the Selected Text"; + } + else if (rDescription.aAction == "CUT") + { + aLogLine = "Cut the Selected Text"; + } + else if (rDescription.aAction == "PASTE") + { + aLogLine = "Paste in the Current Cursor Location"; + } + else if (rDescription.aAction == "BREAK_PAGE") + { + aLogLine = "Insert Break Page"; + } + } + else if (rDescription.aID == "grid_window") + { + if (rDescription.aAction == "SELECT") + { + OUString type = GetKeyInMapWithIndex(rDescription.aParameters, 0); + if (type == "CELL" || type == "RANGE") + { + aLogLine = "Select from calc" + aParameterString; + } + else if (type == "TABLE") + { + aLogLine = "Switch to sheet number " + + GetValueInMapWithIndex(rDescription.aParameters, 0); + } + } + else if (rDescription.aAction == "LAUNCH") + { + aLogLine = "Launch AutoFilter from Col " + + GetValueInMapWithIndex(rDescription.aParameters, 2) + " and Row " + + GetValueInMapWithIndex(rDescription.aParameters, 1); + } + else if (rDescription.aAction == "DELETE_CONTENT") + { + aLogLine = "Remove Content from This " + aParameterString; + } + else if (rDescription.aAction == "DELETE_CELLS") + { + aLogLine = "Delete The Cells in" + aParameterString; + } + else if (rDescription.aAction == "INSERT_CELLS") + { + aLogLine = "Insert Cell around the " + aParameterString; + } + else if (rDescription.aAction == "CUT") + { + aLogLine = "CUT the selected " + aParameterString; + } + else if (rDescription.aAction == "COPY") + { + aLogLine = "COPY the selected " + aParameterString; + } + else if (rDescription.aAction == "PASTE") + { + aLogLine = "Paste in the " + aParameterString; + } + else if (rDescription.aAction == "MERGE_CELLS") + { + aLogLine = "Merge " + aParameterString; + } + else if (rDescription.aAction == "UNMERGE_CELL") + { + aLogLine = "Delete the merged " + aParameterString; + } + else if (rDescription.aAction == "Rename_Sheet") + { + aLogLine = "Rename The Selected Tab to \"" + + GetValueInMapWithIndex(rDescription.aParameters, 0) + "\""; + } + else if (rDescription.aAction == "InsertTab") + { + aLogLine = "Insert New Tab "; + } + } + else if (rDescription.aID == "impress_win_or_draw_win") + { + if (rDescription.aAction == "Insert_New_Page_or_Slide") + { + if (UITestLogger::getInstance().getAppName() == "impress") + { + aLogLine = "Insert New Slide at Position " + + GetValueInMapWithIndex(rDescription.aParameters, 0); + } + else if (UITestLogger::getInstance().getAppName() == "draw") + { + aLogLine = "Insert New Page at Position " + + GetValueInMapWithIndex(rDescription.aParameters, 0); + } + } + else if (rDescription.aAction == "Delete_Slide_or_Page") + { + if (UITestLogger::getInstance().getAppName() == "impress") + { + aLogLine + = "Delete Slide number " + GetValueInMapWithIndex(rDescription.aParameters, 0); + } + else if (UITestLogger::getInstance().getAppName() == "draw") + { + aLogLine + = "Delete Page number " + GetValueInMapWithIndex(rDescription.aParameters, 0); + } + } + else if (rDescription.aAction == "Duplicate") + { + aLogLine = "Duplicate The Selected Slide "; + } + else if (rDescription.aAction == "RENAME") + { + if (UITestLogger::getInstance().getAppName() == "impress") + { + aLogLine = "Rename The Selected Slide from \"" + + GetValueInMapWithIndex(rDescription.aParameters, 1) + "\" to \"" + + GetValueInMapWithIndex(rDescription.aParameters, 0) + "\""; + } + else if (UITestLogger::getInstance().getAppName() == "draw") + { + aLogLine = "Rename The Selected Page from \"" + + GetValueInMapWithIndex(rDescription.aParameters, 1) + "\" to \"" + + GetValueInMapWithIndex(rDescription.aParameters, 0) + "\""; + } + } + } + else if (rDescription.aParent == "element_selector") + { + aLogLine = "Select element no " + rDescription.aID + " From " + rDescription.aParent; + } + else + { + aLogLine = rDescription.aKeyWord + " Action:" + rDescription.aAction + " Id:" + + rDescription.aID + " Parent:" + rDescription.aParent + aParameterString; + } + log(aLogLine); +} + +UITestLogger& UITestLogger::getInstance() +{ + ImplSVData* const pSVData = ImplGetSVData(); + assert(pSVData); + + if (!pSVData->maFrameData.m_pUITestLogger) + { + pSVData->maFrameData.m_pUITestLogger.reset(new UITestLogger); + } + + return *pSVData->maFrameData.m_pUITestLogger; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/uitest/uiobject.cxx b/vcl/source/uitest/uiobject.cxx new file mode 100644 index 000000000..5fb0a5c01 --- /dev/null +++ b/vcl/source/uitest/uiobject.cxx @@ -0,0 +1,1506 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + */ + +#include <vcl/uitest/uiobject.hxx> +#include <vcl/uitest/metricfielduiobject.hxx> +#include <vcl/uitest/formattedfielduiobject.hxx> + +#include <vcl/svapp.hxx> +#include <vcl/toolkit/combobox.hxx> +#include <vcl/event.hxx> +#include <vcl/floatwin.hxx> +#include <vcl/tabpage.hxx> +#include <vcl/tabctrl.hxx> +#include <vcl/lstbox.hxx> +#include <vcl/toolkit/spin.hxx> +#include <vcl/fmtfield.hxx> +#include <vcl/spinfld.hxx> +#include <vcl/toolkit/button.hxx> +#include <vcl/toolkit/dialog.hxx> +#include <vcl/toolkit/field.hxx> +#include <vcl/edit.hxx> +#include <vcl/vclmedit.hxx> +#include <vcl/uitest/logger.hxx> +#include <uiobject-internal.hxx> + +#include <comphelper/string.hxx> +#include <comphelper/lok.hxx> + +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> + +#include <iostream> +#include <memory> +#include <vector> + +UIObject::~UIObject() +{ +} + +StringMap UIObject::get_state() +{ + StringMap aMap; + aMap["NotImplemented"] = "NotImplemented"; + return aMap; +} + +void UIObject::execute(const OUString& /*rAction*/, + const StringMap& /*rParameters*/) +{ + // should never be called + throw std::exception(); +} + +OUString UIObject::get_type() const +{ + return "Generic UIObject"; +} + +std::unique_ptr<UIObject> UIObject::get_child(const OUString&) +{ + return std::unique_ptr<UIObject>(); +} + +std::set<OUString> UIObject::get_children() const +{ + return std::set<OUString>(); +} + +OUString UIObject::dumpState() const +{ + return OUString(); +} + +OUString UIObject::dumpHierarchy() const +{ + return OUString(); +} + +OUString UIObject::get_action(VclEventId /*nEvent*/) const +{ + return OUString(); +} + +namespace { + +bool isDialogWindow(vcl::Window const * pWindow) +{ + WindowType nType = pWindow->GetType(); + // DIALOG to MODALDIALOG + if (nType >= WindowType::DIALOG && nType <= WindowType::MODALDIALOG) + return true; + + // MESSBOX, INFOBOX, WARNINGBOX, ERRORBOX, QUERYBOX + if (nType >= WindowType::MESSBOX && nType <= WindowType::QUERYBOX) + return true; + + if (nType == WindowType::TABDIALOG) + return true; + + return false; +} + +bool isTopWindow(vcl::Window const * pWindow) +{ + WindowType eType = pWindow->GetType(); + if (eType == WindowType::FLOATINGWINDOW) + { + return pWindow->GetStyle() & WB_SYSTEMFLOATWIN; + } + return false; +} + +vcl::Window* get_top_parent(vcl::Window* pWindow) +{ + if (isDialogWindow(pWindow) || isTopWindow(pWindow)) + return pWindow; + + vcl::Window* pParent = pWindow->GetParent(); + if (!pParent) + return pWindow; + + return get_top_parent(pParent); +} + +std::vector<KeyEvent> generate_key_events_from_text(const OUString& rStr) +{ + std::vector<KeyEvent> aEvents; + vcl::KeyCode aCode; + for (sal_Int32 i = 0, n = rStr.getLength(); + i != n; ++i) + { + aEvents.emplace_back(rStr[i], aCode); + } + return aEvents; +} + +sal_uInt16 get_key(sal_Unicode cChar, bool& bShift) +{ + bShift = false; + if (cChar >= 'a' && cChar <= 'z') + return KEY_A + (cChar - 'a'); + else if (cChar >= 'A' && cChar <= 'Z') + { + bShift = true; + return KEY_A + (cChar - 'A'); + } + else if (cChar >= '0' && cChar <= '9') + return KEY_0 + (cChar - 'A'); + + return cChar; +} + +bool isFunctionKey(const OUString& rStr, sal_uInt16& rKeyCode) +{ + std::map<OUString, sal_uInt16> aFunctionKeyMap = { + {"F1", KEY_F1}, + {"F2", KEY_F2}, + {"F3", KEY_F3}, + {"F4", KEY_F4}, + {"F5", KEY_F5}, + {"F6", KEY_F6}, + {"F7", KEY_F7}, + {"F8", KEY_F8}, + {"F9", KEY_F9}, + {"F10", KEY_F10}, + {"F11", KEY_F11}, + {"F12", KEY_F12} + }; + + rKeyCode = 0; + auto itr = aFunctionKeyMap.find(rStr); + if (itr == aFunctionKeyMap.end()) + return false; + + rKeyCode = itr->second; + return true; +} + +std::vector<KeyEvent> generate_key_events_from_keycode(const OUString& rStr) +{ + std::vector<KeyEvent> aEvents; + + std::map<OUString, sal_uInt16> aKeyMap = { + {"ESC", KEY_ESCAPE}, + {"TAB", KEY_TAB}, + {"DOWN", KEY_DOWN}, + {"UP", KEY_UP}, + {"LEFT", KEY_LEFT}, + {"RIGHT", KEY_RIGHT}, + {"DELETE", KEY_DELETE}, + {"INSERT", KEY_INSERT}, + {"BACKSPACE", KEY_BACKSPACE}, + {"RETURN", KEY_RETURN}, + {"HOME", KEY_HOME}, + {"END", KEY_END}, + {"PAGEUP", KEY_PAGEUP}, + {"PAGEDOWN", KEY_PAGEDOWN} + }; + + // split string along '+' + // then translate to keycodes + bool bShift = false; + bool bMod1 = false; + bool bMod2 = false; + OUString aRemainingText; + + std::vector<OUString> aTokens = comphelper::string::split(rStr, '+'); + for (auto const& token : aTokens) + { + OUString aToken = token.trim(); + if (aToken == "CTRL") + { + bMod1 = true; + } + else if (aToken == "SHIFT") + { + bShift = true; + } + else if (aToken == "ALT") + { + bMod2 = true; + } + else + aRemainingText = aToken; + } + + sal_uInt16 nFunctionKey = 0; + if (isFunctionKey(aRemainingText, nFunctionKey)) + { + vcl::KeyCode aCode(nFunctionKey, bShift, bMod1, bMod2, false); + aEvents.emplace_back(0, aCode); + } + else if (aKeyMap.find(aRemainingText) != aKeyMap.end()) + { + sal_uInt16 nKey = aKeyMap[aRemainingText]; + vcl::KeyCode aCode(nKey, bShift, bMod1, bMod2, false); + aEvents.emplace_back( 'a', aCode); + } + else + { + for (sal_Int32 i = 0; i < aRemainingText.getLength(); ++i) + { + bool bShiftThroughKey = false; + sal_uInt16 nKey = get_key(aRemainingText[i], bShiftThroughKey); + vcl::KeyCode aCode(nKey, bShift || bShiftThroughKey, bMod1, bMod2, false); + aEvents.emplace_back(aRemainingText[i], aCode); + } + } + + return aEvents; +} + +OUString to_string(const Point& rPos) +{ + OUString sStr = OUString::number(rPos.X()) + + "x" + + OUString::number(rPos.Y()); + + return sStr; +} + +OUString to_string(const Size& rSize) +{ + OUString sStr = OUString::number(rSize.Width()) + + "x" + + OUString::number(rSize.Height()); + + return sStr; +} + +} + +WindowUIObject::WindowUIObject(const VclPtr<vcl::Window>& xWindow): + mxWindow(xWindow) +{ +} + +StringMap WindowUIObject::get_state() +{ + // Double-buffering is not interesting for uitesting, but can result in direct paint for a + // double-buffered widget, which is incorrect. + if (mxWindow->SupportsDoubleBuffering()) + mxWindow->RequestDoubleBuffering(false); + + StringMap aMap; + aMap["Visible"] = OUString::boolean(mxWindow->IsVisible()); + aMap["ReallyVisible"] = OUString::boolean(mxWindow->IsReallyVisible()); + aMap["Enabled"] = OUString::boolean(mxWindow->IsEnabled()); + aMap["WindowType"] = OUString::number(static_cast<sal_uInt16>(mxWindow->GetType()), 16); + + Point aPos = mxWindow->GetPosPixel(); + aMap["RelPosition"] = to_string(aPos); + aMap["Size"] = to_string(mxWindow->GetSizePixel()); + aMap["ID"] = mxWindow->get_id(); + vcl::Window* pParent = mxWindow->GetParent(); + if (pParent) + aMap["Parent"] = mxWindow->GetParent()->get_id(); + + bool bIgnoreAllExceptTop = isDialogWindow(mxWindow.get()); + while(pParent) + { + Point aParentPos = pParent->GetPosPixel(); + if (!bIgnoreAllExceptTop) + aPos += aParentPos; + + if (isDialogWindow(pParent)) + { + bIgnoreAllExceptTop = true; + } + + pParent = pParent->GetParent(); + + if (!pParent && bIgnoreAllExceptTop) + aPos += aParentPos; + } + aMap["AbsPosition"] = to_string(aPos); + aMap["Text"] = mxWindow->GetText(); + aMap["DisplayText"] = mxWindow->GetDisplayText(); + + return aMap; +} + +void WindowUIObject::execute(const OUString& rAction, + const StringMap& rParameters) +{ + bool bHandled = true; + if (rAction == "SET") + { + for (auto const& parameter : rParameters) + { + std::cout << parameter.first; + } + } + else if (rAction == "TYPE") + { + auto it = rParameters.find("TEXT"); + if (it != rParameters.end()) + { + const OUString& rText = it->second; + auto aKeyEvents = generate_key_events_from_text(rText); + for (auto const& keyEvent : aKeyEvents) + { + mxWindow->KeyInput(keyEvent); + } + } + else if (rParameters.find("KEYCODE") != rParameters.end()) + { + auto itr = rParameters.find("KEYCODE"); + const OUString rText = itr->second; + auto aKeyEvents = generate_key_events_from_keycode(rText); + for (auto const& keyEvent : aKeyEvents) + { + mxWindow->KeyInput(keyEvent); + } + } + else + { + SAL_WARN("vcl.uitest", "missing parameter TEXT to action TYPE"); + return; + } + } + else if (rAction == "FOCUS") + { + mxWindow->GrabFocus(); + } + else + { + bHandled = false; + } + + if (!bHandled) + { + SAL_WARN("vcl.uitest", "unknown action or parameter for " << get_name() << ". Action: " << rAction); + } +} + +OUString WindowUIObject::get_type() const +{ + return get_name(); +} + +namespace { + +vcl::Window* findChild(vcl::Window* pParent, const OUString& rID) +{ + if (!pParent) + return nullptr; + + if (pParent->get_id() == rID) + return pParent; + + size_t nCount = pParent->GetChildCount(); + for (size_t i = 0; i < nCount; ++i) + { + vcl::Window* pChild = pParent->GetChild(i); + if (pChild && pChild->get_id() == rID) + return pChild; + + vcl::Window* pResult = findChild(pChild, rID); + if (pResult) + return pResult; + } + + return nullptr; +} + +void addChildren(vcl::Window const * pParent, std::set<OUString>& rChildren) +{ + if (!pParent) + return; + + size_t nCount = pParent->GetChildCount(); + for (size_t i = 0; i < nCount; ++i) + { + vcl::Window* pChild = pParent->GetChild(i); + if (pChild) + { + OUString aId = pChild->get_id(); + if (!aId.isEmpty()) + { + auto ret = rChildren.insert(aId); + SAL_WARN_IF(!ret.second, "vcl.uitest", "duplicate ids for ui elements. violates locally unique requirement"); + } + + addChildren(pChild, rChildren); + } + } +} + +} + +std::unique_ptr<UIObject> WindowUIObject::get_child(const OUString& rID) +{ + // in a first step try the real children before moving to the top level parent + // This makes it easier to handle cases with the same ID as there is a way + // to resolve conflicts + vcl::Window* pWindow = findChild(mxWindow.get(), rID); + if (!pWindow) + { + vcl::Window* pDialogParent = get_top_parent(mxWindow.get()); + pWindow = findChild(pDialogParent, rID); + } + + if (!pWindow) + throw css::uno::RuntimeException("Could not find child with id: " + rID); + + FactoryFunction aFunction = pWindow->GetUITestFactory(); + return aFunction(pWindow); +} + +std::set<OUString> WindowUIObject::get_children() const +{ + vcl::Window* pDialogParent = get_top_parent(mxWindow.get()); + std::set<OUString> aChildren; + aChildren.insert(pDialogParent->get_id()); + addChildren(pDialogParent, aChildren); + return aChildren; +} + +OUString WindowUIObject::get_name() const +{ + return "WindowUIObject"; +} + +namespace { + +OUString escape(const OUString& rStr) +{ + return rStr.replaceAll("\"", "\\\""); +} + +} + +OUString WindowUIObject::dumpState() const +{ + OUStringBuffer aStateString = "{\"name\":\"" + mxWindow->get_id() + "\""; + aStateString.append(", \"ImplementationName\":\"").appendAscii(typeid(*mxWindow).name()).append("\""); + StringMap aState = const_cast<WindowUIObject*>(this)->get_state(); + for (auto const& elem : aState) + { + OUString property = ",\"" + elem.first + "\":\"" + escape(elem.second) + "\""; + aStateString.append(property); + } + + size_t nCount = mxWindow->GetChildCount(); + + if (nCount) + aStateString.append(",\"children\":["); + + for (size_t i = 0; i < nCount; ++i) + { + if (i != 0) + { + aStateString.append(","); + } + vcl::Window* pChild = mxWindow->GetChild(i); + std::unique_ptr<UIObject> pChildWrapper = + pChild->GetUITestFactory()(pChild); + OUString children = pChildWrapper->dumpState(); + aStateString.append(children); + } + + if (nCount) + aStateString.append("]"); + + aStateString.append("}"); + + OUString aString = aStateString.makeStringAndClear(); + return aString.replaceAll("\n", "\\n"); +} + +OUString WindowUIObject::dumpHierarchy() const +{ + vcl::Window* pDialogParent = get_top_parent(mxWindow.get()); + std::unique_ptr<UIObject> pParentWrapper = + pDialogParent->GetUITestFactory()(pDialogParent); + return pParentWrapper->dumpState(); +} + +OUString WindowUIObject::get_action(VclEventId nEvent) const +{ + + OUString aActionName; + switch (nEvent) + { + case VclEventId::ControlGetFocus: + case VclEventId::ControlLoseFocus: + return OUString(); + + case VclEventId::ButtonClick: + case VclEventId::CheckboxToggle: + aActionName = "CLICK"; + break; + + case VclEventId::EditModify: + aActionName = "TYPE"; + break; + default: + aActionName = OUString::number(static_cast<int>(nEvent)); + } + return ""; + //return "Action on element: " + mxWindow->get_id() + " with action : " + aActionName; +} + +std::unique_ptr<UIObject> WindowUIObject::create(vcl::Window* pWindow) +{ + return std::unique_ptr<UIObject>(new WindowUIObject(pWindow)); +} + +ButtonUIObject::ButtonUIObject(const VclPtr<Button>& xButton): + WindowUIObject(xButton), + mxButton(xButton) +{ +} + +ButtonUIObject::~ButtonUIObject() +{ +} + +StringMap ButtonUIObject::get_state() +{ + StringMap aMap = WindowUIObject::get_state(); + // Move that to a Control base class + aMap["Label"] = mxButton->GetDisplayText(); + + return aMap; +} + +void ButtonUIObject::execute(const OUString& rAction, + const StringMap& rParameters) +{ + if (rAction == "CLICK") + { + //Click doesn't call toggle when it's a pushbutton tweaked to be a toggle-button + if (PushButton *pPushButton = (mxButton->GetStyle() & WB_TOGGLE) ? dynamic_cast<PushButton*>(mxButton.get()) : nullptr) + { + pPushButton->Check(!pPushButton->IsChecked()); + pPushButton->Toggle(); + return; + } + mxButton->Click(); + } + else + WindowUIObject::execute(rAction, rParameters); +} + +OUString ButtonUIObject::get_name() const +{ + return "ButtonUIObject"; +} + +OUString ButtonUIObject::get_action(VclEventId nEvent) const +{ + if (nEvent == VclEventId::ButtonClick) + { + if(mxButton->get_id()=="writer_all") + { + UITestLogger::getInstance().setAppName("writer"); + return "Start writer" ; + } + else if(mxButton->get_id()=="calc_all") + { + UITestLogger::getInstance().setAppName("calc"); + return "Start calc" ; + } + else if(mxButton->get_id()=="impress_all") + { + UITestLogger::getInstance().setAppName("impress"); + return "Start impress" ; + } + else if(mxButton->get_id()=="draw_all") + { + UITestLogger::getInstance().setAppName("draw"); + return "Start draw" ; + } + else if(mxButton->get_id()=="math_all") + { + UITestLogger::getInstance().setAppName("math"); + return "Start math" ; + } + else if(mxButton->get_id()=="database_all") + { + UITestLogger::getInstance().setAppName("database"); + return "Start database" ; + } + else{ + if (get_top_parent(mxButton)->get_id().isEmpty()){ + //This part because if we don't have parent + return "Click on '" + mxButton->get_id() ; + } + return "Click on '" + mxButton->get_id() + "' from "+ + get_top_parent(mxButton)->get_id(); + } + } + else + return WindowUIObject::get_action(nEvent); +} + +std::unique_ptr<UIObject> ButtonUIObject::create(vcl::Window* pWindow) +{ + Button* pButton = dynamic_cast<Button*>(pWindow); + assert(pButton); + return std::unique_ptr<UIObject>(new ButtonUIObject(pButton)); +} + +DialogUIObject::DialogUIObject(const VclPtr<Dialog>& xDialog): + WindowUIObject(xDialog), + mxDialog(xDialog) +{ +} + +DialogUIObject::~DialogUIObject() +{ +} + +StringMap DialogUIObject::get_state() +{ + StringMap aMap = WindowUIObject::get_state(); + aMap["Modal"] = OUString::boolean(mxDialog->IsModalInputMode()); + + return aMap; +} + +OUString DialogUIObject::get_name() const +{ + return "DialogUIObject"; +} + +std::unique_ptr<UIObject> DialogUIObject::create(vcl::Window* pWindow) +{ + Dialog* pDialog = dynamic_cast<Dialog*>(pWindow); + assert(pDialog); + return std::unique_ptr<UIObject>(new DialogUIObject(pDialog)); +} + +EditUIObject::EditUIObject(const VclPtr<Edit>& xEdit): + WindowUIObject(xEdit), + mxEdit(xEdit) +{ +} + +EditUIObject::~EditUIObject() +{ +} + +void EditUIObject::execute(const OUString& rAction, + const StringMap& rParameters) +{ + bool bHandled = true; + if (rAction == "SET") + { + if (rParameters.find("TEXT") != rParameters.end()) + { + auto it = rParameters.find("TEXT"); + if (it == rParameters.end()) + { + SAL_WARN("vcl.uitest", "missing parameter TEXT to action SET"); + return; + } + + const OUString& rText = it->second; + auto aKeyEvents = generate_key_events_from_text(rText); + for (auto const& keyEvent : aKeyEvents) + { + mxEdit->KeyInput(keyEvent); + } + } + else + { + bHandled = false; + } + } + else if (rAction == "SELECT") + { + if (rParameters.find("FROM") != rParameters.end() && + rParameters.find("TO") != rParameters.end()) + { + long nMin = rParameters.find("FROM")->second.toInt32(); + long nMax = rParameters.find("TO")->second.toInt32(); + Selection aSelection(nMin, nMax); + mxEdit->SetSelection(aSelection); + } + } + else if (rAction == "CLEAR") + { + mxEdit->SetText(""); + mxEdit->Modify(); + bHandled = true; + } + else + { + bHandled = false; + } + + if (!bHandled) + WindowUIObject::execute(rAction, rParameters); +} + +StringMap EditUIObject::get_state() +{ + StringMap aMap = WindowUIObject::get_state(); + aMap["MaxTextLength"] = OUString::number(mxEdit->GetMaxTextLen()); + aMap["SelectedText"] = mxEdit->GetSelected(); + aMap["Text"] = mxEdit->GetText(); + + return aMap; +} + +OUString EditUIObject::get_action(VclEventId nEvent) const +{ + if (nEvent == VclEventId::EditSelectionChanged) + { + const Selection& rSelection = mxEdit->GetSelection(); + long nMin = rSelection.Min(); + long nMax = rSelection.Max(); + if(get_top_parent(mxEdit)->get_id().isEmpty()){ + //This part because if we don't have parent + return "Select in '" + + mxEdit->get_id() + + "' {\"FROM\": \"" + OUString::number(nMin) + "\", \"TO\": \"" + + OUString::number(nMax) + "\"}" + ; + } + return "Select in '" + + mxEdit->get_id() + + "' {\"FROM\": \"" + OUString::number(nMin) + "\", \"TO\": \"" + + OUString::number(nMax) + "\"} from " + + get_top_parent(mxEdit)->get_id() + ; + } + else + return WindowUIObject::get_action(nEvent); +} + +OUString EditUIObject::get_name() const +{ + return "EditUIObject"; +} + +std::unique_ptr<UIObject> EditUIObject::create(vcl::Window* pWindow) +{ + Edit* pEdit = dynamic_cast<Edit*>(pWindow); + assert(pEdit); + return std::unique_ptr<UIObject>(new EditUIObject(pEdit)); +} + +MultiLineEditUIObject::MultiLineEditUIObject(const VclPtr<VclMultiLineEdit>& xEdit): + WindowUIObject(xEdit), + mxEdit(xEdit) +{ +} + +MultiLineEditUIObject::~MultiLineEditUIObject() +{ +} + +void MultiLineEditUIObject::execute(const OUString& rAction, + const StringMap& rParameters) +{ + bool bHandled = true; + if (rAction == "TYPE") + { + WindowUIObject aChildObj(mxEdit->GetTextWindow()); + aChildObj.execute(rAction, rParameters); + } + else if (rAction == "SELECT") + { + if (rParameters.find("FROM") != rParameters.end() && + rParameters.find("TO") != rParameters.end()) + { + long nMin = rParameters.find("FROM")->second.toInt32(); + long nMax = rParameters.find("TO")->second.toInt32(); + Selection aSelection(nMin, nMax); + mxEdit->SetSelection(aSelection); + } + } + else + { + bHandled = false; + } + + if (!bHandled) + WindowUIObject::execute(rAction, rParameters); +} + +StringMap MultiLineEditUIObject::get_state() +{ + StringMap aMap = WindowUIObject::get_state(); + aMap["MaxTextLength"] = OUString::number(mxEdit->GetMaxTextLen()); + aMap["SelectedText"] = mxEdit->GetSelected(); + aMap["Text"] = mxEdit->GetText(); + + return aMap; +} + +OUString MultiLineEditUIObject::get_name() const +{ + return "MultiLineEditUIObject"; +} + +std::unique_ptr<UIObject> MultiLineEditUIObject::create(vcl::Window* pWindow) +{ + VclMultiLineEdit* pEdit = dynamic_cast<VclMultiLineEdit*>(pWindow); + assert(pEdit); + return std::unique_ptr<UIObject>(new MultiLineEditUIObject(pEdit)); +} + +CheckBoxUIObject::CheckBoxUIObject(const VclPtr<CheckBox>& xCheckbox): + WindowUIObject(xCheckbox), + mxCheckBox(xCheckbox) +{ +} + +CheckBoxUIObject::~CheckBoxUIObject() +{ +} + +void CheckBoxUIObject::execute(const OUString& rAction, + const StringMap& /*rParameters*/) +{ + if (rAction == "CLICK") + { + // don't use toggle directly, it does not set the value + mxCheckBox->ImplCheck(); + } +} + +StringMap CheckBoxUIObject::get_state() +{ + StringMap aMap = WindowUIObject::get_state(); + aMap["Selected"] = OUString::boolean(mxCheckBox->IsChecked()); + aMap["TriStateEnabled"] = OUString::boolean(mxCheckBox->IsTriStateEnabled()); + return aMap; +} + +OUString CheckBoxUIObject::get_name() const +{ + return "CheckBoxUIObject"; +} + +OUString CheckBoxUIObject::get_action(VclEventId nEvent) const +{ + if (nEvent == VclEventId::CheckboxToggle) + { + if(get_top_parent(mxCheckBox)->get_id().isEmpty()){ + //This part because if we don't have parent + return "Toggle '" + mxCheckBox->get_id() + "' CheckBox"; + } + return "Toggle '" + mxCheckBox->get_id() + "' CheckBox from " + + get_top_parent(mxCheckBox)->get_id(); + } + else + return WindowUIObject::get_action(nEvent); +} + +std::unique_ptr<UIObject> CheckBoxUIObject::create(vcl::Window* pWindow) +{ + CheckBox* pCheckBox = dynamic_cast<CheckBox*>(pWindow); + assert(pCheckBox); + return std::unique_ptr<UIObject>(new CheckBoxUIObject(pCheckBox)); +} + +RadioButtonUIObject::RadioButtonUIObject(const VclPtr<RadioButton>& xRadioButton): + WindowUIObject(xRadioButton), + mxRadioButton(xRadioButton) +{ +} + +RadioButtonUIObject::~RadioButtonUIObject() +{ +} + +void RadioButtonUIObject::execute(const OUString& rAction, + const StringMap& /*rParameters*/) +{ + if (rAction == "CLICK") + { + mxRadioButton->ImplCallClick(); + } +} + +StringMap RadioButtonUIObject::get_state() +{ + StringMap aMap = WindowUIObject::get_state(); + aMap["Checked"] = OUString::boolean(mxRadioButton->IsChecked()); + + return aMap; +} + +OUString RadioButtonUIObject::get_name() const +{ + return "RadioButtonUIObject"; +} + +OUString RadioButtonUIObject::get_action(VclEventId nEvent) const +{ + if (nEvent == VclEventId::RadiobuttonToggle) + { + if(get_top_parent(mxRadioButton)->get_id().isEmpty()){ + //This part because if we don't have parent + return "Select '" + mxRadioButton->get_id() + "' RadioButton"; + } + return "Select '" + mxRadioButton->get_id() + "' RadioButton from " + + get_top_parent(mxRadioButton)->get_id(); + } + else + return WindowUIObject::get_action(nEvent); +} + +std::unique_ptr<UIObject> RadioButtonUIObject::create(vcl::Window* pWindow) +{ + RadioButton* pRadioButton = dynamic_cast<RadioButton*>(pWindow); + assert(pRadioButton); + return std::unique_ptr<UIObject>(new RadioButtonUIObject(pRadioButton)); +} + +TabPageUIObject::TabPageUIObject(const VclPtr<TabPage>& xTabPage): + WindowUIObject(xTabPage), + mxTabPage(xTabPage) +{ +} + +TabPageUIObject::~TabPageUIObject() +{ +} + +void TabPageUIObject::execute(const OUString& rAction, + const StringMap& /*rParameters*/) +{ + if (rAction == "SELECT") + { + /* code */ + } +} + +StringMap TabPageUIObject::get_state() +{ + StringMap aMap = WindowUIObject::get_state(); + + return aMap; +} + +OUString TabPageUIObject::get_name() const +{ + return "TabPageUIObject"; +} + +ListBoxUIObject::ListBoxUIObject(const VclPtr<ListBox>& xListBox): + WindowUIObject(xListBox), + mxListBox(xListBox) +{ +} + +ListBoxUIObject::~ListBoxUIObject() +{ +} + +void ListBoxUIObject::execute(const OUString& rAction, + const StringMap& rParameters) +{ + if (!mxListBox->IsEnabled()) + return; + + bool isTiledRendering = comphelper::LibreOfficeKit::isActive(); + if (!isTiledRendering && !mxListBox->IsReallyVisible()) + return; + + if (rAction == "SELECT") + { + bool bSelect = true; + if (rParameters.find("POS") != rParameters.end()) + { + auto itr = rParameters.find("POS"); + OUString aVal = itr->second; + sal_Int32 nPos = aVal.toInt32(); + mxListBox->SelectEntryPos(nPos, bSelect); + } + else if (rParameters.find("TEXT") != rParameters.end()) + { + auto itr = rParameters.find("TEXT"); + OUString aText = itr->second; + mxListBox->SelectEntry(aText, bSelect); + } + mxListBox->Select(); + } + else + WindowUIObject::execute(rAction, rParameters); +} + +StringMap ListBoxUIObject::get_state() +{ + StringMap aMap = WindowUIObject::get_state(); + aMap["ReadOnly"] = OUString::boolean(mxListBox->IsReadOnly()); + aMap["MultiSelect"] = OUString::boolean(mxListBox->IsMultiSelectionEnabled()); + aMap["EntryCount"] = OUString::number(mxListBox->GetEntryCount()); + aMap["SelectEntryCount"] = OUString::number(mxListBox->GetSelectedEntryCount()); + aMap["SelectEntryPos"] = OUString::number(mxListBox->GetSelectedEntryPos()); + aMap["SelectEntryText"] = mxListBox->GetSelectedEntry(); + + return aMap; +} + +OUString ListBoxUIObject::get_name() const +{ + return "ListBoxUIObject"; +} + +OUString ListBoxUIObject::get_action(VclEventId nEvent) const +{ + if (nEvent == VclEventId::ListboxSelect) + { + sal_Int32 nPos = mxListBox->GetSelectedEntryPos(); + if(get_top_parent(mxListBox)->get_id().isEmpty()){ + //This part because if we don't have parent + return "Select element with position " + OUString::number(nPos) + + " in '" + mxListBox->get_id(); + } + return "Select element with position " + OUString::number(nPos) + + " in '" + mxListBox->get_id() +"' from" + get_top_parent(mxListBox)->get_id() ; + } + else if (nEvent == VclEventId::ListboxFocus) + { + if(get_top_parent(mxListBox)->get_id().isEmpty()) + { + //This part because if we don't have parent + return this->get_type() + " Action:FOCUS Id:" + mxListBox->get_id(); + } + return this->get_type() + " Action:FOCUS Id:" + mxListBox->get_id() + + " Parent:" + get_top_parent(mxListBox)->get_id(); + } + else + return WindowUIObject::get_action(nEvent); +} + +std::unique_ptr<UIObject> ListBoxUIObject::create(vcl::Window* pWindow) +{ + ListBox* pListBox = dynamic_cast<ListBox*>(pWindow); + assert(pListBox); + return std::unique_ptr<UIObject>(new ListBoxUIObject(pListBox)); +} + +ComboBoxUIObject::ComboBoxUIObject(const VclPtr<ComboBox>& xComboBox): + WindowUIObject(xComboBox), + mxComboBox(xComboBox) +{ +} + +ComboBoxUIObject::~ComboBoxUIObject() +{ +} + +void ComboBoxUIObject::execute(const OUString& rAction, + const StringMap& rParameters) +{ + if (rAction == "SELECT") + { + if (rParameters.find("POS") != rParameters.end()) + { + auto itr = rParameters.find("POS"); + OUString aVal = itr->second; + sal_Int32 nPos = aVal.toInt32(); + mxComboBox->SelectEntryPos(nPos); + } + mxComboBox->Select(); + } + else if (rAction == "TYPE") + { + if (mxComboBox->GetSubEdit()) + { + Edit* pEdit = mxComboBox->GetSubEdit(); + std::unique_ptr<UIObject> pObj = EditUIObject::create(pEdit); + pObj->execute(rAction, rParameters); + } + else + WindowUIObject::execute(rAction, rParameters); + } + else + WindowUIObject::execute(rAction, rParameters); +} + +StringMap ComboBoxUIObject::get_state() +{ + StringMap aMap = WindowUIObject::get_state(); + + return aMap; +} + +OUString ComboBoxUIObject::get_name() const +{ + return "ComboBoxUIObject"; +} + +OUString ComboBoxUIObject::get_action(VclEventId nEvent) const +{ + if (nEvent == VclEventId::ComboboxSelect) + { + sal_Int32 nPos = mxComboBox->GetSelectedEntryPos(); + if (get_top_parent(mxComboBox)->get_id().isEmpty()){ + //This part because if we don't have parent + return "Select in '" + mxComboBox->get_id() + + "' ComboBox item number " + OUString::number(nPos); + } + return "Select in '" + mxComboBox->get_id() + + "' ComboBox item number " + OUString::number(nPos) + + " from " + get_top_parent(mxComboBox)->get_id(); + } + else + return WindowUIObject::get_action(nEvent); +} + +std::unique_ptr<UIObject> ComboBoxUIObject::create(vcl::Window* pWindow) +{ + ComboBox* pComboBox = dynamic_cast<ComboBox*>(pWindow); + assert(pComboBox); + return std::unique_ptr<UIObject>(new ComboBoxUIObject(pComboBox)); +} + +SpinUIObject::SpinUIObject(const VclPtr<SpinButton>& xSpinButton): + WindowUIObject(xSpinButton), + mxSpinButton(xSpinButton) +{ +} + +SpinUIObject::~SpinUIObject() +{ +} + +void SpinUIObject::execute(const OUString& rAction, + const StringMap& /*rParameters*/) +{ + if (rAction == "UP") + { + mxSpinButton->Up(); + } + else if (rAction == "DOWN") + { + mxSpinButton->Down(); + } +} + +StringMap SpinUIObject::get_state() +{ + StringMap aMap = WindowUIObject::get_state(); + aMap["Min"] = OUString::number(mxSpinButton->GetRangeMin()); + aMap["Max"] = OUString::number(mxSpinButton->GetRangeMax()); + aMap["Step"] = OUString::number(mxSpinButton->GetValueStep()); + aMap["Value"] = OUString::number(mxSpinButton->GetValue()); + + return aMap; +} + +OUString SpinUIObject::get_action(VclEventId nEvent) const +{ + if (nEvent == VclEventId::SpinbuttonUp) + { + return this->get_type() + " Action:UP Id:" + mxSpinButton->get_id() + + " Parent:" + get_top_parent(mxSpinButton)->get_id(); + } + else if (nEvent == VclEventId::SpinbuttonDown) + { + return this->get_type() + " Action:DOWN Id:" + mxSpinButton->get_id() + + " Parent:" + get_top_parent(mxSpinButton)->get_id(); + } + else + return WindowUIObject::get_action(nEvent); +} + +OUString SpinUIObject::get_name() const +{ + return "SpinUIObject"; +} + +SpinFieldUIObject::SpinFieldUIObject(const VclPtr<SpinField>& xSpinField): + EditUIObject(xSpinField), + mxSpinField(xSpinField) +{ +} + +SpinFieldUIObject::~SpinFieldUIObject() +{ +} + +void SpinFieldUIObject::execute(const OUString& rAction, + const StringMap& rParameters) +{ + if (rAction == "UP") + { + mxSpinField->Up(); + } + else if (rAction == "DOWN") + { + mxSpinField->Down(); + } + else if (rAction == "TYPE") + { + if (mxSpinField->GetSubEdit()) + { + Edit* pSubEdit = mxSpinField->GetSubEdit(); + EditUIObject aSubObject(pSubEdit); + aSubObject.execute(rAction, rParameters); + } + } + else + EditUIObject::execute(rAction, rParameters); +} + +StringMap SpinFieldUIObject::get_state() +{ + StringMap aMap = EditUIObject::get_state(); + + return aMap; +} + +OUString SpinFieldUIObject::get_action(VclEventId nEvent) const +{ + if (nEvent == VclEventId::SpinfieldUp) + { + if(get_top_parent(mxSpinField)->get_id().isEmpty()) + { + //This part because if we don't have parent + return "Increase '" + mxSpinField->get_id(); + } + return "Increase '" + mxSpinField->get_id() + + "' from " + get_top_parent(mxSpinField)->get_id(); + } + else if (nEvent == VclEventId::SpinfieldDown) + { + if(get_top_parent(mxSpinField)->get_id().isEmpty()) + { + //This part because if we don't have parent + return "Decrease '" + mxSpinField->get_id(); + } + return "Decrease '" + mxSpinField->get_id() + + "' from " + get_top_parent(mxSpinField)->get_id(); + } + else + return WindowUIObject::get_action(nEvent); +} + +OUString SpinFieldUIObject::get_name() const +{ + return "SpinFieldUIObject"; +} + +std::unique_ptr<UIObject> SpinFieldUIObject::create(vcl::Window* pWindow) +{ + SpinField* pSpinField = dynamic_cast<SpinField*>(pWindow); + assert(pSpinField); + return std::unique_ptr<UIObject>(new SpinFieldUIObject(pSpinField)); +} + + +MetricFieldUIObject::MetricFieldUIObject(const VclPtr<MetricField>& xMetricField): + SpinFieldUIObject(xMetricField), + mxMetricField(xMetricField) +{ +} + +MetricFieldUIObject::~MetricFieldUIObject() +{ +} + +void MetricFieldUIObject::execute(const OUString& rAction, + const StringMap& rParameters) +{ + if (rAction == "VALUE") + { + auto itPos = rParameters.find("VALUE"); + if (itPos != rParameters.end()) + { + mxMetricField->SetValueFromString(itPos->second); + } + } + else + SpinFieldUIObject::execute(rAction, rParameters); +} + +StringMap MetricFieldUIObject::get_state() +{ + StringMap aMap = EditUIObject::get_state(); + aMap["Value"] = mxMetricField->GetValueString(); + + return aMap; +} + +OUString MetricFieldUIObject::get_name() const +{ + return "MetricFieldUIObject"; +} + +std::unique_ptr<UIObject> MetricFieldUIObject::create(vcl::Window* pWindow) +{ + MetricField* pMetricField = dynamic_cast<MetricField*>(pWindow); + assert(pMetricField); + return std::unique_ptr<UIObject>(new MetricFieldUIObject(pMetricField)); +} + +FormattedFieldUIObject::FormattedFieldUIObject(const VclPtr<FormattedField>& xFormattedField): + SpinFieldUIObject(xFormattedField), + mxFormattedField(xFormattedField) +{ +} + +FormattedFieldUIObject::~FormattedFieldUIObject() +{ +} + +void FormattedFieldUIObject::execute(const OUString& rAction, + const StringMap& rParameters) +{ + if (rAction == "VALUE") + { + auto itPos = rParameters.find("VALUE"); + if (itPos != rParameters.end()) + { + mxFormattedField->SetValueFromString(itPos->second); + } + } + else + SpinFieldUIObject::execute(rAction, rParameters); +} + +StringMap FormattedFieldUIObject::get_state() +{ + StringMap aMap = EditUIObject::get_state(); + aMap["Value"] = OUString::number(mxFormattedField->GetValue()); + + return aMap; +} + +OUString FormattedFieldUIObject::get_name() const +{ + return "FormattedFieldUIObject"; +} + +std::unique_ptr<UIObject> FormattedFieldUIObject::create(vcl::Window* pWindow) +{ + FormattedField* pFormattedField = dynamic_cast<FormattedField*>(pWindow); + assert(pFormattedField); + return std::unique_ptr<UIObject>(new FormattedFieldUIObject(pFormattedField)); +} + +TabControlUIObject::TabControlUIObject(const VclPtr<TabControl>& xTabControl): + WindowUIObject(xTabControl), + mxTabControl(xTabControl) +{ +} + +TabControlUIObject::~TabControlUIObject() +{ +} + +void TabControlUIObject::execute(const OUString& rAction, + const StringMap& rParameters) +{ + if (rAction == "SELECT") + { + if (rParameters.find("POS") != rParameters.end()) + { + auto itr = rParameters.find("POS"); + sal_uInt32 nPos = itr->second.toUInt32(); + std::vector<sal_uInt16> aIds = mxTabControl->GetPageIDs(); + mxTabControl->SelectTabPage(aIds[nPos]); + } + } + else + WindowUIObject::execute(rAction, rParameters); +} + +StringMap TabControlUIObject::get_state() +{ + StringMap aMap = WindowUIObject::get_state(); + aMap["PageCount"] = OUString::number(mxTabControl->GetPageCount()); + + sal_uInt16 nPageId = mxTabControl->GetCurPageId(); + aMap["CurrPageId"] = OUString::number(nPageId); + aMap["CurrPagePos"] = OUString::number(mxTabControl->GetPagePos(nPageId)); + + return aMap; +} + +OUString TabControlUIObject::get_action(VclEventId nEvent) const +{ + if (nEvent == VclEventId::TabpageActivate) + { + sal_Int32 nPageId = mxTabControl->GetCurPageId(); + + if(get_top_parent(mxTabControl)->get_id().isEmpty()){ + //This part because if we don't have parent + return "Choose Tab number " + OUString::number(mxTabControl->GetPagePos(nPageId)) + + " in '" + mxTabControl->get_id(); + } + return "Choose Tab number " + OUString::number(mxTabControl->GetPagePos(nPageId)) + + " in '" + mxTabControl->get_id()+ + "' from " + get_top_parent(mxTabControl)->get_id() ; + } + else + return WindowUIObject::get_action(nEvent); +} + +OUString TabControlUIObject::get_name() const +{ + return "TabControlUIObject"; +} + +std::unique_ptr<UIObject> TabControlUIObject::create(vcl::Window* pWindow) +{ + TabControl* pTabControl = dynamic_cast<TabControl*>(pWindow); + assert(pTabControl); + return std::unique_ptr<UIObject>(new TabControlUIObject(pTabControl)); +} + +RoadmapWizardUIObject::RoadmapWizardUIObject(const VclPtr<vcl::RoadmapWizard>& xRoadmapWizard): + WindowUIObject(xRoadmapWizard), + mxRoadmapWizard(xRoadmapWizard) +{ +} + +RoadmapWizardUIObject::~RoadmapWizardUIObject() +{ +} + +void RoadmapWizardUIObject::execute(const OUString& rAction, + const StringMap& rParameters) +{ + if (rAction == "SELECT") + { + if (rParameters.find("POS") != rParameters.end()) + { + auto itr = rParameters.find("POS"); + sal_uInt32 nPos = itr->second.toUInt32(); + mxRoadmapWizard->SelectRoadmapItemByID(nPos); + } + } + else + WindowUIObject::execute(rAction, rParameters); +} + +StringMap RoadmapWizardUIObject::get_state() +{ + StringMap aMap = WindowUIObject::get_state(); + + aMap["CurrentStep"] = OUString::number(mxRoadmapWizard->GetCurrentRoadmapItemID()); + + return aMap; +} + +OUString RoadmapWizardUIObject::get_name() const +{ + return "RoadmapWizardUIObject"; +} + +std::unique_ptr<UIObject> RoadmapWizardUIObject::create(vcl::Window* pWindow) +{ + vcl::RoadmapWizard* pRoadmapWizard = dynamic_cast<vcl::RoadmapWizard*>(pWindow); + assert(pRoadmapWizard); + return std::unique_ptr<UIObject>(new RoadmapWizardUIObject(pRoadmapWizard)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/uitest/uitest.cxx b/vcl/source/uitest/uitest.cxx new file mode 100644 index 000000000..9edcba7a4 --- /dev/null +++ b/vcl/source/uitest/uitest.cxx @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + */ + +#include <memory> +#include <vcl/floatwin.hxx> +#include <vcl/uitest/uitest.hxx> +#include <vcl/uitest/uiobject.hxx> + +#include <vcl/toolkit/dialog.hxx> + +#include <svdata.hxx> + +#include <comphelper/dispatchcommand.hxx> +#include <com/sun/star/beans/PropertyValue.hpp> + +bool UITest::executeCommand(const OUString& rCommand) +{ + return comphelper::dispatchCommand( + rCommand, + {{"SynchronMode", -1, css::uno::Any(true), + css::beans::PropertyState_DIRECT_VALUE}}); +} + +bool UITest::executeCommandWithParameters(const OUString& rCommand, + const css::uno::Sequence< css::beans::PropertyValue >& rArgs) +{ + css::uno::Sequence< css::beans::PropertyValue > lNewArgs = + {{"SynchronMode", -1, css::uno::Any(true), + css::beans::PropertyState_DIRECT_VALUE}}; + + if ( rArgs.hasElements() ) + { + sal_uInt32 nIndex( lNewArgs.getLength() ); + lNewArgs.realloc( lNewArgs.getLength()+rArgs.getLength() ); + + std::copy(rArgs.begin(), rArgs.end(), std::next(lNewArgs.begin(), nIndex)); + } + return comphelper::dispatchCommand(rCommand,lNewArgs); +} + +bool UITest::executeDialog(const OUString& rCommand) +{ + return comphelper::dispatchCommand( + rCommand, + {{"SynchronMode", -1, css::uno::Any(false), + css::beans::PropertyState_DIRECT_VALUE}}); +} + +std::unique_ptr<UIObject> UITest::getFocusTopWindow() +{ + ImplSVData* pSVData = ImplGetSVData(); + ImplSVWinData& rWinData = *pSVData->mpWinData; + + if (!rWinData.mpExecuteDialogs.empty()) + { + return rWinData.mpExecuteDialogs.back()->GetUITestFactory()(rWinData.mpExecuteDialogs.back()); + } + + return pSVData->maFrameData.mpFirstFrame->GetUITestFactory()(pSVData->maFrameData.mpFirstFrame); +} + +std::unique_ptr<UIObject> UITest::getFloatWindow() +{ + ImplSVData* pSVData = ImplGetSVData(); + ImplSVWinData& rWinData = *pSVData->mpWinData; + + VclPtr<vcl::Window> pFloatWin = rWinData.mpFirstFloat; + if (pFloatWin) + return pFloatWin->GetUITestFactory()(pFloatWin); + + return nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/uitest/uno/uiobject_uno.cxx b/vcl/source/uitest/uno/uiobject_uno.cxx new file mode 100644 index 000000000..593f4a4c1 --- /dev/null +++ b/vcl/source/uitest/uno/uiobject_uno.cxx @@ -0,0 +1,210 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + */ + +#include <memory> +#include "uiobject_uno.hxx" +#include <utility> +#include <cppuhelper/supportsservice.hxx> +#include <vcl/svapp.hxx> +#include <vcl/idle.hxx> + +#include <set> + +UIObjectUnoObj::UIObjectUnoObj(std::unique_ptr<UIObject> pObj): + UIObjectBase(m_aMutex), + mpObj(std::move(pObj)), + mReady(true) +{ +} + +UIObjectUnoObj::~UIObjectUnoObj() +{ + { + std::scoped_lock<std::mutex> lk3(mMutex); + } + SolarMutexGuard aGuard; + mpObj.reset(); +} + +css::uno::Reference<css::ui::test::XUIObject> SAL_CALL UIObjectUnoObj::getChild(const OUString& rID) +{ + if (!mpObj) + throw css::uno::RuntimeException(); + + SolarMutexGuard aGuard; + std::unique_ptr<UIObject> pObj = mpObj->get_child(rID); + return new UIObjectUnoObj(std::move(pObj)); +} + +IMPL_LINK_NOARG(UIObjectUnoObj, NotifyHdl, Timer*, void) +{ + std::scoped_lock<std::mutex> lk(mMutex); + mReady = true; + cv.notify_all(); +} + +namespace { + +class ExecuteWrapper +{ + std::function<void()> mFunc; + Link<Timer*, void> mHandler; + volatile bool mbSignal; + +public: + + ExecuteWrapper(std::function<void()> func, Link<Timer*, void> handler): + mFunc(std::move(func)), + mHandler(handler), + mbSignal(false) + { + } + + void setSignal() + { + mbSignal = true; + } + + DECL_LINK( ExecuteActionHdl, Timer*, void ); +}; + + +IMPL_LINK_NOARG(ExecuteWrapper, ExecuteActionHdl, Timer*, void) +{ + { + Idle aIdle; + { + mFunc(); + aIdle.SetDebugName("UI Test Idle Handler2"); + aIdle.SetPriority(TaskPriority::LOWEST); + aIdle.SetInvokeHandler(mHandler); + aIdle.Start(); + } + + while (!mbSignal) { + Application::Reschedule(); + } + } + delete this; +} + +} + +void SAL_CALL UIObjectUnoObj::executeAction(const OUString& rAction, const css::uno::Sequence<css::beans::PropertyValue>& rPropValues) +{ + if (!mpObj) + throw css::uno::RuntimeException(); + + std::unique_lock<std::mutex> lk(mMutex); + mAction = rAction; + mPropValues = rPropValues; + mReady = false; + auto aIdle = std::make_unique<Idle>(); + aIdle->SetDebugName("UI Test Idle Handler"); + aIdle->SetPriority(TaskPriority::HIGHEST); + + std::function<void()> func = [this](){ + + SolarMutexGuard aGuard; + StringMap aMap; + for (const auto& rPropVal : std::as_const(mPropValues)) + { + OUString aVal; + if (!(rPropVal.Value >>= aVal)) + continue; + + aMap[rPropVal.Name] = aVal; + } + mpObj->execute(mAction, aMap); + }; + + ExecuteWrapper* pWrapper = new ExecuteWrapper(func, LINK(this, UIObjectUnoObj, NotifyHdl)); + aIdle->SetInvokeHandler(LINK(pWrapper, ExecuteWrapper, ExecuteActionHdl)); + { + SolarMutexGuard aGuard; + aIdle->Start(); + } + + cv.wait(lk, [this]{return mReady;}); + pWrapper->setSignal(); + + SolarMutexGuard aGuard; + aIdle.reset(); +} + +css::uno::Sequence<css::beans::PropertyValue> UIObjectUnoObj::getState() +{ + if (!mpObj) + throw css::uno::RuntimeException(); + + SolarMutexGuard aGuard; + StringMap aMap = mpObj->get_state(); + css::uno::Sequence<css::beans::PropertyValue> aProps(aMap.size()); + sal_Int32 i = 0; + for (auto const& elem : aMap) + { + aProps[i].Name = elem.first; + aProps[i].Value <<= elem.second; + ++i; + } + + return aProps; +} + +css::uno::Sequence<OUString> UIObjectUnoObj::getChildren() +{ + if (!mpObj) + throw css::uno::RuntimeException(); + + std::set<OUString> aChildren = mpObj->get_children(); + + css::uno::Sequence<OUString> aRet(aChildren.size()); + sal_Int32 i = 0; + for (auto const& child : aChildren) + { + aRet[i] = child; + ++i; + } + + return aRet; +} + +OUString SAL_CALL UIObjectUnoObj::getType() +{ + if (!mpObj) + throw css::uno::RuntimeException(); + + return mpObj->get_type(); +} + +OUString SAL_CALL UIObjectUnoObj::getImplementationName() +{ + return "org.libreoffice.uitest.UIObject"; +} + +sal_Bool UIObjectUnoObj::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence<OUString> UIObjectUnoObj::getSupportedServiceNames() +{ + return { "com.sun.star.ui.test.UIObject" }; +} + +OUString SAL_CALL UIObjectUnoObj::getHierarchy() +{ + if (!mpObj) + throw css::uno::RuntimeException(); + + SolarMutexGuard aGuard; + return mpObj->dumpHierarchy(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/uitest/uno/uiobject_uno.hxx b/vcl/source/uitest/uno/uiobject_uno.hxx new file mode 100644 index 000000000..2f98ab7c0 --- /dev/null +++ b/vcl/source/uitest/uno/uiobject_uno.hxx @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + */ + +#ifndef INCLUDED_VCL_SOURCE_UITEST_UNO_UIOBJECT_UNO_HXX +#define INCLUDED_VCL_SOURCE_UITEST_UNO_UIOBJECT_UNO_HXX + +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/basemutex.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/ui/test/XUIObject.hpp> + +#include <memory> +#include <condition_variable> +#include <mutex> + +#include <vcl/uitest/uiobject.hxx> + +class Timer; + +typedef ::cppu::WeakComponentImplHelper < + css::ui::test::XUIObject, css::lang::XServiceInfo + > UIObjectBase; + +class UIObjectUnoObj : public cppu::BaseMutex, + public UIObjectBase +{ +private: + std::unique_ptr<UIObject> mpObj; + +public: + + explicit UIObjectUnoObj(std::unique_ptr<UIObject> pObj); + virtual ~UIObjectUnoObj() override; + + css::uno::Reference<css::ui::test::XUIObject> SAL_CALL getChild(const OUString& rID) override; + + void SAL_CALL executeAction(const OUString& rAction, const css::uno::Sequence<css::beans::PropertyValue>& xPropValues) override; + + css::uno::Sequence<css::beans::PropertyValue> SAL_CALL getState() override; + + css::uno::Sequence<OUString> SAL_CALL getChildren() override; + + OUString SAL_CALL getType() override; + + OUString SAL_CALL getImplementationName() override; + + sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override; + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + + OUString SAL_CALL getHierarchy() override; + +private: + + DECL_LINK( NotifyHdl, Timer*, void ); + + std::condition_variable cv; + std::mutex mMutex; + bool mReady; + + OUString mAction; + css::uno::Sequence<css::beans::PropertyValue> mPropValues; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/uitest/uno/uitest_uno.cxx b/vcl/source/uitest/uno/uitest_uno.cxx new file mode 100644 index 000000000..d0d70a816 --- /dev/null +++ b/vcl/source/uitest/uno/uitest_uno.cxx @@ -0,0 +1,120 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + */ + +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/ui/test/XUITest.hpp> + +#include <memory> + +#include <vcl/uitest/uitest.hxx> +#include <vcl/svapp.hxx> + +#include "uiobject_uno.hxx" + +namespace +{ + typedef ::cppu::WeakComponentImplHelper < + css::ui::test::XUITest, css::lang::XServiceInfo + > UITestBase; + +class UITestUnoObj : public cppu::BaseMutex, + public UITestBase +{ +private: + std::unique_ptr<UITest> mpUITest; + +public: + + UITestUnoObj(); + + sal_Bool SAL_CALL executeCommand(const OUString& rCommand) override; + + sal_Bool SAL_CALL executeCommandWithParameters(const OUString& rCommand, + const css::uno::Sequence< css::beans::PropertyValue >& rArgs) override; + + sal_Bool SAL_CALL executeDialog(const OUString& rCommand) override; + + css::uno::Reference<css::ui::test::XUIObject> SAL_CALL getTopFocusWindow() override; + + css::uno::Reference<css::ui::test::XUIObject> SAL_CALL getFloatWindow() override; + + OUString SAL_CALL getImplementationName() override; + + sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override; + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; +}; + +} + +UITestUnoObj::UITestUnoObj(): + UITestBase(m_aMutex), + mpUITest(new UITest) +{ +} + +sal_Bool SAL_CALL UITestUnoObj::executeCommand(const OUString& rCommand) +{ + SolarMutexGuard aGuard; + return UITest::executeCommand(rCommand); +} + +sal_Bool SAL_CALL UITestUnoObj::executeCommandWithParameters(const OUString& rCommand, + const css::uno::Sequence< css::beans::PropertyValue >& rArgs) +{ + SolarMutexGuard aGuard; + return UITest::executeCommandWithParameters(rCommand,rArgs); +} + +sal_Bool SAL_CALL UITestUnoObj::executeDialog(const OUString& rCommand) +{ + SolarMutexGuard aGuard; + return UITest::executeDialog(rCommand); +} + +css::uno::Reference<css::ui::test::XUIObject> SAL_CALL UITestUnoObj::getTopFocusWindow() +{ + SolarMutexGuard aGuard; + std::unique_ptr<UIObject> pObj = UITest::getFocusTopWindow(); + return new UIObjectUnoObj(std::move(pObj)); +} + +css::uno::Reference<css::ui::test::XUIObject> SAL_CALL UITestUnoObj::getFloatWindow() +{ + SolarMutexGuard aGuard; + std::unique_ptr<UIObject> pObj = UITest::getFloatWindow(); + return new UIObjectUnoObj(std::move(pObj)); +} + +OUString SAL_CALL UITestUnoObj::getImplementationName() +{ + return "org.libreoffice.uitest.UITest"; +} + +sal_Bool UITestUnoObj::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence<OUString> UITestUnoObj::getSupportedServiceNames() +{ + return { "com.sun.star.ui.test.UITest" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +UITest_get_implementation(css::uno::XComponentContext*, css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new UITestUnoObj()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |