com.hp.octane.integrations.uft.UftTestDiscoveryUtils Maven / Gradle / Ivy
/**
* Copyright 2017-2023 Open Text
*
* The only warranties for products and services of Open Text and
* its affiliates and licensors (“Open Text”) are as may be set forth
* in the express warranty statements accompanying such products and services.
* Nothing herein should be construed as constituting an additional warranty.
* Open Text shall not be liable for technical or editorial errors or
* omissions contained herein. The information contained herein is subject
* to change without notice.
*
* Except as specifically indicated otherwise, this document contains
* confidential information and a valid license is required for possession,
* use or copying. If this work is provided to the U.S. Government,
* consistent with FAR 12.211 and 12.212, Commercial Computer Software,
* Computer Software Documentation, and Technical Data for Commercial Items are
* licensed to the U.S. Government under vendor's standard commercial license.
*
* Licensed 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hp.octane.integrations.uft;
import com.hp.octane.integrations.dto.executor.impl.TestingToolType;
import com.hp.octane.integrations.uft.items.*;
import com.hp.octane.integrations.utils.SdkConstants;
import com.hp.octane.integrations.utils.SdkStringUtils;
import org.apache.commons.lang3.SystemUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.poi.poifs.filesystem.*;
import org.apache.poi.util.StringUtil;
import org.w3c.dom.*;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class UftTestDiscoveryUtils {
private static final Logger logger = LogManager.getLogger(UftTestDiscoveryUtils.class);
private static final String STFileExtention = ".st";//api test
private static final String QTPFileExtention = ".tsp";//gui test
private static final String XLSXExtention = ".xlsx";//excel file
private static final String XLSExtention = ".xls";//excel file
private static final String GUI_TEST_FILE = "test" + QTPFileExtention;//gui test
private static final String API_ACTIONS_FILE = "actions.xml";//api test
private static final String RESOURCE_MTR_FILE = "resource.mtr";//parameters file
private static final Set SKIP_FOLDERS = Stream.of("_discovery_results").collect(Collectors.toSet());
public static final String ACTION_0 = "action0";
public static final String UFT_PARAM_ARG_DEFAULT_VALUE_NODE_NAME = "ArgDefaultValue";
public static final String UFT_PARAM_ARG_NAME_NODE_NAME = "ArgName";
public static final String UFT_PARAM_ARGUMENTS_COLLECTION_NODE_NAME = "ArgumentsCollection";
public static final String UFT_ACTION_DESCRIPTION_NODE_NAME = "Description";
public static final String UFT_COMPONENT_NODE_NAME = "Component";
public static final String UFT_DEPENDENCY_NODE_NAME = "Dependency";
public static final String UFT_ACTION_LOGICAL_ATTR = "Logical";
public static final String UFT_ACTION_KIND_ATTR = "Kind";
public static final String UFT_ACTION_TYPE_ATTR = "Type";
public static final String UFT_ACTION_SCOPE_ATTR = "Scope";
public static final String UFT_ACTION_KIND_VALUE = "16";
public static final String UFT_ACTION_TYPE_VALUE = "1";
public static final String UFT_ACTION_SCOPE_VALUE = "0";
public static UftTestDiscoveryResult doFullDiscovery(File root) {
return doFullDiscovery(root, TestingToolType.UFT);
}
public static UftTestDiscoveryResult doFullDiscovery(File root, TestingToolType testingToolType) {
logger.info("Full sync requested for: {}, path: {}, ", testingToolType, root.getAbsolutePath());
UftTestDiscoveryResult result = new UftTestDiscoveryResult();
scanFileSystemRecursively(root, root, result, testingToolType);
result.setFullScan(true);
result.setTestingToolType(testingToolType);
return result;
}
private static void scanFileSystemRecursively(File root, File dirPath, UftTestDiscoveryResult discoveryResult, TestingToolType testingToolType) {
if (dirPath.isDirectory() && SKIP_FOLDERS.contains(dirPath.getName())) {
return;
}
File[] paths = dirPath.isDirectory() ? dirPath.listFiles() : new File[]{dirPath};
//if it test folder - create new test, else drill down to subFolders
UftTestType testType = paths != null ? isUftTestFolder(paths) : UftTestType.None;
if (!testType.isNone()) {
// if UFT mode or (MBT mode and test type GUI)- API tests are currently not supported in MBT mode
if (!(TestingToolType.MBT.equals(testingToolType) && (UftTestType.API.equals(testType)))) {
AutomatedTest test = createAutomatedTest(root, dirPath, testType, testingToolType);
discoveryResult.getAllTests().add(test);
}
} else if (paths != null) {
for (File path : paths) {
if (path.isDirectory()) {
scanFileSystemRecursively(root, path, discoveryResult, testingToolType);
} else if (isUftDataTableFile(path.getName())) {
ScmResourceFile dataTable = createDataTable(root, path);
discoveryResult.getAllScmResourceFiles().add(dataTable);
}
}
}
}
public static ScmResourceFile createDataTable(File root, File path) {
ScmResourceFile resourceFile = new ScmResourceFile();
resourceFile.setName(path.getName());
resourceFile.setRelativePath(getRelativePath(root, path));
resourceFile.setOctaneStatus(OctaneStatus.NEW);
return resourceFile;
}
public static boolean isUftDataTableFile(String path) {
String loweredPath = path.toLowerCase();
return loweredPath.endsWith(XLSXExtention) || loweredPath.endsWith(XLSExtention);
}
public static UftTestType isUftTestFolder(File[] paths) {
if (paths == null) {
return UftTestType.None;
}
for (File path : paths) {
if (path.getName().endsWith(STFileExtention)) {
return UftTestType.API;
}
if (path.getName().endsWith(QTPFileExtention)) {
return UftTestType.GUI;
}
}
return UftTestType.None;
}
public static boolean isUftActionFile(String path) {
String loweredPath = path.toLowerCase();
return loweredPath.endsWith(RESOURCE_MTR_FILE);
}
public static boolean isTestMainFilePath(String path) {
return !getUftTestType(path).isNone();
}
public static UftTestType getUftTestType(String testMainFilePath) {
String lowerPath = testMainFilePath.toLowerCase();
if (lowerPath.endsWith(QTPFileExtention)) {
return UftTestType.GUI;
}
if (lowerPath.endsWith(STFileExtention) || lowerPath.endsWith(API_ACTIONS_FILE)) {
return UftTestType.API;
}
return UftTestType.None;
}
public static File getTestFolderForTestMainFile(String path) {
if (isTestMainFilePath(path)) {
File file = new File(path);
File parent = file.getParentFile();
return parent;
}
return null;
}
public static AutomatedTest createAutomatedTest(File root, File dirPath, UftTestType testType, TestingToolType testingToolType) {
AutomatedTest test = new AutomatedTest();
test.setName(dirPath.getName());
String relativePath = getRelativePath(root, dirPath);
String packageName = relativePath.length() != dirPath.getName().length() ? relativePath.substring(0, relativePath.length() - dirPath.getName().length() - 1) : "";
test.setPackage(packageName);
test.setExecutable(true);
test.setUftTestType(testType);
Document testDocument = getDocument(dirPath, testType);
String description = getTestDescription(testDocument, testType);
description = convertToHtmlFormatIfRequired(description);
test.setDescription(description);
test.setOctaneStatus(OctaneStatus.NEW);
// discover actions only for mbt testingToolType and gui tests
if (TestingToolType.MBT.equals(testingToolType) && UftTestType.GUI.equals(testType)) {
String actionPathPrefix = getActionPathPrefix(test, false);
test.setActions(parseActionsAndParameters(testDocument, actionPathPrefix, test.getName(), dirPath));
}
return test;
}
// in case a test was moved and we need the action path prefix before the move then set orgPath to true
protected static String getActionPathPrefix(AutomatedTest test, boolean orgPath) {
return getTestPathPrefix(test, orgPath) + "\\%s:%s";
}
// constructs a test path that contains only the test package and name
protected static String getTestPathPrefix(AutomatedTest test, boolean orgPath) {
String testPackage = orgPath ? test.getOldPackage() : test.getPackage();
String testName = orgPath ? test.getOldName() : test.getName();
return (SdkStringUtils.isEmpty(testPackage) ? "" : testPackage + "\\") + testName;
}
public static String convertToHtmlFormatIfRequired(String description) {
if (description == null || !description.contains("\n")) {
return description;
}
//aaa\nbbb = > aaa
bbb
String[] lines = description.split("\n");
StringBuilder sb = new StringBuilder(description.length() + lines.length * 10 + 30);
sb.append("");
for (String line : lines) {
sb.append("");
sb.append(line);
sb.append("
");
sb.append("\n");
}
sb.append("");
return sb.toString();
}
private static String getRelativePath(File root, File path) {
String testPath = path.getPath();
String rootPath = root.getPath();
String relativePath = testPath.replace(rootPath, "");
relativePath = SdkStringUtils.strip(relativePath, SdkConstants.FileSystem.WINDOWS_PATH_SPLITTER + SdkConstants.FileSystem.LINUX_PATH_SPLITTER);
//we want all paths will be in windows style, because tests are run in windows, therefore we replace all linux splitters (/) by windows one (\)
//http://stackoverflow.com/questions/23869613/how-to-replace-one-or-more-in-string-with-just
relativePath = relativePath.replaceAll(SdkConstants.FileSystem.LINUX_PATH_SPLITTER, SdkConstants.FileSystem.WINDOWS_PATH_SPLITTER + SdkConstants.FileSystem.WINDOWS_PATH_SPLITTER);//str.replaceAll("/", "\\\\");
return relativePath;
}
/**
* Extract test description from UFT GUI test.
* Note : UFT API test doesn't contain description
*
* @param testDocument document created from the test path
* @param testType GUI or API
* @return test description
*/
public static String getTestDescription(Document testDocument, UftTestType testType) {
if (Objects.isNull(testDocument) || testType.isNone()) {
return null;
}
String description;
if (UftTestType.GUI.equals(testType)) {
description = getTestDescriptionFromGuiTest(testDocument);
} else {
description = getTestDescriptionFromAPITest(testDocument);
}
if (description != null) {
description = description.trim();
}
return description;
}
private static String getTestDescriptionFromAPITest(Document document) {
//Actions.xml
//
//
//
NodeList actions = document.getElementsByTagName("Action");
for (int temp = 0; temp < actions.getLength(); temp++) {
Node nNode = actions.item(temp);
NamedNodeMap attributes = nNode.getAttributes();
Node internalNameAttr = attributes.getNamedItem("internalName");
if (internalNameAttr != null && "MainAction".equals(internalNameAttr.getNodeValue())) {
return attributes.getNamedItem("description").getNodeValue();
}
}
return null;
}
private static String getTestDescriptionFromGuiTest(Document document) {
return document.getElementsByTagName("Description").item(0).getTextContent();
}
public static String extractXmlContentFromTspFile(InputStream stream) throws IOException {
POIFSFileSystem poiFS = new POIFSFileSystem(stream);
DirectoryNode root = poiFS.getRoot();
String xmlData = "";
for (Entry entry : root) {
String name = entry.getName();
if ("ComponentInfo".equals(name)) {
if (entry instanceof DirectoryEntry) {
System.out.println(entry);
} else if (entry instanceof DocumentEntry) {
byte[] content = new byte[((DocumentEntry) entry).getSize()];
int readBytes = poiFS.createDocumentInputStream("ComponentInfo").read(content);
if (readBytes < content.length) {
// [YG] probably should handle this case and continue to read
logger.warn("expected to read " + content.length + " bytes, but read and stopped after " + readBytes);
}
String fromUnicodeLE = StringUtil.getFromUnicodeLE(content);
xmlData = fromUnicodeLE.substring(fromUnicodeLE.indexOf('<')).replaceAll("\u0000", "");
}
}
}
return xmlData;
}
public static Document getDocument(File dirPath, UftTestType testType) {
if (Objects.isNull(dirPath) || !dirPath.exists()) {
logger.error("test path is expected to be non null or exist");
return null;
}
try {
if (UftTestType.GUI.equals(testType)) {
return getGuiTestDocument(dirPath);
} else {
return getApiTestDocument(dirPath);
}
} catch (Exception e) {
logger.error("Failed to create document for path: {}, test type: {}", dirPath.getPath(), testType.name(), e);
return null;
}
}
private static Document getGuiTestDocument(File dirPath) throws IOException, ParserConfigurationException, SAXException {
File tspTestFile = getFileIfExist(dirPath, GUI_TEST_FILE);
if (tspTestFile == null) {
return null;
}
InputStream is = new FileInputStream(tspTestFile);
String xmlContent = extractXmlContentFromTspFile(is);
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
return documentBuilder.parse(new InputSource(new StringReader(xmlContent)));
}
private static Document getApiTestDocument(File dirPath) throws ParserConfigurationException, IOException, SAXException {
File actionsFile = getFileIfExist(dirPath, API_ACTIONS_FILE);
if (actionsFile == null) {
return null;
}
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
return dBuilder.parse(actionsFile);
}
private static File getFileIfExist(File dirPath, String fileName) {
if(SystemUtils.IS_OS_LINUX) {
Optional optionalFile = Arrays.stream(dirPath.listFiles()).filter(file -> file.getName().equalsIgnoreCase(fileName)).findFirst();
if(optionalFile.isPresent()) {
return optionalFile.get();
} else {
logger.warn("File {} does not exist", dirPath.getAbsolutePath() + File.separator + fileName);
}
} else {
File file = new File(dirPath, fileName);
if(file.exists()) {
return file;
} else {
logger.warn("File {} does not exist", file.getAbsolutePath());
}
}
return null;
}
private static List parseActionsAndParameters(Document document, String actionPathPrefix, String testName, File dirPath) {
List actions = new ArrayList<>();
if (Objects.isNull(document)) {
logger.warn("received null gui test document, actions will not be parsed");
} else {
Map actionMap = parseActionComponents(document, testName);
fillActionsLogicalName(document, actionMap, actionPathPrefix);
actions.addAll(actionMap.values());
try {
readParameters(dirPath, actionMap);
} catch (IOException | ParserConfigurationException | SAXException e) {
logger.error("failed to parse action's parameters", e);
}
}
return actions;
}
private static Map parseActionComponents(Document document, String testName) {
Map actionMap = new HashMap<>();
NodeList componentNodes = document.getElementsByTagName(UFT_COMPONENT_NODE_NAME);
Node componentNode;
for (int i = 0; i < componentNodes.getLength(); i++) {
componentNode = componentNodes.item(i);
String actionName = componentNode.getTextContent();
if (!actionName.equalsIgnoreCase(ACTION_0)) { // filter action0
UftTestAction action = new UftTestAction();
action.setName(actionName);
action.setTestName(testName);
actionMap.put(actionName, action);
}
}
return actionMap;
}
private static void fillActionsLogicalName(Document document, Map actionMap, String actionPathPrefix) {
NodeList dependencyNodes = document.getElementsByTagName(UFT_DEPENDENCY_NODE_NAME);
Node dependencyNode;
NamedNodeMap attributes;
for (int i = 0; i < dependencyNodes.getLength(); i++) {
dependencyNode = dependencyNodes.item(i);
attributes = dependencyNode.getAttributes();
String type = attributes.getNamedItem(UFT_ACTION_TYPE_ATTR).getNodeValue();
String kind = attributes.getNamedItem(UFT_ACTION_KIND_ATTR).getNodeValue();
String scope = attributes.getNamedItem(UFT_ACTION_SCOPE_ATTR).getNodeValue();
String logicalName = attributes.getNamedItem(UFT_ACTION_LOGICAL_ATTR).getNodeValue();
if (type.equals(UFT_ACTION_TYPE_VALUE) && kind.equals(UFT_ACTION_KIND_VALUE) && scope.equals(UFT_ACTION_SCOPE_VALUE) && SdkStringUtils.isNotEmpty(logicalName)) {
String dependencyStr = dependencyNode.getTextContent();
String actionName = dependencyStr.substring(0, dependencyStr.indexOf("\\"));
if (!actionName.equalsIgnoreCase(ACTION_0)) { // action0 is not relevant
UftTestAction action = actionMap.get(actionName);
action.setLogicalName(logicalName);
setActionPath(action, actionPathPrefix);
}
}
}
}
// repository path is of the following format:
// \\:
private static void setActionPath(UftTestAction action, String actionPathPrefix) {
String actionName = SdkStringUtils.isEmpty(action.getLogicalName()) ? action.getName() : action.getLogicalName();
action.setRepositoryPath(String.format(actionPathPrefix, action.getName(), actionName));
}
private static void readParameters(File testDirPath, Map actionMap) throws IOException, ParserConfigurationException, SAXException {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
for (Map.Entry entry : actionMap.entrySet()) {
String actionName = entry.getKey();
File actionFolder = new File(testDirPath, actionName);
if (actionFolder.exists()) {
File resourceMtrFile = getFileIfExist(actionFolder, RESOURCE_MTR_FILE);
if (resourceMtrFile != null) {
parseActionMtrFile(resourceMtrFile, documentBuilder, entry.getValue());
} else {
logger.warn("resource.mtr file for action {} does not exist", actionName);
}
} else {
entry.getValue().setParameters(Collections.emptyList());
logger.warn("folder for action {} does not exist", actionName);
}
}
}
private static void parseActionMtrFile(File resourceMtrFile, DocumentBuilder documentBuilder, UftTestAction action) throws IOException, SAXException {
List parameters = new ArrayList<>();
InputStream is = new FileInputStream(resourceMtrFile);
String xmlContent = extractXmlContentFromTspFile(is);
Document document = documentBuilder.parse(new InputSource(new StringReader(xmlContent)));
NodeList argumentsCollectionElement = document.getElementsByTagName(UFT_PARAM_ARGUMENTS_COLLECTION_NODE_NAME);
if (argumentsCollectionElement.getLength() > 0) {
Node argumentsCollectionItem = argumentsCollectionElement.item(0);
NodeList childArgumentElements = argumentsCollectionItem.getChildNodes();
for (int i = 0; i < childArgumentElements.getLength(); i++) {
Element argumentElement = (Element) childArgumentElements.item(i);
UftTestParameter parameter = new UftTestParameter();
parameter.setName(argumentElement.getElementsByTagName(UFT_PARAM_ARG_NAME_NODE_NAME).item(0).getTextContent());
parameter.setDirection(UftParameterDirection.get(Integer.parseInt(argumentElement.getElementsByTagName("ArgDirection").item(0).getTextContent())));
Node defaultValueNode = argumentElement.getElementsByTagName(UFT_PARAM_ARG_DEFAULT_VALUE_NODE_NAME).item(0);
if (null != defaultValueNode) {
parameter.setDefaultValue(defaultValueNode.getTextContent());
}
parameters.add(parameter);
}
}
action.setParameters(parameters);
String description = document.getElementsByTagName(UFT_ACTION_DESCRIPTION_NODE_NAME).item(0).getTextContent();
action.setDescription(description);
}
}