/* * Sakuli - Testing and Monitoring-Tool for Websites and common UIs. * * Copyright 2013 - 2015 the original author or authors. * * 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 org.sakuli.starter; import org.apache.commons.cli.*; import org.sakuli.actions.environment.CipherUtil; import org.sakuli.datamodel.TestSuite; import org.sakuli.datamodel.properties.ActionProperties; import org.sakuli.datamodel.state.TestSuiteState; import org.sakuli.exceptions.SakuliCipherException; import org.sakuli.exceptions.SakuliInitException; import org.sakuli.loader.BeanLoader; import org.sakuli.services.InitializingServiceHelper; import org.sakuli.services.TeardownServiceHelper; import org.sakuli.starter.helper.CmdPrintHelper; import org.sakuli.starter.helper.SakuliFolderHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.FileNotFoundException; import java.util.AbstractMap; import java.util.Map.Entry; import static org.apache.commons.lang.StringUtils.isNotEmpty; @SuppressWarnings("AccessStaticViaInstance") public class SakuliStarter { public static final Logger LOGGER = LoggerFactory.getLogger(SakuliStarter.class); private final static Option help = OptionBuilder.withDescription("display help").withLongOpt("help").create("h"); private final static Option run = OptionBuilder .withArgName("test-suite-folder") .hasArg() .withDescription("run a sakuli test suite") .withLongOpt("run") .create("r"); private final static Option browser = OptionBuilder .withArgName("browser") .hasArg() .withDescription("(optional) browser for the test execution, \ndefault: property 'testsuite.browser'") .withLongOpt("browser") .isRequired(false) .create("b"); private final static Option sakuliHome = OptionBuilder .withArgName("sakuli-folder") .hasArg() .withDescription("(optional) SAKULI_HOME folder, \ndefault: environment variable 'SAKULI_HOME'") .isRequired(false) .withLongOpt("sakuli_home") .create(); private final static Option sahiHome = OptionBuilder .withArgName("sahi-folder") .hasArg() .withDescription("(optional) Sahi installation folder, \ndefault: property 'sahi.proxy.homePath'") .isRequired(false) .withLongOpt("sahi_home") .create(); private final static Option encrypt = OptionBuilder .withArgName("secret") .hasArg() .withDescription("encrypt a secret") .withLongOpt("encrypt") .create("e"); private final static Option anInterface = OptionBuilder .withArgName("interface-name") .hasArg() .withDescription("(optional) network interface used for encryption, default: auto-selection") .isRequired(false) .withLongOpt("interface") .create("i"); /** * {@link System#exit(int)} value if the help is printed out. This value will be used in the `sakuli` starter */ private static final int SYSTEM_EXIT_VALUE_HELP = 100; /** * The Sakuli-Starter executes a specific sakuli-testsuite. A test suite has to contain as minimum following files: * <ul> * <li>testsuite.suite - specifies the testcases</li> * <li>testsuite.properties - specifies the runtime settings like the browser for the test suite.</li> * </ul> * * @param args relative or absolute path to the folder of your test suite */ public static void main(String[] args) { CommandLineParser parser = new PosixParser(); Options options = new Options(); options.addOption(help); options.addOption(run); options.addOption(browser); options.addOption(sakuliHome); options.addOption(sahiHome); options.addOption(encrypt); options.addOption(anInterface); try { CommandLine cmd = parser.parse(options, args); final String browserValue = getOptionValue(cmd, browser); final String testSuiteFolderPath = getOptionValue(cmd, run); final String sakuliMainFolderPath = getOptionValue(cmd, sakuliHome); final String sahiHomePath = getOptionValue(cmd, sahiHome); final String ethInterface = getOptionValue(cmd, anInterface); final String strToEncrypt = getOptionValue(cmd, encrypt); if (cmd.hasOption(run.getLongOpt()) || cmd.hasOption(run.getOpt())) { TestSuite testSuite = runTestSuite(testSuiteFolderPath, sakuliMainFolderPath, browserValue, sahiHomePath); //return the state as system exit parameter //return values are corresponding to the error codes in file "sahi_return_codes.txt" System.exit(testSuite.getState().getErrorCode()); } else if (cmd.hasOption(encrypt.getLongOpt()) || cmd.hasOption(encrypt.getOpt())) { System.out.printf("\nString to Encrypt: %s \n...", strToEncrypt); final Entry<String, String> secret = encryptSecret(strToEncrypt, ethInterface); System.out.printf("\nEncrypted secret with interface '%s': %s", secret.getKey(), secret.getValue()); System.out.println("\n\n... now copy the secret to your testcase!"); System.exit(0); } else { CmdPrintHelper.printHelp(options); System.exit(SYSTEM_EXIT_VALUE_HELP); } } catch (ParseException e) { System.err.println("Parsing of command line failed: " + e.getMessage()); CmdPrintHelper.printHelp(options); System.exit(SYSTEM_EXIT_VALUE_HELP); } catch (Exception e) { e.printStackTrace(); System.err.println("Error: " + e.getMessage()); System.exit(TestSuiteState.ERRORS.getErrorCode()); } } private static String getOptionValue(CommandLine cmd, Option option) { return option.getOpt() != null ? cmd.getOptionValue(option.getOpt()) : cmd.getOptionValue(option.getLongOpt()); } /** * wrapper for {@link #runTestSuite(String, String, String)} with usage of the default sahi-proxy configured in the * 'sakuli.properties' and the configured browser 'testsuite.browser'. */ public static TestSuite runTestSuite(String testSuiteFolderPath, String sakuliMainFolder) throws FileNotFoundException { return runTestSuite(testSuiteFolderPath, sakuliMainFolder, null); } /** * wrapper for {@link #runTestSuite(String, String, String, String)} with usage of the default sahi-proxy configured in the * 'sakuli.properties'. */ public static TestSuite runTestSuite(String testSuiteFolderPath, String sakuliMainFolder, String browser) throws FileNotFoundException { return runTestSuite(testSuiteFolderPath, sakuliMainFolder, browser, null); } /** * Executes a specific Sakuli test suite in the assigend 'testSuiteFolder'. A test suite has to contain as minimum * following files: * <ul> * <li>testsuite.suite = specifies the testcases</li> * <li>testsuite.properties = specifies the runtime settings like the browser for the test suite.</li> * </ul> * * @param testSuiteFolderPath path to the Sakuli test suite * @param sakuliHomeFolderPath path to the folder which contains 'config' and the 'libs' folder * @param browser (optional) browser for the test execution, default: property 'testsuite.browser' * @param sahiHomeFolder (optional) specifies a different sahi proxy as in the 'sakuli.properties' file * @return the {@link TestSuiteState} of the Sakuli test execution. * @throws FileNotFoundException */ public static TestSuite runTestSuite(String testSuiteFolderPath, String sakuliHomeFolderPath, String browser, String sahiHomeFolder) throws FileNotFoundException { LOGGER.info(String.format("\n\n=========== START new SAKULI Testsuite from '%s' =================", testSuiteFolderPath)); //temp log cache for init stuff -> should be in the log file String tempLogCache = ""; //check and set the path to the test suite tempLogCache = SakuliFolderHelper.checkTestSuiteFolderAndSetContextVariables(testSuiteFolderPath, tempLogCache); //check and set the sakuli main folder tempLogCache = SakuliFolderHelper.checkSakuliHomeFolderAndSetContextVariables(sakuliHomeFolderPath, tempLogCache); //if sahi home have been set overwrite the default if (isNotEmpty(sahiHomeFolder)) { tempLogCache = SakuliFolderHelper.checkSahiProxyHomeAndSetContextVariables(sahiHomeFolder, tempLogCache); } //set browser for test execution if not empty if (isNotEmpty(browser)) { tempLogCache = SakuliFolderHelper.setTestSuiteBrowserContextVariable(browser, tempLogCache); } //because of the property loading strategy, the logger must be initialized through the Spring Context! SahiConnector sahiConnector = BeanLoader.loadBean(SahiConnector.class); LOGGER.debug(tempLogCache); //Call init services InitializingServiceHelper.invokeInitializingServcies(); TestSuite result = BeanLoader.loadBean(TestSuite.class); /*** * SAKULI Starter to run the test suite with embedded sahi proxy */ try { sahiConnector.init(); //start the execution of the test suite LOGGER.debug("start new sakuli test suite"); sahiConnector.startSahiTestSuite(); } /** * will be catched if the Sahi-Proxy could not shutdown; */ catch (SakuliInitException e) { LOGGER.error("Unexpected error occurred:", e); System.exit(99); } finally { LOGGER.info("========== TEAR-DOWN SAKULI TEST SUITE '{}' ==========", result.getId()); TeardownServiceHelper.invokeTeardownServices(); //finally shutdown context and return the result result = BeanLoader.loadBean(TestSuite.class); BeanLoader.releaseContext(); } return result; } /** * Encrypt a secret based on the assigned interface. * * @param strToEncrypt secret to encrypt * @param ethInterface name of network interface, if NULL use the auto-detection * @return a Key-Value Pair of used interface for the encryption and the encrypted secret as strings. * @throws SakuliCipherException */ public static Entry<String, String> encryptSecret(String strToEncrypt, String ethInterface) throws SakuliCipherException { ActionProperties cipherProps = new ActionProperties(); if (isNotEmpty(ethInterface)) { cipherProps.setEncryptionInterface(ethInterface); cipherProps.setEncryptionInterfaceAutodetect(false); } else { cipherProps.setEncryptionInterfaceAutodetect(true); } CipherUtil cipher = new CipherUtil(cipherProps); cipher.scanNetworkInterfaces(); return new AbstractMap.SimpleEntry<>(cipher.getInterfaceName(), cipher.encrypt(strToEncrypt)); } }