/* +----------------------------------------------------------------------+ | Class: CLauncher | | | | Developper: Eric Gavaldo (eric.gavaldo@xqual.com) | | Version: 1.0 | +----------------------------------------------------------------------+ */ 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.CParamParsingException; 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.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 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 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); setDefaultTestcaseMustBeCreated(true); // if there is not testcase, let the systems create a default one // 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 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 eexecuted (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 ../../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 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] ", CFileUtils.quoteFilePath(browserProxyEnable), 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 testcaseIndex, String testcaseName, 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)); // +------------------------------------+ // | 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); addAttachment(htmlReportFile); addAttachment(new File(TRACES_PROXY)); addAttachment(new File(TRACES_TESTCASE)); return parseResultFile(htmlReportFile, 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] ", CFileUtils.quoteFilePath(browserProxyEnable), 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 short result = proxyServer.requestAction(IRunner.STOP_PROCESS); proxyServer.killProcess(); 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; } File[] testFolders = logsFolder.listFiles(); for (int i=0; i executionSteps) { executionSteps.add(new CExecutionStep(RESULT_SUCCESS, "Parsing the reports...")); String reportContent = "" + CFileUtils.FileToString(htmlReportFile) .replaceAll("
", "") .replaceAll("&n=", "&n=") + ""; boolean atLeastOneFailure = false; Document xmlDocument = CXmlDocumentFactory.createXMLDoc(reportContent); NodeList nodeList = CXmlDocumentFactory.getNodeObjListFromXPath(xmlDocument, "//div"); for (int i=0; i