/* +----------------------------------------------------------------------+ | Class: CLauncher | | | | Developper: Eric Gavaldo (eric.gavaldo@xqual.com) | | Version: 1.5 | | License: LGPL (http://www.gnu.org/licenses/lgpl.html) | +----------------------------------------------------------------------+ */ package com.xqual.xlauncher.sahi; import java.io.File; import java.io.IOException; import java.util.Vector; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.xqual.xagent.launcher.CExecutionStep; import com.xqual.xagent.launcher.CLauncher; import com.xqual.xagent.launcher.CReturnStatus; import com.xqual.xagent.launcher.runner.CRunner; import com.xqual.xagent.launcher.runner.IRunner; import com.xqual.xcommon.CAttribute; import com.xqual.xcommon.CXmlDocumentFactory; import com.xqual.xcommon.IConstantsResults; import com.xqual.xcommon.type.CParam; import com.xqual.xcommon.type.CParamParsingException; import com.xqual.xcommon.utils.CFileUtils; import com.xqual.xcommon.utils.CUtils; /** * The CLauncherImpl implementation of ILauncher for AutoIt. * @author egavaldo */ public class CLauncherImpl extends CLauncher implements IConstantsResults { // +==============================================================+ // | Attributes | // +==============================================================+ static final String TRACE_HEADER = "{sahi } "; // parameters impacting executing at run time set by the test operator private boolean sahiPro; private String testRootPath; private String javaInstallPath; private String sahiInstallPath; private String baseUrl; private String browser; private String browserExePath; private String browserProcName; private String browserOption; private String browserProxyEnable; private String browserProxyDisable; private File workingDir; private static final String JAVA_INTERPRETER_EXE = CUtils.getExecutableName("java"); private static final String TESTRUNNER_JAR = "../../lib/ant-sahi.jar"; private static final String TESTRUNNER_CLASSPATH = "net.sf.sahi.test.TestRunner"; private static final String PROXYRUNNER_CLASSPATH = "net.sf.sahi.Proxy"; private static final String CLASSPATH_SAHI_FREE = "../../lib/sahi.jar;../../extlib/rhino/js.jar;../../extlib/apc/commons-codec-1.3.jar"; private static final String CLASSPATH_SAHI_PRO = "../../lib/sahi.jar;../../extlib/rhino/js.jar;../../extlib/apc/commons-codec-1.3.jar;../../extlib/db/h2-1.2.134.jar;../../extlib/license/truelicense.jar;../../extlib/license/truexml.jar"; private static final String TRACES_PROXY = "proxy_traces.txt"; private static final String TRACES_TESTCASE = "testcase_traces.txt"; private CRunner proxyServer; private CRunner sahiTestcaseRunner; private String testAdditionalInfo; private String testcaseAdditionalInfo; // +==============================================================+ // | Constructors | // +==============================================================+ public CLauncherImpl() { super(TRACE_HEADER); } // +==============================================================+ // | Methods | // +==============================================================+ @Override public CReturnStatus initialize(int sutId, String sutName, String sutVersion) { setSutDetails(sutId, sutName, sutVersion); // check the configuration sent by the manager printConfiguration(); Vector executionSteps = new Vector(); try { // retrieve the parameters we need testRootPath = getStringParamValue("General", "Test root path"); // i.e. Y:/Externals/sahi/userdata/scripts sahiPro = getBooleanParamValue("Sahi", "Sahi Pro"); // i.e. true javaInstallPath = getStringParamValue("Sahi", "Java install path"); // i.e. C:/Program Files/Java/jdk1.6.0_17 sahiInstallPath = getStringParamValue("Sahi", "Sahi install path"); // i.e. Y:/Externals/sahi baseUrl = getStringParamValue("Sahi", "Url"); // i.e. http://www.google.com browser = getStringParamValue("Sahi", "Browser"); // i.e. Firefox browserExePath = getStringParamValue(browser, "Executable path"); // i.e. C:/Program Files/Mozilla Firefox/firefox.exe browserProcName = getStringParamValue(browser, "Process name"); // i.e. firefox.exe browserOption = getStringParamValue(browser, "Option"); // i.e. -profile Y:/externals/sahi/userdata/browser/ff/profiles/sahi$threadNo -no-remote browserProxyEnable = getStringParamValue(browser, "Proxy enabling tool"); // i.e. browserProxyDisable = getStringParamValue(browser, "Proxy disabling tool"); // i.e. workingDir = new File(sahiInstallPath + "/userdata/bin"); } catch (CParamParsingException e) { traceln(LOG_PRIORITY_SEVERE, "parsing error during initialization"); executionSteps.add(new CExecutionStep(RESULT_FAILURE, "Exception during initialize: " + e.getMessage())); return new CReturnStatus(RESULT_FAILURE, executionSteps); } // delete all previous session's reports so that we don't mix things up // once the reports are named as "xxx_2010-12-31 23:59_xxx" in Sahi we could remove this hack and use the date and time to retrieve the good one :/ if (!deleteAllHtmlReportFiles()) { traceln(LOG_PRIORITY_SEVERE, "couldn't delete the former reports !"); } return new CReturnStatus(RESULT_SUCCESS, executionSteps); } @Override public CReturnStatus preRun(int testId, String testPath, String testName, Vector attributes, String additionalInfo) { traceln(LOG_PRIORITY_INFO, "preRun testId=" + testId + " testPath=" + testPath + " [" + testName + "]..."); testAdditionalInfo = additionalInfo; deleteAndTouchFile(new File(TRACES_PROXY)); short result = RESULT_SUCCESS; // +------------------------------------+ // | Start the proxy // +------------------------------------+ /* Running a bat file is really problematic. To run it correctly we need to use "cmd /c /start /min xxx.bat". But then it is impossible to kill it later on as it will kill only the command interpreter but not what is being executed (java proxy program) File proxyBatchFile = new File(CFileUtils.quoteFilePath(sahiInstallPath + "/userdata/bin/" + PROXY_BATCH)); proxyServer = new CRunner("[Sahi proxy] ", "cmd /c /start /min" + CFileUtils.quoteFilePath(sahiInstallPath + "/userdata/bin/" + PROXY_BATCH), // batch files are NOT executable workingDir); So we need to run directly the java progam proxyServer = new CRunner("[Sahi proxy] ", CFileUtils.quoteFilePath(javaInstallPath + "/bin/" + JAVA_INTERPRETER_EXE) + " -classpath ../../lib/sahi.jar;../../extlib/rhino/js.jar;../../extlib/apc/commons-codec-1.3.jar net.sf.sahi.Proxy ../.. ..", workingDir); // important we MUST be in userdata/bin so that the relative path above are ok result = proxyServer.requestAction(IRunner.START_PROCESS, IRunner.DO_NOT_WAIT_END_OF_EXECUTION, new File("C:/sahi_proxy_traces.txt")); but if we run the java program with DO_NOT_WAIT_END_OF_EXECUTION, the proxy cannot kill the testrunner So we need to execute this in a separate thread */ new Thread() { @Override public void run() { proxyServer = new CRunner("[Sahi proxy] ", CFileUtils.quoteFilePath(javaInstallPath + "/bin/" + JAVA_INTERPRETER_EXE) + " " + "-cp " + (sahiPro ? CLASSPATH_SAHI_PRO : CLASSPATH_SAHI_FREE) + " " + PROXYRUNNER_CLASSPATH + " ../.. ..", workingDir); // important we MUST be in userdata/bin so that the relative path above are ok proxyServer.requestAction(IRunner.START_PROCESS, IRunner.WAIT_END_OF_EXECUTION, new File(TRACES_PROXY)); } }.start(); CUtils.sleep(2000); traceln(LOG_PRIORITY_INFO, "preRun: proxy started"); // +------------------------------------+ // | Set the browser to use the proxy // +------------------------------------+ if (browserProxyEnable.length()>0) { CRunner proxyEnablingTool = new CRunner("[Proxy enabling tool] ", browserProxyEnable, // do NOT quote the whole thing (only the path to cscript might be quoted) workingDir); result = proxyEnablingTool.requestAction(IRunner.START_PROCESS, IRunner.WAIT_END_OF_EXECUTION); if (result != RESULT_SUCCESS) { traceln(LOG_PRIORITY_SEVERE, "enabling proxy on browser failed!"); return new CReturnStatus(RESULT_FAILURE); } } return new CReturnStatus(result); } @Override public CReturnStatus run(int testId, String testPath, String testName, int testcaseId, int testcaseIndex, String testcaseName, Vector params, String additionalInfo) { traceln(LOG_PRIORITY_INFO, "run testId=" + testId + " testPath=" + testRootPath + "/" + testPath + "/" + testName + " testcaseIndex=" + testcaseIndex + "..."); testcaseAdditionalInfo = additionalInfo; Vector executionSteps = new Vector(); deleteAndTouchFile(new File(TRACES_TESTCASE)); if (!deleteAllHtmlReportFiles()) { traceln(LOG_PRIORITY_SEVERE, "couldn't delete the former reports !"); } // +------------------------------------+ // | Interpret the script // +------------------------------------+ String url=""; if (testcaseAdditionalInfo.length()>0) { url = baseUrl + "/" + testcaseAdditionalInfo; } else if (testAdditionalInfo.length()>0) { url = baseUrl + "/" + testAdditionalInfo; } else { url = baseUrl; } sahiTestcaseRunner = new CRunner("[" + testId + "] "+ testPath + ":" + testName + "." + testcaseIndex, CFileUtils.quoteFilePath(javaInstallPath + "/bin/" + JAVA_INTERPRETER_EXE) + " " + "-cp " + TESTRUNNER_JAR + " " + TESTRUNNER_CLASSPATH + " " + CFileUtils.quoteFilePath(testRootPath + "/" + testPath + "/" + testName + ".sah") + " " + CFileUtils.quoteFilePath(browserExePath) + " " + url + " default localhost 9999 3 " + browserProcName + " " + "\"" + browserOption.replaceAll("\\(dollar\\)", "\\$") + "\"", // the firefox option includes a '$' but XStudio does not authorize users to enter such character :( workingDir); short result = sahiTestcaseRunner.requestAction(IRunner.START_PROCESS, IRunner.WAIT_END_OF_EXECUTION, new File(TRACES_TESTCASE)); if (result == RESULT_FAILURE) { executionSteps.add(new CExecutionStep(RESULT_FAILURE, "script interpretation failed")); return new CReturnStatus(RESULT_FAILURE, executionSteps); } File htmlReportFile = retrieveHtmlReportFile(testName); if (htmlReportFile != null && htmlReportFile.exists()) { traceln(LOG_PRIORITY_INFO, "report exists"); addAttachment(htmlReportFile); addAttachment(new File(TRACES_PROXY)); addAttachment(new File(TRACES_TESTCASE)); return parseResultFile(htmlReportFile, executionSteps); } else { traceln(LOG_PRIORITY_SEVERE, "report does not exist!"); executionSteps.add(new CExecutionStep(RESULT_FAILURE, "couldn't find Sahi report to parse!")); return new CReturnStatus(RESULT_FAILURE, executionSteps); } } @Override public CReturnStatus postRun(int testId, String testPath, String testName) { traceln(LOG_PRIORITY_INFO, "postRun testId=" + testId + " testPath=" + testPath + ":" + testName + "..."); // +------------------------------------+ // | Set the browser to not use proxy // +------------------------------------+ if (browserProxyDisable.length()>0) { CRunner proxyDisablingTool = new CRunner("[Proxy disabling tool] ", browserProxyDisable, // do NOT quote the whole thing (only the path to cscript might be quoted) workingDir); short result = proxyDisablingTool.requestAction(IRunner.START_PROCESS, IRunner.WAIT_END_OF_EXECUTION); if (result != RESULT_SUCCESS) { traceln(LOG_PRIORITY_SEVERE, "disabling proxy on browser failed!"); return new CReturnStatus(RESULT_FAILURE); } } // +------------------------------------+ // | Stop the proxy // +------------------------------------+ short result = proxyServer.requestAction(IRunner.STOP_PROCESS); proxyServer.killProcess(); //the file should be already deleted by the agent's thread deleteFile(new File(TRACES_PROXY)); deleteFile(new File(TRACES_TESTCASE)); return new CReturnStatus(RESULT_SUCCESS); } @Override public CReturnStatus terminate() { // the proxy server should be already stopped; but let's try to kill it anyway try { short result = proxyServer.requestAction(IRunner.STOP_PROCESS); proxyServer.killProcess(); } catch (Exception e) { } return new CReturnStatus(RESULT_SUCCESS); } // +--------------------------+ // ¦ Utilities ¦ // +--------------------------+ private void deleteFile(File input) { if (input.exists()) input.delete(); } private void deleteAndTouchFile(File input) { if (input.exists()) input.delete(); try { input.createNewFile(); } catch (IOException e) { traceln(LOG_PRIORITY_SEVERE, "Could not create file \"" + input + "\"..."); } } private File retrieveHtmlReportFile(String testName) { File logsFolder = new File(sahiInstallPath + "/userdata/logs/playback"); if (!logsFolder.exists()) { traceln(LOG_PRIORITY_WARNING, "Could not find \"" + logsFolder + "\"..."); return null; } traceln(LOG_PRIORITY_INFO, "searching for report file in \"" + logsFolder + "\"..."); File reportFile = retrieveHtmlReportFile(logsFolder, testName); if (reportFile != null) { traceln(LOG_PRIORITY_INFO, "report found in root folder: \"" + reportFile + "\"..."); return reportFile; } traceln(LOG_PRIORITY_INFO, "searching for sub-folders possibly hosting report file..."); File[] testFolders = logsFolder.listFiles(); for (int i=0; i \"" + testFolder + "\"..."); if (testFolder.isDirectory()) { reportFile = retrieveHtmlReportFile(testFolder, testName); if (reportFile != null) { traceln(LOG_PRIORITY_INFO, "report found in a subfolder: \"" + reportFile + "\"..."); return reportFile; } } } traceln(LOG_PRIORITY_SEVERE, "no test report!"); return null; } private File retrieveHtmlReportFile(File parentFolder, String testName) { File output = null; File[] testReports = parentFolder.listFiles(); for (int j=0; j " + testReport); if (testReport.getName().indexOf(testName) >=0) { // name includes traceln(LOG_PRIORITY_INFO, "!!!! TEST REPORT FOUND !!! " + testReport); return testReport; } System.err.println("- - - > " + testReport); if (testReport.getName().startsWith("index")) { // name starts with "index" traceln(LOG_PRIORITY_INFO, "!!!! BACKUP TEST REPORT FOUND !!! " + testReport); output = testReport; // backup solution do not return immediately but only if nothing else is found } } } return output; } private boolean deleteAllHtmlReportFiles() { System.err.println("- - - > deleting reports..."); File logsFolder = new File(sahiInstallPath + "/userdata/logs/playback"); if (!logsFolder.exists()) { traceln(LOG_PRIORITY_WARNING, "Could not find \"" + logsFolder + "\"..."); return false; } File[] testFolders = logsFolder.listFiles(); for (int i=0; i deleting reports and potential containers " + testFolder + "..."); if (!CFileUtils.deleteFile(testFolder)) { // delete the complete folder or the file traceln(LOG_PRIORITY_WARNING, "Could not delete \"" + testFolder + "\"..."); return false; } } return true; } private CReturnStatus parseResultFile(File htmlReportFile, Vector executionSteps) { executionSteps.add(new CExecutionStep(RESULT_SUCCESS, "Parsing the reports...")); //trick to workaround a problem with the format generated (invalid) String xmlContent = CFileUtils.FileToString(htmlReportFile) .replaceAll("
", "") .replaceAll("&", "&"); // there are some '&' characters in some traces in the xml so that MAY corrupt the XML validity //trick to workaround an inconsistency between Sahi and SahiPro String reportContent = (xmlContent.indexOf("xml version")>=0) ? xmlContent : "" + xmlContent + ""; //trick to workaround an issue in the format generated by Sahi (what's inside the ", "]]>"); System.out.println("report content = " + reportContent); boolean atLeastOneFailure = false; Document xmlDocument = CXmlDocumentFactory.createXMLDoc(reportContent); System.err.println("xmlDocument = " + xmlDocument); NodeList nodeList = CXmlDocumentFactory.getNodeObjListFromXPath(xmlDocument, "/root/div"); if (nodeList != null && nodeList.getLength() > 0) { System.out.println("div nodes length = " + nodeList.getLength()); for (int i=0; i RESULT_SUCCESS"); executionSteps.add(new CExecutionStep(timeStamp2, RESULT_SUCCESS, log)); } else if (classAttr.equals("ERROR") || classAttr.equals("FAILURE")) { System.out.println(" ===> RESULT_FAILURE"); executionSteps.add(new CExecutionStep(timeStamp2, RESULT_FAILURE, log)); atLeastOneFailure = true; } else { System.out.println(" ===> RESULT_UNKNOWN"); executionSteps.add(new CExecutionStep(timeStamp2, RESULT_UNKNOWN, log)); } } catch (Exception e) { e.printStackTrace(); continue; } } } else { atLeastOneFailure = true; executionSteps.add(new CExecutionStep(RESULT_FAILURE, "couldn't parse the results xml")); } return new CReturnStatus(atLeastOneFailure ? RESULT_FAILURE : RESULT_SUCCESS, executionSteps); } public static void main(String[] args) { File inputFile = new File("C:/Documents and Settings/egavaldo/Desktop/Nouveau1.xml"); Vector executionSteps = new Vector(); CLauncherImpl myLauncher = new CLauncherImpl(); myLauncher.parseResultFile(inputFile, executionSteps); } }