summaryrefslogtreecommitdiffstats
path: root/vcl/source/uitest/logger.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/source/uitest/logger.cxx')
-rw-r--r--vcl/source/uitest/logger.cxx624
1 files changed, 624 insertions, 0 deletions
diff --git a/vcl/source/uitest/logger.cxx b/vcl/source/uitest/logger.cxx
new file mode 100644
index 0000000000..29520fd613
--- /dev/null
+++ b/vcl/source/uitest/logger.cxx
@@ -0,0 +1,624 @@
+/* -*- 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();
+ if (nType == WindowType::DIALOG || nType == WindowType::MODELESSDIALOG)
+ 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()
+ : 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(std::u16string_view 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("\"" + rProp.Name + "\": " + OUString::number(nValue) + ", ");
+ }
+ else if (aTypeName == "unsigned long")
+ {
+ sal_uInt32 nValue = 0;
+ rProp.Value >>= nValue;
+ aBuffer.append("\"" + rProp.Name + "\": " + OUString::number(nValue) + ", ");
+ }
+ else if (aTypeName == "boolean")
+ {
+ bool bValue = false;
+ rProp.Value >>= bValue;
+ aBuffer.append("\"" + rProp.Name + "\": ");
+ 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::logAction(vcl::Window* const& xUIWin, VclEventId nEvent)
+{
+ if (!mbValid)
+ return;
+
+ if (xUIWin->get_id().isEmpty())
+ return;
+
+ std::unique_ptr<UIObject> pUIObject = xUIWin->GetUITestFactory()(xUIWin);
+ OUString aAction = pUIObject->get_action(nEvent);
+
+ if (!aAction.isEmpty())
+ maStream.WriteLine(OUStringToOString(aAction, RTL_TEXTENCODING_UTF8));
+}
+
+void UITestLogger::log(std::u16string_view rString)
+{
+ if (!mbValid)
+ return;
+
+ if (rString.empty())
+ 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 && !pParent->IsTopWindow())
+ {
+ pParent = pParent->GetParent();
+ }
+
+ OUString aParentID = pParent ? pParent->get_id() : OUString();
+
+ 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("\"" + itr->first + "\": \"" + itr->second + "\"");
+ }
+
+ aParameterString.append("}");
+
+ return aParameterString.makeStringAndClear();
+}
+
+const 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;
+}
+
+const 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.aKeyWord == "ValueSet")
+ {
+ aLogLine = "Choose element with position "
+ + GetValueInMapWithIndex(rDescription.aParameters, 0) + " in '"
+ + rDescription.aID + "' from '" + rDescription.aParent + "'";
+ }
+ 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" + GetKeyInMapWithIndex(rDescription.aParameters, 2) + " 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.aAction == "COMMENT")
+ {
+ OUString type = GetKeyInMapWithIndex(rDescription.aParameters, 0);
+ if (type == "OPEN")
+ {
+ aLogLine = "Open Comment";
+ }
+ else if (type == "CLOSE")
+ {
+ aLogLine = "Close Comment";
+ }
+ }
+ }
+ 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.aKeyWord == "SwEditWinUIObject")
+ {
+ if (rDescription.aAction == "LEAVE")
+ {
+ aLogLine = "Leave '" + rDescription.aID + "'";
+ }
+ else if (rDescription.aAction == "SHOW")
+ {
+ aLogLine = "Show '" + rDescription.aID + "'";
+ }
+ else if (rDescription.aAction == "HIDE")
+ {
+ aLogLine = "Hide '" + rDescription.aID + "'";
+ }
+ else if (rDescription.aAction == "DELETE")
+ {
+ aLogLine = "Delete '" + rDescription.aID + "'";
+ }
+ else if (rDescription.aAction == "SETRESOLVED")
+ {
+ aLogLine = "Resolve '" + rDescription.aID + "'";
+ }
+ }
+ else if (rDescription.aParent == "element_selector")
+ {
+ aLogLine = "Select element no " + rDescription.aID + " From " + rDescription.aParent;
+ }
+ else if (rDescription.aKeyWord == "MenuButton")
+ {
+ if (rDescription.aAction == "OPENLIST")
+ {
+ aLogLine = "Open List From " + rDescription.aID;
+ }
+ else if (rDescription.aAction == "CLOSELIST")
+ {
+ aLogLine = "Close List From " + rDescription.aID;
+ }
+ else if (rDescription.aAction == "OPENFROMLIST")
+ {
+ aLogLine = "Select item no " + GetValueInMapWithIndex(rDescription.aParameters, 0)
+ + " From List of " + rDescription.aID;
+ }
+ }
+ else if (rDescription.aKeyWord == "VerticalTab")
+ {
+ aLogLine = "Choose Tab number " + GetValueInMapWithIndex(rDescription.aParameters, 0)
+ + " in '" + rDescription.aID + "'";
+ }
+ 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: */