/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #define DUMP_UITEST(x) SAL_INFO("vcl.uitest", x) 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 OUString("Generic UIObject"); } std::unique_ptr UIObject::get_child(const OUString&) { return std::unique_ptr(); } std::set UIObject::get_children() const { return std::set(); } void UIObject::dumpState() const { } void UIObject::dumpHierarchy() const { } namespace { bool isDialogWindow(vcl::Window* pWindow) { WindowType nType = pWindow->GetType(); // DIALOG to FONTDIALOG if (nType >= 0x13a && nType <= 0x143) return true; // MESSBOX, INFOBOX, QUERYBOX, WARNINGBOX, ERRORBOX if (nType >= 0x130 && nType <= 0x134) return true; if (nType == WINDOW_TABDIALOG) return true; return false; } vcl::Window* get_dialog_parent(vcl::Window* pWindow) { if (isDialogWindow(pWindow)) return pWindow; vcl::Window* pParent = pWindow->GetParent(); if (!pParent) return pWindow; return get_dialog_parent(pParent); } std::vector generate_key_events_from_text(const OUString& rStr) { std::vector aEvents; vcl::KeyCode aCode; for (sal_Int32 i = 0, n = rStr.getLength(); i != n; ++i) { aEvents.push_back(KeyEvent(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; } std::vector generate_key_events_from_keycode(const OUString& rStr) { std::vector aEvents; std::map aKeyMap = { {"ESC", KEY_ESCAPE}, {"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 aTokens = comphelper::string::split(rStr, '+'); for (auto itr = aTokens.begin(), itrEnd = aTokens.end(); itr != itrEnd; ++itr) { OUString aToken = itr->trim(); if (aToken == "CTRL") { bMod1 = true; } else if (aToken == "SHIFT") { bShift = true; } else if (aToken == "ALT") { bMod2 = true; } else aRemainingText = aToken; } if (aKeyMap.find(aRemainingText) != aKeyMap.end()) { sal_uInt16 nKey = aKeyMap[aRemainingText]; vcl::KeyCode aCode(nKey, bShift, bMod1, bMod2, false); aEvents.push_back(KeyEvent( '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.push_back(KeyEvent(aRemainingText[i], aCode)); } } return aEvents; } OUString to_string(const Point& rPos) { OUStringBuffer aBuffer; aBuffer.append(OUString::number(rPos.X())); aBuffer.append("x"); aBuffer.append(OUString::number(rPos.Y())); return aBuffer.makeStringAndClear(); } OUString to_string(const Size& rSize) { OUStringBuffer aBuffer; aBuffer.append(rSize.Width()); aBuffer.append("x"); aBuffer.append(rSize.Height()); return aBuffer.makeStringAndClear(); } } WindowUIObject::WindowUIObject(VclPtr xWindow): mxWindow(xWindow) { } StringMap WindowUIObject::get_state() { StringMap aMap; aMap["Visible"] = OUString::boolean(mxWindow->IsVisible()); aMap["ReallyVisible"] = OUString::boolean(mxWindow->IsReallyVisible()); aMap["Enabled"] = OUString::boolean(mxWindow->IsEnabled()); aMap["WindowType"] = OUString::number(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 itr = rParameters.begin(); itr != rParameters.end(); ++itr) { std::cout << itr->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 itr = aKeyEvents.begin(), itrEnd = aKeyEvents.end(); itr != itrEnd; ++itr) { mxWindow->KeyInput(*itr); } } 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 itrKey = aKeyEvents.begin(), itrKeyEnd = aKeyEvents.end(); itrKey != itrKeyEnd; ++itrKey) { mxWindow->KeyInput(*itrKey); } } else { SAL_WARN("vcl.uitest", "missing parameter TEXT to action TYPE"); return; } } else { bHandled = false; } if (!bHandled) { SAL_WARN("vcl.uitest", "unkown 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; 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* pParent, std::set& 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 WindowUIObject::get_child(const OUString& rID) { vcl::Window* pDialogParent = get_dialog_parent(mxWindow.get()); vcl::Window* pWindow = findChild(pDialogParent, rID); FactoryFunction aFunction = pWindow->GetUITestFactory(); return aFunction(pWindow); } std::set WindowUIObject::get_children() const { vcl::Window* pDialogParent = get_dialog_parent(mxWindow.get()); std::set aChildren; aChildren.insert(pDialogParent->get_id()); addChildren(pDialogParent, aChildren); return aChildren; } OUString WindowUIObject::get_name() const { return OUString("WindowUIObject"); } void WindowUIObject::dumpState() const { DUMP_UITEST(get_name() << " " << mxWindow->get_id()); DUMP_UITEST("Implementation Name: " << typeid(*mxWindow.get()).name()); StringMap aState = const_cast(this)->get_state(); for (auto itr = aState.begin(), itrEnd = aState.end(); itr != itrEnd; ++itr) { DUMP_UITEST("Property: " << itr->first << " with value: " << itr->second); } size_t nCount = mxWindow->GetChildCount(); if (nCount) DUMP_UITEST("With " << nCount << " Children:"); for (size_t i = 0; i < nCount; ++i) { vcl::Window* pChild = mxWindow->GetChild(i); std::unique_ptr pChildWrapper = pChild->GetUITestFactory()(pChild); pChildWrapper->dumpState(); } } void WindowUIObject::dumpHierarchy() const { vcl::Window* pDialogParent = get_dialog_parent(mxWindow.get()); std::unique_ptr pParentWrapper = pDialogParent->GetUITestFactory()(pDialogParent); pParentWrapper->dumpState(); } std::unique_ptr WindowUIObject::create(vcl::Window* pWindow) { return std::unique_ptr(new WindowUIObject(pWindow)); } ButtonUIObject::ButtonUIObject(VclPtr