/** * Sahi - Web Automation and Test Tool * * Copyright 2006 V Narayan Raman * * 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 net.sf.sahi.test; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.ConnectException; import java.net.URL; import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import net.sf.sahi.ant.CreateIssue; import net.sf.sahi.ant.Report; import net.sf.sahi.util.Utils; /** * The TestRunner class invokes Sahi scripts from Java. * * Sample usage * * <p><blockquote><pre> * String browser = "C:\\Program Files\\Mozilla Firefox\\firefox.exe"; * String base = "http://gramam/demo/"; * String sahiHost = "localhost"; * String port = "9999"; * String threads = "1"; * String browserOption = "-profile $userDir/browser/ff/profiles/sahi$threadNo -no-remote"; * String browserProcessName = "firefox.exe"; * String logDir = "D:/temp/logs/"; // relative paths will be resolved relative to userdata dir. * TestRunner testRunner = * new TestRunner(suiteName, browser, base, sahiHost, * port, threads, browserOption, browserProcessName); * testRunner.addReport(new Report("html", logDir)); * String status = testRunner.execute(); * </pre></blockquote> * * @author narayan * */ public class TestRunner { private final String suiteName; private final String browser; private final String base; protected final String sahiHost; protected final String port; private final String threads; protected String sessionId = null; private String browserOption; private CreateIssue createIssue; private List<Report> listReport; private String browserProcessName; private String extraInfo; private String initJS; private String browserType; private boolean isSingleSession; public static void main(String[] args) { try { if (args.length == 0) { help(); } if (mainCLIParams(args)) return; String suiteName = args[0]; String base = getBase(args); String browser = getBrowser(args); String logDir = args[3]; if ("default".equalsIgnoreCase(logDir)) { logDir = ""; } String sahiHost = args[4]; String port = args[5]; String threads = args[6]; String browserOption = ""; String browserProcessName = args[7]; if (args.length == 9) { browserOption = args[8]; } TestRunner testRunner = new TestRunner(suiteName, browser, base, sahiHost, port, threads, browserOption, browserProcessName); testRunner.addReport(new Report("html", logDir)); String status = testRunner.execute(); System.out.println("Status:" + status); } catch (ConnectException ce) { System.err.println(ce.getMessage()); System.err.println("Could not connect to Sahi Proxy.\nVerify that the Sahi Proxy is running on the specified host and port."); help(); } catch (Exception e) { e.printStackTrace(); help(); } } public static boolean mainCLIParams(String[] args) throws IOException, InterruptedException { final HashMap<String, String> map = Utils.parseCLInput(args); String testName = getTestName(map); if (testName == null) return false; String host = map.get("host"); String port2 = map.get("port"); if (host == null) host = "localhost"; if (port2 == null) port2 = "9999"; String threads = map.get("threads"); if (threads == null) threads = "1"; final String browserType2 = map.get("browserType"); final TestRunner testRunner; if (browserType2 != null){ testRunner = new TestRunner(testName, browserType2, map.get("baseURL"), host, port2, threads); } else { testRunner = new TestRunner(testName, map.get("browser"), map.get("baseURL"), host, port2, threads, map.get("browserOption"), map.get("browserProcessName")); } if ("true".equals(map.get("htmlLog"))) { String logDir = map.get("htmlLogDir"); if (logDir == null || "default".equals(logDir)) logDir = ""; testRunner.addReport(new Report("html", logDir)); } if ("true".equals(map.get("tm6Log"))) { String logDir = map.get("tm6LogDir"); if (logDir == null || "default".equals(logDir)) logDir = ""; testRunner.addReport(new Report("tm6", logDir)); } if ("true".equals(map.get("junitLog"))) { String logDir = map.get("junitLogDir"); if (logDir == null || "default".equals(logDir)) logDir = ""; testRunner.addReport(new Report("junit", logDir)); } if ("true".equals(map.get("consoleLog"))) { testRunner.addReport(new Report("console", "true")); } if (map.get("initJS") != null) { testRunner.setInitJS(map.get("initJS")); } if (map.get("extraInfo") != null) { testRunner.setExtraInfo(map.get("extraInfo")); } if (map.get("useSingleSession") != null) { testRunner.setIsSingleSession("true".equals(map.get("useSingleSession"))); } System.out.println(testRunner.execute()); return true; } public void setIsSingleSession(boolean isSingleSession) { this.isSingleSession = isSingleSession; } private static String getTestName(final HashMap<String, String> map) { String testName = map.get("test"); if (testName == null) testName = map.get("suite"); return testName; } private static String guessBrowserProcessName(String browser2) { browser2 = browser2.replace('\\', '/'); int lastIx = browser2.lastIndexOf('/'); String executable = ""; if (lastIx >= 0){ executable = browser2.substring(lastIx + 1); } return executable; } protected static void help() { System.out.println("------------------------"); System.out.println("Usage: java -cp /path/to/sahi-test-runner.jar net.sf.sahi.test.TestRunner -test <test_or_suite_name> -browserType <browser_type> -baseURL <start_url>"); System.out.println("--- More options ---"); System.out.println(" -test \t\t\tpath to test or suite"); System.out.println(" -baseURL \t\tbaseURL for all tests"); System.out.println(" -threads \t\tno. of browser instances to run in parallel. Default is 1"); System.out.println(" -browserType \t\tbrowserType as specified in sahi/userdata/config/browser_types.xml"); System.out.println(" -browser \t\tfull browser to browser. Ignored if browserType specified."); System.out.println(" -browserProcessName \tbrowser process name used to find the pid when using ps or tasklist commands. Ignored if browserType specified."); System.out.println(" -browserOption \tOptions to be passed to browser. Ignored if browserType specified."); System.out.println(" -junitLog \t\ttrue or false. Enable or disable junit logs"); System.out.println(" -junitLogDir \t\tpath to junit log dir. If not specified, uses default location in userdata/logs"); System.out.println(" -htmlLog \t\ttrue or false. Enable or disable html logs"); System.out.println(" -htmlLogDir \t\tpath to html log dir. If not specified, uses default location in userdata/logs"); System.out.println(" -consoleLog \t\ttrue or false. Enable or disable console logs"); System.out.println(" -initJS \t\tAny javascript which would be executed before every script"); System.out.println(" -useSingleSession \ttrue or false. Execute all scripts sequentially in a single browser session. Default is false."); System.out.println(" -extraInfo \t\tAny extra info that may be accessed using _extraInfo()"); System.out.println("--- OR ---"); System.out.println("Usage: java -cp /path/to/sahi-test-runner.jar net.sf.sahi.test.TestRunner <test_or_suite_name> <browser_executable> <start_url> <log_dir> <sahi_host> <sahi_port> <number_of_threads> <browser_executable> [<browser_option>]"); System.out.println("Set log_dir to \"default\" to log to the default log dir"); System.out.println("Set number_of_threads to a number which is compatible with your machine CPU and RAM."); System.out.println("Look at http://sahi.co.in/w/Running+multiple+tests+in+batch+mode for details on browser options for various browsers."); System.out.println("------------------------"); System.exit(-1); } public void addReport(Report report) { if (listReport == null) { listReport = new ArrayList<Report>(); } listReport.add(report); } /** * TestRunner constructor which takes in a browser, browserExecutable and browserOptions * This does not use sahi/userdata/config/browser_types.xml * * @param suiteName * @param browser specifies the full path to browser executable * @param base * @param sahiHost * @param port * @param threads * @param browserOption specifies options that may be passed to the browser * @param browserProcessName specifies the name that appears when tasklist or ps is run. eg. firefox.exe */ public TestRunner(String suiteName, String browser, String base, String sahiHost, String port, String threads, String browserOption, String browserProcessName) { this.suiteName = suiteName; this.browser = browser; this.base = base; this.sahiHost = sahiHost; this.port = port; this.threads = threads; this.browserOption = browserOption; this.browserProcessName = browserProcessName; if (browserProcessName == null){ this.browserProcessName = guessBrowserProcessName(browser); } System.out.println(this.toString()); } /** * TestRunner constructor which takes in a browserType. * Browser Types are defined in sahi/userdata/config/browser_types.xml * Also assumes sahiHost=localhost and port=9999 * * @param suiteName * @param browserType * @param base * @param threads */ public TestRunner(String suiteName, String browserType, String base, String threads) { this(suiteName, browserType, base, "localhost", "9999", threads); } /** * TestRunner constructor which takes in a browserType. * Browser Types are defined in sahi/userdata/config/browser_types.xml * * * @param suiteName * @param browserType * @param base * @param sahiHost * @param port * @param threads */ public TestRunner(String suiteName, String browserType, String base, String sahiHost, String port, String threads) { this.suiteName = suiteName; this.browser = null; this.browserType = browserType; this.base = Utils.replaceLocalhostWithMachineName(base); this.sahiHost = sahiHost; this.port = port; this.threads = threads; System.out.println(this.toString()); } public TestRunner(String suiteName, String browser, String base, String sahiHost, String port, String threads, String browserOption, String browserProcessName, List<Report> listReporter, CreateIssue createIssue) { this(suiteName, browser, base, sahiHost, port, threads, browserOption, browserProcessName); this.listReport = listReporter; this.createIssue = createIssue; } public TestRunner(String suiteName, String browserType, String base, String sahiHost, String port, String threads, List<Report> listReporter, CreateIssue createIssue) { this(suiteName, browserType, base, sahiHost, port, threads); this.listReport = listReporter; this.createIssue = createIssue; } public TestRunner(String suiteName, String browserType, String base, String sahiHost, String port, String threads, String initJS) { this(suiteName, browserType, base, sahiHost, port, threads); this.initJS = initJS; } public String execute() throws IOException, InterruptedException { return execute("start"); } public String execute(String command) throws IOException, InterruptedException { if (this.sessionId == null) this.sessionId = Utils.generateId(); String urlStr = buildURL(command); try { Thread thread = new Thread(new ShutDownHook(sahiHost, port, sessionId)); Runtime.getRuntime().addShutdownHook(thread); } catch (Exception e) { e.printStackTrace(); } System.out.println(urlStr); URL url = new URL(urlStr); InputStream in = url.openStream(); in.close(); return getStatus(); } protected String getStatus() throws InterruptedException { String status; int retries = 0; while (true) { Thread.sleep(2000); status = getSuiteStatus(sessionId); if ("SUCCESS".equals(status) || "FAILURE".equals(status)) { break; } else if ("RETRY".equals(status)) { if (retries++ == 10) { status = "FAILURE"; break; } } } return status; } protected String buildURL(String command) throws UnsupportedEncodingException { StringBuffer urlStr = new StringBuffer(200).append("http://").append(sahiHost).append(":").append(port).append( "/_s_/dyn/Suite_" + command + "?suite=").append(encode(suiteName)) .append("&base=").append(encode(base)) .append("&threads=").append(encode(threads)) .append("&sahisid=").append(encode(this.sessionId)); if (browserType != null) { urlStr.append("&browserType=").append(encode(browserType)); } else { urlStr.append("&browser=").append(encode(browser)); urlStr.append("&browserProcessName=").append(encode(browserProcessName)); if (browserOption != null) { urlStr.append("&browserOption=").append(encode(browserOption)); } } if(this.extraInfo != null){ urlStr.append("&extraInfo=").append(encode(this.extraInfo)); } if(this.initJS != null){ urlStr.append("&initJS=").append(encode(this.initJS)); } System.out.println("this.isSingleSession == " + this.isSingleSession); if(this.isSingleSession){ urlStr.append("&useSingleSession=").append(this.isSingleSession); } if (listReport == null || listReport.size() == 0) { addReport(new Report("html", null)); } for (Iterator<Report> iterator = listReport.iterator(); iterator.hasNext();) { Report report = iterator.next(); urlStr.append("&").append(encode(report.getType())).append("=").append(report.getLogDir() != null ? encode(report.getLogDir()) : ""); } if (createIssue != null) { urlStr.append("&").append(encode(createIssue.getTool())).append("="); if (createIssue.getPropertiesFile() != null) { urlStr.append(encode(createIssue.getPropertiesFile())); } } return urlStr.toString(); } private String getSuiteStatus(String sessionId) { String status; String urlStr; try { urlStr = "http://" + sahiHost + ":" + port + "/_s_/dyn/Suite_status?s" + "&sahisid=" + encode(sessionId); URL url = new URL(urlStr); InputStream in = url.openStream(); StringBuffer sb = new StringBuffer(); int c = ' '; while ((c = in.read()) != -1) { sb.append((char) c); } status = sb.toString(); in.close(); } catch (Exception e) { e.printStackTrace(); System.out.println("Exception while connecting to Sahi proxy to check status. Retrying ..."); status = "RETRY"; } return status; } protected static String encode(String s) throws UnsupportedEncodingException { return URLEncoder.encode(s, "UTF8"); } protected static String getBase(String[] args) { String base = ""; try { base = args[2]; } catch (Exception e) { } return base; } protected static String getBrowser(String[] args) { String browser = ""; try { browser = args[1]; } catch (Exception e) { } return browser; } public String toString() { StringBuffer sb = new StringBuffer(); sb.append("\nsuiteName = " + suiteName); sb.append("\nbase = " + base); sb.append("\nsahiHost = " + sahiHost); sb.append("\nport = " + port); sb.append("\nthreads = " + threads); if (browserType != null){ sb.append("\nbrowserType = " + browserType); } else { sb.append("\nbrowser = " + browser); sb.append("\nbrowserOption = " + browserOption); sb.append("\nbrowserProcessName = " + browserProcessName); } return sb.toString(); } public void setInitJS(String initJS) { this.initJS = initJS; } public void setInitJS(HashMap<String, Object> variableHashMap) { StringBuilder sb = new StringBuilder(); for (Iterator<String> iterator = variableHashMap.keySet().iterator(); iterator.hasNext();) { String key = iterator.next(); sb.append("var " + key).append(" = ").append(getJSValue(variableHashMap.get(key))).append(";"); } System.out.println(sb.toString()); this.initJS = sb.toString(); } private Object getJSValue(Object object) { if (object instanceof String) return "\"" + Utils.escapeDoubleQuotesAndBackSlashes((String)object) + "\""; return object.toString(); } public void setExtraInfo(String extraInfo) { this.extraInfo = extraInfo; } }